From 3c9ee8524f125e74b7f70ef4f396211892194005 Mon Sep 17 00:00:00 2001 From: sekelsenmat Date: Fri, 16 Dec 2011 08:22:27 +0000 Subject: [PATCH] Adds the new Application.ExtendedKeysSupport flag and advances the implementation of keyboard support in LCL-CustomDrawn-Cocoa git-svn-id: trunk@34219 - --- lcl/forms.pp | 2 + lcl/interfaces/customdrawn/cocoaprivate.pas | 268 +++++++----------- .../customdrawn/customdrawnobject_cocoa.inc | 2 + lcl/lclproc.pas | 17 ++ lcl/lcltype.pp | 31 +- 5 files changed, 144 insertions(+), 176 deletions(-) diff --git a/lcl/forms.pp b/lcl/forms.pp index f8ec5593e8..b506df9ddb 100644 --- a/lcl/forms.pp +++ b/lcl/forms.pp @@ -1239,6 +1239,7 @@ type FComponentsToRelease: TFPList; FComponentsReleasing: TFPList; FCreatingForm: TForm;// currently created form (CreateForm), candidate for MainForm + FExtendedKeysSupport: Boolean; FFindGlobalComponentEnabled: boolean; FFlags: TApplicationFlags; FHint: string; @@ -1469,6 +1470,7 @@ type property BidiMode: TBiDiMode read FBidiMode write SetBidiMode; property CaptureExceptions: boolean read FCaptureExceptions write SetCaptureExceptions; + property ExtendedKeysSupport: Boolean read FExtendedKeysSupport write FExtendedKeysSupport; // See VK_LSHIFT in LCLType for more details property FindGlobalComponentEnabled: boolean read FFindGlobalComponentEnabled write FFindGlobalComponentEnabled; property Flags: TApplicationFlags read FFlags write SetFlags; diff --git a/lcl/interfaces/customdrawn/cocoaprivate.pas b/lcl/interfaces/customdrawn/cocoaprivate.pas index 41c4cc2a5b..8a0511972a 100644 --- a/lcl/interfaces/customdrawn/cocoaprivate.pas +++ b/lcl/interfaces/customdrawn/cocoaprivate.pas @@ -47,7 +47,7 @@ type // Keyboard events procedure keyDown(theEvent: NSEvent); override; procedure keyUp(theEvent: NSEvent); override; - function MacKeyCodeToLCLKey(AKey: Word; var SendKeyUpDown, SendChar: Boolean): Word; message 'MacKeyCodeToLCLKey:sendkey:sendchar:'; + function MacKeyCodeToLCLKey(AKeyEvent: NSEvent; var SendKeyUpDown, SendChar: Boolean; var AUTF8Char: TUTF8Char): Word; message 'MacKeyCodeToLCLKey:sendkey:sendchar:AUTF8Char:'; // function lclIsVisible: Boolean; message 'lclIsVisible'; procedure lclInvalidateRect(const r: TRect); message 'lclInvalidateRect:'; @@ -352,12 +352,14 @@ end; procedure TCocoaForm.keyDown(theEvent: NSEvent); var lKey: Word; + lUTF8Char: TUTF8Char; lSendKey, lSendChar: Boolean; begin inherited keyDown(theEvent); - lKey := MacKeyCodeToLCLKey(theEvent.keyCode(), lSendKey, lSendChar); - DebugLn('KeyDown='+IntToHex(theEvent.keyCode(), 4)); + lKey := MacKeyCodeToLCLKey(theEvent, lSendKey, lSendChar, lUTF8Char); + //DebugLn('KeyDown='+IntToHex(theEvent.keyCode(), 4)); if lSendKey then CallbackKeyDown(WindowHandle, lKey); + if lSendChar then CallbackKeyChar(WindowHandle, 0, lUTF8Char); end; procedure TCocoaForm.keyUp(theEvent: NSEvent); @@ -367,17 +369,19 @@ var lSendKey, lSendChar: Boolean; begin inherited keyUp(theEvent); - lKey := MacKeyCodeToLCLKey(theEvent.keyCode(), lSendKey, lSendChar); + lKey := MacKeyCodeToLCLKey(theEvent, lSendKey, lSendChar, lUTF8Char); if lSendKey then CallbackKeyUp(WindowHandle, lKey); - if lSendChar then CallbackKeyChar(WindowHandle, 0, lUTF8Char); end; -function TCocoaForm.MacKeyCodeToLCLKey(AKey: Word; var SendKeyUpDown, SendChar: Boolean): Word; -(*var - KeyCode, DeadKeys: UInt32; - TextLen : UInt32; - CharLen : integer; - widebuf: array[1..2] of widechar; +function TCocoaForm.MacKeyCodeToLCLKey(AKeyEvent: NSEvent; var SendKeyUpDown, SendChar: Boolean; var AUTF8Char: TUTF8Char): Word; +var + KeyCode: Word; + ExtendedKeysSupport: Boolean; +{ TextLen : UInt32; + CharLen : integer;} + characters: string; + charactersIgnoringModifiers: string; +(* widebuf: array[1..2] of widechar; U: Cardinal; Layout: UCKeyboardLayoutPtr; KeyboardLayout: KeyboardLayoutRef;*) @@ -385,17 +389,19 @@ begin SendKeyUpDown := False; SendChar := False; Result := VK_UNKNOWN; + AUTF8Char := ''; + ExtendedKeysSupport := Application.ExtendedKeysSupport; + + // Obtain data from the event + KeyCode := AKeyEvent.keyCode(); + characters := NSStringToString(AKeyEvent.characters()); + charactersIgnoringModifiers := NSStringToString(AKeyEvent.charactersIgnoringModifiers()); -// KeyData:=GetCarbonMsgKeyState; // IsSysKey:=(GetCurrentEventKeyModifiers and cmdKey)>0; -{ if OSError(GetEventParameter(AEvent, kEventParamKeyCode, typeUInt32, nil, - Sizeof(KeyCode), nil, @KeyCode), SName, AGetEvent, - 'kEventParamKeyCode') then Exit;} - //non-printable keys (see mackeycodes.inc) //for these keys, only send keydown/keyup (not char or UTF8KeyPress) - case AKey of + case KeyCode of MK_F1 : Result:=VK_F1; MK_F2 : Result:=VK_F2; MK_F3 : Result:=VK_F3; @@ -424,6 +430,13 @@ begin MK_LEFT : Result:= VK_LEFT; MK_RIGHT : Result:= VK_RIGHT; MK_NUMLOCK : Result:= VK_NUMLOCK; + MK_BACKSPACE: Result:= VK_BACK; +// MK_CAPSLOCK : Result:= VK_CAPSLOCK; <- We don't seam to receive it at all + //Modifiers codes - you'll never get these directly + {MK_SHIFTKEY = $38; + MK_CTRL = $3B; + MK_ALT = $3A; MK_OPTION = MK_ALT; + MK_COMMAND = $37; MK_APPLE = MK_COMMAND;} end; if Result<>VK_UNKNOWN then @@ -433,167 +446,76 @@ begin Exit; end; -(* // get untranslated key (key without modifiers) - OSError(KLGetCurrentKeyboardLayout(KeyboardLayout), SName, 'KLGetCurrentKeyboardLayout'); - OSError(KLGetKeyboardLayoutProperty(KeyboardLayout, kKLuchrData, Layout), SName, 'KLGetKeyboardLayoutProperty'); - {$IFDEF VerboseKeyboard} - DebugLn('[Keyboard layout] UCHR layout = ', DbgS(Layout)); - {$ENDIF} + // Now other keys which might also send utf8keypress - TextLen:=0; - DeadKeys:=0; - UTF8VKCharacter:=''; - VKKeyChar:=#0; - CharLen:=0; - - if Layout <> nil then - begin - OSError(UCKeyTranslate(Layout^, KeyCode, kUCKeyActionDisplay, - 0, LMGetKbdType, - kUCKeyTranslateNoDeadKeysMask, DeadKeys, 6, TextLen, @WideBuf[1]), SName, 'UCKeyTranslate'); - - if TextLen>0 then begin - u:=UTF16CharacterToUnicode(@WideBuf[1],CharLen); - if CharLen>0 then begin - UTF8VKCharacter:=UnicodeToUTF8(u); - if (UTF8VKCharacter<>'') and (ord(Utf8VKCharacter[1])<=127) then //It's (true) ascii. - VKKeyChar:=Utf8VKCharacter[1] - else //not ascii, get the Mac character. - OSError( - GetEventParameter(AEvent, kEventParamKeyMacCharCodes, typeChar, nil, - Sizeof(VKKeyChar), nil, @VKKeyChar), SName, AGetEvent, - 'kEventParamKeyMacCharCodes'); - end; - end; - - TextLen := 0; - - if IsSysKey then - begin // workaround for Command modifier suppressing shift - DeadKeys := 0; - OSError(UCKeyTranslate(Layout^, KeyCode, kUCKeyActionDisplay, - (GetCurrentEventKeyModifiers and not cmdkey) shr 8, LMGetKbdType, - kUCKeyTranslateNoDeadKeysMask, DeadKeys, 6, TextLen, @WideBuf[1]), SName, 'UCKeyTranslate'); - {$IFDEF VerboseKeyboard} - debugln(['TranslateMacKeyCode IsSysKey: TextLen=',TextLen,' CharLen=',CharLen,' UTF8VKCharacter=',UTF8VKCharacter]); - {$ENDIF} - end; - end - else - begin - // uchr style keyboard layouts not always available - fall back to older style - OSError(KLGetKeyboardLayoutProperty(KeyboardLayout, kKLKCHRData, Layout), SName, 'KLGetKeyboardLayoutProperty'); - {$IFDEF VerboseKeyboard} - DebugLn('[Keyboard layout] KCHR layout = ', DbgS(Layout)); - {$ENDIF} - VKKeyChar := Char(KeyTranslate(Layout, KeyCode, DeadKeys) and 255); - { TODO: workaround for Command modifier suppressing shift? } - end; - - {$IFDEF VerboseKeyboard} - debugln(['TranslateMacKeyCode TextLen=',TextLen,' CharLen=',CharLen,' UTF8VKCharacter=',UTF8VKCharacter,' VKKeyChar=',DbgStr(VKKeyChar)]); - {$ENDIF} - - //printable keys - //for these keys, send char or UTF8KeyPress - - if TextLen = 0 then - begin - if OSError( - GetEventParameter(AEvent, kEventParamKeyUnicodes, typeUnicodeText, nil, - 6, @TextLen, @WideBuf[1]), SName, AGetEvent, 'kEventParamKeyUnicodes') then Exit; - end; - - if TextLen>0 then - begin - SendChar:=true; - - u:=UTF16CharacterToUnicode(@WideBuf[1],CharLen); - if CharLen=0 then exit; - UTF8Character:=UnicodeToUTF8(u); - - if (UTF8Character<>'') and (ord(Utf8Character[1])<=127) then //It's (true) ascii. - KeyChar:=Utf8Character[1] - else //not ascii, get the Mac character. - if OSError( - GetEventParameter(AEvent, kEventParamKeyMacCharCodes, typeChar, nil, - Sizeof(KeyChar), nil, @KeyChar), SName, AGetEvent, - 'kEventParamKeyMacCharCodes') then Exit; - - {$IFDEF VerboseKeyboard} - debugln(['TranslateMacKeyCode printable key: TextLen=',TextLen,' UTF8Character=',UTF8Character,' KeyChar=',DbgStr(KeyChar),' VKKeyChar=',DbgStr(VKKeyChar)]); - {$ENDIF} - - // the VKKeyCode is independent of the modifier - // => use the VKKeyChar instead of the KeyChar - case VKKeyChar of - 'a'..'z': VKKeyCode:=VK_A+ord(VKKeyChar)-ord('a'); - 'A'..'Z': VKKeyCode:=ord(VKKeyChar); - #27 : VKKeyCode:=VK_ESCAPE; - #8 : VKKeyCode:=VK_BACK; - ' ' : VKKeyCode:=VK_SPACE; - #13 : VKKeyCode:=VK_RETURN; - '0'..'9': - case KeyCode of - MK_NUMPAD0: VKKeyCode:=VK_NUMPAD0; - MK_NUMPAD1: VKKeyCode:=VK_NUMPAD1; - MK_NUMPAD2: VKKeyCode:=VK_NUMPAD2; - MK_NUMPAD3: VKKeyCode:=VK_NUMPAD3; - MK_NUMPAD4: VKKeyCode:=VK_NUMPAD4; - MK_NUMPAD5: VKKeyCode:=VK_NUMPAD5; - MK_NUMPAD6: VKKeyCode:=VK_NUMPAD6; - MK_NUMPAD7: VKKeyCode:=VK_NUMPAD7; - MK_NUMPAD8: VKKeyCode:=VK_NUMPAD8; - MK_NUMPAD9: VKKeyCode:=VK_NUMPAD9 - else VKKeyCode:=ord(VKKeyChar); - end; - else - case KeyCode of - MK_PADDIV : VKKeyCode:=VK_DIVIDE; - MK_PADMULT : VKKeyCode:=VK_MULTIPLY; - MK_PADSUB : VKKeyCode:=VK_SUBTRACT; - MK_PADADD : VKKeyCode:=VK_ADD; - MK_PADDEC : VKKeyCode:=VK_DECIMAL; - MK_PADENTER: - begin - VKKeyCode:=VK_RETURN; - VKKeyChar:=#13; - UTF8Character:=VKKeyChar; - end; - MK_TILDE: VKKeyCode := VK_OEM_3; - MK_MINUS: VKKeyCode := VK_OEM_MINUS; - MK_EQUAL: VKKeyCode := VK_OEM_PLUS; - MK_BACKSLASH: VKKeyCode := VK_OEM_5; - MK_LEFTBRACKET: VKKeyCode := VK_OEM_4; - MK_RIGHTBRACKET: VKKeyCode := VK_OEM_6; - MK_SEMICOLON: VKKeyCode := VK_OEM_1; - MK_QUOTE: VKKeyCode := VK_OEM_7; - MK_COMMA: VKKeyCode := VK_OEM_COMMA; - MK_PERIOD: VKKeyCode := VK_OEM_PERIOD; - MK_SLASH: VKKeyCode := VK_OEM_2; - end; - end; - - if VKKeyCode=VK_UNKNOWN then + case KeyCode of + MK_NUMPAD0: Result:=VK_NUMPAD0; + MK_NUMPAD1: Result:=VK_NUMPAD1; + MK_NUMPAD2: Result:=VK_NUMPAD2; + MK_NUMPAD3: Result:=VK_NUMPAD3; + MK_NUMPAD4: Result:=VK_NUMPAD4; + MK_NUMPAD5: Result:=VK_NUMPAD5; + MK_NUMPAD6: Result:=VK_NUMPAD6; + MK_NUMPAD7: Result:=VK_NUMPAD7; + MK_NUMPAD8: Result:=VK_NUMPAD8; + MK_NUMPAD9: Result:=VK_NUMPAD9; + MK_PADDIV : Result:=VK_DIVIDE; + MK_PADMULT : Result:=VK_MULTIPLY; + MK_PADSUB : Result:=VK_SUBTRACT; + MK_PADADD : Result:=VK_ADD; + MK_PADDEC : Result:=VK_DECIMAL; + MK_PADENTER: begin - // There is no known VK_ code for this characther. Use a dummy keycode - // (E8, which is unused by Windows) so that KeyUp/KeyDown events will be - // triggered by LCL. - // Note: we can't use the raw mac keycode, since it could collide with - // well known VK_ keycodes (e.g on my italian ADB keyboard, keycode for - // "è" is 33, which is the same as VK_PRIOR) - VKKeyCode:=$E8; + Result:=VK_RETURN; + AUTF8Char:=#13; end; + MK_TILDE: Result := VK_TILDE; // `/~ key + MK_MINUS: Result := VK_MINUS; + MK_EQUAL: Result := VK_LCL_EQUAL; + MK_BACKSLASH: Result := VK_BACKSLASH; + MK_LEFTBRACKET: Result := VK_OPEN_BRAKET; + MK_RIGHTBRACKET: Result := VK_CLOSE_BRAKET; + MK_SEMICOLON: Result := VK_SEMI_COMMA; + MK_QUOTE: Result := VK_QUOTE; + MK_COMMA: Result := VK_LCL_COMMA; + MK_PERIOD: Result := VK_LCL_POINT; + MK_SLASH: Result := VK_LCL_SLASH; + end; - {$IFDEF VerboseKeyboard} - DebugLn('[TranslateMacKeyCode] VKKeyCode=', DbgsVKCode(VKKeyCode), ' Utf8="', - UTF8Character, '" VKKeyChar="', DbgStr(VKKeyChar), '" KeyChar="',DbgStr(KeyChar),'"' ); - {$ENDIF} + if Result<>VK_UNKNOWN then SendKeyUpDown:=true; - Result := True; - end - else DebugLn('[TranslateMacKeyCode] Error Unable to get Unicode char RawKeyCode = ', - DbgsVKCode(KeyCode));*) + if AUTF8Char <> '' then + begin + SendChar := True; + Exit; + end; + + if Length(charactersIgnoringModifiers) = 1 then + begin + case charactersIgnoringModifiers[1] of + 'a'..'z': Result:=VK_A+ord(charactersIgnoringModifiers[1])-ord('a'); + 'A'..'Z': Result:=ord(charactersIgnoringModifiers[1]); + #27 : Result:=VK_ESCAPE; + #8 : Result:=VK_BACK; + ' ' : Result:=VK_SPACE; + #13 : Result:=VK_RETURN; + '0'..'9': Result:=VK_0+ord(charactersIgnoringModifiers[1])-ord('0'); + end; + end; + + if Result<>VK_UNKNOWN then SendKeyUpDown:=true; + + if Length(characters) > 1 then + begin + // ToDo: Merge the accents into the UTF-8 character if necessary + AUTF8Char := characters; + // + SendChar := True; + end; + + if Result = VK_UNKNOWN then + DebugLn('[MacKeyCodeToLCLKey] Unknown Key! KeyCode=%d characters=%s charactersIgnoringModifiers=%s', + [KeyCode, characters, charactersIgnoringModifiers]); end; procedure TCocoaForm.mouseEntered(event: NSEvent); diff --git a/lcl/interfaces/customdrawn/customdrawnobject_cocoa.inc b/lcl/interfaces/customdrawn/customdrawnobject_cocoa.inc index 1dc87c5718..110e8976bd 100644 --- a/lcl/interfaces/customdrawn/customdrawnobject_cocoa.inc +++ b/lcl/interfaces/customdrawn/customdrawnobject_cocoa.inc @@ -36,6 +36,8 @@ begin DebugLn('TCDWidgetSet.AppInit'); {$ENDIF} + Application.ExtendedKeysSupport := True; + delegate:=TCDAppDelegate.alloc; { Creates the application NSApp object } diff --git a/lcl/lclproc.pas b/lcl/lclproc.pas index 8b091f447e..9cbbb8042e 100644 --- a/lcl/lclproc.pas +++ b/lcl/lclproc.pas @@ -2246,6 +2246,23 @@ begin VK_LAUNCH_MEDIA_SELECT: Result:='VK_LAUNCH_MEDIA_SELECT'; VK_LAUNCH_APP1: Result:='VK_LAUNCH_APP1'; VK_LAUNCH_APP2: Result:='VK_LAUNCH_APP2'; + // New keys in 0.9.31+ + VK_LCL_EQUAL: Result:='VK_LCL_EQUAL'; + VK_LCL_COMMA: Result:='VK_LCL_COMMA'; + VK_LCL_POINT: Result:='VK_LCL_POINT'; + VK_LCL_SLASH: Result:='VK_LCL_SLASH'; + VK_SEMI_COMMA:Result:='VK_SEMI_COMMA'; + VK_MINUS :Result:='VK_MINUS'; + VK_OPEN_BRAKET:Result:='VK_OPEN_BRAKET'; + VK_CLOSE_BRAKET:Result:='VK_CLOSE_BRAKET'; + VK_BACKSLASH :Result:='VK_BACKSLASH'; + VK_TILDE :Result:='VK_TILDE'; + VK_QUOTE :Result:='VK_QUOTE'; + // + VK_POWER: Result:='VK_POWER'; + VK_CALL: Result:='VK_CALL'; + VK_ENDCALL: Result:='VK_ENDCALL'; + VK_LCL_AT: Result:='VK_LCL_AT'; else Result:='VK_('+dbgs(c)+')'; end; diff --git a/lcl/lcltype.pp b/lcl/lcltype.pp index 522fe6b895..79f7844bdc 100644 --- a/lcl/lcltype.pp +++ b/lcl/lcltype.pp @@ -532,15 +532,19 @@ const //MWE: And should not be used. // The keys they are on map to another VK -// VK_EQUAL = 187; +// VK_EQUAL = 187; // $BB // VK_COMMA = 188; // VK_POINT = 190; // VK_SLASH = 191; // VK_AT = 192; // VK_L & VK_R - left and right Alt, Ctrl and Shift virtual keys. - // Used only as parameters to GetAsyncKeyState() and GetKeyState(). + // When Application.ExtendedKeysSupport is false, these keys are + // used only as parameters to GetAsyncKeyState() and GetKeyState(). // No other API or message will distinguish left and right keys in this way + // + // When Application.ExtendedKeysSupport is true, these keys will be sent + // on KeyDown / KeyUp instead of the generic VK_SHIFT, VK_CONTROL, etc. VK_LSHIFT = $A0; VK_RSHIFT = $A1; VK_LCONTROL = $A2; @@ -566,6 +570,9 @@ const VK_LAUNCH_MEDIA_SELECT = $B5; VK_LAUNCH_APP1 = $B6; VK_LAUNCH_APP2 = $B7; + + // VK_OEM keys are utilized only when Application.ExtendedKeysSupport is false + // $B8-$B9 Reserved VK_OEM_1 = $BA; // Used for miscellaneous characters; it can vary by keyboard. // For the US standard keyboard, the ';:' key @@ -614,12 +621,30 @@ const VK_UNDEFINED = $FF; // defined by LCL //============================================== -// Now LCL defined keys +// LCL aliases for more clear naming of keys +//============================================== + + VK_LCL_EQUAL = VK_OEM_PLUS; // The "=+" Key + VK_LCL_COMMA = VK_OEM_COMMA; // The ",<" Key + VK_LCL_POINT = VK_OEM_PERIOD;// The ".>" Key + VK_LCL_SLASH = VK_OEM_2; // The "/?" Key + VK_SEMI_COMMA = VK_OEM_1; // The ";:" Key + VK_MINUS = VK_OEM_MINUS; // The "-_" Key + VK_OPEN_BRAKET = VK_OEM_4; // The "[{" Key + VK_CLOSE_BRAKET = VK_OEM_6; // The "]}" Key + VK_BACKSLASH = VK_OEM_5; // The "\|" Key + VK_TILDE = VK_OEM_3; // The "`~" Key + VK_QUOTE = VK_OEM_7; // The "'"" Key + +//============================================== +// New LCL defined keys //============================================== VK_POWER = $100; VK_CALL = $101; VK_ENDCALL = $102; + VK_LCL_AT = $103; // Not equivalent to anything < $FF, will only be sent by a primary "@" key + // but not for a @ key as secondary action of a "2" key for example //============================================== //