* X11Keyboard.cs: Detect and use the num lock mask.
[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.Text;
35 using System.Runtime.InteropServices;
36
37 namespace System.Windows.Forms {
38
39         internal class X11Keyboard {
40
41                 private IntPtr display;
42                 private int min_keycode, max_keycode, keysyms_per_keycode, syms;
43                 private int [] keyc2vkey = new int [256];
44                 private int [] keyc2scan = new int [256];
45                 private byte [] key_state_table = new byte [256];
46                 private bool num_state, cap_state;
47                 private KeyboardLayout layout;
48
49                 // TODO
50                 private int NumLockMask;
51                 private int AltGrMask;
52                 
53                 public X11Keyboard (IntPtr display)
54                 {
55                         this.display = display;
56                         DetectLayout ();
57                         CreateConversionArray (layout);
58                 }
59
60                 public Keys ModifierKeys {
61                         get {
62                                 Keys keys = Keys.None;
63                                 if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0)
64                                         keys |= Keys.Shift;
65                                 if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0)
66                                         keys |= Keys.Control;
67                                 if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0)
68                                         keys |= Keys.Alt;
69                                 return keys;
70                         }
71                 }
72
73                 public void KeyEvent (IntPtr hwnd, XEvent xevent, ref MSG msg)
74                 {
75                         if ((xevent.KeyEvent.keycode >> 8) == 0x10)
76                                 xevent.KeyEvent.keycode = xevent.KeyEvent.keycode & 0xFF;
77
78                         int event_time = xevent.KeyEvent.time;
79                         int vkey = EventToVkey (xevent);
80
81                         if (vkey == 0)
82                                 return;
83
84                         switch ((VirtualKeys) (vkey & 0xFF)) {
85                         case VirtualKeys.VK_NUMLOCK:
86                                 GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, xevent.type, event_time);
87                                 break;
88                         case VirtualKeys.VK_CAPITAL:
89                                 GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, xevent.type, event_time);
90                                 break;
91                         default:
92
93                                 if (((key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x01) == 0) != ((xevent.KeyEvent.state & NumLockMask) == 0)) {
94                                         GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, XEventName.KeyPress, event_time);
95                                         GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, XEventName.KeyRelease, event_time);
96                                 }
97
98                                 if (((key_state_table [(int) VirtualKeys.VK_CAPITAL] & 0x01) == 0) != ((xevent.KeyEvent.state & (int) KeyMasks.LockMask) == 0)) {
99                                         GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, XEventName.KeyPress, event_time);
100                                         GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, XEventName.KeyRelease, event_time);
101                                 }
102
103                                 num_state = false;
104                                 cap_state = false;
105
106                                 int bscan = (keyc2scan [xevent.KeyEvent.keycode] & 0xFF);
107                                 KeybdEventFlags dw_flags = KeybdEventFlags.None;
108                                 if (xevent.type == XEventName.KeyRelease)
109                                         dw_flags |= KeybdEventFlags.KeyUp;
110                                 if ((vkey & 0x100) != 0)
111                                         dw_flags |= KeybdEventFlags.ExtendedKey;
112                                 msg = SendKeyboardInput ((VirtualKeys) (vkey & 0xFF), bscan, dw_flags, event_time);
113                                 msg.hwnd = hwnd;
114                                 break;
115                         }
116                 }
117
118                 public bool TranslateMessage (ref MSG msg)
119                 {
120                         bool res = false;
121
122                         if (msg.message >= Msg.WM_KEYFIRST && msg.message <= Msg.WM_KEYLAST)
123                                 res = true;
124
125                         if (msg.message != Msg.WM_KEYDOWN && msg.message != Msg.WM_SYSKEYDOWN)
126                                 return res;
127
128                         string buffer;
129                         Msg message;
130
131                         int tu = ToUnicode ((int) msg.wParam, Control.HighOrder ((int) msg.lParam), out buffer);
132                         switch (tu) {
133                         case 1:
134                                 message = (msg.message == Msg.WM_KEYDOWN ? Msg.WM_CHAR : Msg.WM_SYSCHAR);
135                                 XplatUIX11.PostMessage (msg.hwnd, message, (IntPtr) buffer [0], msg.lParam);
136                                 break;
137                         case -1:
138                                 message = (msg.message == Msg.WM_KEYDOWN ? Msg.WM_DEADCHAR : Msg.WM_SYSDEADCHAR);
139                                 XplatUIX11.PostMessage (msg.hwnd, message, (IntPtr) buffer [0], msg.lParam);
140                                 return true;
141                         }
142                         
143                         return res;
144                 }
145
146                 private int ToUnicode (int vkey, int scan, out string buffer)
147                 {
148                         if ((scan & 0x8000) != 0) {
149                                 buffer = String.Empty;
150                                 return 0;
151                         }
152
153                         XEvent e = new XEvent ();
154                         e.KeyEvent.display = display;
155                         e.KeyEvent.keycode = 0;
156                         e.KeyEvent.state = 0;
157
158                         if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0) {
159                                 e.KeyEvent.state |= (int) KeyMasks.ShiftMask;
160                         }
161
162                         if ((key_state_table [(int) VirtualKeys.VK_CAPITAL] & 0x01) != 0) {
163                                 e.KeyEvent.state |= (int) KeyMasks.LockMask;
164                         }
165
166                         if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) {
167                                 e.KeyEvent.state |= (int) KeyMasks.ControlMask;
168                         }
169
170                         if ((key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x01) != 0) {
171                                 e.KeyEvent.state |= NumLockMask;
172                         }
173
174                         e.KeyEvent.state |= AltGrMask;
175
176                         for (int keyc = min_keycode; (keyc <= max_keycode) && (e.KeyEvent.keycode == 0); keyc++) {
177                                 // find keycode that could have generated this vkey
178                                 if ((keyc2vkey [keyc] & 0xFF) == vkey) {
179                                         // filter extended bit because it is not known
180                                         e.KeyEvent.keycode = keyc;
181                                         if ((EventToVkey (e) & 0xFF) != vkey) {
182                                                 // Wrong one (ex: because of num,lock state)
183                                                 e.KeyEvent.keycode = 0;
184                                         }
185                                 }
186                         }
187
188                         if ((vkey >= (int) VirtualKeys.VK_NUMPAD0) && (vkey <= (int) VirtualKeys.VK_NUMPAD9))
189                                 e.KeyEvent.keycode = XKeysymToKeycode (display, vkey - (int) VirtualKeys.VK_NUMPAD0 + (int) KeypadKeys.XK_KP_0);
190
191                         if (vkey == (int) VirtualKeys.VK_DECIMAL)
192                                 e.KeyEvent.keycode = XKeysymToKeycode (display, (int) KeypadKeys.XK_KP_Decimal);
193
194                         if (e.KeyEvent.keycode == 0) {
195                                 // And I couldn't find the keycode so i returned the vkey and was like whatever
196                                 Console.Error.WriteLine ("unknown virtual key {0:X}", vkey);
197                                 buffer = String.Empty;
198                                 return vkey; 
199                         }
200
201                         IntPtr  buf = Marshal.AllocHGlobal (2);
202                         XKeySym t;
203                         int res = XLookupString (ref e, buf, 2, out t, IntPtr.Zero);
204                         int keysym = (int) t;
205
206                         buffer = String.Empty;
207                         if (res == 0) {
208                                 int dead_char = MapDeadKeySym (keysym);
209                                 // TODO: deal with dead chars
210                         } else {
211                                 // Shift + arrow, shift + home, ....
212                                 // X returns a char for it, but windows doesn't
213                                 if (((e.KeyEvent.state & NumLockMask) == 0) && ((e.KeyEvent.state & (int) KeyMasks.ShiftMask) != 0) &&
214                                                 (keysym >= (int) KeypadKeys.XK_KP_0) && (keysym <= (int) KeypadKeys.XK_KP_9)) {
215                                         buffer = String.Empty;
216                                         res = 0;
217                                 }
218
219                                 // CTRL + number, X returns chars, windows does not
220                                 if ((e.KeyEvent.state & (int) KeyMasks.ControlMask) != 0) {
221                                         if (((keysym >= 33) && (keysym < 'A')) || ((keysym > 'Z') && (keysym < 'a'))) {
222                                                 buffer = String.Empty;
223                                                 res = 0;
224                                         }
225                                 }
226
227                                 // X returns a char for delete key on extended keyboards, windows does not
228                                 if (keysym == (int) TtyKeys.XK_Delete) {
229                                         buffer = String.Empty;
230                                         res = 0;
231                                 }
232
233                                 if (res != 0) {
234                                         byte [] bytes = new byte [2];
235                                         bytes [0] = Marshal.ReadByte (buf);
236                                         bytes [1] = Marshal.ReadByte (buf, 1);
237                                         Encoding encoding = Encoding.GetEncoding (layout.CodePage);
238                                         buffer = new string (encoding.GetChars (bytes));
239                                 }
240                         }
241
242                         return res;
243                 }
244
245                 private MSG SendKeyboardInput (VirtualKeys vkey, int scan, KeybdEventFlags dw_flags, int time)
246                 {
247                         Msg message;
248
249                         if ((dw_flags & KeybdEventFlags.KeyUp) != 0) {
250                                 bool sys_key = (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 &&
251                                               ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) == 0);
252                                 key_state_table [(int) vkey] &= unchecked ((byte) ~0x80);
253                                 message = (sys_key ? Msg.WM_SYSKEYUP : Msg.WM_KEYUP);
254                         } else {
255                                 if ((key_state_table [(int) vkey] & 0x80) == 0) {
256                                         key_state_table [(int) vkey] ^= 0x01;
257                                 }
258                                 key_state_table [(int) vkey] |= 0x80;
259                                 bool sys_key = (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 &&
260                                               ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) == 0);
261                                 message = (sys_key ? Msg.WM_SYSKEYDOWN : Msg.WM_KEYDOWN);
262                         }
263
264                         MSG msg = new MSG ();
265                         msg.message = message;
266                         msg.wParam = (IntPtr) vkey;
267                         msg.lParam = IntPtr.Zero;
268
269                         return msg;
270                 }
271
272                 private void GenerateMessage (VirtualKeys vkey, int scan, XEventName type, int event_time)
273                 {
274                         bool state = (vkey == VirtualKeys.VK_NUMLOCK ? num_state : cap_state);
275                         KeybdEventFlags up, down;
276
277                         if (state) {
278                                 // The INTERMEDIARY state means : just after a 'press' event, if a 'release' event comes,
279                                 // don't treat it. It's from the same key press. Then the state goes to ON.
280                                 // And from there, a 'release' event will switch off the toggle key.
281                                 SetState (vkey, false);
282                         } else {
283                                 down = (vkey == VirtualKeys.VK_NUMLOCK ? KeybdEventFlags.ExtendedKey : KeybdEventFlags.None);
284                                 up = (vkey == VirtualKeys.VK_NUMLOCK ? KeybdEventFlags.ExtendedKey :
285                                                 KeybdEventFlags.None) | KeybdEventFlags.KeyUp;
286                                 if ((key_state_table [(int) vkey] & 0x1) != 0) { // it was on
287                                         if (type != XEventName.KeyPress) {
288                                                 // SendKeyboardInput (vkey, scan, down, event_time);
289                                                 // SendKeyboardInput (vkey, scan, up, event_time);
290                                                 SetState (vkey, false);
291                                                 key_state_table [(int) vkey] &= unchecked ((byte) ~0x01);
292                                         }
293                                 } else {
294                                         if (type == XEventName.KeyPress) {
295                                                 // SendKeyboardInput (vkey, scan, down, event_time);
296                                                 // SendKeyboardInput (vkey, scan, up, event_time);
297                                                 SetState (vkey, true);
298                                                 key_state_table [(int) vkey] |= 0x01;
299                                         }
300                                 }
301                         }
302                 }
303
304                 private void SetState (VirtualKeys key, bool state)
305                 {
306                         if (VirtualKeys.VK_NUMLOCK == key)
307                                 num_state = state;
308                         else
309                                 cap_state = state;
310                 }
311
312                 public int EventToVkey (XEvent e)
313                 {
314                         XKeySym ks;
315
316                         XLookupString (ref e, IntPtr.Zero, 0, out ks, IntPtr.Zero);
317                         int keysym = (int) ks;
318
319                         if ((keysym >= 0xFFAE) && (keysym <= 0xFFB9) && (keysym != 0xFFAF)
320                                         && ((e.KeyEvent.state & NumLockMask) !=0)) {
321                                 // Only the Keypad keys 0-9 and . send different keysyms
322                                 // depending on the NumLock state
323                                 return KeyboardLayouts.nonchar_key_vkey [keysym & 0xFF];
324                         }
325
326                         return keyc2vkey [e.KeyEvent.keycode];
327                 }
328
329                 public void CreateConversionArray (KeyboardLayout layout)
330                 {
331
332                         XEvent e2 = new XEvent ();
333                         int keysym = 0;
334                         int [] ckey = new int [] { 0, 0, 0, 0 };
335
336                         VirtualKeys oem_key = VirtualKeys.VK_OEM_7;
337                         
338                         e2.KeyEvent.display = display;
339                         e2.KeyEvent.state = 0;
340
341                         int oem_vkey = (int) VirtualKeys.VK_OEM_7;
342                         for (int keyc = min_keycode; keyc <= max_keycode; keyc++) {
343                                 int vkey = 0;
344                                 int scan = 0;
345
346                                 e2.KeyEvent.keycode = keyc;
347                                 XKeySym t;
348                                 XLookupString (ref e2, IntPtr.Zero, 0, out t, IntPtr.Zero);
349                                 keysym = (int) t;
350                                 if (keysym != 0) {
351                                         if ((keysym >> 8) == 0xFF) {
352                                                 vkey = KeyboardLayouts.nonchar_key_vkey [keysym & 0xFF];
353                                                 scan = KeyboardLayouts.nonchar_key_scan [keysym & 0xFF];
354                                                 // Set extended bit
355                                                 if ((scan & 0x100) != 0)
356                                                         vkey |= 0x100;
357                                         } else if (keysym == 0x20) { // spacebar
358                                                 vkey = (int) VirtualKeys.VK_SPACE;
359                                                 scan = 0x39;
360                                         } else {
361                                                 // Search layout dependent scancodes
362                                                 int maxlen = 0;
363                                                 int maxval = -1;;
364                                                 int ok;
365                                                 
366                                                 for (int i = 0; i < syms; i++) {
367                                                         keysym = (int) XKeycodeToKeysym (display, keyc, i);
368                                                         if ((keysym < 0x800) && (keysym != ' '))
369                                                                 ckey [i] = keysym & 0xFF;
370                                                         else
371                                                                 ckey [i] = MapDeadKeySym (keysym);
372                                                 }
373                                                 
374                                                 for (int keyn = 0; keyn < layout.Key.Length; keyn++) {
375                                                         int i = 0;
376                                                         int ml = (layout.Key [keyn].Length > 4 ? 4 : layout.Key [keyn].Length);
377                                                         for (ok = layout.Key [keyn][i]; (ok != 0) && (i < ml); i++) {
378                                                                 if (layout.Key [keyn][i] != ckey [i])
379                                                                         ok = 0;
380                                                                 if ((ok != 0) || (i > maxlen)) {
381                                                                         maxlen = i;
382                                                                         maxval = keyn;
383                                                                 }
384                                                                 if (ok != 0)
385                                                                         break;
386                                                         }
387                                                 }
388                                                 if (maxval >= 0) {
389                                                         scan = layout.Scan [maxval];
390                                                         vkey = (int) layout.VKey [maxval];
391                                                 }
392                                                 
393                                         }
394
395                                         for (int i = 0; (i < keysyms_per_keycode) && (vkey == 0); i++) {
396                                                 keysym = (int) XLookupKeysym (ref e2, i);
397                                                 if ((keysym >= (int) VirtualKeys.VK_0 && keysym <= (int) VirtualKeys.VK_9) ||
398                                                                 (keysym >= (int) VirtualKeys.VK_A && keysym <= (int) VirtualKeys.VK_Z)) {
399                                                         vkey = keysym;
400                                                 }
401                                         }
402
403                                         for (int i = 0; (i < keysyms_per_keycode) && (vkey != 0); i++) {
404                                                 keysym = (int) XLookupKeysym (ref e2, i);
405                                                 switch ((char) keysym) {
406                                                 case ';':
407                                                         vkey = (int) VirtualKeys.VK_OEM_1;
408                                                         break;
409                                                 case '/':
410                                                         vkey = (int) VirtualKeys.VK_OEM_2;
411                                                         break;
412                                                 case '`':
413                                                         vkey = (int) VirtualKeys.VK_OEM_3;
414                                                         break;
415                                                 case '[':
416                                                         vkey = (int) VirtualKeys.VK_OEM_4;
417                                                         break;
418                                                 case '\\':
419                                                         vkey = (int) VirtualKeys.VK_OEM_5;
420                                                         break;
421                                                 case ']':
422                                                         vkey = (int) VirtualKeys.VK_OEM_6;
423                                                         break;
424                                                 case '\'':
425                                                         vkey = (int) VirtualKeys.VK_OEM_7;
426                                                         break;
427                                                 case ',':
428                                                         vkey = (int) VirtualKeys.VK_OEM_COMMA;
429                                                         break;
430                                                 case '.':
431                                                         vkey = (int) VirtualKeys.VK_OEM_PERIOD;
432                                                         break;
433                                                 case '-':
434                                                         vkey = (int) VirtualKeys.VK_OEM_MINUS;
435                                                         break;
436                                                 case '+':
437                                                         vkey = (int) VirtualKeys.VK_OEM_PLUS;
438                                                         break;
439
440                                                 }
441                                         }
442
443                                         if (vkey == 0) {
444                                                 switch (++oem_vkey) {
445                                                 case 0xc1:
446                                                         oem_vkey = 0xDB;
447                                                         break;
448                                                 case 0xE5:
449                                                         oem_vkey = 0xE9;
450                                                         break;
451                                                 case 0xF6:
452                                                         oem_vkey = 0xF5;
453                                                         break;
454                                                 }
455                                                 vkey = oem_vkey;
456                                         }
457                                 }
458                                 keyc2vkey [e2.KeyEvent.keycode] = vkey;
459                                 keyc2scan [e2.KeyEvent.keycode] = scan;
460                         }
461                         
462                         
463                 }
464
465                 public void DetectLayout ()
466                 {
467                         XDisplayKeycodes (display, out min_keycode, out max_keycode);
468                         IntPtr ksp = XGetKeyboardMapping (display, (byte) min_keycode,
469                                         max_keycode + 1 - min_keycode, out keysyms_per_keycode);
470                         XplatUIX11.XFree (ksp);
471
472                         syms = keysyms_per_keycode;
473                         if (syms > 4) {
474                                 Console.Error.WriteLine ("{0} keysymbols per a keycode is not supported, setting to 4", syms);
475                                 syms = 2;
476                         }
477
478                         IntPtr  modmap_unmanaged;
479                         XModifierKeymap xmk = new XModifierKeymap ();
480
481                         modmap_unmanaged = XGetModifierMapping (display);
482                         xmk = (XModifierKeymap) Marshal.PtrToStructure (modmap_unmanaged, typeof (XModifierKeymap));
483
484                         int mmp = 0;
485                         for (int i = 0; i < 8; i++) {
486                                 for (int j = 0; j < xmk.max_keypermod; j++, mmp++) {
487                                         byte b = Marshal.ReadByte (xmk.modifiermap, mmp);
488                                         if (b != 0) {
489                                                 for (int k = 0; k < keysyms_per_keycode; k++) {
490                                                         if ((int) XKeycodeToKeysym (display, b, k) == (int) MiscKeys.XK_Num_Lock)
491                                                                 NumLockMask = 1 << i;
492                                                 }
493                                         }
494                                 }
495                         }
496                         XFreeModifiermap (modmap_unmanaged);
497
498                         int [] ckey = new int [4];
499                         bool vk_set = false;
500                         KeyboardLayout layout = null;
501                         int max_score = 0;
502                         int max_seq = 0;
503                         bool ismatch = false;
504                         
505                         foreach (KeyboardLayout current in KeyboardLayouts.Layouts) {
506                                 int ind = 0;
507                                 int ok = 0;
508                                 int score = 0;
509                                 int match = 0;
510                                 int seq = 0;
511                                 int pkey = -1;
512                                 bool mismatch = false;
513                                 int key = min_keycode;
514
515                                 for (int keyc = min_keycode; keyc <= max_keycode; keyc++) {
516                                         for (int i = 0; i < syms; i++) {
517                                                 int keysym = (int) XKeycodeToKeysym (display, keyc, i);
518                                                 
519                                                 if ((keysym != 0xFF1B) && (keysym < 0x800) && (keysym != ' ')) {
520                                                         ckey [i] = keysym & 0xFF;
521                                                 } else {
522                                                         ckey [i] = MapDeadKeySym (keysym);
523                                                 }
524                                         }
525                                         if (ckey [0] != 0) {
526
527                                                 for (key = 0; key < current.Key.Length; key++) {
528                                                         ok = 0;
529                                                         int ml = (current.Key [key].Length > syms ? syms : current.Key [key].Length);
530                                                         for (int i = 0; (ok >= 0) && (i < ml); i++) {
531                                                                 if (ckey [i] != 0 && current.Key [key][i] == (char) ckey [i]) {
532                                                                         ok++;
533                                                                 }
534                                                                 if (ckey [i] != 0 && current.Key [key][i] != (char) ckey [i])
535                                                                         ok = -1;
536                                                         }
537                                                         if (ok >= 0) {
538                                                                 score += ok;
539                                                                 break;
540                                                         }
541                                                 }
542                                                 if (ok > 0) {
543                                                         match++;
544                                                         if (key > pkey)
545                                                                 seq++;
546                                                         pkey = key;
547                                                 } else {
548                                                         mismatch = true;
549                                                         score -= syms;
550                                                 }
551                                         }
552                                 }
553
554                                 if ((score > max_score) || ((score == max_score) && (seq > max_seq))) {
555                                         // best match so far
556                                         layout = current;
557                                         max_score = score;
558                                         max_seq = seq;
559                                         ismatch = !mismatch;
560                                 }
561                         }
562
563                         if (layout != null) 
564                                 Console.WriteLine ("done detecting keyboard:  " + layout.Comment);
565                         else
566                                 Console.WriteLine ("no keyboard detected");
567                         this.layout = layout;
568                 }
569
570                 // TODO
571                 private int MapDeadKeySym (int val)
572                 {
573                         return 0;
574                 }
575
576                 [DllImport ("libX11")]
577                 internal extern static int XLookupString(ref XEvent xevent, IntPtr buffer,
578                                 int num_bytes, out XKeySym keysym, IntPtr status);
579
580                 [DllImport ("libX11")]
581                 private static extern XKeySym XLookupKeysym (ref XEvent xevent, int index);
582
583                 [DllImport ("libX11")]
584                 private static extern IntPtr XGetKeyboardMapping (IntPtr display, byte first_keycode, int keycode_count, 
585                                 out int keysyms_per_keycode_return);
586
587                 [DllImport ("libX11")]
588                 private static extern void XDisplayKeycodes (IntPtr display, out int min, out int max);
589
590                 [DllImport ("libX11")]
591                 private static extern XKeySym XKeycodeToKeysym (IntPtr display, int keycode, int index);
592
593                 [DllImport ("libX11")]
594                 private static extern int XKeysymToKeycode (IntPtr display, int keysym);
595
596                 [DllImport ("libX11")]
597                 internal extern static IntPtr XGetModifierMapping (IntPtr display);
598
599                 [DllImport ("libX11")]
600                 internal extern static int XFreeModifiermap (IntPtr modmap);
601                 
602         }
603
604 }
605