2008-03-28 Atsushi Enomoto <atsushi@ximian.com>
authorAtsushi Eno <atsushieno@gmail.com>
Fri, 28 Mar 2008 09:14:00 +0000 (09:14 -0000)
committerAtsushi Eno <atsushieno@gmail.com>
Fri, 28 Mar 2008 09:14:00 +0000 (09:14 -0000)
        * X11Structs.cs : added couple of structs for XIM support.
        * X11Keyboard.cs : release XIM in case it failed to create XIC.
          Use consts for XNblah string.
          Add support for IM style customization and (not-yet-working)
          XIC creation for preedit-position and preedit-callback.
          Right now use MONO_WINFORMS_XIM_STYLE environment variable
          (list of: over-the-spot | on-the-spot | root). I'm still
          looking for the reason why those preedit modes don't work.

svn path=/trunk/mcs/; revision=99172

mcs/class/Managed.Windows.Forms/System.Windows.Forms/ChangeLog
mcs/class/Managed.Windows.Forms/System.Windows.Forms/X11Keyboard.cs
mcs/class/Managed.Windows.Forms/System.Windows.Forms/X11Structs.cs

index 0b7f53528fe656afff6eac837839dc1fcd28bf24..30389b16fbd3f1e8ccbf1336d3a5b045445436c0 100644 (file)
@@ -1,3 +1,14 @@
+2008-03-28  Atsushi Enomoto  <atsushi@ximian.com>
+
+       * X11Structs.cs : added couple of structs for XIM support.
+       * X11Keyboard.cs : release XIM in case it failed to create XIC.
+         Use consts for XNblah string.
+         Add support for IM style customization and (not-yet-working)
+         XIC creation for preedit-position and preedit-callback.
+         Right now use MONO_WINFORMS_XIM_STYLE environment variable
+         (list of: over-the-spot | on-the-spot | root). I'm still
+         looking for the reason why those preedit modes don't work.
+
 2008-03-27  Jonathan Pobst  <monkey@jpobst.com>
 
        * TreeView.cs: Create the scrollbars even earlier to be
index 5d2351b908291c22d6823acd6ea7a96f74ded522..6a593286b065ea1cdcc75444ed6900ac58d40253 100644 (file)
@@ -98,16 +98,20 @@ namespace System.Windows.Forms {
                                Console.Error.WriteLine ("Could not get XIM");
                        else 
                                xic = CreateXic (window, xim);
-                       if (xic != IntPtr.Zero)
+                       if (xic == IntPtr.Zero) {
+                               Console.Error.WriteLine ("Could not get XIC");
+                               XCloseIM (xim);
+                       } else {
                                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);
+                               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;
                }
 
@@ -728,13 +732,146 @@ namespace System.Windows.Forms {
                        return 0;
                }
 
