mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-26 14:29:08 +01:00
Add ExtendedMouseButtons for back/forward mouse buttons
This commit is contained in:
@@ -272,15 +272,15 @@ func ensureEventSource() uintptr {
|
|||||||
|
|
||||||
// MacInputInjector injects keyboard and mouse events via Core Graphics.
|
// MacInputInjector injects keyboard and mouse events via Core Graphics.
|
||||||
type MacInputInjector struct {
|
type MacInputInjector struct {
|
||||||
lastButtons uint8
|
lastButtons uint16
|
||||||
pbcopyPath string
|
pbcopyPath string
|
||||||
pbpastePath string
|
pbpastePath string
|
||||||
// clickCount[i] / clickAt[i] track the multi-click sequence for
|
// clickCount[i] / clickAt[i] track the multi-click sequence for
|
||||||
// button i (0=left, 1=right, 2=middle). macOS apps reconstruct
|
// button i (0=left, 1=right, 2=middle). macOS apps reconstruct
|
||||||
// double/triple click semantics from the kCGMouseEventClickState
|
// double/triple click semantics from the kCGMouseEventClickState
|
||||||
// field on each posted event, not from event timing.
|
// field on each posted event, not from event timing.
|
||||||
clickCount [3]int64
|
clickCount [5]int64
|
||||||
clickAt [3]time.Time
|
clickAt [5]time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMacInputInjector creates a macOS input injector.
|
// NewMacInputInjector creates a macOS input injector.
|
||||||
@@ -406,7 +406,7 @@ func (m *MacInputInjector) postMacKey(src uintptr, keycode uint16, down bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// InjectPointer simulates mouse movement and button events.
|
// InjectPointer simulates mouse movement and button events.
|
||||||
func (m *MacInputInjector) InjectPointer(buttonMask uint8, px, py, serverW, serverH int) {
|
func (m *MacInputInjector) InjectPointer(buttonMask uint16, px, py, serverW, serverH int) {
|
||||||
wakeDisplay()
|
wakeDisplay()
|
||||||
if serverW == 0 || serverH == 0 {
|
if serverW == 0 || serverH == 0 {
|
||||||
return
|
return
|
||||||
@@ -438,7 +438,7 @@ func scalePxToLogical(px, py, serverW, serverH int) (float64, float64) {
|
|||||||
float64(py) * float64(logicalH) / float64(serverH)
|
float64(py) * float64(logicalH) / float64(serverH)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MacInputInjector) dispatchPointer(src uintptr, buttonMask uint8, x, y float64) {
|
func (m *MacInputInjector) dispatchPointer(src uintptr, buttonMask uint16, x, y float64) {
|
||||||
leftDown := buttonMask&0x01 != 0
|
leftDown := buttonMask&0x01 != 0
|
||||||
rightDown := buttonMask&0x04 != 0
|
rightDown := buttonMask&0x04 != 0
|
||||||
middleDown := buttonMask&0x02 != 0
|
middleDown := buttonMask&0x02 != 0
|
||||||
@@ -462,8 +462,8 @@ func (m *MacInputInjector) postMoveOrDrag(src uintptr, leftDown, rightDown bool,
|
|||||||
// postButtonTransitions emits the up/down events for each button whose
|
// postButtonTransitions emits the up/down events for each button whose
|
||||||
// state changed against m.lastButtons, computing the click count so
|
// state changed against m.lastButtons, computing the click count so
|
||||||
// macOS recognises double / triple clicks.
|
// macOS recognises double / triple clicks.
|
||||||
func (m *MacInputInjector) postButtonTransitions(src uintptr, buttonMask uint8, x, y float64) {
|
func (m *MacInputInjector) postButtonTransitions(src uintptr, buttonMask uint16, x, y float64) {
|
||||||
emit := func(curBit, prevBit uint8, down, up int32, button int32, idx int) {
|
emit := func(curBit, prevBit uint16, down, up int32, button int32, idx int) {
|
||||||
cur := buttonMask&curBit != 0
|
cur := buttonMask&curBit != 0
|
||||||
prev := m.lastButtons&prevBit != 0
|
prev := m.lastButtons&prevBit != 0
|
||||||
if cur && !prev {
|
if cur && !prev {
|
||||||
@@ -486,9 +486,14 @@ func (m *MacInputInjector) postButtonTransitions(src uintptr, buttonMask uint8,
|
|||||||
emit(0x01, 0x01, kCGEventLeftMouseDown, kCGEventLeftMouseUp, kCGMouseButtonLeft, 0)
|
emit(0x01, 0x01, kCGEventLeftMouseDown, kCGEventLeftMouseUp, kCGMouseButtonLeft, 0)
|
||||||
emit(0x04, 0x04, kCGEventRightMouseDown, kCGEventRightMouseUp, kCGMouseButtonRight, 1)
|
emit(0x04, 0x04, kCGEventRightMouseDown, kCGEventRightMouseUp, kCGMouseButtonRight, 1)
|
||||||
emit(0x02, 0x02, kCGEventOtherMouseDown, kCGEventOtherMouseUp, kCGMouseButtonCenter, 2)
|
emit(0x02, 0x02, kCGEventOtherMouseDown, kCGEventOtherMouseUp, kCGMouseButtonCenter, 2)
|
||||||
|
// CG mouse-button numbers 3 (back) and 4 (forward) are emitted as
|
||||||
|
// "other" events; macOS apps that swallow Browser nav (Finder, web
|
||||||
|
// views) react to these directly.
|
||||||
|
emit(1<<7, 1<<7, kCGEventOtherMouseDown, kCGEventOtherMouseUp, 3, 3)
|
||||||
|
emit(1<<8, 1<<8, kCGEventOtherMouseDown, kCGEventOtherMouseUp, 4, 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MacInputInjector) postScrollWheel(src uintptr, buttonMask uint8) {
|
func (m *MacInputInjector) postScrollWheel(src uintptr, buttonMask uint16) {
|
||||||
if buttonMask&0x08 != 0 {
|
if buttonMask&0x08 != 0 {
|
||||||
m.postScroll(src, scrollPixelsPerWheelTick)
|
m.postScroll(src, scrollPixelsPerWheelTick)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ const (
|
|||||||
btnLeft = 0x110
|
btnLeft = 0x110
|
||||||
btnRight = 0x111
|
btnRight = 0x111
|
||||||
btnMiddle = 0x112
|
btnMiddle = 0x112
|
||||||
|
btnSide = 0x113 // mouse-back (X1)
|
||||||
|
btnExtra = 0x114 // mouse-forward (X2)
|
||||||
)
|
)
|
||||||
|
|
||||||
// inputEvent matches struct input_event for x86_64 (timeval is 16 bytes).
|
// inputEvent matches struct input_event for x86_64 (timeval is 16 bytes).
|
||||||
@@ -63,7 +65,7 @@ type UInputInjector struct {
|
|||||||
fd int
|
fd int
|
||||||
closeOnce sync.Once
|
closeOnce sync.Once
|
||||||
keysymToKey map[uint32]uint16
|
keysymToKey map[uint32]uint16
|
||||||
prevButtons uint8
|
prevButtons uint16
|
||||||
screenW int
|
screenW int
|
||||||
screenH int
|
screenH int
|
||||||
}
|
}
|
||||||
@@ -233,7 +235,7 @@ func (u *UInputInjector) emitKeyCode(code uint16, down bool) {
|
|||||||
|
|
||||||
// InjectPointer moves the absolute pointer and presses/releases buttons
|
// InjectPointer moves the absolute pointer and presses/releases buttons
|
||||||
// based on the RFB button mask delta against the previous mask.
|
// based on the RFB button mask delta against the previous mask.
|
||||||
func (u *UInputInjector) InjectPointer(buttonMask uint8, x, y, serverW, serverH int) {
|
func (u *UInputInjector) InjectPointer(buttonMask uint16, x, y, serverW, serverH int) {
|
||||||
u.mu.Lock()
|
u.mu.Lock()
|
||||||
defer u.mu.Unlock()
|
defer u.mu.Unlock()
|
||||||
if serverW <= 1 || serverH <= 1 {
|
if serverW <= 1 || serverH <= 1 {
|
||||||
@@ -245,13 +247,15 @@ func (u *UInputInjector) InjectPointer(buttonMask uint8, x, y, serverW, serverH
|
|||||||
_ = u.emit(evAbs, absY, absYVal)
|
_ = u.emit(evAbs, absY, absYVal)
|
||||||
|
|
||||||
type btnMap struct {
|
type btnMap struct {
|
||||||
bit uint8
|
bit uint16
|
||||||
key uint16
|
key uint16
|
||||||
}
|
}
|
||||||
for _, b := range []btnMap{
|
for _, b := range []btnMap{
|
||||||
{0x01, btnLeft},
|
{0x01, btnLeft},
|
||||||
{0x02, btnMiddle},
|
{0x02, btnMiddle},
|
||||||
{0x04, btnRight},
|
{0x04, btnRight},
|
||||||
|
{1 << 7, btnSide},
|
||||||
|
{1 << 8, btnExtra},
|
||||||
} {
|
} {
|
||||||
pressed := buttonMask&b.bit != 0
|
pressed := buttonMask&b.bit != 0
|
||||||
was := u.prevButtons&b.bit != 0
|
was := u.prevButtons&b.bit != 0
|
||||||
|
|||||||
@@ -30,9 +30,16 @@ const (
|
|||||||
mouseeventfRightUp = 0x0010
|
mouseeventfRightUp = 0x0010
|
||||||
mouseeventfMiddleDown = 0x0020
|
mouseeventfMiddleDown = 0x0020
|
||||||
mouseeventfMiddleUp = 0x0040
|
mouseeventfMiddleUp = 0x0040
|
||||||
|
mouseeventfXDown = 0x0080
|
||||||
|
mouseeventfXUp = 0x0100
|
||||||
mouseeventfWheel = 0x0800
|
mouseeventfWheel = 0x0800
|
||||||
mouseeventfAbsolute = 0x8000
|
mouseeventfAbsolute = 0x8000
|
||||||
|
|
||||||
|
// X-button identifiers carried in the dwData field of MOUSEEVENTF_X*
|
||||||
|
// events. XBUTTON1 is mouse-back, XBUTTON2 is mouse-forward.
|
||||||
|
xButton1 = 0x0001
|
||||||
|
xButton2 = 0x0002
|
||||||
|
|
||||||
wheelDelta = 120
|
wheelDelta = 120
|
||||||
|
|
||||||
keyeventfExtendedKey = 0x0001
|
keyeventfExtendedKey = 0x0001
|
||||||
@@ -112,7 +119,7 @@ type inputCmd struct {
|
|||||||
keysym uint32
|
keysym uint32
|
||||||
scancode uint32
|
scancode uint32
|
||||||
down bool
|
down bool
|
||||||
buttonMask uint8
|
buttonMask uint16
|
||||||
x, y int
|
x, y int
|
||||||
serverW int
|
serverW int
|
||||||
serverH int
|
serverH int
|
||||||
@@ -127,7 +134,7 @@ type WindowsInputInjector struct {
|
|||||||
ch chan inputCmd
|
ch chan inputCmd
|
||||||
closed chan struct{}
|
closed chan struct{}
|
||||||
closeOnce sync.Once
|
closeOnce sync.Once
|
||||||
prevButtonMask uint8
|
prevButtonMask uint16
|
||||||
ctrlDown bool
|
ctrlDown bool
|
||||||
altDown bool
|
altDown bool
|
||||||
}
|
}
|
||||||
@@ -220,7 +227,7 @@ func (w *WindowsInputInjector) InjectKeyScancode(scancode uint32, keysym uint32,
|
|||||||
// thread. Pointer events coalesce: when the channel is full (slow desktop
|
// thread. Pointer events coalesce: when the channel is full (slow desktop
|
||||||
// switch, hung SendInput), drop the new sample so the read loop never
|
// switch, hung SendInput), drop the new sample so the read loop never
|
||||||
// blocks. The next mouse event carries fresher position anyway.
|
// blocks. The next mouse event carries fresher position anyway.
|
||||||
func (w *WindowsInputInjector) InjectPointer(buttonMask uint8, x, y, serverW, serverH int) {
|
func (w *WindowsInputInjector) InjectPointer(buttonMask uint16, x, y, serverW, serverH int) {
|
||||||
w.tryEnqueue(inputCmd{buttonMask: buttonMask, x: x, y: y, serverW: serverW, serverH: serverH})
|
w.tryEnqueue(inputCmd{buttonMask: buttonMask, x: x, y: y, serverW: serverW, serverH: serverH})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,7 +310,7 @@ func signalSAS() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WindowsInputInjector) doInjectPointer(buttonMask uint8, x, y, serverW, serverH int) {
|
func (w *WindowsInputInjector) doInjectPointer(buttonMask uint16, x, y, serverW, serverH int) {
|
||||||
if serverW == 0 || serverH == 0 {
|
if serverW == 0 || serverH == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -317,7 +324,7 @@ func (w *WindowsInputInjector) doInjectPointer(buttonMask uint8, x, y, serverW,
|
|||||||
w.prevButtonMask = buttonMask
|
w.prevButtonMask = buttonMask
|
||||||
|
|
||||||
type btnMap struct {
|
type btnMap struct {
|
||||||
bit uint8
|
bit uint16
|
||||||
down uint32
|
down uint32
|
||||||
up uint32
|
up uint32
|
||||||
}
|
}
|
||||||
@@ -346,6 +353,26 @@ func (w *WindowsInputInjector) doInjectPointer(buttonMask uint8, x, y, serverW,
|
|||||||
if changed&0x10 != 0 && buttonMask&0x10 != 0 {
|
if changed&0x10 != 0 && buttonMask&0x10 != 0 {
|
||||||
sendMouseInput(mouseeventfWheel|mouseeventfAbsolute, absX, absY, negWheelDelta)
|
sendMouseInput(mouseeventfWheel|mouseeventfAbsolute, absX, absY, negWheelDelta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XBUTTON1/back at bit 7, XBUTTON2/forward at bit 8. SendInput
|
||||||
|
// MOUSEEVENTF_X{DOWN,UP} carries the X button number in dwData.
|
||||||
|
xbuttons := [...]struct {
|
||||||
|
bit uint16
|
||||||
|
data uint32
|
||||||
|
}{
|
||||||
|
{1 << 7, xButton1},
|
||||||
|
{1 << 8, xButton2},
|
||||||
|
}
|
||||||
|
for _, b := range xbuttons {
|
||||||
|
if changed&b.bit == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var flags uint32 = mouseeventfXUp
|
||||||
|
if buttonMask&b.bit != 0 {
|
||||||
|
flags = mouseeventfXDown
|
||||||
|
}
|
||||||
|
sendMouseInput(flags|mouseeventfAbsolute, absX, absY, b.data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// keysym2VK converts an X11 keysym to a Windows virtual key code.
|
// keysym2VK converts an X11 keysym to a Windows virtual key code.
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ type X11InputInjector struct {
|
|||||||
screen *xproto.ScreenInfo
|
screen *xproto.ScreenInfo
|
||||||
display string
|
display string
|
||||||
keysymMap map[uint32]byte
|
keysymMap map[uint32]byte
|
||||||
lastButtons uint8
|
lastButtons uint16
|
||||||
clipboardTool string
|
clipboardTool string
|
||||||
clipboardToolName string
|
clipboardToolName string
|
||||||
}
|
}
|
||||||
@@ -110,7 +110,7 @@ func (x *X11InputInjector) fakeKeyEvent(keycode byte, down bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// InjectPointer simulates mouse movement and button events.
|
// InjectPointer simulates mouse movement and button events.
|
||||||
func (x *X11InputInjector) InjectPointer(buttonMask uint8, px, py, serverW, serverH int) {
|
func (x *X11InputInjector) InjectPointer(buttonMask uint16, px, py, serverW, serverH int) {
|
||||||
if serverW == 0 || serverH == 0 {
|
if serverW == 0 || serverH == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -128,15 +128,19 @@ func (x *X11InputInjector) InjectPointer(buttonMask uint8, px, py, serverW, serv
|
|||||||
// bit3=scrollUp, bit4=scrollDown. X11 buttons: 1=left, 2=middle, 3=right,
|
// bit3=scrollUp, bit4=scrollDown. X11 buttons: 1=left, 2=middle, 3=right,
|
||||||
// 4=scrollUp, 5=scrollDown.
|
// 4=scrollUp, 5=scrollDown.
|
||||||
type btnMap struct {
|
type btnMap struct {
|
||||||
rfbBit uint8
|
rfbBit uint16
|
||||||
x11Btn byte
|
x11Btn byte
|
||||||
}
|
}
|
||||||
|
// X11 button numbers: 1=left, 2=middle, 3=right, 4/5=scroll up/down,
|
||||||
|
// 6/7=scroll left/right (skipped), 8=back, 9=forward.
|
||||||
buttons := [...]btnMap{
|
buttons := [...]btnMap{
|
||||||
{0x01, 1}, // left
|
{0x01, 1},
|
||||||
{0x02, 2}, // middle
|
{0x02, 2},
|
||||||
{0x04, 3}, // right
|
{0x04, 3},
|
||||||
{0x08, 4}, // scroll up
|
{0x08, 4},
|
||||||
{0x10, 5}, // scroll down
|
{0x10, 5},
|
||||||
|
{1 << 7, 8},
|
||||||
|
{1 << 8, 9},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, b := range buttons {
|
for _, b := range buttons {
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ const (
|
|||||||
pseudoEncQEMUExtendedKeyEvent = -258
|
pseudoEncQEMUExtendedKeyEvent = -258
|
||||||
pseudoEncDesktopName = -307
|
pseudoEncDesktopName = -307
|
||||||
pseudoEncExtendedDesktopSize = -308
|
pseudoEncExtendedDesktopSize = -308
|
||||||
|
pseudoEncExtendedMouseButtons = -316
|
||||||
|
|
||||||
// Quality/Compression level pseudo-encodings. The client picks one
|
// Quality/Compression level pseudo-encodings. The client picks one
|
||||||
// value from each range to tune JPEG quality and zlib effort. 0 is
|
// value from each range to tune JPEG quality and zlib effort. 0 is
|
||||||
|
|||||||
@@ -105,7 +105,10 @@ type InputInjector interface {
|
|||||||
// for the given code; that's strictly no worse than the legacy path.
|
// for the given code; that's strictly no worse than the legacy path.
|
||||||
InjectKeyScancode(scancode uint32, keysym uint32, down bool)
|
InjectKeyScancode(scancode uint32, keysym uint32, down bool)
|
||||||
// InjectPointer simulates mouse movement and button state.
|
// InjectPointer simulates mouse movement and button state.
|
||||||
InjectPointer(buttonMask uint8, x, y, serverW, serverH int)
|
// buttonMask is the RFB ExtendedMouseButtons mask: bits 0-6 follow
|
||||||
|
// the standard PointerEvent layout (left/middle/right/wheel),
|
||||||
|
// bit 7 is mouse-back (X1), bit 8 is mouse-forward (X2).
|
||||||
|
InjectPointer(buttonMask uint16, x, y, serverW, serverH int)
|
||||||
// SetClipboard sets the system clipboard to the given text.
|
// SetClipboard sets the system clipboard to the given text.
|
||||||
SetClipboard(text string)
|
SetClipboard(text string)
|
||||||
// GetClipboard returns the current system clipboard text.
|
// GetClipboard returns the current system clipboard text.
|
||||||
|
|||||||
@@ -79,7 +79,18 @@ type session struct {
|
|||||||
clientSupportsQEMUKey bool
|
clientSupportsQEMUKey bool
|
||||||
clientSupportsExtClipboard bool
|
clientSupportsExtClipboard bool
|
||||||
clientSupportsCursor bool
|
clientSupportsCursor bool
|
||||||
extClipCapsSent bool
|
// clientSupportsExtMouseButtons is set when the client advertises the
|
||||||
|
// ExtendedMouseButtons pseudo-encoding (-316). Once the server emits
|
||||||
|
// the ack rect, the client switches its pointer events to the 6-byte
|
||||||
|
// extended format that carries back/forward buttons in a second mask
|
||||||
|
// byte. Without this gate the byte after the type field would still
|
||||||
|
// be a standard 7-bit mask and our parser must not look further.
|
||||||
|
clientSupportsExtMouseButtons bool
|
||||||
|
// extMouseAckSent is set once we've emitted the pseudo-rect ack that
|
||||||
|
// flips the client into extended-pointer mode. Sticky for the
|
||||||
|
// session because the client only needs to see it once.
|
||||||
|
extMouseAckSent bool
|
||||||
|
extClipCapsSent bool
|
||||||
// lastCursorSerial is the serial of the cursor sprite last emitted.
|
// lastCursorSerial is the serial of the cursor sprite last emitted.
|
||||||
// The encoder re-queries the source each cycle and only emits when
|
// The encoder re-queries the source each cycle and only emits when
|
||||||
// the serial changes.
|
// the serial changes.
|
||||||
@@ -359,6 +370,10 @@ func (s *session) handleSetEncodings() error {
|
|||||||
if sendExtClipCaps {
|
if sendExtClipCaps {
|
||||||
s.extClipCapsSent = true
|
s.extClipCapsSent = true
|
||||||
}
|
}
|
||||||
|
sendExtMouseAck := s.clientSupportsExtMouseButtons && !s.extMouseAckSent
|
||||||
|
if sendExtMouseAck {
|
||||||
|
s.extMouseAckSent = true
|
||||||
|
}
|
||||||
s.encMu.Unlock()
|
s.encMu.Unlock()
|
||||||
if len(encs) > 0 {
|
if len(encs) > 0 {
|
||||||
s.log.Debugf("client supports encodings: %s", strings.Join(encs, ", "))
|
s.log.Debugf("client supports encodings: %s", strings.Join(encs, ", "))
|
||||||
@@ -368,6 +383,11 @@ func (s *session) handleSetEncodings() error {
|
|||||||
return fmt.Errorf("send ext clipboard caps: %w", err)
|
return fmt.Errorf("send ext clipboard caps: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if sendExtMouseAck {
|
||||||
|
if err := s.sendExtMouseAck(); err != nil {
|
||||||
|
return fmt.Errorf("send ext mouse ack: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,6 +407,7 @@ func (s *session) resetEncodingCaps() {
|
|||||||
s.clientSupportsQEMUKey = false
|
s.clientSupportsQEMUKey = false
|
||||||
s.clientSupportsExtClipboard = false
|
s.clientSupportsExtClipboard = false
|
||||||
s.clientSupportsCursor = false
|
s.clientSupportsCursor = false
|
||||||
|
s.clientSupportsExtMouseButtons = false
|
||||||
s.cursorSourceFailed = false
|
s.cursorSourceFailed = false
|
||||||
s.clientJPEGQuality = -1
|
s.clientJPEGQuality = -1
|
||||||
s.clientZlibLevel = -1
|
s.clientZlibLevel = -1
|
||||||
@@ -427,6 +448,9 @@ func (s *session) applyEncoding(enc int32) string {
|
|||||||
}
|
}
|
||||||
s.clientSupportsCursor = true
|
s.clientSupportsCursor = true
|
||||||
return "cursor"
|
return "cursor"
|
||||||
|
case pseudoEncExtendedMouseButtons:
|
||||||
|
s.clientSupportsExtMouseButtons = true
|
||||||
|
return "ext-mouse-buttons"
|
||||||
case encTight:
|
case encTight:
|
||||||
s.useTight = true
|
s.useTight = true
|
||||||
return "tight"
|
return "tight"
|
||||||
@@ -541,14 +565,28 @@ func (s *session) handlePointerEvent() error {
|
|||||||
if _, err := io.ReadFull(s.conn, data[:]); err != nil {
|
if _, err := io.ReadFull(s.conn, data[:]); err != nil {
|
||||||
return fmt.Errorf("read PointerEvent: %w", err)
|
return fmt.Errorf("read PointerEvent: %w", err)
|
||||||
}
|
}
|
||||||
buttonMask := data[0]
|
mask := uint16(data[0])
|
||||||
x := int(binary.BigEndian.Uint16(data[1:3]))
|
x := int(binary.BigEndian.Uint16(data[1:3]))
|
||||||
y := int(binary.BigEndian.Uint16(data[3:5]))
|
y := int(binary.BigEndian.Uint16(data[3:5]))
|
||||||
|
|
||||||
|
s.encMu.RLock()
|
||||||
|
extended := s.clientSupportsExtMouseButtons && s.extMouseAckSent
|
||||||
|
s.encMu.RUnlock()
|
||||||
|
if extended && mask&0x80 != 0 {
|
||||||
|
var hi [1]byte
|
||||||
|
if _, err := io.ReadFull(s.conn, hi[:]); err != nil {
|
||||||
|
return fmt.Errorf("read ExtendedPointerEvent tail: %w", err)
|
||||||
|
}
|
||||||
|
// Strip the marker bit; bits 0..6 are the low part of the mask,
|
||||||
|
// hi byte holds bits 7..14 (back at bit 7, forward at bit 8).
|
||||||
|
mask = (mask & 0x7f) | uint16(hi[0])<<7
|
||||||
|
}
|
||||||
|
|
||||||
s.pointerMu.Lock()
|
s.pointerMu.Lock()
|
||||||
s.lastPointerX = x
|
s.lastPointerX = x
|
||||||
s.lastPointerY = y
|
s.lastPointerY = y
|
||||||
s.pointerMu.Unlock()
|
s.pointerMu.Unlock()
|
||||||
s.injector.InjectPointer(buttonMask, x, y, s.serverW, s.serverH)
|
s.injector.InjectPointer(mask, x, y, s.serverW, s.serverH)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -269,6 +269,28 @@ func (s *session) sendDesktopSize(w, h int) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sendExtMouseAck emits the pseudo-rect that flips the client into
|
||||||
|
// ExtendedMouseButtons mode, where mouse-back and mouse-forward are
|
||||||
|
// carried in a second mask byte. The rect has zero geometry and no
|
||||||
|
// body; the encoding number alone is the signal.
|
||||||
|
func (s *session) sendExtMouseAck() error {
|
||||||
|
header := make([]byte, 4)
|
||||||
|
header[0] = serverFramebufferUpdate
|
||||||
|
binary.BigEndian.PutUint16(header[2:4], 1)
|
||||||
|
|
||||||
|
rect := make([]byte, 12)
|
||||||
|
enc := int32(pseudoEncExtendedMouseButtons)
|
||||||
|
binary.BigEndian.PutUint32(rect[8:12], uint32(enc))
|
||||||
|
|
||||||
|
s.writeMu.Lock()
|
||||||
|
defer s.writeMu.Unlock()
|
||||||
|
if _, err := s.conn.Write(header); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err := s.conn.Write(rect)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// refreshCopyRectIndex does a full hash sweep of the just-swapped prevFrame.
|
// refreshCopyRectIndex does a full hash sweep of the just-swapped prevFrame.
|
||||||
// Used after full-frame sends, where we don't have a per-tile dirty list to
|
// Used after full-frame sends, where we don't have a per-tile dirty list to
|
||||||
// drive an incremental update.
|
// drive an incremental update.
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func (s *StubInputInjector) InjectKeyScancode(_ uint32, _ uint32, _ bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// InjectPointer is a no-op on unsupported platforms.
|
// InjectPointer is a no-op on unsupported platforms.
|
||||||
func (s *StubInputInjector) InjectPointer(_ uint8, _, _, _, _ int) {
|
func (s *StubInputInjector) InjectPointer(_ uint16, _, _, _, _ int) {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user