Forráskód Böngészése

Added new firmware for v3fw

TC 1 napja
szülő
commit
19c462a2a6

+ 3 - 2
remdeskd/main.go

@@ -48,8 +48,9 @@ func main() {
 
 	// Initiate the HID controller
 	usbKVM = remdeshid.NewHIDController(&remdeshid.Config{
-		PortName: *usbKVMDeviceName,
-		BaudRate: *usbKVMBaudRate,
+		PortName:          *usbKVMDeviceName,
+		BaudRate:          *usbKVMBaudRate,
+		ScrollSensitivity: 0x01, // Set mouse scroll sensitivity
 	})
 
 	//Start the HID controller

+ 78 - 0
remdeskd/mod/remdeshid/ch9329.go

@@ -0,0 +1,78 @@
+package remdeshid
+
+import (
+	"fmt"
+	"time"
+)
+
+func (c *Controller) IsModifierKeys(keycode int) bool {
+	// Modifier keycodes for JavaScript
+	modifierKeys := []int{16, 17, 18, 91} // Shift, Ctrl, Alt, Meta (Windows/Command key)
+	for _, key := range modifierKeys {
+		if keycode == key {
+			return true
+		}
+	}
+	return false
+}
+
+// ConstructAndSendCmd constructs a HID command based on the provided HIDCommand and sends it.
+func (c *Controller) ConstructAndSendCmd(HIDCommand *HIDCommand) ([]byte, error) {
+	switch HIDCommand.Event {
+	case EventTypeKeyPress:
+		if IsModifierKey(uint8(HIDCommand.Keycode)) {
+			//modifier keys
+			return c.SetModifierKey(uint8(HIDCommand.Keycode), HIDCommand.IsRightModKey)
+		} else if HIDCommand.Keycode == 13 && HIDCommand.IsRightModKey {
+			// Numpad enter
+			return c.SendKeyboardPress(uint8(146))
+		}
+		return c.SendKeyboardPress(uint8(HIDCommand.Keycode))
+	case EventTypeKeyRelease:
+		if IsModifierKey(uint8(HIDCommand.Keycode)) {
+			//modifier keys
+			return c.UnsetModifierKey(uint8(HIDCommand.Keycode), HIDCommand.IsRightModKey)
+		} else if HIDCommand.Keycode == 13 && HIDCommand.IsRightModKey {
+			// Numpad enter
+			return c.SendKeyboardRelease(uint8(146))
+		}
+		return c.SendKeyboardRelease(uint8(HIDCommand.Keycode))
+	case EventTypeMouseMove:
+		if time.Now().UnixMilli()-c.lastCursorEventTime < MinCusorEventInterval {
+			// Ignore mouse move events that are too close together
+			return []byte{}, nil
+		}
+		c.lastCursorEventTime = time.Now().UnixMilli()
+		if HIDCommand.MouseAbsX != 0 || HIDCommand.MouseAbsY != 0 {
+			xLSB := byte(HIDCommand.MouseAbsX & 0xFF)        // Extract LSB of X
+			xMSB := byte((HIDCommand.MouseAbsX >> 8) & 0xFF) // Extract MSB of X
+			yLSB := byte(HIDCommand.MouseAbsY & 0xFF)        // Extract LSB of Y
+			yMSB := byte((HIDCommand.MouseAbsY >> 8) & 0xFF) // Extract MSB of Y
+			return c.MouseMoveAbsolute(xLSB, xMSB, yLSB, yMSB)
+		} else if HIDCommand.MouseRelX != 0 || HIDCommand.MouseRelY != 0 {
+			//Todo
+		}
+		return []byte{}, nil
+	case EventTypeMousePress:
+		if HIDCommand.MouseButton < 1 || HIDCommand.MouseButton > 3 {
+			return nil, fmt.Errorf("invalid mouse button: %d", HIDCommand.MouseButton)
+		}
+		button := uint8(HIDCommand.MouseButton)
+		return c.MouseButtonPress(button)
+	case EventTypeMouseRelease:
+		if HIDCommand.MouseButton < 1 || HIDCommand.MouseButton > 3 {
+			return nil, fmt.Errorf("invalid mouse button: %d", HIDCommand.MouseButton)
+		}
+		button := uint8(HIDCommand.MouseButton)
+		return c.MouseButtonRelease(button)
+	case EventTypeMouseScroll:
+		if time.Now().UnixMilli()-c.lastCursorEventTime < MinCusorEventInterval {
+			// Ignore mouse move events that are too close together
+			return []byte{}, nil
+		}
+		c.lastCursorEventTime = time.Now().UnixMilli()
+		return c.MouseScroll(HIDCommand.MouseScroll)
+	default:
+		return nil, fmt.Errorf("unsupported HID command event type: %d", HIDCommand.Event)
+	}
+}

+ 9 - 169
remdeskd/mod/remdeshid/handler.go

@@ -5,38 +5,11 @@ import (
 	"fmt"
 	"log"
 	"net/http"
-	"time"
 
 	"github.com/gorilla/websocket"
 )
 
-type EventType int
-
-const (
-	EventTypeKeyPress EventType = iota
-	EventTypeKeyRelease
-	EventTypeMouseMove
-	EventTypeMousePress
-	EventTypeMouseRelease
-	EventTypeMouseScroll
-	EventTypeHIDCommand
-)
-
-const MinCusrorEventInterval = 1 //ms
-
-type HIDCommand struct {
-	Event         EventType `json:"event"`
-	Keycode       int       `json:"keycode,omitempty"`
-	IsRightModKey bool      `json:"is_right_modifier_key,omitempty"` // true if the key is a right modifier key (Ctrl, Shift, Alt, GUI)
-	MouseAbsX     int       `json:"mouse_x,omitempty"`               // Absolute mouse position in X direction
-	MouseAbsY     int       `json:"mouse_y,omitempty"`               // Absolute mouse position in Y direction
-	MouseRelX     int       `json:"mouse_rel_x,omitempty"`           // Relative mouse movement in X direction
-	MouseRelY     int       `json:"mouse_rel_y,omitempty"`           // Relative mouse movement in Y direction
-	MouseButton   int       `json:"mouse_button,omitempty"`          //0x01 for left click, 0x02 for right click, 0x03 for middle clicks
-	MouseScroll   int       `json:"mouse_scroll,omitempty"`          // Positive for scroll up, negative for scroll down, max 127
-}
-
-// HIDWebSocketHandler is a handler for the HID WebSocket connection
+// upgrader is used to upgrade HTTP connections to WebSocket connections
 var upgrader = websocket.Upgrader{
 	ReadBufferSize:  1024,
 	WriteBufferSize: 1024,
@@ -45,127 +18,7 @@ var upgrader = websocket.Upgrader{
 	},
 }
 
-func (c *Controller) IsModifierKeys(keycode int) bool {
-	// Modifier keycodes for JavaScript
-	modifierKeys := []int{16, 17, 18, 91} // Shift, Ctrl, Alt, Meta (Windows/Command key)
-	for _, key := range modifierKeys {
-		if keycode == key {
-			return true
-		}
-	}
-	return false
-}
-
-// Convert a keycode to the corresponding HID opcode for modifier keys
-func (c *Controller) GetModkeyOpcode(keycode int, isRight bool) int {
-	switch keycode {
-	case 17: // Ctrl
-		if isRight {
-			return 0x05 // Right Ctrl
-		}
-		return 0x01 // Left Ctrl
-	case 16: // Shift
-		if isRight {
-			return 0x06 // Right Shift
-		}
-		return 0x02 // Left Shift
-	case 18: // Alt
-		if isRight {
-			return 0x07 // Right Alt
-		}
-		return 0x03 // Left Alt
-	case 91: // Meta (Windows/Command key)
-		if isRight {
-			return 0x08 // Right GUI (Windows)
-		}
-		return 0x04 // Left GUI (Windows)
-	default:
-		return 0x00 // Unknown or unmapped key
-	}
-}
-
-// Generate the required LTV (length, type, value) bytes for the HID command
-func (c *Controller) GenerateUARTCmd(HIDCommand *HIDCommand) ([]byte, error) {
-	switch HIDCommand.Event {
-	case EventTypeKeyPress:
-		if c.IsModifierKeys(HIDCommand.Keycode) {
-			// If it's a modifier key, we need to return the appropriate opcode
-			opcode := c.GetModkeyOpcode(HIDCommand.Keycode, HIDCommand.IsRightModKey)
-			if opcode == 0x00 {
-				return nil, fmt.Errorf("unsupported modifier key: %d", HIDCommand.Keycode)
-			}
-			return []byte{0x02, 0x03, byte(opcode)}, nil
-		} else if HIDCommand.Keycode == 13 && HIDCommand.IsRightModKey {
-			//Special case for right Enter key
-			return []byte{0x02, 0x01, 0x92}, nil
-		}
-		return []byte{0x02, 0x01, byte(HIDCommand.Keycode)}, nil
-	case EventTypeKeyRelease:
-		if c.IsModifierKeys(HIDCommand.Keycode) {
-			// If it's a modifier key, we need to return the appropriate opcode
-			opcode := c.GetModkeyOpcode(HIDCommand.Keycode, HIDCommand.IsRightModKey)
-			if opcode == 0x00 {
-				return nil, fmt.Errorf("unsupported modifier key: %d", HIDCommand.Keycode)
-			}
-			return []byte{0x02, 0x04, byte(opcode)}, nil
-		} else if HIDCommand.Keycode == 13 && HIDCommand.IsRightModKey {
-			//Special case for right Enter key
-			return []byte{0x02, 0x02, 0x92}, nil
-		}
-		return []byte{0x02, 0x02, byte(HIDCommand.Keycode)}, nil
-	case EventTypeMouseMove:
-		if (time.Now().UnixMilli() - c.lastCursorEventTime) < MinCusrorEventInterval {
-			// If the last cursor event was too recent, we skip sending this one
-			return []byte{}, nil
-		}
-		c.lastCursorEventTime = time.Now().UnixMilli()
-		if HIDCommand.MouseAbsX != 0 || HIDCommand.MouseAbsY != 0 {
-			// If absolute mouse movement is specified, we send it as a single command
-			xLSB := byte(HIDCommand.MouseAbsX & 0xFF)
-			xMSB := byte((HIDCommand.MouseAbsX >> 8) & 0xFF)
-			yLSB := byte(HIDCommand.MouseAbsY & 0xFF)
-			yMSB := byte((HIDCommand.MouseAbsY >> 8) & 0xFF)
-			return []byte{0x05, 0x09, xLSB, xMSB, yLSB, yMSB}, nil
-		} else if HIDCommand.MouseRelX != 0 || HIDCommand.MouseRelY != 0 {
-			// If relative mouse movement is specified, we send it as a single command
-			//TODO
-			return []byte{0x03, 0x0A, byte(HIDCommand.MouseRelX), byte(HIDCommand.MouseRelY)}, nil
-		}
-		return nil, fmt.Errorf("no mouse movement specified")
-	case EventTypeMousePress:
-		if HIDCommand.MouseButton < 1 || HIDCommand.MouseButton > 3 {
-			return nil, fmt.Errorf("invalid mouse button: %d", HIDCommand.MouseButton)
-		}
-		// Mouse button click event
-		return []byte{0x02, 0x05, byte(HIDCommand.MouseButton)}, nil
-	case EventTypeMouseRelease:
-		if HIDCommand.MouseButton < 1 || HIDCommand.MouseButton > 3 {
-			return nil, fmt.Errorf("invalid mouse button: %d", HIDCommand.MouseButton)
-		}
-		// Mouse button release event
-		return []byte{0x02, 0x06, byte(HIDCommand.MouseButton)}, nil
-	case EventTypeMouseScroll:
-		if (time.Now().UnixMilli() - c.lastCursorEventTime) < MinCusrorEventInterval {
-			// If the last cursor event was too recent, we skip sending this one
-			return []byte{}, nil
-		}
-		c.lastCursorEventTime = time.Now().UnixMilli()
-
-		if HIDCommand.MouseScroll < -127 || HIDCommand.MouseScroll > 127 {
-			return nil, fmt.Errorf("invalid mouse scroll value: %d", HIDCommand.MouseScroll)
-		}
-		// Mouse scroll event
-		if HIDCommand.MouseScroll < 0 {
-			return []byte{0x02, 0x07, byte(HIDCommand.MouseScroll * -1)}, nil
-		} else {
-			return []byte{0x02, 0x08, byte(HIDCommand.MouseScroll)}, nil
-		}
-
-	default:
-		return nil, fmt.Errorf("unsupported HID command event type: %d", HIDCommand.Event)
-	}
-}
-
+// HIDWebSocketHandler handles incoming WebSocket connections for HID commands
 func (c *Controller) HIDWebSocketHandler(w http.ResponseWriter, r *http.Request) {
 	conn, err := upgrader.Upgrade(w, r, nil)
 	if err != nil {
@@ -189,38 +42,25 @@ func (c *Controller) HIDWebSocketHandler(w http.ResponseWriter, r *http.Request)
 			continue
 		}
 
-		bytes, err := c.GenerateUARTCmd(&hidCmd)
+		bytes, err := c.ConstructAndSendCmd(&hidCmd)
 		if err != nil {
 			errmsg := map[string]string{"error": err.Error()}
 			if err := conn.WriteJSON(errmsg); err != nil {
 				log.Println("Error writing message:", err)
 				continue
 			}
-			log.Println("Error generating UART command:", err)
+			log.Println("Error sending command:", err)
 			continue
 		}
 
-		if len(bytes) == 0 {
-			if err := conn.WriteMessage(websocket.TextMessage, []byte("ok")); err != nil {
-				log.Println("Error writing message:", err)
-			}
-			continue
+		prettyBytes := ""
+		for _, b := range bytes {
+			prettyBytes += fmt.Sprintf("0x%02X ", b)
 		}
-
-		fmt.Println("Sending bytes:", bytes)
-
-		//Write the bytes to the serial port
-		if err := c.Send(bytes); err != nil {
-			errmsg := map[string]string{"error": err.Error()}
-			if err := conn.WriteJSON(errmsg); err != nil {
-				log.Println("Error writing message:", err)
-			}
-			continue
-		}
-
-		if err := conn.WriteMessage(websocket.TextMessage, []byte("ok")); err != nil {
+		if err := conn.WriteMessage(websocket.TextMessage, []byte(prettyBytes)); err != nil {
 			log.Println("Error writing message:", err)
 			continue
 		}
+
 	}
 }

+ 306 - 0
remdeskd/mod/remdeshid/keyboard.go

@@ -0,0 +1,306 @@
+package remdeshid
+
+import "errors"
+
+const (
+	MOD_LCTRL  = 0x01
+	MOD_LSHIFT = 0x02
+	MOD_LALT   = 0x04
+	MOD_LGUI   = 0x08
+	MOD_RCTRL  = 0x10
+	MOD_RSHIFT = 0x20
+	MOD_RALT   = 0x40
+	MOD_RGUI   = 0x80
+	MOD_RENTER = 0x58
+)
+
+// IsModifierKey checks if the given JavaScript keycode corresponds to a modifier key
+func IsModifierKey(keycode uint8) bool {
+	switch keycode {
+	case 16: // Shift
+		return true
+	case 17: // Control
+		return true
+	case 18: // Alt
+		return true
+	case 91: // Meta (Windows/Command key)
+		return true
+	default:
+		return false
+	}
+}
+
+func (c *Controller) SetModifierKey(keycode uint8, isRight bool) ([]byte, error) {
+	// Determine the modifier bit based on HID keycode
+	var modifierBit uint8
+	switch keycode {
+	case 17:
+		if isRight {
+			modifierBit = MOD_RCTRL
+		} else {
+			modifierBit = MOD_LCTRL
+		}
+	case 16:
+		if isRight {
+			modifierBit = MOD_RSHIFT
+		} else {
+			modifierBit = MOD_LSHIFT
+		}
+	case 18:
+		if isRight {
+			modifierBit = MOD_RALT
+		} else {
+			modifierBit = MOD_LALT
+		}
+	case 91:
+		if isRight {
+			modifierBit = MOD_RGUI
+		} else {
+			modifierBit = MOD_LGUI
+		}
+	default:
+		// Not a modifier key
+		return nil, errors.ErrUnsupported
+	}
+
+	c.hidState.Modkey |= modifierBit
+
+	return keyboardSendKeyCombinations(c)
+}
+
+func (c *Controller) UnsetModifierKey(keycode uint8, isRight bool) ([]byte, error) {
+	// Determine the modifier bit based on HID keycode
+	var modifierBit uint8
+	switch keycode {
+	case 17:
+		if isRight {
+			modifierBit = MOD_RCTRL
+		} else {
+			modifierBit = MOD_LCTRL
+		}
+	case 16:
+		if isRight {
+			modifierBit = MOD_RSHIFT
+		} else {
+			modifierBit = MOD_LSHIFT
+		}
+	case 18:
+		if isRight {
+			modifierBit = MOD_RALT
+		} else {
+			modifierBit = MOD_LALT
+		}
+	case 91:
+		if isRight {
+			modifierBit = MOD_RGUI
+		} else {
+			modifierBit = MOD_LGUI
+		}
+	default:
+		// Not a modifier key
+		return nil, errors.ErrUnsupported
+	}
+
+	c.hidState.Modkey &^= modifierBit
+	return keyboardSendKeyCombinations(c)
+}
+
+// SendKeyboardPress sends a keyboard press by JavaScript keycode
+func (c *Controller) SendKeyboardPress(keycode uint8) ([]byte, error) {
+	// Convert JavaScript keycode to HID
+	keycode = javaScriptKeycodeToHIDOpcode(keycode)
+	if keycode == 0x00 {
+		// Not supported
+		return nil, errors.New("Unsupported keycode: " + string(keycode))
+	}
+
+	// Already pressed? Skip
+	for i := 0; i < 6; i++ {
+		if c.hidState.KeyboardButtons[i] == keycode {
+			return nil, nil
+		}
+	}
+
+	// Get the empty slot in the current HID list
+	for i := 0; i < 6; i++ {
+		if c.hidState.KeyboardButtons[i] == 0x00 {
+			c.hidState.KeyboardButtons[i] = keycode
+			return keyboardSendKeyCombinations(c)
+		}
+	}
+
+	// No space left
+	return nil, errors.New("No space left in keyboard state to press key: " + string(keycode))
+}
+
+// SendKeyboardRelease sends a keyboard release by JavaScript keycode
+func (c *Controller) SendKeyboardRelease(keycode uint8) ([]byte, error) {
+	// Convert JavaScript keycode to HID
+	keycode = javaScriptKeycodeToHIDOpcode(keycode)
+	if keycode == 0x00 {
+		// Not supported
+		return nil, errors.New("Unsupported keycode: " + string(keycode))
+	}
+
+	// Find the position where the key is pressed
+	for i := 0; i < 6; i++ {
+		if c.hidState.KeyboardButtons[i] == keycode {
+			c.hidState.KeyboardButtons[i] = 0x00
+			return keyboardSendKeyCombinations(c)
+		}
+	}
+
+	// That key is not pressed
+	return nil, nil
+}
+
+// keyboardSendKeyCombinations simulates sending the current key combinations
+func keyboardSendKeyCombinations(c *Controller) ([]byte, error) {
+	// Prepare the packet
+	packet := []uint8{
+		0x57, 0xAB, 0x00, 0x02, 0x08,
+		c.hidState.Modkey, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00,
+	}
+
+	// Populate the HID keycodes
+	for i := 0; i < len(c.hidState.KeyboardButtons); i++ {
+		packet[7+i] = c.hidState.KeyboardButtons[i]
+	}
+
+	// Calculate checksum
+	packet[13] = calcChecksum(packet[:13])
+
+	err := c.Send(packet)
+	if err != nil {
+		return nil, errors.New("failed to send mouse move command: " + err.Error())
+	}
+
+	// Wait for a reply from the device
+	return c.WaitForReply(0x02)
+}
+
+// JavaScriptKeycodeToHIDOpcode converts JavaScript keycode into HID keycode
+func javaScriptKeycodeToHIDOpcode(keycode uint8) uint8 {
+	// Letters A-Z
+	if keycode >= 65 && keycode <= 90 {
+		return (keycode - 65) + 0x04 // 'A' is 0x04
+	}
+
+	// Numbers 1-9 (top row, not numpad)
+	if keycode >= 49 && keycode <= 57 {
+		return (keycode - 49) + 0x1E // '1' is 0x1E
+	}
+
+	// Numpad 1-9
+	if keycode >= 97 && keycode <= 105 {
+		return (keycode - 97) + 0x59 // '1' (numpad) is 0x59
+	}
+
+	// F1 to F12
+	if keycode >= 112 && keycode <= 123 {
+		return (keycode - 112) + 0x3A // 'F1' is 0x3A
+	}
+
+	switch keycode {
+	case 8:
+		return 0x2A // Backspace
+	case 9:
+		return 0x2B // Tab
+	case 13:
+		return 0x28 // Enter
+	case 16:
+		return 0xE1 // Left shift
+	case 17:
+		return 0xE0 // Left Ctrl
+	case 18:
+		return 0xE6 // Left Alt
+	case 19:
+		return 0x48 // Pause
+	case 20:
+		return 0x39 // Caps Lock
+	case 27:
+		return 0x29 // Escape
+	case 32:
+		return 0x2C // Spacebar
+	case 33:
+		return 0x4B // Page Up
+	case 34:
+		return 0x4E // Page Down
+	case 35:
+		return 0x4D // End
+	case 36:
+		return 0x4A // Home
+	case 37:
+		return 0x50 // Left Arrow
+	case 38:
+		return 0x52 // Up Arrow
+	case 39:
+		return 0x4F // Right Arrow
+	case 40:
+		return 0x51 // Down Arrow
+	case 44:
+		return 0x46 // Print Screen or F13 (Firefox)
+	case 45:
+		return 0x49 // Insert
+	case 46:
+		return 0x4C // Delete
+	case 48:
+		return 0x27 // 0 (not Numpads)
+	case 59:
+		return 0x33 // ';'
+	case 61:
+		return 0x2E // '='
+	case 91:
+		return 0xE3 // Left GUI (Windows)
+	case 92:
+		return 0xE7 // Right GUI
+	case 93:
+		return 0x65 // Menu key
+	case 96:
+		return 0x62 // 0 (Numpads)
+	case 106:
+		return 0x55 // * (Numpads)
+	case 107:
+		return 0x57 // + (Numpads)
+	case 109:
+		return 0x56 // - (Numpads)
+	case 110:
+		return 0x63 // dot (Numpads)
+	case 111:
+		return 0x54 // divide (Numpads)
+	case 144:
+		return 0x53 // Num Lock
+	case 145:
+		return 0x47 // Scroll Lock
+	case 146:
+		return 0x58 // Numpad enter
+	case 173:
+		return 0x2D // -
+	case 186:
+		return 0x33 // ';'
+	case 187:
+		return 0x2E // '='
+	case 188:
+		return 0x36 // ','
+	case 189:
+		return 0x2D // '-'
+	case 190:
+		return 0x37 // '.'
+	case 191:
+		return 0x38 // '/'
+	case 192:
+		return 0x35 // '`'
+	case 219:
+		return 0x2F // '['
+	case 220:
+		return 0x31 // backslash
+	case 221:
+		return 0x30 // ']'
+	case 222:
+		return 0x34 // '\''
+	default:
+		return 0x00 // Unknown / unsupported
+	}
+}

+ 131 - 0
remdeskd/mod/remdeshid/mouse.go

@@ -0,0 +1,131 @@
+package remdeshid
+
+import (
+	"bytes"
+	"encoding/binary"
+	"errors"
+	"fmt"
+)
+
+// calcChecksum calculates the checksum for a given data slice.
+func calcChecksum(data []uint8) uint8 {
+	var sum uint8 = 0
+	for _, value := range data {
+		sum += value
+	}
+	return sum
+}
+
+func (c *Controller) MouseMoveAbsolute(xLSB, xMSB, yLSB, yMSB uint8) ([]byte, error) {
+	packet := []uint8{
+		0x57, 0xAB, 0x00, 0x04, 0x07, 0x02,
+		c.hidState.MouseButtons,
+		xLSB, // X LSB
+		xMSB, // X MSB
+		yLSB, // Y LSB
+		yMSB, // Y MSB
+		0x00, // Scroll
+		0x00, // Checksum placeholder
+	}
+
+	packet[12] = calcChecksum(packet[:12])
+
+	buf := new(bytes.Buffer)
+	if err := binary.Write(buf, binary.LittleEndian, packet); err != nil {
+		return nil, errors.New("failed to write packet to buffer")
+	}
+
+	err := c.Send(buf.Bytes())
+	if err != nil {
+		return nil, errors.New("failed to send mouse move command: " + err.Error())
+	}
+
+	// Wait for a reply from the device
+	return c.WaitForReply(0x04)
+}
+
+func (c *Controller) MouseMoveRelative(dx, dy, wheel uint8) ([]byte, error) {
+	// Ensure 0x80 is not used
+	if dx == 0x80 {
+		dx = 0x81
+	}
+	if dy == 0x80 {
+		dy = 0x81
+	}
+
+	packet := []uint8{
+		0x57, 0xAB, 0x00, 0x05, 0x05, 0x01,
+		c.hidState.MouseButtons,
+		dx,    // Delta X
+		dy,    // Delta Y
+		wheel, // Scroll wheel
+		0x00,  // Checksum placeholder
+	}
+
+	packet[10] = calcChecksum(packet[:10])
+
+	buf := new(bytes.Buffer)
+	if err := binary.Write(buf, binary.LittleEndian, packet); err != nil {
+		return nil, errors.New("failed to write packet to buffer")
+	}
+
+	err := c.Send(buf.Bytes())
+	if err != nil {
+		return nil, errors.New("failed to send mouse move relative command: " + err.Error())
+	}
+
+	return c.WaitForReply(0x05)
+}
+
+// Handle mouse button press events
+func (c *Controller) MouseButtonPress(button uint8) ([]byte, error) {
+	switch button {
+	case 0x01: // Left
+		c.hidState.MouseButtons |= 0x01
+	case 0x02: // Right
+		c.hidState.MouseButtons |= 0x02
+	case 0x03: // Middle
+		c.hidState.MouseButtons |= 0x04
+	default:
+		return nil, errors.New("invalid opcode for mouse button press")
+	}
+
+	// Send updated button state with no movement
+	return c.MouseMoveRelative(0, 0, 0)
+}
+
+// Handle mouse button release events
+func (c *Controller) MouseButtonRelease(button uint8) ([]byte, error) {
+	switch button {
+	case 0x00: // Release all
+		c.hidState.MouseButtons = 0x00
+	case 0x01: // Left
+		c.hidState.MouseButtons &^= 0x01
+	case 0x02: // Right
+		c.hidState.MouseButtons &^= 0x02
+	case 0x03: // Middle
+		c.hidState.MouseButtons &^= 0x04
+	default:
+		return nil, errors.New("invalid opcode for mouse button release")
+	}
+
+	// Send updated button state with no movement
+	return c.MouseMoveRelative(0, 0, 0)
+}
+
+func (c *Controller) MouseScroll(tilt int) ([]byte, error) {
+	if tilt == 0 {
+		// No need to scroll
+		return nil, nil
+	}
+
+	var wheel uint8
+	if tilt < 0 {
+		wheel = uint8(c.Config.ScrollSensitivity)
+	} else {
+		wheel = uint8(0xFF - c.Config.ScrollSensitivity)
+	}
+
+	fmt.Println(tilt, "-->", wheel)
+	return c.MouseMoveRelative(0, 0, wheel)
+}

+ 98 - 44
remdeskd/mod/remdeshid/remdeshid.go

@@ -3,34 +3,28 @@ package remdeshid
 import (
 	"fmt"
 	"log"
+	"time"
 
 	"github.com/tarm/serial"
 )
 
-type Config struct {
-	/* Bindings and callback */
-	OnWriteError  func(error)         // Callback for when an error occurs while writing to USBKVM device
-	OnReadError   func(error)         // Callback for when an error occurs while reading from USBKVM device
-	OnDataHandler func([]byte, error) // Callback for when data is received from USBKVM device
-
-	/* Serial port configs */
-	PortName string
-	BaudRate int
-}
-
-// Controller is a struct that represents a HID controller
-type Controller struct {
-	Config              *Config
-	serialPort          *serial.Port
-	serialRunning       bool
-	writeQueue          chan []byte
-	lastCursorEventTime int64
-}
-
 func NewHIDController(config *Config) *Controller {
+	// Initialize the HID state with default values
+	defaultHidState := HIDState{
+		Modkey:          0x00,
+		KeyboardButtons: [6]uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		Leds:            0x00,
+		MouseButtons:    0x00, // No mouse buttons pressed
+		MouseX:          0,
+		MouseY:          0,
+	}
+
 	return &Controller{
-		Config:        config,
-		serialRunning: false,
+		Config:          config,
+		serialRunning:   false,
+		hidState:        defaultHidState,
+		writeQueue:      make(chan []byte, 32),
+		incomgDataQueue: make(chan []byte, 1024),
 	}
 }
 
@@ -56,40 +50,28 @@ func (c *Controller) Connect() error {
 		for {
 			n, err := port.Read(buf)
 			if err != nil {
-				if c.Config.OnReadError != nil {
-					c.Config.OnReadError(err)
-				} else {
-					log.Println(err.Error())
-				}
+				log.Println(err.Error())
 				return
 			}
 			if n > 0 {
-				if c.Config.OnDataHandler != nil {
-					c.Config.OnDataHandler(buf[:n], nil)
-				} else {
-					fmt.Print("Received bytes: ")
-					for i := 0; i < n; i++ {
-						fmt.Printf("0x%02X ", buf[i])
-					}
-					fmt.Println()
-				}
+				c.incomgDataQueue <- buf[:n]
+				//fmt.Print("Received bytes: ")
+				//for i := 0; i < n; i++ {
+				//	fmt.Printf("0x%02X ", buf[i])
+				//}
+				fmt.Println()
 			}
 		}
 	}()
 
 	//Create a loop to write to the serial port
-	c.writeQueue = make(chan []byte, 1)
 	c.serialRunning = true
 	go func() {
 		for {
 			data := <-c.writeQueue
 			_, err := port.Write(data)
 			if err != nil {
-				if c.Config.OnWriteError != nil {
-					c.Config.OnWriteError(err)
-				} else {
-					log.Println(err.Error())
-				}
+				log.Println(err.Error())
 				return
 			}
 		}
@@ -108,8 +90,80 @@ func (c *Controller) Send(data []byte) error {
 	if !c.serialRunning {
 		return fmt.Errorf("serial port is not running")
 	}
-	c.writeQueue <- data
-	return nil
+	select {
+	case c.writeQueue <- data:
+		return nil
+	case <-time.After(30 * time.Millisecond):
+		return fmt.Errorf("timeout waiting to send data")
+	}
+}
+
+func (c *Controller) ClearReadQueue() {
+	// Clear the incoming data queue
+	for len(c.incomgDataQueue) > 0 {
+		<-c.incomgDataQueue
+	}
+}
+
+func (c *Controller) WaitForReply(cmdByte byte) ([]byte, error) {
+	// Wait for a reply from the device
+	succReplyByte := cmdByte | 0x80
+	errorReplyByte := cmdByte | 0xC0
+	timeout := make(chan bool, 1)
+	go func() {
+		// Timeout after 500ms
+		time.Sleep(500 * time.Millisecond)
+		timeout <- true
+	}()
+
+	var reply []byte
+	for {
+		select {
+		case data := <-c.incomgDataQueue:
+			reply = append(reply, data...)
+			// Check if we have received enough bytes for a complete packet
+			if len(reply) >= 7 {
+				// Validate header
+				if reply[0] == 0x57 && reply[1] == 0xAB {
+					// Extract fields
+					//address := reply[2]
+					replyByte := reply[3]
+					dataLength := reply[4]
+					expectedLength := 5 + int(dataLength) + 1 // Header + address + replyByte + dataLength + data + checksum
+
+					// Check if the full packet is received
+					if len(reply) >= expectedLength {
+						data := reply[5 : 5+dataLength]
+						checksum := reply[5+dataLength]
+
+						// Calculate checksum
+						sum := byte(0)
+						for _, b := range reply[:5+dataLength] {
+							sum += b
+						}
+
+						// Validate checksum
+						if sum == checksum {
+							// Check reply byte for success or error
+							switch replyByte {
+							case succReplyByte:
+								return data, nil
+							case errorReplyByte:
+								return nil, fmt.Errorf("device returned error reply")
+							}
+						} else {
+							return nil, fmt.Errorf("checksum validation failed")
+						}
+					}
+				} else {
+					// Invalid header, discard data
+					reply = nil
+				}
+			}
+		case <-timeout:
+			return nil, fmt.Errorf("timeout waiting for reply")
+		}
+	}
 }
 
 func (c *Controller) Close() {

+ 59 - 0
remdeskd/mod/remdeshid/typedef.go

@@ -0,0 +1,59 @@
+package remdeshid
+
+import "github.com/tarm/serial"
+
+type EventType int
+
+const (
+	EventTypeKeyPress EventType = iota
+	EventTypeKeyRelease
+	EventTypeMouseMove
+	EventTypeMousePress
+	EventTypeMouseRelease
+	EventTypeMouseScroll
+	EventTypeHIDCommand
+)
+
+const MinCusorEventInterval = 36 // Minimum interval between cursor events in milliseconds
+
+type Config struct {
+	/* Serial port configs */
+	PortName          string
+	BaudRate          int
+	ScrollSensitivity uint8 // Mouse scroll sensitivity, range 0x00 to 0x7E
+}
+
+type HIDState struct {
+	/* Keyboard state */
+	Modkey          uint8    // Modifier key state
+	KeyboardButtons [6]uint8 // Keyboard buttons state
+	Leds            uint8    // LED state
+
+	/* Mouse state */
+	MouseButtons uint8 // Mouse buttons state
+	MouseX       int16 // Mouse X movement
+	MouseY       int16 // Mouse Y movement
+}
+
+// Controller is a struct that represents a HID controller
+type Controller struct {
+	Config              *Config
+	serialPort          *serial.Port
+	hidState            HIDState // Current state of the HID device
+	serialRunning       bool
+	writeQueue          chan []byte
+	incomgDataQueue     chan []byte // Queue for incoming data
+	lastCursorEventTime int64
+}
+
+type HIDCommand struct {
+	Event         EventType `json:"event"`
+	Keycode       int       `json:"keycode,omitempty"`
+	IsRightModKey bool      `json:"is_right_modifier_key,omitempty"` // true if the key is a right modifier key (Ctrl, Shift, Alt, GUI)
+	MouseAbsX     int       `json:"mouse_x,omitempty"`               // Absolute mouse position in X direction
+	MouseAbsY     int       `json:"mouse_y,omitempty"`               // Absolute mouse position in Y direction
+	MouseRelX     int       `json:"mouse_rel_x,omitempty"`           // Relative mouse movement in X direction
+	MouseRelY     int       `json:"mouse_rel_y,omitempty"`           // Relative mouse movement in Y direction
+	MouseButton   int       `json:"mouse_button,omitempty"`          //0x01 for left click, 0x02 for right click, 0x03 for middle clicks
+	MouseScroll   int       `json:"mouse_scroll,omitempty"`          // Positive for scroll up, negative for scroll down, max 127
+}

+ 3 - 4
remdeskd/www/index.html

@@ -35,7 +35,6 @@
         let port = window.location.port ? window.location.port : (protocol === 'wss' ? 443 : 80);
         let socketURL = `${protocol}://${window.location.hostname}:${port}/hid`;
         let mouseMoveAbsolte = true; // Set to true for absolute mouse coordinates, false for relativeZ
-        let mouseScrollSensitivity = 0.01; // Adjust this value to change scroll sensitivity
         let mouseIsOutside = false;
 
         /* Mouse events */
@@ -72,6 +71,7 @@
                 console.error("WebSocket is not open.");
             }
         }
+        
 
         function handleMousePress(event) {
             event.preventDefault();
@@ -130,7 +130,7 @@
         function handleMouseScroll(event) {
             const hidCommand = {
                 event: 5,
-                mouse_scroll: parseInt(event.deltaY * mouseScrollSensitivity)
+                mouse_scroll: event.deltaY
             };
             if (mouseIsOutside) {
                 console.warn("Mouse is outside the capture area, ignoring mouse press.");
@@ -138,7 +138,7 @@
             }
 
 
-            console.log(`Mouse scroll: mouse_scroll=${event.deltaY} (scaled: ${hidCommand.mouse_scroll})`);
+            console.log(`Mouse scroll: mouse_scroll=${event.deltaY}`);
 
             if (socket && socket.readyState === WebSocket.OPEN) {
                 socket.send(JSON.stringify(hidCommand));
@@ -153,7 +153,6 @@
         document.addEventListener('mouseup', handleMouseRelease);
         document.addEventListener('wheel', handleMouseScroll);
 
-
         /* Keyboard */
         function isNumpadEvent(event) {
             return event.location === 3;