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