From 4cc7dbdd81297227ae943f7c4a84f60a58aac4ed Mon Sep 17 00:00:00 2001 From: vishesh92 Date: Thu, 4 Dec 2025 15:01:24 +0530 Subject: [PATCH 1/3] Reset modifier button's state --- systemvm/agent/noVNC/app/ui.js | 72 ++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/systemvm/agent/noVNC/app/ui.js b/systemvm/agent/noVNC/app/ui.js index 75f2eb03f71a..86a2a80f4d4b 100644 --- a/systemvm/agent/noVNC/app/ui.js +++ b/systemvm/agent/noVNC/app/ui.js @@ -29,6 +29,14 @@ const UI = { connected: false, desktopName: "", + // Modifier key configuration + _modifierKeys: { + shift: { keysym: KeyTable.XK_Shift_L, code: "ShiftLeft", buttonId: 'noVNC_toggle_shift_button' }, + ctrl: { keysym: KeyTable.XK_Control_L, code: "ControlLeft", buttonId: 'noVNC_toggle_ctrl_button' }, + alt: { keysym: KeyTable.XK_Alt_L, code: "AltLeft", buttonId: 'noVNC_toggle_alt_button' }, + windows: { keysym: KeyTable.XK_Super_L, code: "MetaLeft", buttonId: 'noVNC_toggle_windows_button' } + }, + statusTimeout: null, hideKeyboardTimeout: null, idleControlbarTimeout: null, @@ -125,6 +133,11 @@ const UI = { document.getElementById("noVNC_status") .addEventListener('click', UI.hideStatus); + // Handle tab/window close to release modifier keys + // This is critical for VMware VMs using websocket reverse proxy + window.addEventListener('beforeunload', UI.handleBeforeUnload); + window.addEventListener('pagehide', UI.handlePageHide); + // Bootstrap fallback input handler UI.keyboardinputReset(); @@ -1740,6 +1753,51 @@ const UI = { UI.idleControlbar(); }, + _sendKeyUp(keysym, code) { + UI.rfb.sendKey(keysym, code, false); + }, + + // Release a single modifier key if it's pressed + _releaseModifierKey(keyName) { + const keyConfig = UI._modifierKeys[keyName]; + if (!keyConfig) return false; + + const btn = document.getElementById(keyConfig.buttonId); + if (!btn || !btn.classList.contains("noVNC_selected")) { + return false; + } + + UI._sendKeyUp(keyConfig.keysym, keyConfig.code); + btn.classList.remove("noVNC_selected"); + return true; + }, + + // Release all currently pressed modifier keys + _releaseAllModifierKeys() { + if (!UI.rfb || UI.rfb._rfbConnectionState !== 'connected') { + return false; + } + + let keysReleased = false; + + // Release all modifier keys + for (const keyName in UI._modifierKeys) { + if (UI._releaseModifierKey(keyName)) { + keysReleased = true; + } + } + + // Also check RFB's internal shift state (it tracks this separately) + if (UI.rfb._shiftPressed) { + const shiftConfig = UI._modifierKeys.shift; + UI._sendKeyUp(shiftConfig.keysym, shiftConfig.code); + keysReleased = true; + } + + return keysReleased; + }, + + // Move focus to the screen in order to be able to use the // keyboard right after these extra keys. // The exception is when a virtual keyboard is used, because @@ -1836,6 +1894,20 @@ const UI = { selectbox.options.add(optn); }, + // Handle tab/window close events + // These fire when the user closes the tab, which doesn't call disconnect() + handleBeforeUnload(event) { + // Release modifier keys before tab closes + // This is critical for VMware VMs using websocket reverse proxy + UI._releaseAllModifierKeys(); + }, + + handlePageHide(event) { + // Also handle pagehide as a fallback (fires in more browsers) + // Release modifier keys before page is hidden + UI._releaseAllModifierKeys(); + }, + /* ------^------- * /MISC * ============== From e39eb244b31b3eb71ab3b6b6a4d67a1821a0bc42 Mon Sep 17 00:00:00 2001 From: vishesh92 Date: Thu, 4 Dec 2025 16:28:55 +0530 Subject: [PATCH 2/3] Address copilot comments --- systemvm/agent/noVNC/app/ui.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/systemvm/agent/noVNC/app/ui.js b/systemvm/agent/noVNC/app/ui.js index 86a2a80f4d4b..747d4a4fd42e 100644 --- a/systemvm/agent/noVNC/app/ui.js +++ b/systemvm/agent/noVNC/app/ui.js @@ -1754,6 +1754,7 @@ const UI = { }, _sendKeyUp(keysym, code) { + if (!UI.rfb) return; UI.rfb.sendKey(keysym, code, false); }, @@ -1774,7 +1775,7 @@ const UI = { // Release all currently pressed modifier keys _releaseAllModifierKeys() { - if (!UI.rfb || UI.rfb._rfbConnectionState !== 'connected') { + if (!UI.rfb) { return false; } @@ -1786,14 +1787,6 @@ const UI = { keysReleased = true; } } - - // Also check RFB's internal shift state (it tracks this separately) - if (UI.rfb._shiftPressed) { - const shiftConfig = UI._modifierKeys.shift; - UI._sendKeyUp(shiftConfig.keysym, shiftConfig.code); - keysReleased = true; - } - return keysReleased; }, From e05b9709c38f2d7a2bab4e5a115587af7a0ab31b Mon Sep 17 00:00:00 2001 From: vishesh92 Date: Wed, 10 Dec 2025 16:54:38 +0530 Subject: [PATCH 3/3] Address comments --- systemvm/agent/noVNC/app/ui.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/systemvm/agent/noVNC/app/ui.js b/systemvm/agent/noVNC/app/ui.js index 747d4a4fd42e..85530549e604 100644 --- a/systemvm/agent/noVNC/app/ui.js +++ b/systemvm/agent/noVNC/app/ui.js @@ -1775,10 +1775,6 @@ const UI = { // Release all currently pressed modifier keys _releaseAllModifierKeys() { - if (!UI.rfb) { - return false; - } - let keysReleased = false; // Release all modifier keys @@ -1790,7 +1786,6 @@ const UI = { return keysReleased; }, - // Move focus to the screen in order to be able to use the // keyboard right after these extra keys. // The exception is when a virtual keyboard is used, because