Mouse
In this article, we will discuss mouse input on Cosmos Gen3: how to track the pointer position, react to button presses and the scroll wheel, and draw a cursor with the graphics API.
The main differences if you come from Gen2:
| Gen2 | Gen3 | |
|---|---|---|
| Manager API | Cosmos.System.MouseManager |
Same model, in Cosmos.Kernel.System.Input |
| Button state | MouseState flags enum |
LeftButton, RightButton, MiddleButton booleans |
| Position | X, Y clamped to the screen size |
Same |
| Scroll wheel | ScrollDelta + ResetScrollDelta() |
Same |
| Cursor | Drawn by your code | Drawn by your code |
| Devices | PS/2 mouse | PS/2 mouse with scroll wheel (x64), virtio-mouse and USB mouse, all over the driver kit (the 8042 on q35 and every PC; virtio over PCI on both architectures, MMIO on ARM64; USB on an xHCI, EHCI or OHCI controller on both) |
If you find bugs or something abnormal, please submit an issue on our repository.
Enable the mouse in your kernel
Mouse support is behind a feature switch. Make sure your kernel's .csproj does not turn it off (it defaults to true):
<PropertyGroup>
<CosmosEnableMouse>true</CosmosEnableMouse>
</PropertyGroup>
These are the usings the snippets below rely on; the drawing types come from the Graphics article:
using System.Drawing;
using Cosmos.Kernel.System.Graphics;
using Cosmos.Kernel.System.Graphics.Fonts;
using Cosmos.Kernel.System.Input;
Like the keyboard, the PS/2 mouse (x64) is bound by the driver kit's Ps2MouseDriver, a virtio mouse by VirtioInputDriver and a USB mouse by UsbMouseDriver during the driver stage, each published to the manager's consumer, which registers it (PS/2 devices, Virtio devices, USB devices); MouseManager is ready as soon as your kernel runs.
USB mice also need the USB drivers, CosmosEnableUsb. You do not have to set it: left unset, it is on whenever CosmosEnableMouse is. Setting it to false keeps PS/2 and virtio mice and drops the USB ones with the host controller drivers.
To try a USB mouse in QEMU, pass --mouse usb-mouse to cosmos run: the launcher adds a qemu-xhci controller (or reuses the one USB disks or a usb-kbd keyboard already brought) and plugs the mouse into it, on either architecture. --usb-controller ehci, ohci or ehci-ohci puts an EHCI, an OHCI or an EHCI with an OHCI companion there instead, with every USB device pinned to a root port of it.
Position and buttons
MouseManager is a set of static properties that the interrupt handler keeps current: X and Y for the pointer position, LeftButton, RightButton and MiddleButton for the buttons. There is no cursor on screen until you draw one, so mouse code is a render loop that reads the properties each frame:
Canvas canvas = Canvas.GetFullScreen();
Font font = PCScreenFont.DefaultFont;
/* Clamp the pointer to the actual screen */
MouseManager.SetScreenSize(canvas.Width, canvas.Height);
while (true)
{
canvas.Clear(Color.MidnightBlue);
canvas.DrawString("X: " + MouseManager.X + " Y: " + MouseManager.Y,
font, Color.White, 40, 40);
canvas.DrawString("Buttons: "
+ (MouseManager.LeftButton ? "L" : "-")
+ (MouseManager.MiddleButton ? "M" : "-")
+ (MouseManager.RightButton ? "R" : "-"),
font, Color.White, 40, 100);
/* A minimal cursor: a white dot with a black outline */
canvas.DrawFilledCircle(Color.White, MouseManager.X, MouseManager.Y, 6);
canvas.DrawCircle(Color.Black, MouseManager.X, MouseManager.Y, 6);
canvas.Display();
Thread.Sleep(15);
}
Call SetScreenSize once at startup: the manager clamps X and Y to those bounds (the default is 1024x768, which rarely matches the framebuffer). For a real arrow cursor, blit a small bitmap instead of the circle.
Painting with the mouse
Buttons are plain booleans, so a paint program is a couple of ifs. The strokes accumulate on an off-screen canvas (see Off-screen canvases) so the cursor can be drawn on top without erasing them:
Canvas screen = Canvas.GetFullScreen();
MouseManager.SetScreenSize(screen.Width, screen.Height);
Canvas paint = new Canvas(screen.Width, screen.Height);
paint.Clear(Color.MidnightBlue);
while (true)
{
if (MouseManager.LeftButton)
{
paint.DrawFilledCircle(Color.Gold, MouseManager.X, MouseManager.Y, 6);
}
if (MouseManager.RightButton)
{
paint.Clear(Color.MidnightBlue);
}
screen.DrawCanvas(paint, 0, 0);
screen.DrawFilledCircle(Color.White, MouseManager.X, MouseManager.Y, 6);
screen.DrawCircle(Color.Black, MouseManager.X, MouseManager.Y, 6);
screen.Display();
Thread.Sleep(15);
}
The scroll wheel
Wheel movement accumulates in MouseManager.ScrollDelta, one unit per click. The driver never clears it: read it, apply it, then call ResetScrollDelta() so the same movement is not applied twice.
Canvas canvas = Canvas.GetFullScreen();
Font font = PCScreenFont.DefaultFont;
int radius = 60;
while (true)
{
int delta = MouseManager.ScrollDelta;
if (delta != 0)
{
radius = Math.Clamp(radius + delta * 10, 12, 320);
MouseManager.ResetScrollDelta();
}
canvas.Clear(Color.MidnightBlue);
canvas.DrawString("Scroll to resize the circle. Radius: " + radius,
font, Color.White, 40, 40);
canvas.DrawFilledCircle(Color.DeepSkyBlue,
canvas.Width / 2, canvas.Height / 2, radius);
canvas.Display();
Thread.Sleep(15);
}
Sensitivity and pointer control
Two more knobs on MouseManager:
Sensitivitymultiplies every movement delta (default1.0f); values below 1 slow the pointer down, values above speed it up.SetPosition(x, y)moves the pointer programmatically, clamped to the screen bounds, for example to center it when a scene opens.
Current limitations
- Only relative pointing devices are supported. There is no absolute (tablet) input, so inside a VM window the guest pointer does not track the host cursor one to one.
- Horizontal wheel tilt is ignored; only the vertical wheel reaches
ScrollDelta. - A PS/2 mouse is published once, during the driver stage, when the mouse driver bound the 8042 driver's auxiliary port node and its reset was answered; a virtio mouse present at boot once, when the stage offers its device. A USB mouse is published when its node is bound and withdrawn when the node is retracted, and so is a virtio mouse whose PCI function sits behind a PCI Express hot-plug slot, which the root port driver publishes and retracts, so those are the mice that can be plugged in and pulled out while the kernel runs.
- A USB mouse is driven in the HID boot protocol, which every PC mouse supports: three buttons, 8-bit X and Y deltas and, on a wheel mouse, the wheel in the byte after them. Extra buttons, horizontal tilt and the finer resolution some mice report in their own report protocol are not read.
How it works
On x64 the PS/2 mouse raises IRQ 12 for every byte of a movement packet, which the 8042 driver reads and hands to the auxiliary port's Ps2Access; the mouse driver's handler assembles the 3 bytes of buttons and X/Y deltas, extended to 4 by the scroll wheel byte once its probe has enabled the IntelliMouse protocol (the sample-rate sequence 200, 100, 80), drops a byte that cannot start a packet, and hands one report per packet through the pointer sink to the manager's consumer. A virtio mouse delivers the same information as event records on its queue, over PCI or MMIO: the driver kit's VirtioInputDriver drains them on the kit's worker (woken by the queue's interrupt, or every 20 ms when none could be routed), folds the axis and button events into one report per sync event, and hands it through the pointer sink to the manager's consumer. A USB mouse's boot reports arrive on its interrupt IN pipe, delivered by the host controller driver's interrupt (an MSI-X message on xHCI, the function's legacy line on EHCI and OHCI) or, when none could be routed, by its 20 ms poll; UsbMouseDriver's handler turns each report into one relative movement (HID points Y down as the manager does, and its wheel is negated so that positive scrolls down, as on PS/2) and hands it through the same sink. Either way the deltas reach MouseManager, which applies Sensitivity, adds them to X and Y, clamps to the screen size and updates the button booleans; wheel deltas accumulate in ScrollDelta until a poller consumes them. Your render loop only ever reads state, which is why no locking is needed.
Render loop (reads X/Y, buttons, ScrollDelta)
│
MouseManager ── Sensitivity, screen clamping, ScrollDelta accumulation
│
PS/2 mouse over the driver kit (the 8042, IRQ 12, x64) / virtio-mouse over the driver kit (PCI, MMIO) / USB mouse over the driver kit (xHCI, EHCI, OHCI)