Hi guys,

I've lately solved couple of puzzles around XIM and I can enter
characters from XIM now.
I had couple of extra changes (from my experimental fixes), so I reduced
the changes and
now it is trimmed down to the attached patch, which should be without
any side effect.

Can someone review it or can I apply these changes? Those XIM support
work will go on
as there should be couple more of improvements, but incremental changes
would be
better (at least for me :).

After these changes, we'll still need:
- better Preedit/Status support (currently it has been
PreeditNothing/StatusNothing).
- some fixage for some IM engine for preedit window (I disabled one
XMapWindow() that
is however required for "atokx3" or iiimf).
- add some context menu items for international text input to TextBox
and RichTextBox.

I have some prepared answers for possible questions:
- I needed to change StringBuilder argument to byte[] in
Xutf8LookupString() because it returns
length in UTF-8 bytes and the string length cannot be retrieved with
StringBuilder.
- I had to move EnsureKeyboardInitialized() to very early stage i.e.
X11Keyboard.ctor()
because the late initialization somehow caused missing IM startup sometimes.
- XFilterEvent() is required for non-KeyPress/KeyRelease event for some
(or every?) IM engine(s)
especially as for ClientMessageEvent.

Atsushi Eno

Index: System.Windows.Forms/X11Keyboard.cs
===================================================================
--- System.Windows.Forms/X11Keyboard.cs	(revision 98231)
+++ System.Windows.Forms/X11Keyboard.cs	(working copy)
@@ -43,7 +43,9 @@
 		private IntPtr display;
 		private IntPtr window;
 		private IntPtr xic;
+		private EventMask xic_event_mask = EventMask.NoEventMask;
 		private StringBuilder lookup_buffer;
+		private byte [] utf8_buffer;
 		private int min_keycode, max_keycode, keysyms_per_keycode, syms;
 		private int [] keyc2vkey = new int [256];
 		private int [] keyc2scan = new int [256];
@@ -61,8 +63,18 @@
 			this.display = display;
 			this.window = window;
 			lookup_buffer = new StringBuilder (24);
+			EnsureLayoutInitialized ();
 		}
 
+		~X11Keyboard ()
+		{
+			if (xic != IntPtr.Zero) {
+				IntPtr im = XIMOfIC (xic);
+				XDestroyIC (xic);
+				XCloseIM (im);
+			}
+		}
+
 		public void EnsureLayoutInitialized ()
 		{
 			if (initialized)
@@ -86,10 +98,27 @@
 				Console.Error.WriteLine ("Could not get XIM");
 			else 
 				xic = CreateXic (window, xim);
-			
+			if (xic != IntPtr.Zero)
+				utf8_buffer = new byte [100];
+			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;
+			xic_event_mask |= mask;
+			XplatUIX11.XSelectInput(display, window, new IntPtr ((int) xic_event_mask));
+			// FIXME: without it some input methods do not show its UI (but it results in
+			// obstacle, so am disabling it).
+			// XplatUIX11.XMapWindow (display, window);
 			initialized = true;
 		}
 
