Feature: Web remote desktop cursor sync with remote host
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
// 读取滚动参数
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<std::mutex> 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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user