diff --git a/server/2015Remote/ScreenSpyDlg.cpp b/server/2015Remote/ScreenSpyDlg.cpp index e5c7b27..5dd12e8 100644 --- a/server/2015Remote/ScreenSpyDlg.cpp +++ b/server/2015Remote/ScreenSpyDlg.cpp @@ -1254,6 +1254,10 @@ VOID CScreenSpyDlg::DrawNextScreenDiff(bool keyFrame) m_bCursorIndex = m_ContextObject->InDeCompressedBuffer.GetBuffer(2+sizeof(POINT))[0]; if (bOldCursorIndex != m_bCursorIndex) { bChange = TRUE; + // 通知 Web 客户端光标变化 + if (WebService().IsRunning()) { + WebService().BroadcastCursor(m_ClientID, m_bCursorIndex); + } if (m_bIsCtrl && !m_bIsTraceCursor) {//替换指定窗口所属类的WNDCLASSEX结构 HCURSOR cursor; if (m_bCursorIndex == 254) { // -2: 使用自定义光标 @@ -1429,6 +1433,10 @@ VOID CScreenSpyDlg::DrawScrollFrame() m_bCursorIndex = m_ContextObject->InDeCompressedBuffer.GetBuffer(2 + sizeof(POINT))[0]; if (bOldCursorIndex != m_bCursorIndex) { bChange = TRUE; + // 通知 Web 客户端光标变化 + if (WebService().IsRunning()) { + WebService().BroadcastCursor(m_ClientID, m_bCursorIndex); + } } // 读取滚动参数 diff --git a/server/2015Remote/WebPage.h b/server/2015Remote/WebPage.h index 014760a..75b37a1 100644 --- a/server/2015Remote/WebPage.h +++ b/server/2015Remote/WebPage.h @@ -1232,6 +1232,18 @@ inline std::string GetWebPageHTML() { updateScreenStatus('connected'); initDecoder(msg.width, msg.height); break; + case 'cursor': + // Update remote cursor style (only for desktop in control mode) + currentCursorIndex = msg.index; + if (controlEnabled && !isTouchDevice) { + const canvas = document.getElementById('screen-canvas'); + // 254=custom cursor (not supported in web), 255=unsupported -> default + const cssCursor = (msg.index >= 0 && msg.index < cursorMap.length) + ? cursorMap[msg.index] + : 'default'; + canvas.style.cursor = cssCursor; + } + break; case 'device_offline': // Only handle if this is the device we're currently viewing if (!token) break; @@ -1748,6 +1760,28 @@ inline std::string GetWebPageHTML() { // Control mode state (mouse/keyboard control) let controlEnabled = false; + // Remote cursor mapping (Windows cursor index -> CSS cursor) + // Index matches CursorInfo.h: IDC_APPSTARTING(0) to IDC_WAIT(15), 254=custom, 255=unsupported + const cursorMap = [ + 'progress', // 0: IDC_APPSTARTING + 'default', // 1: IDC_ARROW + 'crosshair', // 2: IDC_CROSS + 'pointer', // 3: IDC_HAND + 'help', // 4: IDC_HELP + 'text', // 5: IDC_IBEAM + 'default', // 6: IDC_ICON (no direct CSS equivalent) + 'not-allowed', // 7: IDC_NO + 'default', // 8: IDC_SIZE (deprecated, use default) + 'move', // 9: IDC_SIZEALL + 'nesw-resize', // 10: IDC_SIZENESW + 'ns-resize', // 11: IDC_SIZENS + 'nwse-resize', // 12: IDC_SIZENWSE + 'ew-resize', // 13: IDC_SIZEWE + 'default', // 14: IDC_UPARROW (no direct CSS equivalent) + 'wait' // 15: IDC_WAIT + ]; + let currentCursorIndex = 1; // Default: arrow + // Floating toolbar state let toolbarVisible = false; let toolbarHideTimer = null; @@ -1899,8 +1933,18 @@ inline std::string GetWebPageHTML() { const canvas = document.getElementById('screen-canvas'); const cursorOverlay = document.getElementById('cursor-overlay'); // Touch devices: hide browser cursor, show overlay (touchpad mode) - // Desktop: keep browser cursor visible, no overlay needed (remote shows cursor) - canvas.style.cursor = (controlEnabled && isTouchDevice) ? 'none' : 'default'; + // Desktop: use remote cursor style when control enabled + if (controlEnabled && isTouchDevice) { + canvas.style.cursor = 'none'; + } else if (controlEnabled && !isTouchDevice) { + // Apply current remote cursor + const cssCursor = (currentCursorIndex >= 0 && currentCursorIndex < cursorMap.length) + ? cursorMap[currentCursorIndex] + : 'default'; + canvas.style.cursor = cssCursor; + } else { + canvas.style.cursor = 'default'; + } cursorOverlay.classList.toggle('active', controlEnabled && isTouchDevice); } @@ -2726,6 +2770,7 @@ inline std::string GetWebPageHTML() { if (qcMouse) qcMouse.classList.remove('active'); document.getElementById('screen-canvas').style.cursor = 'default'; document.getElementById('cursor-overlay').classList.remove('active'); + currentCursorIndex = 1; // Reset to default arrow // Reset zoom state zoomState.scale = 1; diff --git a/server/2015Remote/WebService.cpp b/server/2015Remote/WebService.cpp index 89e3ecc..026ea14 100644 --- a/server/2015Remote/WebService.cpp +++ b/server/2015Remote/WebService.cpp @@ -1475,6 +1475,27 @@ void CWebService::NotifyResolutionChange(uint64_t device_id, int width, int heig } } +void CWebService::BroadcastCursor(uint64_t device_id, uint8_t cursor_index) { + if (m_bStopping) return; + + // Build JSON message + Json::Value res; + res["cmd"] = "cursor"; + res["index"] = cursor_index; + + Json::StreamWriterBuilder builder; + builder["indentation"] = ""; + std::string json = Json::writeString(builder, res); + + // Send to all watching clients + std::lock_guard lock(m_ClientsMutex); + for (auto& [ws_ptr, client] : m_Clients) { + if (client.watch_device_id == device_id) { + SendText(ws_ptr, json); + } + } +} + bool CWebService::StartRemoteDesktop(uint64_t device_id) { if (!m_pParentDlg) return false; diff --git a/server/2015Remote/WebService.h b/server/2015Remote/WebService.h index c558bb0..6de8fa6 100644 --- a/server/2015Remote/WebService.h +++ b/server/2015Remote/WebService.h @@ -96,6 +96,9 @@ public: // Resolution change notification void NotifyResolutionChange(uint64_t device_id, int width, int height); + // Cursor change notification (called from ScreenSpyDlg) + void BroadcastCursor(uint64_t device_id, uint8_t cursor_index); + // Get count of web clients watching a device int GetWebClientCount(uint64_t device_id);