+		internal IntPtr Window {
+			get { return window; }
+		}
+
+		public EventMask KeyEventMask {
+			get { return xic_event_mask; }
+		}
+		
 		public Keys ModifierKeys {
 			get {
 				Keys keys = Keys.None;
@@ -134,8 +163,6 @@
 
 		public void KeyEvent (IntPtr hwnd, XEvent xevent, ref MSG msg)
 		{
-			EnsureLayoutInitialized ();
-
 			XKeySym keysym;
 			int ascii_chars;
 
@@ -155,8 +182,9 @@
 			int event_time = (int)xevent.KeyEvent.time;
 
 			if (status == (IntPtr) 2) {
-				// Copy chars into a globally accessible var, i don't think
-				// this var is exposed anywhere though, so we can just ignore this
+				// do not ignore those inputs. They are mostly from XIM.
+				msg = SendImeComposition (lookup_buffer.ToString (0, lookup_buffer.Length));
+				msg.hwnd = hwnd;
 				return;
 			}
 
@@ -361,6 +389,22 @@
 			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, KeybdEventFlags dw_flags, int time)
 		{
 			Msg message;
@@ -450,8 +494,6 @@
 
 		public int EventToVkey (XEvent e)
 		{
-			EnsureLayoutInitialized ();
-
 			IntPtr status;
 			XKeySym ks;
 
@@ -702,14 +744,23 @@
 			int res;
 
 			status = IntPtr.Zero;
-			lookup_buffer.Length = 0;
-			if (xic != IntPtr.Zero)
-				res = Xutf8LookupString (xic, ref xevent, lookup_buffer, len, out keysym_res,  out status);
-			else
+			if (xic != IntPtr.Zero) {
+				do {
+					res = Xutf8LookupString (xic, ref xevent, utf8_buffer, 100, out keysym_res,  out status);
+					if ((int) status != -1) // XLookupBufferOverflow
+						break;
+					utf8_buffer = new byte [utf8_buffer.Length << 1];
+				} while (true);
+				lookup_buffer.Length = 0;
+				string s = Encoding.UTF8.GetString (utf8_buffer, 0, res);
+				lookup_buffer.Append (s);
+				keysym = (XKeySym) keysym_res.ToInt32 ();
+				return s.Length;
+			} else {
 				res = XLookupString (ref xevent, lookup_buffer, len, out keysym_res, IntPtr.Zero);
-
-			keysym = (XKeySym) keysym_res.ToInt32 ();
-			return res;
+				keysym = (XKeySym) keysym_res.ToInt32 ();
+				return res;
+			}
 		}
 
 		[DllImport ("libX11")]
@@ -719,6 +770,18 @@
 		private static extern IntPtr XCreateIC (IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, string name3, IntPtr value3, IntPtr terminator);
 
 		[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 XGetICValues (IntPtr xic, string name, out EventMask value, IntPtr terminator);
+
+		[DllImport ("libX11")]
 		private static extern void XSetICFocus (IntPtr xic);
 
 		[DllImport ("libX11")]
@@ -733,29 +796,8 @@
 		[DllImport ("libX11")]
 		internal extern static int XLookupString(ref XEvent xevent, StringBuilder buffer, int num_bytes, out IntPtr keysym, IntPtr status);
 		[DllImport ("libX11")]
-		internal extern static int Xutf8LookupString(IntPtr xic, ref XEvent xevent, StringBuilder buffer, int num_bytes, out IntPtr keysym, out IntPtr status);
+		internal extern static int Xutf8LookupString(IntPtr xic, ref XEvent xevent, byte [] buffer, int num_bytes, out IntPtr keysym, out IntPtr status);
 
-		internal static int Xutf8LookupString (IntPtr xic, ref XEvent xevent, StringBuilder buffer, int num_bytes, out XKeySym keysym, out IntPtr status) {
-			IntPtr	keysym_ret;
-			int	ret;
-
-			ret = Xutf8LookupString (xic, ref xevent, buffer, num_bytes, out keysym_ret, out status);
-
-			keysym = (XKeySym)keysym_ret.ToInt32();
-
-			return ret;
-		}
-
-		internal static int XLookupString (ref XEvent xevent, StringBuilder buffer, int num_bytes, out XKeySym keysym, IntPtr status) {
-			IntPtr	keysym_ret;
-			int	ret;
-
-			ret = XLookupString (ref xevent, buffer, num_bytes, out keysym_ret, status);
-			keysym = (XKeySym)keysym_ret.ToInt32();
-
-			return ret;
-		}
-
 		[DllImport ("libX11")]
 		private static extern IntPtr XGetKeyboardMapping (IntPtr display, byte first_keycode, int keycode_count, 
 				out int keysyms_per_keycode_return);
Index: System.Windows.Forms/XplatUIX11.cs
===================================================================
--- System.Windows.Forms/XplatUIX11.cs	(revision 98231)
+++ System.Windows.Forms/XplatUIX11.cs	(working copy)
@@ -509,7 +509,7 @@
 				SetupAtoms();
 
 				// Grab atom changes off the root window to catch certain WM events
-				XSelectInput(DisplayHandle, RootWindow, new IntPtr ((int)EventMask.PropertyChangeMask));
+				XSelectInput(DisplayHandle, RootWindow, new IntPtr ((int) (EventMask.PropertyChangeMask | Keyboard.KeyEventMask)));
 
 				// Handle any upcoming errors
 				ErrorHandler = new XErrorHandler(HandleError);
@@ -1561,11 +1561,17 @@
 
 					XNextEvent (DisplayHandle, ref xevent);
 
-					if (xevent.AnyEvent.type == XEventName.KeyPress) {
+					if (xevent.AnyEvent.type == XEventName.KeyPress ||
+					    xevent.AnyEvent.type == XEventName.KeyRelease) {
 						if (XFilterEvent(ref xevent, FosterParent)) {
+							// probably here we could raise WM_IME_KEYDOWN and
+							// WM_IME_KEYUP, but I'm not sure it is worthy.
 							continue;
 						}
 					}
+
+					else if (XFilterEvent (ref xevent, IntPtr.Zero))
+						continue;
 				}
 
 				hwnd = Hwnd.GetObjectFromWindow(xevent.AnyEvent.window);
@@ -2672,9 +2678,9 @@
 			}
 
 			lock (XlibLock) {
-				XSelectInput(DisplayHandle, hwnd.whole_window, new IntPtr ((int)(SelectInputMask | EventMask.StructureNotifyMask | EventMask.PropertyChangeMask)));
+				XSelectInput(DisplayHandle, hwnd.whole_window, new IntPtr ((int)(SelectInputMask | EventMask.StructureNotifyMask | EventMask.PropertyChangeMask | Keyboard.KeyEventMask)));
 				if (hwnd.whole_window != hwnd.client_window)
-					XSelectInput(DisplayHandle, hwnd.client_window, new IntPtr ((int)(SelectInputMask | EventMask.StructureNotifyMask)));
+					XSelectInput(DisplayHandle, hwnd.client_window, new IntPtr ((int)(SelectInputMask | EventMask.StructureNotifyMask | Keyboard.KeyEventMask)));
 			}
 
 			if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_TOPMOST)) {
@@ -3059,6 +3065,21 @@
 
 		internal override IntPtr DefWndProc(ref Message msg) {
 			switch ((Msg)msg.Msg) {
+				
+				case Msg.WM_IME_COMPOSITION:
+					string s = Keyboard.GetCompositionString ();
+					foreach (char c in s)
+						SendMessage (msg.HWnd, Msg.WM_IME_CHAR, (IntPtr) c, msg.LParam);
+					return IntPtr.Zero;
+
+				case Msg.WM_IME_CHAR:
+					int c = (int) msg.WParam;
+					// On Windows API it sends two WM_CHAR messages for each byte, but
+					// I wonder if it is worthy to emulate it (also no idea how to 
+					// reconstruct those bytes into chars).
+					SendMessage (msg.HWnd, Msg.WM_CHAR, msg.WParam, msg.LParam);
+					return IntPtr.Zero;
+
 				case Msg.WM_PAINT: {
 					Hwnd hwnd;
 
@@ -5122,6 +5143,7 @@
 
 			prev_focus_window = FocusWindow;
 			FocusWindow = hwnd.client_window;
+			Keyboard.FocusIn (FocusWindow);
 
 			if (prev_focus_window != IntPtr.Zero) {
 				SendMessage(prev_focus_window, Msg.WM_KILLFOCUS, FocusWindow, IntPtr.Zero);
_______________________________________________
Mono-winforms-list maillist  -  [email protected]
http://lists.ximian.com/mailman/listinfo/mono-winforms-list

Reply via email to