File: fxkeyboard.cpp

package info (click to toggle)
gogglesmm 1.2.5-6
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 16,812 kB
  • sloc: cpp: 231,960; ansic: 893; xml: 222; makefile: 33
file content (415 lines) | stat: -rw-r--r-- 14,769 bytes parent folder | download | duplicates (2)
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
403
404
405
406
407
408
409
410
411
412
413
414
415
/********************************************************************************
*                                                                               *
*                         K e y b o a r d   H a n d l i n g                     *
*                                                                               *
*********************************************************************************
* Copyright (C) 1997,2022 by Jeroen van der Zijp.   All Rights Reserved.        *
*********************************************************************************
* This library is free software; you can redistribute it and/or modify          *
* it under the terms of the GNU Lesser General Public License as published by   *
* the Free Software Foundation; either version 3 of the License, or             *
* (at your option) any later version.                                           *
*                                                                               *
* This library is distributed in the hope that it will be useful,               *
* but WITHOUT ANY WARRANTY; without even the implied warranty of                *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
* GNU Lesser General Public License for more details.                           *
*                                                                               *
* You should have received a copy of the GNU Lesser General Public License      *
* along with this program.  If not, see <http://www.gnu.org/licenses/>          *
********************************************************************************/
#include "xincs.h"
#include "fxver.h"
#include "fxdefs.h"
#include "fxmath.h"
#include "fxkeys.h"
#include "FXArray.h"
#include "FXHash.h"
#include "FXMutex.h"
#include "FXStream.h"
#include "FXObject.h"
#include "FXString.h"
#include "FXStringDictionary.h"
#include "FXSettings.h"
#include "FXRegistry.h"
#include "FXSize.h"
#include "FXPoint.h"
#include "FXRectangle.h"
#include "FXEvent.h"
#include "FXWindow.h"
#include "FXApp.h"
#include "fxpriv.h"

/*
  Notes:

  - Translate key cap codes to FOX key symbols on MS-Windows.
  - Windows code first written by Daniel Gehriger <gehriger@linkcad.com.
*/

using namespace FX;

/*******************************************************************************/

