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