VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 17 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use