Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/cpp/imehandler/windows/src/winapihandler.cc
2868 views
1
/*
2
Licensed to the Software Freedom Conservancy (SFC) under one
3
or more contributor license agreements. See the NOTICE file
4
distributed with this work for additional information
5
regarding copyright ownership. The SFC licenses this file
6
to you under the Apache License, Version 2.0 (the "License");
7
you may not use this file except in compliance with the License.
8
You may obtain a copy of the License at
9
10
http://www.apache.org/licenses/LICENSE-2.0
11
12
Unless required by applicable law or agreed to in writing, software
13
distributed under the License is distributed on an "AS IS" BASIS,
14
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
See the License for the specific language governing permissions and
16
limitations under the License.
17
18
Author: [email protected]
19
*/
20
21
#include "winapihandler.h"
22
23
/*
24
#ifdef _MSC_VER
25
#include "stdafx.h"
26
#endif
27
*/
28
29
#include <Objbase.h>
30
#include <Msctf.h>
31
#include <Windows.h>
32
33
#include <sstream>
34
#include <iostream>
35
#include <algorithm>
36
#include <iomanip>
37
38
// Windows registry constants.
39
#define MAX_KEY_LENGTH 255
40
#define MAX_VALUE_NAME 16383
41
#define MAX_LAYOUT_NAME_LENGTH 255
42
43
WinapiHandler::WinapiHandler() {
44
// Open the registry and load the lookup table for available layouts.
45
LoadAvailableLayouts();
46
}
47
48
std::string WinapiHandler::GetLayoutName(std::string hkl) const {
49
std::string name = "";
50
std::vector<keyboard_layout>::const_iterator it = keyboard_layouts.begin();
51
// First we look for existing entries without change.
52
while (it != keyboard_layouts.end() && (it->first != hkl)) { it++; }
53
if (it != keyboard_layouts.end()) {
54
name = it->second;
55
} else {
56
// If we didn't find it, then look if the name must be changed.
57
// For instance 04090409 is loaded as 00000409 (US).
58
if (hkl.find("0000") != 0) {
59
hkl = "0000"+hkl.substr(4);
60
it = keyboard_layouts.begin();
61
while (it != keyboard_layouts.end() && (it->first != hkl)) { it++; }
62
if (it != keyboard_layouts.end()) {
63
name = it->second;
64
}
65
}
66
}
67
return name;
68
}
69
70
std::string WinapiHandler::GetLayoutHkl(const std::string& name) const {
71
std::string hkl = "";
72
std::vector<keyboard_layout>::const_iterator it = keyboard_layouts.begin();
73
while (it != keyboard_layouts.end() && (it->second != name)) { it++; }
74
if (it != keyboard_layouts.end()) {
75
hkl = it->first;
76
}
77
return hkl;
78
}
79
80
void WinapiHandler::LoadAvailableLayouts() {
81
HKEY key;
82
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
83
TEXT("SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts"),
84
0, KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) {
85
TCHAR hkl_string[MAX_KEY_LENGTH]; // Buffer for subkey name.
86
DWORD key_name_size; // Size of name string.
87
DWORD nb_subkeys = 0; // Number of subkeys (layouts).
88
TCHAR layout_name[MAX_LAYOUT_NAME_LENGTH]; // Buffer for layout name.
89
90
DWORD ret_code;
91
// Get the number of layouts.
92
ret_code = RegQueryInfoKey(key, // Key handle.
93
NULL, // Buffer for class name.
94
NULL, // Size of class string.
95
NULL, // Reserved.
96
&nb_subkeys, // Number of subkeys.
97
NULL, // Longest subkey size.
98
NULL, // Longest class string.
99
NULL, // Number of values for this key.
100
NULL, // Longest value name.
101
NULL, // Longest value data.
102
NULL, // Security descriptor.
103
NULL); // Last write time.
104
105
if (ret_code == ERROR_SUCCESS) {
106
// Enumerate the subkeys, until RegEnumKeyEx fails.
107
if (nb_subkeys > 0) {
108
keyboard_layouts.clear();
109
keyboard_layouts.reserve(nb_subkeys);
110
111
for (unsigned int i = 0; i < nb_subkeys; i++) {
112
key_name_size = MAX_KEY_LENGTH;
113
ret_code = RegEnumKeyEx(key, i, hkl_string, &key_name_size,
114
NULL, NULL, NULL, NULL);
115
if (ret_code == ERROR_SUCCESS) {
116
DWORD nb_bytes = sizeof(layout_name);
117
if (RegGetValue(key, hkl_string, "Layout Text", RRF_RT_ANY, NULL,
118
&layout_name, &nb_bytes) == ERROR_SUCCESS) {
119
keyboard_layout k = keyboard_layout(hkl_string, layout_name);
120
keyboard_layouts.push_back(k);
121
} else {
122
std::cerr << "error getting key modifiers" << std::endl;
123
}
124
}
125
}
126
}
127
RegCloseKey(key);
128
} else {
129
std::cerr << "could not access the registry" << std::endl;
130
}
131
}
132
}
133
134
/*
135
* Returns the name of the engine currently active.
136
*/
137
std::string WinapiHandler::GetActiveEngine() const {
138
char * keyboard_name = new char[KL_NAMELENGTH];
139
GetKeyboardLayoutName(keyboard_name);
140
std::string engine_name = GetLayoutName(keyboard_name);
141
delete keyboard_name;
142
return engine_name;
143
}
144
145
// http://msdn.microsoft.com/en-us/library/ms927178.aspx
146
std::string WinapiHandler::GetToggleKeys() const {
147
return "";
148
}
149
150
bool WinapiHandler::IsActivated() const {
151
return true;
152
}
153
154
/*
155
* Activates the first input among the loaded ones.
156
*/
157
void WinapiHandler::Deactivate() {
158
std::vector<std::string> engines = GetLoadedEngines();
159
if (engines.size() > 0) {
160
ActivateEngine(engines[0]);
161
} else {
162
std::cerr << "No default engine could be activated" << std::endl;
163
}
164
}
165
166
/*
167
* Return the key combination as written in the list that enables
168
* the selection of such keys on the control panel.
169
*/
170
std::string WinapiHandler::GetNextEngineKeys() const {
171
std::string keys = "";
172
HKEY key;
173
if(RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Keyboard Layout"), 0,
174
KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) {
175
char val[2];
176
DWORD nb_bytes = sizeof(val);
177
if(RegGetValue(key, "Toggle", "Hotkey", RRF_RT_REG_SZ, NULL, &val,
178
&nb_bytes) == ERROR_SUCCESS) {
179
switch (atoi(val)) {
180
case 1:
181
keys = "LEFT ALT+SHIFT";
182
break;
183
case 2:
184
keys = "CTRL+SHIFT";
185
break;
186
case 3:
187
keys = "NOT ASSIGNED";
188
break;
189
case 4:
190
keys = "GRAVE ACCENT";
191
break;
192
}
193
}
194
RegCloseKey(key);
195
}
196
return keys;
197
}
198
199
/*
200
* Returns the HKL of loaded engines.
201
* see http://msdn.microsoft.com/en-us/goglobal/bb688135.aspx
202
* see http://blogs.msdn.com/b/michkap/archive/2004/12/16/318271.aspx
203
*/
204
std::vector<std::string> WinapiHandler::GetLoadedEngines() const {
205
std::vector<std::string> loaded_engines;
206
int nb_keyboards = GetKeyboardLayoutList(0, 0);
207
HKL* keyboards = new HKL[nb_keyboards];
208
GetKeyboardLayoutList(nb_keyboards, keyboards);
209
std::stringstream ss;
210
for (int i = 0 ; i < nb_keyboards ; i++) {
211
ss << keyboards[i];
212
std::string keyboard_name = ss.str();
213
// On x64 machines 8 useless characters are included...
214
// TODO(timothe): fix that at compile time instead of the "if" here.
215
if ( keyboard_name.size() > 8 ) {
216
keyboard_name = keyboard_name.substr(8);
217
}
218
loaded_engines.push_back(GetLayoutName(keyboard_name));
219
// Reset the string stream.
220
ss.str("");
221
}
222
delete keyboards;
223
return loaded_engines;
224
}
225
226
std::vector<std::string> WinapiHandler::GetAvailableEngines() const {
227
std::vector<std::string> available_engines;
228
available_engines.reserve(keyboard_layouts.size());
229
for(std::vector<keyboard_layout>::const_iterator it =
230
keyboard_layouts.begin() ; it != keyboard_layouts.end() ; it++) {
231
available_engines.push_back(it->second);
232
}
233
return available_engines;
234
}
235
236
int WinapiHandler::LoadEngines(const std::vector<std::string>& engine_names) {
237
int nb_loaded_engines = 0;
238
if (!engine_names.empty()) {
239
// First we get the currently loaded engines, to unload the unused
240
// ones afterwards.
241
std::vector<std::string> loaded_engines = GetLoadedEngines();
242
243
// Then we load the new ones, for now it is the same as activating them
244
for (std::vector<std::string>::const_iterator it = engine_names.begin() ;
245
it != engine_names.end() ; it++) {
246
// std::cout <<"Loading keyboard " << it->c_str() << std::endl;
247
// XXX: bug: this never returns NULL as the documentation says...
248
if (LoadKeyboardLayout(GetLayoutHkl(*it).c_str(), 0) != NULL) {
249
++nb_loaded_engines;
250
}
251
}
252
253
// Then we unload the new ones so that there was always at least one engine
254
// active (windows does not accept unloading the engines first, then
255
// activate the new ones)
256
for (std::vector<std::string>::const_iterator it =
257
loaded_engines.begin() ; it != loaded_engines.end() ; it++) {
258
if (std::find(engine_names.begin(), engine_names.end(), *it) ==
259
engine_names.end()) {
260
// We need to activate it to get the HKL...
261
ActivateEngine(*it);
262
HKL hkl = GetKeyboardLayout(0);
263
// And we activate another one to be able to unload it
264
ActivateEngine(engine_names[0]);
265
if (!UnloadKeyboardLayout(hkl)) {
266
std::cerr << "could not unload keyboard layout " << *it;
267
std::cerr << std::endl;
268
}
269
}
270
}
271
}
272
return nb_loaded_engines;
273
}
274
275
bool WinapiHandler::ActivateEngine(const std::string& engine) {
276
std::string hkl = GetLayoutHkl(engine);
277
if (hkl != "") {
278
return LoadKeyboardLayout(hkl.c_str(),
279
KLF_ACTIVATE|KLF_REPLACELANG|KLF_SETFORPROCESS)
280
!= NULL;
281
}
282
return false;
283
}
284
285
std::string WinapiHandler::GetModifiers(DWORD value) {
286
std::stringstream poss;
287
std::stringstream modss;
288
//if ((value & MOD_LEFT) != 0 ) {
289
//poss << "LEFT";
290
//}
291
//if ((value & MOD_RIGHT) != 0) {
292
//if (!poss.str().empty()) poss << "/";
293
//poss << "RIGHT";
294
//}
295
296
if ((value & MOD_ALT) != 0) {
297
if (!modss.str().empty()) modss << "+";
298
modss << "ALT";
299
}
300
if ((value & MOD_CONTROL) != 0) {
301
if(!modss.str().empty()) modss << "+";
302
modss << "CTRL";
303
}
304
if ((value & MOD_SHIFT) != 0) {
305
if (!modss.str().empty()) modss << "+";
306
modss << "SHIFT";
307
}
308
309
if ((value & MOD_ON_KEYUP) != 0) {
310
modss << "(ON_KEY_UP)";
311
}
312
313
if ((value & MOD_IGNORE_ALL_MODIFIER) != 0) {
314
modss << "(MOD_IGNORE_ALL_MODIFIER)";
315
}
316
317
std::string pos = poss.str();
318
std::string mods = modss.str();
319
return pos+mods;
320
}
321
322
std::string WinapiHandler::GetSwitchingKeysForEngine(std::string engine) {
323
std::string keys = "";
324
HKEY key;
325
if (RegOpenKeyEx(HKEY_CURRENT_USER,
326
TEXT("Control Panel\\Input Method\\Hot Keys"),
327
0,
328
KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE, &key)
329
==ERROR_SUCCESS) {
330
TCHAR subkey_buffer[MAX_KEY_LENGTH];
331
DWORD subname_size = 0;
332
DWORD nb_subkeys = 0;
333
334
// Get the class name and the value count.
335
if (RegQueryInfoKey(key, // key handle
336
NULL, // buffer for class name
337
NULL, // size of class string
338
NULL, // reserved
339
&nb_subkeys, // number of subkeys
340
NULL, // longest subkey size
341
NULL, // longest class string
342
NULL, // number of values for this key
343
NULL, // longest value name
344
NULL, // longest value data
345
NULL, // security descriptor
346
NULL) // last write time
347
== ERROR_SUCCESS) {
348
// Enumerate the subkeys, until RegEnumKeyEx fails.
349
if (nb_subkeys > 0) {
350
for (unsigned int i = 0; i < nb_subkeys; i++) {
351
subname_size = MAX_KEY_LENGTH;
352
if (RegEnumKeyEx(key, i, subkey_buffer, &subname_size,
353
NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
354
DWORD modifiers = 0;
355
DWORD target_IME = 0;
356
DWORD virtual_key = 0;
357
DWORD nb_bytes = sizeof(modifiers);
358
if (RegGetValue(key, subkey_buffer, "Target IME", RRF_RT_ANY,
359
NULL, &target_IME, &nb_bytes) == ERROR_SUCCESS
360
&& (target_IME != 0)) {
361
std::stringstream hkl;
362
hkl << std::hex << std::setw(8) << std::setfill('0') <<
363
target_IME;
364
std::string layout_hkl = GetLayoutHkl(engine);
365
if (layout_hkl.find("0000") == 0) {
366
layout_hkl = layout_hkl.substr(4)+ layout_hkl.substr(4);
367
}
368
if (hkl.str() == layout_hkl) {
369
RegGetValue(key, subkey_buffer, "Key Modifiers", RRF_RT_ANY,
370
NULL, &modifiers, &nb_bytes);
371
RegGetValue(key, subkey_buffer, "Virtual Key", RRF_RT_ANY,
372
NULL, &virtual_key, &nb_bytes);
373
// Reset the string stream to build the resulting string.
374
hkl.str("");
375
hkl << GetModifiers(modifiers) << "+" <<
376
static_cast<char>(virtual_key);
377
keys = hkl.str();
378
}
379
}
380
}
381
}
382
}
383
}
384
RegCloseKey(key);
385
}
386
return keys;
387
}
388
389
/*
390
Also look at:
391
HWND hWnd = GetForegroundWindow();
392
DWORD threadId = GetWindowThreadProcessId(hWnd, NULL);
393
394
unsigned int hkl = 0;
395
std::stringstream ss;
396
ss << std::hex << jp2;
397
ss >> hkl;
398
PostMessage(hWnd, WM_INPUTLANGCHANGEREQUEST, 0, hkl);
399
400
But this indices a latency in activating the engines that make the tests
401
flacky and fails...
402
*/
403
404