[PATCH 1/2] Improved fix for mono bug #692206
[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         internal class X11Keyboard : IDisposable {
44                 internal static object XlibLock;
45
46                 private IntPtr display;
47                 private IntPtr client_window;
48                 private IntPtr xim;
49                 private Hashtable xic_table = new Hashtable ();
50                 private XIMPositionContext positionContext;
51                 private XIMCallbackContext callbackContext;
52                 private XIMProperties ximStyle;
53                 private EventMask xic_event_mask = EventMask.NoEventMask;
54                 private StringBuilder lookup_buffer;
55                 private byte [] lookup_byte_buffer = new byte [100];
56                 private int min_keycode, max_keycode, keysyms_per_keycode, syms;
57                 private int [] keyc2vkey = new int [256];
58                 private int [] keyc2scan = new int [256];
59                 private byte [] key_state_table = new byte [256];
60                 private int lcid;
61                 private bool num_state, cap_state;
62                 private bool initialized;
63                 private bool menu_state = false;
64                 private Encoding encoding;
65
66                 private int NumLockMask;
67                 private int AltGrMask;
68
69                 public X11Keyboard (IntPtr display, IntPtr clientWindow)
70                 {
71                         this.display = display;
72                         lookup_buffer = new StringBuilder (24);
73                         EnsureLayoutInitialized ();
74                 }
75
76                 private Encoding AnsiEncoding
77                 {
78                         get
79                         {
80                                 if (encoding == null)
81                                         encoding = Encoding.GetEncoding(new CultureInfo(lcid).TextInfo.ANSICodePage);
82                                 return encoding;
83                         }
84                 }
85
86                 public IntPtr ClientWindow {
87                         get { return client_window; }
88                 }
89
90                 void IDisposable.Dispose ()
91                 {
92                         if (xim != IntPtr.Zero) {
93                                 foreach (IntPtr xic in xic_table.Values)
94                                         XDestroyIC (xic);
95                                 xic_table.Clear ();
96
97                                 XCloseIM (xim);
98                                 xim = IntPtr.Zero;
99                         }
100                 }
101
102                 public void DestroyICForWindow (IntPtr window)
103                 {
104                         IntPtr xic = GetXic (window);
105                         if (xic != IntPtr.Zero) {
106                                 xic_table.Remove ((long) window);
107                                 XDestroyIC (xic);
108                         }
109                 }
110
111                 public void EnsureLayoutInitialized ()
112                 {
113                         if (initialized)
114                                 return;
115
116                         KeyboardLayouts layouts = new KeyboardLayouts ();
117                         KeyboardLayout layout = DetectLayout (layouts);
118                         lcid = layout.Lcid;
119                         CreateConversionArray (layouts, layout);
120                         SetupXIM ();
121                         initialized = true;
122                 }
123
124                 private void SetupXIM ()
125                 {
126                         xim = IntPtr.Zero;
127
128                         if (!XSupportsLocale ()) {
129                                 Console.Error.WriteLine ("X does not support your locale");
130                                 return;
131                         }
132
133                         if (!XSetLocaleModifiers (String.Empty)) {
134                                 Console.Error.WriteLine ("Could not set X locale modifiers");
135                                 return;
136                         }
137
138                         if (Environment.GetEnvironmentVariable (ENV_NAME_XIM_STYLE) == "disabled") {
139                                 return;
140                         }
141
142                         xim = XOpenIM (display, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
143                         if (xim == IntPtr.Zero) 
144                                 Console.Error.WriteLine ("Could not get XIM");
145
146                         initialized = true;
147                 }
148
149                 void CreateXicForWindow (IntPtr window)
150                 {
151                         IntPtr xic = CreateXic (window, xim);
152                         xic_table [(long) window] = xic;
153                         if (xic == IntPtr.Zero)
154                                 Console.Error.WriteLine ("Could not get XIC");
155                         else {
156                                 if (XGetICValues (xic, "filterEvents", out xic_event_mask, IntPtr.Zero) != null)
157                                         Console.Error.WriteLine ("Could not get XIC values");
158                                 EventMask mask = EventMask.ExposureMask | EventMask.KeyPressMask | EventMask.FocusChangeMask;
159                                 if ((xic_event_mask | mask) == xic_event_mask) {
160                                         xic_event_mask |= mask;
161                                         lock (XlibLock) {
162                                                 XplatUIX11.XSelectInput(display, window, new IntPtr ((int) xic_event_mask));
163                                         }
164                                 }
165                         }
166                 }
167
168                 public EventMask KeyEventMask {
169                         get { return xic_event_mask; }
170                 }
171                 
172                 public Keys ModifierKeys {
173                         get {
174                                 Keys keys = Keys.None;
175                                 if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0)
176                                         keys |= Keys.Shift;
177                                 if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0)
178                                         keys |= Keys.Control;
179                                 if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0)
180                                         keys |= Keys.Alt;
181                                 return keys;
182                         }
183                 }
184
185                 private IntPtr GetXic (IntPtr window)
186                 {
187                         if (xim != IntPtr.Zero && xic_table.ContainsKey ((long) window))
188                                 return (IntPtr) xic_table [(long) window];
189                         else
190                                 return IntPtr.Zero;
191                 }
192
193                 private bool FilterKey (XEvent e, int vkey)
194                 {
195                         if (XplatUI.key_filters.Count == 0)
196                                 return false;
197                         IntPtr status;
198                         XKeySym ks;
199                         KeyFilterData data;
200                         data.Down = (e.type == XEventName.KeyPress);
201                         data.ModifierKeys = ModifierKeys;
202                         LookupString (ref e, 0, out ks, out status);
203                         data.keysym = (int)ks;
204                         data.keycode = e.KeyEvent.keycode;
205                         data.str = lookup_buffer.ToString (0, lookup_buffer.Length);
206                         return XplatUI.FilterKey (data);
207                 }
208
209                 public void FocusIn (IntPtr window)
210                 {
211                         this.client_window = window;
212                         if (xim == IntPtr.Zero)
213                                 return;
214
215                         if (!xic_table.ContainsKey ((long) window))
216                                 CreateXicForWindow (window);
217                         IntPtr xic = GetXic (window);
218                         if (xic != IntPtr.Zero)
219                                 XSetICFocus (xic);
220                 }
221
222                 private bool have_Xutf8ResetIC = true;
223
224                 public void FocusOut (IntPtr window)
225                 {
226                         this.client_window = IntPtr.Zero;
227                         if (xim == IntPtr.Zero)
228                                 return;
229
230                         IntPtr xic = GetXic (window);
231                         if (xic != IntPtr.Zero) {
232                                 if (have_Xutf8ResetIC) {
233                                         try {
234                                                 Xutf8ResetIC (xic);
235                                         } catch (EntryPointNotFoundException) {
236                                                 have_Xutf8ResetIC = false;
237                                         }
238                                 }
239                                 XUnsetICFocus (xic);
240                         }
241                 }
242
243                 public bool ResetKeyState(IntPtr hwnd, ref MSG msg) {
244                         // FIXME - keep defining events/msg and return true until we've 'restored' all
245                         // pending keypresses
246                         if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0) {
247                                 key_state_table [(int) VirtualKeys.VK_SHIFT] &=  unchecked((byte)~0x80);
248                         }
249
250                         if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) {
251                                 key_state_table [(int) VirtualKeys.VK_CONTROL] &=  unchecked((byte)~0x80);
252                         }
253
254                         if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0) {
255                                 key_state_table [(int) VirtualKeys.VK_MENU] &=  unchecked((byte)~0x80);
256                         }
257                         return false;
258                 }
259
260                 // Almost identical to UpdateKeyState() but does not call LookupString().
261                 // The actual purpose is to handle shift state correctly.
262                 public void PreFilter (XEvent xevent)
263                 {
264                         // It is still possible that some keyboards could have some shift
265                         // keys outside this range, but let's think about such cases only
266                         // if it actually happened.
267                         if (xevent.KeyEvent.keycode >= keyc2vkey.Length)
268                                 return;
269
270                         int vkey = keyc2vkey [xevent.KeyEvent.keycode];
271
272                         switch (xevent.type) {
273                         case XEventName.KeyRelease:
274                                 key_state_table [vkey & 0xff] &= unchecked ((byte) ~0x80);
275                                 break;
276                         case XEventName.KeyPress:
277                                 if ((key_state_table [vkey & 0xff] & 0x80) == 0) {
278                                         key_state_table [vkey & 0xff] ^= 0x01;
279                                 }
280                                 key_state_table [vkey & 0xff] |= 0x80;
281                                 break;
282                         }
283                 }
284
285                 public void KeyEvent (IntPtr hwnd, XEvent xevent, ref MSG msg)
286                 {
287                         XKeySym keysym;
288                         int ascii_chars;
289
290                         IntPtr status = IntPtr.Zero;
291                         ascii_chars = LookupString (ref xevent, 24, out keysym, out status);
292
293                         if (((int) keysym >= (int) MiscKeys.XK_ISO_Lock && 
294                                 (int) keysym <= (int) MiscKeys.XK_ISO_Last_Group_Lock) ||
295                                 (int) keysym == (int) MiscKeys.XK_Mode_switch) {
296                                 UpdateKeyState (xevent);
297                                 return;
298                         }
299
300                         if ((xevent.KeyEvent.keycode >> 8) == 0x10)
301                                 xevent.KeyEvent.keycode = xevent.KeyEvent.keycode & 0xFF;
302
303                         int event_time = (int)xevent.KeyEvent.time;
304
305                         if (status == (IntPtr) 2) {
306                                 // do not ignore those inputs. They are mostly from XIM.
307                                 msg = SendImeComposition (lookup_buffer.ToString (0, lookup_buffer.Length));
308                                 msg.hwnd = hwnd;
309                                 return;
310                         }
311
312                         AltGrMask = xevent.KeyEvent.state & (0x6000 | (int) KeyMasks.ModMasks);
313                         int vkey = EventToVkey (xevent);
314                         if (vkey == 0 && ascii_chars != 0) {
315                                 vkey = (int) VirtualKeys.VK_NONAME;
316                         }
317
318                         if (FilterKey (xevent, vkey))
319                                 return;
320                         switch ((VirtualKeys) (vkey & 0xFF)) {
321                         case VirtualKeys.VK_NUMLOCK:
322                                 GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, xevent.KeyEvent.keycode, xevent.type, event_time);
323                                 break;
324                         case VirtualKeys.VK_CAPITAL:
325                                 GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, xevent.KeyEvent.keycode, xevent.type, event_time);
326                                 break;
327                         default:
328                                 if (((key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x01) == 0) != ((xevent.KeyEvent.state & NumLockMask) == 0)) {
329                                         GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, xevent.KeyEvent.keycode, XEventName.KeyPress, event_time);
330                                         GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, xevent.KeyEvent.keycode, XEventName.KeyRelease, event_time);
331                                 }
332
333                                 if (((key_state_table [(int) VirtualKeys.VK_CAPITAL] & 0x01) == 0) != ((xevent.KeyEvent.state & (int) KeyMasks.LockMask) == 0)) {
334                                         GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, xevent.KeyEvent.keycode, XEventName.KeyPress, event_time);
335                                         GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, xevent.KeyEvent.keycode, XEventName.KeyRelease, event_time);
336                                 }
337
338                                 num_state = false;
339                                 cap_state = false;
340
341                                 int bscan = (keyc2scan [xevent.KeyEvent.keycode] & 0xFF);
342                                 KeybdEventFlags dw_flags = KeybdEventFlags.None;
343                                 if (xevent.type == XEventName.KeyRelease)
344                                         dw_flags |= KeybdEventFlags.KeyUp;
345                                 if ((vkey & 0x100) != 0)
346                                         dw_flags |= KeybdEventFlags.ExtendedKey;
347                                 msg = SendKeyboardInput ((VirtualKeys) (vkey & 0xFF), bscan, xevent.KeyEvent.keycode, dw_flags, event_time);
348                                 msg.hwnd = hwnd;
349                                 break;
350                         }
351                 }
352
353                 public bool TranslateMessage (ref MSG msg)
354                 {
355                         bool res = false;
356
357                         if (msg.message >= Msg.WM_KEYFIRST && msg.message <= Msg.WM_KEYLAST)
358                                 res = true;
359
360                         if (msg.message == Msg.WM_SYSKEYUP && msg.wParam == (IntPtr) 0x12 && menu_state) {
361                                 msg.message = Msg.WM_KEYUP;
362                                 menu_state = false;
363                         }
364
365                         if (msg.message != Msg.WM_KEYDOWN && msg.message != Msg.WM_SYSKEYDOWN)
366                                 return res;
367
368                         if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 && msg.wParam != (IntPtr) 0x12)
369                                 menu_state = true;
370
371                         EnsureLayoutInitialized ();
372
373                         string buffer;
374                         Msg message;
375                         int tu = ToUnicode ((int) msg.wParam, Control.HighOrder ((int) msg.lParam), out buffer);
376                         switch (tu) {
377                         case 1:
378                                 message = (msg.message == Msg.WM_KEYDOWN ? Msg.WM_CHAR : Msg.WM_SYSCHAR);
379                                 XplatUI.PostMessage (msg.hwnd, message, (IntPtr) buffer [0], msg.lParam);
380                                 break;
381                         case -1:
382                                 message = (msg.message == Msg.WM_KEYDOWN ? Msg.WM_DEADCHAR : Msg.WM_SYSDEADCHAR);
383                                 XplatUI.PostMessage (msg.hwnd, message, (IntPtr) buffer [0], msg.lParam);
384                                 return true;
385                         }
386                         
387                         return res;
388                 }
389
390                 public int ToKeycode(int key) 
391                 {
392                         int keycode = 0;
393                         
394                         if (nonchar_vkey_key[key] > 0)
395                                 keycode = XKeysymToKeycode (display, nonchar_vkey_key[key]);
396                         
397                         if (keycode == 0)
398                                 keycode = XKeysymToKeycode (display, key);
399
400                         return keycode;         
401                 }
402
403                 public int ToUnicode (int vkey, int scan, out string buffer)
404                 {
405                         if ((scan & 0x8000) != 0) {
406                                 buffer = String.Empty;
407                                 return 0;
408                         }
409
410                         XEvent e = new XEvent ();
411                         e.AnyEvent.type = XEventName.KeyPress;
412                         e.KeyEvent.display = display;
413                         e.KeyEvent.keycode = 0;
414                         e.KeyEvent.state = 0;
415
416                         if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0) {
417                                 e.KeyEvent.state |= (int) KeyMasks.ShiftMask;
418                         }
419
420                         if ((key_state_table [(int) VirtualKeys.VK_CAPITAL] & 0x01) != 0) {
421                                 e.KeyEvent.state |= (int) KeyMasks.LockMask;
422                         }
423
424                         if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) {
425                                 e.KeyEvent.state |= (int) KeyMasks.ControlMask;
426                         }
427
428                         if ((key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x01) != 0) {
429                                 e.KeyEvent.state |= NumLockMask;
430                         }
431
432                         e.KeyEvent.state |= AltGrMask;
433
434                         for (int keyc = min_keycode; (keyc <= max_keycode) && (e.KeyEvent.keycode == 0); keyc++) {
435                                 // find keycode that could have generated this vkey
436                                 if ((keyc2vkey [keyc] & 0xFF) == vkey) {
437                                         // filter extended bit because it is not known
438                                         e.KeyEvent.keycode = keyc;
439                                         if ((EventToVkey (e) & 0xFF) != vkey) {
440                                                 // Wrong one (ex: because of num,lock state)
441                                                 e.KeyEvent.keycode = 0;
442                                         }
443                                 }
444                         }
445
446                         if ((vkey >= (int) VirtualKeys.VK_NUMPAD0) && (vkey <= (int) VirtualKeys.VK_NUMPAD9))
447                                 e.KeyEvent.keycode = XKeysymToKeycode (display, vkey - (int) VirtualKeys.VK_NUMPAD0 + (int) KeypadKeys.XK_KP_0);
448
449                         if (vkey == (int) VirtualKeys.VK_DECIMAL)
450                                 e.KeyEvent.keycode = XKeysymToKeycode (display, (int) KeypadKeys.XK_KP_Decimal);
451                         
452                         if (vkey == (int) VirtualKeys.VK_SEPARATOR)
453                                 e.KeyEvent.keycode = XKeysymToKeycode(display, (int) KeypadKeys.XK_KP_Separator);
454
455                         if (e.KeyEvent.keycode == 0 && vkey != (int) VirtualKeys.VK_NONAME) {
456                                 // And I couldn't find the keycode so i returned the vkey and was like whatever
457                                 Console.Error.WriteLine ("unknown virtual key {0:X}", vkey);
458                                 buffer = String.Empty;
459                                 return vkey; 
460                         }
461
462                         XKeySym t;
463                         IntPtr status;
464                         int res = LookupString (ref e, 24, out t, out status);
465                         int keysym = (int) t;
466
467                         buffer = String.Empty;
468                         if (res == 0) {
469                                 int dead_char = MapDeadKeySym (keysym);
470                                 if (dead_char != 0) {
471                                         byte [] bytes = new byte [1];
472                                         bytes [0] = (byte) dead_char;
473                                         buffer = new string (AnsiEncoding.GetChars (bytes));
474                                         res = -1;
475                                 }
476                         } else {
477                                 // Shift + arrow, shift + home, ....
478                                 // X returns a char for it, but windows doesn't
479                                 if (((e.KeyEvent.state & NumLockMask) == 0) && ((e.KeyEvent.state & (int) KeyMasks.ShiftMask) != 0) &&
480                                                 (keysym >= (int) KeypadKeys.XK_KP_0) && (keysym <= (int) KeypadKeys.XK_KP_9)) {
481                                         buffer = String.Empty;
482                                         res = 0;
483                                 }
484
485                                 // CTRL + number, X returns chars, windows does not
486                                 if ((e.KeyEvent.state & (int) KeyMasks.ControlMask) != 0) {
487                                         if (((keysym >= 33) && (keysym < 'A')) || ((keysym > 'Z') && (keysym < 'a'))) {
488                                                 buffer = String.Empty;
489                                                 res = 0;
490                                         }
491                                 }
492
493                                 // X returns a char for delete key on extended keyboards, windows does not
494                                 if (keysym == (int) TtyKeys.XK_Delete) {
495                                         buffer = String.Empty;
496                                         res = 0;
497                                 }
498
499                                 if (keysym == (int) TtyKeys.XK_BackSpace && (key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) {
500                                         buffer = new string (new char [] { (char) 127 });
501
502                                         return 1;
503                                 }
504
505                                 // For some special chars, such backspace and enter, looking up for a string
506                                 // can randomly fail to properly fill the buffer (either marshaling or X11), so we use
507                                 // the keysysm value to fill the gap
508                                 if (keysym == (int) XKeySym.XK_BackSpace) {
509                                         buffer = new string (new char [] { (char) 8 });
510                                         return 1;
511                                 }
512                                 if (keysym == (int) XKeySym.XK_Return) {
513                                         buffer = new string (new char [] { (char)13 });
514                                         return 1;
515                                 }
516
517                                 if (res != 0) {
518                                         buffer = lookup_buffer.ToString ();
519                                         res = buffer.Length;
520                                 }
521                         }
522
523                         return res;
524                 }
525
526                 string stored_keyevent_string;
527
528                 internal string GetCompositionString ()
529                 {
530                         return stored_keyevent_string;
531                 }
532
533                 private MSG SendImeComposition (string s)
534                 {
535                         MSG msg = new MSG ();
536                         msg.message = Msg.WM_IME_COMPOSITION;
537                         msg.refobject = s;
538                         stored_keyevent_string = s;
539                         return msg;
540                 }
541
542                 private MSG SendKeyboardInput (VirtualKeys vkey, int scan, int keycode, KeybdEventFlags dw_flags, int time)
543                 {
544                         Msg message;
545
546                         if ((dw_flags & KeybdEventFlags.KeyUp) != 0) {
547                                 bool sys_key = (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 &&
548                                               ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) == 0);
549                                 key_state_table [(int) vkey] &= unchecked ((byte) ~0x80);
550                                 message = (sys_key ? Msg.WM_SYSKEYUP : Msg.WM_KEYUP);
551                         } else {
552                                 if ((key_state_table [(int) vkey] & 0x80) == 0) {
553                                         key_state_table [(int) vkey] ^= 0x01;
554                                 }
555                                 key_state_table [(int) vkey] |= 0x80;
556                                 bool sys_key = (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 &&
557                                               ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) == 0);
558                                 message = (sys_key ? Msg.WM_SYSKEYDOWN : Msg.WM_KEYDOWN);
559                         }
560
561                         MSG msg = new MSG ();
562                         msg.message = message;
563                         msg.wParam = (IntPtr) vkey;
564                         msg.lParam = GenerateLParam (msg, keycode);
565                         return msg;
566                 }
567
568                 private IntPtr GenerateLParam (MSG m, int keyCode)
569                 {
570                         // http://msdn.microsoft.com/en-us/library/ms646267(VS.85).aspx
571                         //
572                         byte flags = 0;
573
574                         if (m.message == Msg.WM_SYSKEYUP || m.message == Msg.WM_KEYUP)
575                                 flags |= 0x80; // transition state flag = 1
576
577                         flags |= 0x40; // previous key state flag = 1
578
579                         if ((key_state_table [(int) VirtualKeys.VK_RMENU] & 0x80) != 0 ||
580                             (key_state_table [(int) VirtualKeys.VK_LMENU] & 0x80) != 0 ||
581                             (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0)
582                                 flags |= 0x20; // context code flag = 1
583
584                         if ((key_state_table [(int) VirtualKeys.VK_INSERT] & 0x80) != 0 ||
585                             (key_state_table [(int) VirtualKeys.VK_DELETE] & 0x80) != 0 ||
586                             (key_state_table [(int) VirtualKeys.VK_HOME] & 0x80) != 0 ||
587                             (key_state_table [(int) VirtualKeys.VK_END] & 0x80) != 0 ||
588                             (key_state_table [(int) VirtualKeys.VK_UP] & 0x80) != 0 ||
589                             (key_state_table [(int) VirtualKeys.VK_DOWN] & 0x80) != 0 ||
590                             (key_state_table [(int) VirtualKeys.VK_LEFT] & 0x80) != 0 ||
591                             (key_state_table [(int) VirtualKeys.VK_RIGHT] & 0x80) != 0 ||
592                             (key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0 ||
593                             (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 ||
594                             (key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x80) != 0 ||
595                             (key_state_table [(int) VirtualKeys.VK_PRINT] & 0x80) != 0 ||
596                             (key_state_table [(int) VirtualKeys.VK_RETURN] & 0x80) != 0 ||
597                             (key_state_table [(int) VirtualKeys.VK_DIVIDE] & 0x80) != 0 ||
598                             (key_state_table [(int) VirtualKeys.VK_PRIOR] & 0x80) != 0 ||
599                             (key_state_table [(int) VirtualKeys.VK_NEXT] & 0x80) != 0)
600                                 flags |= 0x01; // extended key flag = 1
601
602                         int lparam = ((((int)flags) & 0x000000FF) << 3*8); // message flags
603                         lparam |= ((keyCode & 0x000000FF) << 2*8); // scan code
604                         lparam |= 0x00000001; // repeat count = 1
605
606                         return (IntPtr)lparam;
607                 }
608
609                 private void GenerateMessage (VirtualKeys vkey, int scan, int key_code, XEventName type, int event_time)
610                 {
611                         bool state = (vkey == VirtualKeys.VK_NUMLOCK ? num_state : cap_state);
612                         KeybdEventFlags up, down;
613
614                         if (state) {
615                                 // The INTERMEDIARY state means : just after a 'press' event, if a 'release' event comes,
616                                 // don't treat it. It's from the same key press. Then the state goes to ON.
617                                 // And from there, a 'release' event will switch off the toggle key.
618                                 SetState (vkey, false);
619                         } else {
620                                 down = (vkey == VirtualKeys.VK_NUMLOCK ? KeybdEventFlags.ExtendedKey : KeybdEventFlags.None);
621                                 up = (vkey == VirtualKeys.VK_NUMLOCK ? KeybdEventFlags.ExtendedKey :
622                                                 KeybdEventFlags.None) | KeybdEventFlags.KeyUp;
623                                 if ((key_state_table [(int) vkey] & 0x1) != 0) { // it was on
624                                         if (type != XEventName.KeyPress) {
625                                                 SendKeyboardInput (vkey, scan, key_code, down, event_time);
626                                                 SendKeyboardInput (vkey, scan, key_code, up, event_time);
627                                                 SetState (vkey, false);
628                                                 key_state_table [(int) vkey] &= unchecked ((byte) ~0x01);
629                                         }
630                                 } else {
631                                         if (type == XEventName.KeyPress) {
632                                                 SendKeyboardInput (vkey, scan, key_code, down, event_time);
633                                                 SendKeyboardInput (vkey, scan, key_code, up, event_time);
634                                                 SetState (vkey, true);
635                                                 key_state_table [(int) vkey] |= 0x01;
636                                         }
637                                 }
638                         }
639                 }
640
641                 private void UpdateKeyState (XEvent xevent)
642                 {
643                         int vkey = EventToVkey (xevent);
644
645                         switch (xevent.type) {
646                         case XEventName.KeyRelease:
647                                 key_state_table [vkey & 0xff] &= unchecked ((byte) ~0x80);
648                                 break;
649                         case XEventName.KeyPress:
650                                 if ((key_state_table [vkey & 0xff] & 0x80) == 0) {
651                                         key_state_table [vkey & 0xff] ^= 0x01;
652                                 }
653                                 key_state_table [vkey & 0xff] |= 0x80;
654                                 break;
655                         }
656                 }
657
658                 private void SetState (VirtualKeys key, bool state)
659                 {
660                         if (VirtualKeys.VK_NUMLOCK == key)
661                                 num_state = state;
662                         else
663                                 cap_state = state;
664                 }
665
666                 public int EventToVkey (XEvent e)
667                 {
668                         IntPtr status;
669                         XKeySym ks;
670
671                         LookupString (ref e, 0, out ks, out status);
672                         int keysym = (int) ks;
673
674                         if (((e.KeyEvent.state & NumLockMask) != 0) &&
675                             (keysym == (int)KeypadKeys.XK_KP_Separator || keysym == (int)KeypadKeys.XK_KP_Decimal ||
676                             (keysym >= (int)KeypadKeys.XK_KP_0 && keysym <= (int)KeypadKeys.XK_KP_9))) {
677                                 // Only the Keypad keys 0-9 and . send different keysyms
678                                 // depending on the NumLock state
679                                 return nonchar_key_vkey [keysym & 0xFF];
680                         }
681
682                         return keyc2vkey [e.KeyEvent.keycode];
683                 }
684
685                 private void CreateConversionArray (KeyboardLayouts layouts, KeyboardLayout layout)
686                 {
687                         XEvent e2 = new XEvent ();
688                         uint keysym = 0;
689                         int [] ckey = new int [] { 0, 0, 0, 0 };
690
691                         e2.KeyEvent.display = display;
692                         e2.KeyEvent.state = 0;
693
694                         for (int keyc = min_keycode; keyc <= max_keycode; keyc++) {
695                                 int vkey = 0;
696                                 int scan = 0;
697
698                                 e2.KeyEvent.keycode = keyc;
699                                 XKeySym t;
700
701                                 IntPtr status;
702                                 LookupString (ref e2, 0, out t, out status);
703
704                                 keysym = (uint) t;
705                                 if (keysym != 0) {
706                                         if ((keysym >> 8) == 0xFF) {
707                                                 vkey = nonchar_key_vkey [keysym & 0xFF];
708                                                 scan = nonchar_key_scan [keysym & 0xFF];
709                                                 // Set extended bit
710                                                 if ((scan & 0x100) != 0)
711                                                         vkey |= 0x100;
712                                         } else if (keysym == 0x20) { // spacebar
713                                                 vkey = (int) VirtualKeys.VK_SPACE;
714                                                 scan = 0x39;
715                                         } else {
716                                                 // Search layout dependent scancodes
717                                                 int maxlen = 0;
718                                                 int maxval = -1;;
719                                                 
720                                                 for (int i = 0; i < syms; i++) {
721                                                         keysym = XKeycodeToKeysym (display, keyc, i);
722                                                         if ((keysym < 0x800) && (keysym != ' '))
723                                                                 ckey [i] = (sbyte) (keysym & 0xFF);
724                                                         else
725                                                                 ckey [i] = (sbyte) MapDeadKeySym ((int) keysym);
726                                                 }
727                                                 
728                                                 for (int keyn = 0; keyn < layout.Keys.Length; keyn++) {
729                                                         int ml = Math.Min (layout.Keys [keyn].Length, 4);
730                                                         int ok = -1;
731                                                         for (int i = 0; (ok != 0) && (i < ml); i++) {
732                                                                 sbyte ck = (sbyte) layout.Keys [keyn][i];
733                                                                 if (ck != ckey [i])
734                                                                         ok = 0;
735                                                                 if ((ok != 0) || (i > maxlen)) {
736                                                                         maxlen = i;
737                                                                         maxval = keyn;
738                                                                 }
739                                                                 if (ok != 0)
740                                                                         break;
741                                                         }
742                                                 }
743                                                 if (maxval >= 0) {
744                                                         if (maxval < layouts.scan_table [(int) layout.ScanIndex].Length)
745                                                                 scan = layouts.scan_table [(int) layout.ScanIndex][maxval];
746                                                         if (maxval < layouts.vkey_table [(int) layout.VKeyIndex].Length)
747                                                                 vkey = layouts.vkey_table [(int) layout.VKeyIndex][maxval];
748                                                 }
749                                         }
750                                 }
751                                 keyc2vkey [e2.KeyEvent.keycode] = vkey;
752                                 keyc2scan [e2.KeyEvent.keycode] = scan;
753                         }
754                         
755                         
756                 }
757
758                 private KeyboardLayout DetectLayout (KeyboardLayouts layouts)
759                 {
760                         XDisplayKeycodes (display, out min_keycode, out max_keycode);
761                         IntPtr ksp = XGetKeyboardMapping (display, (byte) min_keycode,
762                                         max_keycode + 1 - min_keycode, out keysyms_per_keycode);
763                         lock (XlibLock) {
764                                 XplatUIX11.XFree (ksp);
765                         }
766
767                         syms = keysyms_per_keycode;
768                         if (syms > 4) {
769                                 //Console.Error.WriteLine ("{0} keysymbols per a keycode is not supported, setting to 4", syms);
770                                 syms = 2;
771                         }
772
773                         IntPtr  modmap_unmanaged;
774                         XModifierKeymap xmk = new XModifierKeymap ();
775
776                         modmap_unmanaged = XGetModifierMapping (display);
777                         xmk = (XModifierKeymap) Marshal.PtrToStructure (modmap_unmanaged, typeof (XModifierKeymap));
778
779                         int mmp = 0;
780                         for (int i = 0; i < 8; i++) {
781                                 for (int j = 0; j < xmk.max_keypermod; j++, mmp++) {
782                                         byte b = Marshal.ReadByte (xmk.modifiermap, mmp);
783                                         if (b != 0) {
784                                                 for (int k = 0; k < keysyms_per_keycode; k++) {
785                                                         if ((int) XKeycodeToKeysym (display, b, k) == (int) MiscKeys.XK_Num_Lock)
786                                                                 NumLockMask = 1 << i;
787                                                 }
788                                         }
789                                 }
790                         }
791                         XFreeModifiermap (modmap_unmanaged);
792
793                         int [] ckey = new int [4];
794                         KeyboardLayout layout = null;
795                         int max_score = 0;
796                         int max_seq = 0;
797                         
798                         foreach (KeyboardLayout current in layouts.Layouts) {
799                                 int ok = 0;
800                                 int score = 0;
801                                 int match = 0;
802                                 int mismatch = 0;
803                                 int seq = 0;
804                                 int pkey = -1;
805                                 int key = min_keycode;
806                                 int i;
807
808                                 for (int keyc = min_keycode; keyc <= max_keycode; keyc++) {
809                                         for (i = 0; i < syms; i++) {
810                                                 uint keysym = XKeycodeToKeysym (display, keyc, i);
811                                                 
812                                                 if ((keysym < 0x800) && (keysym != ' ')) {
813                                                         ckey [i] = (sbyte) (keysym & 0xFF);
814                                                 } else {
815                                                         ckey [i] = (sbyte) MapDeadKeySym ((int) keysym);
816                                                 }
817                                         }
818                                         if (ckey [0] != 0) {
819                                                 for (key = 0; key < current.Keys.Length; key++) {
820                                                         int ml = Math.Min (syms, current.Keys [key].Length);
821                                                         for (ok = 0, i = 0; (ok >= 0) && (i < ml); i++) {
822                                                                 sbyte ck = (sbyte) current.Keys [key][i];
823                                                                 if (ck != 0 && ck == ckey[i])
824                                                                         ok++;
825                                                                 if (ck != 0 && ck != ckey[i])
826                                                                         ok = -1;
827                                                         }
828                                                         if (ok > 0) {
829                                                                 score += ok;
830                                                                 break;
831                                                         }
832                                                 }
833                                                 if (ok > 0) {
834                                                         match++;
835                                                         /* and how much the keycode order matches */
836                                                         if (key > pkey)
837                                                                 seq++;
838                                                         pkey = key;
839                                                 } else {
840                                                         /* print spaces instead of \0's */
841                                                         mismatch++;
842                                                         score -= syms;
843                                                 }
844                                         }
845                                 }
846
847                                 if ((score > max_score) || ((score == max_score) && (seq > max_seq))) {
848                                         // best match so far
849                                         layout = current;
850                                         max_score = score;
851                                         max_seq = seq;
852                                 }
853                         }
854
855                         if (layout != null)  {
856                                 return layout;
857                         } else {
858                                 Console.WriteLine (Locale.GetText("Keyboard layout not recognized, using default layout: " +
859                                                                    layouts.Layouts [0].Name));
860                         }
861
862                         return layouts.Layouts [0];
863                 }
864
865                 // TODO
866                 private int MapDeadKeySym (int val)
867                 {
868                         switch (val) {
869                         case (int) DeadKeys.XK_dead_tilde :
870                         case 0x1000FE7E : // Xfree's Dtilde
871                                 return '~';
872                         case (int) DeadKeys.XK_dead_acute :
873                         case 0x1000FE27 : // Xfree's XK_Dacute_accent
874                                 return 0xb4;
875                         case (int) DeadKeys.XK_dead_circumflex:
876                         case 0x1000FE5E : // Xfree's XK_.Dcircumflex_accent
877                                 return '^';
878                         case (int) DeadKeys.XK_dead_grave :
879                         case 0x1000FE60 : // Xfree's XK_.Dgrave_accent
880                                 return '`';
881                         case (int) DeadKeys.XK_dead_diaeresis :
882                         case 0x1000FE22 : // Xfree's XK_.Ddiaeresis
883                                 return 0xa8;
884                         case (int) DeadKeys.XK_dead_cedilla :
885                                 return 0xb8;
886                         case (int) DeadKeys.XK_dead_macron :
887                                 return '-';
888                         case (int) DeadKeys.XK_dead_breve :
889                                 return 0xa2;
890                         case (int) DeadKeys.XK_dead_abovedot :
891                                 return 0xff;
892                         case (int) DeadKeys.XK_dead_abovering :
893                                 return '0';
894                         case (int) DeadKeys.XK_dead_doubleacute :
895                                 return 0xbd;
896                         case (int) DeadKeys.XK_dead_caron :
897                                 return 0xb7;
898                         case (int) DeadKeys.XK_dead_ogonek :
899                                 return 0xb2;
900                         }
901
902                         return 0;
903                 }
904
905                 private XIMProperties [] GetSupportedInputStyles (IntPtr xim)
906                 {
907                         IntPtr stylesPtr;
908                         string ret = XGetIMValues (xim, XNames.XNQueryInputStyle, out stylesPtr, IntPtr.Zero);
909                         if (ret != null || stylesPtr == IntPtr.Zero)
910                                 return new XIMProperties [0];
911                         XIMStyles styles = (XIMStyles) Marshal.PtrToStructure (stylesPtr, typeof (XIMStyles));
912                         XIMProperties [] supportedStyles = new XIMProperties [styles.count_styles];
913                         for (int i = 0; i < styles.count_styles; i++)
914                                 supportedStyles [i] = (XIMProperties) Marshal.PtrToStructure (new IntPtr ((long) styles.supported_styles + i * Marshal.SizeOf (typeof (IntPtr))), typeof (XIMProperties));
915                         lock (XlibLock) {
916                                 XplatUIX11.XFree (stylesPtr);
917                         }
918                         return supportedStyles;
919                 }
920
921                 const XIMProperties styleRoot = XIMProperties.XIMPreeditNothing | XIMProperties.XIMStatusNothing;
922                 const XIMProperties styleOverTheSpot = XIMProperties.XIMPreeditPosition | XIMProperties.XIMStatusNothing;
923                 const XIMProperties styleOnTheSpot = XIMProperties.XIMPreeditCallbacks | XIMProperties.XIMStatusNothing;
924                 const string ENV_NAME_XIM_STYLE = "MONO_WINFORMS_XIM_STYLE";
925
926                 private XIMProperties [] GetPreferredStyles ()
927                 {
928                         string env = Environment.GetEnvironmentVariable (ENV_NAME_XIM_STYLE);
929                         if (env == null)
930                                 env = "over-the-spot";
931                         string [] list = env.Split (' ');
932                         XIMProperties [] ret = new XIMProperties [list.Length];
933                         for (int i = 0; i < list.Length; i++) {
934                                 string s = list [i];
935                                 switch (s) {
936                                 case "over-the-spot":
937                                         ret [i] = styleOverTheSpot;
938                                         break;
939                                 case "on-the-spot":
940                                         ret [i] = styleOnTheSpot;
941                                         break;
942                                 case "root":
943                                         ret [i] = styleRoot;
944                                         break;
945                                 }
946                         }
947                         return ret;
948                 }
949
950                 private IEnumerable GetMatchingStylesInPreferredOrder (IntPtr xim)
951                 {
952                         XIMProperties [] supportedStyles = GetSupportedInputStyles (xim);
953                         foreach (XIMProperties p in GetPreferredStyles ())
954                                 if (Array.IndexOf (supportedStyles, p) >= 0)
955                                         yield return p;
956                 }
957
958                 private IntPtr CreateXic (IntPtr window, IntPtr xim)
959                 {
960                         IntPtr xic = IntPtr.Zero;
961                         foreach (XIMProperties targetStyle in GetMatchingStylesInPreferredOrder (xim)) {
962                                 ximStyle = targetStyle;
963                                 // FIXME: use __arglist when it gets working. See bug #321686
964                                 switch (targetStyle) {
965                                 case styleOverTheSpot:
966                                         xic = CreateOverTheSpotXic (window, xim);
967                                         if (xic != IntPtr.Zero)
968                                                 break;
969                                         //Console.WriteLine ("failed to create XIC in over-the-spot mode.");
970                                         continue;
971                                 case styleOnTheSpot:
972                                         // Since .NET/Winforms seems to support only over-the-spot mode,,
973                                         // I'm not likely to continue on-the-spot implementation. But in
974                                         // case we need it, this code will be still useful.
975                                         xic = CreateOnTheSpotXic (window, xim);
976                                         if (xic != IntPtr.Zero)
977                                                 break;
978                                         //Console.WriteLine ("failed to create XIC in on-the-spot mode.");
979                                         continue;
980                                 case styleRoot:
981                                         xic = XCreateIC (xim,
982                                                 XNames.XNInputStyle, styleRoot,
983                                                 XNames.XNClientWindow, window,
984                                                 IntPtr.Zero);
985                                         break;
986                                 }
987                         }
988                         // fall back to root mode if all modes failed
989                         if (xic == IntPtr.Zero) {
990                                 ximStyle = styleRoot;
991                                 xic = XCreateIC (xim,
992                                         XNames.XNInputStyle, styleRoot,
993                                         XNames.XNClientWindow, window,
994                                         XNames.XNFocusWindow, window,
995                                         IntPtr.Zero);
996                         }
997                         return xic;
998                 }
999
1000                 private IntPtr CreateOverTheSpotXic (IntPtr window, IntPtr xim)
1001                 {
1002                         IntPtr list;
1003                         int count;
1004                         Control c = Control.FromHandle (window);
1005                         string xlfd = String.Format ("-*-*-*-*-*-*-{0}-*-*-*-*-*-*-*", (int) c.Font.Size);
1006                         IntPtr fontSet = XCreateFontSet (display, xlfd, out list, out count, IntPtr.Zero);
1007                         XPoint spot = new XPoint ();
1008                         spot.X = 0;
1009                         spot.Y = 0;
1010                         IntPtr pSL = IntPtr.Zero, pFS = IntPtr.Zero;
1011                         try {
1012                                 pSL = Marshal.StringToHGlobalAnsi (XNames.XNSpotLocation);
1013                                 pFS = Marshal.StringToHGlobalAnsi (XNames.XNFontSet);
1014                                 IntPtr preedit = XVaCreateNestedList (0,
1015                                         pSL, spot,
1016                                         pFS, fontSet,
1017                                         IntPtr.Zero);
1018                                 return XCreateIC (xim,
1019                                         XNames.XNInputStyle, styleOverTheSpot,
1020                                         XNames.XNClientWindow, window,
1021                                         XNames.XNPreeditAttributes, preedit,
1022                                         IntPtr.Zero);
1023                         } finally {
1024                                 if (pSL != IntPtr.Zero)
1025                                         Marshal.FreeHGlobal (pSL);
1026                                 if (pFS != IntPtr.Zero)
1027                                         Marshal.FreeHGlobal (pFS);
1028                                 XFreeStringList (list);
1029                                 //XplatUIX11.XFree (preedit);
1030                                 //XFreeFontSet (fontSet);
1031                         }
1032                 }
1033
1034                 private IntPtr CreateOnTheSpotXic (IntPtr window, IntPtr xim)
1035                 {
1036                         callbackContext = new XIMCallbackContext (window);
1037                         return callbackContext.CreateXic (window, xim);
1038                 }
1039
1040                 class XIMCallbackContext
1041                 {
1042                         XIMCallback startCB, doneCB, drawCB, caretCB;
1043                         IntPtr pStartCB = IntPtr.Zero, pDoneCB = IntPtr.Zero, pDrawCB = IntPtr.Zero, pCaretCB = IntPtr.Zero;
1044                         IntPtr pStartCBN = IntPtr.Zero, pDoneCBN = IntPtr.Zero, pDrawCBN = IntPtr.Zero, pCaretCBN = IntPtr.Zero;
1045
1046                         public XIMCallbackContext (IntPtr clientWindow)
1047                         {
1048                                 startCB = new XIMCallback (clientWindow, DoPreeditStart);
1049                                 doneCB = new XIMCallback (clientWindow, DoPreeditDone);
1050                                 drawCB = new XIMCallback (clientWindow, DoPreeditDraw);
1051                                 caretCB = new XIMCallback (clientWindow, DoPreeditCaret);
1052                                 pStartCB = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (XIMCallback)));
1053                                 pDoneCB = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (XIMCallback)));
1054                                 pDrawCB = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (XIMCallback)));
1055                                 pCaretCB = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (XIMCallback)));
1056                                 pStartCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditStartCallback);
1057                                 pDoneCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditDoneCallback);
1058                                 pDrawCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditDrawCallback);
1059                                 pCaretCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditCaretCallback);
1060                         }
1061
1062                         ~XIMCallbackContext ()
1063                         {
1064                                 if (pStartCBN != IntPtr.Zero)
1065                                         Marshal.FreeHGlobal (pStartCBN);
1066                                 if (pDoneCBN != IntPtr.Zero)
1067                                         Marshal.FreeHGlobal (pDoneCBN);
1068                                 if (pDrawCBN != IntPtr.Zero)
1069                                         Marshal.FreeHGlobal (pDrawCBN);
1070                                 if (pCaretCBN != IntPtr.Zero)
1071                                         Marshal.FreeHGlobal (pCaretCBN);
1072
1073                                 if (pStartCB != IntPtr.Zero)
1074                                         Marshal.FreeHGlobal (pStartCB);
1075                                 if (pDoneCB != IntPtr.Zero)
1076                                         Marshal.FreeHGlobal (pDoneCB);
1077                                 if (pDrawCB != IntPtr.Zero)
1078                                         Marshal.FreeHGlobal (pDrawCB);
1079                                 if (pCaretCB != IntPtr.Zero)
1080                                         Marshal.FreeHGlobal (pCaretCB);
1081                         }
1082
1083                         int DoPreeditStart (IntPtr xic, IntPtr clientData, IntPtr callData)
1084                         {
1085                                 Debug.WriteLine ("DoPreeditStart");
1086                                 XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITSTART, clientData, callData);
1087                                 return 100;
1088                         }
1089
1090                         int DoPreeditDone (IntPtr xic, IntPtr clientData, IntPtr callData)
1091                         {
1092                                 Debug.WriteLine ("DoPreeditDone");
1093                                 XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITDONE, clientData, callData);
1094                                 return 0;
1095                         }
1096
1097                         int DoPreeditDraw (IntPtr xic, IntPtr clientData, IntPtr callData)
1098                         {
1099                                 Debug.WriteLine ("DoPreeditDraw");
1100                                 XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITDRAW, clientData, callData);
1101                                 return 0;
1102                         }
1103
1104                         int DoPreeditCaret (IntPtr xic, IntPtr clientData, IntPtr callData)
1105                         {
1106                                 Debug.WriteLine ("DoPreeditCaret");
1107                                 XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITCARET, clientData, callData);
1108                                 return 0;
1109                         }
1110
1111                         public IntPtr CreateXic (IntPtr window, IntPtr xim)
1112                         {
1113                                 Marshal.StructureToPtr (startCB, pStartCB, false);
1114                                 Marshal.StructureToPtr (doneCB, pDoneCB, false);
1115                                 Marshal.StructureToPtr (drawCB, pDrawCB, false);
1116                                 Marshal.StructureToPtr (caretCB, pCaretCB, false);
1117                                 IntPtr preedit = XVaCreateNestedList (0,
1118                                         pStartCBN, pStartCB,
1119                                         pDoneCBN, pDoneCB,
1120                                         pDrawCBN, pDrawCB,
1121                                         pCaretCBN, pCaretCB,
1122                                         IntPtr.Zero);
1123                                 return XCreateIC (xim,
1124                                         XNames.XNInputStyle, styleOnTheSpot,
1125                                         XNames.XNClientWindow, window,
1126                                         XNames.XNPreeditAttributes, preedit,
1127                                         IntPtr.Zero);
1128                         }
1129                 }
1130
1131                 class XIMPositionContext
1132                 {
1133                         public CaretStruct Caret;
1134                         public int X;
1135                         public int Y;
1136                 }
1137
1138                 internal void SetCaretPos (CaretStruct caret, IntPtr handle, int x, int y)
1139                 {
1140                         if (ximStyle != styleOverTheSpot)
1141                                 return;
1142
1143                         if (positionContext == null)
1144                                 this.positionContext = new XIMPositionContext ();
1145
1146                         positionContext.Caret = caret;
1147                         positionContext.X = x;
1148                         positionContext.Y = y + caret.Height;
1149
1150                         MoveCurrentCaretPos ();
1151                 }
1152
1153                 internal void MoveCurrentCaretPos ()
1154                 {
1155                         if (positionContext == null || ximStyle != styleOverTheSpot || client_window == IntPtr.Zero)
1156                                 return;
1157
1158                         int x = positionContext.X;
1159                         int y = positionContext.Y;
1160                         CaretStruct caret = positionContext.Caret;
1161                         IntPtr xic = GetXic (client_window);
1162                         if (xic == IntPtr.Zero)
1163                                 return;
1164                         Control control = Control.FromHandle (client_window);
1165                         if (control == null || !control.IsHandleCreated)
1166                                 return;
1167                         control = Control.FromHandle (caret.Hwnd);
1168                         if (control == null || !control.IsHandleCreated)
1169                                 return;
1170                         Hwnd hwnd = Hwnd.ObjectFromHandle (client_window);
1171                         if (!hwnd.mapped)
1172                                 return;
1173
1174                         int dx, dy;
1175                         IntPtr child;
1176                         lock (XlibLock) {
1177                                 XplatUIX11.XTranslateCoordinates (display, client_window, client_window, x, y, out dx, out dy, out child);
1178                         }
1179
1180                         XPoint spot = new XPoint ();
1181                         spot.X = (short) dx;
1182                         spot.Y = (short) dy;
1183
1184                         IntPtr pSL = IntPtr.Zero;
1185                         try {
1186                                 pSL = Marshal.StringToHGlobalAnsi (XNames.XNSpotLocation);
1187                                 IntPtr preedit = XVaCreateNestedList (0, pSL, spot, IntPtr.Zero);
1188                                 XSetICValues (xic, XNames.XNPreeditAttributes, preedit, IntPtr.Zero);
1189                         } finally {
1190                                 if (pSL != IntPtr.Zero)
1191                                         Marshal.FreeHGlobal (pSL);
1192                         }
1193                 }
1194
1195                 private bool have_Xutf8LookupString = true;
1196
1197                 private int LookupString (ref XEvent xevent, int len, out XKeySym keysym, out IntPtr status)
1198                 {
1199                         IntPtr keysym_res;
1200                         int res;
1201
1202                         status = IntPtr.Zero;
1203                         IntPtr xic = GetXic (client_window);
1204                         if (xic != IntPtr.Zero && have_Xutf8LookupString && xevent.type == XEventName.KeyPress) {
1205                                 do {
1206                                         try {
1207                                                 res = Xutf8LookupString (xic, ref xevent, lookup_byte_buffer, 100, out keysym_res,  out status);
1208                                         } catch (EntryPointNotFoundException) {
1209                                                 have_Xutf8LookupString = false;
1210
1211                                                 // call again, this time we'll go through the non-xic clause
1212                                                 return LookupString (ref xevent, len, out keysym, out status);
1213                                         }
1214                                         if ((int) status != -1) // XLookupBufferOverflow
1215                                                 break;
1216                                         lookup_byte_buffer = new byte [lookup_byte_buffer.Length << 1];
1217                                 } while (true);
1218                                 lookup_buffer.Length = 0;
1219                                 string s = Encoding.UTF8.GetString (lookup_byte_buffer, 0, res);
1220                                 lookup_buffer.Append (s);
1221                                 keysym = (XKeySym) keysym_res.ToInt32 ();
1222                                 return s.Length;
1223                         } else {
1224                                 IntPtr statusPtr = IntPtr.Zero;
1225                                 lookup_buffer.Length = 0;
1226                                 res = XLookupString (ref xevent, lookup_buffer, len, out keysym_res, out statusPtr);
1227                                 keysym = (XKeySym) keysym_res.ToInt32 ();
1228                                 return res;
1229                         }
1230                 }
1231
1232                 [DllImport ("libX11")]
1233                 private static extern IntPtr XOpenIM (IntPtr display, IntPtr rdb, IntPtr res_name, IntPtr res_class);
1234
1235                 [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
1236                 private static extern IntPtr XCreateIC (IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, IntPtr terminator);
1237                 [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
1238                 private static extern IntPtr XCreateIC (IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, string name3, IntPtr value3, IntPtr terminator);
1239 //              [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
1240 //              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);
1241
1242                 [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
1243                 private static extern IntPtr XVaCreateNestedList (int dummy, IntPtr name0, XPoint value0, IntPtr terminator);
1244                 [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
1245                 private static extern IntPtr XVaCreateNestedList (int dummy, IntPtr name0, XPoint value0, IntPtr name1, IntPtr value1, IntPtr terminator);
1246                 [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
1247                 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);
1248
1249                 [DllImport ("libX11")]
1250                 private static extern IntPtr XCreateFontSet (IntPtr display, string name, out IntPtr list, out int count, IntPtr terminator);
1251
1252                 [DllImport ("libX11")]
1253                 internal extern static void XFreeFontSet (IntPtr data);
1254
1255                 [DllImport ("libX11")]
1256                 private static extern void XFreeStringList (IntPtr ptr);
1257
1258                 //[DllImport ("libX11")]
1259                 //private static extern IntPtr XIMOfIC (IntPtr xic);
1260
1261                 [DllImport ("libX11")]
1262                 private static extern void XCloseIM (IntPtr xim);
1263
1264                 [DllImport ("libX11")]
1265                 private static extern void XDestroyIC (IntPtr xic);
1266
1267                 [DllImport ("libX11")]
1268                 private static extern string XGetIMValues (IntPtr xim, string name, out IntPtr value, IntPtr terminator);
1269
1270                 [DllImport ("libX11")]
1271                 private static extern string XGetICValues (IntPtr xic, string name, out EventMask value, IntPtr terminator);
1272
1273                 [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
1274                 private static extern void XSetICValues (IntPtr xic, string name, IntPtr value, IntPtr terminator);
1275
1276                 [DllImport ("libX11")]
1277                 private static extern void XSetICFocus (IntPtr xic);
1278
1279                 [DllImport ("libX11")]
1280                 private static extern void XUnsetICFocus (IntPtr xic);
1281
1282                 [DllImport ("libX11")]
1283                 private static extern string Xutf8ResetIC (IntPtr xic);
1284
1285                 [DllImport ("libX11")]
1286                 private static extern bool XSupportsLocale ();
1287
1288                 [DllImport ("libX11")]
1289                 private static extern bool XSetLocaleModifiers (string mods);
1290
1291                 [DllImport ("libX11")]
1292                 internal extern static int XLookupString(ref XEvent xevent, StringBuilder buffer, int num_bytes, out IntPtr keysym, out IntPtr status);
1293                 [DllImport ("libX11")]
1294                 internal extern static int Xutf8LookupString(IntPtr xic, ref XEvent xevent, byte [] buffer, int num_bytes, out IntPtr keysym, out IntPtr status);
1295
1296                 [DllImport ("libX11")]
1297                 private static extern IntPtr XGetKeyboardMapping (IntPtr display, byte first_keycode, int keycode_count, 
1298                                 out int keysyms_per_keycode_return);
1299
1300                 [DllImport ("libX11")]
1301                 private static extern void XDisplayKeycodes (IntPtr display, out int min, out int max);
1302
1303                 [DllImport ("libX11", EntryPoint="XKeycodeToKeysym")]
1304                 private static extern uint XKeycodeToKeysym (IntPtr display, int keycode, int index);
1305
1306                 [DllImport ("libX11")]
1307                 private static extern int XKeysymToKeycode (IntPtr display, IntPtr keysym);
1308                 private static int XKeysymToKeycode (IntPtr display, int keysym) {
1309                         return XKeysymToKeycode(display, (IntPtr)keysym);
1310                 }
1311
1312                 [DllImport ("libX11")]
1313                 internal extern static IntPtr XGetModifierMapping (IntPtr display);
1314
1315                 [DllImport ("libX11")]
1316                 internal extern static int XFreeModifiermap (IntPtr modmap);
1317
1318
1319                 private readonly static int [] nonchar_key_vkey = new int []
1320                 {
1321                         /* unused */
1322                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF00 */
1323                         /* special keys */
1324                         (int) VirtualKeys.VK_BACK, (int) VirtualKeys.VK_TAB, 0, (int) VirtualKeys.VK_CLEAR, 0, (int) VirtualKeys.VK_RETURN, 0, 0,           /* FF08 */
1325                         0, 0, 0, (int) VirtualKeys.VK_PAUSE, (int) VirtualKeys.VK_SCROLL, 0, 0, 0,                           /* FF10 */
1326                         0, 0, 0, (int) VirtualKeys.VK_ESCAPE, 0, 0, 0, 0,                             /* FF18 */
1327                         0, 0, (int) VirtualKeys.VK_NONCONVERT, (int) VirtualKeys.VK_CONVERT, 0, 0, 0, 0,                                            /* FF20 */
1328                         0, 0, (int) VirtualKeys.VK_OEM_AUTO, 0, 0, 0, 0, 0,                                         /* FF28 */
1329                         /* unused */
1330                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF30 */
1331                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF38 */
1332                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF40 */
1333                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF48 */
1334                         /* cursor keys */
1335                         (int) VirtualKeys.VK_HOME, (int) VirtualKeys.VK_LEFT, (int) VirtualKeys.VK_UP, (int) VirtualKeys.VK_RIGHT,                          /* FF50 */
1336                         (int) VirtualKeys.VK_DOWN, (int) VirtualKeys.VK_PRIOR, (int) VirtualKeys.VK_NEXT, (int) VirtualKeys.VK_END,
1337                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF58 */
1338                         /* misc keys */
1339                         (int) VirtualKeys.VK_SELECT, (int) VirtualKeys.VK_SNAPSHOT, (int) VirtualKeys.VK_EXECUTE, (int) VirtualKeys.VK_INSERT, 0, 0, 0, 0,  /* FF60 */
1340                         (int) VirtualKeys.VK_CANCEL, (int) VirtualKeys.VK_HELP, (int) VirtualKeys.VK_CANCEL, (int) VirtualKeys.VK_CANCEL, 0, 0, 0, 0,       /* FF68 */
1341                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF70 */
1342                         /* keypad keys */
1343                         0, 0, 0, 0, 0, 0, 0, (int) VirtualKeys.VK_NUMLOCK,                            /* FF78 */
1344                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF80 */
1345                         0, 0, 0, 0, 0, (int) VirtualKeys.VK_RETURN, 0, 0,                             /* FF88 */
1346                         0, 0, 0, 0, 0, (int) VirtualKeys.VK_HOME, (int) VirtualKeys.VK_LEFT, (int) VirtualKeys.VK_UP,                     /* FF90 */
1347                         (int) VirtualKeys.VK_RIGHT, (int) VirtualKeys.VK_DOWN, (int) VirtualKeys.VK_PRIOR, (int) VirtualKeys.VK_NEXT,                       /* FF98 */
1348                         (int) VirtualKeys.VK_END, 0, (int) VirtualKeys.VK_INSERT, (int) VirtualKeys.VK_DELETE,
1349                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FFA0 */
1350                         0, 0, (int) VirtualKeys.VK_MULTIPLY, (int) VirtualKeys.VK_ADD,                                  /* FFA8 */
1351                         (int) VirtualKeys.VK_SEPARATOR, (int) VirtualKeys.VK_SUBTRACT, (int) VirtualKeys.VK_DECIMAL, (int) VirtualKeys.VK_DIVIDE,
1352                         (int) VirtualKeys.VK_NUMPAD0, (int) VirtualKeys.VK_NUMPAD1, (int) VirtualKeys.VK_NUMPAD2, (int) VirtualKeys.VK_NUMPAD3,             /* FFB0 */
1353                         (int) VirtualKeys.VK_NUMPAD4, (int) VirtualKeys.VK_NUMPAD5, (int) VirtualKeys.VK_NUMPAD6, (int) VirtualKeys.VK_NUMPAD7,
1354                         (int) VirtualKeys.VK_NUMPAD8, (int) VirtualKeys.VK_NUMPAD9, 0, 0, 0, 0,                         /* FFB8 */
1355                         /* function keys */
1356                         (int) VirtualKeys.VK_F1, (int) VirtualKeys.VK_F2,
1357                         (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 */
1358                         (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 */
1359                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FFD0 */
1360                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FFD8 */
1361                         /* modifier keys */
1362                         0, (int) VirtualKeys.VK_SHIFT, (int) VirtualKeys.VK_SHIFT, (int) VirtualKeys.VK_CONTROL,                          /* FFE0 */
1363                         (int) VirtualKeys.VK_CONTROL, (int) VirtualKeys.VK_CAPITAL, 0, (int) VirtualKeys.VK_MENU,
1364                         (int) VirtualKeys.VK_MENU, (int) VirtualKeys.VK_MENU, (int) VirtualKeys.VK_MENU, 0, 0, 0, 0, 0,                   /* FFE8 */
1365                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FFF0 */
1366                         0, 0, 0, 0, 0, 0, 0, (int) VirtualKeys.VK_DELETE                              /* FFF8 */
1367                 };
1368
1369                 private static readonly int [] nonchar_key_scan = new int []
1370                 {
1371                         /* unused */
1372                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF00 */
1373                         /* special keys */
1374                         0x0E, 0x0F, 0x00, /*?*/ 0, 0x00, 0x1C, 0x00, 0x00,           /* FF08 */
1375                         0x00, 0x00, 0x00, 0x45, 0x46, 0x00, 0x00, 0x00,              /* FF10 */
1376                         0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,              /* FF18 */
1377                         /* unused */
1378                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF20 */
1379                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF28 */
1380                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF30 */
1381                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF38 */
1382                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF40 */
1383                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF48 */
1384                         /* cursor keys */
1385                         0x147, 0x14B, 0x148, 0x14D, 0x150, 0x149, 0x151, 0x14F,      /* FF50 */
1386                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF58 */
1387                         /* misc keys */
1388                         /*?*/ 0, 0x137, /*?*/ 0, 0x152, 0x00, 0x00, 0x00, 0x00,      /* FF60 */
1389                         /*?*/ 0, /*?*/ 0, 0x38, 0x146, 0x00, 0x00, 0x00, 0x00,       /* FF68 */
1390                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF70 */
1391                         /* keypad keys */
1392                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x138, 0x145,            /* FF78 */
1393                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF80 */
1394                         0x00, 0x00, 0x00, 0x00, 0x00, 0x11C, 0x00, 0x00,             /* FF88 */
1395                         0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x4B, 0x48,              /* FF90 */
1396                         0x4D, 0x50, 0x49, 0x51, 0x4F, 0x4C, 0x52, 0x53,              /* FF98 */
1397                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FFA0 */
1398                         0x00, 0x00, 0x37, 0x4E, /*?*/ 0, 0x4A, 0x53, 0x135,          /* FFA8 */
1399                         0x52, 0x4F, 0x50, 0x51, 0x4B, 0x4C, 0x4D, 0x47,              /* FFB0 */
1400                         0x48, 0x49, 0x00, 0x00, 0x00, 0x00,                          /* FFB8 */
1401                         /* function keys */
1402                         0x3B, 0x3C,
1403                         0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44,              /* FFC0 */
1404                         0x57, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FFC8 */
1405                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FFD0 */
1406                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FFD8 */
1407                         /* modifier keys */
1408                         0x00, 0x2A, 0x36, 0x1D, 0x11D, 0x3A, 0x00, 0x38,             /* FFE0 */
1409                         0x138, 0x38, 0x138, 0x00, 0x00, 0x00, 0x00, 0x00,            /* FFE8 */
1410                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FFF0 */
1411                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x153              /* FFF8 */
1412                 };
1413
1414                 private readonly static int [] nonchar_vkey_key = new int []
1415                 {
1416                         0, 0, 0, 0, 0,          /* 00-04 */ 
1417                         0, 0, 0, (int)XKeySym.XK_BackSpace, (int)XKeySym.XK_Tab,                /* 05-09 */
1418                         0, 0, (int)XKeySym.XK_Clear, (int)XKeySym.XK_Return,    0, 0,   /* 0A-0F */
1419                         (int)XKeySym.XK_Shift_L, (int)XKeySym.XK_Control_L, (int)XKeySym.XK_Menu, 0, (int)XKeySym.XK_Caps_Lock,         /* 10-14 */ 
1420                         0, 0, 0, 0, 0,          /* 15-19 */
1421                         0, 0, 0, 0,     0, 0,   /* 1A-1F */
1422                         0, 0, 0, (int)XKeySym.XK_End, (int)XKeySym.XK_Home,             /* 20-24 */ 
1423                         (int)XKeySym.XK_Left, (int)XKeySym.XK_Up, (int)XKeySym.XK_Right, (int)XKeySym.XK_Down, 0,               /* 25-29 */
1424                         0, 0, 0, 0,     0, 0,   /* 2A-2F */
1425                         0, 0, 0, 0, 0,          /* 30-34 */ 
1426                         0, 0, 0, 0, 0,          /* 35-39 */
1427                         0, 0, 0, 0,     0, 0,   /* 3A-3F */
1428                         0, 0, 0, 0, 0,          /* 40-44 */ 
1429                         0, 0, 0, 0, 0,          /* 45-49 */
1430                         0, 0, 0, 0,     0, 0,   /* 4A-4F */
1431                         0, 0, 0, 0, 0,          /* 50-54 */ 
1432                         0, 0, 0, 0, 0,          /* 55-59 */
1433                         0, (int)XKeySym.XK_Meta_L, (int)XKeySym.XK_Meta_R, 0,   0, 0,   /* 5A-5F */
1434                         0, 0, 0, 0, 0,          /* 60-64 */ 
1435                         0, 0, 0, 0, 0,          /* 65-69 */
1436                         0, 0, 0, 0,     0, 0,   /* 6A-6F */
1437                         0, 0, 0, 0, 0,          /* 70-74 */ 
1438                         0, 0, 0, 0, 0,          /* 75-79 */
1439                         0, 0, 0, 0,     0, 0,   /* 7A-7F */
1440                         0, 0, 0, 0, 0,          /* 80-84 */ 
1441                         0, 0, 0, 0, 0,          /* 85-89 */
1442                         0, 0, 0, 0,     0, 0,   /* 8A-8F */
1443                         0, 0, 0, 0, 0,          /* 90-94 */ 
1444                         0, 0, 0, 0, 0,          /* 95-99 */
1445                         0, 0, 0, 0,     0, 0,   /* 9A-9F */
1446                         (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 */ 
1447                         (int)XKeySym.XK_Alt_R, 0, 0, 0, 0,              /* A5-A9 */
1448                         0, 0, 0, 0,     0, 0,   /* AA-AF */
1449                         0, 0, 0, 0, 0,          /* B0-B4 */ 
1450                         0, 0, 0, 0, 0,          /* B5-B9 */
1451                         0, 0, 0, 0,     0, 0,   /* BA-BF */
1452                         0, 0, 0, 0, 0,          /* C0-C4 */ 
1453                         0, 0, 0, 0, 0,          /* C5-C9 */
1454                         0, 0, 0, 0,     0, 0,   /* CA-CF */
1455                         0, 0, 0, 0, 0,          /* D0-D4 */ 
1456                         0, 0, 0, 0, 0,          /* D5-D9 */
1457                         0, 0, 0, 0,     0, 0,   /* DA-DF */
1458                         0, 0, 0, 0, 0,          /* E0-E4 */ 
1459                         0, 0, 0, 0, 0,          /* E5-E9 */
1460                         0, 0, 0, 0,     0, 0,   /* EA-EF */
1461                         0, 0, 0, 0, 0,          /* F0-F4 */ 
1462                         0, 0, 0, 0, 0,          /* F5-F9 */
1463                         0, 0, 0, 0,     0, 0    /* FA-FF */
1464                 };
1465                 
1466         }
1467
1468 }
1469