+               void DoPreeditPositionStart (IntPtr xic, IntPtr clientData, IntPtr callData)
+               {
+                       // FIXME: implement
+                       Console.WriteLine ("DoPreeditPositionStart");
+               }
+
+               void DoPreeditPositionDone (IntPtr xic, IntPtr clientData, IntPtr callData)
+               {
+                       // FIXME: implement
+                       Console.WriteLine ("DoPreeditPositionDone");
+               }
+
+               void DoPreeditPositionDraw (IntPtr xic, IntPtr clientData, IntPtr callData)
+               {
+                       // FIXME: implement
+                       Console.WriteLine ("DoPreeditPositionDraw");
+               }
+
+               void DoPreeditPositionCaret (IntPtr xic, IntPtr clientData, IntPtr callData)
+               {
+                       // FIXME: implement
+                       Console.WriteLine ("DoPreeditPositionCaret");
+               }
+
+               private XIMProperties [] GetSupportedInputStyles (IntPtr xim)
+               {
+                       IntPtr stylesPtr;
+                       XGetIMValues (xim, XNames.XNQueryInputStyle, out stylesPtr, IntPtr.Zero);
+                       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 ((IntPtr) ((int) styles.supported_styles + i * 4), typeof (XIMProperties));
+                       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 = String.Empty;
+                       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)
                {
-                       xic = XCreateIC (xim, 
-                               "inputStyle", XIMProperties.XIMPreeditNothing | XIMProperties.XIMStatusNothing,
-                               "clientWindow", window,
-                               "focusWindow", window,
-                               IntPtr.Zero);
+                       foreach (XIMProperties targetStyle in GetMatchingStylesInPreferredOrder (xim)) {
+                               // FIXME: use __arglist when it gets working.
+                               switch (targetStyle) {
+                               case styleOverTheSpot:
+                                       IntPtr list;
+                                       int count;
+                                       IntPtr fontSet = XCreateFontSet (display, "*", out list, out count, IntPtr.Zero);
+                                       XPoint spot = new XPoint ();
+                                       // FIXME: provide proper position
+                                       spot.X = 0;
+                                       spot.Y = 20;
+                                       IntPtr preedit = XVaCreateNestedList (0,
+                                               XNames.XNSpotLocation, ref spot,
+                                               XNames.XNFontSet, fontSet,
+                                               IntPtr.Zero);
+                                       xic = XCreateIC (xim,
+                                               XNames.XNInputStyle, styleOverTheSpot,
+                                               XNames.XNClientWindow, window,
+                                               XNames.XNFocusWindow, window,
+                                               XNames.XNPreeditAttributes, preedit,
+                                               IntPtr.Zero);
+                                       XFreeStringList (list);
+                                       XplatUIX11.XFree (preedit);
+                                       if (xic != IntPtr.Zero)
+                                               break;
+                                       //Console.Error.WriteLine ("failed to create XIC in over-the-spot mode.");
+                                       continue;
+                               case styleOnTheSpot:
+                                       XIMCallback startCB = new XIMCallback (IntPtr.Zero, DoPreeditPositionStart);
+                                       XIMCallback doneCB = new XIMCallback (IntPtr.Zero, DoPreeditPositionDone);
+                                       XIMCallback drawCB = new XIMCallback (IntPtr.Zero, DoPreeditPositionDraw);
+                                       XIMCallback caretCB = new XIMCallback (IntPtr.Zero, DoPreeditPositionCaret);
+                                       preedit = XVaCreateNestedList (0,
+                                               XNames.XNPreeditStartCallback, ref startCB,
+                                               XNames.XNPreeditDoneCallback, ref doneCB,
+                                               XNames.XNPreeditDrawCallback, ref drawCB,
+                                               XNames.XNPreeditCaretCallback, ref caretCB,
+                                               IntPtr.Zero);
+                                       xic = XCreateIC (xim,
+                                               XNames.XNInputStyle, styleOnTheSpot,
+                                               XNames.XNPreeditAttributes, preedit,
+                                               XNames.XNClientWindow, window,
+                                               XNames.XNFocusWindow, window,
+                                               IntPtr.Zero);
+                                       XplatUIX11.XFree (preedit);
+                                       if (xic != IntPtr.Zero)
+                                               break;
+                                       //Console.Error.WriteLine ("failed to create XIC in on-the-spot mode.");
+                                       continue;
+                               case styleRoot:
+                                       xic = XCreateIC (xim,
+                                               XNames.XNInputStyle, styleRoot,
+                                               XNames.XNClientWindow, window,
+                                               XNames.XNFocusWindow, window,
+                                               IntPtr.Zero);
+                                       break;
+                               }
+                       }
+                       // fall back to root mode if all modes failed
+                       if (xic == IntPtr.Zero)
+                               xic = XCreateIC (xim,
+                                       XNames.XNInputStyle, styleRoot,
+                                       XNames.XNClientWindow, window,
+                                       XNames.XNFocusWindow, window,
+                                       IntPtr.Zero);
                        return xic;
                }
 
