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