1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004 Novell, Inc.
23 // Jackson Harper (jackson@ximian.com)
30 // - dead chars are not translated properly
31 // - There is a lot of potential for optimmization in here
34 using System
.Collections
;
36 using System
.Globalization
;
37 using System
.Runtime
.InteropServices
;
39 namespace System
.Windows
.Forms
{
41 internal class X11Keyboard
{
43 private IntPtr display
;
44 private IntPtr window
;
46 private StringBuilder lookup_buffer
;
47 private int min_keycode
, max_keycode
, keysyms_per_keycode
, syms
;
48 private int [] keyc2vkey
= new int [256];
49 private int [] keyc2scan
= new int [256];
50 private byte [] key_state_table
= new byte [256];
52 private bool num_state
, cap_state
;
53 private bool initialized
;
55 private int NumLockMask
;
56 private int AltGrMask
;
58 public X11Keyboard (IntPtr display
, IntPtr window
)
60 this.display
= display
;
62 lookup_buffer
= new StringBuilder (24);
65 public void EnsureLayoutInitialized ()
70 KeyboardLayouts layouts
= new KeyboardLayouts ();
71 KeyboardLayout layout
= DetectLayout (layouts
);
73 CreateConversionArray (layouts
, layout
);
75 if (!XSupportsLocale ()) {
76 Console
.Error
.WriteLine ("X does not support your locale");
79 if (!XSetLocaleModifiers (String
.Empty
)) {
80 Console
.Error
.WriteLine ("Could not set X locale modifiers");
83 IntPtr xim
= XOpenIM (display
, IntPtr
.Zero
, IntPtr
.Zero
, IntPtr
.Zero
);
84 if (xim
== IntPtr
.Zero
)
85 Console
.Error
.WriteLine ("Could not get XIM");
87 xic
= CreateXic (window
, xim
);
92 public Keys ModifierKeys
{
94 Keys keys
= Keys
.None
;
95 if ((key_state_table
[(int) VirtualKeys
.VK_SHIFT
] & 0x80) != 0)
97 if ((key_state_table
[(int) VirtualKeys
.VK_CONTROL
] & 0x80) != 0)
99 if ((key_state_table
[(int) VirtualKeys
.VK_MENU
] & 0x80) != 0)
105 public void FocusIn (IntPtr focus_window
)
107 if (xic
!= IntPtr
.Zero
)
111 public void FocusOut (IntPtr focus_window
)
113 if (xic
!= IntPtr
.Zero
)
117 public bool ResetKeyState(IntPtr hwnd
, ref MSG msg
) {
118 // FIXME - keep defining events/msg and return true until we've 'restored' all
119 // pending keypresses
120 if ((key_state_table
[(int) VirtualKeys
.VK_SHIFT
] & 0x80) != 0) {
121 key_state_table
[(int) VirtualKeys
.VK_SHIFT
] &= unchecked((byte)~
0x80);
124 if ((key_state_table
[(int) VirtualKeys
.VK_CONTROL
] & 0x80) != 0) {
125 key_state_table
[(int) VirtualKeys
.VK_CONTROL
] &= unchecked((byte)~
0x80);
128 if ((key_state_table
[(int) VirtualKeys
.VK_MENU
] & 0x80) != 0) {
129 key_state_table
[(int) VirtualKeys
.VK_MENU
] &= unchecked((byte)~
0x80);
134 public void KeyEvent (IntPtr hwnd
, XEvent xevent
, ref MSG msg
)
136 EnsureLayoutInitialized ();
141 IntPtr status
= IntPtr
.Zero
;
142 ascii_chars
= LookupString (ref xevent
, 24, out keysym
, out status
);
144 if (((int) keysym
>= (int) MiscKeys
.XK_ISO_Lock
&&
145 (int) keysym
<= (int) MiscKeys
.XK_ISO_Last_Group_Lock
) ||
146 (int) keysym
== (int) MiscKeys
.XK_Mode_switch
) {
147 UpdateKeyState (xevent
);
151 if ((xevent
.KeyEvent
.keycode
>> 8) == 0x10)
152 xevent
.KeyEvent
.keycode
= xevent
.KeyEvent
.keycode
& 0xFF;
154 int event_time
= (int)xevent
.KeyEvent
.time
;
156 if (status
== (IntPtr
) 2) {
157 // Copy chars into a globally accessible var, i don't think
158 // this var is exposed anywhere though, so we can just ignore this
162 AltGrMask
= xevent
.KeyEvent
.state
& (0x6000 | (int) KeyMasks
.ModMasks
);
163 int vkey
= EventToVkey (xevent
);
164 if (vkey
== 0 && ascii_chars
!= 0) {
165 vkey
= (int) VirtualKeys
.VK_NONAME
;
168 switch ((VirtualKeys
) (vkey
& 0xFF)) {
169 case VirtualKeys
.VK_NUMLOCK
:
170 GenerateMessage (VirtualKeys
.VK_NUMLOCK
, 0x45, xevent
.type
, event_time
);
172 case VirtualKeys
.VK_CAPITAL
:
173 GenerateMessage (VirtualKeys
.VK_CAPITAL
, 0x3A, xevent
.type
, event_time
);
176 if (((key_state_table
[(int) VirtualKeys
.VK_NUMLOCK
] & 0x01) == 0) != ((xevent
.KeyEvent
.state
& NumLockMask
) == 0)) {
177 GenerateMessage (VirtualKeys
.VK_NUMLOCK
, 0x45, XEventName
.KeyPress
, event_time
);
178 GenerateMessage (VirtualKeys
.VK_NUMLOCK
, 0x45, XEventName
.KeyRelease
, event_time
);
181 if (((key_state_table
[(int) VirtualKeys
.VK_CAPITAL
] & 0x01) == 0) != ((xevent
.KeyEvent
.state
& (int) KeyMasks
.LockMask
) == 0)) {
182 GenerateMessage (VirtualKeys
.VK_CAPITAL
, 0x3A, XEventName
.KeyPress
, event_time
);
183 GenerateMessage (VirtualKeys
.VK_CAPITAL
, 0x3A, XEventName
.KeyRelease
, event_time
);
189 int bscan
= (keyc2scan
[xevent
.KeyEvent
.keycode
] & 0xFF);
190 KeybdEventFlags dw_flags
= KeybdEventFlags
.None
;
191 if (xevent
.type
== XEventName
.KeyRelease
)
192 dw_flags
|= KeybdEventFlags
.KeyUp
;
193 if ((vkey
& 0x100) != 0)
194 dw_flags
|= KeybdEventFlags
.ExtendedKey
;
195 msg
= SendKeyboardInput ((VirtualKeys
) (vkey
& 0xFF), bscan
, dw_flags
, event_time
);
201 public bool TranslateMessage (ref MSG msg
)
205 if (msg
.message
>= Msg
.WM_KEYFIRST
&& msg
.message
<= Msg
.WM_KEYLAST
)
208 if (msg
.message
!= Msg
.WM_KEYDOWN
&& msg
.message
!= Msg
.WM_SYSKEYDOWN
)
211 EnsureLayoutInitialized ();
215 int tu
= ToUnicode ((int) msg
.wParam
, Control
.HighOrder ((int) msg
.lParam
), out buffer
);
218 message
= (msg
.message
== Msg
.WM_KEYDOWN
? Msg
.WM_CHAR
: Msg
.WM_SYSCHAR
);
219 XplatUI
.PostMessage (msg
.hwnd
, message
, (IntPtr
) buffer
[0], msg
.lParam
);
222 message
= (msg
.message
== Msg
.WM_KEYDOWN
? Msg
.WM_DEADCHAR
: Msg
.WM_SYSDEADCHAR
);
223 XplatUI
.PostMessage (msg
.hwnd
, message
, (IntPtr
) buffer
[0], msg
.lParam
);
230 public int ToKeycode(int key
)
234 if (nonchar_vkey_key
[key
] > 0)
235 keycode
= XKeysymToKeycode (display
, nonchar_vkey_key
[key
]);
238 keycode
= XKeysymToKeycode (display
, key
);
243 public int ToUnicode (int vkey
, int scan
, out string buffer
)
245 if ((scan
& 0x8000) != 0) {
246 buffer
= String
.Empty
;
250 XEvent e
= new XEvent ();
251 e
.AnyEvent
.type
= XEventName
.KeyPress
;
252 e
.KeyEvent
.display
= display
;
253 e
.KeyEvent
.keycode
= 0;
254 e
.KeyEvent
.state
= 0;
256 if ((key_state_table
[(int) VirtualKeys
.VK_SHIFT
] & 0x80) != 0) {
257 e
.KeyEvent
.state
|= (int) KeyMasks
.ShiftMask
;
260 if ((key_state_table
[(int) VirtualKeys
.VK_CAPITAL
] & 0x01) != 0) {
261 e
.KeyEvent
.state
|= (int) KeyMasks
.LockMask
;
264 if ((key_state_table
[(int) VirtualKeys
.VK_CONTROL
] & 0x80) != 0) {
265 e
.KeyEvent
.state
|= (int) KeyMasks
.ControlMask
;
268 if ((key_state_table
[(int) VirtualKeys
.VK_NUMLOCK
] & 0x01) != 0) {
269 e
.KeyEvent
.state
|= NumLockMask
;
272 e
.KeyEvent
.state
|= AltGrMask
;
274 for (int keyc
= min_keycode
; (keyc
<= max_keycode
) && (e
.KeyEvent
.keycode
== 0); keyc
++) {
275 // find keycode that could have generated this vkey
276 if ((keyc2vkey
[keyc
] & 0xFF) == vkey
) {
277 // filter extended bit because it is not known
278 e
.KeyEvent
.keycode
= keyc
;
279 if ((EventToVkey (e
) & 0xFF) != vkey
) {
280 // Wrong one (ex: because of num,lock state)
281 e
.KeyEvent
.keycode
= 0;
286 if ((vkey
>= (int) VirtualKeys
.VK_NUMPAD0
) && (vkey
<= (int) VirtualKeys
.VK_NUMPAD9
))
287 e
.KeyEvent
.keycode
= XKeysymToKeycode (display
, vkey
- (int) VirtualKeys
.VK_NUMPAD0
+ (int) KeypadKeys
.XK_KP_0
);
289 if (vkey
== (int) VirtualKeys
.VK_DECIMAL
)
290 e
.KeyEvent
.keycode
= XKeysymToKeycode (display
, (int) KeypadKeys
.XK_KP_Decimal
);
292 if (vkey
== (int) VirtualKeys
.VK_SEPARATOR
)
293 e
.KeyEvent
.keycode
= XKeysymToKeycode(display
, (int) KeypadKeys
.XK_KP_Separator
);
295 if (e
.KeyEvent
.keycode
== 0 && vkey
!= (int) VirtualKeys
.VK_NONAME
) {
296 // And I couldn't find the keycode so i returned the vkey and was like whatever
297 Console
.Error
.WriteLine ("unknown virtual key {0:X}", vkey
);
298 buffer
= String
.Empty
;
304 int res
= LookupString (ref e
, 24, out t
, out status
);
305 int keysym
= (int) t
;
307 buffer
= String
.Empty
;
309 int dead_char
= MapDeadKeySym (keysym
);
310 if (dead_char
!= 0) {
311 byte [] bytes
= new byte [1];
312 bytes
[0] = (byte) dead_char
;
313 Encoding encoding
= Encoding
.GetEncoding (new CultureInfo (lcid
).TextInfo
.ANSICodePage
);
314 buffer
= new string (encoding
.GetChars (bytes
));
318 // Shift + arrow, shift + home, ....
319 // X returns a char for it, but windows doesn't
320 if (((e
.KeyEvent
.state
& NumLockMask
) == 0) && ((e
.KeyEvent
.state
& (int) KeyMasks
.ShiftMask
) != 0) &&
321 (keysym
>= (int) KeypadKeys
.XK_KP_0
) && (keysym
<= (int) KeypadKeys
.XK_KP_9
)) {
322 buffer
= String
.Empty
;
326 // CTRL + number, X returns chars, windows does not
327 if ((e
.KeyEvent
.state
& (int) KeyMasks
.ControlMask
) != 0) {
328 if (((keysym
>= 33) && (keysym
< 'A')) || ((keysym
> 'Z') && (keysym
< 'a'))) {
329 buffer
= String
.Empty
;
334 // X returns a char for delete key on extended keyboards, windows does not
335 if (keysym
== (int) TtyKeys
.XK_Delete
) {
336 buffer
= String
.Empty
;
341 buffer
= lookup_buffer
.ToString ();
349 private MSG
SendKeyboardInput (VirtualKeys vkey
, int scan
, KeybdEventFlags dw_flags
, int time
)
353 if ((dw_flags
& KeybdEventFlags
.KeyUp
) != 0) {
354 bool sys_key
= (key_state_table
[(int) VirtualKeys
.VK_MENU
] & 0x80) != 0 &&
355 ((key_state_table
[(int) VirtualKeys
.VK_CONTROL
] & 0x80) == 0);
356 key_state_table
[(int) vkey
] &= unchecked ((byte) ~
0x80);
357 message
= (sys_key
? Msg
.WM_SYSKEYUP
: Msg
.WM_KEYUP
);
359 if ((key_state_table
[(int) vkey
] & 0x80) == 0) {
360 key_state_table
[(int) vkey
] ^
= 0x01;
362 key_state_table
[(int) vkey
] |= 0x80;
363 bool sys_key
= (key_state_table
[(int) VirtualKeys
.VK_MENU
] & 0x80) != 0 &&
364 ((key_state_table
[(int) VirtualKeys
.VK_CONTROL
] & 0x80) == 0);
365 message
= (sys_key
? Msg
.WM_SYSKEYDOWN
: Msg
.WM_KEYDOWN
);
368 MSG msg
= new MSG ();
369 msg
.message
= message
;
370 msg
.wParam
= (IntPtr
) vkey
;
371 if ((key_state_table
[(int) VirtualKeys
.VK_MENU
] & 0x80) != 0)
372 msg
.lParam
= new IntPtr (0x20000000);
374 msg
.lParam
= IntPtr
.Zero
;
379 private void GenerateMessage (VirtualKeys vkey
, int scan
, XEventName type
, int event_time
)
381 bool state
= (vkey
== VirtualKeys
.VK_NUMLOCK
? num_state
: cap_state
);
382 KeybdEventFlags up
, down
;
385 // The INTERMEDIARY state means : just after a 'press' event, if a 'release' event comes,
386 // don't treat it. It's from the same key press. Then the state goes to ON.
387 // And from there, a 'release' event will switch off the toggle key.
388 SetState (vkey
, false);
390 down
= (vkey
== VirtualKeys
.VK_NUMLOCK
? KeybdEventFlags
.ExtendedKey
: KeybdEventFlags
.None
);
391 up
= (vkey
== VirtualKeys
.VK_NUMLOCK
? KeybdEventFlags
.ExtendedKey
:
392 KeybdEventFlags
.None
) | KeybdEventFlags
.KeyUp
;
393 if ((key_state_table
[(int) vkey
] & 0x1) != 0) { // it was on
394 if (type
!= XEventName
.KeyPress
) {
395 SendKeyboardInput (vkey
, scan
, down
, event_time
);
396 SendKeyboardInput (vkey
, scan
, up
, event_time
);
397 SetState (vkey
, false);
398 key_state_table
[(int) vkey
] &= unchecked ((byte) ~
0x01);
401 if (type
== XEventName
.KeyPress
) {
402 SendKeyboardInput (vkey
, scan
, down
, event_time
);
403 SendKeyboardInput (vkey
, scan
, up
, event_time
);
404 SetState (vkey
, true);
405 key_state_table
[(int) vkey
] |= 0x01;
411 private void UpdateKeyState (XEvent xevent
)
413 int vkey
= EventToVkey (xevent
);
415 switch (xevent
.type
) {
416 case XEventName
.KeyRelease
:
417 key_state_table
[vkey
& 0xff] &= unchecked ((byte) ~
0x80);
419 case XEventName
.KeyPress
:
420 if ((key_state_table
[vkey
& 0xff] & 0x80) == 0) {
421 key_state_table
[vkey
& 0xff] ^
= 0x01;
423 key_state_table
[vkey
& 0xff] |= 0x80;
428 private void SetState (VirtualKeys key
, bool state
)
430 if (VirtualKeys
.VK_NUMLOCK
== key
)
436 public int EventToVkey (XEvent e
)
438 EnsureLayoutInitialized ();
443 LookupString (ref e
, 0, out ks
, out status
);
444 int keysym
= (int) ks
;
446 if (((e
.KeyEvent
.state
& NumLockMask
) != 0) &&
447 (keysym
== (int)KeypadKeys
.XK_KP_Separator
|| keysym
== (int)KeypadKeys
.XK_KP_Decimal
||
448 (keysym
>= (int)KeypadKeys
.XK_KP_0
&& keysym
<= (int)KeypadKeys
.XK_KP_9
))) {
449 // Only the Keypad keys 0-9 and . send different keysyms
450 // depending on the NumLock state
451 return nonchar_key_vkey
[keysym
& 0xFF];
454 return keyc2vkey
[e
.KeyEvent
.keycode
];
457 private void CreateConversionArray (KeyboardLayouts layouts
, KeyboardLayout layout
)
459 XEvent e2
= new XEvent ();
461 int [] ckey
= new int [] { 0, 0, 0, 0 }
;
463 e2
.KeyEvent
.display
= display
;
464 e2
.KeyEvent
.state
= 0;
466 for (int keyc
= min_keycode
; keyc
<= max_keycode
; keyc
++) {
470 e2
.KeyEvent
.keycode
= keyc
;
474 LookupString (ref e2
, 0, out t
, out status
);
478 if ((keysym
>> 8) == 0xFF) {
479 vkey
= nonchar_key_vkey
[keysym
& 0xFF];
480 scan
= nonchar_key_scan
[keysym
& 0xFF];
482 if ((scan
& 0x100) != 0)
484 } else if (keysym
== 0x20) { // spacebar
485 vkey
= (int) VirtualKeys
.VK_SPACE
;
488 // Search layout dependent scancodes
492 for (int i
= 0; i
< syms
; i
++) {
493 keysym
= XKeycodeToKeysym (display
, keyc
, i
);
494 if ((keysym
< 0x800) && (keysym
!= ' '))
495 ckey
[i
] = (sbyte) (keysym
& 0xFF);
497 ckey
[i
] = (sbyte) MapDeadKeySym ((int) keysym
);
500 for (int keyn
= 0; keyn
< layout
.Keys
.Length
; keyn
++) {
501 int ml
= Math
.Min (layout
.Keys
[keyn
].Length
, 4);
503 for (int i
= 0; (ok
!= 0) && (i
< ml
); i
++) {
504 sbyte ck
= (sbyte) layout
.Keys
[keyn
][i
];
507 if ((ok
!= 0) || (i
> maxlen
)) {
516 scan
= layouts
.scan_table
[(int) layout
.ScanIndex
][maxval
];
517 vkey
= layouts
.vkey_table
[(int) layout
.VKeyIndex
][maxval
];
522 keyc2vkey
[e2
.KeyEvent
.keycode
] = vkey
;
523 keyc2scan
[e2
.KeyEvent
.keycode
] = scan
;
529 private KeyboardLayout
DetectLayout (KeyboardLayouts layouts
)
531 XDisplayKeycodes (display
, out min_keycode
, out max_keycode
);
532 IntPtr ksp
= XGetKeyboardMapping (display
, (byte) min_keycode
,
533 max_keycode
+ 1 - min_keycode
, out keysyms_per_keycode
);
534 XplatUIX11
.XFree (ksp
);
536 syms
= keysyms_per_keycode
;
538 //Console.Error.WriteLine ("{0} keysymbols per a keycode is not supported, setting to 4", syms);
542 IntPtr modmap_unmanaged
;
543 XModifierKeymap xmk
= new XModifierKeymap ();
545 modmap_unmanaged
= XGetModifierMapping (display
);
546 xmk
= (XModifierKeymap
) Marshal
.PtrToStructure (modmap_unmanaged
, typeof (XModifierKeymap
));
549 for (int i
= 0; i
< 8; i
++) {
550 for (int j
= 0; j
< xmk
.max_keypermod
; j
++, mmp
++) {
551 byte b
= Marshal
.ReadByte (xmk
.modifiermap
, mmp
);
553 for (int k
= 0; k
< keysyms_per_keycode
; k
++) {
554 if ((int) XKeycodeToKeysym (display
, b
, k
) == (int) MiscKeys
.XK_Num_Lock
)
555 NumLockMask
= 1 << i
;
560 XFreeModifiermap (modmap_unmanaged
);
562 int [] ckey
= new int [4];
563 KeyboardLayout layout
= null;
567 foreach (KeyboardLayout current
in layouts
.Layouts
) {
574 int key
= min_keycode
;
577 for (int keyc
= min_keycode
; keyc
<= max_keycode
; keyc
++) {
578 for (i
= 0; i
< syms
; i
++) {
579 uint keysym
= XKeycodeToKeysym (display
, keyc
, i
);
581 if ((keysym
< 0x800) && (keysym
!= ' ')) {
582 ckey
[i
] = (sbyte) (keysym
& 0xFF);
584 ckey
[i
] = (sbyte) MapDeadKeySym ((int) keysym
);
588 for (key
= 0; key
< current
.Keys
.Length
; key
++) {
589 int ml
= Math
.Min (syms
, current
.Keys
[key
].Length
);
590 for (ok
= 0, i
= 0; (ok
>= 0) && (i
< ml
); i
++) {
591 sbyte ck
= (sbyte) current
.Keys
[key
][i
];
592 if (ck
!= 0 && ck
== ckey
[i
])
594 if (ck
!= 0 && ck
!= ckey
[i
])
604 /* and how much the keycode order matches */
609 /* print spaces instead of \0's */
616 if ((score
> max_score
) || ((score
== max_score
) && (seq
> max_seq
))) {
624 if (layout
!= null) {
627 Console
.WriteLine (Locale
.GetText("Keyboard layout not recognized, using default layout: " +
628 layouts
.Layouts
[0].Name
));
631 return layouts
.Layouts
[0];
635 private int MapDeadKeySym (int val
)
638 case (int) DeadKeys
.XK_dead_tilde
:
639 case 0x1000FE7E : // Xfree's Dtilde
641 case (int) DeadKeys
.XK_dead_acute
:
642 case 0x1000FE27 : // Xfree's XK_Dacute_accent
644 case (int) DeadKeys
.XK_dead_circumflex
:
645 case 0x1000FE5E : // Xfree's XK_.Dcircumflex_accent
647 case (int) DeadKeys
.XK_dead_grave
:
648 case 0x1000FE60 : // Xfree's XK_.Dgrave_accent
650 case (int) DeadKeys
.XK_dead_diaeresis
:
651 case 0x1000FE22 : // Xfree's XK_.Ddiaeresis
653 case (int) DeadKeys
.XK_dead_cedilla
:
655 case (int) DeadKeys
.XK_dead_macron
:
657 case (int) DeadKeys
.XK_dead_breve
:
659 case (int) DeadKeys
.XK_dead_abovedot
:
661 case (int) DeadKeys
.XK_dead_abovering
:
663 case (int) DeadKeys
.XK_dead_doubleacute
:
665 case (int) DeadKeys
.XK_dead_caron
:
667 case (int) DeadKeys
.XK_dead_ogonek
:
674 private IntPtr
CreateXic (IntPtr window
, IntPtr xim
)
676 xic
= XCreateIC (xim
,
677 "inputStyle", XIMProperties
.XIMPreeditNothing
| XIMProperties
.XIMStatusNothing
,
678 "clientWindow", window
,
679 "focusWindow", window
,
684 private int LookupString (ref XEvent xevent
, int len
, out XKeySym keysym
, out IntPtr status
)
689 status
= IntPtr
.Zero
;
690 lookup_buffer
.Length
= 0;
691 if (xic
!= IntPtr
.Zero
)
692 res
= XmbLookupString (xic
, ref xevent
, lookup_buffer
, len
, out keysym_res
, out status
);
694 res
= XLookupString (ref xevent
, lookup_buffer
, len
, out keysym_res
, IntPtr
.Zero
);
696 keysym
= (XKeySym
) keysym_res
.ToInt32 ();
700 [DllImport ("libX11")]
701 private static extern IntPtr
XOpenIM (IntPtr display
, IntPtr rdb
, IntPtr res_name
, IntPtr res_class
);
703 [DllImport ("libX11")]
704 private static extern IntPtr
XCreateIC (IntPtr xim
, string name
, XIMProperties im_style
, string name2
, IntPtr value2
, string name3
, IntPtr value3
, IntPtr terminator
);
706 [DllImport ("libX11")]
707 private static extern void XSetICFocus (IntPtr xic
);
709 [DllImport ("libX11")]
710 private static extern void XUnsetICFocus (IntPtr xic
);
712 [DllImport ("libX11")]
713 private static extern bool XSupportsLocale ();
715 [DllImport ("libX11")]
716 private static extern bool XSetLocaleModifiers (string mods
);
718 [DllImport ("libX11")]
719 internal extern static int XLookupString(ref XEvent xevent
, StringBuilder buffer
, int num_bytes
, out IntPtr keysym
, IntPtr status
);
720 [DllImport ("libX11")]
721 internal extern static int XmbLookupString(IntPtr xic
, ref XEvent xevent
, StringBuilder buffer
, int num_bytes
, out IntPtr keysym
, out IntPtr status
);
723 internal static int XmbLookupString (IntPtr xic
, ref XEvent xevent
, StringBuilder buffer
, int num_bytes
, out XKeySym keysym
, out IntPtr status
) {
727 ret
= XmbLookupString (xic
, ref xevent
, buffer
, num_bytes
, out keysym_ret
, out status
);
729 keysym
= (XKeySym
)keysym_ret
.ToInt32();
734 internal static int XLookupString (ref XEvent xevent
, StringBuilder buffer
, int num_bytes
, out XKeySym keysym
, IntPtr status
) {
738 ret
= XLookupString (ref xevent
, buffer
, num_bytes
, out keysym_ret
, status
);
739 keysym
= (XKeySym
)keysym_ret
.ToInt32();
744 [DllImport ("libX11")]
745 private static extern IntPtr
XGetKeyboardMapping (IntPtr display
, byte first_keycode
, int keycode_count
,
746 out int keysyms_per_keycode_return
);
748 [DllImport ("libX11")]
749 private static extern void XDisplayKeycodes (IntPtr display
, out int min
, out int max
);
751 [DllImport ("libX11", EntryPoint
="XKeycodeToKeysym")]
752 private static extern uint XKeycodeToKeysym (IntPtr display
, int keycode
, int index
);
754 [DllImport ("libX11")]
755 private static extern int XKeysymToKeycode (IntPtr display
, IntPtr keysym
);
756 private static int XKeysymToKeycode (IntPtr display
, int keysym
) {
757 return XKeysymToKeycode(display
, (IntPtr
)keysym
);
760 [DllImport ("libX11")]
761 internal extern static IntPtr
XGetModifierMapping (IntPtr display
);
763 [DllImport ("libX11")]
764 internal extern static int XFreeModifiermap (IntPtr modmap
);
767 private readonly static int [] nonchar_key_vkey
= new int []
770 0, 0, 0, 0, 0, 0, 0, 0, /* FF00 */
772 (int) VirtualKeys
.VK_BACK
, (int) VirtualKeys
.VK_TAB
, 0, (int) VirtualKeys
.VK_CLEAR
, 0, (int) VirtualKeys
.VK_RETURN
, 0, 0, /* FF08 */
773 0, 0, 0, (int) VirtualKeys
.VK_PAUSE
, (int) VirtualKeys
.VK_SCROLL
, 0, 0, 0, /* FF10 */
774 0, 0, 0, (int) VirtualKeys
.VK_ESCAPE
, 0, 0, 0, 0, /* FF18 */
776 0, 0, 0, 0, 0, 0, 0, 0, /* FF20 */
777 0, 0, 0, 0, 0, 0, 0, 0, /* FF28 */
778 0, 0, 0, 0, 0, 0, 0, 0, /* FF30 */
779 0, 0, 0, 0, 0, 0, 0, 0, /* FF38 */
780 0, 0, 0, 0, 0, 0, 0, 0, /* FF40 */
781 0, 0, 0, 0, 0, 0, 0, 0, /* FF48 */
783 (int) VirtualKeys
.VK_HOME
, (int) VirtualKeys
.VK_LEFT
, (int) VirtualKeys
.VK_UP
, (int) VirtualKeys
.VK_RIGHT
, /* FF50 */
784 (int) VirtualKeys
.VK_DOWN
, (int) VirtualKeys
.VK_PRIOR
, (int) VirtualKeys
.VK_NEXT
, (int) VirtualKeys
.VK_END
,
785 0, 0, 0, 0, 0, 0, 0, 0, /* FF58 */
787 (int) VirtualKeys
.VK_SELECT
, (int) VirtualKeys
.VK_SNAPSHOT
, (int) VirtualKeys
.VK_EXECUTE
, (int) VirtualKeys
.VK_INSERT
, 0, 0, 0, 0, /* FF60 */
788 (int) VirtualKeys
.VK_CANCEL
, (int) VirtualKeys
.VK_HELP
, (int) VirtualKeys
.VK_CANCEL
, (int) VirtualKeys
.VK_CANCEL
, 0, 0, 0, 0, /* FF68 */
789 0, 0, 0, 0, 0, 0, 0, 0, /* FF70 */
791 0, 0, 0, 0, 0, 0, 0, (int) VirtualKeys
.VK_NUMLOCK
, /* FF78 */
792 0, 0, 0, 0, 0, 0, 0, 0, /* FF80 */
793 0, 0, 0, 0, 0, (int) VirtualKeys
.VK_RETURN
, 0, 0, /* FF88 */
794 0, 0, 0, 0, 0, (int) VirtualKeys
.VK_HOME
, (int) VirtualKeys
.VK_LEFT
, (int) VirtualKeys
.VK_UP
, /* FF90 */
795 (int) VirtualKeys
.VK_RIGHT
, (int) VirtualKeys
.VK_DOWN
, (int) VirtualKeys
.VK_PRIOR
, (int) VirtualKeys
.VK_NEXT
, /* FF98 */
796 (int) VirtualKeys
.VK_END
, 0, (int) VirtualKeys
.VK_INSERT
, (int) VirtualKeys
.VK_DELETE
,
797 0, 0, 0, 0, 0, 0, 0, 0, /* FFA0 */
798 0, 0, (int) VirtualKeys
.VK_MULTIPLY
, (int) VirtualKeys
.VK_ADD
, /* FFA8 */
799 (int) VirtualKeys
.VK_SEPARATOR
, (int) VirtualKeys
.VK_SUBTRACT
, (int) VirtualKeys
.VK_DECIMAL
, (int) VirtualKeys
.VK_DIVIDE
,
800 (int) VirtualKeys
.VK_NUMPAD0
, (int) VirtualKeys
.VK_NUMPAD1
, (int) VirtualKeys
.VK_NUMPAD2
, (int) VirtualKeys
.VK_NUMPAD3
, /* FFB0 */
801 (int) VirtualKeys
.VK_NUMPAD4
, (int) VirtualKeys
.VK_NUMPAD5
, (int) VirtualKeys
.VK_NUMPAD6
, (int) VirtualKeys
.VK_NUMPAD7
,
802 (int) VirtualKeys
.VK_NUMPAD8
, (int) VirtualKeys
.VK_NUMPAD9
, 0, 0, 0, 0, /* FFB8 */
804 (int) VirtualKeys
.VK_F1
, (int) VirtualKeys
.VK_F2
,
805 (int) VirtualKeys
.VK_F3
, (int) VirtualKeys
.VK_F4
, (int) VirtualKeys
.VK_F5
, (int) VirtualKeys
.VK_F6
, (int) VirtualKeys
.VK_F7
, (int) VirtualKeys
.VK_F8
, (int) VirtualKeys
.VK_F9
, (int) VirtualKeys
.VK_F10
, /* FFC0 */
806 (int) VirtualKeys
.VK_F11
, (int) VirtualKeys
.VK_F12
, (int) VirtualKeys
.VK_F13
, (int) VirtualKeys
.VK_F14
, (int) VirtualKeys
.VK_F15
, (int) VirtualKeys
.VK_F16
, 0, 0, /* FFC8 */
807 0, 0, 0, 0, 0, 0, 0, 0, /* FFD0 */
808 0, 0, 0, 0, 0, 0, 0, 0, /* FFD8 */
810 0, (int) VirtualKeys
.VK_SHIFT
, (int) VirtualKeys
.VK_SHIFT
, (int) VirtualKeys
.VK_CONTROL
, /* FFE0 */
811 (int) VirtualKeys
.VK_CONTROL
, (int) VirtualKeys
.VK_CAPITAL
, 0, (int) VirtualKeys
.VK_MENU
,
812 (int) VirtualKeys
.VK_MENU
, (int) VirtualKeys
.VK_MENU
, (int) VirtualKeys
.VK_MENU
, 0, 0, 0, 0, 0, /* FFE8 */
813 0, 0, 0, 0, 0, 0, 0, 0, /* FFF0 */
814 0, 0, 0, 0, 0, 0, 0, (int) VirtualKeys
.VK_DELETE
/* FFF8 */
817 private static readonly int [] nonchar_key_scan
= new int []
820 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF00 */
822 0x0E, 0x0F, 0x00, /*?*/ 0, 0x00, 0x1C, 0x00, 0x00, /* FF08 */
823 0x00, 0x00, 0x00, 0x45, 0x46, 0x00, 0x00, 0x00, /* FF10 */
824 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, /* FF18 */
826 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF20 */
827 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF28 */
828 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF30 */
829 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF38 */
830 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF40 */
831 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF48 */
833 0x147, 0x14B, 0x148, 0x14D, 0x150, 0x149, 0x151, 0x14F, /* FF50 */
834 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF58 */
836 /*?*/ 0, 0x137, /*?*/ 0, 0x152, 0x00, 0x00, 0x00, 0x00, /* FF60 */
837 /*?*/ 0, /*?*/ 0, 0x38, 0x146, 0x00, 0x00, 0x00, 0x00, /* FF68 */
838 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF70 */
840 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x138, 0x145, /* FF78 */
841 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF80 */
842 0x00, 0x00, 0x00, 0x00, 0x00, 0x11C, 0x00, 0x00, /* FF88 */
843 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x4B, 0x48, /* FF90 */
844 0x4D, 0x50, 0x49, 0x51, 0x4F, 0x4C, 0x52, 0x53, /* FF98 */
845 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFA0 */
846 0x00, 0x00, 0x37, 0x4E, /*?*/ 0, 0x4A, 0x53, 0x135, /* FFA8 */
847 0x52, 0x4F, 0x50, 0x51, 0x4B, 0x4C, 0x4D, 0x47, /* FFB0 */
848 0x48, 0x49, 0x00, 0x00, 0x00, 0x00, /* FFB8 */
851 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, /* FFC0 */
852 0x57, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFC8 */
853 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFD0 */
854 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFD8 */
856 0x00, 0x2A, 0x36, 0x1D, 0x11D, 0x3A, 0x00, 0x38, /* FFE0 */
857 0x138, 0x38, 0x138, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFE8 */
858 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFF0 */
859 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x153 /* FFF8 */
862 private readonly static int [] nonchar_vkey_key
= new int []
864 0, 0, 0, 0, 0, /* 00-04 */
865 0, 0, 0, (int)XKeySym
.XK_BackSpace
, (int)XKeySym
.XK_Tab
, /* 05-09 */
866 0, 0, (int)XKeySym
.XK_Clear
, (int)XKeySym
.XK_Return
, 0, 0, /* 0A-0F */
867 (int)XKeySym
.XK_Shift_L
, (int)XKeySym
.XK_Control_L
, (int)XKeySym
.XK_Menu
, 0, (int)XKeySym
.XK_Caps_Lock
, /* 10-14 */
868 0, 0, 0, 0, 0, /* 15-19 */
869 0, 0, 0, 0, 0, 0, /* 1A-1F */
870 0, 0, 0, (int)XKeySym
.XK_End
, (int)XKeySym
.XK_Home
, /* 20-24 */
871 (int)XKeySym
.XK_Left
, (int)XKeySym
.XK_Up
, (int)XKeySym
.XK_Right
, (int)XKeySym
.XK_Down
, 0, /* 25-29 */
872 0, 0, 0, 0, 0, 0, /* 2A-2F */
873 0, 0, 0, 0, 0, /* 30-34 */
874 0, 0, 0, 0, 0, /* 35-39 */
875 0, 0, 0, 0, 0, 0, /* 3A-3F */
876 0, 0, 0, 0, 0, /* 40-44 */
877 0, 0, 0, 0, 0, /* 45-49 */
878 0, 0, 0, 0, 0, 0, /* 4A-4F */
879 0, 0, 0, 0, 0, /* 50-54 */
880 0, 0, 0, 0, 0, /* 55-59 */
881 0, (int)XKeySym
.XK_Meta_L
, (int)XKeySym
.XK_Meta_R
, 0, 0, 0, /* 5A-5F */
882 0, 0, 0, 0, 0, /* 60-64 */
883 0, 0, 0, 0, 0, /* 65-69 */
884 0, 0, 0, 0, 0, 0, /* 6A-6F */
885 0, 0, 0, 0, 0, /* 70-74 */
886 0, 0, 0, 0, 0, /* 75-79 */
887 0, 0, 0, 0, 0, 0, /* 7A-7F */
888 0, 0, 0, 0, 0, /* 80-84 */
889 0, 0, 0, 0, 0, /* 85-89 */
890 0, 0, 0, 0, 0, 0, /* 8A-8F */
891 0, 0, 0, 0, 0, /* 90-94 */
892 0, 0, 0, 0, 0, /* 95-99 */
893 0, 0, 0, 0, 0, 0, /* 9A-9F */
894 (int)XKeySym
.XK_Shift_L
, (int)XKeySym
.XK_Shift_R
, (int)XKeySym
.XK_Control_L
, (int)XKeySym
.XK_Control_R
, (int)XKeySym
.XK_Alt_L
, /* A0-A4 */
895 (int)XKeySym
.XK_Alt_R
, 0, 0, 0, 0, /* A5-A9 */
896 0, 0, 0, 0, 0, 0, /* AA-AF */
897 0, 0, 0, 0, 0, /* B0-B4 */
898 0, 0, 0, 0, 0, /* B5-B9 */
899 0, 0, 0, 0, 0, 0, /* BA-BF */
900 0, 0, 0, 0, 0, /* C0-C4 */
901 0, 0, 0, 0, 0, /* C5-C9 */
902 0, 0, 0, 0, 0, 0, /* CA-CF */
903 0, 0, 0, 0, 0, /* D0-D4 */
904 0, 0, 0, 0, 0, /* D5-D9 */
905 0, 0, 0, 0, 0, 0, /* DA-DF */
906 0, 0, 0, 0, 0, /* E0-E4 */
907 0, 0, 0, 0, 0, /* E5-E9 */
908 0, 0, 0, 0, 0, 0, /* EA-EF */
909 0, 0, 0, 0, 0, /* F0-F4 */
910 0, 0, 0, 0, 0, /* F5-F9 */
911 0, 0, 0, 0, 0, 0 /* FA-FF */