Go Client Documentation
The Go client is the reference implementation for interacting with the VIIPER TCP API.
It's included in the repository under /apiclient (and /device).
The Go client features:
- Type-safe API: Structured request/response types with context support
- Device Control/Feedback: Bidirectional binary communication
- Built-in: No code generation needed; part of the main repository
- Flexible timeouts: Configurable connection and I/O timeouts
Example
package main
import (
"context"
"log"
"time"
apiclient "github.com/Alia5/VIIPER/apiclient"
"github.com/Alia5/VIIPER/device"
"github.com/Alia5/VIIPER/device/keyboard"
)
func main() {
// Create new Viiper client
client := apiclient.New("127.0.0.1:3242")
ctx := context.Background()
// Create or find a bus
buses, err := client.BusList()
if err != nil {
log.Fatal(err)
}
var busID uint32
if len(buses) > 0 {
busID = buses[0]
} else {
resp, err := client.BusCreate(nil)
if err != nil {
log.Fatal(err)
}
busID = resp.BusID
}
// Add device and connect (optional CreateOptions parameter for VID/PID)
// Pass nil to use default VID/PID for the device type.
stream, resp, err := client.AddDeviceAndConnect(ctx, busID, "keyboard", nil)
if err != nil {
log.Fatal(err)
}
defer stream.Close()
log.Printf("Connected to device %s", resp.ID)
// Send keyboard input
input := &keyboard.InputState{
Modifiers: keyboard.ModLeftShift,
}
input.SetKey(keyboard.KeyH, true)
if err := stream.WriteBinary(input); err != nil {
log.Fatal(err)
}
time.Sleep(100 * time.Millisecond)
// Release
input = &keyboard.InputState{}
stream.WriteBinary(input)
}
Device Control/Feedback API
Creating and Connecting
The simplest way to add a device and open its control stream (nil opts):
// Use default VID/PID for the device type
stream, resp, err := client.AddDeviceAndConnect(ctx, busID, "xbox360", nil)
if err != nil {
log.Fatal(err)
}
defer stream.Close()
log.Printf("Connected to device %s", resp.ID)
Sending Input
Device input is sent using structs that implement encoding.BinaryMarshaler.
Every device package (e.g. device/xbox360) provides type-safe input state structs.
import "github.com/Alia5/VIIPER/device/xbox360"
input := &xbox360.InputState{
Buttons: xbox360.ButtonA,
LX: -32768, // Left stick left
LY: 32767, // Left stick up
}
if err := stream.WriteBinary(input); err != nil {
log.Fatal(err)
}
Receiving Feedback
For devices that send feedback (rumble, LEDs), use StartReading with a decode function:
import (
"bufio"
"encoding"
"io"
"github.com/Alia5/VIIPER/device/xbox360"
)
// Start async reading for rumble commands
rumbleCh, errCh := stream.StartReading(ctx, 10, func(r *bufio.Reader) (encoding.BinaryUnmarshaler, error) {
var b [2]byte
if _, err := io.ReadFull(r, b[:]); err != nil { return nil, err }
msg := new(xbox360.XRumbleState)
if err := msg.UnmarshalBinary(b[:]); err != nil { return nil, err }
return msg, nil
})
go func() {
for {
select {
case msg := <-rumbleCh:
rumble := msg.(*xbox360.XRumbleState)
fmt.Printf("Rumble: Left=%d Right=%d\n", rumble.LeftMotor, rumble.RightMotor)
case err := <-errCh:
if err != nil { log.Printf("Stream error: %v", err) }
return
}
}
}()
Closing a Stream / Removing a Device
The VIIPER server automatically removes the device when the stream is closed after a short timeout.
Device-Specific Notes
Each device type has specific wire formats and helper methods.
For wire format details and usage patterns, see the Devices section of the documentation.
The Go client provides device packages under /device/ with type-safe structs and constants (e.g., keyboard.InputState, keyboard.KeyA, mouse.Btn_Left).
Configuration and Advanced Usage
Custom Timeouts
cfg := &apiclient.Config{
DialTimeout: 2 * time.Second,
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
}
client := apiclient.NewWithConfig("127.0.0.1:3242", cfg)
Default timeouts are: Dial 3s, Read/Write 5s.
Context-Aware Calls
All methods have context-aware variants ending with Ctx:
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
buses, err := client.BusListCtx(ctx)
Error Handling
The server returns errors as { "error": "message" } JSON. The client wraps these as Go errors:
Examples
Full working examples are available in the repository:
- Virtual Mouse:
examples/go/virtual_mouse/main.go - Virtual Keyboard:
examples/go/virtual_keyboard/main.go - Virtual Xbox360 Controller:
examples/go/virtual_x360_pad/main.go - More examples are always being added!
See Also
- Generator Documentation: How generated client libraries work
- C++ Client Library Documentation: Header-only C++ client library
- C# Client Library Documentation: .NET client library
- Rust Client Library Documentation: Rust client library
- TypeScript Client Library Documentation: Node.js client library
- API Overview: Management API reference