2008-04-07 Atsushi Enomoto <atsushi@ximian.com>
authorAtsushi Eno <atsushieno@gmail.com>
Mon, 7 Apr 2008 19:31:07 +0000 (19:31 -0000)
committerAtsushi Eno <atsushieno@gmail.com>
Mon, 7 Apr 2008 19:31:07 +0000 (19:31 -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 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). Only root
          mode works so far.

          (redoing r99172 with fix.)

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

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 7fd21bc45636ac6ebfb2cafacb4fbaca04eb4ce1..3454f1feec895915a67042fdfd3fa67f8c0a997c 100644 (file)
@@ -1,3 +1,17 @@
+2008-04-07  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 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). Only root
+          mode works so far.
+
+         (redoing r99172 with fix.)
+
 2008-04-07  Jonathan Pobst  <monkey@jpobst.com>
 
        * TreeView.cs: Center the checkbox a little better.
index 5d2351b908291c22d6823acd6ea7a96f74ded522..9cc1c48c03e0b8975751c5819475d05736c30f56 100644 (file)
@@ -98,16 +98,21 @@ 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");
+                               if (xim != IntPtr.Zero)
+                                       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,16 +733,222 @@ namespace System.Windows.Forms {
                        return 0;
                }
 
+               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. 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:
+                                       xic = CreateOnTheSpotXic (window, xim);
+                                       // XplatUIX11.XFree (preedit);
+                                       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,
+                                               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;
                }
 
+               private IntPtr CreateOverTheSpotXic (IntPtr window, IntPtr xim)
+               {
+                       IntPtr list;
+                       int count;
+                       IntPtr fontSet = XCreateFontSet (display, "*", out list, out count, IntPtr.Zero);
+                       // FIXME: give appropriate corrdinate.
+                       XPoint spot = new XPoint ();
+                       spot.X = 0;
+                       spot.Y = 100;
+                       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.XNFocusWindow, 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 ();
+                       return callbackContext.CreateXic (window, xim);
+               }
+
+               XIMCallbackContext callbackContext;
+
+               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 ()
+                       {
+                               startCB = new XIMCallback (IntPtr.Zero, DoPreeditStart);
+                               doneCB = new XIMCallback (IntPtr.Zero, DoPreeditDone);
+                               drawCB = new XIMCallback (IntPtr.Zero, DoPreeditDraw);
+                               caretCB = new XIMCallback (IntPtr.Zero, 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)
+                       {
+                               Console.WriteLine ("DoPreeditStart");
+                               return 128;
+                       }
+
+                       int DoPreeditDone (IntPtr xic, IntPtr clientData, IntPtr callData)
+                       {
+                               Console.WriteLine ("DoPreeditDone");
+                               return 0;
+                       }
+
+                       int DoPreeditDraw (IntPtr xic, IntPtr clientData, IntPtr callData)
+                       {
+                               Console.WriteLine ("DoPreeditDraw");
+                               return 0;
+                       }
+
+                       int DoPreeditCaret (IntPtr xic, IntPtr clientData, IntPtr callData)
+                       {
+                               Console.WriteLine ("DoPreeditCaret");
+                               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.XNFocusWindow, window,
+                                       XNames.XNPreeditAttributes, preedit,
+                                       IntPtr.Zero);
+                       }
+               }
+
                private int LookupString (ref XEvent xevent, int len, out XKeySym keysym, out IntPtr status)
                {
                        IntPtr keysym_res;
@@ -766,8 +977,24 @@ namespace System.Windows.Forms {
                [DllImport ("libX11")]
                private static extern IntPtr XOpenIM (IntPtr display, IntPtr rdb, IntPtr res_name, IntPtr res_class);
 
-               [DllImport ("libX11")]
+               [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 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);
@@ -778,6 +1005,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..be337ae63cef7833daff50f067890ba9e8b31e77 100644 (file)
@@ -1659,4 +1659,71 @@ 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)]
+       [Serializable]
+       internal class XPoint
+       {
+               public short X;
+               public short Y;
+       }
+
+       [StructLayout (LayoutKind.Sequential)]
+       [Serializable]
+       internal class XIMCallback
+       {
+               public IntPtr client_data;
+               public XIMProc callback;
+               [NonSerialized]
+               GCHandle gch;
+
+               public XIMCallback (IntPtr clientData, XIMProc proc)
+               {
+                       this.client_data = clientData;
+                       this.gch = GCHandle.Alloc (proc);
+                       this.callback = proc;
+               }
+
+               ~XIMCallback ()
+               {
+                       gch.Free ();
+               }
+       }
+
+       // only PreeditStartCallback requires return value though.
+       internal delegate int 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";
+       }
 }