1 /********************************************************************************
2 * *
3 * K e y b o a r d H a n d l i n g *
4 * *
5 *********************************************************************************
6 * Copyright (C) 1997,2006 by Jeroen van der Zijp. All Rights Reserved. *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or *
9 * modify it under the terms of the GNU Lesser General Public *
10 * License as published by the Free Software Foundation; either *
11 * version 2.1 of the License, or (at your option) any later version. *
12 * *
13 * This library is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
16 * Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public *
19 * License along with this library; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
21 *********************************************************************************
22 * $Id: fxkeyboard.cpp,v 1.14.2.2 2006/04/14 01:21:01 fox Exp $ *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "fxkeys.h"
28 #include "fxpriv.h"
29 #include "FXHash.h"
30 #include "FXThread.h"
31 #include "FXStream.h"
32 #include "FXObject.h"
33 #include "FXDict.h"
34 #include "FXString.h"
35 #include "FXSettings.h"
36 #include "FXRegistry.h"
37 #include "FXSize.h"
38 #include "FXPoint.h"
39 #include "FXRectangle.h"
40 #include "FXApp.h"
41 #include "FXId.h"
42 #include "FXDrawable.h"
43 #include "FXWindow.h"
44
45 /*
46 Notes:
47
48 - Translate key cap codes to FOX key symbols on MS-Windows.
49 - Windows code first written by Daniel Gehriger <gehriger@linkcad.com.
50 */
51
52 using namespace FX;
53
54 /*******************************************************************************/
55
56 namespace FX {
57
58
59 #ifdef WIN32
60
61 //-------------------------------------------------------------------------
62 //
63 // Notes (Daniel Gehriger <gehriger@linkcad.com>:
64 //
65 //
66 // [In the comments below, WM_KEYx refers to WM_KEYDOWN|WM_SYSKEYDOWN|
67 // WM_KEYUP|WM_SYSKEYUP; WM_CHAR refers to WM_CHAR and WM_IME_CHAR]
68 //
69 // Notes
70 // =====
71 //
72 // 1. MapVirtualKeyEx and ToAsciiEx don't work in Korean Win98 (although
73 // they're fine in other Korean Windows products); MapVirtualKey and
74 // ToAscii work fine.
75 //
76 //
77 // 2. Some keyboard layouts use the Right-ALT key as a 2nd level
78 // shift key, and label it as AltGr. With these layouts, it is IMPOSSIBLE
79 // to distinguish between "Ctrl + AltGr + Key" and "AltGr + Key" during the
80 // WM_KEYDOWN messages. Neither the keyboard state nor the messages differ
81 // between the two key sequences.
82 //
83 // Implications: suppose some menu command has been bound to "Ctrl + }". On my
84 // Swiss keyboard, I have to type "AltGr + $" to get the right bracket. This means
85 // that FOX receives the sames messages if I type "}" or "Left-Ctrl + }" !
86 //
87 // Solution: I wrote a function winkeyCheckLayout() that checks if a keyboard
88 // layout uses the Right-Alt key as AltGr. If it does, I interpret any combination
89 // of "Left-Ctrl + AltGr" as "AltGr", i.e. I drop the Left-Ctrl key. Keyboard
90 // accelerators can still be reached with the "Right-Ctrl".
91 //
92 //
93 // 3. Related to the previous comment: how can we distinguish between left
94 // and right Alt / Ctrl / Shift ? Windows 95/98/Me do not set the
95 // corresponding VK_L*/VK_R* keyboard states, as NT does. We must look
96 // at bit 24, the "extended bit" of lParam in the window messages. The code
97 // below goes to great lenghts to correctly determine the state of the
98 // left / right keys on these systems.
99 //
100 //
101 // 4. The Win32 function ToAscii() has internal state. This means that calling this
102 // function twice with the same arguments does NOT generally yield the same result.
103 // For instance, if the circumflex (^) is used to compose characters in the
104 // selected keyboard layout, and one types '^' followed by 'e', calling
105 // ToAscii() to translate the 'e' yields '�' (e-grave). Calling ToAscii() again,
106 // however, yields a raw 'e'. Also, I found that the Win32 function
107 // TranslateMessage(), which is used to have Win32 generate WM_CHAR messages,
108 // calls ToAscii() internally.
109 //
110 // The implications of this are :
111 //
112 // 1. Either use TranslateMessage() OR use ToAscii() when processing the above
113 // messages. Calling both breaks keyboard processing !!!!
114 //
115 // 2. Only call ToAscii() when a WM_KEYUP/WM_KEYDOWN/WM_SYSKEYUP/WM_SYSKEYDOWN
116 // message has been received.
117 //
118 //
119 // 5. Keyboard accelerators: they are used to trigger some command by typing
120 // a character sequence. Some accelerator sequences include the Shift key:
121 //
122 // Example: "Ctrl + {" versus "Ctrl + Shift + {".
123 //
124 // However, on some keyboards, some of the characters in the accelerator
125 // sequences themselves require the Shift key as a character modifier.
126 //
127 // Example: the US keyboard layout requires "Shift + [" to type "{".
128 //
129 // There is NO workaround, except not using Shift in accelerator sequences
130 // that include non-letters.
131 //
132 //-------------------------------------------------------------------------
133
134 // Keyboard map for control keys
135 static const FXuint keymapCtl[] = {
136 VK_PRIOR, KEY_Prior,
137 VK_NEXT, KEY_Next,
138 VK_END, KEY_End,
139 VK_HOME, KEY_Home,
140 VK_LEFT, KEY_Left,
141 VK_UP, KEY_Up,
142 VK_RIGHT, KEY_Right,
143 VK_DOWN, KEY_Down,
144 VK_INSERT, KEY_Insert,
145 VK_DELETE, KEY_Delete,
146 VK_HELP, KEY_Help,
147 VK_F1, KEY_F1,
148 VK_F2, KEY_F2,
149 VK_F3, KEY_F3,
150 VK_F4, KEY_F4,
151 VK_F5, KEY_F5,
152 VK_F6, KEY_F6,
153 VK_F7, KEY_F7,
154 VK_F8, KEY_F8,
155 VK_F9, KEY_F9,
156 VK_F10, KEY_F10,
157 VK_F11, KEY_F11,
158 VK_F12, KEY_F12,
159 VK_F13, KEY_F13,
160 VK_F14, KEY_F14,
161 VK_F15, KEY_F15,
162 VK_F16, KEY_F16,
163 VK_F17, KEY_F17,
164 VK_F18, KEY_F18,
165 VK_F19, KEY_F19,
166 VK_F20, KEY_F20,
167 VK_F21, KEY_F21,
168 VK_F22, KEY_F22,
169 VK_F23, KEY_F23,
170 VK_F24, KEY_F24,
171 VK_SCROLL, KEY_Scroll_Lock,
172 VK_CLEAR, KEY_Begin,
173 VK_CAPITAL, KEY_Caps_Lock,
174 VK_NUMLOCK, KEY_Num_Lock,
175 VK_SNAPSHOT, KEY_Print,
176 VK_CANCEL, KEY_Break,
177 VK_PAUSE, KEY_Pause,
178 VK_BACK, KEY_BackSpace,
179 VK_TAB, KEY_Tab,
180 VK_ESCAPE, KEY_Escape,
181 VK_SPACE, KEY_space,
182 VK_MULTIPLY, KEY_KP_Multiply,
183 VK_ADD, KEY_KP_Add,
184 VK_SEPARATOR, KEY_KP_Separator,
185 VK_SUBTRACT, KEY_KP_Subtract,
186 VK_DECIMAL, KEY_KP_Decimal,
187 VK_DIVIDE, KEY_KP_Divide,
188 VK_NUMPAD0, KEY_KP_0,
189 VK_NUMPAD1, KEY_KP_1,
190 VK_NUMPAD2, KEY_KP_2,
191 VK_NUMPAD3, KEY_KP_3,
192 VK_NUMPAD4, KEY_KP_4,
193 VK_NUMPAD5, KEY_KP_5,
194 VK_NUMPAD6, KEY_KP_6,
195 VK_NUMPAD7, KEY_KP_7,
196 VK_NUMPAD8, KEY_KP_8,
197 VK_NUMPAD9, KEY_KP_9,
198 VK_LWIN, KEY_Super_L,
199 VK_RWIN, KEY_Super_R
200 };
201
202
203 #define KEYDOWN(ks,vk) (((ks)[vk]&0x80)!=0)
204 #define KEYUP(ks,vk) (((ks)[vk]&0x80)==0)
205 #define KEYTOGGLED(ks,vk) (((ks)[vk]&0x01)!=0)
206
207
208 // True if OS does not distinguish between left & right Alt/Ctrl/Shift keys
209 static FXbool bNoLR=FALSE;
210 static BYTE ksRShft=0;
211 static BYTE ksRCtrl=0;
212 static BYTE ksLMenu=0;
213 static BYTE ksRMenu=0;
214
215
216 // Retrieves the current input code page
wkbGetCodePage()217 UINT wkbGetCodePage(){
218 static HKL hklOld=NULL;
219 static UINT uCPID=0;
220 HKL hkl=GetKeyboardLayout(0);
221 if(hklOld!=hkl || uCPID==0){
222 hklOld=hkl;
223 char lpLCData[256];
224 if(GetLocaleInfoA(LANGIDFROMLCID(LOWORD(hkl)),LOCALE_IDEFAULTANSICODEPAGE,lpLCData,sizeof(lpLCData))==0) return CP_ACP;
225 uCPID=atoi(lpLCData);
226 }
227 return uCPID;
228 }
229
230
231 // Checks if the right-hand ALT key is used as a 2nd shift key
wkbAltGrDown(PBYTE ks)232 static FXbool wkbAltGrDown(PBYTE ks){
233 static FXbool bHasAltGr=FALSE;
234 static HKL hklOld = NULL;
235 HKL hkl=GetKeyboardLayout(0);
236 if(hklOld!=hkl){
237 hklOld=hkl;
238 bHasAltGr=FALSE;
239 for(FXuint ch=0x20; ch<=0xff ; ++ch){
240 // <MSDN>
241 // For keyboard layouts that use the right-hand ALT key as a shift key
242 // (for example, the French keyboard layout), the shift state is
243 // represented by the value 6, because the right-hand ALT key is
244 // converted internally into CTRL+ALT.
245 // </MSDN>
246 if(HIBYTE(VkKeyScanEx(ch,hkl))==6){
247 bHasAltGr=TRUE;
248 break;
249 }
250 }
251 }
252 if(bNoLR)
253 return bHasAltGr && KEYDOWN(ks,VK_MENU) && ksRMenu;
254 else
255 return bHasAltGr /* && KEYDOWN(ks, VK_LCONTROL) */ && KEYDOWN(ks,VK_RMENU);
256 }
257
258
259 // Return the current state of the modifier keys and mouse buttons
fxmodifierkeys()260 unsigned int fxmodifierkeys(){
261 FXuint state=0;
262 BYTE ks[256];
263 GetKeyboardState(ks);
264 if(KEYDOWN(ks,VK_SHIFT)) state|=SHIFTMASK;
265 if(KEYTOGGLED(ks,VK_CAPITAL)) state|=CAPSLOCKMASK;
266 if(KEYTOGGLED(ks,VK_NUMLOCK)) state|=NUMLOCKMASK;
267 if(KEYTOGGLED(ks,VK_SCROLL)) state|=SCROLLLOCKMASK;
268 if(KEYDOWN(ks,VK_LBUTTON)) state|=LEFTBUTTONMASK;
269 if(KEYDOWN(ks,VK_MBUTTON)) state|=MIDDLEBUTTONMASK;
270 if(KEYDOWN(ks,VK_RBUTTON)) state|=RIGHTBUTTONMASK;
271 if(KEYDOWN(ks,VK_LWIN)) state|=METAMASK; // Added JVZ
272 if(KEYDOWN(ks,VK_RWIN)) state|=METAMASK;
273 if(wkbAltGrDown(ks)){
274 // Left-Ctrl + Right-Alt = AltGr is used to compose characters;
275 // If AltGr is pressed, only allow Right-Control & Left-Alt
276 if(ksRCtrl) state|=CONTROLMASK;
277 if(ksLMenu) state|=ALTMASK;
278 }
279 else{
280 if(KEYDOWN(ks,VK_CONTROL)) state|=CONTROLMASK;
281 if(KEYDOWN(ks,VK_MENU)) state|=ALTMASK;
282 }
283 return state;
284 }
285
286 //VK_LWIN (5B)
287 //Left Windows key (Microsoft Natural keyboard)
288
289 //VK_RWIN (5C)
290 //Right Windows key (Natural keyboard)
291
292 // Map Win32 virtual key codes to FOX key codes
wkbMapKeyCode(UINT iMsg,WPARAM uVirtKey,LPARAM lParam)293 FXuint wkbMapKeyCode(UINT iMsg,WPARAM uVirtKey,LPARAM lParam){
294 BYTE ks[256];
295 char c;
296
297 // Get keyboard state
298 if(GetKeyboardState(ks)!=0){
299
300 // Determine left/right key states
301 BYTE ksOldRShft=ksRShft;
302 BYTE ksOldRCtrl=ksRCtrl;
303 BYTE ksOldRMenu=ksRMenu;
304
305 FXuint xt=HIWORD(lParam)&KF_EXTENDED;
306
307 if(!bNoLR && iMsg==WM_KEYDOWN){
308 if(uVirtKey==VK_CONTROL) bNoLR|=(KEYDOWN(ks,xt ? VK_RCONTROL : VK_LCONTROL) ^ KEYDOWN(ks,VK_CONTROL));
309 if(uVirtKey==VK_MENU) bNoLR|=(KEYDOWN(ks,xt ? VK_RMENU : VK_LMENU) ^ KEYDOWN(ks,VK_MENU));
310 }
311
312 // OS does not save correct left/right key states (most Win95/98/Me)
313 if(bNoLR){
314 ksRShft = ks[VK_RSHIFT];
315 ksRCtrl = (KEYDOWN(ks,VK_CONTROL) ? (uVirtKey==VK_CONTROL&&xt) ? 0x80 : ksRCtrl : 0x00);
316 ksRMenu = (KEYDOWN(ks,VK_MENU) ? (uVirtKey==VK_MENU && xt) ? 0x80 : ksRMenu : 0x00);
317 ksLMenu = (KEYDOWN(ks,VK_MENU) ? (uVirtKey==VK_MENU && !xt) ? 0x80 : ksLMenu : 0x00);
318 }
319
320 // OS saves correct left/right key states
321 else{
322 ksRShft = KEYDOWN(ks,VK_RSHIFT);
323 ksRCtrl = KEYDOWN(ks,VK_RCONTROL);
324 ksRMenu = KEYDOWN(ks,VK_RMENU);
325 ksLMenu = KEYDOWN(ks,VK_LMENU);
326 }
327
328 // Map virtual key code
329 switch(uVirtKey){
330 case VK_SHIFT:
331 if(iMsg==WM_KEYDOWN && HIWORD(lParam)&KF_REPEAT)
332 return KEY_VoidSymbol;
333 else
334 return (ksRShft^ksOldRShft) ? KEY_Shift_R : KEY_Shift_L;
335
336 case VK_CONTROL:
337 if(iMsg==WM_KEYDOWN && HIWORD(lParam)&KF_REPEAT)
338 return KEY_VoidSymbol;
339 else
340 return (ksRCtrl^ksOldRCtrl) ? KEY_Control_R : KEY_Control_L;
341
342 case VK_MENU:
343 if((iMsg==WM_KEYDOWN || iMsg==WM_SYSKEYDOWN) && (HIWORD(lParam)&KF_REPEAT)) // Patch Jon Sargeant <delta17@cox.net>
344 return KEY_VoidSymbol;
345 else
346 return (ksRMenu^ksOldRMenu) ? KEY_Alt_R : KEY_Alt_L;
347
348 case VK_RETURN:
349 if(uVirtKey==VK_RETURN && (HIWORD(lParam)&KF_EXTENDED))
350 return KEY_KP_Enter;
351 else
352 return KEY_Return;
353
354 default:
355
356 // Map remaining special keys
357 for(FXuint i=0; i<ARRAYNUMBER(keymapCtl)/2; ++i){
358 if(keymapCtl[2*i]==uVirtKey){
359 return keymapCtl[2*i+1];
360 }
361 }
362
363 // Map characters (they come in as uppercase,
364 // but FOX wants them converted according the current shift state...
365 if('A'<=uVirtKey && uVirtKey<='Z'){
366 return (FXuint)(KEYDOWN(ks,VK_SHIFT) ? uVirtKey : uVirtKey-'A'+KEY_a);
367 }
368
369 // Ask Windows to map remaining characters
370 c=(char)LOWORD(MapVirtualKeyEx(uVirtKey,2,GetKeyboardLayout(0))); // FIXME ";" and ":" map to same keysym; this is wrong!
371 if(c) return c;
372 }
373 }
374 return KEY_VoidSymbol;
375 }
376
377
378 // Windows 9x don't support ToUnicodeEx (but Windows 98/Me export the function
379 // as a no-op from User32.DLL. The following code determines upon the first call
380 // to fxToUnicodeEx if the OS supports ToUnicodeEx and fixes the function pointer
381 // to send subsequent calls directly to the OS. Otherwise, a stub is used.
382 int WINAPI wkbToUnicodeExStub(UINT,UINT,const BYTE*,LPWSTR,int,UINT,HKL);
383 int WINAPI wkbToUnicodeExWin9x(UINT,UINT,const BYTE*,LPWSTR,int,UINT,HKL);
384
385 typedef int (WINAPI *PFN_TOUNICODEEX)(UINT,UINT,const BYTE*,LPWSTR,int,UINT,HKL);
386
387 PFN_TOUNICODEEX ToUnicodeEx=wkbToUnicodeExStub;
388
389 // Stub function for first call to fxToUnicodeEx()
wkbToUnicodeExStub(UINT uVirtKey,UINT uScanCode,const BYTE * lpKeyState,LPWSTR pwszBuff,int cchBuff,UINT wFlags,HKL dwhkl)390 int WINAPI wkbToUnicodeExStub(UINT uVirtKey,UINT uScanCode,const BYTE* lpKeyState,LPWSTR pwszBuff,int cchBuff,UINT wFlags,HKL dwhkl){
391 OSVERSIONINFOA osinfo={sizeof(OSVERSIONINFOA)};
392 GetVersionExA(&osinfo);
393 if(osinfo.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS){
394 // Windows 9x/Me => use stub
395 ToUnicodeEx = wkbToUnicodeExWin9x;
396 }
397 else{
398 // Windows NT => forward to OS
399 HMODULE user32Dll=LoadLibraryA("user32");
400 ToUnicodeEx=(PFN_TOUNICODEEX)GetProcAddress(user32Dll,"ToUnicodeEx");
401 if(!ToUnicodeEx){ToUnicodeEx=wkbToUnicodeExWin9x;}
402 }
403 return ToUnicodeEx(uVirtKey, uScanCode, lpKeyState, pwszBuff, cchBuff, wFlags, dwhkl);
404 }
405
406
407 // Adapter function for Windows 9x/Me
wkbToUnicodeExWin9x(UINT uVirtKey,UINT uScanCode,const BYTE * lpKeyState,LPWSTR pwszBuff,int cchBuff,UINT wFlags,HKL dwhkl)408 int WINAPI wkbToUnicodeExWin9x(UINT uVirtKey,UINT uScanCode,const BYTE* lpKeyState,LPWSTR pwszBuff,int cchBuff,UINT wFlags,HKL dwhkl){
409 WORD c;
410 int cnt=ToAsciiEx(uVirtKey,uScanCode,(BYTE*)lpKeyState,&c,wFlags,dwhkl);
411 if(cnt<=0) return cnt;
412 return MultiByteToWideChar(CP_ACP,0,(LPCSTR)&c,cnt,pwszBuff,cchBuff);
413 }
414
415
416 #endif
417
418 }
419