package main import ( "bytes" "context" "encoding/binary" "fmt" "log" "os" "os/signal" "syscall" "github.com/mafredri/magic4linux/input" "github.com/mafredri/magic4linux/m4p" ) const ( broadcastPort = 42830 ) func main() { ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer cancel() if err := run(ctx); err != nil { panic(err) } } func createInputs() (input.Keyboard, input.Mouse) { keyboard, err := input.CreateKeyboard(input.InputType_uinput) if err != nil { panic(err) } mouse, err := input.CreateMouse(input.InputType_uinput) if err != nil { panic(err) } return keyboard, mouse } func discoverer() *m4p.Discoverer { discoverer, err := m4p.NewDiscoverer(broadcastPort) if err != nil { panic(err) } return discoverer } func run(ctx context.Context) error { keyboard, mouse := createInputs() discoverer := discoverer() defer discoverer.Close() defer keyboard.Close() defer mouse.Close() for { select { case <-ctx.Done(): return nil case device := <-discoverer.NextDevice(): err := process(ctx, device, keyboard, mouse) if err != nil { panic(err) } } } } func process(ctx context.Context, dev m4p.DeviceInfo, keyboard input.Keyboard, mouse input.Mouse) error { addr := fmt.Sprintf("%s:%d", dev.IPAddr, dev.Port) log.Printf("Starting processing with Device: %s", addr) client, err := m4p.Dial(ctx, addr) if err != nil { return err } defer client.Close() for { message, err := client.Recv(ctx) if err != nil { return err } switch message.Type { case m4p.InputMessage: processKey(message, keyboard) case m4p.RemoteUpdateMessage: processMouseMove(message, mouse) case m4p.MouseMessage: processMouseButtons(message, mouse) case m4p.WheelMessage: processMouseWheel(message, mouse) default: } } } func processKey(message m4p.Message, keyboard input.Keyboard) { key := message.Input.Parameters.KeyCode switch key { case m4p.KeyChannelUp: key = input.KeyPageup case m4p.KeyChannelDown: key = input.KeyPagedown case m4p.KeyLeft: key = input.KeyLeft case m4p.KeyUp: key = input.KeyUp case m4p.KeyRight: key = input.KeyRight case m4p.KeyDown: key = input.KeyDown case m4p.Key0: key = input.Key0 case m4p.Key1: key = input.Key1 case m4p.Key2: key = input.Key2 case m4p.Key3: key = input.Key3 case m4p.Key4: key = input.Key4 case m4p.Key5: key = input.Key5 case m4p.Key6: key = input.Key6 case m4p.Key7: key = input.Key7 case m4p.Key8: key = input.Key8 case m4p.Key9: key = input.Key9 case m4p.KeyPlay: case m4p.KeyPause: key = input.KeyPlaypause case m4p.KeyBack: key = input.KeyBackspace } if message.Input.Parameters.IsDown { keyboard.KeyDown(key) } else { keyboard.KeyUp(key) } } func processMouseButtons(message m4p.Message, mouse input.Mouse) { switch message.Mouse.Type { case "mousedown": mouse.LeftPress() case "mouseup": mouse.LeftRelease() } } func processMouseWheel(message m4p.Message, mouse input.Mouse) { mouse.Wheel(false, message.Wheel.Delta/100) } func processMouseMove(message m4p.Message, mouse input.Mouse) { r := bytes.NewReader(message.RemoteUpdate.Payload) var returnValue, deviceID uint8 var coordinate [2]int32 var gyroscope, acceleration [3]float32 var quaternion [4]float32 for _, fn := range []func() error{ func() error { return binary.Read(r, binary.LittleEndian, &returnValue) }, func() error { return binary.Read(r, binary.LittleEndian, &deviceID) }, func() error { return binary.Read(r, binary.LittleEndian, coordinate[:]) }, func() error { return binary.Read(r, binary.LittleEndian, gyroscope[:]) }, func() error { return binary.Read(r, binary.LittleEndian, acceleration[:]) }, func() error { return binary.Read(r, binary.LittleEndian, quaternion[:]) }, } { if err := fn(); err != nil { log.Printf("connect: %s decode failed: %v", message.Type, err) break } } x := coordinate[0] y := coordinate[1] mouse.Move(x, y) }