@@ -768,6 +905,19 @@ namespace System.Windows.Forms {
 
                [DllImport ("libX11")]
                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 XCreateIC (IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, string name3, IntPtr value3, string name4, IntPtr value4, /*string name5, IntPtr value5, */IntPtr terminator);
+
+               [DllImport ("libX11")]
+               private static extern IntPtr XVaCreateNestedList (int dummy, string name0, ref XPoint value0, string name1, IntPtr value1, IntPtr terminator);
+               [DllImport ("libX11")]
+               private static extern IntPtr XVaCreateNestedList (int dummy, /*string name0, IntPtr value0, string name1, ref XPoint value1, */string name2, ref XIMCallback value2, string name3, ref XIMCallback value3, string name4, ref XIMCallback value4, string name5, ref XIMCallback value5, IntPtr terminator);
+
+               [DllImport ("libX11")]
+               private static extern IntPtr XCreateFontSet (IntPtr display, string name, out IntPtr list, out int count, IntPtr terminator);
+
+               [DllImport ("libX11")]
+               private static extern void XFreeStringList (IntPtr ptr);
 
                [DllImport ("libX11")]
                private static extern IntPtr XIMOfIC (IntPtr xic);
@@ -778,6 +928,9 @@ namespace System.Windows.Forms {
                [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);
 
index d3b8c9f67cef133b05d1dc573f49a162b4eb6630..aace04aa6074cc85aeb5092e715fbaf9baafa225 100644 (file)
@@ -1659,4 +1659,60 @@ namespace System.Windows.Forms {
                public int nimage;     /* number of images */
                public IntPtr images;   /* array of XcursorImage pointers */
        }
+
+       [StructLayout (LayoutKind.Sequential)]
+       internal struct XIMStyles
+       {
+               public ushort count_styles;
+               public IntPtr supported_styles;
+       }
+
+       [StructLayout (LayoutKind.Sequential)]
+       internal struct XPoint
+       {
+               public short X;
+               public short Y;
+       }
+
+       [StructLayout (LayoutKind.Sequential)]
+       internal struct XIMCallback
+       {
+               public IntPtr client_data;
+               public XIMProc callback;
+
+               public XIMCallback (IntPtr clientData, XIMProc proc)
+               {
+                       this.client_data = clientData;
+                       this.callback = proc;
+               }
+       }
+
+       internal delegate void XIMProc (IntPtr xim, IntPtr clientData, IntPtr callData);
+
+       internal static class XNames
+       {
+               public const string XNVaNestedList = "XNVaNestedList";
+               public const string XNQueryInputStyle = "queryInputStyle";
+               public const string XNClientWindow = "clientWindow";
+               public const string XNInputStyle = "inputStyle";
+               public const string XNFocusWindow = "focusWindow";
+
+               // XIMPreeditCallbacks delegate names.
+               public const string XNPreeditStartCallback = "preeditStartCallback";
+               public const string XNPreeditDoneCallback = "preeditDoneCallback";
+               public const string XNPreeditDrawCallback = "preeditDrawCallback";
+               public const string XNPreeditCaretCallback = "preeditCaretCallback";
+               public const string XNPreeditStateNotifyCallback = "preeditStateNotifyCallback";
+               public const string XNPreeditAttributes = "preeditAttributes";
+               // XIMStatusCallbacks delegate names.
+               public const string XNStatusStartCallback = "statusStartCallback";
+               public const string XNStatusDoneCallback = "statusDoneCallback";
+               public const string XNStatusDrawCallback = "statusDrawCallback";
+               public const string XNStatusAttributes = "statusAttributes";
+
+               public const string XNArea = "area";
+               public const string XNAreaNeeded = "areaNeeded";
+               public const string XNSpotLocation = "spotLocation";
+               public const string XNFontSet = "fontSet";
+       }
 }