30b30726e5fc026b2beaad46413b73576f78e511
[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 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   = FRAME_X_XIC (f);
430   XIMStyle style = FRAME_X_XIC_STYLE (f);
431   XRectangle area;
432
433   if (!xic || !f)
434     return;
435
436   if (style & XIMStatusArea)
437     {
438       /* Place Status Area in bottom right corner */
439       /* Negotiate geometry of status area */
440       /* See O'Reilly Xlib XIM chapter (but beware, it's buggy) */
441       XRectangle *needed;
442
443       /* If input method has existing status area, use its current size */
444       /* The following at least works for Sun's htt */
445       area.x = area.y = area.width = area.height = 0;
446       XIC_Value (Set, xic, XNStatusAttributes, XNAreaNeeded, &area);
447       XIC_Value (Get, xic, XNStatusAttributes, XNAreaNeeded, &needed);
448       if (needed->width == 0)   /* Use XNArea instead of XNAreaNeeded */
449         XIC_Value (Get, xic, XNStatusAttributes, XNArea, &needed);
450
451       area.width  = needed->width;
452       area.height = needed->height;
453       area.x = FRAME_RIGHT_BORDER_START  (f) - area.width;
454       area.y = FRAME_BOTTOM_BORDER_START (f) - area.height;
455
456 #ifdef DEBUG_XIM
457       stderr_out ("Putting StatusArea in x=%d y=%d w=%d h=%d\n",
458                   area.x, area.y, area.width, area.height);
459 #endif /* DEBUG_XIM */
460
461       XIC_Value (Set, xic, XNStatusAttributes, XNArea, &area);
462     }
463
464   if (style & XIMPreeditPosition)
465     {
466       /* Set Preedit Area to whole frame size (sans border) */
467       /* We include the border because Preedit window might be larger
468          than display line at edge. #### FIX: we should adjust to make
469          sure that there is always room for the spot sub-window */
470       area.x      = FRAME_LEFT_BORDER_START (f);
471       area.y      = FRAME_TOP_BORDER_START  (f);
472       area.width  = FRAME_RIGHT_BORDER_END  (f) - area.x;
473       area.height = FRAME_BOTTOM_BORDER_END (f) - area.y;
474       XIC_Value(Set, xic, XNPreeditAttributes, XNArea, &area);
475     }
476
477 #ifdef DEBUG_XIM
478   describe_XIC (xic);
479 #endif
480 }
481
482 void
483 XIM_SetSpotLocation (struct frame *f, int x, int y)
484 {
485   XIC xic = FRAME_X_XIC (f);
486   XPoint *spot = &(FRAME_X_XIC_SPOT (f));
487
488   /* Only care if we have a valid XIC using Over the Spot in
489    * a different location */
490   if (!xic ||
491       !(FRAME_X_XIC_STYLE (f) & XIMPreeditPosition) ||
492       (spot->x == (short) x &&
493        spot->y == (short) y))
494     return;
495
496   spot->x = (short) x;
497   spot->y = (short) y;
498
499   /* #### FIX: Must make sure spot fits within Preedit Area */
500   XIC_Value (Set, xic, XNPreeditAttributes, XNSpotLocation, spot);
501 #ifdef DEBUG_XIM
502   stderr_out ("Spot: %d %d\n", spot->x, spot->y);
503 #endif
504 }
505
506 void
507 XIM_focus_event (struct frame *f, int in_p)
508 {
509   if (FRAME_X_XIC (f) /* && FRAME_X_XIM_REGISTERED(f) */)
510     (in_p ? XSetICFocus : XUnsetICFocus) (FRAME_X_XIC (f));
511 }
512
513 #if 0
514 #define XIM_Composed_Text_BUFSIZE 64
515 typedef struct XIM_Composed_Text
516 {
517   int size;
518   wchar_t data [XIM_Composed_Text_BUFSIZE];
519 } XIM_Composed_Text;
520
521 static XIM_Composed_Text composed_input_buf = {XIM_Composed_Text_BUFSIZE, {0}};
522 Window main_window;
523
524 /* get_XIM_input -- Process results of input method composition.
525
526    This function copies the results of the input method composition to
527    composed_input_buf.  Then for each character, a custom event of type
528    wc_atom is sent with the character as its data.
529
530    It is probably more efficient to copy the composition results to some
531    allocated memory and send a single event pointing to that memory.
532    That would cut down on the event processing as well as allow quick
533    insertion into the buffer of the whole string.  It might require some
534    care, though, to avoid fragmenting memory through the allocation and
535    freeing of many small chunks.  Maybe the existing system for
536    (single-byte) string allocation can be used, multiplying the length by
537    sizeof (wchar_t) to get the right size.
538 */
539 void
540 get_XIM_input (XKeyPressedEvent *x_key_event, XIC ic, Display *dpy)
541 {
542   KeySym keysym;
543   Status status;
544   int len;
545   int i;
546   XClientMessageEvent new_event;
547
548 retry:
549   len = XwcLookupString (ic, x_key_event, composed_input_buf.data,
550                          composed_input_buf.size, &keysym, &status);
551   switch (status)
552     {
553     case XBufferOverflow:
554       /* GROW_WC_STRING (&composed_input_buf, 32); mrb */
555       goto retry;
556     case XLookupChars:
557       break;
558     default:
559       ABORT ();
560     }
561
562   new_event.type = ClientMessage;
563   new_event.display = x_key_event->display;
564   new_event.window = x_key_event->window;
565   new_event.message_type = wc_atom;
566   new_event.format = 32;  /* 32-bit wide data */
567   new_event.data.l[2] = new_event.data.l[3] = new_event.data.l[4] = 0L;
568   new_event.data.l[0] = x_key_event->time;
569   for (i = 0; i < len; i++)
570     {
571       new_event.data.l[1] = ((wchar_t *) composed_input_buf.data)[i];
572       XSendEvent (display, main_window, False, 0L, (XEvent *) &new_event);
573     }
574 }
575 #endif /* 0 */
576
577 /* ============================================================== */
578 /* X input method style determination */
579 /* ============================================================== */
580
581 #if 0
582 #define done(type, value)                \
583   if (toVal->addr != NULL) {             \
584     if (toVal->size < sizeof(type)) {    \
585       toVal->size = sizeof(type);        \
586       return False;                      \
587     }                                    \
588     *(type*)toVal->addr = (value);       \
589   } else {                               \
590     static type static_val;              \
591     static_val = (value);                \
592     toVal->addr = (XPointer)&static_val; \
593   }                                      \
594   toVal->size = sizeof(type);            \
595   return True /* Caller supplies `;' */
596 #endif /* 0 */
597
598 /*
599  * This is a standard Xt type converter, except that the caller MUST
600  * supply a proper non-NULL toVal XIMStyles structure that we will
601  * fill in.
602  *
603  * fromVal points to a string like
604  *
605  "XIMPreeditPosition|XIMStatusArea,
606  XIMPreeditPosition|XIMStatusNothing
607  XIMPreeditNothing|XIMStatusNothing"
608  *
609  * This is converted in the obvious way to a XIMStyles structure.
610  *
611  * mrb: #### Fix this to handle Motif-style specifications for
612  * XIMStyles as well: overTheSpot, rootWindow, none */
613
614 /* XtTypeConverter */
615 Boolean
616 EmacsXtCvtStringToXIMStyles (
617   Display     *dpy,
618   XrmValuePtr  args,
619   Cardinal    *num_args,
620   XrmValuePtr  fromVal,
621   XrmValuePtr  toVal,
622   XtPointer   *converter_data)
623 {
624 #define STYLE_INFO(style) { style, #style, sizeof(#style) }
625   static struct XIMStyleInfo
626   {
627     const XIMStyle style;
628     const char   * const name;
629     const int      namelen;
630   } emacs_XIMStyleInfo[] = {
631     STYLE_INFO (XIMPreeditPosition|XIMStatusArea),
632     STYLE_INFO (XIMPreeditPosition|XIMStatusNothing),
633     STYLE_INFO (XIMPreeditPosition|XIMStatusNone),
634     STYLE_INFO (XIMPreeditNothing|XIMStatusArea),
635     STYLE_INFO (XIMPreeditNothing|XIMStatusNothing),
636     STYLE_INFO (XIMPreeditNothing|XIMStatusNone),
637     STYLE_INFO (XIMPreeditNone|XIMStatusArea),
638     STYLE_INFO (XIMPreeditNone|XIMStatusNothing),
639     STYLE_INFO (XIMPreeditNone|XIMStatusNone)
640   };
641 #undef STYLE_INFO
642
643   char *s   = (char *) fromVal->addr;
644   char *end = s + fromVal->size;
645   XIMStyles * const p = (XIMStyles *) toVal->addr;
646   const char * const delimiter = " \t\n\r:;," ;
647   const int  max_styles = XtNumber(emacs_XIMStyleInfo);
648   int i;
649   char *c;
650
651 #ifdef DEBUG_XIM
652   stderr_out ("EmacsCvtStringToXIMStyles called with size=%d, string=\"%s\"\n",
653               fromVal->size, (char *) fromVal->addr);
654 #endif /* DEBUG_XIM */
655
656   if (*num_args != 0)
657     {
658       XtAppContext the_app_con = XtDisplayToApplicationContext (dpy);
659       XtAppWarningMsg(the_app_con, "wrongParameters", "cvtStringToXIMStyle",
660                       "XtToolkitError",
661                       "String to XIMStyle conversion requires exactly 0 parameters",
662                       (String *)NULL, (Cardinal *)NULL);
663       return False;
664     }
665
666 #ifdef DEBUG_XEMACS
667   /* Make sure caller is giving us good data */
668   assert (fromVal->addr != NULL);
669   assert (fromVal->size == strlen(fromVal->addr)+1);
670   assert (toVal->addr   != NULL);
671   assert (toVal->size   == sizeof(XIMStyles));
672 #endif /* DEBUG_XEMACS */
673
674   p->count_styles = 0;
675   p->supported_styles = xnew_array (XIMStyle, max_styles);
676
677   /*
678    * The following routine assumes that the style name resource is
679    * identical with the programmatic name of style.  For example,
680    * "XIMPreeditPosition|XIMStatusArea" means the
681    * XIMPreeditPosition|XIMStatusArea value is specified.  If the
682    * style name is changed, such as "OverTheSpot|imDisplaysInClient",
683    * the parsing logic below should be modified as well. */
684
685   if ((c = strtok(s, delimiter)) == NULL)
686     c = end;
687
688   while (c < end)
689     {
690       for(i=0 ; i<max_styles ; i++)
691         {
692           struct XIMStyleInfo *rec = emacs_XIMStyleInfo + i;
693           if(!strncmp(c, rec->name, rec->namelen - 1)) {
694             p->supported_styles[p->count_styles] = rec->style;
695             p->count_styles++;
696             break;
697           }
698         }
699       if((c = strtok(NULL, delimiter)) == NULL) {
700         break ;
701       }
702     }
703
704   if (p->count_styles == 0)
705     {   /* No valid styles? */
706       char *buf = (char *)alloca (strlen (fromVal->addr)
707                                   + strlen (DefaultXIMStyles)
708                                   + 100);
709       XrmValue new_from;
710       XtAppContext the_app_con = XtDisplayToApplicationContext (dpy);
711
712       sprintf(buf, "Cannot convert string \"%s\" to type XIMStyles.\n"
713               "Using default string \"%s\" instead.\n",
714               fromVal->addr, DefaultXIMStyles);
715       XtAppWarningMsg(the_app_con, "wrongParameters", "cvtStringToXIMStyle",
716                       "XtToolkitError",
717                       buf, (String *)NULL, (Cardinal *)NULL);
718       new_from.addr = DefaultXIMStyles;
719       new_from.size = sizeof(DefaultXIMStyles);
720       return EmacsXtCvtStringToXIMStyles (dpy, args, num_args,
721                                           &new_from, toVal, converter_data);
722     }
723   XREALLOC_ARRAY (p->supported_styles, XIMStyle, p->count_styles);
724   *converter_data = (char *) True;
725   return True;
726 }
727
728 /* XtDestructor */
729 void
730 EmacsFreeXIMStyles (
731   XtAppContext app,
732   XrmValuePtr  toVal,
733   XtPointer    converter_data,
734   XrmValuePtr  args,
735   Cardinal    *num_args)
736 {
737 #ifdef DEBUG_XIM
738   stderr_out ("Converter data: %x\n", converter_data);
739   stderr_out ("EmacsFreeXIMStyles called\n");
740 #endif /* DEBUG_XIM */
741
742   if (*num_args != 0)
743     {
744       XtAppWarningMsg(app, "wrongParameters","freeXIMStyles","XtToolkitError",
745                       "Freeing an XIMStyles requires that zero arguments be passwd",
746                       (String *)NULL, (Cardinal *)NULL);
747       return;
748     }
749
750   if (converter_data)
751     {
752       Boolean free_p    = (Boolean) (int) converter_data;
753       XIMStyles *styles = (XIMStyles *) toVal->addr;
754       if (free_p)
755         XFree ( styles->supported_styles );
756     }
757 }
758
759 #if 0
760 /* O'Reilly XLib Programming Manual, pg. 371 */
761 /* Much nicer implementation than O'Reilly */
762 /* Choose the more `complicated', hence nicer, XIM input style */
763 static XIMStyle
764 BetterStyle (XIMStyle s, XIMStyle t)
765 {
766 #define CHECK_XIMStyle_BIT(bit)  \
767   if ((s ^ t) & bit) { return (s & bit) ? s : t; }
768
769   CHECK_XIMStyle_BIT (XIMPreeditCallbacks);
770   CHECK_XIMStyle_BIT (XIMPreeditPosition);
771   CHECK_XIMStyle_BIT (XIMPreeditArea);
772   CHECK_XIMStyle_BIT (XIMPreeditNothing);
773   CHECK_XIMStyle_BIT (XIMStatusCallbacks);
774   CHECK_XIMStyle_BIT (XIMStatusArea);
775   CHECK_XIMStyle_BIT (XIMStatusNothing);
776 #undef CHECK_XIMStyle_BIT
777   return s ? s : t ;
778 }
779 #endif /* 0 */
780
781 /* Choose the best style, given:
782  * - user preferences (already checked to be supported by XEmacs)
783  * - styles supported by the input method */
784 #define DEFAULTStyle  (XIMPreeditNothing|XIMStatusNothing)
785 static XIMStyle
786 best_style (XIMStyles *user, XIMStyles *xim)
787 {
788   REGISTER int i, j;
789   for (i=0 ; i<user->count_styles ; i++)
790     {
791       for (j=0 ; j<xim->count_styles ; j++)
792         {
793           if (user->supported_styles[i] == xim->supported_styles[j])
794             return user->supported_styles[i];
795         }
796     }
797   return DEFAULTStyle; /* Default Style */
798 }
799
800 /* These lisp-callable functions will be sealed until xim-leim is needed. 
801    Oct 22 1999 - kazz */
802 #if 0
803 /*
804  * External callable function for XIM
805  */
806 DEFUN ("x-open-xim", Fx_open_xim, 1, 1, 0, /*
807 Open the XIC on the frame if XIM is available.
808 Commonly, use this as \(x-open-xim \(selected-frame)).
809 If the frame is not on X device, return signal.
810 If XIC is created successfully return t.  If not return nil.
811 */
812        (frame))
813 {
814   struct frame *f;
815
816   CHECK_LIVE_FRAME (frame);
817   f = XFRAME (frame);
818   if (!FRAME_X_P (f))
819     return signal_simple_error ("This frame is not on X device", frame);
820
821   XIM_init_frame (f);
822   return FRAME_X_XIC (f) ? Qt : Qnil;
823 }
824
825 DEFUN ("x-close-xim", Fx_close_xim, 1, 1, 0, /*
826 Close the XIC on the frame if it exists.
827 Commonly, use this as \(x-close-xim \(selected-frame)).
828 If the frame is not on X device, return signal.
829 Otherwise, it destroys the XIC if it exists, then returns t anyway.
830 */
831        (frame))
832 {
833   struct frame *f;
834   struct device *d;
835
836   CHECK_LIVE_FRAME (frame);
837   f = XFRAME (frame);
838   if (!FRAME_X_P (f))
839     return signal_simple_error ("This frame is not on X device", frame);
840
841   d = XDEVICE (FRAME_DEVICE (f));
842   if (DEVICE_X_XIM (d)) {
843     /* XDestroyIC (FRAME_X_XIC (XFRAME (f))); */
844     FRAME_X_XIC (XFRAME (f)) = NULL;
845   }
846   return Qt;
847 }
848 #endif /* if 0 */
849
850 void
851 syms_of_input_method_xlib (void)
852 {
853   defsymbol (&Qxim_xlib, "xim-xlib");
854 #if 0 /* see above */
855   DEFSUBR (Fx_open_xim);
856   DEFSUBR (Fx_close_xim);
857 #endif
858 }
859
860 void
861 vars_of_input_method_xlib (void)
862 {
863   Fprovide (intern ("xim"));
864 }
865
866
867 /* ====================================================================== */
868 /* Internal Debugging Routines */
869 /* ====================================================================== */
870 #ifdef DEBUG_XEMACS
871
872 void
873 describe_XIM (XIM xim)
874 {
875   XIMStyles *styles;
876
877   /* Print locale of XIM */
878   stderr_out ("\nXIM Locale of IM: %s\n", XLocaleOfIM(xim));
879
880   /* List supported input method styles */
881   XGetIMValues(xim, XNQueryInputStyle, &styles, NULL);
882
883   stderr_out ("\n%d input style(s) supported by input method.\n",
884               styles->count_styles);
885
886 #ifdef DEBUG_XIM
887   {
888     int i;
889     for (i=0; i < styles->count_styles; i++)
890       describe_XIMStyle (styles->supported_styles[i]);
891   }
892 #endif /* DEBUG_XIM */
893   XFree(styles);
894 }
895
896 void
897 describe_XFontSet (XFontSet fontset)
898 {
899   XFontStruct **font_struct_list;
900   char **font_name_list;
901   int count, i;
902
903   if (fontset == NULL)
904     {
905       stderr_out ("NULL\n");
906       return;
907     }
908
909   count = XFontsOfFontSet (fontset, &font_struct_list, &font_name_list);
910   stderr_out ( "%d font(s) available:\n", count);
911   for (i=0 ; i < count ; i++)
912     stderr_out ("Font: %s\n", *(font_name_list+i));
913 }
914
915 void
916 describe_Status (Status status)
917 {
918 #define DESCRIBE_STATUS(value) \
919   if (status == value) stderr_out ("Status: " #value "\n")
920
921   DESCRIBE_STATUS (XBufferOverflow);
922   DESCRIBE_STATUS (XLookupNone);
923   DESCRIBE_STATUS (XLookupKeySym);
924   DESCRIBE_STATUS (XLookupBoth);
925   DESCRIBE_STATUS (XLookupChars);
926 #undef DESCRIBE_STATUS
927 }
928
929 void
930 describe_Window (Window win)
931 {
932   char xwincmd[128];
933   sprintf (xwincmd, "xwininfo -id 0x%x >&2; xwininfo -events -id 0x%x >&2",
934            (int) win, (int) win);
935   system (xwincmd);
936 }
937
938 void
939 describe_XIC (XIC xic)
940 {
941   XIMStyle style;
942   Window client_win=0, focus_win=0;
943   char *resourceName  = NULL;
944   char *resourceClass = NULL;
945   char *bad_arg       = NULL;
946   unsigned long filter_mask = NoEventMask;
947   XVaNestedList p_list, s_list;
948   XFontSet      p_fontset = NULL, s_fontset = NULL;
949   Pixel         p_fg=0, p_bg = 0, s_fg=0, s_bg = 0;
950   XRectangle   *p_area   = NULL, *s_area   = NULL;
951   XRectangle   *p_needed = NULL, *s_needed = NULL;
952   XPoint       *p_spot = NULL;
953
954   /* Check for valid input context and method */
955   if (!xic)
956     stderr_out ("Input method is NULL\n");
957
958   if (!XIMOfIC(xic))
959     stderr_out ("XIMOfIC() returns NULL\n");
960
961   /* Print out Input Context Attributes */
962   p_list = XVaCreateNestedList (0,
963                                 XNFontSet,      &p_fontset,
964                                 XNArea,         &p_area,
965                                 XNAreaNeeded,   &p_needed,
966                                 XNSpotLocation, &p_spot,
967                                 XNForeground,   &p_fg,
968                                 XNBackground,   &p_bg,
969                                 NULL);
970
971   s_list = XVaCreateNestedList (0,
972                                 XNFontSet,      &s_fontset,
973                                 XNArea,         &s_area,
974                                 XNAreaNeeded,   &s_needed,
975                                 XNForeground,   &s_fg,
976                                 XNBackground,   &s_bg,
977                                 NULL);
978
979   bad_arg = XGetICValues(xic,
980                          XNInputStyle,        &style,
981                          XNFilterEvents,      &filter_mask,
982                          XNClientWindow,      &client_win,
983                          XNFocusWindow,       &focus_win,
984                          XNResourceName,      &resourceName,
985                          XNResourceClass,     &resourceClass,
986                          XNPreeditAttributes, p_list,
987                          XNStatusAttributes,  s_list,
988                          NULL);
989   XFree(p_list);
990   XFree(s_list);
991
992   if (bad_arg != NULL)
993     stderr_out ("Couldn't get IC value: %s\n", bad_arg);
994
995   stderr_out ("\nInput method context attributes:\n");
996   stderr_out ("Style: "); describe_XIMStyle (style);
997   stderr_out ("Client window: %lx\n", (unsigned long int)client_win);
998   stderr_out ("Focus window: %lx\n",  (unsigned long int)focus_win);
999   stderr_out ("Preedit:\n");
1000   describe_XRectangle ("  Area", p_area);
1001   describe_XRectangle ("  Area needed", p_needed);
1002   stderr_out ("  foreground: %lx\n", (unsigned long int)p_fg);
1003   stderr_out ("  background: %lx\n", (unsigned long int)p_bg);
1004   stderr_out ("  fontset: "); describe_XFontSet (p_fontset);
1005   stderr_out ("Status:\n");
1006   describe_XRectangle ("  Area", s_area);
1007   describe_XRectangle ("  Area needed", s_needed);
1008   stderr_out ("  foreground: %lx\n", (unsigned long int)s_fg);
1009   stderr_out ("  background: %lx\n", (unsigned long int)s_bg);
1010   stderr_out ("  fontset: \n"); describe_XFontSet (s_fontset);
1011   stderr_out ("XNResourceName: %s\n",  resourceName  ? resourceName  : "NULL");
1012   stderr_out ("XNResourceClass: %s\n", resourceClass ? resourceClass : "NULL");
1013   stderr_out ("XNFilterEvents: "); describe_event_mask (filter_mask);
1014 }
1015
1016 void
1017 describe_XRectangle (char *name, XRectangle *r)
1018 {
1019   if (r == NULL)
1020     stderr_out ("%s: NULL\n", name);
1021   else
1022     stderr_out ("%s: x=%d y=%d w=%d h=%d\n",
1023                 name, r->x, r->y, r->width, r->height);
1024 }
1025
1026 /* Print out elements of Event mask */
1027 /* Defines from X11/X.h */
1028 void
1029 describe_event_mask (unsigned long mask)
1030 {
1031 #define DESCRIBE_EVENT_MASK(bit) if ((bit) & mask) stderr_out (#bit " ")
1032   DESCRIBE_EVENT_MASK (NoEventMask);
1033   DESCRIBE_EVENT_MASK (KeyPressMask);
1034   DESCRIBE_EVENT_MASK (KeyReleaseMask);
1035   DESCRIBE_EVENT_MASK (ButtonPressMask);
1036   DESCRIBE_EVENT_MASK (ButtonReleaseMask);
1037   DESCRIBE_EVENT_MASK (EnterWindowMask);
1038   DESCRIBE_EVENT_MASK (LeaveWindowMask);
1039   DESCRIBE_EVENT_MASK (PointerMotionMask);
1040   DESCRIBE_EVENT_MASK (PointerMotionHintMask);
1041   DESCRIBE_EVENT_MASK (Button1MotionMask);
1042   DESCRIBE_EVENT_MASK (Button2MotionMask);
1043   DESCRIBE_EVENT_MASK (Button3MotionMask);
1044   DESCRIBE_EVENT_MASK (Button4MotionMask);
1045   DESCRIBE_EVENT_MASK (Button5MotionMask);
1046   DESCRIBE_EVENT_MASK (ButtonMotionMask);
1047   DESCRIBE_EVENT_MASK (KeymapStateMask);
1048   DESCRIBE_EVENT_MASK (ExposureMask);
1049   DESCRIBE_EVENT_MASK (VisibilityChangeMask);
1050   DESCRIBE_EVENT_MASK (StructureNotifyMask);
1051   DESCRIBE_EVENT_MASK (ResizeRedirectMask);
1052   DESCRIBE_EVENT_MASK (SubstructureNotifyMask);
1053   DESCRIBE_EVENT_MASK (SubstructureRedirectMask);
1054   DESCRIBE_EVENT_MASK (FocusChangeMask);
1055   DESCRIBE_EVENT_MASK (PropertyChangeMask);
1056   DESCRIBE_EVENT_MASK (ColormapChangeMask);
1057   DESCRIBE_EVENT_MASK (OwnerGrabButtonMask);
1058 #undef DESCRIBE_EVENT_MASK
1059   stderr_out("\n");
1060 }
1061
1062 void
1063 describe_XIMStyle (XIMStyle style)
1064 {
1065 #define DESCRIBE_STYLE(bit) \
1066   if (bit & style)          \
1067     stderr_out (#bit " ");
1068
1069   DESCRIBE_STYLE (XIMPreeditArea);
1070   DESCRIBE_STYLE (XIMPreeditCallbacks);
1071   DESCRIBE_STYLE (XIMPreeditPosition);
1072   DESCRIBE_STYLE (XIMPreeditNothing);
1073   DESCRIBE_STYLE (XIMPreeditNone);
1074   DESCRIBE_STYLE (XIMStatusArea);
1075   DESCRIBE_STYLE (XIMStatusCallbacks);
1076   DESCRIBE_STYLE (XIMStatusNothing);
1077   DESCRIBE_STYLE (XIMStatusNone);
1078 #undef DESCRIBE_STYLE
1079   stderr_out("\n");
1080 }
1081
1082 void
1083 describe_XIMStyles (XIMStyles *p)
1084 {
1085   int i;
1086   stderr_out ("%d Style(s):\n", p->count_styles);
1087   for (i=0; i<p->count_styles ; i++)
1088     {
1089       describe_XIMStyle (p->supported_styles[i]);
1090     }
1091 }
1092
1093 #endif /* DEBUG_XEMACS */
1094
1095 /* Random cruft follows */
1096
1097 #if 0
1098 static void
1099 Unit_Test (struct frame *f, char * s)
1100 /* mrb unit testing */
1101 {
1102   XrmValue fromVal, toVal;
1103
1104   fromVal.addr = s;
1105   fromVal.size = strlen (s);
1106   toVal.addr = (XtPointer) &user_preferred_XIMStyles;
1107   toVal.size = sizeof (XIMStyles);
1108
1109   if (XtConvertAndStore (FRAME_X_TEXT_WIDGET (f), XtRString, &fromVal,
1110                          XtRXimStyles, &toVal) != False)
1111     {
1112       stderr_out ("Unit_Test: fromVal.addr=0x%x\n",fromVal.addr);
1113       stderr_out ("Unit_Test: fromVal.size=%d\n",  fromVal.size);
1114       stderr_out ("Unit_Test:   toVal.addr=0x%x\n",  toVal.addr);
1115       stderr_out ("Unit_Test:   toVal.size=%d\n",    toVal.size);
1116       describe_XIMStyles ((XIMStyles *) toVal.addr);
1117     }
1118 }
1119 #endif
1120 #endif /* XIM_XLIB only */
1121
1122 #if 0
1123 /* Get a fontset for IM to use */
1124 void
1125 x_init_fontset (struct device *d)
1126 {
1127   Display *dpy = DEVICE_X_DISPLAY (d);
1128   XFontSet fontset;
1129   char ** missing_charsets;
1130   int num_missing_charsets;
1131   char * default_string;
1132   /*  char * font_set_string = "-dt-interface user-medium-r-normal-s*-*-*-*-*-*-*-*-*";*/
1133   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" ;
1134
1135   DEVICE_X_FONTSET (d) = fontset =
1136     XCreateFontSet (dpy,
1137                     font_set_string,
1138                     &missing_charsets,
1139                     &num_missing_charsets,
1140                     &default_string);
1141
1142   if (fontset == NULL)
1143     {
1144       stderr_out ("Unable to create fontset from string:\n%s\n", font_set_string);
1145       return;
1146     }
1147   if (num_missing_charsets > 0)
1148     {
1149       int i;
1150       stderr_out ("\nMissing charsets for fontset %s:\n", font_set_string);
1151       for (i=0; i < num_missing_charsets; i++)
1152         {
1153           stderr_out ("%s\n", missing_charsets[i]);
1154         }
1155       XFreeStringList (missing_charsets);
1156       stderr_out ("Default string: %s\n", default_string);
1157     }
1158
1159 #ifdef DEBUG_XIM
1160   describe_XFontSet (fontset);
1161 #endif
1162 }
1163 #endif /* 0 */