VirtualBox

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

Last change on this file since 74942 was 71784, checked in by vboxsync, 6 years ago

FE/Qt: bugref:9049: VBoxGlobal library fixes for Windows.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use