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;
35 using System.Diagnostics;
38 using System.Globalization;
39 using System.Runtime.InteropServices;
41 namespace System.Windows.Forms {
43 public enum XLookupStatus
52 internal class X11Keyboard : IDisposable {
53 internal static object XlibLock;
55 private IntPtr display;
56 private IntPtr client_window;
58 private Hashtable xic_table = new Hashtable ();
59 private XIMPositionContext positionContext;
60 private XIMCallbackContext callbackContext;
61 private XIMProperties ximStyle;
62 private EventMask xic_event_mask = EventMask.NoEventMask;
63 private StringBuilder lookup_buffer;
64 private byte [] lookup_byte_buffer = new byte [100];
65 private int min_keycode, max_keycode, keysyms_per_keycode, syms;
66 private int [] keyc2vkey = new int [256];
67 private int [] keyc2scan = new int [256];
68 private byte [] key_state_table = new byte [256];
70 private bool num_state, cap_state;
71 private bool initialized;
72 private bool menu_state = false;
73 private Encoding encoding;
75 private int NumLockMask;
76 private int AltGrMask;
78 public X11Keyboard (IntPtr display, IntPtr clientWindow)
80 this.display = display;
81 lookup_buffer = new StringBuilder (24);
82 EnsureLayoutInitialized ();
85 private Encoding AnsiEncoding
90 encoding = Encoding.GetEncoding(new CultureInfo(lcid).TextInfo.ANSICodePage);
95 public IntPtr ClientWindow {
96 get { return client_window; }
99 void IDisposable.Dispose ()
101 if (xim != IntPtr.Zero) {
102 foreach (IntPtr xic in xic_table.Values)
111 public void DestroyICForWindow (IntPtr window)
113 IntPtr xic = GetXic (window);
114 if (xic != IntPtr.Zero) {
115 xic_table.Remove ((long) window);
120 public void EnsureLayoutInitialized ()
125 KeyboardLayouts layouts = new KeyboardLayouts ();
126 KeyboardLayout layout = DetectLayout (layouts);
128 CreateConversionArray (layouts, layout);
133 private void SetupXIM ()
137 if (!XSupportsLocale ()) {
138 Console.Error.WriteLine ("X does not support your locale");
142 if (!XSetLocaleModifiers (String.Empty)) {
143 Console.Error.WriteLine ("Could not set X locale modifiers");
147 if (Environment.GetEnvironmentVariable (ENV_NAME_XIM_STYLE) == "disabled") {
151 xim = XOpenIM (display, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
152 if (xim == IntPtr.Zero)
153 Console.Error.WriteLine ("Could not get XIM");
158 void CreateXicForWindow (IntPtr window)
160 IntPtr xic = CreateXic (window, xim);
161 xic_table [(long) window] = xic;
162 if (xic == IntPtr.Zero)
163 Console.Error.WriteLine ("Could not get XIC");
165 if (XGetICValues (xic, "filterEvents", out xic_event_mask, IntPtr.Zero) != null)
166 Console.Error.WriteLine ("Could not get XIC values");
167 EventMask mask = EventMask.ExposureMask | EventMask.KeyPressMask | EventMask.FocusChangeMask;
168 if ((xic_event_mask | mask) == xic_event_mask) {
169 xic_event_mask |= mask;
171 XplatUIX11.XSelectInput(display, window, new IntPtr ((int) xic_event_mask));
177 public EventMask KeyEventMask {
178 get { return xic_event_mask; }
181 public Keys ModifierKeys {
183 Keys keys = Keys.None;
184 if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0)
186 if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0)
187 keys |= Keys.Control;
188 if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0)
194 private IntPtr GetXic (IntPtr window)
196 if (xim != IntPtr.Zero && xic_table.ContainsKey ((long) window))
197 return (IntPtr) xic_table [(long) window];
202 private bool FilterKey (XEvent e, int vkey)
204 if (XplatUI.key_filters.Count == 0)
206 XLookupStatus status;
209 data.Down = (e.type == XEventName.KeyPress);
210 data.ModifierKeys = ModifierKeys;
211 LookupString (ref e, 0, out ks, out status);
212 data.keysym = (int)ks;
213 data.keycode = e.KeyEvent.keycode;
214 data.str = lookup_buffer.ToString (0, lookup_buffer.Length);
215 return XplatUI.FilterKey (data);
218 public void FocusIn (IntPtr window)
220 this.client_window = window;
221 if (xim == IntPtr.Zero)
224 if (!xic_table.ContainsKey ((long) window))
225 CreateXicForWindow (window);
226 IntPtr xic = GetXic (window);
227 if (xic != IntPtr.Zero)
231 private bool have_Xutf8ResetIC = true;
233 public void FocusOut (IntPtr window)
235 this.client_window = IntPtr.Zero;
236 if (xim == IntPtr.Zero)
239 IntPtr xic = GetXic (window);
240 if (xic != IntPtr.Zero) {
241 if (have_Xutf8ResetIC) {
244 } catch (EntryPointNotFoundException) {
245 have_Xutf8ResetIC = false;
252 public bool ResetKeyState(IntPtr hwnd, ref MSG msg) {
253 // FIXME - keep defining events/msg and return true until we've 'restored' all
254 // pending keypresses
255 if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0) {
256 key_state_table [(int) VirtualKeys.VK_SHIFT] &= unchecked((byte)~0x80);
259 if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) {
260 key_state_table [(int) VirtualKeys.VK_CONTROL] &= unchecked((byte)~0x80);
263 if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0) {
264 key_state_table [(int) VirtualKeys.VK_MENU] &= unchecked((byte)~0x80);
269 // Almost identical to UpdateKeyState() but does not call LookupString().
270 // The actual purpose is to handle shift state correctly.
271 public void PreFilter (XEvent xevent)
273 // It is still possible that some keyboards could have some shift
274 // keys outside this range, but let's think about such cases only
275 // if it actually happened.
276 if (xevent.KeyEvent.keycode >= keyc2vkey.Length)
279 int vkey = keyc2vkey [xevent.KeyEvent.keycode];
281 switch (xevent.type) {
282 case XEventName.KeyRelease:
283 key_state_table [vkey & 0xff] &= unchecked ((byte) ~0x80);
285 case XEventName.KeyPress:
286 if ((key_state_table [vkey & 0xff] & 0x80) == 0) {
287 key_state_table [vkey & 0xff] ^= 0x01;
289 key_state_table [vkey & 0xff] |= 0x80;
294 public void KeyEvent (IntPtr hwnd, XEvent xevent, ref MSG msg)
299 XLookupStatus status;
300 ascii_chars = LookupString (ref xevent, 24, out keysym, out status);
302 if (((int) keysym >= (int) MiscKeys.XK_ISO_Lock &&
303 (int) keysym <= (int) MiscKeys.XK_ISO_Last_Group_Lock) ||
304 (int) keysym == (int) MiscKeys.XK_Mode_switch) {
305 UpdateKeyState (xevent);
309 if ((xevent.KeyEvent.keycode >> 8) == 0x10)
310 xevent.KeyEvent.keycode = xevent.KeyEvent.keycode & 0xFF;
312 int event_time = (int)xevent.KeyEvent.time;
314 if (status == XLookupStatus.XLookupChars) {
315 // do not ignore those inputs. They are mostly from XIM.
316 msg = SendImeComposition (lookup_buffer.ToString (0, lookup_buffer.Length));
321 AltGrMask = xevent.KeyEvent.state & (0x6000 | (int) KeyMasks.ModMasks);
322 int vkey = EventToVkey (xevent);
323 if (vkey == 0 && ascii_chars != 0) {
324 vkey = (int) VirtualKeys.VK_NONAME;
327 if (FilterKey (xevent, vkey))
329 switch ((VirtualKeys) (vkey & 0xFF)) {
330 case VirtualKeys.VK_NUMLOCK:
331 GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, xevent.KeyEvent.keycode, xevent.type, event_time);
333 case VirtualKeys.VK_CAPITAL:
334 GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, xevent.KeyEvent.keycode, xevent.type, event_time);
337 if (((key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x01) == 0) != ((xevent.KeyEvent.state & NumLockMask) == 0)) {
338 GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, xevent.KeyEvent.keycode, XEventName.KeyPress, event_time);
339 GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, xevent.KeyEvent.keycode, XEventName.KeyRelease, event_time);
342 if (((key_state_table [(int) VirtualKeys.VK_CAPITAL] & 0x01) == 0) != ((xevent.KeyEvent.state & (int) KeyMasks.LockMask) == 0)) {
343 GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, xevent.KeyEvent.keycode, XEventName.KeyPress, event_time);
344 GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, xevent.KeyEvent.keycode, XEventName.KeyRelease, event_time);
350 int bscan = (keyc2scan [xevent.KeyEvent.keycode] & 0xFF);
351 KeybdEventFlags dw_flags = KeybdEventFlags.None;
352 if (xevent.type == XEventName.KeyRelease)
353 dw_flags |= KeybdEventFlags.KeyUp;
354 if ((vkey & 0x100) != 0)
355 dw_flags |= KeybdEventFlags.ExtendedKey;
356 msg = SendKeyboardInput ((VirtualKeys) (vkey & 0xFF), bscan, xevent.KeyEvent.keycode, dw_flags, event_time);
362 public bool TranslateMessage (ref MSG msg)
366 if (msg.message >= Msg.WM_KEYFIRST && msg.message <= Msg.WM_KEYLAST)
369 if (msg.message == Msg.WM_SYSKEYUP && msg.wParam == (IntPtr) 0x12 && menu_state) {
370 msg.message = Msg.WM_KEYUP;
374 if (msg.message != Msg.WM_KEYDOWN && msg.message != Msg.WM_SYSKEYDOWN)
377 if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 && msg.wParam != (IntPtr) 0x12)
380 EnsureLayoutInitialized ();
384 int tu = ToUnicode ((int) msg.wParam, Control.HighOrder ((int) msg.lParam), out buffer);
387 message = (msg.message == Msg.WM_KEYDOWN ? Msg.WM_CHAR : Msg.WM_SYSCHAR);
388 XplatUI.PostMessage (msg.hwnd, message, (IntPtr) buffer [0], msg.lParam);
391 message = (msg.message == Msg.WM_KEYDOWN ? Msg.WM_DEADCHAR : Msg.WM_SYSDEADCHAR);
392 XplatUI.PostMessage (msg.hwnd, message, (IntPtr) buffer [0], msg.lParam);
399 public int ToKeycode(int key)
403 if (nonchar_vkey_key[key] > 0)
404 keycode = XKeysymToKeycode (display, nonchar_vkey_key[key]);
407 keycode = XKeysymToKeycode (display, key);
412 public int ToUnicode (int vkey, int scan, out string buffer)
414 if ((scan & 0x8000) != 0) {
415 buffer = String.Empty;
419 XEvent e = new XEvent ();
420 e.AnyEvent.type = XEventName.KeyPress;
421 e.KeyEvent.display = display;
422 e.KeyEvent.keycode = 0;
423 e.KeyEvent.state = 0;
425 if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0) {
426 e.KeyEvent.state |= (int) KeyMasks.ShiftMask;
429 if ((key_state_table [(int) VirtualKeys.VK_CAPITAL] & 0x01) != 0) {
430 e.KeyEvent.state |= (int) KeyMasks.LockMask;
433 if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) {
434 e.KeyEvent.state |= (int) KeyMasks.ControlMask;
437 if ((key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x01) != 0) {
438 e.KeyEvent.state |= NumLockMask;
441 e.KeyEvent.state |= AltGrMask;
443 for (int keyc = min_keycode; (keyc <= max_keycode) && (e.KeyEvent.keycode == 0); keyc++) {
444 // find keycode that could have generated this vkey
445 if ((keyc2vkey [keyc] & 0xFF) == vkey) {
446 // filter extended bit because it is not known
447 e.KeyEvent.keycode = keyc;
448 if ((EventToVkey (e) & 0xFF) != vkey) {
449 // Wrong one (ex: because of num,lock state)
450 e.KeyEvent.keycode = 0;
455 if ((vkey >= (int) VirtualKeys.VK_NUMPAD0) && (vkey <= (int) VirtualKeys.VK_NUMPAD9))
456 e.KeyEvent.keycode = XKeysymToKeycode (display, vkey - (int) VirtualKeys.VK_NUMPAD0 + (int) KeypadKeys.XK_KP_0);
458 if (vkey == (int) VirtualKeys.VK_DECIMAL)
459 e.KeyEvent.keycode = XKeysymToKeycode (display, (int) KeypadKeys.XK_KP_Decimal);
461 if (vkey == (int) VirtualKeys.VK_SEPARATOR)
462 e.KeyEvent.keycode = XKeysymToKeycode(display, (int) KeypadKeys.XK_KP_Separator);
464 if (e.KeyEvent.keycode == 0 && vkey != (int) VirtualKeys.VK_NONAME) {
465 // And I couldn't find the keycode so i returned the vkey and was like whatever
466 Console.Error.WriteLine ("unknown virtual key {0:X}", vkey);
467 buffer = String.Empty;
472 XLookupStatus status;
473 int res = LookupString (ref e, 24, out t, out status);
474 int keysym = (int) t;
476 buffer = String.Empty;
478 int dead_char = MapDeadKeySym (keysym);
479 if (dead_char != 0) {
480 byte [] bytes = new byte [1];
481 bytes [0] = (byte) dead_char;
482 buffer = new string (AnsiEncoding.GetChars (bytes));
486 // Shift + arrow, shift + home, ....
487 // X returns a char for it, but windows doesn't
488 if (((e.KeyEvent.state & NumLockMask) == 0) && ((e.KeyEvent.state & (int) KeyMasks.ShiftMask) != 0) &&
489 (keysym >= (int) KeypadKeys.XK_KP_0) && (keysym <= (int) KeypadKeys.XK_KP_9)) {
490 buffer = String.Empty;
494 // CTRL + number, X returns chars, windows does not
495 if ((e.KeyEvent.state & (int) KeyMasks.ControlMask) != 0) {
496 if (((keysym >= 33) && (keysym < 'A')) || ((keysym > 'Z') && (keysym < 'a'))) {
497 buffer = String.Empty;
502 // X returns a char for delete key on extended keyboards, windows does not
503 if (keysym == (int) TtyKeys.XK_Delete) {
504 buffer = String.Empty;
508 if (keysym == (int) TtyKeys.XK_BackSpace && (key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) {
509 buffer = new string (new char [] { (char) 127 });
514 // For some special chars, such backspace and enter, looking up for a string
515 // can randomly fail to properly fill the buffer (either marshaling or X11), so we use
516 // the keysysm value to fill the gap
517 if (keysym == (int) XKeySym.XK_BackSpace) {
518 buffer = new string (new char [] { (char) 8 });
521 if (keysym == (int) XKeySym.XK_Return) {
522 buffer = new string (new char [] { (char)13 });
527 buffer = lookup_buffer.ToString ();
535 string stored_keyevent_string;
537 internal string GetCompositionString ()
539 return stored_keyevent_string;
542 private MSG SendImeComposition (string s)
544 MSG msg = new MSG ();
545 msg.message = Msg.WM_IME_COMPOSITION;
547 stored_keyevent_string = s;
551 private MSG SendKeyboardInput (VirtualKeys vkey, int scan, int keycode, KeybdEventFlags dw_flags, int time)
555 if ((dw_flags & KeybdEventFlags.KeyUp) != 0) {
556 bool sys_key = (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 &&
557 ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) == 0);
558 key_state_table [(int) vkey] &= unchecked ((byte) ~0x80);
559 message = (sys_key ? Msg.WM_SYSKEYUP : Msg.WM_KEYUP);
561 if ((key_state_table [(int) vkey] & 0x80) == 0) {
562 key_state_table [(int) vkey] ^= 0x01;
564 key_state_table [(int) vkey] |= 0x80;
565 bool sys_key = (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 &&
566 ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) == 0);
567 message = (sys_key ? Msg.WM_SYSKEYDOWN : Msg.WM_KEYDOWN);
570 MSG msg = new MSG ();
571 msg.message = message;
572 msg.wParam = (IntPtr) vkey;
573 msg.lParam = GenerateLParam (msg, keycode);
577 private IntPtr GenerateLParam (MSG m, int keyCode)
579 // http://msdn.microsoft.com/en-us/library/ms646267(VS.85).aspx
583 if (m.message == Msg.WM_SYSKEYUP || m.message == Msg.WM_KEYUP)
584 flags |= 0x80; // transition state flag = 1
586 flags |= 0x40; // previous key state flag = 1
588 if ((key_state_table [(int) VirtualKeys.VK_RMENU] & 0x80) != 0 ||
589 (key_state_table [(int) VirtualKeys.VK_LMENU] & 0x80) != 0 ||
590 (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0)
591 flags |= 0x20; // context code flag = 1
593 if ((key_state_table [(int) VirtualKeys.VK_INSERT] & 0x80) != 0 ||
594 (key_state_table [(int) VirtualKeys.VK_DELETE] & 0x80) != 0 ||
595 (key_state_table [(int) VirtualKeys.VK_HOME] & 0x80) != 0 ||
596 (key_state_table [(int) VirtualKeys.VK_END] & 0x80) != 0 ||
597 (key_state_table [(int) VirtualKeys.VK_UP] & 0x80) != 0 ||
598 (key_state_table [(int) VirtualKeys.VK_DOWN] & 0x80) != 0 ||
599 (key_state_table [(int) VirtualKeys.VK_LEFT] & 0x80) != 0 ||
600 (key_state_table [(int) VirtualKeys.VK_RIGHT] & 0x80) != 0 ||
601 (key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0 ||
602 (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 ||
603 (key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x80) != 0 ||
604 (key_state_table [(int) VirtualKeys.VK_PRINT] & 0x80) != 0 ||
605 (key_state_table [(int) VirtualKeys.VK_RETURN] & 0x80) != 0 ||
606 (key_state_table [(int) VirtualKeys.VK_DIVIDE] & 0x80) != 0 ||
607 (key_state_table [(int) VirtualKeys.VK_PRIOR] & 0x80) != 0 ||
608 (key_state_table [(int) VirtualKeys.VK_NEXT] & 0x80) != 0)
609 flags |= 0x01; // extended key flag = 1
611 int lparam = ((((int)flags) & 0x000000FF) << 3*8); // message flags
612 lparam |= ((keyCode & 0x000000FF) << 2*8); // scan code
613 lparam |= 0x00000001; // repeat count = 1
615 return (IntPtr)lparam;
618 private void GenerateMessage (VirtualKeys vkey, int scan, int key_code, XEventName type, int event_time)
620 bool state = (vkey == VirtualKeys.VK_NUMLOCK ? num_state : cap_state);
621 KeybdEventFlags up, down;
624 // The INTERMEDIARY state means : just after a 'press' event, if a 'release' event comes,
625 // don't treat it. It's from the same key press. Then the state goes to ON.
626 // And from there, a 'release' event will switch off the toggle key.
627 SetState (vkey, false);
629 down = (vkey == VirtualKeys.VK_NUMLOCK ? KeybdEventFlags.ExtendedKey : KeybdEventFlags.None);
630 up = (vkey == VirtualKeys.VK_NUMLOCK ? KeybdEventFlags.ExtendedKey :
631 KeybdEventFlags.None) | KeybdEventFlags.KeyUp;
632 if ((key_state_table [(int) vkey] & 0x1) != 0) { // it was on
633 if (type != XEventName.KeyPress) {
634 SendKeyboardInput (vkey, scan, key_code, down, event_time);
635 SendKeyboardInput (vkey, scan, key_code, up, event_time);
636 SetState (vkey, false);
637 key_state_table [(int) vkey] &= unchecked ((byte) ~0x01);
640 if (type == XEventName.KeyPress) {
641 SendKeyboardInput (vkey, scan, key_code, down, event_time);
642 SendKeyboardInput (vkey, scan, key_code, up, event_time);
643 SetState (vkey, true);
644 key_state_table [(int) vkey] |= 0x01;
650 private void UpdateKeyState (XEvent xevent)
652 int vkey = EventToVkey (xevent);
654 switch (xevent.type) {
655 case XEventName.KeyRelease:
656 key_state_table [vkey & 0xff] &= unchecked ((byte) ~0x80);
658 case XEventName.KeyPress:
659 if ((key_state_table [vkey & 0xff] & 0x80) == 0) {
660 key_state_table [vkey & 0xff] ^= 0x01;
662 key_state_table [vkey & 0xff] |= 0x80;
667 private void SetState (VirtualKeys key, bool state)
669 if (VirtualKeys.VK_NUMLOCK == key)
675 public int EventToVkey (XEvent e)
677 XLookupStatus status;
680 LookupString (ref e, 0, out ks, out status);
681 int keysym = (int) ks;
683 if (((e.KeyEvent.state & NumLockMask) != 0) &&
684 (keysym == (int)KeypadKeys.XK_KP_Separator || keysym == (int)KeypadKeys.XK_KP_Decimal ||
685 (keysym >= (int)KeypadKeys.XK_KP_0 && keysym <= (int)KeypadKeys.XK_KP_9))) {
686 // Only the Keypad keys 0-9 and . send different keysyms
687 // depending on the NumLock state
688 return nonchar_key_vkey [keysym & 0xFF];
691 return keyc2vkey [e.KeyEvent.keycode];
694 private void CreateConversionArray (KeyboardLayouts layouts, KeyboardLayout layout)
696 XEvent e2 = new XEvent ();
698 int [] ckey = new int [] { 0, 0, 0, 0 };
700 e2.KeyEvent.display = display;
701 e2.KeyEvent.state = 0;
703 for (int keyc = min_keycode; keyc <= max_keycode; keyc++) {
707 e2.KeyEvent.keycode = keyc;
710 XLookupStatus status;
711 LookupString (ref e2, 0, out t, out status);
715 if ((keysym >> 8) == 0xFF) {
716 vkey = nonchar_key_vkey [keysym & 0xFF];
717 scan = nonchar_key_scan [keysym & 0xFF];
719 if ((scan & 0x100) != 0)
721 } else if (keysym == 0x20) { // spacebar
722 vkey = (int) VirtualKeys.VK_SPACE;
725 // Search layout dependent scancodes
729 for (int i = 0; i < syms; i++) {
730 keysym = XKeycodeToKeysym (display, keyc, i);
731 if ((keysym < 0x800) && (keysym != ' '))
732 ckey [i] = (sbyte) (keysym & 0xFF);
734 ckey [i] = (sbyte) MapDeadKeySym ((int) keysym);
737 for (int keyn = 0; keyn < layout.Keys.Length; keyn++) {
738 int ml = Math.Min (layout.Keys [keyn].Length, 4);
740 for (int i = 0; (ok != 0) && (i < ml); i++) {
741 sbyte ck = (sbyte) layout.Keys [keyn][i];
744 if ((ok != 0) || (i > maxlen)) {
753 if (maxval < layouts.scan_table [(int) layout.ScanIndex].Length)
754 scan = layouts.scan_table [(int) layout.ScanIndex][maxval];
755 if (maxval < layouts.vkey_table [(int) layout.VKeyIndex].Length)
756 vkey = layouts.vkey_table [(int) layout.VKeyIndex][maxval];
760 keyc2vkey [e2.KeyEvent.keycode] = vkey;
761 keyc2scan [e2.KeyEvent.keycode] = scan;
767 private KeyboardLayout DetectLayout (KeyboardLayouts layouts)
769 XDisplayKeycodes (display, out min_keycode, out max_keycode);
770 IntPtr ksp = XGetKeyboardMapping (display, (byte) min_keycode,
771 max_keycode + 1 - min_keycode, out keysyms_per_keycode);
773 XplatUIX11.XFree (ksp);
776 syms = keysyms_per_keycode;
778 //Console.Error.WriteLine ("{0} keysymbols per a keycode is not supported, setting to 4", syms);
782 IntPtr modmap_unmanaged;
783 XModifierKeymap xmk = new XModifierKeymap ();
785 modmap_unmanaged = XGetModifierMapping (display);
786 xmk = (XModifierKeymap) Marshal.PtrToStructure (modmap_unmanaged, typeof (XModifierKeymap));
789 for (int i = 0; i < 8; i++) {
790 for (int j = 0; j < xmk.max_keypermod; j++, mmp++) {
791 byte b = Marshal.ReadByte (xmk.modifiermap, mmp);
793 for (int k = 0; k < keysyms_per_keycode; k++) {
794 if ((int) XKeycodeToKeysym (display, b, k) == (int) MiscKeys.XK_Num_Lock)
795 NumLockMask = 1 << i;
800 XFreeModifiermap (modmap_unmanaged);
802 int [] ckey = new int [4];
803 KeyboardLayout layout = null;
807 foreach (KeyboardLayout current in layouts.Layouts) {
814 int key = min_keycode;
817 for (int keyc = min_keycode; keyc <= max_keycode; keyc++) {
818 for (i = 0; i < syms; i++) {
819 uint keysym = XKeycodeToKeysym (display, keyc, i);
821 if ((keysym < 0x800) && (keysym != ' ')) {
822 ckey [i] = (sbyte) (keysym & 0xFF);
824 ckey [i] = (sbyte) MapDeadKeySym ((int) keysym);
828 for (key = 0; key < current.Keys.Length; key++) {
829 int ml = Math.Min (syms, current.Keys [key].Length);
830 for (ok = 0, i = 0; (ok >= 0) && (i < ml); i++) {
831 sbyte ck = (sbyte) current.Keys [key][i];
832 if (ck != 0 && ck == ckey[i])
834 if (ck != 0 && ck != ckey[i])
844 /* and how much the keycode order matches */
849 /* print spaces instead of \0's */
856 if ((score > max_score) || ((score == max_score) && (seq > max_seq))) {
864 if (layout != null) {
867 Console.WriteLine (Locale.GetText("Keyboard layout not recognized, using default layout: " +
868 layouts.Layouts [0].Name));
871 return layouts.Layouts [0];
875 private int MapDeadKeySym (int val)
878 case (int) DeadKeys.XK_dead_tilde :
879 case 0x1000FE7E : // Xfree's Dtilde
881 case (int) DeadKeys.XK_dead_acute :
882 case 0x1000FE27 : // Xfree's XK_Dacute_accent
884 case (int) DeadKeys.XK_dead_circumflex:
885 case 0x1000FE5E : // Xfree's XK_.Dcircumflex_accent
887 case (int) DeadKeys.XK_dead_grave :
888 case 0x1000FE60 : // Xfree's XK_.Dgrave_accent
890 case (int) DeadKeys.XK_dead_diaeresis :
891 case 0x1000FE22 : // Xfree's XK_.Ddiaeresis
893 case (int) DeadKeys.XK_dead_cedilla :
895 case (int) DeadKeys.XK_dead_macron :
897 case (int) DeadKeys.XK_dead_breve :
899 case (int) DeadKeys.XK_dead_abovedot :
901 case (int) DeadKeys.XK_dead_abovering :
903 case (int) DeadKeys.XK_dead_doubleacute :
905 case (int) DeadKeys.XK_dead_caron :
907 case (int) DeadKeys.XK_dead_ogonek :
914 private XIMProperties [] GetSupportedInputStyles (IntPtr xim)
917 string ret = XGetIMValues (xim, XNames.XNQueryInputStyle, out stylesPtr, IntPtr.Zero);
918 if (ret != null || stylesPtr == IntPtr.Zero)
919 return new XIMProperties [0];
920 XIMStyles styles = (XIMStyles) Marshal.PtrToStructure (stylesPtr, typeof (XIMStyles));
921 XIMProperties [] supportedStyles = new XIMProperties [styles.count_styles];
922 for (int i = 0; i < styles.count_styles; i++)
923 supportedStyles [i] = (XIMProperties) Marshal.PtrToStructure (new IntPtr ((long) styles.supported_styles + i * Marshal.SizeOf (typeof (IntPtr))), typeof (XIMProperties));
925 XplatUIX11.XFree (stylesPtr);
927 return supportedStyles;
930 const XIMProperties styleRoot = XIMProperties.XIMPreeditNothing | XIMProperties.XIMStatusNothing;
931 const XIMProperties styleOverTheSpot = XIMProperties.XIMPreeditPosition | XIMProperties.XIMStatusNothing;
932 const XIMProperties styleOnTheSpot = XIMProperties.XIMPreeditCallbacks | XIMProperties.XIMStatusNothing;
933 const string ENV_NAME_XIM_STYLE = "MONO_WINFORMS_XIM_STYLE";
935 private XIMProperties [] GetPreferredStyles ()
937 string env = Environment.GetEnvironmentVariable (ENV_NAME_XIM_STYLE);
939 env = "over-the-spot";
940 string [] list = env.Split (' ');
941 XIMProperties [] ret = new XIMProperties [list.Length];
942 for (int i = 0; i < list.Length; i++) {
945 case "over-the-spot":
946 ret [i] = styleOverTheSpot;
949 ret [i] = styleOnTheSpot;
959 private IEnumerable GetMatchingStylesInPreferredOrder (IntPtr xim)
961 XIMProperties [] supportedStyles = GetSupportedInputStyles (xim);
962 foreach (XIMProperties p in GetPreferredStyles ())
963 if (Array.IndexOf (supportedStyles, p) >= 0)
967 private IntPtr CreateXic (IntPtr window, IntPtr xim)
969 IntPtr xic = IntPtr.Zero;
970 foreach (XIMProperties targetStyle in GetMatchingStylesInPreferredOrder (xim)) {
971 ximStyle = targetStyle;
972 // FIXME: use __arglist when it gets working. See bug #321686
973 switch (targetStyle) {
974 case styleOverTheSpot:
975 xic = CreateOverTheSpotXic (window, xim);
976 if (xic != IntPtr.Zero)
978 //Console.WriteLine ("failed to create XIC in over-the-spot mode.");
981 // Since .NET/Winforms seems to support only over-the-spot mode,,
982 // I'm not likely to continue on-the-spot implementation. But in
983 // case we need it, this code will be still useful.
984 xic = CreateOnTheSpotXic (window, xim);
985 if (xic != IntPtr.Zero)
987 //Console.WriteLine ("failed to create XIC in on-the-spot mode.");
990 xic = XCreateIC (xim,
991 XNames.XNInputStyle, styleRoot,
992 XNames.XNClientWindow, window,
997 // fall back to root mode if all modes failed
998 if (xic == IntPtr.Zero) {
999 ximStyle = styleRoot;
1000 xic = XCreateIC (xim,
1001 XNames.XNInputStyle, styleRoot,
1002 XNames.XNClientWindow, window,
1003 XNames.XNFocusWindow, window,
1009 private IntPtr CreateOverTheSpotXic (IntPtr window, IntPtr xim)
1013 Control c = Control.FromHandle (window);
1014 string xlfd = String.Format ("-*-*-*-*-*-*-{0}-*-*-*-*-*-*-*", (int) c.Font.Size);
1015 IntPtr fontSet = XCreateFontSet (display, xlfd, out list, out count, IntPtr.Zero);
1016 XPoint spot = new XPoint ();
1019 IntPtr pSL = IntPtr.Zero, pFS = IntPtr.Zero;
1021 pSL = Marshal.StringToHGlobalAnsi (XNames.XNSpotLocation);
1022 pFS = Marshal.StringToHGlobalAnsi (XNames.XNFontSet);
1023 IntPtr preedit = XVaCreateNestedList (0,
1027 return XCreateIC (xim,
1028 XNames.XNInputStyle, styleOverTheSpot,
1029 XNames.XNClientWindow, window,
1030 XNames.XNPreeditAttributes, preedit,
1033 if (pSL != IntPtr.Zero)
1034 Marshal.FreeHGlobal (pSL);
1035 if (pFS != IntPtr.Zero)
1036 Marshal.FreeHGlobal (pFS);
1037 XFreeStringList (list);
1038 //XplatUIX11.XFree (preedit);
1039 //XFreeFontSet (fontSet);
1043 private IntPtr CreateOnTheSpotXic (IntPtr window, IntPtr xim)
1045 callbackContext = new XIMCallbackContext (window);
1046 return callbackContext.CreateXic (window, xim);
1049 class XIMCallbackContext
1051 XIMCallback startCB, doneCB, drawCB, caretCB;
1052 IntPtr pStartCB = IntPtr.Zero, pDoneCB = IntPtr.Zero, pDrawCB = IntPtr.Zero, pCaretCB = IntPtr.Zero;
1053 IntPtr pStartCBN = IntPtr.Zero, pDoneCBN = IntPtr.Zero, pDrawCBN = IntPtr.Zero, pCaretCBN = IntPtr.Zero;
1055 public XIMCallbackContext (IntPtr clientWindow)
1057 startCB = new XIMCallback (clientWindow, DoPreeditStart);
1058 doneCB = new XIMCallback (clientWindow, DoPreeditDone);
1059 drawCB = new XIMCallback (clientWindow, DoPreeditDraw);
1060 caretCB = new XIMCallback (clientWindow, DoPreeditCaret);
1061 pStartCB = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (XIMCallback)));
1062 pDoneCB = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (XIMCallback)));
1063 pDrawCB = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (XIMCallback)));
1064 pCaretCB = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (XIMCallback)));
1065 pStartCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditStartCallback);
1066 pDoneCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditDoneCallback);
1067 pDrawCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditDrawCallback);
1068 pCaretCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditCaretCallback);
1071 ~XIMCallbackContext ()
1073 if (pStartCBN != IntPtr.Zero)
1074 Marshal.FreeHGlobal (pStartCBN);
1075 if (pDoneCBN != IntPtr.Zero)
1076 Marshal.FreeHGlobal (pDoneCBN);
1077 if (pDrawCBN != IntPtr.Zero)
1078 Marshal.FreeHGlobal (pDrawCBN);
1079 if (pCaretCBN != IntPtr.Zero)
1080 Marshal.FreeHGlobal (pCaretCBN);
1082 if (pStartCB != IntPtr.Zero)
1083 Marshal.FreeHGlobal (pStartCB);
1084 if (pDoneCB != IntPtr.Zero)
1085 Marshal.FreeHGlobal (pDoneCB);
1086 if (pDrawCB != IntPtr.Zero)
1087 Marshal.FreeHGlobal (pDrawCB);
1088 if (pCaretCB != IntPtr.Zero)
1089 Marshal.FreeHGlobal (pCaretCB);
1092 int DoPreeditStart (IntPtr xic, IntPtr clientData, IntPtr callData)
1094 Debug.WriteLine ("DoPreeditStart");
1095 XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITSTART, clientData, callData);
1099 int DoPreeditDone (IntPtr xic, IntPtr clientData, IntPtr callData)
1101 Debug.WriteLine ("DoPreeditDone");
1102 XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITDONE, clientData, callData);
1106 int DoPreeditDraw (IntPtr xic, IntPtr clientData, IntPtr callData)
1108 Debug.WriteLine ("DoPreeditDraw");
1109 XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITDRAW, clientData, callData);
1113 int DoPreeditCaret (IntPtr xic, IntPtr clientData, IntPtr callData)
1115 Debug.WriteLine ("DoPreeditCaret");
1116 XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITCARET, clientData, callData);
1120 public IntPtr CreateXic (IntPtr window, IntPtr xim)
1122 Marshal.StructureToPtr (startCB, pStartCB, false);
1123 Marshal.StructureToPtr (doneCB, pDoneCB, false);
1124 Marshal.StructureToPtr (drawCB, pDrawCB, false);
1125 Marshal.StructureToPtr (caretCB, pCaretCB, false);
1126 IntPtr preedit = XVaCreateNestedList (0,
1127 pStartCBN, pStartCB,
1130 pCaretCBN, pCaretCB,
1132 return XCreateIC (xim,
1133 XNames.XNInputStyle, styleOnTheSpot,
1134 XNames.XNClientWindow, window,
1135 XNames.XNPreeditAttributes, preedit,
1140 class XIMPositionContext
1142 public CaretStruct Caret;
1147 internal void SetCaretPos (CaretStruct caret, IntPtr handle, int x, int y)
1149 if (ximStyle != styleOverTheSpot)
1152 if (positionContext == null)
1153 this.positionContext = new XIMPositionContext ();
1155 positionContext.Caret = caret;
1156 positionContext.X = x;
1157 positionContext.Y = y + caret.Height;
1159 MoveCurrentCaretPos ();
1162 internal void MoveCurrentCaretPos ()
1164 if (positionContext == null || ximStyle != styleOverTheSpot || client_window == IntPtr.Zero)
1167 int x = positionContext.X;
1168 int y = positionContext.Y;
1169 CaretStruct caret = positionContext.Caret;
1170 IntPtr xic = GetXic (client_window);
1171 if (xic == IntPtr.Zero)
1173 Control control = Control.FromHandle (client_window);
1174 if (control == null || !control.IsHandleCreated)
1176 control = Control.FromHandle (caret.Hwnd);
1177 if (control == null || !control.IsHandleCreated)
1179 Hwnd hwnd = Hwnd.ObjectFromHandle (client_window);
1186 XplatUIX11.XTranslateCoordinates (display, client_window, client_window, x, y, out dx, out dy, out child);
1189 XPoint spot = new XPoint ();
1190 spot.X = (short) dx;
1191 spot.Y = (short) dy;
1193 IntPtr pSL = IntPtr.Zero;
1195 pSL = Marshal.StringToHGlobalAnsi (XNames.XNSpotLocation);
1196 IntPtr preedit = XVaCreateNestedList (0, pSL, spot, IntPtr.Zero);
1197 XSetICValues (xic, XNames.XNPreeditAttributes, preedit, IntPtr.Zero);
1199 if (pSL != IntPtr.Zero)
1200 Marshal.FreeHGlobal (pSL);
1204 private bool have_Xutf8LookupString = true;
1206 private int LookupString (ref XEvent xevent, int len, out XKeySym keysym, out XLookupStatus status)
1211 status = XLookupStatus.XLookupNone;
1212 IntPtr xic = GetXic (client_window);
1213 if (xic != IntPtr.Zero && have_Xutf8LookupString && xevent.type == XEventName.KeyPress) {
1216 res = Xutf8LookupString (xic, ref xevent, lookup_byte_buffer, 100, out keysym_res, out status);
1217 } catch (EntryPointNotFoundException) {
1218 have_Xutf8LookupString = false;
1220 // call again, this time we'll go through the non-xic clause
1221 return LookupString (ref xevent, len, out keysym, out status);
1223 if (status != XLookupStatus.XBufferOverflow)
1225 lookup_byte_buffer = new byte [lookup_byte_buffer.Length << 1];
1227 lookup_buffer.Length = 0;
1228 string s = Encoding.UTF8.GetString (lookup_byte_buffer, 0, res);
1229 lookup_buffer.Append (s);
1230 keysym = (XKeySym) keysym_res.ToInt32 ();
1233 IntPtr statusPtr = IntPtr.Zero;
1234 lookup_buffer.Length = 0;
1235 res = XLookupString (ref xevent, lookup_buffer, len, out keysym_res, out statusPtr);
1236 keysym = (XKeySym) keysym_res.ToInt32 ();
1241 [DllImport ("libX11")]
1242 private static extern IntPtr XOpenIM (IntPtr display, IntPtr rdb, IntPtr res_name, IntPtr res_class);
1244 [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
1245 private static extern IntPtr XCreateIC (IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, IntPtr terminator);
1246 [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
1247 private static extern IntPtr XCreateIC (IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, string name3, IntPtr value3, IntPtr terminator);
1248 // [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
1249 // private static extern IntPtr XCreateIC (IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, string name3, IntPtr value3, string name4, IntPtr value4, IntPtr terminator);
1251 [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
1252 private static extern IntPtr XVaCreateNestedList (int dummy, IntPtr name0, XPoint value0, IntPtr terminator);
1253 [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
1254 private static extern IntPtr XVaCreateNestedList (int dummy, IntPtr name0, XPoint value0, IntPtr name1, IntPtr value1, IntPtr terminator);
1255 [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
1256 private static extern IntPtr XVaCreateNestedList (int dummy, IntPtr name0, IntPtr value0, IntPtr name1, IntPtr value1, IntPtr name2, IntPtr value2, IntPtr name3, IntPtr value3, IntPtr terminator);
1258 [DllImport ("libX11")]
1259 private static extern IntPtr XCreateFontSet (IntPtr display, string name, out IntPtr list, out int count, IntPtr terminator);
1261 [DllImport ("libX11")]
1262 internal extern static void XFreeFontSet (IntPtr data);
1264 [DllImport ("libX11")]
1265 private static extern void XFreeStringList (IntPtr ptr);
1267 //[DllImport ("libX11")]
1268 //private static extern IntPtr XIMOfIC (IntPtr xic);
1270 [DllImport ("libX11")]
1271 private static extern void XCloseIM (IntPtr xim);
1273 [DllImport ("libX11")]
1274 private static extern void XDestroyIC (IntPtr xic);
1276 [DllImport ("libX11")]
1277 private static extern string XGetIMValues (IntPtr xim, string name, out IntPtr value, IntPtr terminator);
1279 [DllImport ("libX11")]
1280 private static extern string XGetICValues (IntPtr xic, string name, out EventMask value, IntPtr terminator);
1282 [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
1283 private static extern void XSetICValues (IntPtr xic, string name, IntPtr value, IntPtr terminator);
1285 [DllImport ("libX11")]
1286 private static extern void XSetICFocus (IntPtr xic);
1288 [DllImport ("libX11")]
1289 private static extern void XUnsetICFocus (IntPtr xic);
1291 [DllImport ("libX11")]
1292 private static extern string Xutf8ResetIC (IntPtr xic);
1294 [DllImport ("libX11")]
1295 private static extern bool XSupportsLocale ();
1297 [DllImport ("libX11")]
1298 private static extern bool XSetLocaleModifiers (string mods);
1300 [DllImport ("libX11")]
1301 internal extern static int XLookupString(ref XEvent xevent, StringBuilder buffer, int num_bytes, out IntPtr keysym, out IntPtr status);
1302 [DllImport ("libX11")]
1303 internal extern static int Xutf8LookupString(IntPtr xic, ref XEvent xevent, byte [] buffer, int num_bytes, out IntPtr keysym, out XLookupStatus status);
1305 [DllImport ("libX11")]
1306 private static extern IntPtr XGetKeyboardMapping (IntPtr display, byte first_keycode, int keycode_count,
1307 out int keysyms_per_keycode_return);
1309 [DllImport ("libX11")]
1310 private static extern void XDisplayKeycodes (IntPtr display, out int min, out int max);
1312 [DllImport ("libX11", EntryPoint="XKeycodeToKeysym")]
1313 private static extern uint XKeycodeToKeysym (IntPtr display, int keycode, int index);
1315 [DllImport ("libX11")]
1316 private static extern int XKeysymToKeycode (IntPtr display, IntPtr keysym);
1317 private static int XKeysymToKeycode (IntPtr display, int keysym) {
1318 return XKeysymToKeycode(display, (IntPtr)keysym);
1321 [DllImport ("libX11")]
1322 internal extern static IntPtr XGetModifierMapping (IntPtr display);
1324 [DllImport ("libX11")]
1325 internal extern static int XFreeModifiermap (IntPtr modmap);
1328 private readonly static int [] nonchar_key_vkey = new int []
1331 0, 0, 0, 0, 0, 0, 0, 0, /* FF00 */
1333 (int) VirtualKeys.VK_BACK, (int) VirtualKeys.VK_TAB, 0, (int) VirtualKeys.VK_CLEAR, 0, (int) VirtualKeys.VK_RETURN, 0, 0, /* FF08 */
1334 0, 0, 0, (int) VirtualKeys.VK_PAUSE, (int) VirtualKeys.VK_SCROLL, 0, 0, 0, /* FF10 */
1335 0, 0, 0, (int) VirtualKeys.VK_ESCAPE, 0, 0, 0, 0, /* FF18 */
1336 0, 0, (int) VirtualKeys.VK_NONCONVERT, (int) VirtualKeys.VK_CONVERT, 0, 0, 0, 0, /* FF20 */
1337 0, 0, (int) VirtualKeys.VK_OEM_AUTO, 0, 0, 0, 0, 0, /* FF28 */
1339 0, 0, 0, 0, 0, 0, 0, 0, /* FF30 */
1340 0, 0, 0, 0, 0, 0, 0, 0, /* FF38 */
1341 0, 0, 0, 0, 0, 0, 0, 0, /* FF40 */
1342 0, 0, 0, 0, 0, 0, 0, 0, /* FF48 */
1344 (int) VirtualKeys.VK_HOME, (int) VirtualKeys.VK_LEFT, (int) VirtualKeys.VK_UP, (int) VirtualKeys.VK_RIGHT, /* FF50 */
1345 (int) VirtualKeys.VK_DOWN, (int) VirtualKeys.VK_PRIOR, (int) VirtualKeys.VK_NEXT, (int) VirtualKeys.VK_END,
1346 0, 0, 0, 0, 0, 0, 0, 0, /* FF58 */
1348 (int) VirtualKeys.VK_SELECT, (int) VirtualKeys.VK_SNAPSHOT, (int) VirtualKeys.VK_EXECUTE, (int) VirtualKeys.VK_INSERT, 0, 0, 0, 0, /* FF60 */
1349 (int) VirtualKeys.VK_CANCEL, (int) VirtualKeys.VK_HELP, (int) VirtualKeys.VK_CANCEL, (int) VirtualKeys.VK_CANCEL, 0, 0, 0, 0, /* FF68 */
1350 0, 0, 0, 0, 0, 0, 0, 0, /* FF70 */
1352 0, 0, 0, 0, 0, 0, 0, (int) VirtualKeys.VK_NUMLOCK, /* FF78 */
1353 0, 0, 0, 0, 0, 0, 0, 0, /* FF80 */
1354 0, 0, 0, 0, 0, (int) VirtualKeys.VK_RETURN, 0, 0, /* FF88 */
1355 0, 0, 0, 0, 0, (int) VirtualKeys.VK_HOME, (int) VirtualKeys.VK_LEFT, (int) VirtualKeys.VK_UP, /* FF90 */
1356 (int) VirtualKeys.VK_RIGHT, (int) VirtualKeys.VK_DOWN, (int) VirtualKeys.VK_PRIOR, (int) VirtualKeys.VK_NEXT, /* FF98 */
1357 (int) VirtualKeys.VK_END, 0, (int) VirtualKeys.VK_INSERT, (int) VirtualKeys.VK_DELETE,
1358 0, 0, 0, 0, 0, 0, 0, 0, /* FFA0 */
1359 0, 0, (int) VirtualKeys.VK_MULTIPLY, (int) VirtualKeys.VK_ADD, /* FFA8 */
1360 (int) VirtualKeys.VK_SEPARATOR, (int) VirtualKeys.VK_SUBTRACT, (int) VirtualKeys.VK_DECIMAL, (int) VirtualKeys.VK_DIVIDE,
1361 (int) VirtualKeys.VK_NUMPAD0, (int) VirtualKeys.VK_NUMPAD1, (int) VirtualKeys.VK_NUMPAD2, (int) VirtualKeys.VK_NUMPAD3, /* FFB0 */
1362 (int) VirtualKeys.VK_NUMPAD4, (int) VirtualKeys.VK_NUMPAD5, (int) VirtualKeys.VK_NUMPAD6, (int) VirtualKeys.VK_NUMPAD7,
1363 (int) VirtualKeys.VK_NUMPAD8, (int) VirtualKeys.VK_NUMPAD9, 0, 0, 0, 0, /* FFB8 */
1365 (int) VirtualKeys.VK_F1, (int) VirtualKeys.VK_F2,
1366 (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 */
1367 (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 */
1368 0, 0, 0, 0, 0, 0, 0, 0, /* FFD0 */
1369 0, 0, 0, 0, 0, 0, 0, 0, /* FFD8 */
1371 0, (int) VirtualKeys.VK_SHIFT, (int) VirtualKeys.VK_SHIFT, (int) VirtualKeys.VK_CONTROL, /* FFE0 */
1372 (int) VirtualKeys.VK_CONTROL, (int) VirtualKeys.VK_CAPITAL, 0, (int) VirtualKeys.VK_MENU,
1373 (int) VirtualKeys.VK_MENU, (int) VirtualKeys.VK_MENU, (int) VirtualKeys.VK_MENU, 0, 0, 0, 0, 0, /* FFE8 */
1374 0, 0, 0, 0, 0, 0, 0, 0, /* FFF0 */
1375 0, 0, 0, 0, 0, 0, 0, (int) VirtualKeys.VK_DELETE /* FFF8 */
1378 private static readonly int [] nonchar_key_scan = new int []
1381 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF00 */
1383 0x0E, 0x0F, 0x00, /*?*/ 0, 0x00, 0x1C, 0x00, 0x00, /* FF08 */
1384 0x00, 0x00, 0x00, 0x45, 0x46, 0x00, 0x00, 0x00, /* FF10 */
1385 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, /* FF18 */
1387 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF20 */
1388 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF28 */
1389 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF30 */
1390 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF38 */
1391 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF40 */
1392 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF48 */
1394 0x147, 0x14B, 0x148, 0x14D, 0x150, 0x149, 0x151, 0x14F, /* FF50 */
1395 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF58 */
1397 /*?*/ 0, 0x137, /*?*/ 0, 0x152, 0x00, 0x00, 0x00, 0x00, /* FF60 */
1398 /*?*/ 0, /*?*/ 0, 0x38, 0x146, 0x00, 0x00, 0x00, 0x00, /* FF68 */
1399 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF70 */
1401 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x138, 0x145, /* FF78 */
1402 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF80 */
1403 0x00, 0x00, 0x00, 0x00, 0x00, 0x11C, 0x00, 0x00, /* FF88 */
1404 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x4B, 0x48, /* FF90 */
1405 0x4D, 0x50, 0x49, 0x51, 0x4F, 0x4C, 0x52, 0x53, /* FF98 */
1406 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFA0 */
1407 0x00, 0x00, 0x37, 0x4E, /*?*/ 0, 0x4A, 0x53, 0x135, /* FFA8 */
1408 0x52, 0x4F, 0x50, 0x51, 0x4B, 0x4C, 0x4D, 0x47, /* FFB0 */
1409 0x48, 0x49, 0x00, 0x00, 0x00, 0x00, /* FFB8 */
1412 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, /* FFC0 */
1413 0x57, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFC8 */
1414 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFD0 */
1415 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFD8 */
1417 0x00, 0x2A, 0x36, 0x1D, 0x11D, 0x3A, 0x00, 0x38, /* FFE0 */
1418 0x138, 0x38, 0x138, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFE8 */
1419 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFF0 */
1420 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x153 /* FFF8 */
1423 private readonly static int [] nonchar_vkey_key = new int []
1425 0, 0, 0, 0, 0, /* 00-04 */
1426 0, 0, 0, (int)XKeySym.XK_BackSpace, (int)XKeySym.XK_Tab, /* 05-09 */
1427 0, 0, (int)XKeySym.XK_Clear, (int)XKeySym.XK_Return, 0, 0, /* 0A-0F */
1428 (int)XKeySym.XK_Shift_L, (int)XKeySym.XK_Control_L, (int)XKeySym.XK_Menu, 0, (int)XKeySym.XK_Caps_Lock, /* 10-14 */
1429 0, 0, 0, 0, 0, /* 15-19 */
1430 0, 0, 0, 0, 0, 0, /* 1A-1F */
1431 0, 0, 0, (int)XKeySym.XK_End, (int)XKeySym.XK_Home, /* 20-24 */
1432 (int)XKeySym.XK_Left, (int)XKeySym.XK_Up, (int)XKeySym.XK_Right, (int)XKeySym.XK_Down, 0, /* 25-29 */
1433 0, 0, 0, 0, 0, 0, /* 2A-2F */
1434 0, 0, 0, 0, 0, /* 30-34 */
1435 0, 0, 0, 0, 0, /* 35-39 */
1436 0, 0, 0, 0, 0, 0, /* 3A-3F */
1437 0, 0, 0, 0, 0, /* 40-44 */
1438 0, 0, 0, 0, 0, /* 45-49 */
1439 0, 0, 0, 0, 0, 0, /* 4A-4F */
1440 0, 0, 0, 0, 0, /* 50-54 */
1441 0, 0, 0, 0, 0, /* 55-59 */
1442 0, (int)XKeySym.XK_Meta_L, (int)XKeySym.XK_Meta_R, 0, 0, 0, /* 5A-5F */
1443 0, 0, 0, 0, 0, /* 60-64 */
1444 0, 0, 0, 0, 0, /* 65-69 */
1445 0, 0, 0, 0, 0, 0, /* 6A-6F */
1446 0, 0, 0, 0, 0, /* 70-74 */
1447 0, 0, 0, 0, 0, /* 75-79 */
1448 0, 0, 0, 0, 0, 0, /* 7A-7F */
1449 0, 0, 0, 0, 0, /* 80-84 */
1450 0, 0, 0, 0, 0, /* 85-89 */
1451 0, 0, 0, 0, 0, 0, /* 8A-8F */
1452 0, 0, 0, 0, 0, /* 90-94 */
1453 0, 0, 0, 0, 0, /* 95-99 */
1454 0, 0, 0, 0, 0, 0, /* 9A-9F */
1455 (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 */
1456 (int)XKeySym.XK_Alt_R, 0, 0, 0, 0, /* A5-A9 */
1457 0, 0, 0, 0, 0, 0, /* AA-AF */
1458 0, 0, 0, 0, 0, /* B0-B4 */
1459 0, 0, 0, 0, 0, /* B5-B9 */
1460 0, 0, 0, 0, 0, 0, /* BA-BF */
1461 0, 0, 0, 0, 0, /* C0-C4 */
1462 0, 0, 0, 0, 0, /* C5-C9 */
1463 0, 0, 0, 0, 0, 0, /* CA-CF */
1464 0, 0, 0, 0, 0, /* D0-D4 */
1465 0, 0, 0, 0, 0, /* D5-D9 */
1466 0, 0, 0, 0, 0, 0, /* DA-DF */
1467 0, 0, 0, 0, 0, /* E0-E4 */
1468 0, 0, 0, 0, 0, /* E5-E9 */
1469 0, 0, 0, 0, 0, 0, /* EA-EF */
1470 0, 0, 0, 0, 0, /* F0-F4 */
1471 0, 0, 0, 0, 0, /* F5-F9 */
1472 0, 0, 0, 0, 0, 0 /* FA-FF */