b52eae9f847c85c2608f9971a16517b4d307e2fe
[chise/xemacs-chise.git.1] / src / input-method-xlib.c
1 /* Various functions for X11R5+ input methods, using the Xlib interface.
2    Copyright (C) 1996 Sun Microsystems.
3
4 This file is part of XEmacs.
5
6 XEmacs is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
9 later version.
10
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with XEmacs; see the file COPYING.  If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.  */
20
21 /* Synched up with: Not in FSF. */
22
23 /* Written by Martin Buchholz. */
24
25 /* This file implements an interface to X input methods, available
26    with X11R5 and above.  See O'Reilly, Xlib programmer's guide,
27    and X11 R6 release guide chapters on internationalized input,
28    for further details */
29
30 /*
31   Policy:
32
33   The XIM is of the device, by the device, for the device.
34   The XIC is of each frame, by each frame, for each frame.
35   The exceptions are:
36       1.  Activate XICs on poor frames when the XIM is back.
37       2.  Deactivate all the XICs when the XIM go down.
38
39   Methods:
40
41     -  Register a callback for an XIM when the X device is being initialized.
42        XIM_init_device (d) { XRegisterIMInstantiateCallback (); }
43        The "XRegisterIMInstantiateCallback" is called when an XIM become
44        available on the X display.
45
46     -  Catch the XIC when the frame is being initialized if XIM was available.
47        XIM_init_frame (f) { ... XCreateIC (); ... }
48
49     -  Release the XIC when the frame is being closed.
50        XIM_delete_frame (f) { ... FRAME_X_XIC (f) = NULL; ... }
51        "XIM_delete_frame" is a "DestroyCallback" function declared in
52        XIM_init_frame ();
53
54     -  Release all the XICs when the XIM was down accidentally.
55        In IMDestroyCallback:
56            DEVICE_FRAME_LOOP (...) { FRAME_X_XIC (f) = NULL; }
57
58     -  Re-enable XIC for all the frames which doesn't have XIC when the XIM
59        is back.
60        In IMInstantiateCallback:
61            DEVICE_FRAME_LOOP (...) { XIM_init_frame (f); }
62
63
64   Note:
65
66     -  Currently, we don't use XDestroyIC because of _XimProtoCloseIM
67        (internally registered as im->methods->close) does "Xfree (ic)".
68
69  */
70
71 #include <config.h>
72 #include "lisp.h"
73 #include <X11/Xlocale.h>        /* More portable than <locale.h> ? */
74 #include "frame.h"
75 #include "device.h"
76 #include "window.h"
77 #include "buffer.h"
78 #include "console-x.h"
79 #include "EmacsFrame.h"
80 #include "events.h"
81
82 #ifdef THIS_IS_X11R6
83 #include <X11/IntrinsicP.h>
84 #include <X11/Xaw/XawImP.h>
85 #endif
86
87 #ifndef XIM_XLIB
88 #error  XIM_XLIB is not defined??
89 #endif
90
91 Lisp_Object Qxim_xlib;
92 #define xim_warn(str) warn_when_safe (Qxim_xlib, Qwarning, str);
93 #define xim_warn1(fmt, str) warn_when_safe (Qxim_xlib, Qwarning, fmt, str);
94 #define xim_info(str) warn_when_safe (Qxim_xlib, Qinfo, str);
95
96 /* Get/Set IC values for just one attribute */
97 #ifdef DEBUG_XEMACS
98 #define XIC_Value(Get_Set, xic, name, attr, value)                      \
99 do {                                                                    \
100   char *bad_arg;                                                        \
101   XVaNestedList list = XVaCreateNestedList (0, attr, value, NULL);      \
102   if ((bad_arg = X##Get_Set##ICValues (xic, name, list, NULL)) != NULL) \
103     stderr_out ("X" #Get_Set "ICValues " "bad Arg: %s\n", bad_arg);     \
104   XFree (list);                                                         \
105 } while (0)
106 #else /* ! DEBUG_XEMACS */
107 #define XIC_Value(Get_Set, xic, name, attr, value)                      \
108 do {                                                                    \
109   XVaNestedList list = XVaCreateNestedList (0, attr, value, NULL);      \
110   X##Get_Set##ICValues (xic, name, list, NULL);                         \
111   XFree (list);                                                         \
112 } while (0)
113 #endif /* DEBUG_XEMACS */
114
115 static char DefaultXIMStyles[] =
116 "XIMPreeditPosition|XIMStatusArea\n"
117 "XIMPreeditPosition|XIMStatusNone\n"
118 "XIMPreeditPosition|XIMStatusNothing\n"
119 "XIMPreeditNothing|XIMStatusArea\n"
120 "XIMPreeditNothing|XIMStatusNothing\n"
121 "XIMPreeditNothing|XIMStatusNone\n"
122 "XIMPreeditNone|XIMStatusArea\n"
123 "XIMPreeditNone|XIMStatusNothing\n"
124 "XIMPreeditNone|XIMStatusNone";
125
126 static Boolean xim_initted = False;
127
128 static XIMStyle best_style (XIMStyles *user, XIMStyles *xim);
129
130 void
131 Initialize_Locale (void)
132 {
133   char *locale;
134
135   /* dverna - Nov. 98: ### DON'T DO THIS !!! The default XtLanguageProc
136      routine calls setlocale(LC_ALL, lang) which fucks up our lower-level
137      locale management, and especially the value of LC_NUMERIC. Anyway, since
138      at this point, we don't know yet whether we're gonna need an X11 frame,
139      we should really do it manually and not use Xlib's dumb default routine */
140   /*XtSetLanguageProc (NULL, (XtLanguageProc) NULL, NULL);*/
141   if ((locale = setlocale (LC_ALL, "")) == NULL)
142     {
143       xim_warn ("Can't set locale.\n"
144                 "Using C locale instead.\n");
145       putenv ("LANG=C");
146       putenv ("LC_ALL=C");
147       if ((locale = setlocale (LC_ALL, "C")) == NULL)
148         {
149           xim_warn ("Can't even set locale to `C'!\n");
150           return;
151         }
152     }
153
154   if (!XSupportsLocale ())
155     {
156       xim_warn1 ("X Windows does not support locale `%s'\n"
157                  "Using C Locale instead\n", locale);
158       putenv ("LANG=C");
159       putenv ("LC_ALL=C");
160       if ((locale = setlocale (LC_ALL, "C")) == NULL)
161         {
162           xim_warn ("Can't even set locale to `C'!\n");
163           return;
164         }
165       if (!XSupportsLocale ())
166         {
167           xim_warn ("X Windows does not even support locale `C'!\n");
168           return;
169         }
170     }
171
172   setlocale(LC_NUMERIC, "C");
173
174   if (XSetLocaleModifiers ("") == NULL)
175     {
176       xim_warn ("XSetLocaleModifiers(\"\") failed\n"
177                 "Check the value of the XMODIFIERS environment variable.\n");
178     }
179 }
180
181 #ifdef THIS_IS_X11R6 /* Callbacks for IM are supported from X11R6 or later. */
182 /* Called from when XIM is destroying.
183    Clear all the XIC when the XIM was destroying... */
184 static void
185 IMDestroyCallback (XIM im, XPointer client_data, XPointer call_data)
186 {
187   struct device *d = (struct device *)client_data;
188   Lisp_Object tail;
189
190   DEVICE_FRAME_LOOP (tail, d)
191     {
192       struct frame *target_frame = XFRAME (XCAR (tail));
193       if (FRAME_X_P (target_frame) && FRAME_X_XIC (target_frame))
194         {
195           /* XDestroyIC (FRAME_X_XIC (target_frame)); */
196           FRAME_X_XIC (target_frame) = NULL;
197         }
198     }
199
200   DEVICE_X_XIM (d) = NULL;
201   xim_initted = False;
202   return;
203 }
204
205 /* This is registered in XIM_init_device (when DEVICE is initializing).
206    This activates XIM when XIM becomes available. */
207 static void
208 IMInstantiateCallback (Display *dpy, XPointer client_data, XPointer call_data)
209 {
210   struct device *d = (struct device *)client_data;
211   XIM xim;
212   char *name, *class;
213   XIMCallback ximcallback;
214   Lisp_Object tail;
215
216   /* if no xim is presented, initialize xim ... */
217   if ( xim_initted == False )
218     {
219       xim_initted = True;
220       XtGetApplicationNameAndClass (dpy, &name, &class);
221       DEVICE_X_XIM (d) = xim = XOpenIM (dpy, XtDatabase (dpy), name, class);
222
223       /* destroy callback for im */
224       ximcallback.callback = IMDestroyCallback;
225       ximcallback.client_data = (XPointer) d;
226       XSetIMValues (xim, XNDestroyCallback, &ximcallback, NULL);
227     }
228
229   /* activate XIC on all the X frames... */
230   DEVICE_FRAME_LOOP (tail, d)
231     {
232       struct frame *target_frame = XFRAME (XCAR (tail));
233       if (FRAME_X_P (target_frame) && !FRAME_X_XIC (target_frame))
234         {
235           XIM_init_frame (target_frame);
236         }
237     }
238   return;
239 }
240 #endif /* if THIS_IS_X11R6 */
241
242 /* Initialize XIM for X device.
243    Register the use of XIM using XRegisterIMInstantiateCallback. */
244 void
245 XIM_init_device (struct device *d)
246 {
247 #ifdef THIS_IS_X11R6
248   DEVICE_X_XIM (d) = NULL;
249   XRegisterIMInstantiateCallback (DEVICE_X_DISPLAY (d), NULL, NULL, NULL,
250                                   IMInstantiateCallback, (XPointer) d);
251   return;
252 #else
253   Display *dpy = DEVICE_X_DISPLAY (d);
254   char *name, *class;
255   XIM xim;
256
257   XtGetApplicationNameAndClass (dpy, &name, &class);
258   DEVICE_X_XIM (d) = xim = XOpenIM (dpy, XtDatabase (dpy), name, class);
259   if (xim == NULL)
260     {
261       xim_warn ("XOpenIM() failed...no input server available\n");
262       return;
263     }
264   else
265     {
266       XGetIMValues (xim, XNQueryInputStyle, &DEVICE_X_XIM_STYLES (d), NULL);
267       return;
268     }
269 #endif
270 }
271
272
273 /*
274  * For the frames
275  */
276
277 /* Callback for the deleting frame. */
278 static void
279 XIM_delete_frame (Widget w, XtPointer client_data, XtPointer call_data)
280 {
281   struct frame *f = (struct frame *) client_data;
282   struct device *d = XDEVICE (FRAME_DEVICE (f));
283
284   if (DEVICE_X_XIM (d))
285     {
286       if (FRAME_X_XIC (f))
287         {
288           XDestroyIC (FRAME_X_XIC (f));
289           FRAME_X_XIC (f) = NULL;
290         }
291     }
292   return;
293 }
294
295 /* Initialize XIC for new frame.
296    Create an X input context (XIC) for this frame. */
297 void
298 XIM_init_frame (struct frame *f)
299 {
300   struct device *d = XDEVICE (FRAME_DEVICE (f));
301   XIM xim;
302   Widget w = FRAME_X_TEXT_WIDGET (f);
303   Window win = XtWindow (w);
304   XRectangle p_area = {0,0,1,1}, s_area = {0,0,1,1};
305   XPoint spot = {0,0};
306   XIMStyle style;
307   XVaNestedList p_list, s_list;
308   typedef struct
309   {
310     XIMStyles styles;
311     XFontSet  fontset;
312     Pixel     fg;
313     Pixel     bg;
314     char      *inputmethod;
315   } xic_vars_t;
316   xic_vars_t xic_vars;
317   XIC xic;
318
319 #define res(name, class, representation, field, default_value) \
320   { name, class, representation, sizeof(xic_vars.field), \
321      XtOffsetOf(xic_vars_t, field), XtRString, default_value }
322
323   static XtResource resources[] =
324   {
325     /*  name              class          represent'n   field    default value */
326 #ifdef THIS_IS_X11R6
327     res(XtNinputMethod,   XtCInputMethod, XtRString,   inputmethod, (XtPointer) NULL),
328 #endif
329     res(XtNximStyles,     XtCXimStyles,  XtRXimStyles, styles,  (XtPointer) DefaultXIMStyles),
330     res(XtNfontSet,       XtCFontSet,    XtRFontSet,   fontset, (XtPointer) XtDefaultFontSet),
331     res(XtNximForeground, XtCForeground, XtRPixel,     fg,      (XtPointer) XtDefaultForeground),
332     res(XtNximBackground, XtCBackground, XtRPixel,     bg,      (XtPointer) XtDefaultBackground)
333   };
334
335
336   xim = DEVICE_X_XIM (d);
337
338   if (!xim)
339     {
340       xim_info ("X Input Method open failed. Waiting for an XIM to be enabled.\n");
341       return;
342     }
343
344   w = FRAME_X_TEXT_WIDGET (f);
345
346   /*
347    * initialize XIC
348    */
349   if (FRAME_X_XIC (f)) return;
350   XtGetApplicationResources (w, &xic_vars,
351                              resources, XtNumber (resources),
352                              NULL, 0);
353   if (!xic_vars.fontset)
354     {
355       xim_warn ("Can't get fontset resource for Input Method\n");
356       FRAME_X_XIC (f) = NULL;
357       return;
358     }
359
360   /* construct xic */
361   XGetIMValues (xim, XNQueryInputStyle, &DEVICE_X_XIM_STYLES(d), NULL);
362   FRAME_X_XIC_STYLE (f) = style =
363     best_style (&xic_vars.styles, (XIMStyles *)DEVICE_X_XIM_STYLES(d));
364
365   p_list = XVaCreateNestedList (0,
366                                 XNArea,         &p_area,
367                                 XNSpotLocation, &spot,
368                                 XNForeground,   xic_vars.fg,
369                                 XNBackground,   xic_vars.bg,
370                                 XNFontSet,      xic_vars.fontset,
371                                 NULL);
372
373   s_list = XVaCreateNestedList (0,
374                                 XNArea,         &s_area,
375                                 XNForeground,   xic_vars.fg,
376                                 XNBackground,   xic_vars.bg,
377                                 XNFontSet,      xic_vars.fontset,
378                                 NULL);
379
380   FRAME_X_XIC (f) = xic =
381     XCreateIC (xim,
382                XNInputStyle, style,
383                XNClientWindow, win,
384                XNFocusWindow, win,
385                XNPreeditAttributes, p_list,
386                XNStatusAttributes, s_list,
387                NULL);
388   XFree (p_list);
389   XFree (s_list);
390
391   if (!xic)
392     {
393       xim_warn ("Warning: XCreateIC failed.\n");
394       return;
395     }
396
397   if (style & XIMPreeditPosition)
398     {
399       XPoint *frame_spot = &(FRAME_X_XIC_SPOT(f));
400       frame_spot->x = frame_spot->y = -1;
401     }
402
403   XIM_SetGeometry (f);
404
405   XSetICFocus (xic);
406
407 #ifdef THIS_IS_X11R6
408   /* when frame is going to be destroyed (closed) */
409   XtAddCallback (FRAME_X_TEXT_WIDGET(f), XNDestroyCallback,
410                  XIM_delete_frame, (XtPointer)f);
411 #endif
412 }
413
414
415 void
416 XIM_SetGeometry (struct frame *f)
417 {
418   XIC      xic   = FRAME_X_XIC (f);
419   XIMStyle style = FRAME_X_XIC_STYLE (f);
420   XRectangle area;
421
422   if (!xic || !f)
423     return;
424
425   if (style & XIMStatusArea)
426     {
427       /* Place Status Area in bottom right corner */
428       /* Negotiate geometry of status area */
429       /* See O'Reilly Xlib XIM chapter (but beware, it's buggy) */
430       XRectangle *needed;
431
432       /* If input method has existing status area, use its current size */
433       /* The following at least works for Sun's htt */
434       area.x = area.y = area.width = area.height = 0;
435       XIC_Value (Set, xic, XNStatusAttributes, XNAreaNeeded, &area);
436       XIC_Value (Get, xic, XNStatusAttributes, XNAreaNeeded, &needed);
437       if (needed->width == 0)   /* Use XNArea instead of XNAreaNeeded */
438         XIC_Value (Get, xic, XNStatusAttributes, XNArea, &needed);
439
440       area.width  = needed->width;
441       area.height = needed->height;
442       area.x = FRAME_RIGHT_BORDER_START  (f) - area.width;
443       area.y = FRAME_BOTTOM_BORDER_START (f) - area.height;
444
445 #ifdef DEBUG_XIM
446       stderr_out ("Putting StatusArea in x=%d y=%d w=%d h=%d\n",
447                   area.x, area.y, area.width, area.height);
448 #endif /* DEBUG_XIM */
449
450       XIC_Value (Set, xic, XNStatusAttributes, XNArea, &area);
451     }
452
453   if (style & XIMPreeditPosition)
454     {
455       /* Set Preedit Area to whole frame size (sans border) */
456       /* We include the border because Preedit window might be larger
457          than display line at edge. #### FIX: we should adjust to make
458          sure that there is always room for the spot sub-window */
459       area.x      = FRAME_LEFT_BORDER_START (f);
460       area.y      = FRAME_TOP_BORDER_START  (f);
461       area.width  = FRAME_RIGHT_BORDER_END  (f) - area.x;
462       area.height = FRAME_BOTTOM_BORDER_END (f) - area.y;
463       XIC_Value(Set, xic, XNPreeditAttributes, XNArea, &area);
464     }
465
466 #ifdef DEBUG_XIM
467   describe_XIC (xic);
468 #endif
469 }
470
471 void
472 XIM_SetSpotLocation (struct frame *f, int x, int y)
473 {
474   XIC xic = FRAME_X_XIC (f);
475   XPoint *spot = &(FRAME_X_XIC_SPOT (f));
476
477   /* Only care if we have a valid XIC using Over the Spot in
478    * a different location */
479   if (!xic ||
480       !(FRAME_X_XIC_STYLE (f) & XIMPreeditPosition) ||
481       (spot->x == (short) x &&
482        spot->y == (short) y))
483     return;
484
485   spot->x = (short) x;
486   spot->y = (short) y;
487
488   /* ### FIX: Must make sure spot fits within Preedit Area */
489   XIC_Value (Set, xic, XNPreeditAttributes, XNSpotLocation, spot);
490 #ifdef DEBUG_XIM
491   stderr_out ("Spot: %d %d\n", spot->x, spot->y);
492 #endif
493 }
494
495 void
496 XIM_focus_event (struct frame *f, int in_p)
497 {
498   if (FRAME_X_XIC (f) /* && FRAME_X_XIM_REGISTERED(f) */)
499     (in_p ? XSetICFocus : XUnsetICFocus) (FRAME_X_XIC (f));
500 }
501
502 #if 0
503 #define XIM_Composed_Text_BUFSIZE 64
504 typedef struct XIM_Composed_Text
505 {
506   int size;
507   wchar_t data [XIM_Composed_Text_BUFSIZE];
508 } XIM_Composed_Text;
509
510 static XIM_Composed_Text composed_input_buf = {XIM_Composed_Text_BUFSIZE, {0}};
511 Window main_window;
512
513 /* get_XIM_input -- Process results of input method composition.
514
515    This function copies the results of the input method composition to
516    composed_input_buf.  Then for each character, a custom event of type
517    wc_atom is sent with the character as its data.
518
519    It is probably more efficient to copy the composition results to some
520    allocated memory and send a single event pointing to that memory.
521    That would cut down on the event processing as well as allow quick
522    insertion into the buffer of the whole string.  It might require some
523    care, though, to avoid fragmenting memory through the allocation and
524    freeing of many small chunks.  Maybe the existing system for
525    (single-byte) string allocation can be used, multiplying the length by
526    sizeof (wchar_t) to get the right size.
527 */
528 void
529 get_XIM_input (XKeyPressedEvent *x_key_event, XIC ic, Display *dpy)
530 {
531   KeySym keysym;
532   Status status;
533   int len;
534   int i;
535   XClientMessageEvent new_event;
536
537 retry:
538   len = XwcLookupString (ic, x_key_event, composed_input_buf.data,
539                          composed_input_buf.size, &keysym, &status);
540   switch (status)
541     {
542     case XBufferOverflow:
543       /* GROW_WC_STRING (&composed_input_buf, 32); mrb */
544       goto retry;
545     case XLookupChars:
546       break;
547     default:
548       abort ();
549     }
550
551   new_event.type = ClientMessage;
552   new_event.display = x_key_event->display;
553   new_event.window = x_key_event->window;
554   new_event.message_type = wc_atom;
555   new_event.format = 32;  /* 32-bit wide data */
556   new_event.data.l[2] = new_event.data.l[3] = new_event.data.l[4] = 0L;
557   new_event.data.l[0] = x_key_event->time;
558   for (i = 0; i < len; i++)
559     {
560       new_event.data.l[1] = ((wchar_t *) composed_input_buf.data)[i];
561       XSendEvent (display, main_window, False, 0L, (XEvent *) &new_event);
562     }
563 }
564 #endif /* 0 */
565
566 /* ============================================================== */
567 /* X input method style determination */
568 /* ============================================================== */
569
570 #if 0
571 #define done(type, value)                \
572   if (toVal->addr != NULL) {             \
573     if (toVal->size < sizeof(type)) {    \
574       toVal->size = sizeof(type);        \
575       return False;                      \
576     }                                    \
577     *(type*)toVal->addr = (value);       \
578   } else {                               \
579     static type static_val;              \
580     static_val = (value);                \
581     toVal->addr = (XPointer)&static_val; \
582   }                                      \
583   toVal->size = sizeof(type);            \
584   return True /* Caller supplies `;' */
585 #endif /* 0 */
586
587 /*
588  * This is a standard Xt type converter, except that the caller MUST
589  * supply a proper non-NULL toVal XIMStyles structure that we will
590  * fill in.
591  *
592  * fromVal points to a string like
593  *
594  "XIMPreeditPosition|XIMStatusArea,
595  XIMPreeditPosition|XIMStatusNothing
596  XIMPreeditNothing|XIMStatusNothing"
597  *
598  * This is converted in the obvious way to a XIMStyles structure.
599  *
600  * mrb: #### Fix this to handle Motif-style specifications for
601  * XIMStyles as well: overTheSpot, rootWindow, none */
602
603 /* XtTypeConverter */
604 Boolean
605 EmacsXtCvtStringToXIMStyles (
606   Display     *dpy,
607   XrmValuePtr  args,
608   Cardinal    *num_args,
609   XrmValuePtr  fromVal,
610   XrmValuePtr  toVal,
611   XtPointer   *converter_data)
612 {
613 #define STYLE_INFO(style) { style, #style, sizeof(#style) }
614   static struct XIMStyleInfo
615   {
616     CONST XIMStyle style;
617     CONST char   * CONST name;
618     CONST int      namelen;
619   } emacs_XIMStyleInfo[] = {
620     STYLE_INFO (XIMPreeditPosition|XIMStatusArea),
621     STYLE_INFO (XIMPreeditPosition|XIMStatusNothing),
622     STYLE_INFO (XIMPreeditPosition|XIMStatusNone),
623     STYLE_INFO (XIMPreeditNothing|XIMStatusArea),
624     STYLE_INFO (XIMPreeditNothing|XIMStatusNothing),
625     STYLE_INFO (XIMPreeditNothing|XIMStatusNone),
626     STYLE_INFO (XIMPreeditNone|XIMStatusArea),
627     STYLE_INFO (XIMPreeditNone|XIMStatusNothing),
628     STYLE_INFO (XIMPreeditNone|XIMStatusNone)
629   };
630 #undef STYLE_INFO
631
632   char *s   = (char *) fromVal->addr;
633   char *end = s + fromVal->size;
634   XIMStyles * CONST p = (XIMStyles *) toVal->addr;
635   CONST char * CONST delimiter = " \t\n\r:;," ;
636   CONST int  max_styles = XtNumber(emacs_XIMStyleInfo);
637   int i;
638   char *c;
639
640 #ifdef DEBUG_XIM
641   stderr_out ("EmacsCvtStringToXIMStyles called with size=%d, string=\"%s\"\n",
642               fromVal->size, (char *) fromVal->addr);
643 #endif /* DEBUG_XIM */
644
645   if (*num_args != 0)
646     {
647       XtAppContext the_app_con = XtDisplayToApplicationContext (dpy);
648       XtAppWarningMsg(the_app_con, "wrongParameters", "cvtStringToXIMStyle",
649                       "XtToolkitError",
650                       "String to XIMStyle conversion requires exactly 0 parameters",
651                       (String *)NULL, (Cardinal *)NULL);
652       return False;
653     }
654
655 #ifdef DEBUG_XEMACS
656   /* Make sure caller is giving us good data */
657   assert (fromVal->addr != NULL);
658   assert (fromVal->size == strlen(fromVal->addr)+1);
659   assert (toVal->addr   != NULL);
660   assert (toVal->size   == sizeof(XIMStyles));
661 #endif /* DEBUG_XEMACS */
662
663   p->count_styles = 0;
664   p->supported_styles = xnew_array (XIMStyle, max_styles);
665
666   /*
667    * The following routine assumes that the style name resource is
668    * identical with the programmatic name of style.  For example,
669    * "XIMPreeditPosition|XIMStatusArea" means the
670    * XIMPreeditPosition|XIMStatusArea value is specified.  If the
671    * style name is changed, such as "OverTheSpot|imDisplaysInClient",
672    * the parsing logic below should be modified as well. */
673
674   if ((c = strtok(s, delimiter)) == NULL)
675     c = end;
676
677   while (c < end)
678     {
679       for(i=0 ; i<max_styles ; i++)
680         {
681           struct XIMStyleInfo *rec = emacs_XIMStyleInfo + i;
682           if(!strncmp(c, rec->name, rec->namelen - 1)) {
683             p->supported_styles[p->count_styles] = rec->style;
684             p->count_styles++;
685             break;
686           }
687         }
688       if((c = strtok(NULL, delimiter)) == NULL) {
689         break ;
690       }
691     }
692
693   if (p->count_styles == 0)
694     {   /* No valid styles? */
695       char *buf = (char *)alloca (strlen (fromVal->addr)
696                                   + strlen (DefaultXIMStyles)
697                                   + 100);
698       XrmValue new_from;
699       XtAppContext the_app_con = XtDisplayToApplicationContext (dpy);
700
701       sprintf(buf, "Cannot convert string \"%s\" to type XIMStyles.\n"
702               "Using default string \"%s\" instead.\n",
703               fromVal->addr, DefaultXIMStyles);
704       XtAppWarningMsg(the_app_con, "wrongParameters", "cvtStringToXIMStyle",
705                       "XtToolkitError",
706                       buf, (String *)NULL, (Cardinal *)NULL);
707       new_from.addr = DefaultXIMStyles;
708       new_from.size = sizeof(DefaultXIMStyles);
709       return EmacsXtCvtStringToXIMStyles (dpy, args, num_args,
710                                           &new_from, toVal, converter_data);
711     }
712   XREALLOC_ARRAY (p->supported_styles, XIMStyle, p->count_styles);
713   *converter_data = (char *) True;
714   return True;
715 }
716
717 /* XtDestructor */
718 void
719 EmacsFreeXIMStyles (
720   XtAppContext app,
721   XrmValuePtr  toVal,
722   XtPointer    converter_data,
723   XrmValuePtr  args,
724   Cardinal    *num_args)
725 {
726 #ifdef DEBUG_XIM
727   stderr_out ("Converter data: %x\n", converter_data);
728   stderr_out ("EmacsFreeXIMStyles called\n");
729 #endif /* DEBUG_XIM */
730
731   if (*num_args != 0)
732     {
733       XtAppWarningMsg(app, "wrongParameters","freeXIMStyles","XtToolkitError",
734                       "Freeing an XIMStyles requires that zero arguments be passwd",
735                       (String *)NULL, (Cardinal *)NULL);
736       return;
737     }
738
739   if (converter_data)
740     {
741       Boolean free_p    = (Boolean) (int) converter_data;
742       XIMStyles *styles = (XIMStyles *) toVal->addr;
743       if (free_p)
744         XFree ( styles->supported_styles );
745     }
746 }
747
748 #if 0
749 /* O'Reilly XLib Programming Manual, pg. 371 */
750 /* Much nicer implementation than O'Reilly */
751 /* Choose the more `complicated', hence nicer, XIM input style */
752 static XIMStyle
753 BetterStyle (XIMStyle s, XIMStyle t)
754 {
755 #define CHECK_XIMStyle_BIT(bit)  \
756   if ((s ^ t) & bit) { return (s & bit) ? s : t; }
757
758   CHECK_XIMStyle_BIT (XIMPreeditCallbacks);
759   CHECK_XIMStyle_BIT (XIMPreeditPosition);
760   CHECK_XIMStyle_BIT (XIMPreeditArea);
761   CHECK_XIMStyle_BIT (XIMPreeditNothing);
762   CHECK_XIMStyle_BIT (XIMStatusCallbacks);
763   CHECK_XIMStyle_BIT (XIMStatusArea);
764   CHECK_XIMStyle_BIT (XIMStatusNothing);
765 #undef CHECK_XIMStyle_BIT
766   return s ? s : t ;
767 }
768 #endif /* 0 */
769
770 /* Choose the best style, given:
771  * - user preferences (already checked to be supported by XEmacs)
772  * - styles supported by the input method */
773 #define DEFAULTStyle  (XIMPreeditNothing|XIMStatusNothing)
774 static XIMStyle
775 best_style (XIMStyles *user, XIMStyles *xim)
776 {
777   REGISTER int i, j;
778   for (i=0 ; i<user->count_styles ; i++)
779     {
780       for (j=0 ; j<xim->count_styles ; j++)
781         {
782           if (user->supported_styles[i] == xim->supported_styles[j])
783             return user->supported_styles[i];
784         }
785     }
786   return DEFAULTStyle; /* Default Style */
787 }
788
789 /* These lisp-callable functions will be sealed until xim-leim is needed. 
790    Oct 22 1999 - kazz */
791 #if 0
792 /*
793  * External callable function for XIM
794  */
795 DEFUN ("x-open-xim", Fx_open_xim, 1, 1, 0, /*
796 Open the XIC on the frame if XIM is available.
797 Commonly, use this as \(x-open-xim \(selected-frame)).
798 If the frame is not on X device, return signal.
799 If XIC is created successfully return t.  If not return nil.
800 */
801        (frame))
802 {
803   struct frame *f;
804
805   CHECK_LIVE_FRAME (frame);
806   f = XFRAME (frame);
807   if (!FRAME_X_P (f))
808     return signal_simple_error ("This frame is not on X device", frame);
809
810   XIM_init_frame (f);
811   return FRAME_X_XIC (f) ? Qt : Qnil;
812 }
813
814 DEFUN ("x-close-xim", Fx_close_xim, 1, 1, 0, /*
815 Close the XIC on the frame if it exists.
816 Commonly, use this as \(x-close-xim \(selected-frame)).
817 If the frame is not on X device, return signal.
818 Otherwise, it destroys the XIC if it exists, then returns t anyway.
819 */
820        (frame))
821 {
822   struct frame *f;
823   struct device *d;
824
825   CHECK_LIVE_FRAME (frame);
826   f = XFRAME (frame);
827   if (!FRAME_X_P (f))
828     return signal_simple_error ("This frame is not on X device", frame);
829
830   d = XDEVICE (FRAME_DEVICE (f));
831   if (DEVICE_X_XIM (d)) {
832     /* XDestroyIC (FRAME_X_XIC (XFRAME (f))); */
833     FRAME_X_XIC (XFRAME (f)) = NULL;
834   }
835   return Qt;
836 }
837 #endif /* if 0 */
838
839 void
840 syms_of_input_method_xlib (void)
841 {
842   defsymbol (&Qxim_xlib, "xim-xlib");
843 #if 0 /* see above */
844   DEFSUBR (Fx_open_xim);
845   DEFSUBR (Fx_close_xim);
846 #endif
847 }
848
849 void
850 vars_of_input_method_xlib (void)
851 {
852   Fprovide (intern ("xim"));
853 }
854
855
856 /* ====================================================================== */
857 /* Internal Debugging Routines */
858 /* ====================================================================== */
859 #ifdef DEBUG_XEMACS
860
861 void
862 describe_XIM (XIM xim)
863 {
864   XIMStyles *styles;
865
866   /* Print locale of XIM */
867   stderr_out ("\nXIM Locale of IM: %s\n", XLocaleOfIM(xim));
868
869   /* List supported input method styles */
870   XGetIMValues(xim, XNQueryInputStyle, &styles, NULL);
871
872   stderr_out ("\n%d input style(s) supported by input method.\n",
873               styles->count_styles);
874
875 #ifdef DEBUG_XIM
876   {
877     int i;
878     for (i=0; i < styles->count_styles; i++)
879       describe_XIMStyle (styles->supported_styles[i]);
880   }
881 #endif /* DEBUG_XIM */
882   XFree(styles);
883 }
884
885 void
886 describe_XFontSet (XFontSet fontset)
887 {
888   XFontStruct **font_struct_list;
889   char **font_name_list;
890   int count, i;
891
892   if (fontset == NULL)
893     {
894       stderr_out ("NULL\n");
895       return;
896     }
897
898   count = XFontsOfFontSet (fontset, &font_struct_list, &font_name_list);
899   stderr_out ( "%d font(s) available:\n", count);
900   for (i=0 ; i < count ; i++)
901     stderr_out ("Font: %s\n", *(font_name_list+i));
902 }
903
904 void
905 describe_Status (Status status)
906 {
907 #define DESCRIBE_STATUS(value) \
908   if (status == value) stderr_out ("Status: " #value "\n")
909
910   DESCRIBE_STATUS (XBufferOverflow);
911   DESCRIBE_STATUS (XLookupNone);
912   DESCRIBE_STATUS (XLookupKeySym);
913   DESCRIBE_STATUS (XLookupBoth);
914   DESCRIBE_STATUS (XLookupChars);
915 #undef DESCRIBE_STATUS
916 }
917
918 void
919 describe_Window (Window win)
920 {
921   char xwincmd[128];
922   sprintf (xwincmd, "xwininfo -id 0x%x >&2; xwininfo -events -id 0x%x >&2",
923            (int) win, (int) win);
924   system (xwincmd);
925 }
926
927 void
928 describe_XIC (XIC xic)
929 {
930   XIMStyle style;
931   Window client_win=0, focus_win=0;
932   char *resourceName  = NULL;
933   char *resourceClass = NULL;
934   char *bad_arg       = NULL;
935   unsigned long filter_mask = NoEventMask;
936   XVaNestedList p_list, s_list;
937   XFontSet      p_fontset = NULL, s_fontset = NULL;
938   Pixel         p_fg=0, p_bg = 0, s_fg=0, s_bg = 0;
939   XRectangle   *p_area   = NULL, *s_area   = NULL;
940   XRectangle   *p_needed = NULL, *s_needed = NULL;
941   XPoint       *p_spot = NULL;
942
943   /* Check for valid input context and method */
944   if (!xic)
945     stderr_out ("Input method is NULL\n");
946
947   if (!XIMOfIC(xic))
948     stderr_out ("XIMOfIC() returns NULL\n");
949
950   /* Print out Input Context Attributes */
951   p_list = XVaCreateNestedList (0,
952                                 XNFontSet,      &p_fontset,
953                                 XNArea,         &p_area,
954                                 XNAreaNeeded,   &p_needed,
955                                 XNSpotLocation, &p_spot,
956                                 XNForeground,   &p_fg,
957                                 XNBackground,   &p_bg,
958                                 NULL);
959
960   s_list = XVaCreateNestedList (0,
961                                 XNFontSet,      &s_fontset,
962                                 XNArea,         &s_area,
963                                 XNAreaNeeded,   &s_needed,
964                                 XNForeground,   &s_fg,
965                                 XNBackground,   &s_bg,
966                                 NULL);
967
968   bad_arg = XGetICValues(xic,
969                          XNInputStyle,        &style,
970                          XNFilterEvents,      &filter_mask,
971                          XNClientWindow,      &client_win,
972                          XNFocusWindow,       &focus_win,
973                          XNResourceName,      &resourceName,
974                          XNResourceClass,     &resourceClass,
975                          XNPreeditAttributes, p_list,
976                          XNStatusAttributes,  s_list,
977                          NULL);
978   XFree(p_list);
979   XFree(s_list);
980
981   if (bad_arg != NULL)
982     stderr_out ("Couldn't get IC value: %s\n", bad_arg);
983
984   stderr_out ("\nInput method context attributes:\n");
985   stderr_out ("Style: "); describe_XIMStyle (style);
986   stderr_out ("Client window: %lx\n", (unsigned long int)client_win);
987   stderr_out ("Focus window: %lx\n",  (unsigned long int)focus_win);
988   stderr_out ("Preedit:\n");
989   describe_XRectangle ("  Area", p_area);
990   describe_XRectangle ("  Area needed", p_needed);
991   stderr_out ("  foreground: %lx\n", (unsigned long int)p_fg);
992   stderr_out ("  background: %lx\n", (unsigned long int)p_bg);
993   stderr_out ("  fontset: "); describe_XFontSet (p_fontset);
994   stderr_out ("Status:\n");
995   describe_XRectangle ("  Area", s_area);
996   describe_XRectangle ("  Area needed", s_needed);
997   stderr_out ("  foreground: %lx\n", (unsigned long int)s_fg);
998   stderr_out ("  background: %lx\n", (unsigned long int)s_bg);
999   stderr_out ("  fontset: \n"); describe_XFontSet (s_fontset);
1000   stderr_out ("XNResourceName: %s\n",  resourceName  ? resourceName  : "NULL");
1001   stderr_out ("XNResourceClass: %s\n", resourceClass ? resourceClass : "NULL");
1002   stderr_out ("XNFilterEvents: "); describe_event_mask (filter_mask);
1003 }
1004
1005 void
1006 describe_XRectangle (char *name, XRectangle *r)
1007 {
1008   if (r == NULL)
1009     stderr_out ("%s: NULL\n", name);
1010   else
1011     stderr_out ("%s: x=%d y=%d w=%d h=%d\n",
1012                 name, r->x, r->y, r->width, r->height);
1013 }
1014
1015 /* Print out elements of Event mask */
1016 /* Defines from X11/X.h */
1017 void
1018 describe_event_mask (unsigned long mask)
1019 {
1020 #define DESCRIBE_EVENT_MASK(bit) if ((bit) & mask) stderr_out (#bit " ")
1021   DESCRIBE_EVENT_MASK (NoEventMask);
1022   DESCRIBE_EVENT_MASK (KeyPressMask);
1023   DESCRIBE_EVENT_MASK (KeyReleaseMask);
1024   DESCRIBE_EVENT_MASK (ButtonPressMask);
1025   DESCRIBE_EVENT_MASK (ButtonReleaseMask);
1026   DESCRIBE_EVENT_MASK (EnterWindowMask);
1027   DESCRIBE_EVENT_MASK (LeaveWindowMask);
1028   DESCRIBE_EVENT_MASK (PointerMotionMask);
1029   DESCRIBE_EVENT_MASK (PointerMotionHintMask);
1030   DESCRIBE_EVENT_MASK (Button1MotionMask);
1031   DESCRIBE_EVENT_MASK (Button2MotionMask);
1032   DESCRIBE_EVENT_MASK (Button3MotionMask);
1033   DESCRIBE_EVENT_MASK (Button4MotionMask);
1034   DESCRIBE_EVENT_MASK (Button5MotionMask);
1035   DESCRIBE_EVENT_MASK (ButtonMotionMask);
1036   DESCRIBE_EVENT_MASK (KeymapStateMask);
1037   DESCRIBE_EVENT_MASK (ExposureMask);
1038   DESCRIBE_EVENT_MASK (VisibilityChangeMask);
1039   DESCRIBE_EVENT_MASK (StructureNotifyMask);
1040   DESCRIBE_EVENT_MASK (ResizeRedirectMask);
1041   DESCRIBE_EVENT_MASK (SubstructureNotifyMask);
1042   DESCRIBE_EVENT_MASK (SubstructureRedirectMask);
1043   DESCRIBE_EVENT_MASK (FocusChangeMask);
1044   DESCRIBE_EVENT_MASK (PropertyChangeMask);
1045   DESCRIBE_EVENT_MASK (ColormapChangeMask);
1046   DESCRIBE_EVENT_MASK (OwnerGrabButtonMask);
1047 #undef DESCRIBE_EVENT_MASK
1048   stderr_out("\n");
1049 }
1050
1051 void
1052 describe_XIMStyle (XIMStyle style)
1053 {
1054 #define DESCRIBE_STYLE(bit) \
1055   if (bit & style)          \
1056     stderr_out (#bit " ");
1057
1058   DESCRIBE_STYLE (XIMPreeditArea);
1059   DESCRIBE_STYLE (XIMPreeditCallbacks);
1060   DESCRIBE_STYLE (XIMPreeditPosition);
1061   DESCRIBE_STYLE (XIMPreeditNothing);
1062   DESCRIBE_STYLE (XIMPreeditNone);
1063   DESCRIBE_STYLE (XIMStatusArea);
1064   DESCRIBE_STYLE (XIMStatusCallbacks);
1065   DESCRIBE_STYLE (XIMStatusNothing);
1066   DESCRIBE_STYLE (XIMStatusNone);
1067 #undef DESCRIBE_STYLE
1068   stderr_out("\n");
1069 }
1070
1071 void
1072 describe_XIMStyles (XIMStyles *p)
1073 {
1074   int i;
1075   stderr_out ("%d Style(s):\n", p->count_styles);
1076   for (i=0; i<p->count_styles ; i++)
1077     {
1078       describe_XIMStyle (p->supported_styles[i]);
1079     }
1080 }
1081
1082 #endif /* DEBUG_XEMACS */
1083
1084 /* Random cruft follows */
1085
1086 #if 0
1087 static void
1088 Unit_Test (struct frame *f, char * s)
1089 /* mrb unit testing */
1090 {
1091   XrmValue fromVal, toVal;
1092
1093   fromVal.addr = s;
1094   fromVal.size = strlen (s);
1095   toVal.addr = (XtPointer) &user_preferred_XIMStyles;
1096   toVal.size = sizeof (XIMStyles);
1097
1098   if (XtConvertAndStore (FRAME_X_TEXT_WIDGET (f), XtRString, &fromVal,
1099                          XtRXimStyles, &toVal) != False)
1100     {
1101       stderr_out ("Unit_Test: fromVal.addr=0x%x\n",fromVal.addr);
1102       stderr_out ("Unit_Test: fromVal.size=%d\n",  fromVal.size);
1103       stderr_out ("Unit_Test:   toVal.addr=0x%x\n",  toVal.addr);
1104       stderr_out ("Unit_Test:   toVal.size=%d\n",    toVal.size);
1105       describe_XIMStyles ((XIMStyles *) toVal.addr);
1106     }
1107 }
1108 #endif
1109
1110 #if 0
1111 /* Get a fontset for IM to use */
1112 void
1113 x_init_fontset (struct device *d)
1114 {
1115   Display *dpy = DEVICE_X_DISPLAY (d);
1116   XFontSet fontset;
1117   char ** missing_charsets;
1118   int num_missing_charsets;
1119   char * default_string;
1120   /*  char * font_set_string = "-dt-interface user-medium-r-normal-s*-*-*-*-*-*-*-*-*";*/
1121   char * font_set_string = "-dt-interface user-medium-r-normal-s*-*-*-*-*-*-*-*-*, -misc-fixed-medium-r-normal--14-130-75-75-c-70-jisx0201.1976-0,-misc-fixed-medium-r-normal--14-130-75-75-c-140-jisx0208.1983-0, -misc-fixed-medium-r-normal--14-130-75-75-c-70-jisx0201.1976-0" ;
1122
1123   DEVICE_X_FONTSET (d) = fontset =
1124     XCreateFontSet (dpy,
1125                     font_set_string,
1126                     &missing_charsets,
1127                     &num_missing_charsets,
1128                     &default_string);
1129
1130   if (fontset == NULL)
1131     {
1132       stderr_out ("Unable to create fontset from string:\n%s\n", font_set_string);
1133       return;
1134     }
1135   if (num_missing_charsets > 0)
1136     {
1137       int i;
1138       stderr_out ("\nMissing charsets for fontset %s:\n", font_set_string);
1139       for (i=0; i < num_missing_charsets; i++)
1140         {
1141           stderr_out ("%s\n", missing_charsets[i]);
1142         }
1143       XFreeStringList (missing_charsets);
1144       stderr_out ("Default string: %s\n", default_string);
1145     }
1146
1147 #ifdef DEBUG_XIM
1148   describe_XFontSet (fontset);
1149 #endif
1150 }
1151 #endif /* 0 */