Skip to content

Commit 4cc7dbd

Browse files
committed
Reset modifier button's state
1 parent 4379666 commit 4cc7dbd

File tree

1 file changed

+72
-0
lines changed
  • systemvm/agent/noVNC/app

1 file changed

+72
-0
lines changed

systemvm/agent/noVNC/app/ui.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ const UI = {
2929
connected: false,
3030
desktopName: "",
3131

32+
// Modifier key configuration
33+
_modifierKeys: {
34+
shift: { keysym: KeyTable.XK_Shift_L, code: "ShiftLeft", buttonId: 'noVNC_toggle_shift_button' },
35+
ctrl: { keysym: KeyTable.XK_Control_L, code: "ControlLeft", buttonId: 'noVNC_toggle_ctrl_button' },
36+
alt: { keysym: KeyTable.XK_Alt_L, code: "AltLeft", buttonId: 'noVNC_toggle_alt_button' },
37+
windows: { keysym: KeyTable.XK_Super_L, code: "MetaLeft", buttonId: 'noVNC_toggle_windows_button' }
38+
},
39+
3240
statusTimeout: null,
3341
hideKeyboardTimeout: null,
3442
idleControlbarTimeout: null,
@@ -125,6 +133,11 @@ const UI = {
125133
document.getElementById("noVNC_status")
126134
.addEventListener('click', UI.hideStatus);
127135

136+
// Handle tab/window close to release modifier keys
137+
// This is critical for VMware VMs using websocket reverse proxy
138+
window.addEventListener('beforeunload', UI.handleBeforeUnload);
139+
window.addEventListener('pagehide', UI.handlePageHide);
140+
128141
// Bootstrap fallback input handler
129142
UI.keyboardinputReset();
130143

@@ -1740,6 +1753,51 @@ const UI = {
17401753
UI.idleControlbar();
17411754
},
17421755

1756+
_sendKeyUp(keysym, code) {
1757+
UI.rfb.sendKey(keysym, code, false);
1758+
},
1759+
1760+
// Release a single modifier key if it's pressed
1761+
_releaseModifierKey(keyName) {
1762+
const keyConfig = UI._modifierKeys[keyName];
1763+
if (!keyConfig) return false;
1764+
1765+
const btn = document.getElementById(keyConfig.buttonId);
1766+
if (!btn || !btn.classList.contains("noVNC_selected")) {
1767+
return false;
1768+
}
1769+
1770+
UI._sendKeyUp(keyConfig.keysym, keyConfig.code);
1771+
btn.classList.remove("noVNC_selected");
1772+
return true;
1773+
},
1774+
1775+
// Release all currently pressed modifier keys
1776+
_releaseAllModifierKeys() {
1777+
if (!UI.rfb || UI.rfb._rfbConnectionState !== 'connected') {
1778+
return false;
1779+
}
1780+
1781+
let keysReleased = false;
1782+
1783+
// Release all modifier keys
1784+
for (const keyName in UI._modifierKeys) {
1785+
if (UI._releaseModifierKey(keyName)) {
1786+
keysReleased = true;
1787+
}
1788+
}
1789+
1790+
// Also check RFB's internal shift state (it tracks this separately)
1791+
if (UI.rfb._shiftPressed) {
1792+
const shiftConfig = UI._modifierKeys.shift;
1793+
UI._sendKeyUp(shiftConfig.keysym, shiftConfig.code);
1794+
keysReleased = true;
1795+
}
1796+
1797+
return keysReleased;
1798+
},
1799+
1800+
17431801
// Move focus to the screen in order to be able to use the
17441802
// keyboard right after these extra keys.
17451803
// The exception is when a virtual keyboard is used, because
@@ -1836,6 +1894,20 @@ const UI = {
18361894
selectbox.options.add(optn);
18371895
},
18381896

1897+
// Handle tab/window close events
1898+
// These fire when the user closes the tab, which doesn't call disconnect()
1899+
handleBeforeUnload(event) {
1900+
// Release modifier keys before tab closes
1901+
// This is critical for VMware VMs using websocket reverse proxy
1902+
UI._releaseAllModifierKeys();
1903+
},
1904+
1905+
handlePageHide(event) {
1906+
// Also handle pagehide as a fallback (fires in more browsers)
1907+
// Release modifier keys before page is hidden
1908+
UI._releaseAllModifierKeys();
1909+
},
1910+
18391911
/* ------^-------
18401912
* /MISC
18411913
* ==============

0 commit comments

Comments
 (0)