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