VirtualDevice Documentation

February 25, 2026 · View on GitHub

The VirtualDevice interface provides a flexible way to define and control virtual input devices on Linux. Below is a detailed guide on how to use it for various operations.


Interface Definition

Configuration Methods

These methods allow you to configure the virtual device before registration.

ActionDescription
WithPathSets the file path for the virtual device. Default is /dev/uinput.
WithModeSets the file mode for the device file. Default is 0660.
WithQueueLenConfigures the event queue length. Default is 1024.
WithBusTypeSets the device bus type (e.g., linux.BUS_USB).
WithVendorSets the vendor ID of the virtual device. (e.g. sdl.USB_VENDOR_MICROSOFT, 0x1234).
WithProductSets the product ID of the virtual device. (e.g. sdl.USB_PRODUCT_XBOX_ONE_S, 0x1234).
WithVersionSets the version number for the virtual device. (e.g. 0x01).
WithNameSets the name of the virtual device.
WithKeysSpecifies the keys supported by the device. (e.g. []linux.Key{linux.KEY_A, linux.KEY_B, linux.KEY_C}).
WithButtonsSpecifies the buttons supported by the device. (e.g. []linux.Button{linux.BTN_LEFT, linux.BTN_RIGHT}).
WithAbsAxesConfigures the absolute axes for the device.
WithRelAxesConfigures the relative axes for the device.
WithRepeatConfigures key repeat delay and period.
WithLEDsSpecifies the LEDs supported by the device. (e.g. []linux.Led{linux.LED_NUML, linux.LED_CAPSL).
WithPropertiesSets device-specific properties (e.g., linux.INPUT_PROP_BUTTONPAD).
WithMiscEventsSpecifies the miscellaneous events (e.g., linux.MSC_SCAN).

Lifecycle Methods

These methods manage the virtual device's lifecycle.

ActionDescription
RegisterRegisters the virtual device with the system. Must be called before sending events.
UnregisterUnregisters the virtual device, cleaning up resources.

Event Handling Methods

These methods send or manipulate input events.

ActionDescription
SendSends a raw input event of the specified type, code, and value.
SyncSends a synchronization event (e.g., linux.SYN_MT_REPORT).
SyncReportSends a default synchronization event (linux.SYN_REPORT).
PressKeySimulates a key press event.
ReleaseKeySimulates a key release event.
PressButtonSimulates a button press event.
ReleaseButtonSimulates a button release event.
SendAbsoluteEventSends an absolute axis event with the specified axis and value.
SendRelativeEventSends a relative axis event with the specified axis and value.
SetLedToggles the state of an LED on the virtual device.
SendMiscEventSends a miscellaneous event (e.g., linux.MSC_SCAN).

Usage

1. Configure a Virtual Device

Use the With... methods to configure your virtual device. These methods allow chaining for easy and clear initialization.

Example:

device := virtual_device.NewVirtualDevice().
   WithVendor(sdl.USB_VENDOR_LOGITECH).
   WithProduct(0xc22d).
   WithVersion(0x111).
   WithName("Logitech G510 Gaming Keyboard"),
   WithKeys([]linux.Key{linux.KEY_A, linux.KEY_B, linux.KEY_C}).
   WithLEDs([]linux.Led{linux.LED_CAPSL})

2. Register the Device

After configuration, call Register() to create the virtual device

err := device.Register()
if err != nil {
    log.Fatalf("Failed to register virtual device: %v", err)
}

3. Send Input Events

The VirtualDevice interface allows you to send various types of input events, simulating actions like key presses, mouse movements, and more.


Key Events

Simulate pressing and releasing keyboard keys:

device.PressKey(linux.KEY_A)  // Simulate pressing the "A" key
device.ReleaseKey(linux.KEY_A)  // Simulate releasing the "A" key

Button Events

Simulate pressing and releasing mouse or gamepad buttons:

device.PressButton(linux.BTN_LEFT)  // Simulate pressing the left mouse button
device.ReleaseButton(linux.BTN_LEFT)  // Simulate releasing the left mouse button

Absolute Axes Events

Used for devices like joysticks, touchpads, or drawing tablets. Send specific positions on an axis:

device.SendAbsoluteEvent(linux.ABS_X, 512)  // Move to the middle of the X-axis
device.SendAbsoluteEvent(linux.ABS_Y, 256)  // Move to a lower position on the Y-axis

Relative Axes Events

Used for devices like mice. Send relative movements instead of absolute positions:

device.SendRelativeEvent(linux.REL_X, 10)  // Move cursor 10 units to the right
device.SendRelativeEvent(linux.REL_Y, -5)  // Move cursor 5 units up

LED Events

Control the state of LEDs, such as Caps Lock or Num Lock:

device.SetLed(linux.LED_CAPSL, true)  // Turn on the Caps Lock LED
device.SetLed(linux.LED_CAPSL, false)  // Turn off the Caps Lock LED

Misc Events

Send miscellaneous events, including raw scan codes, to simulate hardware-level key events:

device.SendMiscEvent(linux.MSC_SCAN, 0x1E) // Send the scan code for the "A" key

4. Synchronize Events

After sending input events, it’s important to synchronize them to ensure the input subsystem processes them correctly. Synchronization informs the system that a complete input report has been sent.


Sync a Specific Event

Synchronize a specific event type (e.g., SYN_MT_REPORT) to signal that the current event sequence is complete:

device.Sync(linux.SYN_MT_REPORT)

Sync Report

Synchronize all pending input events. This is often used after sending a batch of input events to ensure they are processed together:

device.SyncReport()

Why Synchronization is Important?

Synchronization ensures that all input events sent to the system are properly interpreted and applied. Without synchronization, the system may ignore or misinterpret events, especially when sending multiple inputs in rapid succession.

5. Unregister the Device

When the device is no longer needed, unregister it to release system resources:

err := device.Unregister()
if err != nil {
    log.Fatalf("Failed to unregister virtual device: %v", err)
}

Example: Simulate Typing

Here’s how to simulate typing a string using a virtual keyboard:

package main

import (
	"fmt"
	virtual_device "github.com/jbdemonte/virtual-device"
	"github.com/jbdemonte/virtual-device/linux"
)

func main() {
	device := virtual_device.NewVirtualDevice().
		WithBusType(linux.BUS_USB).
		WithVendor(0xDEAD).
		WithProduct(0xBABE).
		WithVersion(0x01).
		WithName("Virtual Keyboard").
		WithKeys([]linux.Key{linux.KEY_A, linux.KEY_B, linux.KEY_C})

	device.Register()
	defer device.Unregister()

	device.PressKey(linux.KEY_B)
	device.ReleaseKey(linux.KEY_B)
}