forked from elifiner/recaps
-
Notifications
You must be signed in to change notification settings - Fork 1
/
fixlayouts.c
402 lines (344 loc) · 10.4 KB
/
fixlayouts.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
#include "stdafx.h"
#include "fixlayouts.h"
#include "clipboard.h"
#include "utils.h"
///////////////////////////////////////////////////////////////////////////////
// Converts the text in the active window from one keyboard layout to another
// using the clipboard.
void ConvertSelectedTextInActiveWindow(HKL hklSource, HKL hklTarget)
{
WCHAR* sourceText = NULL;
WCHAR* targetText = NULL;
const WCHAR dummy[] = L"__RECAPS__";
// store previous clipboard data and set clipboard to dummy string
ClipboardData prevClipboardData;
if(!StoreClipboardData(&prevClipboardData))
return;
if(!SetClipboardText(dummy))
{
// restore the original clipboard data
RestoreClipboardData(&prevClipboardData);
return;
}
// copy the selected text by simulating Ctrl-C
SendKeyCombo('C', TRUE, FALSE, FALSE);
// wait until copy operation completes and get the copied data from the clipboard
// this loop has the nice side effect of setting copyOK to FALSE if there's no
// selected text, since nothing is copied and the clipboard still contains the
// contents of `dummy`.
BOOL copyOK = FALSE;
for(int i = 0; i < 10; i++)
{
sourceText = GetClipboardText();
if(sourceText && wcscmp(sourceText, dummy) != 0)
{
copyOK = TRUE;
break;
}
else
{
free(sourceText);
Sleep(30);
}
}
if(copyOK)
{
// if the string only matches one particular layout, use it
// otherwise use the provided layout
int matches = 0;
HKL hklDetected = DetectLayoutFromString(sourceText, &matches);
if(matches == 1)
hklSource = hklDetected;
// convert the text between layouts
size_t length = wcslen(sourceText);
targetText = (WCHAR*)malloc(sizeof(WCHAR) * (length + 1));
size_t converted = LayoutConvertString(sourceText, targetText, length + 1, hklSource, hklTarget);
if(converted)
{
// put the converted string on the clipboard
if(SetClipboardText(targetText))
{
// simulate Ctrl-V to paste the text, replacing the previous text
SendKeyCombo('V', TRUE, FALSE, FALSE);
// let the application complete pasting before putting the old data back on the clipboard
Sleep(REMOTE_APP_WAIT);
}
}
// free allocated memory
free(sourceText);
free(targetText);
}
// restore the original clipboard data
RestoreClipboardData(&prevClipboardData);
}
///////////////////////////////////////////////////////////////////////////////
// Converts a character from one keyboard layout to another
WCHAR LayoutConvertChar(WCHAR ch, HKL hklSource, HKL hklTarget)
{
// special handling for some ambivalent characters in Hebrew layout
if(LOWORD(hklSource) == MAKELANGID(LANG_HEBREW, SUBLANG_HEBREW_ISRAEL) &&
LOWORD(hklTarget) == MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US))
{
switch(ch)
{
case L'.': return L'/';
case L'/': return L'q';
case L'\'': return L'w';
case L',': return L'\'';
}
}
else if(LOWORD(hklSource) == MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US) &&
LOWORD(hklTarget) == MAKELANGID(LANG_HEBREW, SUBLANG_HEBREW_ISRAEL))
{
switch(ch)
{
case L'/': return L'.';
case L'q': return L'/';
case L'w': return L'\'';
case L'\'': return L',';
}
}
// get the virtual key code and the shift state using the character and the source keyboard layout
SHORT vkAndShift = VkKeyScanEx(ch, hklSource);
if(vkAndShift == -1)
return 0; // no such char in source keyboard layout
BYTE vk = LOBYTE(vkAndShift);
BYTE shift = HIBYTE(vkAndShift);
// convert the shift state returned from VkKeyScanEx to an array that represents the
// key state usable with ToUnicodeEx that we'll be calling next
BYTE keyState[256] = { 0 };
if(shift & 1) keyState[VK_SHIFT] = 0x80; // turn on high bit
if(shift & 2) keyState[VK_CONTROL] = 0x80;
if(shift & 4) keyState[VK_MENU] = 0x80;
// convert virtual key and key state to a new character using the target keyboard layout
WCHAR buffer[10] = { 0 };
int result = ToUnicodeEx(vk, 0, keyState, buffer, 10, 0, hklTarget);
// result can be more than 1 if the character in the source layout is represented by
// several characters in the target layout, but we ignore this to simplify the function.
if(result == 1)
return buffer[0];
// conversion failed for some reason
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// Converts a string from one keyboard layout to another
size_t LayoutConvertString(const WCHAR* str, WCHAR* buffer, size_t size, HKL hklSource, HKL hklTarget)
{
size_t i;
for(i = 0; i < wcslen(str) && i < size - 1; i++)
{
WCHAR ch = LayoutConvertChar(str[i], hklSource, hklTarget);
if(ch == 0)
return 0;
buffer[i] = ch;
}
buffer[i] = '\0';
return i;
}
///////////////////////////////////////////////////////////////////////////////
// Goes through all the installed keyboard layouts and returns a layout that
// can generate the string. If not matching layout is found, returns NULL.
// If `multiple` isn't NULL it will be set to the number of matched layouts.
HKL DetectLayoutFromString(const WCHAR* str, int* pmatches)
{
HKL result = NULL;
HKL* hkls;
UINT layoutCount;
layoutCount = GetKeyboardLayoutList(0, NULL);
hkls = (HKL*)malloc(sizeof(HKL) * layoutCount);
GetKeyboardLayoutList(layoutCount, hkls);
int matches = 0;
for(size_t layout = 0; layout < layoutCount; layout++)
{
BOOL validLayout = TRUE;
for(size_t i = 0; i < wcslen(str); i++)
{
UINT vk = VkKeyScanEx(str[i], hkls[layout]);
if(vk == -1)
{
validLayout = FALSE;
break;
}
}
if(validLayout)
{
matches++;
if(!result)
result = hkls[layout];
}
}
if(pmatches)
*pmatches = matches;
return result;
}
///////////////////////////////////////////////////////////////////////////////
// Stores the clipboard data in all its formats in `formats`.
// You must call FreeAllClipboardData on `formats` when it's no longer needed.
BOOL StoreClipboardData(ClipboardData* formats)
{
if(!OpenClipboard(NULL))
return FALSE;
formats->count = CountClipboardFormats();
if(formats->count == 0)
{
DWORD dwError = GetLastError();
CloseClipboard();
return dwError == ERROR_SUCCESS;
}
formats->dataArray = (ClipboardFormat*)malloc(sizeof(ClipboardData) * formats->count);
ZeroMemory(formats->dataArray, sizeof(ClipboardData) * formats->count);
int i = 0;
UINT format = EnumClipboardFormats(0);
while(format)
{
if(i > formats->count - 1)
break;
HANDLE dataHandle = GetClipboardData(format);
if(!dataHandle && GetLastError() != ERROR_SUCCESS)
break;
if(dataHandle)
{
size_t size;
formats->dataArray[i].format = format;
formats->dataArray[i].dataHandle = clipboard_copy_data(format, dataHandle, &size);
if(!formats->dataArray[i].dataHandle)
break;
i++;
}
// next format
format = EnumClipboardFormats(format);
}
CloseClipboard();
if(format) // if failed before completion
{
for(int j = 0; j < i; j++)
{
clipboard_free_data(formats->dataArray[j].format, formats->dataArray[j].dataHandle);
}
free(formats->dataArray);
return FALSE;
}
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
// Restores the data in the clipboard from `formats` that was generated by
// StoreClipboardData, and frees allocated data.
BOOL RestoreClipboardData(ClipboardData* formats)
{
if(!OpenClipboard(NULL))
return FALSE;
EmptyClipboard();
for(int i = 0; i < formats->count; i++)
{
SetClipboardData(formats->dataArray[i].format, formats->dataArray[i].dataHandle);
}
CloseClipboard();
free(formats->dataArray);
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
// Gets unicode text from the clipboard.
// You must free the returned string when you don't need it anymore.
WCHAR* GetClipboardText()
{
if(!OpenClipboard(NULL))
return NULL;
WCHAR* text = NULL;
HANDLE handle = GetClipboardData(CF_UNICODETEXT);
if(handle)
{
WCHAR* clipboardText = (WCHAR*)GlobalLock(handle);
if(clipboardText)
{
size_t size = sizeof(WCHAR) * (wcslen(clipboardText) + 1);
text = (WCHAR*)malloc(size);
if(text)
memcpy(text, clipboardText, size);
GlobalUnlock(handle);
}
}
CloseClipboard();
return text;
}
///////////////////////////////////////////////////////////////////////////////
// Puts unicode text on the clipboard
BOOL SetClipboardText(const WCHAR* text)
{
if(!OpenClipboard(NULL))
return FALSE;
BOOL bSucceeded = FALSE;
size_t size = sizeof(WCHAR) * (wcslen(text) + 1);
HANDLE handle = GlobalAlloc(GHND, size);
if(handle)
{
WCHAR* clipboardText = (WCHAR*)GlobalLock(handle);
if(clipboardText)
{
memcpy(clipboardText, text, size);
GlobalUnlock(handle);
bSucceeded = EmptyClipboard() &&
SetClipboardData(CF_UNICODETEXT, handle);
}
if(!bSucceeded)
GlobalFree(handle);
}
CloseClipboard();
return bSucceeded;
}
///////////////////////////////////////////////////////////////////////////////
// Simulates a key combination (such as Ctrl+X) in the active window
void SendKeyCombo(BYTE vk, BOOL ctrl, BOOL alt, BOOL shift)
{
BYTE vkModifiers[3] = { VK_CONTROL, VK_MENU, VK_SHIFT };
BOOL bModRequested[3] = { ctrl != 0, alt != 0, shift != 0 };
BOOL bModPressed[3];
BOOL bKeyPressed;
for(int i = 0; i < 3; i++)
bModPressed[i] = GetKeyState(vkModifiers[i]) < 0;
bKeyPressed = GetKeyState(vk) < 0;
for(int i = 0; i < 3; i++)
{
if(bModRequested[i] != bModPressed[i])
keybd_event(vkModifiers[i], 0, bModPressed[i] ? KEYEVENTF_KEYUP : 0, 0);
}
if(!bKeyPressed)
{
keybd_event(vk, 0, 0, 0);
keybd_event(vk, 0, KEYEVENTF_KEYUP, 0);
}
else
{
keybd_event(vk, 0, KEYEVENTF_KEYUP, 0);
keybd_event(vk, 0, 0, 0);
}
for(int i = 2; i >= 0; i--)
{
if(bModRequested[i] != bModPressed[i])
keybd_event(vkModifiers[i], 0, bModPressed[i] ? 0 : KEYEVENTF_KEYUP, 0);
}
}
///////////////////////////////////////////////////////////////////////////////
// Simulates Alt+Shift to change languages
void SendAltShift()
{
BYTE vkModifiers[3] = { VK_CONTROL, VK_MENU, VK_SHIFT };
BOOL bModRequested[3] = { FALSE, TRUE, TRUE };
BOOL bModPressed[3];
for(int i = 0; i < 3; i++)
bModPressed[i] = GetKeyState(vkModifiers[i]) < 0;
for(int i = 0; i < 3; i++)
{
if(bModRequested[i] != bModPressed[i])
keybd_event(vkModifiers[i], 0, bModPressed[i] ? KEYEVENTF_KEYUP : 0, 0);
}
if(bModRequested[2] && bModPressed[2])
{
keybd_event(vkModifiers[2], 0, KEYEVENTF_KEYUP, 0);
keybd_event(vkModifiers[2], 0, 0, 0);
}
for(int i = 2; i >= 0; i--)
{
if(bModRequested[i] != bModPressed[i])
keybd_event(vkModifiers[i], 0, bModPressed[i] ? 0 : KEYEVENTF_KEYUP, 0);
}
}