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 = KeyboardLayouts.Layouts [0];
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)
77 XLookupString (ref xevent, IntPtr.Zero, 0, out keysym, IntPtr.Zero);
78 if (((int) keysym >= (int) MiscKeys.XK_ISO_Lock &&
79 (int) keysym <= (int) MiscKeys.XK_ISO_Last_Group_Lock) ||
80 (int) keysym == (int) MiscKeys.XK_Mode_switch) {
81 UpdateKeyState (xevent);
85 if ((xevent.KeyEvent.keycode >> 8) == 0x10)
86 xevent.KeyEvent.keycode = xevent.KeyEvent.keycode & 0xFF;
88 int event_time = (int)xevent.KeyEvent.time;
90 AltGrMask = xevent.KeyEvent.state & (0x6000 | (int) KeyMasks.ModMasks);
91 int vkey = EventToVkey (xevent);
95 switch ((VirtualKeys) (vkey & 0xFF)) {
96 case VirtualKeys.VK_NUMLOCK:
97 GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, xevent.type, event_time);
99 case VirtualKeys.VK_CAPITAL:
100 GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, xevent.type, event_time);
104 if (((key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x01) == 0) != ((xevent.KeyEvent.state & NumLockMask) == 0)) {
105 GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, XEventName.KeyPress, event_time);
106 GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, XEventName.KeyRelease, event_time);
109 if (((key_state_table [(int) VirtualKeys.VK_CAPITAL] & 0x01) == 0) != ((xevent.KeyEvent.state & (int) KeyMasks.LockMask) == 0)) {
110 GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, XEventName.KeyPress, event_time);
111 GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, XEventName.KeyRelease, event_time);
117 int bscan = (keyc2scan [xevent.KeyEvent.keycode] & 0xFF);
118 KeybdEventFlags dw_flags = KeybdEventFlags.None;
119 if (xevent.type == XEventName.KeyRelease)
120 dw_flags |= KeybdEventFlags.KeyUp;
121 if ((vkey & 0x100) != 0)
122 dw_flags |= KeybdEventFlags.ExtendedKey;
123 msg = SendKeyboardInput ((VirtualKeys) (vkey & 0xFF), bscan, dw_flags, event_time);
129 public bool TranslateMessage (ref MSG msg)
133 if (msg.message >= Msg.WM_KEYFIRST && msg.message <= Msg.WM_KEYLAST)
136 if (msg.message != Msg.WM_KEYDOWN && msg.message != Msg.WM_SYSKEYDOWN)
142 int tu = ToUnicode ((int) msg.wParam, Control.HighOrder ((int) msg.lParam), out buffer);
145 message = (msg.message == Msg.WM_KEYDOWN ? Msg.WM_CHAR : Msg.WM_SYSCHAR);
146 XplatUIX11.PostMessage (msg.hwnd, message, (IntPtr) buffer [0], msg.lParam);
149 message = (msg.message == Msg.WM_KEYDOWN ? Msg.WM_DEADCHAR : Msg.WM_SYSDEADCHAR);
150 XplatUIX11.PostMessage (msg.hwnd, message, (IntPtr) buffer [0], msg.lParam);
157 private int ToUnicode (int vkey, int scan, out string buffer)
159 if ((scan & 0x8000) != 0) {
160 buffer = String.Empty;
164 XEvent e = new XEvent ();
165 e.KeyEvent.display = display;
166 e.KeyEvent.keycode = 0;
167 e.KeyEvent.state = 0;
169 if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0) {
170 e.KeyEvent.state |= (int) KeyMasks.ShiftMask;
173 if ((key_state_table [(int) VirtualKeys.VK_CAPITAL] & 0x01) != 0) {
174 e.KeyEvent.state |= (int) KeyMasks.LockMask;
177 if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) {
178 e.KeyEvent.state |= (int) KeyMasks.ControlMask;
181 if ((key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x01) != 0) {
182 e.KeyEvent.state |= NumLockMask;
185 e.KeyEvent.state |= AltGrMask;
187 for (int keyc = min_keycode; (keyc <= max_keycode) && (e.KeyEvent.keycode == 0); keyc++) {
188 // find keycode that could have generated this vkey
189 if ((keyc2vkey [keyc] & 0xFF) == vkey) {
190 // filter extended bit because it is not known
191 e.KeyEvent.keycode = keyc;
192 if ((EventToVkey (e) & 0xFF) != vkey) {
193 // Wrong one (ex: because of num,lock state)
194 e.KeyEvent.keycode = 0;
199 if ((vkey >= (int) VirtualKeys.VK_NUMPAD0) && (vkey <= (int) VirtualKeys.VK_NUMPAD9))
200 e.KeyEvent.keycode = XKeysymToKeycode (display, vkey - (int) VirtualKeys.VK_NUMPAD0 + (int) KeypadKeys.XK_KP_0);
202 if (vkey == (int) VirtualKeys.VK_DECIMAL)
203 e.KeyEvent.keycode = XKeysymToKeycode (display, (int) KeypadKeys.XK_KP_Decimal);
205 if (e.KeyEvent.keycode == 0) {
206 // And I couldn't find the keycode so i returned the vkey and was like whatever
207 Console.Error.WriteLine ("unknown virtual key {0:X}", vkey);
208 buffer = String.Empty;
212 IntPtr buf = Marshal.AllocHGlobal (2);
214 int res = XLookupString (ref e, buf, 2, out t, IntPtr.Zero);
215 int keysym = (int) t;
217 buffer = String.Empty;
219 int dead_char = MapDeadKeySym (keysym);
220 if (dead_char != 0) {
221 byte [] bytes = new byte [1];
222 bytes [0] = (byte) dead_char;
223 Encoding encoding = Encoding.GetEncoding (layout.CodePage);
224 buffer = new string (encoding.GetChars (bytes));
228 // Shift + arrow, shift + home, ....
229 // X returns a char for it, but windows doesn't
230 if (((e.KeyEvent.state & NumLockMask) == 0) && ((e.KeyEvent.state & (int) KeyMasks.ShiftMask) != 0) &&
231 (keysym >= (int) KeypadKeys.XK_KP_0) && (keysym <= (int) KeypadKeys.XK_KP_9)) {
232 buffer = String.Empty;
236 // CTRL + number, X returns chars, windows does not
237 if ((e.KeyEvent.state & (int) KeyMasks.ControlMask) != 0) {
238 if (((keysym >= 33) && (keysym < 'A')) || ((keysym > 'Z') && (keysym < 'a'))) {
239 buffer = String.Empty;
244 // X returns a char for delete key on extended keyboards, windows does not
245 if (keysym == (int) TtyKeys.XK_Delete) {
246 buffer = String.Empty;
251 byte [] bytes = new byte [2];
252 bytes [0] = Marshal.ReadByte (buf);
253 bytes [1] = Marshal.ReadByte (buf, 1);
254 Encoding encoding = Encoding.GetEncoding (layout.CodePage);
255 buffer = new string (encoding.GetChars (bytes));
262 private MSG SendKeyboardInput (VirtualKeys vkey, int scan, KeybdEventFlags dw_flags, int time)
266 if ((dw_flags & KeybdEventFlags.KeyUp) != 0) {
267 bool sys_key = (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 &&
268 ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) == 0);
269 key_state_table [(int) vkey] &= unchecked ((byte) ~0x80);
270 message = (sys_key ? Msg.WM_SYSKEYUP : Msg.WM_KEYUP);
272 if ((key_state_table [(int) vkey] & 0x80) == 0) {
273 key_state_table [(int) vkey] ^= 0x01;
275 key_state_table [(int) vkey] |= 0x80;
276 bool sys_key = (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 &&
277 ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) == 0);
278 message = (sys_key ? Msg.WM_SYSKEYDOWN : Msg.WM_KEYDOWN);
281 MSG msg = new MSG ();
282 msg.message = message;
283 msg.wParam = (IntPtr) vkey;
284 msg.lParam = IntPtr.Zero;
289 private void GenerateMessage (VirtualKeys vkey, int scan, XEventName type, int event_time)
291 bool state = (vkey == VirtualKeys.VK_NUMLOCK ? num_state : cap_state);
292 KeybdEventFlags up, down;
295 // The INTERMEDIARY state means : just after a 'press' event, if a 'release' event comes,
296 // don't treat it. It's from the same key press. Then the state goes to ON.
297 // And from there, a 'release' event will switch off the toggle key.
298 SetState (vkey, false);
300 down = (vkey == VirtualKeys.VK_NUMLOCK ? KeybdEventFlags.ExtendedKey : KeybdEventFlags.None);
301 up = (vkey == VirtualKeys.VK_NUMLOCK ? KeybdEventFlags.ExtendedKey :
302 KeybdEventFlags.None) | KeybdEventFlags.KeyUp;
303 if ((key_state_table [(int) vkey] & 0x1) != 0) { // it was on
304 if (type != XEventName.KeyPress) {
305 SendKeyboardInput (vkey, scan, down, event_time);
306 SendKeyboardInput (vkey, scan, up, event_time);
307 SetState (vkey, false);
308 key_state_table [(int) vkey] &= unchecked ((byte) ~0x01);
311 if (type == XEventName.KeyPress) {
312 SendKeyboardInput (vkey, scan, down, event_time);
313 SendKeyboardInput (vkey, scan, up, event_time);
314 SetState (vkey, true);
315 key_state_table [(int) vkey] |= 0x01;
321 private void UpdateKeyState (XEvent xevent)
323 int vkey = EventToVkey (xevent);
325 switch (xevent.type) {
326 case XEventName.KeyRelease:
327 key_state_table [(int) vkey] &= unchecked ((byte) ~0x80);
329 case XEventName.KeyPress:
330 if ((key_state_table [(int) vkey] & 0x80) == 0) {
331 key_state_table [(int) vkey] ^= 0x01;
333 key_state_table [(int) vkey] |= 0x80;
338 private void SetState (VirtualKeys key, bool state)
340 if (VirtualKeys.VK_NUMLOCK == key)
346 public int EventToVkey (XEvent e)
350 XLookupString (ref e, IntPtr.Zero, 0, out ks, IntPtr.Zero);
351 int keysym = (int) ks;
353 if ((keysym >= 0xFFAE) && (keysym <= 0xFFB9) && (keysym != 0xFFAF)
354 && ((e.KeyEvent.state & NumLockMask) !=0)) {
355 // Only the Keypad keys 0-9 and . send different keysyms
356 // depending on the NumLock state
357 return KeyboardLayouts.nonchar_key_vkey [keysym & 0xFF];
360 return keyc2vkey [e.KeyEvent.keycode];
363 public void CreateConversionArray (KeyboardLayout layout)
366 XEvent e2 = new XEvent ();
368 int [] ckey = new int [] { 0, 0, 0, 0 };
370 e2.KeyEvent.display = display;
371 e2.KeyEvent.state = 0;
373 int oem_vkey = (int) VirtualKeys.VK_OEM_7;
374 for (int keyc = min_keycode; keyc <= max_keycode; keyc++) {
378 e2.KeyEvent.keycode = keyc;
380 XLookupString (ref e2, IntPtr.Zero, 0, out t, IntPtr.Zero);
383 if ((keysym >> 8) == 0xFF) {
384 vkey = KeyboardLayouts.nonchar_key_vkey [keysym & 0xFF];
385 scan = KeyboardLayouts.nonchar_key_scan [keysym & 0xFF];
387 if ((scan & 0x100) != 0)
389 } else if (keysym == 0x20) { // spacebar
390 vkey = (int) VirtualKeys.VK_SPACE;
393 // Search layout dependent scancodes
398 for (int i = 0; i < syms; i++) {
399 keysym = (int) XKeycodeToKeysym (display, keyc, i);
400 if ((keysym < 0x800) && (keysym != ' '))
401 ckey [i] = keysym & 0xFF;
403 ckey [i] = MapDeadKeySym (keysym);
406 for (int keyn = 0; keyn < layout.Key.Length; keyn++) {
408 int ml = (layout.Key [keyn].Length > 4 ? 4 : layout.Key [keyn].Length);
409 for (ok = layout.Key [keyn][i]; (ok != 0) && (i < ml); i++) {
410 if (layout.Key [keyn][i] != ckey [i])
412 if ((ok != 0) || (i > maxlen)) {
421 scan = layout.Scan [maxval];
422 vkey = (int) layout.VKey [maxval];
427 for (int i = 0; (i < keysyms_per_keycode) && (vkey == 0); i++) {
428 keysym = (int) XLookupKeysym (ref e2, i);
429 if ((keysym >= (int) VirtualKeys.VK_0 && keysym <= (int) VirtualKeys.VK_9) ||
430 (keysym >= (int) VirtualKeys.VK_A && keysym <= (int) VirtualKeys.VK_Z)) {
435 for (int i = 0; (i < keysyms_per_keycode) && (vkey == 0); i++) {
436 keysym = (int) XLookupKeysym (ref e2, i);
437 switch ((char) keysym) {
439 vkey = (int) VirtualKeys.VK_OEM_1;
442 vkey = (int) VirtualKeys.VK_OEM_2;
445 vkey = (int) VirtualKeys.VK_OEM_3;
448 vkey = (int) VirtualKeys.VK_OEM_4;
451 vkey = (int) VirtualKeys.VK_OEM_5;
454 vkey = (int) VirtualKeys.VK_OEM_6;
457 vkey = (int) VirtualKeys.VK_OEM_7;
460 vkey = (int) VirtualKeys.VK_OEM_COMMA;
463 vkey = (int) VirtualKeys.VK_OEM_PERIOD;
466 vkey = (int) VirtualKeys.VK_OEM_MINUS;
469 vkey = (int) VirtualKeys.VK_OEM_PLUS;
476 switch (++oem_vkey) {
490 keyc2vkey [e2.KeyEvent.keycode] = vkey;
491 keyc2scan [e2.KeyEvent.keycode] = scan;
497 public void DetectLayout ()
499 XDisplayKeycodes (display, out min_keycode, out max_keycode);
500 IntPtr ksp = XGetKeyboardMapping (display, (byte) min_keycode,
501 max_keycode + 1 - min_keycode, out keysyms_per_keycode);
502 XplatUIX11.XFree (ksp);
504 syms = keysyms_per_keycode;
506 //Console.Error.WriteLine ("{0} keysymbols per a keycode is not supported, setting to 4", syms);
510 IntPtr modmap_unmanaged;
511 XModifierKeymap xmk = new XModifierKeymap ();
513 modmap_unmanaged = XGetModifierMapping (display);
514 xmk = (XModifierKeymap) Marshal.PtrToStructure (modmap_unmanaged, typeof (XModifierKeymap));
517 for (int i = 0; i < 8; i++) {
518 for (int j = 0; j < xmk.max_keypermod; j++, mmp++) {
519 byte b = Marshal.ReadByte (xmk.modifiermap, mmp);
521 for (int k = 0; k < keysyms_per_keycode; k++) {
522 if ((int) XKeycodeToKeysym (display, b, k) == (int) MiscKeys.XK_Num_Lock)
523 NumLockMask = 1 << i;
528 XFreeModifiermap (modmap_unmanaged);
530 int [] ckey = new int [4];
531 KeyboardLayout layout = null;
535 foreach (KeyboardLayout current in KeyboardLayouts.Layouts) {
541 int key = min_keycode;
543 for (int keyc = min_keycode; keyc <= max_keycode; keyc++) {
544 for (int i = 0; i < syms; i++) {
545 int keysym = (int) XKeycodeToKeysym (display, keyc, i);
547 if ((keysym != 0xFF1B) && (keysym < 0x800) && (keysym != ' ')) {
548 ckey [i] = keysym & 0xFF;
550 ckey [i] = MapDeadKeySym (keysym);
555 for (key = 0; key < current.Key.Length; key++) {
557 int ml = (current.Key [key].Length > syms ? syms : current.Key [key].Length);
558 for (int i = 0; (ok >= 0) && (i < ml); i++) {
559 if (ckey [i] != 0 && current.Key [key][i] == (char) ckey [i]) {
562 if (ckey [i] != 0 && current.Key [key][i] != (char) ckey [i])
581 if ((score > max_score) || ((score == max_score) && (seq > max_seq))) {
589 if (layout != null) {
590 this.layout = layout;
591 Console.WriteLine (Locale.GetText("Keyboard") + ": " + layout.Comment);
593 Console.WriteLine (Locale.GetText("Keyboard layout not recognized, using default layout: " + layout.Comment));
598 private int MapDeadKeySym (int val)
601 case (int) DeadKeys.XK_dead_tilde :
602 case 0x1000FE7E : // Xfree's Dtilde
604 case (int) DeadKeys.XK_dead_acute :
605 case 0x1000FE27 : // Xfree's XK_Dacute_accent
607 case (int) DeadKeys.XK_dead_circumflex:
608 case 0x1000FE5E : // Xfree's XK_.Dcircumflex_accent
610 case (int) DeadKeys.XK_dead_grave :
611 case 0x1000FE60 : // Xfree's XK_.Dgrave_accent
613 case (int) DeadKeys.XK_dead_diaeresis :
614 case 0x1000FE22 : // Xfree's XK_.Ddiaeresis
616 case (int) DeadKeys.XK_dead_cedilla :
618 case (int) DeadKeys.XK_dead_macron :
620 case (int) DeadKeys.XK_dead_breve :
622 case (int) DeadKeys.XK_dead_abovedot :
624 case (int) DeadKeys.XK_dead_abovering :
626 case (int) DeadKeys.XK_dead_doubleacute :
628 case (int) DeadKeys.XK_dead_caron :
630 case (int) DeadKeys.XK_dead_ogonek :
637 [DllImport ("libX11")]
638 internal extern static int XLookupString(ref XEvent xevent, IntPtr buffer, int num_bytes, out IntPtr keysym, IntPtr status);
639 internal static int XLookupString (ref XEvent xevent, IntPtr buffer, int num_bytes, out XKeySym keysym, IntPtr status) {
643 ret = XLookupString (ref xevent, buffer, num_bytes, out keysym_ret, status);
644 keysym = (XKeySym)keysym_ret.ToInt32();
649 [DllImport ("libX11", EntryPoint="XLookupKeysym")]
650 private static extern IntPtr XLookupKeysymX11(ref XEvent xevent, int index);
651 private static XKeySym XLookupKeysym(ref XEvent xevent, int index) {
652 return (XKeySym)XLookupKeysymX11(ref xevent, index).ToInt32();
655 [DllImport ("libX11")]
656 private static extern IntPtr XGetKeyboardMapping (IntPtr display, byte first_keycode, int keycode_count,
657 out int keysyms_per_keycode_return);
659 [DllImport ("libX11")]
660 private static extern void XDisplayKeycodes (IntPtr display, out int min, out int max);
662 [DllImport ("libX11", EntryPoint="XKeycodeToKeysym")]
663 private static extern IntPtr XKeycodeToKeysymX11(IntPtr display, int keycode, int index);
664 private static XKeySym XKeycodeToKeysym(IntPtr display, int keycode, int index) {
665 return (XKeySym)XKeycodeToKeysymX11(display, keycode, index).ToInt32();
668 [DllImport ("libX11")]
669 private static extern int XKeysymToKeycode (IntPtr display, IntPtr keysym);
670 private static int XKeysymToKeycode (IntPtr display, int keysym) {
671 return XKeysymToKeycode(display, (IntPtr)keysym);
674 [DllImport ("libX11")]
675 internal extern static IntPtr XGetModifierMapping (IntPtr display);
677 [DllImport ("libX11")]
678 internal extern static int XFreeModifiermap (IntPtr modmap);