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