Fix: Web remote desktop double-click not working for macOS clients

This commit is contained in:
yuanyuanxiang
2026-05-01 11:08:12 +02:00
parent cfa9b581fc
commit ed4b9eeb25
5 changed files with 40 additions and 29 deletions

View File

@@ -156,6 +156,8 @@ void InputHandler::handleMouseButton(CGMouseButton button, bool down, int x, int
CGEventRef event = CGEventCreateMouseEvent(NULL, eventType, point, button); CGEventRef event = CGEventCreateMouseEvent(NULL, eventType, point, button);
if (event) { if (event) {
// clickState=1 for all single clicks
CGEventSetIntegerValueField(event, kCGMouseEventClickState, 1);
CGEventPost(kCGHIDEventTap, event); CGEventPost(kCGHIDEventTap, event);
CFRelease(event); CFRelease(event);
} }
@@ -163,6 +165,13 @@ void InputHandler::handleMouseButton(CGMouseButton button, bool down, int x, int
void InputHandler::handleMouseDoubleClick(CGMouseButton button, int x, int y) void InputHandler::handleMouseDoubleClick(CGMouseButton button, int x, int y)
{ {
// WM_LBUTTONDBLCLK represents the second click of a double-click.
// The first click was already sent via WM_LBUTTONDOWN/WM_LBUTTONUP.
//
// We send complete down(2) + up(2) here because:
// - Web client: dblclick fires AFTER mouseup, no subsequent WM_LBUTTONUP
// - MFC client: WM_LBUTTONUP follows, but extra up(1) is harmless
CGPoint point = CGPointMake(x, y); CGPoint point = CGPointMake(x, y);
m_lastMousePos = point; m_lastMousePos = point;
@@ -184,36 +193,24 @@ void InputHandler::handleMouseDoubleClick(CGMouseButton button, int x, int y)
break; break;
} }
// First click (clickState=1) // Send second click: down(2) + up(2)
CGEventRef down1 = CGEventCreateMouseEvent(NULL, downType, point, button); CGEventRef down = CGEventCreateMouseEvent(NULL, downType, point, button);
CGEventRef up1 = CGEventCreateMouseEvent(NULL, upType, point, button); CGEventRef up = CGEventCreateMouseEvent(NULL, upType, point, button);
if (down1 && up1) { if (down) {
CGEventSetIntegerValueField(down1, kCGMouseEventClickState, 1); CGEventSetIntegerValueField(down, kCGMouseEventClickState, 2);
CGEventSetIntegerValueField(up1, kCGMouseEventClickState, 1); CGEventPost(kCGHIDEventTap, down);
CGEventPost(kCGHIDEventTap, down1); CFRelease(down);
CGEventPost(kCGHIDEventTap, up1);
} }
if (down1) CFRelease(down1); if (up) {
if (up1) CFRelease(up1); CGEventSetIntegerValueField(up, kCGMouseEventClickState, 2);
CGEventPost(kCGHIDEventTap, up);
// Brief delay between clicks (50ms) CFRelease(up);
usleep(50000);
// Second click (clickState=2)
CGEventRef down2 = CGEventCreateMouseEvent(NULL, downType, point, button);
CGEventRef up2 = CGEventCreateMouseEvent(NULL, upType, point, button);
if (down2 && up2) {
CGEventSetIntegerValueField(down2, kCGMouseEventClickState, 2);
CGEventSetIntegerValueField(up2, kCGMouseEventClickState, 2);
CGEventPost(kCGHIDEventTap, down2);
CGEventPost(kCGHIDEventTap, up2);
} }
if (down2) CFRelease(down2); // Note: For MFC client, an extra WM_LBUTTONUP will follow (sending up(1)),
if (up2) CFRelease(up2); // but this is harmless since mouse is already up.
} }
void InputHandler::handleMouseWheel(int delta) void InputHandler::handleMouseWheel(int delta)

View File

@@ -25,7 +25,7 @@ ScreenHandler::ScreenHandler(IOCPClient* client)
, m_algorithm(ALGORITHM_H264) , m_algorithm(ALGORITHM_H264)
, m_maxFPS(15) , m_maxFPS(15)
, m_qualityLevel(QUALITY_GOOD) // Use fixed QUALITY_GOOD (H264) for web compatibility , m_qualityLevel(QUALITY_GOOD) // Use fixed QUALITY_GOOD (H264) for web compatibility
, m_h264Bitrate(2000000) // 2 Mbps default , m_h264Bitrate(3000000) // 3 Mbps (matches Windows QUALITY_GOOD)
{ {
memset(&m_bmpHeader, 0, sizeof(m_bmpHeader)); memset(&m_bmpHeader, 0, sizeof(m_bmpHeader));

View File

@@ -272,8 +272,8 @@ static void fillLoginInfo(LOGIN_INFOR& info)
// Reserved fields (pipe-separated, must match Windows client order) // Reserved fields (pipe-separated, must match Windows client order)
// Order: Type|Bits|Cores|Memory|Path|?|InstallTime|?|ProgBits|Auth|Location|IP|Version|User|IsAdmin|Resolution|ClientID // Order: Type|Bits|Cores|Memory|Path|?|InstallTime|?|ProgBits|Auth|Location|IP|Version|User|IsAdmin|Resolution|ClientID
// 1. Client type // 1. Client type (use GetClientType for consistency with Windows client)
info.AddReserved("macOS"); info.AddReserved(GetClientType(CLIENT_TYPE_MACOS));
// 2. System bits (OS bits, always 64 on modern macOS) // 2. System bits (OS bits, always 64 on modern macOS)
info.AddReserved(64); info.AddReserved(64);

View File

@@ -2696,7 +2696,12 @@ inline std::string GetWebPageHTML() {
sendMouse('up', pos.x, pos.y, e.button); sendMouse('up', pos.x, pos.y, e.button);
}); });
// Note: dblclick is handled by mousedown-mouseup sequence, no separate handler needed // dblclick handler - server will forward only to macOS clients
canvas.addEventListener('dblclick', function(e) {
e.preventDefault();
const pos = getMousePos(e);
sendMouse('dblclick', pos.x, pos.y, e.button);
});
canvas.addEventListener('mousemove', function(e) { canvas.addEventListener('mousemove', function(e) {
const now = Date.now(); const now = Date.now();

View File

@@ -770,6 +770,15 @@ void CWebService::HandleMouse(void* ws_ptr, const std::string& msg) {
short wheelDelta = (short)(delta > 0 ? -120 : (delta < 0 ? 120 : 0)); short wheelDelta = (short)(delta > 0 ? -120 : (delta < 0 ? 120 : 0));
msg64.wParam = MAKEWPARAM(0, wheelDelta); msg64.wParam = MAKEWPARAM(0, wheelDelta);
} else if (type == "dblclick") { } else if (type == "dblclick") {
// dblclick is only needed for macOS clients
// Windows detects double-click from rapid mousedown/mouseup sequence
context* mainCtx = m_pParentDlg->FindHost(device_id);
if (!mainCtx) return;
CString clientType = mainCtx->GetAdditionalData(RES_CLIENT_TYPE);
// Check for both "MAC" (new) and "macOS" (legacy) for compatibility
if (clientType != GetClientType(CLIENT_TYPE_MACOS) && clientType != "macOS") {
return; // Skip dblclick for non-macOS clients
}
if (button == 0) { if (button == 0) {
msg64.message = WM_LBUTTONDBLCLK; msg64.message = WM_LBUTTONDBLCLK;
msg64.wParam = MK_LBUTTON; msg64.wParam = MK_LBUTTON;