Skip to content

C# Client Library Documentation

The VIIPER C# client library provides a modern, type-safe .NET client library for interacting with VIIPER servers and controlling virtual devices.

The C# client library features:

  • Async/await support: Full async API with cancellation token support
  • Type-safe: Generated classes with enums, structs, and helper maps
  • Event-driven: OnOutput event for device feedback (LEDs, rumble)
  • Modern .NET: Targets .NET 10.0 with nullable reference types
  • Zero external dependencies: Uses only built-in .NET libraries

License

The C# client library is licensed under the MIT License, providing maximum flexibility for integration into your projects.
The core VIIPER server remains under its original license.

Installation

The Viiper.Client package is no longer published to NuGet; reference the generated source directly from this repository:

<ItemGroup>
    <ProjectReference Include="..\..\clients\csharp\Viiper.Client\Viiper.Client.csproj" />
</ItemGroup>

Example

using Viiper.Client;
using Viiper.Client.Devices.Keyboard;

// Create new Viiper client
var client = new ViiperClient("localhost", 3242);

// Find or create a bus
var buses = await client.BusListAsync();
uint busId;
if (buses.Buses.Length == 0)
{
    var resp = await client.BusCreateAsync(null); // null = auto-assign ID
    // Or specify ID: await client.BusCreateAsync(5);
    busId = resp.BusID;
}
else
{
    busId = buses.Buses[0];
}

// Add device and connect
var deviceReq = new DeviceCreateRequest { Type = "keyboard" };
var deviceResp = await client.BusDeviceAddAsync(busId, deviceReq);
var device = await client.ConnectDeviceAsync(busId, deviceResp.DevId);

Console.WriteLine($"Connected to device {deviceResp.BusID}-{deviceResp.DevId}");

// Send keyboard input
var input = new KeyboardInput
{
    Modifiers = (byte)Mod.LeftShift,
    Count = 1,
    Keys = new[] { (byte)Key.H }
};
await device.SendAsync(input);

// Cleanup
await client.BusDeviceRemoveAsync(busId, deviceResp.DevId);

Device Control/Feedback

Creating a Device + Control/Feedback Stream

The simplest way to add a device and connect:

var deviceReq = new DeviceCreateRequest { Type = "xbox360" };
var deviceResp = await client.BusDeviceAddAsync(busId, deviceReq);
var device = await client.ConnectDeviceAsync(busId, deviceResp.DevId);

Or connect to an existing device:

var device = await client.ConnectDeviceAsync(busId, deviceId);

Sending Input

Device input is sent using generated structs with async methods:

using Viiper.Client.Devices.Xbox360;

var input = new Xbox360Input
{
    Buttons = (uint)Button.A,
    LeftTrigger = 255,
    RightTrigger = 0,
    ThumbLX = -32768,  // Left stick left
    ThumbLY = 32767,   // Left stick up
    ThumbRX = 0,
    ThumbRY = 0
};
await device.SendAsync(input);

Receiving Feedback

For devices that send feedback (rumble, LEDs), subscribe to the OnOutput event:

using Viiper.Client.Devices.Keyboard;

device.OnOutput += data =>
{
    if (data.Length < 1) return;
    byte leds = data[0];

    Console.WriteLine($"LEDs: " +
        $"Num={(leds & (byte)LED.NumLock) != 0} " +
        $"Caps={(leds & (byte)LED.CapsLock) != 0} " +
        $"Scroll={(leds & (byte)LED.ScrollLock) != 0}");
};

For Xbox360 rumble:

using Viiper.Client.Devices.Xbox360;

device.OnOutput += data =>
{
    if (data.Length < 2) return;
    byte leftMotor = data[0];
    byte rightMotor = data[1];
    Console.WriteLine($"Rumble: Left={leftMotor} Right={rightMotor}");
};

Closing a Device

device.Dispose();
// or
await using var device = await client.ConnectDeviceAsync(busId, deviceId);

The VIIPER server automatically removes the device when the stream is closed after a short timeout.

Generated Constants and Maps

The C# client library automatically generates enums and helper maps for each device type.

Keyboard Constants

Key Enum:

using Viiper.Client.Devices.Keyboard;

var key = Key.A;               // 0x04
var f1 = Key.F1;               // 0x3A
var enter = Key.Enter;         // 0x28

Modifier Flags:

var mods = (byte)(Mod.LeftShift | Mod.LeftCtrl);  // 0x03

LED Flags:

bool numLock = (leds & (byte)LED.NumLock) != 0;
bool capsLock = (leds & (byte)LED.CapsLock) != 0;

Helper Maps

The client library generates useful lookup maps for working with keyboard input:

CharToKey Map - Convert ASCII characters to key codes:

if (CharToKey.TryGetValue((byte)'A', out var key))
{
    Console.WriteLine($"'A' maps to {key}");  // Key.A
}

KeyName Map - Get human-readable key names:

if (KeyName.TryGetValue((byte)Key.F1, out var name))
{
    Console.WriteLine($"Key name: {name}");  // "F1"
}

ShiftChars Map - Check if a character requires shift:

bool needsShift = ShiftChars.ContainsKey((byte)'A');  // true for uppercase

Configuration and Advanced Usage

Custom Timeouts

var client = new ViiperClient("localhost", 3242)
{
    Timeout = TimeSpan.FromSeconds(10)
};

Default timeout is 5 seconds.

Cancellation Tokens

All async methods support cancellation:

using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));

try
{
    var buses = await client.BusListAsync(cts.Token);
}
catch (OperationCanceledException)
{
    Console.WriteLine("Request timed out");
}

Error Handling

The server returns errors as JSON. The client throws exceptions:

try
{
    await client.BusCreateAsync("invalid-bus-id");
}
catch (Exception ex)
{
    Console.WriteLine($"Request failed: {ex.Message}");
}

Resource Management

ViiperDevice implements IDisposable:

await using var device = await client.ConnectDeviceAsync(busId, deviceId);
// Device automatically closed when scope exits

Or manual cleanup:

try
{
    var device = await client.ConnectDeviceAsync(busId, deviceId);
    // ... use device ...
}
finally
{
    device.Dispose();
}

Examples

Full working examples are available in the repository:

  • Virtual Keyboard: examples/csharp/virtual_keyboard/Program.cs

    • Types "Hello!" every 5 seconds using generated maps
    • Displays LED feedback in console
  • Virtual Mouse: examples/csharp/virtual_mouse/Program.cs

    • Moves cursor in a circle pattern
    • Demonstrates button clicks and scroll wheel
  • Virtual Xbox360 Controller: examples/csharp/virtual_x360_pad/Program.cs

    • Presses buttons and moves sticks
    • Handles rumble feedback

Running Examples

cd examples/csharp/virtual_keyboard
dotnet run -- localhost

Troubleshooting

Build Errors:

Ensure you have .NET 8.0 SDK installed:

dotnet --version  # Should be 8.0 or higher

Nullable Reference Warnings:

The generated code uses nullable annotations. You may see warnings like CS8601/CS8625. These are safe to ignore or suppress in your project file:

<PropertyGroup>
    <NoWarn>$(NoWarn);CS8601;CS8625</NoWarn>
</PropertyGroup>

See Also