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 3297 2015-12-14 20:30:04Z arthurcnorman $                      *
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