aboutsummaryrefslogtreecommitdiff
path: root/source/ShiftUI/Internal/X11Keyboard.cs
diff options
context:
space:
mode:
authorMichaelTheShifter <[email protected]>2016-07-20 09:40:36 -0400
committerMichaelTheShifter <[email protected]>2016-07-20 09:40:36 -0400
commitd40fed5ce2bc806a91245adb18039634eac13ed0 (patch)
treef1d7168aee6db109ac2c738ad18c9db667a6ba69 /source/ShiftUI/Internal/X11Keyboard.cs
parentf1856e8ed30ed882229fd3fa2a4038122a5fb441 (diff)
downloadshiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.tar.gz
shiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.tar.bz2
shiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.zip
Move ShiftUI source code to ShiftOS
This'll be a lot easier to work on.
Diffstat (limited to 'source/ShiftUI/Internal/X11Keyboard.cs')
-rw-r--r--source/ShiftUI/Internal/X11Keyboard.cs1478
1 files changed, 1478 insertions, 0 deletions
diff --git a/source/ShiftUI/Internal/X11Keyboard.cs b/source/ShiftUI/Internal/X11Keyboard.cs
new file mode 100644
index 0000000..70a6c03
--- /dev/null
+++ b/source/ShiftUI/Internal/X11Keyboard.cs
@@ -0,0 +1,1478 @@
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Copyright (c) 2004 Novell, Inc.
+//
+// Authors:
+// Jackson Harper ([email protected])
+//
+//
+
+
+//
+// TODO:
+// - dead chars are not translated properly
+// - There is a lot of potential for optimmization in here
+//
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.Drawing;
+using System.Text;
+using System.Globalization;
+using System.Runtime.InteropServices;
+
+namespace ShiftUI {
+
+ public enum XLookupStatus
+ {
+ XBufferOverflow = -1,
+ XLookupNone = 1,
+ XLookupChars = 2,
+ XLookupKeySym = 3,
+ XLookupBoth = 4
+ }
+
+ internal class X11Keyboard : IDisposable {
+ internal static object XlibLock;
+
+ private IntPtr display;
+ private IntPtr client_window;
+ private IntPtr xim;
+ private Hashtable xic_table = new Hashtable ();
+ private XIMPositionContext positionContext;
+ private XIMCallbackContext callbackContext;
+ private XIMProperties ximStyle;
+ private EventMask xic_event_mask = EventMask.NoEventMask;
+ private StringBuilder lookup_buffer;
+ private byte [] lookup_byte_buffer = new byte [100];
+ private int min_keycode, max_keycode, keysyms_per_keycode, syms;
+ private int [] keyc2vkey = new int [256];
+ private int [] keyc2scan = new int [256];
+ private byte [] key_state_table = new byte [256];
+ private int lcid;
+ private bool num_state, cap_state;
+ private bool initialized;
+ private bool menu_state = false;
+ private Encoding encoding;
+
+ private int NumLockMask;
+ private int AltGrMask;
+
+ public X11Keyboard (IntPtr display, IntPtr clientWindow)
+ {
+ this.display = display;
+ lookup_buffer = new StringBuilder (24);
+ EnsureLayoutInitialized ();
+ }
+
+ private Encoding AnsiEncoding
+ {
+ get
+ {
+ if (encoding == null)
+ encoding = Encoding.GetEncoding(new CultureInfo(lcid).TextInfo.ANSICodePage);
+ return encoding;
+ }
+ }
+
+ public IntPtr ClientWindow {
+ get { return client_window; }
+ }
+
+ void IDisposable.Dispose ()
+ {
+ if (xim != IntPtr.Zero) {
+ foreach (IntPtr xic in xic_table.Values)
+ XDestroyIC (xic);
+ xic_table.Clear ();
+
+ XCloseIM (xim);
+ xim = IntPtr.Zero;
+ }
+ }
+
+ public void DestroyICForWindow (IntPtr window)
+ {
+ IntPtr xic = GetXic (window);
+ if (xic != IntPtr.Zero) {
+ xic_table.Remove ((long) window);
+ XDestroyIC (xic);
+ }
+ }
+
+ public void EnsureLayoutInitialized ()
+ {
+ if (initialized)
+ return;
+
+ KeyboardLayouts layouts = new KeyboardLayouts ();
+ KeyboardLayout layout = DetectLayout (layouts);
+ lcid = layout.Lcid;
+ CreateConversionArray (layouts, layout);
+ SetupXIM ();
+ initialized = true;
+ }
+
+ private void SetupXIM ()
+ {
+ xim = IntPtr.Zero;
+
+ if (!XSupportsLocale ()) {
+ Console.Error.WriteLine ("X does not support your locale");
+ return;
+ }
+
+ if (!XSetLocaleModifiers (String.Empty)) {
+ Console.Error.WriteLine ("Could not set X locale modifiers");
+ return;
+ }
+
+ if (Environment.GetEnvironmentVariable (ENV_NAME_XIM_STYLE) == "disabled") {
+ return;
+ }
+
+ xim = XOpenIM (display, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
+ if (xim == IntPtr.Zero)
+ Console.Error.WriteLine ("Could not get XIM");
+
+ initialized = true;
+ }
+
+ void CreateXicForWindow (IntPtr window)
+ {
+ IntPtr xic = CreateXic (window, xim);
+ xic_table [(long) window] = xic;
+ if (xic == IntPtr.Zero)
+ Console.Error.WriteLine ("Could not get XIC");
+ else {
+ if (XGetICValues (xic, "filterEvents", out xic_event_mask, IntPtr.Zero) != null)
+ Console.Error.WriteLine ("Could not get XIC values");
+ EventMask mask = EventMask.ExposureMask | EventMask.KeyPressMask | EventMask.FocusChangeMask;
+ if ((xic_event_mask | mask) == xic_event_mask) {
+ xic_event_mask |= mask;
+ lock (XlibLock) {
+ XplatUIX11.XSelectInput(display, window, new IntPtr ((int) xic_event_mask));
+ }
+ }
+ }
+ }
+
+ public EventMask KeyEventMask {
+ get { return xic_event_mask; }
+ }
+
+ public Keys ModifierKeys {
+ get {
+ Keys keys = Keys.None;
+ if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0)
+ keys |= Keys.Shift;
+ if ((key_state_table [(int) VirtualKeys.VK_CONTROL ] & 0x80) != 0)
+ keys |= Keys.Widget;
+ if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0)
+ keys |= Keys.Alt;
+ return keys;
+ }
+ }
+
+ private IntPtr GetXic (IntPtr window)
+ {
+ if (xim != IntPtr.Zero && xic_table.ContainsKey ((long) window))
+ return (IntPtr) xic_table [(long) window];
+ else
+ return IntPtr.Zero;
+ }
+
+ private bool FilterKey (XEvent e, int vkey)
+ {
+ if (XplatUI.key_filters.Count == 0)
+ return false;
+ XLookupStatus status;
+ XKeySym ks;
+ KeyFilterData data;
+ data.Down = (e.type == XEventName.KeyPress);
+ data.ModifierKeys = ModifierKeys;
+ LookupString (ref e, 0, out ks, out status);
+ data.keysym = (int)ks;
+ data.keycode = e.KeyEvent.keycode;
+ data.str = lookup_buffer.ToString (0, lookup_buffer.Length);
+ return XplatUI.FilterKey (data);
+ }
+
+ public void FocusIn (IntPtr window)
+ {
+ this.client_window = window;
+ if (xim == IntPtr.Zero)
+ return;
+
+ if (!xic_table.ContainsKey ((long) window))
+ CreateXicForWindow (window);
+ IntPtr xic = GetXic (window);
+ if (xic != IntPtr.Zero)
+ XSetICFocus (xic);
+ }
+
+ private bool have_Xutf8ResetIC = true;
+
+ public void FocusOut (IntPtr window)
+ {
+ this.client_window = IntPtr.Zero;
+ if (xim == IntPtr.Zero)
+ return;
+
+ IntPtr xic = GetXic (window);
+ if (xic != IntPtr.Zero) {
+ if (have_Xutf8ResetIC) {
+ try {
+ Xutf8ResetIC (xic);
+ } catch (EntryPointNotFoundException) {
+ have_Xutf8ResetIC = false;
+ }
+ }
+ XUnsetICFocus (xic);
+ }
+ }
+
+ public bool ResetKeyState(IntPtr hwnd, ref MSG msg) {
+ // FIXME - keep defining events/msg and return true until we've 'restored' all
+ // pending keypresses
+ if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0) {
+ key_state_table [(int) VirtualKeys.VK_SHIFT] &= unchecked((byte)~0x80);
+ }
+
+ if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) {
+ key_state_table [(int) VirtualKeys.VK_CONTROL] &= unchecked((byte)~0x80);
+ }
+
+ if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0) {
+ key_state_table [(int) VirtualKeys.VK_MENU] &= unchecked((byte)~0x80);
+ }
+ return false;
+ }
+
+ // Almost identical to UpdateKeyState() but does not call LookupString().
+ // The actual purpose is to handle shift state correctly.
+ public void PreFilter (XEvent xevent)
+ {
+ // It is still possible that some keyboards could have some shift
+ // keys outside this range, but let's think about such cases only
+ // if it actually happened.
+ if (xevent.KeyEvent.keycode >= keyc2vkey.Length)
+ return;
+
+ int vkey = keyc2vkey [xevent.KeyEvent.keycode];
+
+ switch (xevent.type) {
+ case XEventName.KeyRelease:
+ key_state_table [vkey & 0xff] &= unchecked ((byte) ~0x80);
+ break;
+ case XEventName.KeyPress:
+ if ((key_state_table [vkey & 0xff] & 0x80) == 0) {
+ key_state_table [vkey & 0xff] ^= 0x01;
+ }
+ key_state_table [vkey & 0xff] |= 0x80;
+ break;
+ }
+ }
+
+ public void KeyEvent (IntPtr hwnd, XEvent xevent, ref MSG msg)
+ {
+ XKeySym keysym;
+ int ascii_chars;
+
+ XLookupStatus status;
+ ascii_chars = LookupString (ref xevent, 24, out keysym, out status);
+
+ if (((int) keysym >= (int) MiscKeys.XK_ISO_Lock &&
+ (int) keysym <= (int) MiscKeys.XK_ISO_Last_Group_Lock) ||
+ (int) keysym == (int) MiscKeys.XK_Mode_switch) {
+ UpdateKeyState (xevent);
+ return;
+ }
+
+ if ((xevent.KeyEvent.keycode >> 8) == 0x10)
+ xevent.KeyEvent.keycode = xevent.KeyEvent.keycode & 0xFF;
+
+ int event_time = (int)xevent.KeyEvent.time;
+
+ if (status == XLookupStatus.XLookupChars) {
+ // do not ignore those inputs. They are mostly from XIM.
+ msg = SendImeComposition (lookup_buffer.ToString (0, lookup_buffer.Length));
+ msg.hwnd = hwnd;
+ return;
+ }
+
+ AltGrMask = xevent.KeyEvent.state & (0x6000 | (int) KeyMasks.ModMasks);
+ int vkey = EventToVkey (xevent);
+ if (vkey == 0 && ascii_chars != 0) {
+ vkey = (int) VirtualKeys.VK_NONAME;
+ }
+
+ if (FilterKey (xevent, vkey))
+ return;
+ switch ((VirtualKeys) (vkey & 0xFF)) {
+ case VirtualKeys.VK_NUMLOCK:
+ GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, xevent.KeyEvent.keycode, xevent.type, event_time);
+ break;
+ case VirtualKeys.VK_CAPITAL:
+ GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, xevent.KeyEvent.keycode, xevent.type, event_time);
+ break;
+ default:
+ if (((key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x01) == 0) != ((xevent.KeyEvent.state & NumLockMask) == 0)) {
+ GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, xevent.KeyEvent.keycode, XEventName.KeyPress, event_time);
+ GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, xevent.KeyEvent.keycode, XEventName.KeyRelease, event_time);
+ }
+
+ if (((key_state_table [(int) VirtualKeys.VK_CAPITAL] & 0x01) == 0) != ((xevent.KeyEvent.state & (int) KeyMasks.LockMask) == 0)) {
+ GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, xevent.KeyEvent.keycode, XEventName.KeyPress, event_time);
+ GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, xevent.KeyEvent.keycode, XEventName.KeyRelease, event_time);
+ }
+
+ num_state = false;
+ cap_state = false;
+
+ int bscan = (keyc2scan [xevent.KeyEvent.keycode] & 0xFF);
+ KeybdEventFlags dw_flags = KeybdEventFlags.None;
+ if (xevent.type == XEventName.KeyRelease)
+ dw_flags |= KeybdEventFlags.KeyUp;
+ if ((vkey & 0x100) != 0)
+ dw_flags |= KeybdEventFlags.ExtendedKey;
+ msg = SendKeyboardInput ((VirtualKeys) (vkey & 0xFF), bscan, xevent.KeyEvent.keycode, dw_flags, event_time);
+ msg.hwnd = hwnd;
+ break;
+ }
+ }
+
+ public bool TranslateMessage (ref MSG msg)
+ {
+ bool res = false;
+
+ if (msg.message >= Msg.WM_KEYFIRST && msg.message <= Msg.WM_KEYLAST)
+ res = true;
+
+ if (msg.message == Msg.WM_SYSKEYUP && msg.wParam == (IntPtr) 0x12 && menu_state) {
+ msg.message = Msg.WM_KEYUP;
+ menu_state = false;
+ }
+
+ if (msg.message != Msg.WM_KEYDOWN && msg.message != Msg.WM_SYSKEYDOWN)
+ return res;
+
+ if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 && msg.wParam != (IntPtr) 0x12)
+ menu_state = true;
+
+ EnsureLayoutInitialized ();
+
+ string buffer;
+ Msg message;
+ int tu = ToUnicode ((int) msg.wParam, Widget.HighOrder ((int) msg.lParam), out buffer);
+ switch (tu) {
+ case 1:
+ message = (msg.message == Msg.WM_KEYDOWN ? Msg.WM_CHAR : Msg.WM_SYSCHAR);
+ XplatUI.PostMessage (msg.hwnd, message, (IntPtr) buffer [0], msg.lParam);
+ break;
+ case -1:
+ message = (msg.message == Msg.WM_KEYDOWN ? Msg.WM_DEADCHAR : Msg.WM_SYSDEADCHAR);
+ XplatUI.PostMessage (msg.hwnd, message, (IntPtr) buffer [0], msg.lParam);
+ return true;
+ }
+
+ return res;
+ }
+
+ public int ToKeycode(int key)
+ {
+ int keycode = 0;
+
+ if (nonchar_vkey_key[key] > 0)
+ keycode = XKeysymToKeycode (display, nonchar_vkey_key[key]);
+
+ if (keycode == 0)
+ keycode = XKeysymToKeycode (display, key);
+
+ return keycode;
+ }
+
+ public int ToUnicode (int vkey, int scan, out string buffer)
+ {
+ if ((scan & 0x8000) != 0) {
+ buffer = String.Empty;
+ return 0;
+ }
+
+ XEvent e = new XEvent ();
+ e.AnyEvent.type = XEventName.KeyPress;
+ e.KeyEvent.display = display;
+ e.KeyEvent.keycode = 0;
+ e.KeyEvent.state = 0;
+
+ if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0) {
+ e.KeyEvent.state |= (int) KeyMasks.ShiftMask;
+ }
+
+ if ((key_state_table [(int) VirtualKeys.VK_CAPITAL] & 0x01) != 0) {
+ e.KeyEvent.state |= (int) KeyMasks.LockMask;
+ }
+
+ if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) {
+ e.KeyEvent.state |= (int) KeyMasks.ControlMask;
+ }
+
+ if ((key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x01) != 0) {
+ e.KeyEvent.state |= NumLockMask;
+ }
+
+ e.KeyEvent.state |= AltGrMask;
+
+ for (int keyc = min_keycode; (keyc <= max_keycode) && (e.KeyEvent.keycode == 0); keyc++) {
+ // find keycode that could have generated this vkey
+ if ((keyc2vkey [keyc] & 0xFF) == vkey) {
+ // filter extended bit because it is not known
+ e.KeyEvent.keycode = keyc;
+ if ((EventToVkey (e) & 0xFF) != vkey) {
+ // Wrong one (ex: because of num,lock state)
+ e.KeyEvent.keycode = 0;
+ }
+ }
+ }
+
+ if ((vkey >= (int) VirtualKeys.VK_NUMPAD0) && (vkey <= (int) VirtualKeys.VK_NUMPAD9))
+ e.KeyEvent.keycode = XKeysymToKeycode (display, vkey - (int) VirtualKeys.VK_NUMPAD0 + (int) KeypadKeys.XK_KP_0);
+
+ if (vkey == (int) VirtualKeys.VK_DECIMAL)
+ e.KeyEvent.keycode = XKeysymToKeycode (display, (int) KeypadKeys.XK_KP_Decimal);
+
+ if (vkey == (int) VirtualKeys.VK_SEPARATOR)
+ e.KeyEvent.keycode = XKeysymToKeycode(display, (int) KeypadKeys.XK_KP_Separator);
+
+ if (e.KeyEvent.keycode == 0 && vkey != (int) VirtualKeys.VK_NONAME) {
+ // And I couldn't find the keycode so i returned the vkey and was like whatever
+ Console.Error.WriteLine ("unknown virtual key {0:X}", vkey);
+ buffer = String.Empty;
+ return vkey;
+ }
+
+ XKeySym t;
+ XLookupStatus status;
+ int res = LookupString (ref e, 24, out t, out status);
+ int keysym = (int) t;
+
+ buffer = String.Empty;
+ if (res == 0) {
+ int dead_char = MapDeadKeySym (keysym);
+ if (dead_char != 0) {
+ byte [] bytes = new byte [1];
+ bytes [0] = (byte) dead_char;
+ buffer = new string (AnsiEncoding.GetChars (bytes));
+ res = -1;
+ }
+ } else {
+ // Shift + arrow, shift + home, ....
+ // X returns a char for it, but windows doesn't
+ if (((e.KeyEvent.state & NumLockMask) == 0) && ((e.KeyEvent.state & (int) KeyMasks.ShiftMask) != 0) &&
+ (keysym >= (int) KeypadKeys.XK_KP_0) && (keysym <= (int) KeypadKeys.XK_KP_9)) {
+ buffer = String.Empty;
+ res = 0;
+ }
+
+ // CTRL + number, X returns chars, windows does not
+ if ((e.KeyEvent.state & (int) KeyMasks.ControlMask) != 0) {
+ if (((keysym >= 33) && (keysym < 'A')) || ((keysym > 'Z') && (keysym < 'a'))) {
+ buffer = String.Empty;
+ res = 0;
+ }
+ }
+
+ // X returns a char for delete key on extended keyboards, windows does not
+ if (keysym == (int) TtyKeys.XK_Delete) {
+ buffer = String.Empty;
+ res = 0;
+ }
+
+ if (keysym == (int) TtyKeys.XK_BackSpace && (key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) {
+ buffer = new string (new char [] { (char) 127 });
+
+ return 1;
+ }
+
+ // For some special chars, such backspace and enter, looking up for a string
+ // can randomly fail to properly fill the buffer (either marshaling or X11), so we use
+ // the keysysm value to fill the gap
+ if (keysym == (int) XKeySym.XK_BackSpace) {
+ buffer = new string (new char [] { (char) 8 });
+ return 1;
+ }
+ if (keysym == (int) XKeySym.XK_Return) {
+ buffer = new string (new char [] { (char)13 });
+ return 1;
+ }
+
+ if (res != 0) {
+ buffer = lookup_buffer.ToString ();
+ res = buffer.Length;
+ }
+ }
+
+ return res;
+ }
+
+ string stored_keyevent_string;
+
+ internal string GetCompositionString ()
+ {
+ return stored_keyevent_string;
+ }
+
+ private MSG SendImeComposition (string s)
+ {
+ MSG msg = new MSG ();
+ msg.message = Msg.WM_IME_COMPOSITION;
+ msg.refobject = s;
+ stored_keyevent_string = s;
+ return msg;
+ }
+
+ private MSG SendKeyboardInput (VirtualKeys vkey, int scan, int keycode, KeybdEventFlags dw_flags, int time)
+ {
+ Msg message;
+
+ if ((dw_flags & KeybdEventFlags.KeyUp) != 0) {
+ bool sys_key = (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 &&
+ ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) == 0);
+ key_state_table [(int) vkey] &= unchecked ((byte) ~0x80);
+ message = (sys_key ? Msg.WM_SYSKEYUP : Msg.WM_KEYUP);
+ } else {
+ if ((key_state_table [(int) vkey] & 0x80) == 0) {
+ key_state_table [(int) vkey] ^= 0x01;
+ }
+ key_state_table [(int) vkey] |= 0x80;
+ bool sys_key = (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 &&
+ ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) == 0);
+ message = (sys_key ? Msg.WM_SYSKEYDOWN : Msg.WM_KEYDOWN);
+ }
+
+ MSG msg = new MSG ();
+ msg.message = message;
+ msg.wParam = (IntPtr) vkey;
+ msg.lParam = GenerateLParam (msg, keycode);
+ return msg;
+ }
+
+ private IntPtr GenerateLParam (MSG m, int keyCode)
+ {
+ // http://msdn.microsoft.com/en-us/library/ms646267(VS.85).aspx
+ //
+ byte flags = 0;
+
+ if (m.message == Msg.WM_SYSKEYUP || m.message == Msg.WM_KEYUP)
+ flags |= 0x80; // transition state flag = 1
+
+ flags |= 0x40; // previous key state flag = 1
+
+ if ((key_state_table [(int) VirtualKeys.VK_RMENU] & 0x80) != 0 ||
+ (key_state_table [(int) VirtualKeys.VK_LMENU] & 0x80) != 0 ||
+ (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0)
+ flags |= 0x20; // context code flag = 1
+
+ if ((key_state_table [(int) VirtualKeys.VK_INSERT] & 0x80) != 0 ||
+ (key_state_table [(int) VirtualKeys.VK_DELETE] & 0x80) != 0 ||
+ (key_state_table [(int) VirtualKeys.VK_HOME] & 0x80) != 0 ||
+ (key_state_table [(int) VirtualKeys.VK_END] & 0x80) != 0 ||
+ (key_state_table [(int) VirtualKeys.VK_UP] & 0x80) != 0 ||
+ (key_state_table [(int) VirtualKeys.VK_DOWN] & 0x80) != 0 ||
+ (key_state_table [(int) VirtualKeys.VK_LEFT] & 0x80) != 0 ||
+ (key_state_table [(int) VirtualKeys.VK_RIGHT] & 0x80) != 0 ||
+ (key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0 ||
+ (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 ||
+ (key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x80) != 0 ||
+ (key_state_table [(int) VirtualKeys.VK_PRINT] & 0x80) != 0 ||
+ (key_state_table [(int) VirtualKeys.VK_RETURN] & 0x80) != 0 ||
+ (key_state_table [(int) VirtualKeys.VK_DIVIDE] & 0x80) != 0 ||
+ (key_state_table [(int) VirtualKeys.VK_PRIOR] & 0x80) != 0 ||
+ (key_state_table [(int) VirtualKeys.VK_NEXT] & 0x80) != 0)
+ flags |= 0x01; // extended key flag = 1
+
+ int lparam = ((((int)flags) & 0x000000FF) << 3*8); // message flags
+ lparam |= ((keyCode & 0x000000FF) << 2*8); // scan code
+ lparam |= 0x00000001; // repeat count = 1
+
+ return (IntPtr)lparam;
+ }
+
+ private void GenerateMessage (VirtualKeys vkey, int scan, int key_code, XEventName type, int event_time)
+ {
+ bool state = (vkey == VirtualKeys.VK_NUMLOCK ? num_state : cap_state);
+ KeybdEventFlags up, down;
+
+ if (state) {
+ // The INTERMEDIARY state means : just after a 'press' event, if a 'release' event comes,
+ // don't treat it. It's from the same key press. Then the state goes to ON.
+ // And from there, a 'release' event will switch off the toggle key.
+ SetState (vkey, false);
+ } else {
+ down = (vkey == VirtualKeys.VK_NUMLOCK ? KeybdEventFlags.ExtendedKey : KeybdEventFlags.None);
+ up = (vkey == VirtualKeys.VK_NUMLOCK ? KeybdEventFlags.ExtendedKey :
+ KeybdEventFlags.None) | KeybdEventFlags.KeyUp;
+ if ((key_state_table [(int) vkey] & 0x1) != 0) { // it was on
+ if (type != XEventName.KeyPress) {
+ SendKeyboardInput (vkey, scan, key_code, down, event_time);
+ SendKeyboardInput (vkey, scan, key_code, up, event_time);
+ SetState (vkey, false);
+ key_state_table [(int) vkey] &= unchecked ((byte) ~0x01);
+ }
+ } else {
+ if (type == XEventName.KeyPress) {
+ SendKeyboardInput (vkey, scan, key_code, down, event_time);
+ SendKeyboardInput (vkey, scan, key_code, up, event_time);
+ SetState (vkey, true);
+ key_state_table [(int) vkey] |= 0x01;
+ }
+ }
+ }
+ }
+
+ private void UpdateKeyState (XEvent xevent)
+ {
+ int vkey = EventToVkey (xevent);
+
+ switch (xevent.type) {
+ case XEventName.KeyRelease:
+ key_state_table [vkey & 0xff] &= unchecked ((byte) ~0x80);
+ break;
+ case XEventName.KeyPress:
+ if ((key_state_table [vkey & 0xff] & 0x80) == 0) {
+ key_state_table [vkey & 0xff] ^= 0x01;
+ }
+ key_state_table [vkey & 0xff] |= 0x80;
+ break;
+ }
+ }
+
+ private void SetState (VirtualKeys key, bool state)
+ {
+ if (VirtualKeys.VK_NUMLOCK == key)
+ num_state = state;
+ else
+ cap_state = state;
+ }
+
+ public int EventToVkey (XEvent e)
+ {
+ XLookupStatus status;
+ XKeySym ks;
+
+ LookupString (ref e, 0, out ks, out status);
+ int keysym = (int) ks;
+
+ if (((e.KeyEvent.state & NumLockMask) != 0) &&
+ (keysym == (int)KeypadKeys.XK_KP_Separator || keysym == (int)KeypadKeys.XK_KP_Decimal ||
+ (keysym >= (int)KeypadKeys.XK_KP_0 && keysym <= (int)KeypadKeys.XK_KP_9))) {
+ // Only the Keypad keys 0-9 and . send different keysyms
+ // depending on the NumLock state
+ return nonchar_key_vkey [keysym & 0xFF];
+ }
+
+ return keyc2vkey [e.KeyEvent.keycode];
+ }
+
+ private void CreateConversionArray (KeyboardLayouts layouts, KeyboardLayout layout)
+ {
+ XEvent e2 = new XEvent ();
+ uint keysym = 0;
+ int [] ckey = new int [] { 0, 0, 0, 0 };
+
+ e2.KeyEvent.display = display;
+ e2.KeyEvent.state = 0;
+
+ for (int keyc = min_keycode; keyc <= max_keycode; keyc++) {
+ int vkey = 0;
+ int scan = 0;
+
+ e2.KeyEvent.keycode = keyc;
+ XKeySym t;
+
+ XLookupStatus status;
+ LookupString (ref e2, 0, out t, out status);
+
+ keysym = (uint) t;
+ if (keysym != 0) {
+ if ((keysym >> 8) == 0xFF) {
+ vkey = nonchar_key_vkey [keysym & 0xFF];
+ scan = nonchar_key_scan [keysym & 0xFF];
+ // Set extended bit
+ if ((scan & 0x100) != 0)
+ vkey |= 0x100;
+ } else if (keysym == 0x20) { // spacebar
+ vkey = (int) VirtualKeys.VK_SPACE;
+ scan = 0x39;
+ } else {
+ // Search layout dependent scancodes
+ int maxlen = 0;
+ int maxval = -1;;
+
+ for (int i = 0; i < syms; i++) {
+ keysym = XKeycodeToKeysym (display, keyc, i);
+ if ((keysym < 0x800) && (keysym != ' '))
+ ckey [i] = (sbyte) (keysym & 0xFF);
+ else
+ ckey [i] = (sbyte) MapDeadKeySym ((int) keysym);
+ }
+
+ for (int keyn = 0; keyn < layout.Keys.Length; keyn++) {
+ int ml = Math.Min (layout.Keys [keyn].Length, 4);
+ int ok = -1;
+ for (int i = 0; (ok != 0) && (i < ml); i++) {
+ sbyte ck = (sbyte) layout.Keys [keyn][i];
+ if (ck != ckey [i])
+ ok = 0;
+ if ((ok != 0) || (i > maxlen)) {
+ maxlen = i;
+ maxval = keyn;
+ }
+ if (ok != 0)
+ break;
+ }
+ }
+ if (maxval >= 0) {
+ if (maxval < layouts.scan_table [(int) layout.ScanIndex].Length)
+ scan = layouts.scan_table [(int) layout.ScanIndex][maxval];
+ if (maxval < layouts.vkey_table [(int) layout.VKeyIndex].Length)
+ vkey = layouts.vkey_table [(int) layout.VKeyIndex][maxval];
+ }
+ }
+ }
+ keyc2vkey [e2.KeyEvent.keycode] = vkey;
+ keyc2scan [e2.KeyEvent.keycode] = scan;
+ }
+
+
+ }
+
+ private KeyboardLayout DetectLayout (KeyboardLayouts layouts)
+ {
+ XDisplayKeycodes (display, out min_keycode, out max_keycode);
+ IntPtr ksp = XGetKeyboardMapping (display, (byte) min_keycode,
+ max_keycode + 1 - min_keycode, out keysyms_per_keycode);
+ lock (XlibLock) {
+ XplatUIX11.XFree (ksp);
+ }
+
+ syms = keysyms_per_keycode;
+ if (syms > 4) {
+ //Console.Error.WriteLine ("{0} keysymbols per a keycode is not supported, setting to 4", syms);
+ syms = 2;
+ }
+
+ IntPtr modmap_unmanaged;
+ XModifierKeymap xmk = new XModifierKeymap ();
+
+ modmap_unmanaged = XGetModifierMapping (display);
+ xmk = (XModifierKeymap) Marshal.PtrToStructure (modmap_unmanaged, typeof (XModifierKeymap));
+
+ int mmp = 0;
+ for (int i = 0; i < 8; i++) {
+ for (int j = 0; j < xmk.max_keypermod; j++, mmp++) {
+ byte b = Marshal.ReadByte (xmk.modifiermap, mmp);
+ if (b != 0) {
+ for (int k = 0; k < keysyms_per_keycode; k++) {
+ if ((int) XKeycodeToKeysym (display, b, k) == (int) MiscKeys.XK_Num_Lock)
+ NumLockMask = 1 << i;
+ }
+ }
+ }
+ }
+ XFreeModifiermap (modmap_unmanaged);
+
+ int [] ckey = new int [4];
+ KeyboardLayout layout = null;
+ int max_score = 0;
+ int max_seq = 0;
+
+ foreach (KeyboardLayout current in layouts.Layouts) {
+ int ok = 0;
+ int score = 0;
+ int match = 0;
+ int mismatch = 0;
+ int seq = 0;
+ int pkey = -1;
+ int key = min_keycode;
+ int i;
+
+ for (int keyc = min_keycode; keyc <= max_keycode; keyc++) {
+ for (i = 0; i < syms; i++) {
+ uint keysym = XKeycodeToKeysym (display, keyc, i);
+
+ if ((keysym < 0x800) && (keysym != ' ')) {
+ ckey [i] = (sbyte) (keysym & 0xFF);
+ } else {
+ ckey [i] = (sbyte) MapDeadKeySym ((int) keysym);
+ }
+ }
+ if (ckey [0] != 0) {
+ for (key = 0; key < current.Keys.Length; key++) {
+ int ml = Math.Min (syms, current.Keys [key].Length);
+ for (ok = 0, i = 0; (ok >= 0) && (i < ml); i++) {
+ sbyte ck = (sbyte) current.Keys [key][i];
+ if (ck != 0 && ck == ckey[i])
+ ok++;
+ if (ck != 0 && ck != ckey[i])
+ ok = -1;
+ }
+ if (ok > 0) {
+ score += ok;
+ break;
+ }
+ }
+ if (ok > 0) {
+ match++;
+ /* and how much the keycode order matches */
+ if (key > pkey)
+ seq++;
+ pkey = key;
+ } else {
+ /* print spaces instead of \0's */
+ mismatch++;
+ score -= syms;
+ }
+ }
+ }
+
+ if ((score > max_score) || ((score == max_score) && (seq > max_seq))) {
+ // best match so far
+ layout = current;
+ max_score = score;
+ max_seq = seq;
+ }
+ }
+
+ if (layout != null) {
+ return layout;
+ } else {
+ Console.WriteLine ("Keyboard layout not recognized, using default layout: " +
+ layouts.Layouts [0].Name);
+ }
+
+ return layouts.Layouts [0];
+ }
+
+ // TODO
+ private int MapDeadKeySym (int val)
+ {
+ switch (val) {
+ case (int) DeadKeys.XK_dead_tilde :
+ case 0x1000FE7E : // Xfree's Dtilde
+ return '~';
+ case (int) DeadKeys.XK_dead_acute :
+ case 0x1000FE27 : // Xfree's XK_Dacute_accent
+ return 0xb4;
+ case (int) DeadKeys.XK_dead_circumflex:
+ case 0x1000FE5E : // Xfree's XK_.Dcircumflex_accent
+ return '^';
+ case (int) DeadKeys.XK_dead_grave :
+ case 0x1000FE60 : // Xfree's XK_.Dgrave_accent
+ return '`';
+ case (int) DeadKeys.XK_dead_diaeresis :
+ case 0x1000FE22 : // Xfree's XK_.Ddiaeresis
+ return 0xa8;
+ case (int) DeadKeys.XK_dead_cedilla :
+ return 0xb8;
+ case (int) DeadKeys.XK_dead_macron :
+ return '-';
+ case (int) DeadKeys.XK_dead_breve :
+ return 0xa2;
+ case (int) DeadKeys.XK_dead_abovedot :
+ return 0xff;
+ case (int) DeadKeys.XK_dead_abovering :
+ return '0';
+ case (int) DeadKeys.XK_dead_doubleacute :
+ return 0xbd;
+ case (int) DeadKeys.XK_dead_caron :
+ return 0xb7;
+ case (int) DeadKeys.XK_dead_ogonek :
+ return 0xb2;
+ }
+
+ return 0;
+ }
+
+ private XIMProperties [] GetSupportedInputStyles (IntPtr xim)
+ {
+ IntPtr stylesPtr;
+ string ret = XGetIMValues (xim, XNames.XNQueryInputStyle, out stylesPtr, IntPtr.Zero);
+ if (ret != null || stylesPtr == IntPtr.Zero)
+ return new XIMProperties [0];
+ XIMStyles styles = (XIMStyles) Marshal.PtrToStructure (stylesPtr, typeof (XIMStyles));
+ XIMProperties [] supportedStyles = new XIMProperties [styles.count_styles];
+ for (int i = 0; i < styles.count_styles; i++)
+ supportedStyles [i] = (XIMProperties) Marshal.PtrToStructure (new IntPtr ((long) styles.supported_styles + i * Marshal.SizeOf (typeof (IntPtr))), typeof (XIMProperties));
+ lock (XlibLock) {
+ XplatUIX11.XFree (stylesPtr);
+ }
+ return supportedStyles;
+ }
+
+ const XIMProperties styleRoot = XIMProperties.XIMPreeditNothing | XIMProperties.XIMStatusNothing;
+ const XIMProperties styleOverTheSpot = XIMProperties.XIMPreeditPosition | XIMProperties.XIMStatusNothing;
+ const XIMProperties styleOnTheSpot = XIMProperties.XIMPreeditCallbacks | XIMProperties.XIMStatusNothing;
+ const string ENV_NAME_XIM_STYLE = "MONO_WINFORMS_XIM_STYLE";
+
+ private XIMProperties [] GetPreferredStyles ()
+ {
+ string env = Environment.GetEnvironmentVariable (ENV_NAME_XIM_STYLE);
+ if (env == null)
+ env = "over-the-spot";
+ string [] list = env.Split (' ');
+ XIMProperties [] ret = new XIMProperties [list.Length];
+ for (int i = 0; i < list.Length; i++) {
+ string s = list [i];
+ switch (s) {
+ case "over-the-spot":
+ ret [i] = styleOverTheSpot;
+ break;
+ case "on-the-spot":
+ ret [i] = styleOnTheSpot;
+ break;
+ case "root":
+ ret [i] = styleRoot;
+ break;
+ }
+ }
+ return ret;
+ }
+
+ private IEnumerable GetMatchingStylesInPreferredOrder (IntPtr xim)
+ {
+ XIMProperties [] supportedStyles = GetSupportedInputStyles (xim);
+ foreach (XIMProperties p in GetPreferredStyles ())
+ if (Array.IndexOf (supportedStyles, p) >= 0)
+ yield return p;
+ }
+
+ private IntPtr CreateXic (IntPtr window, IntPtr xim)
+ {
+ IntPtr xic = IntPtr.Zero;
+ foreach (XIMProperties targetStyle in GetMatchingStylesInPreferredOrder (xim)) {
+ ximStyle = targetStyle;
+ // FIXME: use __arglist when it gets working. See bug #321686
+ switch (targetStyle) {
+ case styleOverTheSpot:
+ xic = CreateOverTheSpotXic (window, xim);
+ if (xic != IntPtr.Zero)
+ break;
+ //Console.WriteLine ("failed to create XIC in over-the-spot mode.");
+ continue;
+ case styleOnTheSpot:
+ // Since .NET/Winforms seems to support only over-the-spot mode,,
+ // I'm not likely to continue on-the-spot implementation. But in
+ // case we need it, this code will be still useful.
+ xic = CreateOnTheSpotXic (window, xim);
+ if (xic != IntPtr.Zero)
+ break;
+ //Console.WriteLine ("failed to create XIC in on-the-spot mode.");
+ continue;
+ case styleRoot:
+ xic = XCreateIC (xim,
+ XNames.XNInputStyle, styleRoot,
+ XNames.XNClientWindow, window,
+ IntPtr.Zero);
+ break;
+ }
+ }
+ // fall back to root mode if all modes failed
+ if (xic == IntPtr.Zero) {
+ ximStyle = styleRoot;
+ xic = XCreateIC (xim,
+ XNames.XNInputStyle, styleRoot,
+ XNames.XNClientWindow, window,
+ XNames.XNFocusWindow, window,
+ IntPtr.Zero);
+ }
+ return xic;
+ }
+
+ private IntPtr CreateOverTheSpotXic (IntPtr window, IntPtr xim)
+ {
+ IntPtr list;
+ int count;
+ Widget c = Widget.FromHandle (window);
+ string xlfd = String.Format ("-*-*-*-*-*-*-{0}-*-*-*-*-*-*-*", (int) c.Font.Size);
+ IntPtr fontSet = XCreateFontSet (display, xlfd, out list, out count, IntPtr.Zero);
+ XPoint spot = new XPoint ();
+ spot.X = 0;
+ spot.Y = 0;
+ IntPtr pSL = IntPtr.Zero, pFS = IntPtr.Zero;
+ try {
+ pSL = Marshal.StringToHGlobalAnsi (XNames.XNSpotLocation);
+ pFS = Marshal.StringToHGlobalAnsi (XNames.XNFontSet);
+ IntPtr preedit = XVaCreateNestedList (0,
+ pSL, spot,
+ pFS, fontSet,
+ IntPtr.Zero);
+ return XCreateIC (xim,
+ XNames.XNInputStyle, styleOverTheSpot,
+ XNames.XNClientWindow, window,
+ XNames.XNPreeditAttributes, preedit,
+ IntPtr.Zero);
+ } finally {
+ if (pSL != IntPtr.Zero)
+ Marshal.FreeHGlobal (pSL);
+ if (pFS != IntPtr.Zero)
+ Marshal.FreeHGlobal (pFS);
+ XFreeStringList (list);
+ //XplatUIX11.XFree (preedit);
+ //XFreeFontSet (fontSet);
+ }
+ }
+
+ private IntPtr CreateOnTheSpotXic (IntPtr window, IntPtr xim)
+ {
+ callbackContext = new XIMCallbackContext (window);
+ return callbackContext.CreateXic (window, xim);
+ }
+
+ class XIMCallbackContext
+ {
+ XIMCallback startCB, doneCB, drawCB, caretCB;
+ IntPtr pStartCB = IntPtr.Zero, pDoneCB = IntPtr.Zero, pDrawCB = IntPtr.Zero, pCaretCB = IntPtr.Zero;
+ IntPtr pStartCBN = IntPtr.Zero, pDoneCBN = IntPtr.Zero, pDrawCBN = IntPtr.Zero, pCaretCBN = IntPtr.Zero;
+
+ public XIMCallbackContext (IntPtr clientWindow)
+ {
+ startCB = new XIMCallback (clientWindow, DoPreeditStart);
+ doneCB = new XIMCallback (clientWindow, DoPreeditDone);
+ drawCB = new XIMCallback (clientWindow, DoPreeditDraw);
+ caretCB = new XIMCallback (clientWindow, DoPreeditCaret);
+ pStartCB = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (XIMCallback)));
+ pDoneCB = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (XIMCallback)));
+ pDrawCB = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (XIMCallback)));
+ pCaretCB = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (XIMCallback)));
+ pStartCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditStartCallback);
+ pDoneCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditDoneCallback);
+ pDrawCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditDrawCallback);
+ pCaretCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditCaretCallback);
+ }
+
+ ~XIMCallbackContext ()
+ {
+ if (pStartCBN != IntPtr.Zero)
+ Marshal.FreeHGlobal (pStartCBN);
+ if (pDoneCBN != IntPtr.Zero)
+ Marshal.FreeHGlobal (pDoneCBN);
+ if (pDrawCBN != IntPtr.Zero)
+ Marshal.FreeHGlobal (pDrawCBN);
+ if (pCaretCBN != IntPtr.Zero)
+ Marshal.FreeHGlobal (pCaretCBN);
+
+ if (pStartCB != IntPtr.Zero)
+ Marshal.FreeHGlobal (pStartCB);
+ if (pDoneCB != IntPtr.Zero)
+ Marshal.FreeHGlobal (pDoneCB);
+ if (pDrawCB != IntPtr.Zero)
+ Marshal.FreeHGlobal (pDrawCB);
+ if (pCaretCB != IntPtr.Zero)
+ Marshal.FreeHGlobal (pCaretCB);
+ }
+
+ int DoPreeditStart (IntPtr xic, IntPtr clientData, IntPtr callData)
+ {
+ Debug.WriteLine ("DoPreeditStart");
+ XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITSTART, clientData, callData);
+ return 100;
+ }
+
+ int DoPreeditDone (IntPtr xic, IntPtr clientData, IntPtr callData)
+ {
+ Debug.WriteLine ("DoPreeditDone");
+ XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITDONE, clientData, callData);
+ return 0;
+ }
+
+ int DoPreeditDraw (IntPtr xic, IntPtr clientData, IntPtr callData)
+ {
+ Debug.WriteLine ("DoPreeditDraw");
+ XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITDRAW, clientData, callData);
+ return 0;
+ }
+
+ int DoPreeditCaret (IntPtr xic, IntPtr clientData, IntPtr callData)
+ {
+ Debug.WriteLine ("DoPreeditCaret");
+ XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITCARET, clientData, callData);
+ return 0;
+ }
+
+ public IntPtr CreateXic (IntPtr window, IntPtr xim)
+ {
+ Marshal.StructureToPtr (startCB, pStartCB, false);
+ Marshal.StructureToPtr (doneCB, pDoneCB, false);
+ Marshal.StructureToPtr (drawCB, pDrawCB, false);
+ Marshal.StructureToPtr (caretCB, pCaretCB, false);
+ IntPtr preedit = XVaCreateNestedList (0,
+ pStartCBN, pStartCB,
+ pDoneCBN, pDoneCB,
+ pDrawCBN, pDrawCB,
+ pCaretCBN, pCaretCB,
+ IntPtr.Zero);
+ return XCreateIC (xim,
+ XNames.XNInputStyle, styleOnTheSpot,
+ XNames.XNClientWindow, window,
+ XNames.XNPreeditAttributes, preedit,
+ IntPtr.Zero);
+ }
+ }
+
+ class XIMPositionContext
+ {
+ public CaretStruct Caret;
+ public int X;
+ public int Y;
+ }
+
+ internal void SetCaretPos (CaretStruct caret, IntPtr handle, int x, int y)
+ {
+ if (ximStyle != styleOverTheSpot)
+ return;
+
+ if (positionContext == null)
+ this.positionContext = new XIMPositionContext ();
+
+ positionContext.Caret = caret;
+ positionContext.X = x;
+ positionContext.Y = y + caret.Height;
+
+ MoveCurrentCaretPos ();
+ }
+
+ internal void MoveCurrentCaretPos ()
+ {
+ if (positionContext == null || ximStyle != styleOverTheSpot || client_window == IntPtr.Zero)
+ return;
+
+ int x = positionContext.X;
+ int y = positionContext.Y;
+ CaretStruct caret = positionContext.Caret;
+ IntPtr xic = GetXic (client_window);
+ if (xic == IntPtr.Zero)
+ return;
+ Widget Widget = Widget.FromHandle (client_window);
+ if (Widget == null || !Widget.IsHandleCreated)
+ return;
+ Widget = Widget.FromHandle (caret.Hwnd);
+ if (Widget == null || !Widget.IsHandleCreated)
+ return;
+ Hwnd hwnd = Hwnd.ObjectFromHandle (client_window);
+ if (!hwnd.mapped)
+ return;
+
+ int dx, dy;
+ IntPtr child;
+ lock (XlibLock) {
+ XplatUIX11.XTranslateCoordinates (display, client_window, client_window, x, y, out dx, out dy, out child);
+ }
+
+ XPoint spot = new XPoint ();
+ spot.X = (short) dx;
+ spot.Y = (short) dy;
+
+ IntPtr pSL = IntPtr.Zero;
+ try {
+ pSL = Marshal.StringToHGlobalAnsi (XNames.XNSpotLocation);
+ IntPtr preedit = XVaCreateNestedList (0, pSL, spot, IntPtr.Zero);
+ XSetICValues (xic, XNames.XNPreeditAttributes, preedit, IntPtr.Zero);
+ } finally {
+ if (pSL != IntPtr.Zero)
+ Marshal.FreeHGlobal (pSL);
+ }
+ }
+
+ private bool have_Xutf8LookupString = true;
+
+ private int LookupString (ref XEvent xevent, int len, out XKeySym keysym, out XLookupStatus status)
+ {
+ IntPtr keysym_res;
+ int res;
+
+ status = XLookupStatus.XLookupNone;
+ IntPtr xic = GetXic (client_window);
+ if (xic != IntPtr.Zero && have_Xutf8LookupString && xevent.type == XEventName.KeyPress) {
+ do {
+ try {
+ res = Xutf8LookupString (xic, ref xevent, lookup_byte_buffer, 100, out keysym_res, out status);
+ } catch (EntryPointNotFoundException) {
+ have_Xutf8LookupString = false;
+
+ // call again, this time we'll go through the non-xic clause
+ return LookupString (ref xevent, len, out keysym, out status);
+ }
+ if (status != XLookupStatus.XBufferOverflow)
+ break;
+ lookup_byte_buffer = new byte [lookup_byte_buffer.Length << 1];
+ } while (true);
+ lookup_buffer.Length = 0;
+ string s = Encoding.UTF8.GetString (lookup_byte_buffer, 0, res);
+ lookup_buffer.Append (s);
+ keysym = (XKeySym) keysym_res.ToInt32 ();
+ return s.Length;
+ } else {
+ IntPtr statusPtr = IntPtr.Zero;
+ lookup_buffer.Length = 0;
+ res = XLookupString (ref xevent, lookup_buffer, len, out keysym_res, out statusPtr);
+ keysym = (XKeySym) keysym_res.ToInt32 ();
+ return res;
+ }
+ }
+
+ [DllImport ("libX11")]
+ private static extern IntPtr XOpenIM (IntPtr display, IntPtr rdb, IntPtr res_name, IntPtr res_class);
+
+ [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr XCreateIC (IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, IntPtr terminator);
+ [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr XCreateIC (IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, string name3, IntPtr value3, IntPtr terminator);
+// [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
+// private static extern IntPtr XCreateIC (IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, string name3, IntPtr value3, string name4, IntPtr value4, IntPtr terminator);
+
+ [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr XVaCreateNestedList (int dummy, IntPtr name0, XPoint value0, IntPtr terminator);
+ [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr XVaCreateNestedList (int dummy, IntPtr name0, XPoint value0, IntPtr name1, IntPtr value1, IntPtr terminator);
+ [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr XVaCreateNestedList (int dummy, IntPtr name0, IntPtr value0, IntPtr name1, IntPtr value1, IntPtr name2, IntPtr value2, IntPtr name3, IntPtr value3, IntPtr terminator);
+
+ [DllImport ("libX11")]
+ private static extern IntPtr XCreateFontSet (IntPtr display, string name, out IntPtr list, out int count, IntPtr terminator);
+
+ [DllImport ("libX11")]
+ internal extern static void XFreeFontSet (IntPtr data);
+
+ [DllImport ("libX11")]
+ private static extern void XFreeStringList (IntPtr ptr);
+
+ //[DllImport ("libX11")]
+ //private static extern IntPtr XIMOfIC (IntPtr xic);
+
+ [DllImport ("libX11")]
+ private static extern void XCloseIM (IntPtr xim);
+
+ [DllImport ("libX11")]
+ private static extern void XDestroyIC (IntPtr xic);
+
+ [DllImport ("libX11")]
+ private static extern string XGetIMValues (IntPtr xim, string name, out IntPtr value, IntPtr terminator);
+
+ [DllImport ("libX11")]
+ private static extern string XGetICValues (IntPtr xic, string name, out EventMask value, IntPtr terminator);
+
+ [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)]
+ private static extern void XSetICValues (IntPtr xic, string name, IntPtr value, IntPtr terminator);
+
+ [DllImport ("libX11")]
+ private static extern void XSetICFocus (IntPtr xic);
+
+ [DllImport ("libX11")]
+ private static extern void XUnsetICFocus (IntPtr xic);
+
+ [DllImport ("libX11")]
+ private static extern string Xutf8ResetIC (IntPtr xic);
+
+ [DllImport ("libX11")]
+ private static extern bool XSupportsLocale ();
+
+ [DllImport ("libX11")]
+ private static extern bool XSetLocaleModifiers (string mods);
+
+ [DllImport ("libX11")]
+ internal extern static int XLookupString(ref XEvent xevent, StringBuilder buffer, int num_bytes, out IntPtr keysym, out IntPtr status);
+ [DllImport ("libX11")]
+ internal extern static int Xutf8LookupString(IntPtr xic, ref XEvent xevent, byte [] buffer, int num_bytes, out IntPtr keysym, out XLookupStatus status);
+
+ [DllImport ("libX11")]
+ private static extern IntPtr XGetKeyboardMapping (IntPtr display, byte first_keycode, int keycode_count,
+ out int keysyms_per_keycode_return);
+
+ [DllImport ("libX11")]
+ private static extern void XDisplayKeycodes (IntPtr display, out int min, out int max);
+
+ [DllImport ("libX11", EntryPoint="XKeycodeToKeysym")]
+ private static extern uint XKeycodeToKeysym (IntPtr display, int keycode, int index);
+
+ [DllImport ("libX11")]
+ private static extern int XKeysymToKeycode (IntPtr display, IntPtr keysym);
+ private static int XKeysymToKeycode (IntPtr display, int keysym) {
+ return XKeysymToKeycode(display, (IntPtr)keysym);
+ }
+
+ [DllImport ("libX11")]
+ internal extern static IntPtr XGetModifierMapping (IntPtr display);
+
+ [DllImport ("libX11")]
+ internal extern static int XFreeModifiermap (IntPtr modmap);
+
+
+ private readonly static int [] nonchar_key_vkey = new int []
+ {
+ /* unused */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* FF00 */
+ /* special keys */
+ (int) VirtualKeys.VK_BACK, (int) VirtualKeys.VK_TAB, 0, (int) VirtualKeys.VK_CLEAR, 0, (int) VirtualKeys.VK_RETURN, 0, 0, /* FF08 */
+ 0, 0, 0, (int) VirtualKeys.VK_PAUSE, (int) VirtualKeys.VK_SCROLL, 0, 0, 0, /* FF10 */
+ 0, 0, 0, (int) VirtualKeys.VK_ESCAPE, 0, 0, 0, 0, /* FF18 */
+ 0, 0, (int) VirtualKeys.VK_NONCONVERT, (int) VirtualKeys.VK_CONVERT, 0, 0, 0, 0, /* FF20 */
+ 0, 0, (int) VirtualKeys.VK_OEM_AUTO, 0, 0, 0, 0, 0, /* FF28 */
+ /* unused */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* FF30 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* FF38 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* FF40 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* FF48 */
+ /* cursor keys */
+ (int) VirtualKeys.VK_HOME, (int) VirtualKeys.VK_LEFT, (int) VirtualKeys.VK_UP, (int) VirtualKeys.VK_RIGHT, /* FF50 */
+ (int) VirtualKeys.VK_DOWN, (int) VirtualKeys.VK_PRIOR, (int) VirtualKeys.VK_NEXT, (int) VirtualKeys.VK_END,
+ 0, 0, 0, 0, 0, 0, 0, 0, /* FF58 */
+ /* misc keys */
+ (int) VirtualKeys.VK_SELECT, (int) VirtualKeys.VK_SNAPSHOT, (int) VirtualKeys.VK_EXECUTE, (int) VirtualKeys.VK_INSERT, 0, 0, 0, 0, /* FF60 */
+ (int) VirtualKeys.VK_CANCEL, (int) VirtualKeys.VK_HELP, (int) VirtualKeys.VK_CANCEL, (int) VirtualKeys.VK_CANCEL, 0, 0, 0, 0, /* FF68 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* FF70 */
+ /* keypad keys */
+ 0, 0, 0, 0, 0, 0, 0, (int) VirtualKeys.VK_NUMLOCK, /* FF78 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* FF80 */
+ 0, 0, 0, 0, 0, (int) VirtualKeys.VK_RETURN, 0, 0, /* FF88 */
+ 0, 0, 0, 0, 0, (int) VirtualKeys.VK_HOME, (int) VirtualKeys.VK_LEFT, (int) VirtualKeys.VK_UP, /* FF90 */
+ (int) VirtualKeys.VK_RIGHT, (int) VirtualKeys.VK_DOWN, (int) VirtualKeys.VK_PRIOR, (int) VirtualKeys.VK_NEXT, /* FF98 */
+ (int) VirtualKeys.VK_END, 0, (int) VirtualKeys.VK_INSERT, (int) VirtualKeys.VK_DELETE,
+ 0, 0, 0, 0, 0, 0, 0, 0, /* FFA0 */
+ 0, 0, (int) VirtualKeys.VK_MULTIPLY, (int) VirtualKeys.VK_ADD, /* FFA8 */
+ (int) VirtualKeys.VK_SEPARATOR, (int) VirtualKeys.VK_SUBTRACT, (int) VirtualKeys.VK_DECIMAL, (int) VirtualKeys.VK_DIVIDE,
+ (int) VirtualKeys.VK_NUMPAD0, (int) VirtualKeys.VK_NUMPAD1, (int) VirtualKeys.VK_NUMPAD2, (int) VirtualKeys.VK_NUMPAD3, /* FFB0 */
+ (int) VirtualKeys.VK_NUMPAD4, (int) VirtualKeys.VK_NUMPAD5, (int) VirtualKeys.VK_NUMPAD6, (int) VirtualKeys.VK_NUMPAD7,
+ (int) VirtualKeys.VK_NUMPAD8, (int) VirtualKeys.VK_NUMPAD9, 0, 0, 0, 0, /* FFB8 */
+ /* function keys */
+ (int) VirtualKeys.VK_F1, (int) VirtualKeys.VK_F2,
+ (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 */
+ (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 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* FFD0 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* FFD8 */
+ /* modifier keys */
+ 0, (int) VirtualKeys.VK_SHIFT, (int) VirtualKeys.VK_SHIFT, (int) VirtualKeys.VK_CONTROL, /* FFE0 */
+ (int) VirtualKeys.VK_CONTROL, (int) VirtualKeys.VK_CAPITAL, 0, (int) VirtualKeys.VK_MENU,
+ (int) VirtualKeys.VK_MENU, (int) VirtualKeys.VK_MENU, (int) VirtualKeys.VK_MENU, 0, 0, 0, 0, 0, /* FFE8 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* FFF0 */
+ 0, 0, 0, 0, 0, 0, 0, (int) VirtualKeys.VK_DELETE /* FFF8 */
+ };
+
+ private static readonly int [] nonchar_key_scan = new int []
+ {
+ /* unused */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF00 */
+ /* special keys */
+ 0x0E, 0x0F, 0x00, /*?*/ 0, 0x00, 0x1C, 0x00, 0x00, /* FF08 */
+ 0x00, 0x00, 0x00, 0x45, 0x46, 0x00, 0x00, 0x00, /* FF10 */
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, /* FF18 */
+ /* unused */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF20 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF28 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF30 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF38 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF40 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF48 */
+ /* cursor keys */
+ 0x147, 0x14B, 0x148, 0x14D, 0x150, 0x149, 0x151, 0x14F, /* FF50 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF58 */
+ /* misc keys */
+ /*?*/ 0, 0x137, /*?*/ 0, 0x152, 0x00, 0x00, 0x00, 0x00, /* FF60 */
+ /*?*/ 0, /*?*/ 0, 0x38, 0x146, 0x00, 0x00, 0x00, 0x00, /* FF68 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF70 */
+ /* keypad keys */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x138, 0x145, /* FF78 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF80 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x11C, 0x00, 0x00, /* FF88 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x4B, 0x48, /* FF90 */
+ 0x4D, 0x50, 0x49, 0x51, 0x4F, 0x4C, 0x52, 0x53, /* FF98 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFA0 */
+ 0x00, 0x00, 0x37, 0x4E, /*?*/ 0, 0x4A, 0x53, 0x135, /* FFA8 */
+ 0x52, 0x4F, 0x50, 0x51, 0x4B, 0x4C, 0x4D, 0x47, /* FFB0 */
+ 0x48, 0x49, 0x00, 0x00, 0x00, 0x00, /* FFB8 */
+ /* function keys */
+ 0x3B, 0x3C,
+ 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, /* FFC0 */
+ 0x57, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFC8 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFD0 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFD8 */
+ /* modifier keys */
+ 0x00, 0x2A, 0x36, 0x1D, 0x11D, 0x3A, 0x00, 0x38, /* FFE0 */
+ 0x138, 0x38, 0x138, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFE8 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFF0 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x153 /* FFF8 */
+ };
+
+ private readonly static int [] nonchar_vkey_key = new int []
+ {
+ 0, 0, 0, 0, 0, /* 00-04 */
+ 0, 0, 0, (int)XKeySym.XK_BackSpace, (int)XKeySym.XK_Tab, /* 05-09 */
+ 0, 0, (int)XKeySym.XK_Clear, (int)XKeySym.XK_Return, 0, 0, /* 0A-0F */
+ (int)XKeySym.XK_Shift_L, (int)XKeySym.XK_Control_L, (int)XKeySym.XK_Menu, 0, (int)XKeySym.XK_Caps_Lock, /* 10-14 */
+ 0, 0, 0, 0, 0, /* 15-19 */
+ 0, 0, 0, 0, 0, 0, /* 1A-1F */
+ 0, 0, 0, (int)XKeySym.XK_End, (int)XKeySym.XK_Home, /* 20-24 */
+ (int)XKeySym.XK_Left, (int)XKeySym.XK_Up, (int)XKeySym.XK_Right, (int)XKeySym.XK_Down, 0, /* 25-29 */
+ 0, 0, 0, 0, 0, 0, /* 2A-2F */
+ 0, 0, 0, 0, 0, /* 30-34 */
+ 0, 0, 0, 0, 0, /* 35-39 */
+ 0, 0, 0, 0, 0, 0, /* 3A-3F */
+ 0, 0, 0, 0, 0, /* 40-44 */
+ 0, 0, 0, 0, 0, /* 45-49 */
+ 0, 0, 0, 0, 0, 0, /* 4A-4F */
+ 0, 0, 0, 0, 0, /* 50-54 */
+ 0, 0, 0, 0, 0, /* 55-59 */
+ 0, (int)XKeySym.XK_Meta_L, (int)XKeySym.XK_Meta_R, 0, 0, 0, /* 5A-5F */
+ 0, 0, 0, 0, 0, /* 60-64 */
+ 0, 0, 0, 0, 0, /* 65-69 */
+ 0, 0, 0, 0, 0, 0, /* 6A-6F */
+ 0, 0, 0, 0, 0, /* 70-74 */
+ 0, 0, 0, 0, 0, /* 75-79 */
+ 0, 0, 0, 0, 0, 0, /* 7A-7F */
+ 0, 0, 0, 0, 0, /* 80-84 */
+ 0, 0, 0, 0, 0, /* 85-89 */
+ 0, 0, 0, 0, 0, 0, /* 8A-8F */
+ 0, 0, 0, 0, 0, /* 90-94 */
+ 0, 0, 0, 0, 0, /* 95-99 */
+ 0, 0, 0, 0, 0, 0, /* 9A-9F */
+ (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 */
+ (int)XKeySym.XK_Alt_R, 0, 0, 0, 0, /* A5-A9 */
+ 0, 0, 0, 0, 0, 0, /* AA-AF */
+ 0, 0, 0, 0, 0, /* B0-B4 */
+ 0, 0, 0, 0, 0, /* B5-B9 */
+ 0, 0, 0, 0, 0, 0, /* BA-BF */
+ 0, 0, 0, 0, 0, /* C0-C4 */
+ 0, 0, 0, 0, 0, /* C5-C9 */
+ 0, 0, 0, 0, 0, 0, /* CA-CF */
+ 0, 0, 0, 0, 0, /* D0-D4 */
+ 0, 0, 0, 0, 0, /* D5-D9 */
+ 0, 0, 0, 0, 0, 0, /* DA-DF */
+ 0, 0, 0, 0, 0, /* E0-E4 */
+ 0, 0, 0, 0, 0, /* E5-E9 */
+ 0, 0, 0, 0, 0, 0, /* EA-EF */
+ 0, 0, 0, 0, 0, /* F0-F4 */
+ 0, 0, 0, 0, 0, /* F5-F9 */
+ 0, 0, 0, 0, 0, 0 /* FA-FF */
+ };
+
+ }
+
+}
+