[PATCH 2/2] Improve code readability
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / X11Keyboard.cs
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:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
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.
19 //
20 // Copyright (c) 2004 Novell, Inc.
21 //
22 // Authors:
23 //      Jackson Harper (jackson@ximian.com)
24 //
25 //
26
27
28 //
29 // TODO:
30 //  - dead chars are not translated properly
31 //  - There is a lot of potential for optimmization in here
32 // 
33 using System;
34 using System.Collections;
35 using System.Diagnostics;
36 using System.Drawing;
37 using System.Text;
38 using System.Globalization;
39 using System.Runtime.InteropServices;
40
41 namespace System.Windows.Forms {
42
43         public enum XLookupStatus
44         {
45                 XBufferOverflow = -1,
46                 XLookupNone = 1,
47                 XLookupChars = 2,
48                 XLookupKeySym = 3,
49                 XLookupBoth = 4
50         }
51
52         internal class X11Keyboard : IDisposable {
53                 internal static object XlibLock;
54
55                 private IntPtr display;
56                 private IntPtr client_window;
57                 private IntPtr xim;
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];
69                 private int lcid;
70                 private bool num_state, cap_state;
71                 private bool initialized;
72                 private bool menu_state = false;
73                 private Encoding encoding;
74
75                 private int NumLockMask;
76                 private int AltGrMask;
77
78                 public X11Keyboard (IntPtr display, IntPtr clientWindow)
79                 {
80                         this.display = display;
81                         lookup_buffer = new StringBuilder (24);
82                         EnsureLayoutInitialized ();
83                 }
84
85                 private Encoding AnsiEncoding
86                 {
87                         get
88                         {
89                                 if (encoding == null)
90                                         encoding = Encoding.GetEncoding(new CultureInfo(lcid).TextInfo.ANSICodePage);
91                                 return encoding;
92                         }
93                 }
94
95                 public IntPtr ClientWindow {
96                         get { return client_window; }
97                 }
98
99                 void IDisposable.Dispose ()
100                 {
101                         if (xim != IntPtr.Zero) {
102                                 foreach (IntPtr xic in xic_table.Values)
103                                         XDestroyIC (xic);
104                                 xic_table.Clear ();
105
106                                 XCloseIM (xim);
107                                 xim = IntPtr.Zero;
108                         }
109                 }
110
111                 public void DestroyICForWindow (IntPtr window)
112                 {
113                         IntPtr xic = GetXic (window);
114                         if (xic != IntPtr.Zero) {
115                                 xic_table.Remove ((long) window);
116                                 XDestroyIC (xic);
117                         }
118                 }
119
120                 public void EnsureLayoutInitialized ()
121                 {
122                         if (initialized)
123                                 return;
124
125                         KeyboardLayouts layouts = new KeyboardLayouts ();
126                         KeyboardLayout layout = DetectLayout (layouts);
127                         lcid = layout.Lcid;
128                         CreateConversionArray (layouts, layout);
129                         SetupXIM ();
130                         initialized = true;
131                 }
132
133                 private void SetupXIM ()
134                 {
135                         xim = IntPtr.Zero;
136
137                         if (!XSupportsLocale ()) {
138                                 Console.Error.WriteLine ("X does not support your locale");
139                                 return;
140                         }
141
142                         if (!XSetLocaleModifiers (String.Empty)) {
143                                 Console.Error.WriteLine ("Could not set X locale modifiers");
144                                 return;
145                         }
146
147                         if (Environment.GetEnvironmentVariable (ENV_NAME_XIM_STYLE) == "disabled") {
148                                 return;
149                         }
150
151                         xim = XOpenIM (display, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
152                         if (xim == IntPtr.Zero) 
153                                 Console.Error.WriteLine ("Could not get XIM");
154
155                         initialized = true;
156                 }
157
158                 void CreateXicForWindow (IntPtr window)
159                 {
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");
164                         else {
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;
170                                         lock (XlibLock) {
171                                                 XplatUIX11.XSelectInput(display, window, new IntPtr ((int) xic_event_mask));
172                                         }
173                                 }
174                         }
175                 }
176
177                 public EventMask KeyEventMask {
178                         get { return xic_event_mask; }
179                 }
180                 
181                 public Keys ModifierKeys {
182                         get {
183                                 Keys keys = Keys.None;
184                                 if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0)
185                                         keys |= Keys.Shift;
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)
189                                         keys |= Keys.Alt;
190                                 return keys;
191                         }
192                 }
193
194                 private IntPtr GetXic (IntPtr window)
195                 {
196                         if (xim != IntPtr.Zero && xic_table.ContainsKey ((long) window))
197                                 return (IntPtr) xic_table [(long) window];
198                         else
199                                 return IntPtr.Zero;
200                 }
201
202                 private bool FilterKey (XEvent e, int vkey)
203                 {
204                         if (XplatUI.key_filters.Count == 0)
205                                 return false;
206                         XLookupStatus status;
207                         XKeySym ks;
208                         KeyFilterData data;
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);
216                 }
217
218                 public void FocusIn (IntPtr window)
219                 {
220                         this.client_window = window;
221                         if (xim == IntPtr.Zero)
222                                 return;
223
224                         if (!xic_table.ContainsKey ((long) window))
225                                 CreateXicForWindow (window);
226                         IntPtr xic = GetXic (window);
227                         if (xic != IntPtr.Zero)
228                                 XSetICFocus (xic);
229                 }
230
231                 private bool have_Xutf8ResetIC = true;
232
233                 public void FocusOut (IntPtr window)
234                 {
235                         this.client_window = IntPtr.Zero;
236                         if (xim == IntPtr.Zero)
237                                 return;
238
239                         IntPtr xic = GetXic (window);
240                         if (xic != IntPtr.Zero) {
241                                 if (have_Xutf8ResetIC) {
242                                         try {
243                                                 Xutf8ResetIC (xic);
244                                         } catch (EntryPointNotFoundException) {
245                                                 have_Xutf8ResetIC = false;
246                                         }
247                                 }
248                                 XUnsetICFocus (xic);
249                         }
250                 }
251
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);
257                         }
258
259                         if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) {
260                                 key_state_table [(int) VirtualKeys.VK_CONTROL] &=  unchecked((byte)~0x80);
261                         }
262
263                         if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0) {
264                                 key_state_table [(int) VirtualKeys.VK_MENU] &=  unchecked((byte)~0x80);
265                         }
266                         return false;
267                 }
268
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)
272                 {
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)
277                                 return;
278
279                         int vkey = keyc2vkey [xevent.KeyEvent.keycode];
280
281                         switch (xevent.type) {
282                         case XEventName.KeyRelease:
283                                 key_state_table [vkey & 0xff] &= unchecked ((byte) ~0x80);
284                                 break;
285                         case XEventName.KeyPress:
286                                 if ((key_state_table [vkey & 0xff] & 0x80) == 0) {
287                                         key_state_table [vkey & 0xff] ^= 0x01;
288                                 }
289                                 key_state_table [vkey & 0xff] |= 0x80;
290                                 break;
291                         }
292                 }
293
294                 public void KeyEvent (IntPtr hwnd, XEvent xevent, ref MSG msg)
295                 {
296                         XKeySym keysym;
297                         int ascii_chars;
298
299                         XLookupStatus status;
300                         ascii_chars = LookupString (ref xevent, 24, out keysym, out status);
301
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);
306                                 return;
307                         }
308
309                         if ((xevent.KeyEvent.keycode >> 8) == 0x10)
310                                 xevent.KeyEvent.keycode = xevent.KeyEvent.keycode & 0xFF;
311
312                         int event_time = (int)xevent.KeyEvent.time;
313
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));
317                                 msg.hwnd = hwnd;
318                                 return;
319                         }
320
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;
325                         }
326
327                         if (FilterKey (xevent, vkey))
328                                 return;
329                         switch ((VirtualKeys) (vkey & 0xFF)) {
330                         case VirtualKeys.VK_NUMLOCK:
331                                 GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, xevent.KeyEvent.keycode, xevent.type, event_time);
332                                 break;
333                         case VirtualKeys.VK_CAPITAL:
334                                 GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, xevent.KeyEvent.keycode, xevent.type, event_time);
335                                 break;
336                         default:
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);
340                                 }
341
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);
345                                 }
346
347                                 num_state = false;
348                                 cap_state = false;
349
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);
357                                 msg.hwnd = hwnd;
358                                 break;
359                         }
360                 }
361
362                 public bool TranslateMessage (ref MSG msg)
363                 {
364                         bool res = false;
365
366                         if (msg.message >= Msg.WM_KEYFIRST && msg.message <= Msg.WM_KEYLAST)
367                                 res = true;
368
369                         if (msg.message == Msg.WM_SYSKEYUP && msg.wParam == (IntPtr) 0x12 && menu_state) {
370                                 msg.message = Msg.WM_KEYUP;
371                                 menu_state = false;
372                         }
373
374                         if (msg.message != Msg.WM_KEYDOWN && msg.message != Msg.WM_SYSKEYDOWN)
375                                 return res;
376
377                         if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 && msg.wParam != (IntPtr) 0x12)
378                                 menu_state = true;
379
380                         EnsureLayoutInitialized ();
381
382                         string buffer;
383                         Msg message;
384                         int tu = ToUnicode ((int) msg.wParam, Control.HighOrder ((int) msg.lParam), out buffer);
385                         switch (tu) {
386                         case 1:
387                                 message = (msg.message == Msg.WM_KEYDOWN ? Msg.WM_CHAR : Msg.WM_SYSCHAR);
388                                 XplatUI.PostMessage (msg.hwnd, message, (IntPtr) buffer [0], msg.lParam);
389                                 break;
390                         case -1:
391                                 message = (msg.message == Msg.WM_KEYDOWN ? Msg.WM_DEADCHAR : Msg.WM_SYSDEADCHAR);
392                                 XplatUI.PostMessage (msg.hwnd, message, (IntPtr) buffer [0], msg.lParam);
393                                 return true;
394                         }
395                         
396                         return res;
397                 }
398
399                 public int ToKeycode(int key) 
400                 {
401                         int keycode = 0;
402                         
403                         if (nonchar_vkey_key[key] > 0)
404                                 keycode = XKeysymToKeycode (display, nonchar_vkey_key[key]);
405                         
406                         if (keycode == 0)
407                                 keycode = XKeysymToKeycode (display, key);
408
409                         return keycode;         
410                 }
411
412                 public int ToUnicode (int vkey, int scan, out string buffer)
413                 {
414                         if ((scan & 0x8000) != 0) {
415                                 buffer = String.Empty;
416                                 return 0;
417                         }
418
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;
424
425                         if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0) {
426                                 e.KeyEvent.state |= (int) KeyMasks.ShiftMask;
427                         }
428
429                         if ((key_state_table [(int) VirtualKeys.VK_CAPITAL] & 0x01) != 0) {
430                                 e.KeyEvent.state |= (int) KeyMasks.LockMask;
431                         }
432
433                         if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) {
434                                 e.KeyEvent.state |= (int) KeyMasks.ControlMask;
435                         }
436
437                         if ((key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x01) != 0) {
438                                 e.KeyEvent.state |= NumLockMask;
439                         }
440
441                         e.KeyEvent.state |= AltGrMask;
442
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;
451                                         }
452                                 }
453                         }
454
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);
457
458                         if (vkey == (int) VirtualKeys.VK_DECIMAL)
459                                 e.KeyEvent.keycode = XKeysymToKeycode (display, (int) KeypadKeys.XK_KP_Decimal);
460                         
461                         if (vkey == (int) VirtualKeys.VK_SEPARATOR)
462                                 e.KeyEvent.keycode = XKeysymToKeycode(display, (int) KeypadKeys.XK_KP_Separator);
463
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;
468                                 return vkey; 
469                         }
470
471                         XKeySym t;
472                         XLookupStatus status;
473                         int res = LookupString (ref e, 24, out t, out status);
474                         int keysym = (int) t;
475
476                         buffer = String.Empty;
477                         if (res == 0) {
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));
483                                         res = -1;
484                                 }
485                         } else {
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;
491                                         res = 0;
492                                 }
493
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;
498                                                 res = 0;
499                                         }
500                                 }
501
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;
505                                         res = 0;
506                                 }
507
508                                 if (keysym == (int) TtyKeys.XK_BackSpace && (key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) {
509                                         buffer = new string (new char [] { (char) 127 });
510
511                                         return 1;
512                                 }
513
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 });
519                                         return 1;
520                                 }
521                                 if (keysym == (int) XKeySym.XK_Return) {
522                                         buffer = new string (new char [] { (char)13 });
523                                         return 1;
524                                 }
525
526                                 if (res != 0) {
527                                         buffer = lookup_buffer.ToString ();
528                                         res = buffer.Length;
529                                 }
530                         }
531
532                         return res;
533                 }
534
535                 string stored_keyevent_string;
536
537                 internal string GetCompositionString ()
538                 {
539                         return stored_keyevent_string;
540                 }
541
542                 private MSG SendImeComposition (string s)
543                 {
544                         MSG msg = new MSG ();
545                         msg.message = Msg.WM_IME_COMPOSITION;
546                         msg.refobject = s;
547                         stored_keyevent_string = s;
548                         return msg;
549                 }
550
551                 private MSG SendKeyboardInput (VirtualKeys vkey, int scan, int keycode, KeybdEventFlags dw_flags, int time)
552                 {
553                         Msg message;
554
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);
560                         } else {
561                                 if ((key_state_table [(int) vkey] & 0x80) == 0) {
562                                         key_state_table [(int) vkey] ^= 0x01;
563                                 }
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);
568                         }
569
570                         MSG msg = new MSG ();
571                         msg.message = message;
572                         msg.wParam = (IntPtr) vkey;
573                         msg.lParam = GenerateLParam (msg, keycode);
574                         return msg;
575                 }
576
577                 private IntPtr GenerateLParam (MSG m, int keyCode)
578                 {
579                         // http://msdn.microsoft.com/en-us/library/ms646267(VS.85).aspx
580                         //
581                         byte flags = 0;
582
583                         if (m.message == Msg.WM_SYSKEYUP || m.message == Msg.WM_KEYUP)
584                                 flags |= 0x80; // transition state flag = 1
585
586                         flags |= 0x40; // previous key state flag = 1
587
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
592
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
610
611                         int lparam = ((((int)flags) & 0x000000FF) << 3*8); // message flags
612                         lparam |= ((keyCode & 0x000000FF) << 2*8); // scan code
613                         lparam |= 0x00000001; // repeat count = 1
614
615                         return (IntPtr)lparam;
616                 }
617
618                 private void GenerateMessage (VirtualKeys vkey, int scan, int key_code, XEventName type, int event_time)
619                 {
620                         bool state = (vkey == VirtualKeys.VK_NUMLOCK ? num_state : cap_state);
621                         KeybdEventFlags up, down;
622
623                         if (state) {
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);
628                         } else {
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);
638                                         }
639                                 } else {
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;
645                                         }
646                                 }
647                         }
648                 }
649
650                 private void UpdateKeyState (XEvent xevent)
651                 {
652                         int vkey = EventToVkey (xevent);
653
654                         switch (xevent.type) {
655                         case XEventName.KeyRelease:
656                                 key_state_table [vkey & 0xff] &= unchecked ((byte) ~0x80);
657                                 break;
658                         case XEventName.KeyPress:
659                                 if ((key_state_table [vkey & 0xff] & 0x80) == 0) {
660                                         key_state_table [vkey & 0xff] ^= 0x01;
661                                 }
662                                 key_state_table [vkey & 0xff] |= 0x80;
663                                 break;
664                         }
665                 }
666
667                 private void SetState (VirtualKeys key, bool state)
668                 {
669                         if (VirtualKeys.VK_NUMLOCK == key)
670                                 num_state = state;
671                         else
672                                 cap_state = state;
673                 }
674
675                 public int EventToVkey (XEvent e)
676                 {
677                         XLookupStatus status;
678                         XKeySym ks;
679
680                         LookupString (ref e, 0, out ks, out status);
681                         int keysym = (int) ks;
682
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];
689                         }
690
691                         return keyc2vkey [e.KeyEvent.keycode];
692                 }
693
694                 private void CreateConversionArray (KeyboardLayouts layouts, KeyboardLayout layout)
695                 {
696                         XEvent e2 = new XEvent ();
697                         uint keysym = 0;
698                         int [] ckey = new int [] { 0, 0, 0, 0 };
699
700                         e2.KeyEvent.display = display;
701                         e2.KeyEvent.state = 0;
702
703                         for (int keyc = min_keycode; keyc <= max_keycode; keyc++) {
704                                 int vkey = 0;
705                                 int scan = 0;
706
707                                 e2.KeyEvent.keycode = keyc;
708                                 XKeySym t;
709
710                                 XLookupStatus status;
711                                 LookupString (ref e2, 0, out t, out status);
712
713                                 keysym = (uint) t;
714                                 if (keysym != 0) {
715                                         if ((keysym >> 8) == 0xFF) {
716                                                 vkey = nonchar_key_vkey [keysym & 0xFF];
717                                                 scan = nonchar_key_scan [keysym & 0xFF];
718                                                 // Set extended bit
719                                                 if ((scan & 0x100) != 0)
720                                                         vkey |= 0x100;
721                                         } else if (keysym == 0x20) { // spacebar
722                                                 vkey = (int) VirtualKeys.VK_SPACE;
723                                                 scan = 0x39;
724                                         } else {
725                                                 // Search layout dependent scancodes
726                                                 int maxlen = 0;
727                                                 int maxval = -1;;
728                                                 
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);
733                                                         else
734                                                                 ckey [i] = (sbyte) MapDeadKeySym ((int) keysym);
735                                                 }
736                                                 
737                                                 for (int keyn = 0; keyn < layout.Keys.Length; keyn++) {
738                                                         int ml = Math.Min (layout.Keys [keyn].Length, 4);
739                                                         int ok = -1;
740                                                         for (int i = 0; (ok != 0) && (i < ml); i++) {
741                                                                 sbyte ck = (sbyte) layout.Keys [keyn][i];
742                                                                 if (ck != ckey [i])
743                                                                         ok = 0;
744                                                                 if ((ok != 0) || (i > maxlen)) {
745                                                                         maxlen = i;
746                                                                         maxval = keyn;
747                                                                 }
748                                                                 if (ok != 0)
749                                                                         break;
750                                                         }
751                                                 }
752                                                 if (maxval >= 0) {
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];
757                                                 }
758                                         }
759                                 }
760                                 keyc2vkey [e2.KeyEvent.keycode] = vkey;
761                                 keyc2scan [e2.KeyEvent.keycode] = scan;
762                         }
763                         
764                         
765                 }
766
767                 private KeyboardLayout DetectLayout (KeyboardLayouts layouts)
768                 {
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);
772                         lock (XlibLock) {
773                                 XplatUIX11.XFree (ksp);
774                         }
775
776                         syms = keysyms_per_keycode;
777                         if (syms > 4) {
778                                 //Console.Error.WriteLine ("{0} keysymbols per a keycode is not supported, setting to 4", syms);
779                                 syms = 2;
780                         }
781
782                         IntPtr  modmap_unmanaged;
783                         XModifierKeymap xmk = new XModifierKeymap ();
784
785                         modmap_unmanaged = XGetModifierMapping (display);
786                         xmk = (XModifierKeymap) Marshal.PtrToStructure (modmap_unmanaged, typeof (XModifierKeymap));
787
788                         int mmp = 0;
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);
792                                         if (b != 0) {
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;
796                                                 }
797                                         }
798                                 }
799                         }
800                         XFreeModifiermap (modmap_unmanaged);
801
802                         int [] ckey = new int [4];
803                         KeyboardLayout layout = null;
804                         int max_score = 0;
805                         int max_seq = 0;
806                         
807                         foreach (KeyboardLayout current in layouts.Layouts) {
808                                 int ok = 0;
809                                 int score = 0;
810                                 int match = 0;
811                                 int mismatch = 0;
812                                 int seq = 0;
813                                 int pkey = -1;
814                                 int key = min_keycode;
815                                 int i;
816
817                                 for (int keyc = min_keycode; keyc <= max_keycode; keyc++) {
818                                         for (i = 0; i < syms; i++) {
819                                                 uint keysym = XKeycodeToKeysym (display, keyc, i);
820                                                 
821                                                 if ((keysym < 0x800) && (keysym != ' ')) {
822                                                         ckey [i] = (sbyte) (keysym & 0xFF);
823                                                 } else {
824                                                         ckey [i] = (sbyte) MapDeadKeySym ((int) keysym);
825                                                 }
826                                         }
827                                         if (ckey [0] != 0) {
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])
833                                                                         ok++;
834                                                                 if (ck != 0 && ck != ckey[i])
835                                                                         ok = -1;
836                                                         }
837                                                         if (ok > 0) {
838                                                                 score += ok;
839                                                                 break;
840                                                         }
841                                                 }
842                                                 if (ok > 0) {
843                                                         match++;
844                                                         /* and how much the keycode order matches */
845                                                         if (key > pkey)
846                                                                 seq++;
847                                                         pkey = key;
848                                                 } else {
849                                                         /* print spaces instead of \0's */
850                                                         mismatch++;
851                                                         score -= syms;
852                                                 }
853                                         }
854                                 }
855
856                                 if ((score > max_score) || ((score == max_score) && (seq > max_seq))) {
857                                         // best match so far
858                                         layout = current;
859                                         max_score = score;
860                                         max_seq = seq;
861                                 }
862                         }
863
864                         if (layout != null)  {
865                                 return layout;
866                         } else {
867                                 Console.WriteLine (Locale.GetText("Keyboard layout not recognized, using default layout: " +
868                                                                    layouts.Layouts [0].Name));
869                         }
870
871                         return layouts.Layouts [0];
872                 }
873
874                 // TODO
875                 private int MapDeadKeySym (int val)
876                 {
877                         switch (val) {
878                         case (int) DeadKeys.XK_dead_tilde :
879                         case 0x1000FE7E : // Xfree's Dtilde
880                                 return '~';
881                         case (int) DeadKeys.XK_dead_acute :
882                         case 0x1000FE27 : // Xfree's XK_Dacute_accent
883                                 return 0xb4;
884                         case (int) DeadKeys.XK_dead_circumflex:
885                         case 0x1000FE5E : // Xfree's XK_.Dcircumflex_accent
886                                 return '^';
887                         case (int) DeadKeys.XK_dead_grave :
888                         case 0x1000FE60 : // Xfree's XK_.Dgrave_accent
889                                 return '`';
890                         case (int) DeadKeys.XK_dead_diaeresis :
891                         case 0x1000FE22 : // Xfree's XK_.Ddiaeresis
892                                 return 0xa8;
893                         case (int) DeadKeys.XK_dead_cedilla :
894                                 return 0xb8;
895                         case (int) DeadKeys.XK_dead_macron :
896                                 return '-';
897                         case (int) DeadKeys.XK_dead_breve :
898                                 return 0xa2;
899                         case (int) DeadKeys.XK_dead_abovedot :
900                                 return 0xff;
901                         case (int) DeadKeys.XK_dead_abovering :
902                                 return '0';
903                         case (int) DeadKeys.XK_dead_doubleacute :
904                                 return 0xbd;
905                         case (int) DeadKeys.XK_dead_caron :
906                                 return 0xb7;
907                         case (int) DeadKeys.XK_dead_ogonek :
908                                 return 0xb2;
909                         }
910
911                         return 0;
912                 }
913
914                 private XIMProperties [] GetSupportedInputStyles (IntPtr xim)
915                 {
916                         IntPtr stylesPtr;
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));
924                         lock (XlibLock) {
925                                 XplatUIX11.XFree (stylesPtr);
926                         }
927                         return supportedStyles;
928                 }
929
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";
934
935                 private XIMProperties [] GetPreferredStyles ()
936                 {
937                         string env = Environment.GetEnvironmentVariable (ENV_NAME_XIM_STYLE);
938                         if (env == null)
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++) {
943                                 string s = list [i];
944                                 switch (s) {
945                                 case "over-the-spot":
946                                         ret [i] = styleOverTheSpot;
947                                         break;
948                                 case "on-the-spot":
949                                         ret [i] = styleOnTheSpot;
950                                         break;
951                                 case "root":
952                                         ret [i] = styleRoot;
953                                         break;
954                                 }
955                         }
956                         return ret;
957                 }
958
959                 private IEnumerable GetMatchingStylesInPreferredOrder (IntPtr xim)
960                 {
961                         XIMProperties [] supportedStyles = GetSupportedInputStyles (xim);
962                         foreach (XIMProperties p in GetPreferredStyles ())
963                                 if (Array.IndexOf (supportedStyles, p) >= 0)
964                                         yield return p;
965                 }
966
967                 private IntPtr CreateXic (IntPtr window, IntPtr xim)
968                 {
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)
977                                                 break;
978                                         //Console.WriteLine ("failed to create XIC in over-the-spot mode.");
979                                         continue;
980                                 case styleOnTheSpot:
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)
986                                                 break;
987                                         //Console.WriteLine ("failed to create XIC in on-the-spot mode.");
988                                         continue;
989                                 case styleRoot:
990                                         xic = XCreateIC (xim,
991                                                 XNames.XNInputStyle, styleRoot,
992                                                 XNames.XNClientWindow, window,
993                                                 IntPtr.Zero);
994                                         break;
995                                 }
996                         }
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,
1004                                         IntPtr.Zero);
1005                         }
1006                         return xic;
1007                 }
1008
1009                 private IntPtr CreateOverTheSpotXic (IntPtr window, IntPtr xim)
1010                 {
1011                         IntPtr list;
1012                         int count;
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 ();
1017                         spot.X = 0;
1018                         spot.Y = 0;
1019                         IntPtr pSL = IntPtr.Zero, pFS = IntPtr.Zero;
1020                         try {
1021                                 pSL = Marshal.StringToHGlobalAnsi (XNames.XNSpotLocation);
1022                                 pFS = Marshal.StringToHGlobalAnsi (XNames.XNFontSet);
1023                                 IntPtr preedit = XVaCreateNestedList (0,
1024                                         pSL, spot,
1025                                         pFS, fontSet,
1026                                         IntPtr.Zero);
1027                                 return XCreateIC (xim,
1028                                         XNames.XNInputStyle, styleOverTheSpot,
1029                                         XNames.XNClientWindow, window,
1030                                         XNames.XNPreeditAttributes, preedit,
1031                                         IntPtr.Zero);
1032                         } finally {
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);
1040                         }
1041                 }
1042
1043                 private IntPtr CreateOnTheSpotXic (IntPtr window, IntPtr xim)
1044                 {
1045                         callbackContext = new XIMCallbackContext (window);
1046                         return callbackContext.CreateXic (window, xim);
1047                 }
1048
1049                 class XIMCallbackContext
1050                 {
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;
1054
1055                         public XIMCallbackContext (IntPtr clientWindow)
1056                         {
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);
1069                         }
1070
1071                         ~XIMCallbackContext ()
1072                         {
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);
1081
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);
1090                         }
1091
1092                         int DoPreeditStart (IntPtr xic, IntPtr clientData, IntPtr callData)
1093                         {
1094                                 Debug.WriteLine ("DoPreeditStart");
1095                                 XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITSTART, clientData, callData);
1096                                 return 100;
1097                         }
1098
1099                         int DoPreeditDone (IntPtr xic, IntPtr clientData, IntPtr callData)
1100                         {
1101                                 Debug.WriteLine ("DoPreeditDone");
1102                                 XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITDONE, clientData, callData);
1103                                 return 0;
1104                         }
1105
1106                         int DoPreeditDraw (IntPtr xic, IntPtr clientData, IntPtr callData)
1107                         {
1108                                 Debug.WriteLine ("DoPreeditDraw");
1109                                 XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITDRAW, clientData, callData);
1110                                 return 0;
1111                         }
1112
1113                         int DoPreeditCaret (IntPtr xic, IntPtr clientData, IntPtr callData)
1114                         {
1115                                 Debug.WriteLine ("DoPreeditCaret");
1116                                 XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITCARET, clientData, callData);
1117                                 return 0;
1118                         }
1119
1120                         public IntPtr CreateXic (IntPtr window, IntPtr xim)
1121                         {
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,
1128                                         pDoneCBN, pDoneCB,
1129                                         pDrawCBN, pDrawCB,
1130                                         pCaretCBN, pCaretCB,
1131                                         IntPtr.Zero);
1132                                 return XCreateIC (xim,
1133                                         XNames.XNInputStyle, styleOnTheSpot,
1134                                         XNames.XNClientWindow, window,
1135                                         XNames.XNPreeditAttributes, preedit,
1136                                         IntPtr.Zero);
1137                         }
1138                 }
1139
1140                 class XIMPositionContext
1141                 {
1142                         public CaretStruct Caret;
1143                         public int X;
1144                         public int Y;
1145                 }
1146
1147                 internal void SetCaretPos (CaretStruct caret, IntPtr handle, int x, int y)
1148                 {
1149                         if (ximStyle != styleOverTheSpot)
1150                                 return;
1151
1152                         if (positionContext == null)
1153                                 this.positionContext = new XIMPositionContext ();
1154
1155                         positionContext.Caret = caret;
1156                         positionContext.X = x;
1157                         positionContext.Y = y + caret.Height;
1158
1159                         MoveCurrentCaretPos ();
1160                 }
1161
1162                 internal void MoveCurrentCaretPos ()
1163                 {
1164                         if (positionContext == null || ximStyle != styleOverTheSpot || client_window == IntPtr.Zero)
1165                                 return;
1166
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)
1172                                 return;
1173                         Control control = Control.FromHandle (client_window);
1174                         if (control == null || !control.IsHandleCreated)
1175                                 return;
1176                         control = Control.FromHandle (caret.Hwnd);
1177                         if (control == null || !control.IsHandleCreated)
1178                                 return;
1179                         Hwnd hwnd = Hwnd.ObjectFromHandle (client_window);
1180                         if (!hwnd.mapped)
1181                                 return;
1182
1183                         int dx, dy;
1184                         IntPtr child;
1185                         lock (XlibLock) {
1186                                 XplatUIX11.XTranslateCoordinates (display, client_window, client_window, x, y, out dx, out dy, out child);
1187                         }
1188
1189                         XPoint spot = new XPoint ();
1190                         spot.X = (short) dx;
1191                         spot.Y = (short) dy;
1192
1193                         IntPtr pSL = IntPtr.Zero;
1194                         try {
1195                                 pSL = Marshal.StringToHGlobalAnsi (XNames.XNSpotLocation);
1196                                 IntPtr preedit = XVaCreateNestedList (0, pSL, spot, IntPtr.Zero);
1197                                 XSetICValues (xic, XNames.XNPreeditAttributes, preedit, IntPtr.Zero);
1198                         } finally {
1199                                 if (pSL != IntPtr.Zero)
1200                                         Marshal.FreeHGlobal (pSL);
1201                         }
1202                 }
1203
1204                 private bool have_Xutf8LookupString = true;
1205
1206                 private int LookupString (ref XEvent xevent, int len, out XKeySym keysym, out XLookupStatus status)
1207                 {
1208                         IntPtr keysym_res;
1209                         int res;
1210
1211                         status = XLookupStatus.XLookupNone;
1212                         IntPtr xic = GetXic (client_window);
1213                         if (xic != IntPtr.Zero && have_Xutf8LookupString && xevent.type == XEventName.KeyPress) {
1214                                 do {
1215                                         try {
1216                                                 res = Xutf8LookupString (xic, ref xevent, lookup_byte_buffer, 100, out keysym_res,  out status);
1217                                         } catch (EntryPointNotFoundException) {
1218                                                 have_Xutf8LookupString = false;
1219
1220                                                 // call again, this time we'll go through the non-xic clause
1221                                                 return LookupString (ref xevent, len, out keysym, out status);
1222                                         }
1223                                         if (status != XLookupStatus.XBufferOverflow)
1224                                                 break;
1225                                         lookup_byte_buffer = new byte [lookup_byte_buffer.Length << 1];
1226                                 } while (true);
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 ();
1231                                 return s.Length;
1232                         } else {
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 ();
1237                                 return res;
1238                         }
1239                 }
1240
1241                 [DllImport ("libX11")]
1242                 private static extern IntPtr XOpenIM (IntPtr display, IntPtr rdb, IntPtr res_name, IntPtr res_class);
1243
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);
1250
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);
1257
1258                 [DllImport ("libX11")]
1259                 private static extern IntPtr XCreateFontSet (IntPtr display, string name, out IntPtr list, out int count, IntPtr terminator);
1260
1261                 [DllImport ("libX11")]
1262                 internal extern static void XFreeFontSet (IntPtr data);
1263
1264                 [DllImport ("libX11")]
1265                 private static extern void XFreeStringList (IntPtr ptr);
1266
1267                 //[DllImport ("libX11")]
1268                 //private static extern IntPtr XIMOfIC (IntPtr xic);
1269
1270                 [DllImport ("libX11")]
1271                 private static extern void XCloseIM (IntPtr xim);
1272
1273                 [DllImport ("libX11")]
1274                 private static extern void XDestroyIC (IntPtr xic);
1275
1276                 [DllImport ("libX11")]
1277                 private static extern string XGetIMValues (IntPtr xim, string name, out IntPtr value, IntPtr terminator);
1278
1279                 [DllImport ("libX11")]
1280                 private static extern string XGetICValues (IntPtr xic, string name, out EventMask value, IntPtr terminator);
1281
1282                 [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
1283                 private static extern void XSetICValues (IntPtr xic, string name, IntPtr value, IntPtr terminator);
1284
1285                 [DllImport ("libX11")]
1286                 private static extern void XSetICFocus (IntPtr xic);
1287
1288                 [DllImport ("libX11")]
1289                 private static extern void XUnsetICFocus (IntPtr xic);
1290
1291                 [DllImport ("libX11")]
1292                 private static extern string Xutf8ResetIC (IntPtr xic);
1293
1294                 [DllImport ("libX11")]
1295                 private static extern bool XSupportsLocale ();
1296
1297                 [DllImport ("libX11")]
1298                 private static extern bool XSetLocaleModifiers (string mods);
1299
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);
1304
1305                 [DllImport ("libX11")]
1306                 private static extern IntPtr XGetKeyboardMapping (IntPtr display, byte first_keycode, int keycode_count, 
1307                                 out int keysyms_per_keycode_return);
1308
1309                 [DllImport ("libX11")]
1310                 private static extern void XDisplayKeycodes (IntPtr display, out int min, out int max);
1311
1312                 [DllImport ("libX11", EntryPoint="XKeycodeToKeysym")]
1313                 private static extern uint XKeycodeToKeysym (IntPtr display, int keycode, int index);
1314
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);
1319                 }
1320
1321                 [DllImport ("libX11")]
1322                 internal extern static IntPtr XGetModifierMapping (IntPtr display);
1323
1324                 [DllImport ("libX11")]
1325                 internal extern static int XFreeModifiermap (IntPtr modmap);
1326
1327
1328                 private readonly static int [] nonchar_key_vkey = new int []
1329                 {
1330                         /* unused */
1331                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF00 */
1332                         /* special keys */
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 */
1338                         /* unused */
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 */
1343                         /* cursor keys */
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 */
1347                         /* misc keys */
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 */
1351                         /* keypad keys */
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 */
1364                         /* function keys */
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 */
1370                         /* modifier keys */
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 */
1376                 };
1377
1378                 private static readonly int [] nonchar_key_scan = new int []
1379                 {
1380                         /* unused */
1381                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF00 */
1382                         /* special keys */
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 */
1386                         /* unused */
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 */
1393                         /* cursor keys */
1394                         0x147, 0x14B, 0x148, 0x14D, 0x150, 0x149, 0x151, 0x14F,      /* FF50 */
1395                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF58 */
1396                         /* misc keys */
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 */
1400                         /* keypad keys */
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 */
1410                         /* function keys */
1411                         0x3B, 0x3C,
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 */
1416                         /* modifier keys */
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 */
1421                 };
1422
1423                 private readonly static int [] nonchar_vkey_key = new int []
1424                 {
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 */
1473                 };
1474                 
1475         }
1476
1477 }
1478