package gateway import ( "encoding/binary" "unicode/utf8" ) const ( apolloChannelGeneric = 0 apolloChannelUrgent = 1 apolloChannelKeyboard = 2 apolloChannelMouse = 3 apolloChannelUTF8 = 6 apolloChannelGamepad = 16 ) type apolloInputPacket struct { channel uint8 payload []byte } func encodeApolloInputEvent(event InputEvent) ([]apolloInputPacket, error) { switch event.Device { case "keyboard": if event.Code < 0 || event.Code > 0xffff || len(event.Payload) > 1 { return nil, ErrInputMalformed } packet := make([]byte, 14) binary.BigEndian.PutUint32(packet[:4], 10) if event.Pressed { binary.LittleEndian.PutUint32(packet[4:8], 3) } else { binary.LittleEndian.PutUint32(packet[4:8], 4) } binary.LittleEndian.PutUint16(packet[9:11], uint16(event.Code)) if len(event.Payload) == 1 { packet[11] = event.Payload[0] } return []apolloInputPacket{{channel: apolloChannelKeyboard, payload: packet}}, nil case "mouse-button": if event.Code < 1 || event.Code > 8 || len(event.Payload) != 0 { return nil, ErrInputMalformed } packet := make([]byte, 9) binary.BigEndian.PutUint32(packet[:4], 5) magic := uint32(8) if !event.Pressed { magic = 9 } binary.LittleEndian.PutUint32(packet[4:8], magic) packet[8] = byte(event.Code) return []apolloInputPacket{{channel: apolloChannelMouse, payload: packet}}, nil case "mouse-relative": if event.Pressed || len(event.Payload) != 4 { return nil, ErrInputMalformed } packet := make([]byte, 12) binary.BigEndian.PutUint32(packet[:4], 8) binary.LittleEndian.PutUint32(packet[4:8], 7) copy(packet[8:], event.Payload) return []apolloInputPacket{{channel: apolloChannelMouse, payload: packet}}, nil case "utf8": if event.Pressed || len(event.Payload) == 0 || len(event.Payload) > utf8.UTFMax || !utf8.Valid(event.Payload) || utf8.RuneCount(event.Payload) != 1 { return nil, ErrInputMalformed } packet := make([]byte, 8+len(event.Payload)) binary.BigEndian.PutUint32(packet[:4], uint32(4+len(event.Payload))) binary.LittleEndian.PutUint32(packet[4:8], 0x17) copy(packet[8:], event.Payload) return []apolloInputPacket{{channel: apolloChannelUTF8, payload: packet}}, nil case "controller": if event.Code < 0 || event.Code > 15 || len(event.Payload) != 16 { return nil, ErrInputMalformed } packet := make([]byte, 34) binary.BigEndian.PutUint32(packet[:4], 30) binary.LittleEndian.PutUint32(packet[4:8], 0x0c) binary.LittleEndian.PutUint16(packet[8:10], 0x1a) binary.LittleEndian.PutUint16(packet[10:12], uint16(event.Code)) copy(packet[12:14], event.Payload[:2]) binary.LittleEndian.PutUint16(packet[14:16], 0x14) copy(packet[16:28], event.Payload[2:14]) binary.LittleEndian.PutUint16(packet[28:30], 0x9c) copy(packet[30:32], event.Payload[14:16]) binary.LittleEndian.PutUint16(packet[32:34], 0x55) return []apolloInputPacket{{channel: apolloChannelGamepad + uint8(event.Code), payload: packet}}, nil case "mouse-absolute": if event.Pressed || event.Code != 0 || !validAbsolutePayload(event.Payload) { return nil, ErrInputMalformed } width, height := binary.BigEndian.Uint16(event.Payload[4:6]), binary.BigEndian.Uint16(event.Payload[6:8]) if width < 2 || height < 2 || width > 0x7fff || height > 0x7fff { return nil, ErrInputMalformed } packet := make([]byte, 18) binary.BigEndian.PutUint32(packet[:4], 14) binary.LittleEndian.PutUint32(packet[4:8], 5) copy(packet[8:12], event.Payload[:4]) binary.BigEndian.PutUint16(packet[14:16], width-1) binary.BigEndian.PutUint16(packet[16:18], height-1) return []apolloInputPacket{{channel: apolloChannelMouse, payload: packet}}, nil case "mouse-scroll": if event.Pressed || event.Code != 0 || len(event.Payload) != 4 { return nil, ErrInputMalformed } packets := make([]apolloInputPacket, 0, 2) if event.Payload[0] != 0 || event.Payload[1] != 0 { packet := make([]byte, 14) binary.BigEndian.PutUint32(packet[:4], 10) binary.LittleEndian.PutUint32(packet[4:8], 10) copy(packet[8:10], event.Payload[:2]) copy(packet[10:12], event.Payload[:2]) packets = append(packets, apolloInputPacket{channel: apolloChannelMouse, payload: packet}) } if event.Payload[2] != 0 || event.Payload[3] != 0 { packet := make([]byte, 10) binary.BigEndian.PutUint32(packet[:4], 6) binary.LittleEndian.PutUint32(packet[4:8], 0x55000001) copy(packet[8:10], event.Payload[2:4]) packets = append(packets, apolloInputPacket{channel: apolloChannelMouse, payload: packet}) } return packets, nil default: return nil, ErrInputMalformed } }