VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/platform/win/WinKeyboard.cpp

Last change on this file was 103538, checked in by vboxsync, 3 months ago

FE/Qt: Moving out logging stuff from UIDefs.h to separate UILoggingDefs.h; This breaks dependency of UIDefs/UICommon headers from VBox/log.h

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.6 KB
Line 
1/* $Id: WinKeyboard.cpp 103538 2024-02-22 17:06:26Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - Declarations of utility functions for handling Windows Keyboard specific tasks.
4 */
5
6/*
7 * Copyright (C) 2014-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* GUI includes: */
29#include "UILoggingDefs.h"
30#include "WinKeyboard.h"
31
32/* Other VBox includes: */
33#include <iprt/assert.h>
34
35/* External includes: */
36#include <stdio.h>
37
38
39/* Beautification of log output */
40#define VBOX_BOOL_TO_STR_STATE(x) (x) ? "ON" : "OFF"
41#define VBOX_CONTROL_TO_STR_NAME(x) ((x == VK_CAPITAL) ? "CAPS" : (x == VK_SCROLL ? "SCROLL" : ((x == VK_NUMLOCK) ? "NUM" : "UNKNOWN")))
42
43/* A structure that contains internal control state representation */
44typedef struct VBoxModifiersState_t {
45 bool fNumLockOn; /** A state of NUM LOCK */
46 bool fCapsLockOn; /** A state of CAPS LOCK */
47 bool fScrollLockOn; /** A state of SCROLL LOCK */
48} VBoxModifiersState_t;
49
50/**
51 * Get current state of a keyboard modifier.
52 *
53 * @param idModifier Modifier to examine (VK_CAPITAL, VK_SCROLL or VK_NUMLOCK)
54 *
55 * @returns modifier state or asserts if wrong modifier is specified.
56 */
57static bool winGetModifierState(int idModifier)
58{
59 AssertReturn((idModifier == VK_CAPITAL) || (idModifier == VK_SCROLL) || (idModifier == VK_NUMLOCK), false);
60 return (GetKeyState(idModifier) & 0x0001) != 0;
61}
62
63/**
64 * Set current state of a keyboard modifier.
65 *
66 * @param idModifier Modifier to set (VK_CAPITAL, VK_SCROLL or VK_NUMLOCK)
67 * @param fState State to set
68 */
69static void winSetModifierState(int idModifier, bool fState)
70{
71 AssertReturnVoid((idModifier == VK_CAPITAL) || (idModifier == VK_SCROLL) || (idModifier == VK_NUMLOCK));
72
73 /* If the modifier is already in desired state, just do nothing. Otherwise, toggle it. */
74 if (winGetModifierState(idModifier) != fState)
75 {
76 /* Simulate KeyUp+KeyDown keystroke */
77 keybd_event(idModifier, 0, KEYEVENTF_EXTENDEDKEY, 0);
78 keybd_event(idModifier, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
79
80 /* Process posted above keyboard events immediately: */
81 MSG msg;
82 while (::PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))
83 ::DispatchMessage(&msg);
84
85 LogRel2(("HID LEDs sync: setting %s state to %s (0x%X).\n",
86 VBOX_CONTROL_TO_STR_NAME(idModifier), VBOX_BOOL_TO_STR_STATE(fState), MapVirtualKey(idModifier, MAPVK_VK_TO_VSC)));
87 }
88 else
89 {
90 LogRel2(("HID LEDs sync: setting %s state: skipped: state is already %s (0x%X).\n",
91 VBOX_CONTROL_TO_STR_NAME(idModifier), VBOX_BOOL_TO_STR_STATE(fState), MapVirtualKey(idModifier, MAPVK_VK_TO_VSC)));
92 }
93}
94
95/** Set all HID LEDs at once. */
96static bool winSetHidLeds(bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn)
97{
98 winSetModifierState(VK_NUMLOCK, fNumLockOn);
99 winSetModifierState(VK_CAPITAL, fCapsLockOn);
100 winSetModifierState(VK_SCROLL, fScrollLockOn);
101 return true;
102}
103
104/** Check if specified LED states correspond to the system modifier states. */
105bool winHidLedsInSync(bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn)
106{
107 if (winGetModifierState(VK_NUMLOCK) != fNumLockOn)
108 return false;
109
110 if (winGetModifierState(VK_CAPITAL) != fCapsLockOn)
111 return false;
112
113 if (winGetModifierState(VK_SCROLL) != fScrollLockOn)
114 return false;
115
116 return true;
117}
118
119/**
120 * Allocate memory and store modifier states there.
121 *
122 * @returns allocated memory witch contains modifier states or NULL.
123 */
124void * WinHidDevicesKeepLedsState(void)
125{
126 VBoxModifiersState_t *pState;
127
128 pState = (VBoxModifiersState_t *)malloc(sizeof(VBoxModifiersState_t));
129 if (pState)
130 {
131 pState->fNumLockOn = winGetModifierState(VK_NUMLOCK);
132 pState->fCapsLockOn = winGetModifierState(VK_CAPITAL);
133 pState->fScrollLockOn = winGetModifierState(VK_SCROLL);
134
135 LogRel2(("HID LEDs sync: host state captured: NUM(%s) CAPS(%s) SCROLL(%s)\n",
136 VBOX_BOOL_TO_STR_STATE(pState->fNumLockOn),
137 VBOX_BOOL_TO_STR_STATE(pState->fCapsLockOn),
138 VBOX_BOOL_TO_STR_STATE(pState->fScrollLockOn)));
139
140 return (void *)pState;
141 }
142
143 LogRel2(("HID LEDs sync: unable to allocate memory for HID LEDs synchronization data. LEDs sync will be disabled.\n"));
144
145 return NULL;
146}
147
148/**
149 * Restore host modifier states and free memory.
150 */
151void WinHidDevicesApplyAndReleaseLedsState(void *pData)
152{
153 VBoxModifiersState_t *pState = (VBoxModifiersState_t *)pData;
154
155 if (pState)
156 {
157 LogRel2(("HID LEDs sync: attempt to restore host state: NUM(%s) CAPS(%s) SCROLL(%s)\n",
158 VBOX_BOOL_TO_STR_STATE(pState->fNumLockOn),
159 VBOX_BOOL_TO_STR_STATE(pState->fCapsLockOn),
160 VBOX_BOOL_TO_STR_STATE(pState->fScrollLockOn)));
161
162 if (winSetHidLeds(pState->fNumLockOn, pState->fCapsLockOn, pState->fScrollLockOn))
163 LogRel2(("HID LEDs sync: host state restored\n"));
164 else
165 LogRel2(("HID LEDs sync: host state restore failed\n"));
166
167 free(pState);
168 }
169}
170
171/**
172 * Broadcast HID modifier states.
173 *
174 * @param fNumLockOn NUM LOCK state
175 * @param fCapsLockOn CAPS LOCK state
176 * @param fScrollLockOn SCROLL LOCK state
177 */
178void WinHidDevicesBroadcastLeds(bool fNumLockOn, bool fCapsLockOn, bool fScrollLockOn)
179{
180 LogRel2(("HID LEDs sync: start broadcast guest modifier states: NUM(%s) CAPS(%s) SCROLL(%s)\n",
181 VBOX_BOOL_TO_STR_STATE(fNumLockOn),
182 VBOX_BOOL_TO_STR_STATE(fCapsLockOn),
183 VBOX_BOOL_TO_STR_STATE(fScrollLockOn)));
184
185 if (winSetHidLeds(fNumLockOn, fCapsLockOn, fScrollLockOn))
186 LogRel2(("HID LEDs sync: broadcast completed\n"));
187 else
188 LogRel2(("HID LEDs sync: broadcast failed\n"));
189}
190
191/** @brief doesCurrentLayoutHaveAltGr
192 *
193 * @return true if this keyboard layout has an AltGr key, false otherwise
194 * Check to see whether the current keyboard layout actually has an AltGr key
195 * by checking whether any of the keys which might do produce a symbol when
196 * AltGr (Control + Alt) is depressed. Generally this loop will exit pretty
197 * early (it exits on the first iteration for my German layout). If there is
198 * no AltGr key in the layout then it will run right through, but that should
199 * hopefully not happen very often.
200 *
201 * In theory we could do this once and cache the result, but that involves
202 * tracking layout switches to invalidate the cache, and I don't think that the
203 * added complexity is worth the price. */
204static bool doesCurrentLayoutHaveAltGr()
205{
206 /* Keyboard state array with VK_CONTROL and VK_MENU depressed. */
207 static const BYTE s_auKeyStates[256] =
208 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0x80 };
209 WORD ach;
210 unsigned i;
211
212 for (i = '0'; i <= VK_OEM_102; ++i)
213 {
214 if (ToAscii(i, 0, s_auKeyStates, &ach, 0))
215 break;
216 /* Skip ranges of virtual keys which are undefined or not relevant. */
217 if (i == '9')
218 i = 'A' - 1;
219 if (i == 'Z')
220 i = VK_OEM_1 - 1;
221 if (i == VK_OEM_3)
222 i = VK_OEM_4 - 1;
223 if (i == VK_OEM_8)
224 i = VK_OEM_102 - 1;
225 }
226 if (i > VK_OEM_102)
227 return false;
228 return true;
229}
230
231void WinAltGrMonitor::updateStateFromKeyEvent(unsigned iDownScanCode,
232 bool fKeyDown, bool fExtendedKey)
233{
234 LONG messageTime = GetMessageTime();
235 /* We do not want the make/break: */
236 AssertRelease(~iDownScanCode & 0x80);
237 /* Depending on m_enmFakeControlDetectionState: */
238 switch (m_enmFakeControlDetectionState)
239 {
240 case NONE:
241 case FAKE_CONTROL_DOWN:
242 if ( iDownScanCode == 0x1D /* left control */
243 && fKeyDown
244 && !fExtendedKey)
245 m_enmFakeControlDetectionState = LAST_EVENT_WAS_LEFT_CONTROL_DOWN;
246 else
247 m_enmFakeControlDetectionState = NONE;
248 break;
249 case LAST_EVENT_WAS_LEFT_CONTROL_DOWN:
250 if ( iDownScanCode == 0x38 /* Alt */
251 && fKeyDown
252 && fExtendedKey
253 && m_timeOfLastKeyEvent == messageTime
254 && doesCurrentLayoutHaveAltGr())
255 {
256 m_enmFakeControlDetectionState = FAKE_CONTROL_DOWN;
257 break;
258 }
259 else
260 m_enmFakeControlDetectionState = LEFT_CONTROL_DOWN;
261 RT_FALL_THRU();
262 case LEFT_CONTROL_DOWN:
263 if ( iDownScanCode == 0x1D /* left control */
264 && !fKeyDown
265 && !fExtendedKey)
266 m_enmFakeControlDetectionState = NONE;
267 break;
268 default:
269 AssertReleaseMsgFailed(("Unknown AltGr detection state.\n"));
270 }
271 m_timeOfLastKeyEvent = messageTime;
272}
273
274bool WinAltGrMonitor::isLeftControlReleaseNeeded() const
275{
276 return m_enmFakeControlDetectionState == FAKE_CONTROL_DOWN;
277}
278
279bool WinAltGrMonitor::isCurrentEventDefinitelyFake(unsigned iDownScanCode,
280 bool fKeyDown,
281 bool fExtendedKey) const
282{
283 if (iDownScanCode != 0x1d /* scan code: Control */ || fExtendedKey)
284 return false;
285
286 LONG messageTime = GetMessageTime();
287 MSG peekMsg;
288 if (!PeekMessage(&peekMsg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_NOREMOVE))
289 return false;
290 if (messageTime != (LONG)peekMsg.time)
291 return false;
292
293 if ( fKeyDown
294 && peekMsg.message != WM_KEYDOWN
295 && peekMsg.message != WM_SYSKEYDOWN)
296 return false;
297 if ( !fKeyDown
298 && peekMsg.message != WM_KEYUP
299 && peekMsg.message != WM_SYSKEYUP)
300 return false;
301 if ( (RT_HIWORD(peekMsg.lParam) & 0xFF) != 0x38 /* scan code: Alt */
302 || !(RT_HIWORD(peekMsg.lParam) & KF_EXTENDED))
303 return false;
304 if (!doesCurrentLayoutHaveAltGr())
305 return false;
306 return true;
307}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use