also, disable the call do BindColumns in
[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.Text;
36 using System.Globalization;
37 using System.Runtime.InteropServices;
38
39 namespace System.Windows.Forms {
40
41         internal class X11Keyboard {
42
43                 private IntPtr display;
44                 private IntPtr window;
45                 private IntPtr xic;
46                 private StringBuilder lookup_buffer;
47                 private int min_keycode, max_keycode, keysyms_per_keycode, syms;
48                 private int [] keyc2vkey = new int [256];
49                 private int [] keyc2scan = new int [256];
50                 private byte [] key_state_table = new byte [256];
51                 private int lcid;
52                 private bool num_state, cap_state;
53                 private bool initialized;
54
55                 private int NumLockMask;
56                 private int AltGrMask;
57
58                 public X11Keyboard (IntPtr display, IntPtr window)
59                 {
60                         this.display = display;
61                         this.window = window;
62                         lookup_buffer = new StringBuilder (24);
63                 }
64
65                 public void EnsureLayoutInitialized ()
66                 {
67                         if (initialized)
68                                 return;
69
70                         KeyboardLayouts layouts = new KeyboardLayouts ();
71                         KeyboardLayout layout = DetectLayout (layouts);
72                         lcid = layout.Lcid;
73                         CreateConversionArray (layouts, layout);
74
75                         if (!XSupportsLocale ()) {
76                                 Console.Error.WriteLine ("X does not support your locale");
77                         }
78
79                         if (!XSetLocaleModifiers (String.Empty)) {
80                                 Console.Error.WriteLine ("Could not set X locale modifiers");
81                         }
82
83                         IntPtr xim = XOpenIM (display, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
84                         if (xim == IntPtr.Zero) 
85                                 Console.Error.WriteLine ("Could not get XIM");
86                         else 
87                                 xic = CreateXic (window, xim);
88                         
89                         initialized = true;
90                 }
91
92                 public Keys ModifierKeys {
93                         get {
94                                 Keys keys = Keys.None;
95                                 if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0)
96                                         keys |= Keys.Shift;
97                                 if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0)
98                                         keys |= Keys.Control;
99                                 if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0)
100                                         keys |= Keys.Alt;
101                                 return keys;
102                         }
103                 }
104
105                 public void FocusIn (IntPtr focus_window)
106                 {
107                         if (xic != IntPtr.Zero)
108                                 XSetICFocus (xic);
109                 }
110
111                 public void FocusOut (IntPtr focus_window)
112                 {
113                         if (xic != IntPtr.Zero)
114                                 XUnsetICFocus (xic);
115                 }
116
117                 public bool ResetKeyState(IntPtr hwnd, ref MSG msg) {
118                         // FIXME - keep defining events/msg and return true until we've 'restored' all
119                         // pending keypresses
120                         if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0) {
121                                 key_state_table [(int) VirtualKeys.VK_SHIFT] &=  unchecked((byte)~0x80);
122                         }
123
124                         if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) {
125                                 key_state_table [(int) VirtualKeys.VK_CONTROL] &=  unchecked((byte)~0x80);
126                         }
127
128                         if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0) {
129                                 key_state_table [(int) VirtualKeys.VK_MENU] &=  unchecked((byte)~0x80);
130                         }
131                         return false;
132                 }
133
134                 public void KeyEvent (IntPtr hwnd, XEvent xevent, ref MSG msg)
135                 {
136                         EnsureLayoutInitialized ();
137
138                         XKeySym keysym;
139                         int ascii_chars;
140
141                         IntPtr status = IntPtr.Zero;
142                         ascii_chars = LookupString (ref xevent, 24, out keysym, out status);
143
144                         if (((int) keysym >= (int) MiscKeys.XK_ISO_Lock && 
145                                 (int) keysym <= (int) MiscKeys.XK_ISO_Last_Group_Lock) ||
146                                 (int) keysym == (int) MiscKeys.XK_Mode_switch) {
147                                 UpdateKeyState (xevent);
148                                 return;
149                         }
150
151                         if ((xevent.KeyEvent.keycode >> 8) == 0x10)
152                                 xevent.KeyEvent.keycode = xevent.KeyEvent.keycode & 0xFF;
153
154                         int event_time = (int)xevent.KeyEvent.time;
155
156                         if (status == (IntPtr) 2) {
157                                 // Copy chars into a globally accessible var, i don't think
158                                 // this var is exposed anywhere though, so we can just ignore this
159                                 return;
160                         }
161
162                         AltGrMask = xevent.KeyEvent.state & (0x6000 | (int) KeyMasks.ModMasks);
163                         int vkey = EventToVkey (xevent);
164                         if (vkey == 0 && ascii_chars != 0) {
165                                 vkey = (int) VirtualKeys.VK_NONAME;
166                         }
167
168                         switch ((VirtualKeys) (vkey & 0xFF)) {
169                         case VirtualKeys.VK_NUMLOCK:
170                                 GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, xevent.type, event_time);
171                                 break;
172                         case VirtualKeys.VK_CAPITAL:
173                                 GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, xevent.type, event_time);
174                                 break;
175                         default:
176                                 if (((key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x01) == 0) != ((xevent.KeyEvent.state & NumLockMask) == 0)) {
177                                         GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, XEventName.KeyPress, event_time);
178                                         GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, XEventName.KeyRelease, event_time);
179                                 }
180
181                                 if (((key_state_table [(int) VirtualKeys.VK_CAPITAL] & 0x01) == 0) != ((xevent.KeyEvent.state & (int) KeyMasks.LockMask) == 0)) {
182                                         GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, XEventName.KeyPress, event_time);
183                                         GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, XEventName.KeyRelease, event_time);
184                                 }
185
186                                 num_state = false;
187                                 cap_state = false;
188
189                                 int bscan = (keyc2scan [xevent.KeyEvent.keycode] & 0xFF);
190                                 KeybdEventFlags dw_flags = KeybdEventFlags.None;
191                                 if (xevent.type == XEventName.KeyRelease)
192                                         dw_flags |= KeybdEventFlags.KeyUp;
193                                 if ((vkey & 0x100) != 0)
194                                         dw_flags |= KeybdEventFlags.ExtendedKey;
195                                 msg = SendKeyboardInput ((VirtualKeys) (vkey & 0xFF), bscan, dw_flags, event_time);
196                                 msg.hwnd = hwnd;
197                                 break;
198                         }
199                 }
200
201                 public bool TranslateMessage (ref MSG msg)
202                 {
203                         bool res = false;
204
205                         if (msg.message >= Msg.WM_KEYFIRST && msg.message <= Msg.WM_KEYLAST)
206                                 res = true;
207
208                         if (msg.message != Msg.WM_KEYDOWN && msg.message != Msg.WM_SYSKEYDOWN)
209                                 return res;
210
211                         EnsureLayoutInitialized ();
212
213                         string buffer;
214                         Msg message;
215                         int tu = ToUnicode ((int) msg.wParam, Control.HighOrder ((int) msg.lParam), out buffer);
216                         switch (tu) {
217                         case 1:
218                                 message = (msg.message == Msg.WM_KEYDOWN ? Msg.WM_CHAR : Msg.WM_SYSCHAR);
219                                 XplatUI.PostMessage (msg.hwnd, message, (IntPtr) buffer [0], msg.lParam);
220                                 break;
221                         case -1:
222                                 message = (msg.message == Msg.WM_KEYDOWN ? Msg.WM_DEADCHAR : Msg.WM_SYSDEADCHAR);
223                                 XplatUI.PostMessage (msg.hwnd, message, (IntPtr) buffer [0], msg.lParam);
224                                 return true;
225                         }
226                         
227                         return res;
228                 }
229
230                 public int ToKeycode(int key) 
231                 {
232                         int keycode = 0;
233                         
234                         if (nonchar_vkey_key[key] > 0)
235                                 keycode = XKeysymToKeycode (display, nonchar_vkey_key[key]);
236                         
237                         if (keycode == 0)
238                                 keycode = XKeysymToKeycode (display, key);
239
240                         return keycode;         
241                 }
242
243                 public int ToUnicode (int vkey, int scan, out string buffer)
244                 {
245                         if ((scan & 0x8000) != 0) {
246                                 buffer = String.Empty;
247                                 return 0;
248                         }
249
250                         XEvent e = new XEvent ();
251                         e.AnyEvent.type = XEventName.KeyPress;
252                         e.KeyEvent.display = display;
253                         e.KeyEvent.keycode = 0;
254                         e.KeyEvent.state = 0;
255
256                         if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0) {
257                                 e.KeyEvent.state |= (int) KeyMasks.ShiftMask;
258                         }
259
260                         if ((key_state_table [(int) VirtualKeys.VK_CAPITAL] & 0x01) != 0) {
261                                 e.KeyEvent.state |= (int) KeyMasks.LockMask;
262                         }
263
264                         if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) {
265                                 e.KeyEvent.state |= (int) KeyMasks.ControlMask;
266                         }
267
268                         if ((key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x01) != 0) {
269                                 e.KeyEvent.state |= NumLockMask;
270                         }
271
272                         e.KeyEvent.state |= AltGrMask;
273
274                         for (int keyc = min_keycode; (keyc <= max_keycode) && (e.KeyEvent.keycode == 0); keyc++) {
275                                 // find keycode that could have generated this vkey
276                                 if ((keyc2vkey [keyc] & 0xFF) == vkey) {
277                                         // filter extended bit because it is not known
278                                         e.KeyEvent.keycode = keyc;
279                                         if ((EventToVkey (e) & 0xFF) != vkey) {
280                                                 // Wrong one (ex: because of num,lock state)
281                                                 e.KeyEvent.keycode = 0;
282                                         }
283                                 }
284                         }
285
286                         if ((vkey >= (int) VirtualKeys.VK_NUMPAD0) && (vkey <= (int) VirtualKeys.VK_NUMPAD9))
287                                 e.KeyEvent.keycode = XKeysymToKeycode (display, vkey - (int) VirtualKeys.VK_NUMPAD0 + (int) KeypadKeys.XK_KP_0);
288
289                         if (vkey == (int) VirtualKeys.VK_DECIMAL)
290                                 e.KeyEvent.keycode = XKeysymToKeycode (display, (int) KeypadKeys.XK_KP_Decimal);
291                         
292                         if (vkey == (int) VirtualKeys.VK_SEPARATOR)
293                                 e.KeyEvent.keycode = XKeysymToKeycode(display, (int) KeypadKeys.XK_KP_Separator);
294
295                         if (e.KeyEvent.keycode == 0 && vkey != (int) VirtualKeys.VK_NONAME) {
296                                 // And I couldn't find the keycode so i returned the vkey and was like whatever
297                                 Console.Error.WriteLine ("unknown virtual key {0:X}", vkey);
298                                 buffer = String.Empty;
299                                 return vkey; 
300                         }
301
302                         XKeySym t;
303                         IntPtr status;
304                         int res = LookupString (ref e, 24, out t, out status);
305                         int keysym = (int) t;
306
307                         buffer = String.Empty;
308                         if (res == 0) {
309                                 int dead_char = MapDeadKeySym (keysym);
310                                 if (dead_char != 0) {
311                                         byte [] bytes = new byte [1];
312                                         bytes [0] = (byte) dead_char;
313                                         Encoding encoding = Encoding.GetEncoding (new CultureInfo (lcid).TextInfo.ANSICodePage);
314                                         buffer = new string (encoding.GetChars (bytes));
315                                         res = -1;
316                                 }
317                         } else {
318                                 // Shift + arrow, shift + home, ....
319                                 // X returns a char for it, but windows doesn't
320                                 if (((e.KeyEvent.state & NumLockMask) == 0) && ((e.KeyEvent.state & (int) KeyMasks.ShiftMask) != 0) &&
321                                                 (keysym >= (int) KeypadKeys.XK_KP_0) && (keysym <= (int) KeypadKeys.XK_KP_9)) {
322                                         buffer = String.Empty;
323                                         res = 0;
324                                 }
325
326                                 // CTRL + number, X returns chars, windows does not
327                                 if ((e.KeyEvent.state & (int) KeyMasks.ControlMask) != 0) {
328                                         if (((keysym >= 33) && (keysym < 'A')) || ((keysym > 'Z') && (keysym < 'a'))) {
329                                                 buffer = String.Empty;
330                                                 res = 0;
331                                         }
332                                 }
333
334                                 // X returns a char for delete key on extended keyboards, windows does not
335                                 if (keysym == (int) TtyKeys.XK_Delete) {
336                                         buffer = String.Empty;
337                                         res = 0;
338                                 }
339
340                                 if (res != 0) {
341                                         buffer = lookup_buffer.ToString ();
342                                         res = buffer.Length;
343                                 }
344                         }
345
346                         return res;
347                 }
348
349                 private MSG SendKeyboardInput (VirtualKeys vkey, int scan, KeybdEventFlags dw_flags, int time)
350                 {
351                         Msg message;
352
353                         if ((dw_flags & KeybdEventFlags.KeyUp) != 0) {
354                                 bool sys_key = (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 &&
355                                               ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) == 0);
356                                 key_state_table [(int) vkey] &= unchecked ((byte) ~0x80);
357                                 message = (sys_key ? Msg.WM_SYSKEYUP : Msg.WM_KEYUP);
358                         } else {
359                                 if ((key_state_table [(int) vkey] & 0x80) == 0) {
360                                         key_state_table [(int) vkey] ^= 0x01;
361                                 }
362                                 key_state_table [(int) vkey] |= 0x80;
363                                 bool sys_key = (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 &&
364                                               ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) == 0);
365                                 message = (sys_key ? Msg.WM_SYSKEYDOWN : Msg.WM_KEYDOWN);
366                         }
367
368                         MSG msg = new MSG ();
369                         msg.message = message;
370                         msg.wParam = (IntPtr) vkey;
371                         if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0)
372                                 msg.lParam = new IntPtr (0x20000000);
373                         else
374                                 msg.lParam = IntPtr.Zero;
375
376                         return msg;
377                 }
378
379                 private void GenerateMessage (VirtualKeys vkey, int scan, XEventName type, int event_time)
380                 {
381                         bool state = (vkey == VirtualKeys.VK_NUMLOCK ? num_state : cap_state);
382                         KeybdEventFlags up, down;
383
384                         if (state) {
385                                 // The INTERMEDIARY state means : just after a 'press' event, if a 'release' event comes,
386                                 // don't treat it. It's from the same key press. Then the state goes to ON.
387                                 // And from there, a 'release' event will switch off the toggle key.
388                                 SetState (vkey, false);
389                         } else {
390                                 down = (vkey == VirtualKeys.VK_NUMLOCK ? KeybdEventFlags.ExtendedKey : KeybdEventFlags.None);
391                                 up = (vkey == VirtualKeys.VK_NUMLOCK ? KeybdEventFlags.ExtendedKey :
392                                                 KeybdEventFlags.None) | KeybdEventFlags.KeyUp;
393                                 if ((key_state_table [(int) vkey] & 0x1) != 0) { // it was on
394                                         if (type != XEventName.KeyPress) {
395                                                 SendKeyboardInput (vkey, scan, down, event_time);
396                                                 SendKeyboardInput (vkey, scan, up, event_time);
397                                                 SetState (vkey, false);
398                                                 key_state_table [(int) vkey] &= unchecked ((byte) ~0x01);
399                                         }
400                                 } else {
401                                         if (type == XEventName.KeyPress) {
402                                                 SendKeyboardInput (vkey, scan, down, event_time);
403                                                 SendKeyboardInput (vkey, scan, up, event_time);
404                                                 SetState (vkey, true);
405                                                 key_state_table [(int) vkey] |= 0x01;
406                                         }
407                                 }
408                         }
409                 }
410
411                 private void UpdateKeyState (XEvent xevent)
412                 {
413                         int vkey = EventToVkey (xevent);
414
415                         switch (xevent.type) {
416                         case XEventName.KeyRelease:
417                                 key_state_table [vkey & 0xff] &= unchecked ((byte) ~0x80);
418                                 break;
419                         case XEventName.KeyPress:
420                                 if ((key_state_table [vkey & 0xff] & 0x80) == 0) {
421                                         key_state_table [vkey & 0xff] ^= 0x01;
422                                 }
423                                 key_state_table [vkey & 0xff] |= 0x80;
424                                 break;
425                         }
426                 }
427
428                 private void SetState (VirtualKeys key, bool state)
429                 {
430                         if (VirtualKeys.VK_NUMLOCK == key)
431                                 num_state = state;
432                         else
433                                 cap_state = state;
434                 }
435
436                 public int EventToVkey (XEvent e)
437                 {
438                         EnsureLayoutInitialized ();
439
440                         IntPtr status;
441                         XKeySym ks;
442
443                         LookupString (ref e, 0, out ks, out status);
444                         int keysym = (int) ks;
445
446                         if (((e.KeyEvent.state & NumLockMask) != 0) &&
447                             (keysym == (int)KeypadKeys.XK_KP_Separator || keysym == (int)KeypadKeys.XK_KP_Decimal ||
448                             (keysym >= (int)KeypadKeys.XK_KP_0 && keysym <= (int)KeypadKeys.XK_KP_9))) {
449                                 // Only the Keypad keys 0-9 and . send different keysyms
450                                 // depending on the NumLock state
451                                 return nonchar_key_vkey [keysym & 0xFF];
452                         }
453
454                         return keyc2vkey [e.KeyEvent.keycode];
455                 }
456
457                 private void CreateConversionArray (KeyboardLayouts layouts, KeyboardLayout layout)
458                 {
459                         XEvent e2 = new XEvent ();
460                         uint keysym = 0;
461                         int [] ckey = new int [] { 0, 0, 0, 0 };
462
463                         e2.KeyEvent.display = display;
464                         e2.KeyEvent.state = 0;
465
466                         for (int keyc = min_keycode; keyc <= max_keycode; keyc++) {
467                                 int vkey = 0;
468                                 int scan = 0;
469
470                                 e2.KeyEvent.keycode = keyc;
471                                 XKeySym t;
472
473                                 IntPtr status;
474                                 LookupString (ref e2, 0, out t, out status);
475
476                                 keysym = (uint) t;
477                                 if (keysym != 0) {
478                                         if ((keysym >> 8) == 0xFF) {
479                                                 vkey = nonchar_key_vkey [keysym & 0xFF];
480                                                 scan = nonchar_key_scan [keysym & 0xFF];
481                                                 // Set extended bit
482                                                 if ((scan & 0x100) != 0)
483                                                         vkey |= 0x100;
484                                         } else if (keysym == 0x20) { // spacebar
485                                                 vkey = (int) VirtualKeys.VK_SPACE;
486                                                 scan = 0x39;
487                                         } else {
488                                                 // Search layout dependent scancodes
489                                                 int maxlen = 0;
490                                                 int maxval = -1;;
491                                                 
492                                                 for (int i = 0; i < syms; i++) {
493                                                         keysym = XKeycodeToKeysym (display, keyc, i);
494                                                         if ((keysym < 0x800) && (keysym != ' '))
495                                                                 ckey [i] = (sbyte) (keysym & 0xFF);
496                                                         else
497                                                                 ckey [i] = (sbyte) MapDeadKeySym ((int) keysym);
498                                                 }
499                                                 
500                                                 for (int keyn = 0; keyn < layout.Keys.Length; keyn++) {
501                                                         int ml = Math.Min (layout.Keys [keyn].Length, 4);
502                                                         int ok = -1;
503                                                         for (int i = 0; (ok != 0) && (i < ml); i++) {
504                                                                 sbyte ck = (sbyte) layout.Keys [keyn][i];
505                                                                 if (ck != ckey [i])
506                                                                         ok = 0;
507                                                                 if ((ok != 0) || (i > maxlen)) {
508                                                                         maxlen = i;
509                                                                         maxval = keyn;
510                                                                 }
511                                                                 if (ok != 0)
512                                                                         break;
513                                                         }
514                                                 }
515                                                 if (maxval >= 0) {
516                                                         scan = layouts.scan_table [(int) layout.ScanIndex][maxval];
517                                                         vkey = layouts.vkey_table [(int) layout.VKeyIndex][maxval];
518                                                 }
519                                                 
520                                         }
521                                 }
522                                 keyc2vkey [e2.KeyEvent.keycode] = vkey;
523                                 keyc2scan [e2.KeyEvent.keycode] = scan;
524                         }
525                         
526                         
527                 }
528
529                 private KeyboardLayout DetectLayout (KeyboardLayouts layouts)
530                 {
531                         XDisplayKeycodes (display, out min_keycode, out max_keycode);
532                         IntPtr ksp = XGetKeyboardMapping (display, (byte) min_keycode,
533                                         max_keycode + 1 - min_keycode, out keysyms_per_keycode);
534                         XplatUIX11.XFree (ksp);
535
536                         syms = keysyms_per_keycode;
537                         if (syms > 4) {
538                                 //Console.Error.WriteLine ("{0} keysymbols per a keycode is not supported, setting to 4", syms);
539                                 syms = 2;
540                         }
541
542                         IntPtr  modmap_unmanaged;
543                         XModifierKeymap xmk = new XModifierKeymap ();
544
545                         modmap_unmanaged = XGetModifierMapping (display);
546                         xmk = (XModifierKeymap) Marshal.PtrToStructure (modmap_unmanaged, typeof (XModifierKeymap));
547
548                         int mmp = 0;
549                         for (int i = 0; i < 8; i++) {
550                                 for (int j = 0; j < xmk.max_keypermod; j++, mmp++) {
551                                         byte b = Marshal.ReadByte (xmk.modifiermap, mmp);
552                                         if (b != 0) {
553                                                 for (int k = 0; k < keysyms_per_keycode; k++) {
554                                                         if ((int) XKeycodeToKeysym (display, b, k) == (int) MiscKeys.XK_Num_Lock)
555                                                                 NumLockMask = 1 << i;
556                                                 }
557                                         }
558                                 }
559                         }
560                         XFreeModifiermap (modmap_unmanaged);
561
562                         int [] ckey = new int [4];
563                         KeyboardLayout layout = null;
564                         int max_score = 0;
565                         int max_seq = 0;
566                         
567                         foreach (KeyboardLayout current in layouts.Layouts) {
568                                 int ok = 0;
569                                 int score = 0;
570                                 int match = 0;
571                                 int mismatch = 0;
572                                 int seq = 0;
573                                 int pkey = -1;
574                                 int key = min_keycode;
575                                 int i;
576
577                                 for (int keyc = min_keycode; keyc <= max_keycode; keyc++) {
578                                         for (i = 0; i < syms; i++) {
579                                                 uint keysym = XKeycodeToKeysym (display, keyc, i);
580                                                 
581                                                 if ((keysym < 0x800) && (keysym != ' ')) {
582                                                         ckey [i] = (sbyte) (keysym & 0xFF);
583                                                 } else {
584                                                         ckey [i] = (sbyte) MapDeadKeySym ((int) keysym);
585                                                 }
586                                         }
587                                         if (ckey [0] != 0) {
588                                                 for (key = 0; key < current.Keys.Length; key++) {
589                                                         int ml = Math.Min (syms, current.Keys [key].Length);
590                                                         for (ok = 0, i = 0; (ok >= 0) && (i < ml); i++) {
591                                                                 sbyte ck = (sbyte) current.Keys [key][i];
592                                                                 if (ck != 0 && ck == ckey[i])
593                                                                         ok++;
594                                                                 if (ck != 0 && ck != ckey[i])
595                                                                         ok = -1;
596                                                         }
597                                                         if (ok > 0) {
598                                                                 score += ok;
599                                                                 break;
600                                                         }
601                                                 }
602                                                 if (ok > 0) {
603                                                         match++;
604                                                         /* and how much the keycode order matches */
605                                                         if (key > pkey)
606                                                                 seq++;
607                                                         pkey = key;
608                                                 } else {
609                                                         /* print spaces instead of \0's */
610                                                         mismatch++;
611                                                         score -= syms;
612                                                 }
613                                         }
614                                 }
615
616                                 if ((score > max_score) || ((score == max_score) && (seq > max_seq))) {
617                                         // best match so far
618                                         layout = current;
619                                         max_score = score;
620                                         max_seq = seq;
621                                 }
622                         }
623
624                         if (layout != null)  {
625                                 return layout;
626                         } else {
627                                 Console.WriteLine (Locale.GetText("Keyboard layout not recognized, using default layout: " +
628                                                                    layouts.Layouts [0].Name));
629                         }
630
631                         return layouts.Layouts [0];
632                 }
633
634                 // TODO
635                 private int MapDeadKeySym (int val)
636                 {
637                         switch (val) {
638                         case (int) DeadKeys.XK_dead_tilde :
639                         case 0x1000FE7E : // Xfree's Dtilde
640                                 return '~';
641                         case (int) DeadKeys.XK_dead_acute :
642                         case 0x1000FE27 : // Xfree's XK_Dacute_accent
643                                 return 0xb4;
644                         case (int) DeadKeys.XK_dead_circumflex:
645                         case 0x1000FE5E : // Xfree's XK_.Dcircumflex_accent
646                                 return '^';
647                         case (int) DeadKeys.XK_dead_grave :
648                         case 0x1000FE60 : // Xfree's XK_.Dgrave_accent
649                                 return '`';
650                         case (int) DeadKeys.XK_dead_diaeresis :
651                         case 0x1000FE22 : // Xfree's XK_.Ddiaeresis
652                                 return 0xa8;
653                         case (int) DeadKeys.XK_dead_cedilla :
654                                 return 0xb8;
655                         case (int) DeadKeys.XK_dead_macron :
656                                 return '-';
657                         case (int) DeadKeys.XK_dead_breve :
658                                 return 0xa2;
659                         case (int) DeadKeys.XK_dead_abovedot :
660                                 return 0xff;
661                         case (int) DeadKeys.XK_dead_abovering :
662                                 return '0';
663                         case (int) DeadKeys.XK_dead_doubleacute :
664                                 return 0xbd;
665                         case (int) DeadKeys.XK_dead_caron :
666                                 return 0xb7;
667                         case (int) DeadKeys.XK_dead_ogonek :
668                                 return 0xb2;
669                         }
670
671                         return 0;
672                 }
673
674                 private IntPtr CreateXic (IntPtr window, IntPtr xim)
675                 {
676                         xic = XCreateIC (xim, 
677                                 "inputStyle", XIMProperties.XIMPreeditNothing | XIMProperties.XIMStatusNothing,
678                                 "clientWindow", window,
679                                 "focusWindow", window,
680                                 IntPtr.Zero);
681                         return xic;
682                 }
683
684                 private int LookupString (ref XEvent xevent, int len, out XKeySym keysym, out IntPtr status)
685                 {
686                         IntPtr keysym_res;
687                         int res;
688
689                         status = IntPtr.Zero;
690                         lookup_buffer.Length = 0;
691                         if (xic != IntPtr.Zero)
692                                 res = XmbLookupString (xic, ref xevent, lookup_buffer, len, out keysym_res,  out status);
693                         else
694                                 res = XLookupString (ref xevent, lookup_buffer, len, out keysym_res, IntPtr.Zero);
695
696                         keysym = (XKeySym) keysym_res.ToInt32 ();
697                         return res;
698                 }
699
700                 [DllImport ("libX11")]
701                 private static extern IntPtr XOpenIM (IntPtr display, IntPtr rdb, IntPtr res_name, IntPtr res_class);
702
703                 [DllImport ("libX11")]
704                 private static extern IntPtr XCreateIC (IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, string name3, IntPtr value3, IntPtr terminator);
705
706                 [DllImport ("libX11")]
707                 private static extern void XSetICFocus (IntPtr xic);
708
709                 [DllImport ("libX11")]
710                 private static extern void XUnsetICFocus (IntPtr xic);
711
712                 [DllImport ("libX11")]
713                 private static extern bool XSupportsLocale ();
714
715                 [DllImport ("libX11")]
716                 private static extern bool XSetLocaleModifiers (string mods);
717
718                 [DllImport ("libX11")]
719                 internal extern static int XLookupString(ref XEvent xevent, StringBuilder buffer, int num_bytes, out IntPtr keysym, IntPtr status);
720                 [DllImport ("libX11")]
721                 internal extern static int XmbLookupString(IntPtr xic, ref XEvent xevent, StringBuilder buffer, int num_bytes, out IntPtr keysym, out IntPtr status);
722
723                 internal static int XmbLookupString (IntPtr xic, ref XEvent xevent, StringBuilder buffer, int num_bytes, out XKeySym keysym, out IntPtr status) {
724                         IntPtr  keysym_ret;
725                         int     ret;
726
727                         ret = XmbLookupString (xic, ref xevent, buffer, num_bytes, out keysym_ret, out status);
728
729                         keysym = (XKeySym)keysym_ret.ToInt32();
730
731                         return ret;
732                 }
733
734                 internal static int XLookupString (ref XEvent xevent, StringBuilder buffer, int num_bytes, out XKeySym keysym, IntPtr status) {
735                         IntPtr  keysym_ret;
736                         int     ret;
737
738                         ret = XLookupString (ref xevent, buffer, num_bytes, out keysym_ret, status);
739                         keysym = (XKeySym)keysym_ret.ToInt32();
740
741                         return ret;
742                 }
743
744                 [DllImport ("libX11")]
745                 private static extern IntPtr XGetKeyboardMapping (IntPtr display, byte first_keycode, int keycode_count, 
746                                 out int keysyms_per_keycode_return);
747
748                 [DllImport ("libX11")]
749                 private static extern void XDisplayKeycodes (IntPtr display, out int min, out int max);
750
751                 [DllImport ("libX11", EntryPoint="XKeycodeToKeysym")]
752                 private static extern uint XKeycodeToKeysym (IntPtr display, int keycode, int index);
753
754                 [DllImport ("libX11")]
755                 private static extern int XKeysymToKeycode (IntPtr display, IntPtr keysym);
756                 private static int XKeysymToKeycode (IntPtr display, int keysym) {
757                         return XKeysymToKeycode(display, (IntPtr)keysym);
758                 }
759
760                 [DllImport ("libX11")]
761                 internal extern static IntPtr XGetModifierMapping (IntPtr display);
762
763                 [DllImport ("libX11")]
764                 internal extern static int XFreeModifiermap (IntPtr modmap);
765
766
767                 private readonly static int [] nonchar_key_vkey = new int []
768                 {
769                         /* unused */
770                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF00 */
771                         /* special keys */
772                         (int) VirtualKeys.VK_BACK, (int) VirtualKeys.VK_TAB, 0, (int) VirtualKeys.VK_CLEAR, 0, (int) VirtualKeys.VK_RETURN, 0, 0,           /* FF08 */
773                         0, 0, 0, (int) VirtualKeys.VK_PAUSE, (int) VirtualKeys.VK_SCROLL, 0, 0, 0,                           /* FF10 */
774                         0, 0, 0, (int) VirtualKeys.VK_ESCAPE, 0, 0, 0, 0,                             /* FF18 */
775                         /* unused */
776                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF20 */
777                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF28 */
778                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF30 */
779                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF38 */
780                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF40 */
781                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF48 */
782                         /* cursor keys */
783                         (int) VirtualKeys.VK_HOME, (int) VirtualKeys.VK_LEFT, (int) VirtualKeys.VK_UP, (int) VirtualKeys.VK_RIGHT,                          /* FF50 */
784                         (int) VirtualKeys.VK_DOWN, (int) VirtualKeys.VK_PRIOR, (int) VirtualKeys.VK_NEXT, (int) VirtualKeys.VK_END,
785                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF58 */
786                         /* misc keys */
787                         (int) VirtualKeys.VK_SELECT, (int) VirtualKeys.VK_SNAPSHOT, (int) VirtualKeys.VK_EXECUTE, (int) VirtualKeys.VK_INSERT, 0, 0, 0, 0,  /* FF60 */
788                         (int) VirtualKeys.VK_CANCEL, (int) VirtualKeys.VK_HELP, (int) VirtualKeys.VK_CANCEL, (int) VirtualKeys.VK_CANCEL, 0, 0, 0, 0,       /* FF68 */
789                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF70 */
790                         /* keypad keys */
791                         0, 0, 0, 0, 0, 0, 0, (int) VirtualKeys.VK_NUMLOCK,                            /* FF78 */
792                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FF80 */
793                         0, 0, 0, 0, 0, (int) VirtualKeys.VK_RETURN, 0, 0,                             /* FF88 */
794                         0, 0, 0, 0, 0, (int) VirtualKeys.VK_HOME, (int) VirtualKeys.VK_LEFT, (int) VirtualKeys.VK_UP,                     /* FF90 */
795                         (int) VirtualKeys.VK_RIGHT, (int) VirtualKeys.VK_DOWN, (int) VirtualKeys.VK_PRIOR, (int) VirtualKeys.VK_NEXT,                       /* FF98 */
796                         (int) VirtualKeys.VK_END, 0, (int) VirtualKeys.VK_INSERT, (int) VirtualKeys.VK_DELETE,
797                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FFA0 */
798                         0, 0, (int) VirtualKeys.VK_MULTIPLY, (int) VirtualKeys.VK_ADD,                                  /* FFA8 */
799                         (int) VirtualKeys.VK_SEPARATOR, (int) VirtualKeys.VK_SUBTRACT, (int) VirtualKeys.VK_DECIMAL, (int) VirtualKeys.VK_DIVIDE,
800                         (int) VirtualKeys.VK_NUMPAD0, (int) VirtualKeys.VK_NUMPAD1, (int) VirtualKeys.VK_NUMPAD2, (int) VirtualKeys.VK_NUMPAD3,             /* FFB0 */
801                         (int) VirtualKeys.VK_NUMPAD4, (int) VirtualKeys.VK_NUMPAD5, (int) VirtualKeys.VK_NUMPAD6, (int) VirtualKeys.VK_NUMPAD7,
802                         (int) VirtualKeys.VK_NUMPAD8, (int) VirtualKeys.VK_NUMPAD9, 0, 0, 0, 0,                         /* FFB8 */
803                         /* function keys */
804                         (int) VirtualKeys.VK_F1, (int) VirtualKeys.VK_F2,
805                         (int) VirtualKeys.VK_F3, (int) VirtualKeys.VK_F4, (int) VirtualKeys.VK_F5, (int) VirtualKeys.VK_F6, (int) VirtualKeys.VK_F7, (int) VirtualKeys.VK_F8, (int) VirtualKeys.VK_F9, (int) VirtualKeys.VK_F10,    /* FFC0 */
806                         (int) VirtualKeys.VK_F11, (int) VirtualKeys.VK_F12, (int) VirtualKeys.VK_F13, (int) VirtualKeys.VK_F14, (int) VirtualKeys.VK_F15, (int) VirtualKeys.VK_F16, 0, 0,       /* FFC8 */
807                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FFD0 */
808                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FFD8 */
809                         /* modifier keys */
810                         0, (int) VirtualKeys.VK_SHIFT, (int) VirtualKeys.VK_SHIFT, (int) VirtualKeys.VK_CONTROL,                          /* FFE0 */
811                         (int) VirtualKeys.VK_CONTROL, (int) VirtualKeys.VK_CAPITAL, 0, (int) VirtualKeys.VK_MENU,
812                         (int) VirtualKeys.VK_MENU, (int) VirtualKeys.VK_MENU, (int) VirtualKeys.VK_MENU, 0, 0, 0, 0, 0,                   /* FFE8 */
813                         0, 0, 0, 0, 0, 0, 0, 0,                                     /* FFF0 */
814                         0, 0, 0, 0, 0, 0, 0, (int) VirtualKeys.VK_DELETE                              /* FFF8 */
815                 };
816
817                 private static readonly int [] nonchar_key_scan = new int []
818                 {
819                         /* unused */
820                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF00 */
821                         /* special keys */
822                         0x0E, 0x0F, 0x00, /*?*/ 0, 0x00, 0x1C, 0x00, 0x00,           /* FF08 */
823                         0x00, 0x00, 0x00, 0x45, 0x46, 0x00, 0x00, 0x00,              /* FF10 */
824                         0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,              /* FF18 */
825                         /* unused */
826                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF20 */
827                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF28 */
828                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF30 */
829                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF38 */
830                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF40 */
831                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF48 */
832                         /* cursor keys */
833                         0x147, 0x14B, 0x148, 0x14D, 0x150, 0x149, 0x151, 0x14F,      /* FF50 */
834                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF58 */
835                         /* misc keys */
836                         /*?*/ 0, 0x137, /*?*/ 0, 0x152, 0x00, 0x00, 0x00, 0x00,      /* FF60 */
837                         /*?*/ 0, /*?*/ 0, 0x38, 0x146, 0x00, 0x00, 0x00, 0x00,       /* FF68 */
838                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF70 */
839                         /* keypad keys */
840                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x138, 0x145,            /* FF78 */
841                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF80 */
842                         0x00, 0x00, 0x00, 0x00, 0x00, 0x11C, 0x00, 0x00,             /* FF88 */
843                         0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x4B, 0x48,              /* FF90 */
844                         0x4D, 0x50, 0x49, 0x51, 0x4F, 0x4C, 0x52, 0x53,              /* FF98 */
845                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FFA0 */
846                         0x00, 0x00, 0x37, 0x4E, /*?*/ 0, 0x4A, 0x53, 0x135,          /* FFA8 */
847                         0x52, 0x4F, 0x50, 0x51, 0x4B, 0x4C, 0x4D, 0x47,              /* FFB0 */
848                         0x48, 0x49, 0x00, 0x00, 0x00, 0x00,                          /* FFB8 */
849                         /* function keys */
850                         0x3B, 0x3C,
851                         0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44,              /* FFC0 */
852                         0x57, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FFC8 */
853                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FFD0 */
854                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FFD8 */
855                         /* modifier keys */
856                         0x00, 0x2A, 0x36, 0x1D, 0x11D, 0x3A, 0x00, 0x38,             /* FFE0 */
857                         0x138, 0x38, 0x138, 0x00, 0x00, 0x00, 0x00, 0x00,            /* FFE8 */
858                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FFF0 */
859                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x153              /* FFF8 */
860                 };
861
862                 private readonly static int [] nonchar_vkey_key = new int []
863                 {
864                         0, 0, 0, 0, 0,          /* 00-04 */ 
865                         0, 0, 0, (int)XKeySym.XK_BackSpace, (int)XKeySym.XK_Tab,                /* 05-09 */
866                         0, 0, (int)XKeySym.XK_Clear, (int)XKeySym.XK_Return,    0, 0,   /* 0A-0F */
867                         (int)XKeySym.XK_Shift_L, (int)XKeySym.XK_Control_L, (int)XKeySym.XK_Menu, 0, (int)XKeySym.XK_Caps_Lock,         /* 10-14 */ 
868                         0, 0, 0, 0, 0,          /* 15-19 */
869                         0, 0, 0, 0,     0, 0,   /* 1A-1F */
870                         0, 0, 0, (int)XKeySym.XK_End, (int)XKeySym.XK_Home,             /* 20-24 */ 
871                         (int)XKeySym.XK_Left, (int)XKeySym.XK_Up, (int)XKeySym.XK_Right, (int)XKeySym.XK_Down, 0,               /* 25-29 */
872                         0, 0, 0, 0,     0, 0,   /* 2A-2F */
873                         0, 0, 0, 0, 0,          /* 30-34 */ 
874                         0, 0, 0, 0, 0,          /* 35-39 */
875                         0, 0, 0, 0,     0, 0,   /* 3A-3F */
876                         0, 0, 0, 0, 0,          /* 40-44 */ 
877                         0, 0, 0, 0, 0,          /* 45-49 */
878                         0, 0, 0, 0,     0, 0,   /* 4A-4F */
879                         0, 0, 0, 0, 0,          /* 50-54 */ 
880                         0, 0, 0, 0, 0,          /* 55-59 */
881                         0, (int)XKeySym.XK_Meta_L, (int)XKeySym.XK_Meta_R, 0,   0, 0,   /* 5A-5F */
882                         0, 0, 0, 0, 0,          /* 60-64 */ 
883                         0, 0, 0, 0, 0,          /* 65-69 */
884                         0, 0, 0, 0,     0, 0,   /* 6A-6F */
885                         0, 0, 0, 0, 0,          /* 70-74 */ 
886                         0, 0, 0, 0, 0,          /* 75-79 */
887                         0, 0, 0, 0,     0, 0,   /* 7A-7F */
888                         0, 0, 0, 0, 0,          /* 80-84 */ 
889                         0, 0, 0, 0, 0,          /* 85-89 */
890                         0, 0, 0, 0,     0, 0,   /* 8A-8F */
891                         0, 0, 0, 0, 0,          /* 90-94 */ 
892                         0, 0, 0, 0, 0,          /* 95-99 */
893                         0, 0, 0, 0,     0, 0,   /* 9A-9F */
894                         (int)XKeySym.XK_Shift_L, (int)XKeySym.XK_Shift_R, (int)XKeySym.XK_Control_L, (int)XKeySym.XK_Control_R, (int)XKeySym.XK_Alt_L,          /* A0-A4 */ 
895                         (int)XKeySym.XK_Alt_R, 0, 0, 0, 0,              /* A5-A9 */
896                         0, 0, 0, 0,     0, 0,   /* AA-AF */
897                         0, 0, 0, 0, 0,          /* B0-B4 */ 
898                         0, 0, 0, 0, 0,          /* B5-B9 */
899                         0, 0, 0, 0,     0, 0,   /* BA-BF */
900                         0, 0, 0, 0, 0,          /* C0-C4 */ 
901                         0, 0, 0, 0, 0,          /* C5-C9 */
902                         0, 0, 0, 0,     0, 0,   /* CA-CF */
903                         0, 0, 0, 0, 0,          /* D0-D4 */ 
904                         0, 0, 0, 0, 0,          /* D5-D9 */
905                         0, 0, 0, 0,     0, 0,   /* DA-DF */
906                         0, 0, 0, 0, 0,          /* E0-E4 */ 
907                         0, 0, 0, 0, 0,          /* E5-E9 */
908                         0, 0, 0, 0,     0, 0,   /* EA-EF */
909                         0, 0, 0, 0, 0,          /* F0-F4 */ 
910                         0, 0, 0, 0, 0,          /* F5-F9 */
911                         0, 0, 0, 0,     0, 0    /* FA-FF */
912                 };
913                 
914         }
915
916 }
917