namespace FX {


#ifdef WIN32

//-------------------------------------------------------------------------
//
// Notes (Daniel Gehriger <gehriger@linkcad.com>:
//
//
//  [In the comments below, WM_KEYx refers to WM_KEYDOWN|WM_SYSKEYDOWN|
//   WM_KEYUP|WM_SYSKEYUP; WM_CHAR refers to WM_CHAR and WM_IME_CHAR]
//
//  Notes
//  =====
//
//  1. MapVirtualKeyEx and ToAsciiEx don't work in Korean Win98 (although
//     they're fine in other Korean Windows products); MapVirtualKey and
//     ToAscii work fine.
//
//
//  2. Some keyboard layouts use the Right-ALT key as a 2nd level
//     shift key, and label it as AltGr. With these layouts, it is IMPOSSIBLE
//     to distinguish between "Ctrl + AltGr + Key" and "AltGr + Key" during the
//     WM_KEYDOWN messages. Neither the keyboard state nor the messages differ
//     between the two key sequences.
//
//     Implications: suppose some menu command has been bound to "Ctrl + }". On my
//     Swiss keyboard, I have to type "AltGr + $" to get the right bracket. This means
//     that FOX receives the sames messages if I type "}" or "Left-Ctrl + }" !
//
//     Solution: I wrote a function winkeyCheckLayout() that checks if a keyboard
//     layout uses the Right-Alt key as AltGr. If it does, I interpret any combination
//     of "Left-Ctrl + AltGr" as "AltGr", i.e. I drop the Left-Ctrl key. Keyboard
//     accelerators can still be reached with the "Right-Ctrl".
//
//
//  3. Related to the previous comment: how can we distinguish between left
//     and right Alt / Ctrl / Shift ? Windows 95/98/Me do not set the
//     corresponding VK_L*/VK_R* keyboard states, as NT does. We must look
//     at bit 24, the "extended bit" of lParam in the window messages. The code
//     below goes to great lenghts to correctly determine the state of the
//     left / right keys on these systems.
//
//
//  4. The Win32 function ToAscii() has internal state. This means that calling this
//     function twice with the same arguments does NOT generally yield the same result.
//     For instance, if the circumflex (^) is used to compose characters in the
//     selected keyboard layout, and one types '^' followed by 'e', calling
//     ToAscii() to translate the 'e' yields '' (e-grave). Calling ToAscii() again,
//     however, yields a raw 'e'. Also, I found that the Win32 function
//     TranslateMessage(), which is used to have Win32 generate WM_CHAR messages,
//     calls ToAscii() internally.
//
//     The implications of this are :
//
//     1. Either use TranslateMessage() OR use ToAscii() when processing the above
//        messages. Calling both breaks keyboard processing !!!!
//
//     2. Only call ToAscii() when a WM_KEYUP/WM_KEYDOWN/WM_SYSKEYUP/WM_SYSKEYDOWN
//        message has been received.
//
//
//  5. Keyboard accelerators: they are used to trigger some command by typing
//     a character sequence. Some accelerator sequences include the Shift key:
//
//     	  Example:  "Ctrl + {"   versus   "Ctrl + Shift + {".
//
//     However, on some keyboards, some of the characters in the accelerator
//     sequences themselves require the Shift key as a character modifier.
//
//         Example: the US keyboard layout requires "Shift + [" to type "{".
//
//     There is NO workaround, except not using Shift in accelerator sequences
//     that include non-letters.
//
//-------------------------------------------------------------------------

// Keyboard map for control keys
static const FXuint keymapCtl[] = {
  VK_PRIOR,     KEY_Prior,
  VK_NEXT,      KEY_Next,
  VK_END,       KEY_End,
  VK_HOME,      KEY_Home,
  VK_LEFT,      KEY_Left,
  VK_UP,        KEY_Up,
  VK_RIGHT,     KEY_Right,
  VK_DOWN,      KEY_Down,
  VK_INSERT,    KEY_Insert,
  VK_DELETE,    KEY_Delete,
  VK_HELP,      KEY_Help,
  VK_F1,        KEY_F1,
  VK_F2,        KEY_F2,
  VK_F3,        KEY_F3,
  VK_F4,        KEY_F4,
  VK_F5,        KEY_F5,
  VK_F6,        KEY_F6,
  VK_F7,        KEY_F7,
  VK_F8,        KEY_F8,
  VK_F9,        KEY_F9,
  VK_F10,       KEY_F10,
  VK_F11,       KEY_F11,
  VK_F12,       KEY_F12,
  VK_F13,       KEY_F13,
  VK_F14,       KEY_F14,
  VK_F15,       KEY_F15,
  VK_F16,       KEY_F16,
  VK_F17,       KEY_F17,
  VK_F18,       KEY_F18,
  VK_F19,       KEY_F19,
  VK_F20,       KEY_F20,
  VK_F21,       KEY_F21,
  VK_F22,       KEY_F22,
  VK_F23,       KEY_F23,
  VK_F24,       KEY_F24,
  VK_SCROLL,    KEY_Scroll_Lock,
  VK_CLEAR,     KEY_Begin,
  VK_CAPITAL,   KEY_Caps_Lock,
  VK_NUMLOCK,   KEY_Num_Lock,
  VK_SNAPSHOT,  KEY_Print,
  VK_CANCEL,    KEY_Break,
  VK_PAUSE,     KEY_Pause,
  VK_BACK,      KEY_BackSpace,
  VK_TAB,       KEY_Tab,
  VK_ESCAPE,    KEY_Escape,
  VK_SPACE,     KEY_space,
  VK_MULTIPLY,  KEY_KP_Multiply,
  VK_ADD,       KEY_KP_Add,
  VK_SEPARATOR, KEY_KP_Separator,
  VK_SUBTRACT,  KEY_KP_Subtract,
  VK_DECIMAL,   KEY_KP_Decimal,
  VK_DIVIDE,    KEY_KP_Divide,
  VK_NUMPAD0,   KEY_KP_0,
  VK_NUMPAD1,   KEY_KP_1,
  VK_NUMPAD2,   KEY_KP_2,
  VK_NUMPAD3,   KEY_KP_3,
  VK_NUMPAD4,   KEY_KP_4,
  VK_NUMPAD5,   KEY_KP_5,
  VK_NUMPAD6,   KEY_KP_6,
  VK_NUMPAD7,   KEY_KP_7,
  VK_NUMPAD8,   KEY_KP_8,
  VK_NUMPAD9,   KEY_KP_9,
  VK_LWIN,      KEY_Super_L,
  VK_RWIN,      KEY_Super_R
  };


#define KEYDOWN(ks,vk)    (((ks)[vk]&0x80)!=0)
#define KEYUP(ks,vk)      (((ks)[vk]&0x80)==0)
#define KEYTOGGLED(ks,vk) (((ks)[vk]&0x01)!=0)


// True if OS does not distinguish between left & right Alt/Ctrl/Shift keys
static FXbool bNoLR=false;
static BYTE ksRShft=0;
static BYTE ksRCtrl=0;
static BYTE ksLMenu=0;
static BYTE ksRMenu=0;


// Retrieves the current input code page
UINT wkbGetCodePage(){
  static HKL hklOld=nullptr;
  static UINT uCPID=0;
  HKL hkl=GetKeyboardLayout(0);
  if(hklOld!=hkl || uCPID==0){
    hklOld=hkl;
    char lpLCData[256];
    if(GetLocaleInfoA(LANGIDFROMLCID(LOWORD(hkl)),LOCALE_IDEFAULTANSICODEPAGE,lpLCData,sizeof(lpLCData))==0) return CP_ACP;
    uCPID=atoi(lpLCData);
    }
  return uCPID;
  }


// Checks if the right-hand ALT key is used as a 2nd shift key
static FXbool wkbAltGrDown(PBYTE ks){
  static FXbool bHasAltGr=false;
  static HKL hklOld = nullptr;
  HKL hkl=GetKeyboardLayout(0);
  if(hklOld!=hkl){
    hklOld=hkl;
    bHasAltGr=false;
    for(FXuint ch=0x20; ch<=0xff ; ++ch){
      // <MSDN>
      // For keyboard layouts that use the right-hand ALT key as a shift key
      // (for example, the French keyboard layout), the shift state is
      // represented by the value 6, because the right-hand ALT key is
      // converted internally into CTRL+ALT.
      // </MSDN>
      if(HIBYTE(VkKeyScanEx(ch,hkl))==6){
        bHasAltGr=true;
        break;
        }
      }
    }
  if(bNoLR)
    return bHasAltGr && KEYDOWN(ks,VK_MENU) && ksRMenu;
  else
    return bHasAltGr /* && KEYDOWN(ks, VK_LCONTROL) */ && KEYDOWN(ks,VK_RMENU);
  }


// Return the current state of the modifier keys and mouse buttons
unsigned int fxmodifierkeys(){
  FXuint state=0;
  BYTE ks[256];
  GetKeyboardState(ks);
  if(KEYDOWN(ks,VK_SHIFT)) state|=SHIFTMASK;
  if(KEYTOGGLED(ks,VK_CAPITAL)) state|=CAPSLOCKMASK;
  if(KEYTOGGLED(ks,VK_NUMLOCK)) state|=NUMLOCKMASK;
  if(KEYTOGGLED(ks,VK_SCROLL)) state|=SCROLLLOCKMASK;
  if(KEYDOWN(ks,VK_LBUTTON)) state|=LEFTBUTTONMASK;
  if(KEYDOWN(ks,VK_MBUTTON)) state|=MIDDLEBUTTONMASK;
  if(KEYDOWN(ks,VK_RBUTTON)) state|=RIGHTBUTTONMASK;
  if(KEYDOWN(ks,VK_LWIN)) state|=METAMASK;		// Added JVZ
  if(KEYDOWN(ks,VK_RWIN)) state|=METAMASK;
  if(wkbAltGrDown(ks)){
    // Left-Ctrl + Right-Alt = AltGr is used to compose characters;
    // If AltGr is pressed, only allow Right-Control & Left-Alt
    if(ksRCtrl) state|=CONTROLMASK;
    if(ksLMenu) state|=ALTMASK;
    }
  else{
    if(KEYDOWN(ks,VK_CONTROL)) state|=CONTROLMASK;
    if(KEYDOWN(ks,VK_MENU)) state|=ALTMASK;
    }
  return state;
  }

//VK_LWIN (5B)
//Left Windows key (Microsoft Natural keyboard)

//VK_RWIN (5C)
//Right Windows key (Natural keyboard)

// Map Win32 virtual key codes to FOX key codes
FXuint wkbMapKeyCode(UINT iMsg,WPARAM uVirtKey,LPARAM lParam){
  BYTE ks[256];
  char c;

  // Get keyboard state
  if(GetKeyboardState(ks)!=0){

    // Determine left/right key states
    BYTE ksOldRShft=ksRShft;
    BYTE ksOldRCtrl=ksRCtrl;
    BYTE ksOldRMenu=ksRMenu;

    FXuint xt=HIWORD(lParam)&KF_EXTENDED;

    if(!bNoLR && iMsg==WM_KEYDOWN){
      if(uVirtKey==VK_CONTROL) bNoLR|=(KEYDOWN(ks,xt ? VK_RCONTROL : VK_LCONTROL) ^ KEYDOWN(ks,VK_CONTROL));
      if(uVirtKey==VK_MENU) bNoLR|=(KEYDOWN(ks,xt ? VK_RMENU : VK_LMENU) ^ KEYDOWN(ks,VK_MENU));
      }

    // OS does not save correct left/right key states (most Win95/98/Me)
    if(bNoLR){
      ksRShft = ks[VK_RSHIFT];
      ksRCtrl = (KEYDOWN(ks,VK_CONTROL) ? (uVirtKey==VK_CONTROL&&xt) ? 0x80 : ksRCtrl : 0x00);
      ksRMenu = (KEYDOWN(ks,VK_MENU) ? (uVirtKey==VK_MENU && xt) ? 0x80 : ksRMenu : 0x00);
      ksLMenu = (KEYDOWN(ks,VK_MENU) ? (uVirtKey==VK_MENU && !xt) ? 0x80 : ksLMenu : 0x00);
      }

    // OS saves correct left/right key states
    else{
      ksRShft = KEYDOWN(ks,VK_RSHIFT);
      ksRCtrl = KEYDOWN(ks,VK_RCONTROL);
      ksRMenu = KEYDOWN(ks,VK_RMENU);
      ksLMenu = KEYDOWN(ks,VK_LMENU);
      }

    // Map virtual key code
    switch(uVirtKey){
      case VK_SHIFT:
        if(iMsg==WM_KEYDOWN && HIWORD(lParam)&KF_REPEAT)
          return KEY_VoidSymbol;
        else
          return (ksRShft^ksOldRShft) ? KEY_Shift_R : KEY_Shift_L;

      case VK_CONTROL:
        if(iMsg==WM_KEYDOWN && HIWORD(lParam)&KF_REPEAT)
          return KEY_VoidSymbol;
        else
          return (ksRCtrl^ksOldRCtrl) ? KEY_Control_R : KEY_Control_L;

      case VK_MENU:
        if((iMsg==WM_KEYDOWN || iMsg==WM_SYSKEYDOWN) && (HIWORD(lParam)&KF_REPEAT))     // Patch Jon Sargeant <delta17@cox.net>
          return KEY_VoidSymbol;
        else
          return (ksRMenu^ksOldRMenu) ? KEY_Alt_R : KEY_Alt_L;

      case VK_RETURN:
        if(uVirtKey==VK_RETURN && (HIWORD(lParam)&KF_EXTENDED))
          return KEY_KP_Enter;
        else
          return KEY_Return;

      default:

        // Map remaining special keys
        for(FXuint i=0; i<ARRAYNUMBER(keymapCtl)/2; ++i){
          if(keymapCtl[2*i]==uVirtKey){
            return keymapCtl[2*i+1];
            }
          }

        // Map characters (they come in as uppercase,
        // but FOX wants them converted according the current shift state...
        if('A'<=uVirtKey && uVirtKey<='Z'){
          return (FXuint)(KEYDOWN(ks,VK_SHIFT) ? uVirtKey : uVirtKey-'A'+KEY_a);
          }

        // Ask Windows to map remaining characters
        c=(char)LOWORD(MapVirtualKeyEx(uVirtKey,2,GetKeyboardLayout(0))); // FIXME ";" and ":" map to same keysym; this is wrong!
        if(c) return c;
      }
    }
  return KEY_VoidSymbol;
  }


// Windows 9x don't support ToUnicodeEx (but Windows 98/Me export the function
// as a no-op from User32.DLL. The following code determines upon the first call
// to fxToUnicodeEx if the OS supports ToUnicodeEx and fixes the function pointer
// to send subsequent calls directly to the OS. Otherwise, a stub is used.
int WINAPI wkbToUnicodeExStub(UINT,UINT,const BYTE*,LPWSTR,int,UINT,HKL);
int WINAPI wkbToUnicodeExWin9x(UINT,UINT,const BYTE*,LPWSTR,int,UINT,HKL);

typedef int (WINAPI *PFN_TOUNICODEEX)(UINT,UINT,const BYTE*,LPWSTR,int,UINT,HKL);

PFN_TOUNICODEEX ToUnicodeEx=wkbToUnicodeExStub;

// Stub function for first call to fxToUnicodeEx()
int WINAPI wkbToUnicodeExStub(UINT uVirtKey,UINT uScanCode,const BYTE* lpKeyState,LPWSTR pwszBuff,int cchBuff,UINT wFlags,HKL dwhkl){
  OSVERSIONINFOA osinfo={sizeof(OSVERSIONINFOA)};
  GetVersionExA(&osinfo);
  if(osinfo.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS){
    // Windows 9x/Me => use stub
    ToUnicodeEx = wkbToUnicodeExWin9x;
    }
  else{
    // Windows NT => forward to OS
    HMODULE user32Dll=LoadLibraryA("user32");
    ToUnicodeEx=(PFN_TOUNICODEEX)GetProcAddress(user32Dll,"ToUnicodeEx");
    if(!ToUnicodeEx){ToUnicodeEx=wkbToUnicodeExWin9x;}
    }
  return ToUnicodeEx(uVirtKey, uScanCode, lpKeyState, pwszBuff, cchBuff, wFlags, dwhkl);
  }


// Adapter function for Windows 9x/Me
int WINAPI wkbToUnicodeExWin9x(UINT uVirtKey,UINT uScanCode,const BYTE* lpKeyState,LPWSTR pwszBuff,int cchBuff,UINT wFlags,HKL dwhkl){
  WORD c;
  int cnt=ToAsciiEx(uVirtKey,uScanCode,(BYTE*)lpKeyState,&c,wFlags,dwhkl);
  if(cnt<=0) return cnt;
  return MultiByteToWideChar(CP_ACP,0,(LPCSTR)&c,cnt,pwszBuff,cchBuff);
  }

#endif

}