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
}
|