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