Skip to main content
Glama
control.go6.45 kB
package protocol import ( "encoding/binary" ) // Control message type aliases for compatibility const ( ControlMsgTypeInjectTouch = ControlMsgTypeInjectTouchEvent ControlMsgTypeInjectScroll = ControlMsgTypeInjectScrollEvent // Touch action constants TouchActionDown = 0 TouchActionUp = 1 TouchActionMove = 2 ) // KeyEvent represents a keyboard event type KeyEvent struct { Action string Keycode int MetaState int Repeat int } // TouchEvent represents a touch/mouse event type TouchEvent struct { Action string X float64 Y float64 Pressure float64 PointerID int } // ScrollEvent represents a scroll event type ScrollEvent struct { X float64 Y float64 HScroll float64 VScroll float64 } // EncodeKeyEvent encodes a key event for scrcpy protocol (like scrcpy-proxy) func EncodeKeyEvent(event KeyEvent) []byte { buf := make([]byte, 0, 16) // Action (1 byte) var actionCode byte if event.Action == "up" { actionCode = 1 } else { actionCode = 0 } buf = append(buf, actionCode) // Keycode (4 bytes) keyBytes := make([]byte, 4) binary.BigEndian.PutUint32(keyBytes, uint32(event.Keycode)) buf = append(buf, keyBytes...) // Repeat (4 bytes) repeatBytes := make([]byte, 4) binary.BigEndian.PutUint32(repeatBytes, uint32(event.Repeat)) buf = append(buf, repeatBytes...) // Meta state (4 bytes) metaBytes := make([]byte, 4) binary.BigEndian.PutUint32(metaBytes, uint32(event.MetaState)) buf = append(buf, metaBytes...) return buf } // EncodeTextEvent encodes a text event for scrcpy protocol // Based on scrcpy official implementation in control_msg.c // Note: This function returns only the message data (without message type) func EncodeTextEvent(text string) []byte { textBytes := []byte(text) textLen := len(textBytes) // Message format: [length][text] buf := make([]byte, 4+textLen) // Text length (4 bytes, big endian) binary.BigEndian.PutUint32(buf[0:4], uint32(textLen)) // Text content copy(buf[4:], textBytes) return buf } // EncodeTouchEvent encodes a touch event for scrcpy protocol (exactly like scrcpy-proxy) func EncodeTouchEvent(event TouchEvent, screenWidth, screenHeight int) []byte { buf := make([]byte, 0, 32) // Action (1 byte) var actionCode byte switch event.Action { case "down": actionCode = 0 // ACTION_DOWN case "up": actionCode = 1 // ACTION_UP case "move": actionCode = 2 // ACTION_MOVE } buf = append(buf, actionCode) // Pointer ID (8 bytes) - always 0 like scrcpy-proxy ptrBytes := make([]byte, 8) binary.BigEndian.PutUint64(ptrBytes, 0) buf = append(buf, ptrBytes...) // Position structure: // - x (4 bytes) - convert normalized coordinates to screen pixels // - y (4 bytes) - convert normalized coordinates to screen pixels // - screenWidth (2 bytes) // - screenHeight (2 bytes) posBytes := make([]byte, 12) screenX := uint32(event.X * float64(screenWidth)) screenY := uint32(event.Y * float64(screenHeight)) binary.BigEndian.PutUint32(posBytes[0:4], screenX) binary.BigEndian.PutUint32(posBytes[4:8], screenY) // Screen dimensions - use actual device screen size binary.BigEndian.PutUint16(posBytes[8:10], uint16(screenWidth)) binary.BigEndian.PutUint16(posBytes[10:12], uint16(screenHeight)) buf = append(buf, posBytes...) // Pressure (16-bit, 0xFFFF = 1.0) - always 1.0 like scrcpy-proxy pressureBytes := make([]byte, 2) binary.BigEndian.PutUint16(pressureBytes, 0xFFFF) // 1.0 pressure buf = append(buf, pressureBytes...) // Action button (32-bit) - always 1 like scrcpy-proxy actionButtonBytes := make([]byte, 4) binary.BigEndian.PutUint32(actionButtonBytes, 1) // Primary button buf = append(buf, actionButtonBytes...) // Buttons (32-bit) - always 1 like scrcpy-proxy buttonBytes := make([]byte, 4) binary.BigEndian.PutUint32(buttonBytes, 1) // Primary button pressed buf = append(buf, buttonBytes...) return buf } // EncodeScrollEvent encodes a scroll event for scrcpy protocol // Based on scrcpy official implementation in control_msg.c // Note: This function returns only the message data (without message type) func EncodeScrollEvent(event ScrollEvent, screenWidth, screenHeight int) []byte { buf := make([]byte, 20) // Position (exactly like scrcpy's write_position function) // Following the exact layout from app/src/control_msg.c:write_position() screenX := uint32(event.X * float64(screenWidth)) screenY := uint32(event.Y * float64(screenHeight)) // write_position writes to buf[0], which contains: // buf[0:4] = x coordinate (4 bytes, big endian) // buf[4:8] = y coordinate (4 bytes, big endian) // buf[8:10] = screen width (2 bytes, big endian) // buf[10:12] = screen height (2 bytes, big endian) binary.BigEndian.PutUint32(buf[0:4], screenX) binary.BigEndian.PutUint32(buf[4:8], screenY) binary.BigEndian.PutUint16(buf[8:10], uint16(screenWidth)) binary.BigEndian.PutUint16(buf[10:12], uint16(screenHeight)) // Scroll amounts - following scrcpy's normalization // Accept values in the range [-16, 16], normalize to [-1, 1] hScrollNorm := event.HScroll / 16.0 if hScrollNorm > 1.0 { hScrollNorm = 1.0 } else if hScrollNorm < -1.0 { hScrollNorm = -1.0 } vScrollNorm := event.VScroll / 16.0 if vScrollNorm > 1.0 { vScrollNorm = 1.0 } else if vScrollNorm < -1.0 { vScrollNorm = -1.0 } // Convert to 16-bit fixed point (exactly like scrcpy's sc_float_to_i16fp) // scrcpy uses: int32_t i = f * 0x1p15f; // 2^15 // Then clamps to [0x7fff, -0x8000] range hScrollInt32 := int32(hScrollNorm * 32768) // 2^15 vScrollInt32 := int32(vScrollNorm * 32768) // 2^15 // Clamp to scrcpy's range: [0x7fff, -0x8000] if hScrollInt32 >= 0x7fff { hScrollInt32 = 0x7fff } if vScrollInt32 >= 0x7fff { vScrollInt32 = 0x7fff } // Convert to int16 (this handles the two's complement conversion automatically) hScroll := int16(hScrollInt32) vScroll := int16(vScrollInt32) // Convert to uint16 exactly like scrcpy does: (uint16_t) hscroll // This preserves the two's complement representation hScrollUint16 := uint16(hScroll) vScrollUint16 := uint16(vScroll) binary.BigEndian.PutUint16(buf[12:14], hScrollUint16) binary.BigEndian.PutUint16(buf[14:16], vScrollUint16) // Buttons (none) binary.BigEndian.PutUint32(buf[16:20], 0) // Debug logging (uncomment for debugging scroll issues) // log.Printf("Scroll event encoded: x=%d, y=%d, hScroll=%d, vScroll=%d", screenX, screenY, hScroll, vScroll) return buf }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/babelcloud/gru-sandbox'

If you have feedback or need assistance with the MCP directory API, please join our Discord server