1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004 Novell, Inc.
23 // Jackson Harper (jackson@ximian.com)
30 // - dead chars are not translated properly
31 // - There is a lot of potential for optimmization in here
35 using System.Runtime.InteropServices;
37 namespace System.Windows.Forms {
39 internal class X11Keyboard {
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;
50 private int NumLockMask;
51 private int AltGrMask;
53 public X11Keyboard (IntPtr display)
55 this.display = display;
57 CreateConversionArray (layout);
60 public Keys ModifierKeys {
62 Keys keys = Keys.None;
63 if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0)
65 if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0)
67 if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0)
73 public void KeyEvent (IntPtr hwnd, XEvent xevent, ref MSG msg)
75 if ((xevent.KeyEvent.keycode >> 8) == 0x10)
76 xevent.KeyEvent.keycode = xevent.KeyEvent.keycode & 0xFF;
78 int event_time = xevent.KeyEvent.time;
79 int vkey = EventToVkey (xevent);
84 switch ((VirtualKeys) (vkey & 0xFF)) {
85 case VirtualKeys.VK_NUMLOCK:
86 GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, xevent.type, event_time);
88 case VirtualKeys.VK_CAPITAL:
89 GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, xevent.type, event_time);
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);
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);
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);
118 public bool TranslateMessage (ref MSG msg)
122 if (msg.message >= Msg.WM_KEYFIRST && msg.message <= Msg.WM_KEYLAST)
125 if (msg.message != Msg.WM_KEYDOWN && msg.message != Msg.WM_SYSKEYDOWN)
131 int tu = ToUnicode ((int) msg.wParam, Control.HighOrder ((int) msg.lParam), out buffer);
134 message = (msg.message == Msg.WM_KEYDOWN ? Msg.WM_CHAR : Msg.WM_SYSCHAR);
135 XplatUIX11.PostMessage (msg.hwnd, message, (IntPtr) buffer [0], msg.lParam);
138 message = (msg.message == Msg.WM_KEYDOWN ? Msg.WM_DEADCHAR : Msg.WM_SYSDEADCHAR);
139 XplatUIX11.PostMessage (msg.hwnd, message, (IntPtr) buffer [0], msg.lParam);
146 private int ToUnicode (int vkey, int scan, out string buffer)
148 if ((scan & 0x8000) != 0) {
149 buffer = String.Empty;
153 XEvent e = new XEvent ();
154 e.KeyEvent.display = display;
155 e.KeyEvent.keycode = 0;
156 e.KeyEvent.state = 0;
158 if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0) {
159 e.KeyEvent.state |= (int) KeyMasks.ShiftMask;
162 if ((key_state_table [(int) VirtualKeys.VK_CAPITAL] & 0x01) != 0) {
163 e.KeyEvent.state |= (int) KeyMasks.LockMask;
166 if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) {
167 e.KeyEvent.state |= (int) KeyMasks.ControlMask;
170 if ((key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x01) != 0) {
171 e.KeyEvent.state |= NumLockMask;
174 e.KeyEvent.state |= AltGrMask;
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;
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);
191 if (vkey == (int) VirtualKeys.VK_DECIMAL)
192 e.KeyEvent.keycode = XKeysymToKeycode (display, (int) KeypadKeys.XK_KP_Decimal);
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;
201 IntPtr buf = Marshal.AllocHGlobal (2);
203 int res = XLookupString (ref e, buf, 2, out t, IntPtr.Zero);
204 int keysym = (int) t;
206 buffer = String.Empty;
208 int dead_char = MapDeadKeySym (keysym);
209 // TODO: deal with dead chars
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;
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;
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;
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));
245 private MSG SendKeyboardInput (VirtualKeys vkey, int scan, KeybdEventFlags dw_flags, int time)
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);
255 if ((key_state_table [(int) vkey] & 0x80) == 0) {
256 key_state_table [(int) vkey] ^= 0x01;
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);
264 MSG msg = new MSG ();
265 msg.message = message;
266 msg.wParam = (IntPtr) vkey;
267 msg.lParam = IntPtr.Zero;
272 private void GenerateMessage (VirtualKeys vkey, int scan, XEventName type, int event_time)
274 bool state = (vkey == VirtualKeys.VK_NUMLOCK ? num_state : cap_state);
275 KeybdEventFlags up, down;
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);
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);
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;
304 private void SetState (VirtualKeys key, bool state)
306 if (VirtualKeys.VK_NUMLOCK == key)
312 public int EventToVkey (XEvent e)
316 XLookupString (ref e, IntPtr.Zero, 0, out ks, IntPtr.Zero);
317 int keysym = (int) ks;
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];
326 return keyc2vkey [e.KeyEvent.keycode];
329 public void CreateConversionArray (KeyboardLayout layout)
332 XEvent e2 = new XEvent ();
334 int [] ckey = new int [] { 0, 0, 0, 0 };
336 VirtualKeys oem_key = VirtualKeys.VK_OEM_7;
338 e2.KeyEvent.display = display;
339 e2.KeyEvent.state = 0;
341 int oem_vkey = (int) VirtualKeys.VK_OEM_7;
342 for (int keyc = min_keycode; keyc <= max_keycode; keyc++) {
346 e2.KeyEvent.keycode = keyc;
348 XLookupString (ref e2, IntPtr.Zero, 0, out t, IntPtr.Zero);
351 if ((keysym >> 8) == 0xFF) {
352 vkey = KeyboardLayouts.nonchar_key_vkey [keysym & 0xFF];
353 scan = KeyboardLayouts.nonchar_key_scan [keysym & 0xFF];
355 if ((scan & 0x100) != 0)
357 } else if (keysym == 0x20) { // spacebar
358 vkey = (int) VirtualKeys.VK_SPACE;
361 // Search layout dependent scancodes
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;
371 ckey [i] = MapDeadKeySym (keysym);
374 for (int keyn = 0; keyn < layout.Key.Length; keyn++) {
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])
380 if ((ok != 0) || (i > maxlen)) {
389 scan = layout.Scan [maxval];
390 vkey = (int) layout.VKey [maxval];
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)) {
403 for (int i = 0; (i < keysyms_per_keycode) && (vkey != 0); i++) {
404 keysym = (int) XLookupKeysym (ref e2, i);
405 switch ((char) keysym) {
407 vkey = (int) VirtualKeys.VK_OEM_1;
410 vkey = (int) VirtualKeys.VK_OEM_2;
413 vkey = (int) VirtualKeys.VK_OEM_3;
416 vkey = (int) VirtualKeys.VK_OEM_4;
419 vkey = (int) VirtualKeys.VK_OEM_5;
422 vkey = (int) VirtualKeys.VK_OEM_6;
425 vkey = (int) VirtualKeys.VK_OEM_7;
428 vkey = (int) VirtualKeys.VK_OEM_COMMA;
431 vkey = (int) VirtualKeys.VK_OEM_PERIOD;
434 vkey = (int) VirtualKeys.VK_OEM_MINUS;
437 vkey = (int) VirtualKeys.VK_OEM_PLUS;
444 switch (++oem_vkey) {
458 keyc2vkey [e2.KeyEvent.keycode] = vkey;
459 keyc2scan [e2.KeyEvent.keycode] = scan;
465 public void DetectLayout ()
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);
472 syms = keysyms_per_keycode;
474 Console.Error.WriteLine ("{0} keysymbols per a keycode is not supported, setting to 4", syms);
478 IntPtr modmap_unmanaged;
479 XModifierKeymap xmk = new XModifierKeymap ();
481 modmap_unmanaged = XGetModifierMapping (display);
482 xmk = (XModifierKeymap) Marshal.PtrToStructure (modmap_unmanaged, typeof (XModifierKeymap));
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);
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;
496 XFreeModifiermap (modmap_unmanaged);
498 int [] ckey = new int [4];
500 KeyboardLayout layout = null;
503 bool ismatch = false;
505 foreach (KeyboardLayout current in KeyboardLayouts.Layouts) {
512 bool mismatch = false;
513 int key = min_keycode;
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);
519 if ((keysym != 0xFF1B) && (keysym < 0x800) && (keysym != ' ')) {
520 ckey [i] = keysym & 0xFF;
522 ckey [i] = MapDeadKeySym (keysym);
527 for (key = 0; key < current.Key.Length; key++) {
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]) {
534 if (ckey [i] != 0 && current.Key [key][i] != (char) ckey [i])
554 if ((score > max_score) || ((score == max_score) && (seq > max_seq))) {
564 Console.WriteLine ("done detecting keyboard: " + layout.Comment);
566 Console.WriteLine ("no keyboard detected");
567 this.layout = layout;
571 private int MapDeadKeySym (int val)
576 [DllImport ("libX11")]
577 internal extern static int XLookupString(ref XEvent xevent, IntPtr buffer,
578 int num_bytes, out XKeySym keysym, IntPtr status);
580 [DllImport ("libX11")]
581 private static extern XKeySym XLookupKeysym (ref XEvent xevent, int index);
583 [DllImport ("libX11")]
584 private static extern IntPtr XGetKeyboardMapping (IntPtr display, byte first_keycode, int keycode_count,
585 out int keysyms_per_keycode_return);
587 [DllImport ("libX11")]
588 private static extern void XDisplayKeycodes (IntPtr display, out int min, out int max);
590 [DllImport ("libX11")]
591 private static extern XKeySym XKeycodeToKeysym (IntPtr display, int keycode, int index);
593 [DllImport ("libX11")]
594 private static extern int XKeysymToKeycode (IntPtr display, int keysym);
596 [DllImport ("libX11")]
597 internal extern static IntPtr XGetModifierMapping (IntPtr display);
599 [DllImport ("libX11")]
600 internal extern static int XFreeModifiermap (IntPtr modmap);