ef5c0bdf4c8e0b3563ac161c72deff23a5f1a6f7
[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_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 (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                 ssize_t len;
710                 XrmValue new_from;
711                 XtAppContext the_app_con = XtDisplayToApplicationContext (dpy);
712
713                 len = snprintf(buf, sizeof(buf), 
714                                "Cannot convert string \"%s\" to type XIMStyles.\n"
715                                "Using default string \"%s\" instead.\n",
716                                fromVal->addr, DefaultXIMStyles);
717                 assert(len >= 0 && (size_t)len < sizeof(buf));
718                 XtAppWarningMsg(the_app_con, "wrongParameters",
719                                 "cvtStringToXIMStyle",
720                                 "XtToolkitError",
721                                 buf, (String *)NULL, (Cardinal *)NULL);
722                 new_from.addr = DefaultXIMStyles;
723                 new_from.size = sizeof(DefaultXIMStyles);
724                 return EmacsXtCvtStringToXIMStyles (dpy, args, num_args,
725                                                     &new_from, toVal,
726                                                     converter_data);
727         }
728         XREALLOC_ARRAY (p->supported_styles, XIMStyle, p->count_styles);
729         *converter_data = (char *) True;
730         return True;
731 }
732
733 /* XtDestructor */
734 void
735 EmacsFreeXIMStyles (
736         XtAppContext app,
737         XrmValuePtr  toVal,
738         XtPointer    converter_data,
739         XrmValuePtr  args,
740         Cardinal    *num_args)
741 {
742 #ifdef DEBUG_XIM
743         stderr_out ("Converter data: %x\n", converter_data);
744         stderr_out ("EmacsFreeXIMStyles called\n");
745 #endif /* DEBUG_XIM */
746
747         if (*num_args != 0) {
748                 XtAppWarningMsg(app, "wrongParameters", "freeXIMStyles",
749                                 "XtToolkitError",
750                                 "Freeing an XIMStyles requires that "
751                                 "zero arguments be passwd",
752                                 (String *)NULL, (Cardinal *)NULL);
753                 return;
754         }
755
756         if (converter_data) {
757                 Boolean free_p    = (Boolean) (EMACS_INT)converter_data;
758                 XIMStyles *free_me = (XIMStyles *) toVal->addr;
759                 if (free_p) {
760                         /* use our free function */
761                         xfree(free_me->supported_styles);
762                 }
763         }
764 }
765
766 #if 0
767 /* O'Reilly XLib Programming Manual, pg. 371 */
768 /* Much nicer implementation than O'Reilly */
769 /* Choose the more `complicated', hence nicer, XIM input style */
770 static XIMStyle
771 BetterStyle (XIMStyle s, XIMStyle t)
772 {
773 #define CHECK_XIMStyle_BIT(bit)                                 \
774         if ((s ^ t) & bit) { return (s & bit) ? s : t; }
775
776         CHECK_XIMStyle_BIT (XIMPreeditCallbacks);
777         CHECK_XIMStyle_BIT (XIMPreeditPosition);
778         CHECK_XIMStyle_BIT (XIMPreeditArea);
779         CHECK_XIMStyle_BIT (XIMPreeditNothing);
780         CHECK_XIMStyle_BIT (XIMStatusCallbacks);
781         CHECK_XIMStyle_BIT (XIMStatusArea);
782         CHECK_XIMStyle_BIT (XIMStatusNothing);
783 #undef CHECK_XIMStyle_BIT
784         return s ? s : t ;
785 }
786 #endif /* 0 */
787
788 /* Choose the best style, given:
789  * - user preferences (already checked to be supported by SXEmacs)
790  * - styles supported by the input method */
791 #define DEFAULTStyle  (XIMPreeditNothing|XIMStatusNothing)
792 static XIMStyle
793 best_style (XIMStyles *user, XIMStyles *xim)
794 {
795         REGISTER int i, j;
796         for (i=0 ; i<user->count_styles ; i++) {
797                 for (j=0 ; j<xim->count_styles ; j++) {
798                         if (user->supported_styles[i] ==
799                             xim->supported_styles[j]) {
800                                 return user->supported_styles[i];
801                         }
802                 }
803         }
804         return DEFAULTStyle; /* Default Style */
805 }
806
807 /* These lisp-callable functions will be sealed until xim-leim is needed. 
808    Oct 22 1999 - kazz */
809 #if 0
810 /*
811  * External callable function for XIM
812  */
813 DEFUN ("x-open-xim", Fx_open_xim, 1, 1, 0, /*
814 Open the XIC on the frame if XIM is available.
815 Commonly, use this as \(x-open-xim \(selected-frame)).
816 If the frame is not on X device, return signal.
817 If XIC is created successfully return t.  If not return nil.
818 */
819        (frame))
820 {
821         struct frame *f;
822
823         CHECK_LIVE_FRAME (frame);
824         f = XFRAME (frame);
825         if (!FRAME_X_P (f)) {
826                 return signal_simple_error(
827                         "This frame is not on X device", frame);
828         }
829         XIM_init_frame (f);
830         return FRAME_X_XIC (f) ? Qt : Qnil;
831 }
832
833 DEFUN ("x-close-xim", Fx_close_xim, 1, 1, 0, /*
834 Close the XIC on the frame if it exists.
835 Commonly, use this as \(x-close-xim \(selected-frame)).
836 If the frame is not on X device, return signal.
837 Otherwise, it destroys the XIC if it exists, then returns t anyway.
838 */
839        (frame))
840 {
841         struct frame *f;
842         struct device *d;
843
844         CHECK_LIVE_FRAME (frame);
845         f = XFRAME (frame);
846         if (!FRAME_X_P (f)) {
847                 return signal_simple_error(
848                         "This frame is not on X device", frame);
849         }
850         d = XDEVICE (FRAME_DEVICE (f));
851         if (DEVICE_X_XIM (d)) {
852                 /* XDestroyIC (FRAME_X_XIC (XFRAME (f))); */
853                 FRAME_X_XIC (XFRAME (f)) = NULL;
854         }
855         return Qt;
856 }
857 #endif /* if 0 */
858
859 void
860 syms_of_input_method_xlib (void)
861 {
862         defsymbol (&Qxim_xlib, "xim-xlib");
863 #if 0 /* see above */
864         DEFSUBR (Fx_open_xim);
865         DEFSUBR (Fx_close_xim);
866 #endif
867 }
868
869 void
870 vars_of_input_method_xlib (void)
871 {
872         Fprovide (intern ("xim"));
873 }
874
875
876 /* ====================================================================== */
877 /* Internal Debugging Routines */
878 /* ====================================================================== */
879 #ifdef DEBUG_SXEMACS
880
881 void
882 describe_XIM (XIM xim)
883 {
884         XIMStyles *styles;
885
886         /* Print locale of XIM */
887         stderr_out ("\nXIM Locale of IM: %s\n", XLocaleOfIM(xim));
888
889         /* List supported input method styles */
890         XGetIMValues(xim, XNQueryInputStyle, &styles, NULL);
891
892         stderr_out ("\n%d input style(s) supported by input method.\n",
893                     styles->count_styles);
894
895 #ifdef DEBUG_XIM
896         for (int i=0; i < styles->count_styles; i++) {
897                 describe_XIMStyle (styles->supported_styles[i]);
898         }
899 #endif /* DEBUG_XIM */
900         XFree(styles);
901 }
902
903 void
904 describe_XFontSet (XFontSet fontset)
905 {
906         XFontStruct **font_struct_list;
907         char **font_name_list;
908         int count, i;
909
910         if (fontset == NULL) {
911                 stderr_out ("NULL\n");
912                 return;
913         }
914
915         count = XFontsOfFontSet(fontset, &font_struct_list, &font_name_list);
916         stderr_out ( "%d font(s) available:\n", count);
917         for (i=0 ; i < count ; i++) {
918                 stderr_out ("Font: %s\n", *(font_name_list+i));
919         }
920 }
921
922 void
923 describe_Status (Status status)
924 {
925 #define DESCRIBE_STATUS(value)                                          \
926         if (status == value) {                                          \
927                 stderr_out ("Status: " #value "\n");                    \
928         }
929
930         DESCRIBE_STATUS (XBufferOverflow);
931         DESCRIBE_STATUS (XLookupNone);
932         DESCRIBE_STATUS (XLookupKeySym);
933         DESCRIBE_STATUS (XLookupBoth);
934         DESCRIBE_STATUS (XLookupChars);
935 #undef DESCRIBE_STATUS
936 }
937
938 void
939 describe_Window (Window win)
940 {
941         char xwincmd[128];
942         int sz = snprintf (xwincmd, sizeof(xwincmd), 
943                            "xwininfo -id 0x%x >&2; xwininfo -events -id 0x%x >&2",
944                            (int) win, (int) win);
945         if ( sz >= 0 && (size_t) sz < sizeof(xwincmd) ) {
946                 int exit_code = system (xwincmd);
947                 if (  exit_code != 0 )
948                         stderr_out("command '%s' failed with exit code %d", 
949                                    xwincmd, exit_code);
950         } else
951                 stderr_out("Could not generate wininfo command line");
952 }
953
954 void
955 describe_XIC (XIC xic)
956 {
957         XIMStyle style;
958         Window client_win=0, focus_win=0;
959         char *resourceName  = NULL;
960         char *resourceClass = NULL;
961         char *bad_arg       = NULL;
962         unsigned long filter_mask = NoEventMask;
963         XVaNestedList p_list, s_list;
964         XFontSet      p_fontset = NULL, s_fontset = NULL;
965         Pixel         p_fg=0, p_bg = 0, s_fg=0, s_bg = 0;
966         XRectangle   *p_area   = NULL, *s_area   = NULL;
967         XRectangle   *p_needed = NULL, *s_needed = NULL;
968         XPoint       *p_spot = NULL;
969
970         /* Check for valid input context and method */
971         if (!xic) {
972                 stderr_out ("Input method is NULL\n");
973         }
974         if (!XIMOfIC(xic)) {
975                 stderr_out ("XIMOfIC() returns NULL\n");
976         }
977         /* Print out Input Context Attributes */
978         p_list = XVaCreateNestedList (0,
979                                       XNFontSet,      &p_fontset,
980                                       XNArea,         &p_area,
981                                       XNAreaNeeded,   &p_needed,
982                                       XNSpotLocation, &p_spot,
983                                       XNForeground,   &p_fg,
984                                       XNBackground,   &p_bg,
985                                       NULL);
986
987         s_list = XVaCreateNestedList (0,
988                                       XNFontSet,      &s_fontset,
989                                       XNArea,         &s_area,
990                                       XNAreaNeeded,   &s_needed,
991                                       XNForeground,   &s_fg,
992                                       XNBackground,   &s_bg,
993                                       NULL);
994
995         bad_arg = XGetICValues(xic,
996                                XNInputStyle,        &style,
997                                XNFilterEvents,      &filter_mask,
998                                XNClientWindow,      &client_win,
999                                XNFocusWindow,       &focus_win,
1000                                XNResourceName,      &resourceName,
1001                                XNResourceClass,     &resourceClass,
1002                                XNPreeditAttributes, p_list,
1003                                XNStatusAttributes,  s_list,
1004                                NULL);
1005         XFree(p_list);
1006         XFree(s_list);
1007
1008         if (bad_arg != NULL) {
1009                 stderr_out ("Couldn't get IC value: %s\n", bad_arg);
1010         }
1011         stderr_out ("\nInput method context attributes:\n");
1012         stderr_out ("Style: "); describe_XIMStyle (style);
1013         stderr_out ("Client window: %lx\n", (unsigned long int)client_win);
1014         stderr_out ("Focus window: %lx\n",  (unsigned long int)focus_win);
1015         stderr_out ("Preedit:\n");
1016         describe_XRectangle ("  Area", p_area);
1017         describe_XRectangle ("  Area needed", p_needed);
1018         stderr_out ("  foreground: %lx\n", (unsigned long int)p_fg);
1019         stderr_out ("  background: %lx\n", (unsigned long int)p_bg);
1020         stderr_out ("  fontset: "); describe_XFontSet (p_fontset);
1021         stderr_out ("Status:\n");
1022         describe_XRectangle ("  Area", s_area);
1023         describe_XRectangle ("  Area needed", s_needed);
1024         stderr_out ("  foreground: %lx\n", (unsigned long int)s_fg);
1025         stderr_out ("  background: %lx\n", (unsigned long int)s_bg);
1026         stderr_out ("  fontset: \n"); describe_XFontSet (s_fontset);
1027         stderr_out ("XNResourceName: %s\n",  resourceName
1028                     ? resourceName  : "NULL");
1029         stderr_out ("XNResourceClass: %s\n", resourceClass
1030                     ? resourceClass : "NULL");
1031         stderr_out ("XNFilterEvents: "); describe_event_mask (filter_mask);
1032 }
1033
1034 void
1035 describe_XRectangle (char *name, XRectangle *r)
1036 {
1037         if (r == NULL) {
1038                 stderr_out ("%s: NULL\n", name);
1039         } else {
1040                 stderr_out ("%s: x=%d y=%d w=%d h=%d\n",
1041                             name, r->x, r->y, r->width, r->height);
1042         }
1043 }
1044
1045 /* Print out elements of Event mask */
1046 /* Defines from X11/X.h */
1047 void
1048 describe_event_mask (unsigned long mask)
1049 {
1050 #define DESCRIBE_EVENT_MASK(bit) if ((bit) & mask) stderr_out (#bit " ")
1051         DESCRIBE_EVENT_MASK (NoEventMask);
1052         DESCRIBE_EVENT_MASK (KeyPressMask);
1053         DESCRIBE_EVENT_MASK (KeyReleaseMask);
1054         DESCRIBE_EVENT_MASK (ButtonPressMask);
1055         DESCRIBE_EVENT_MASK (ButtonReleaseMask);
1056         DESCRIBE_EVENT_MASK (EnterWindowMask);
1057         DESCRIBE_EVENT_MASK (LeaveWindowMask);
1058         DESCRIBE_EVENT_MASK (PointerMotionMask);
1059         DESCRIBE_EVENT_MASK (PointerMotionHintMask);
1060         DESCRIBE_EVENT_MASK (Button1MotionMask);
1061         DESCRIBE_EVENT_MASK (Button2MotionMask);
1062         DESCRIBE_EVENT_MASK (Button3MotionMask);
1063         DESCRIBE_EVENT_MASK (Button4MotionMask);
1064         DESCRIBE_EVENT_MASK (Button5MotionMask);
1065         DESCRIBE_EVENT_MASK (ButtonMotionMask);
1066         DESCRIBE_EVENT_MASK (KeymapStateMask);
1067         DESCRIBE_EVENT_MASK (ExposureMask);
1068         DESCRIBE_EVENT_MASK (VisibilityChangeMask);
1069         DESCRIBE_EVENT_MASK (StructureNotifyMask);
1070         DESCRIBE_EVENT_MASK (ResizeRedirectMask);
1071         DESCRIBE_EVENT_MASK (SubstructureNotifyMask);
1072         DESCRIBE_EVENT_MASK (SubstructureRedirectMask);
1073         DESCRIBE_EVENT_MASK (FocusChangeMask);
1074         DESCRIBE_EVENT_MASK (PropertyChangeMask);
1075         DESCRIBE_EVENT_MASK (ColormapChangeMask);
1076         DESCRIBE_EVENT_MASK (OwnerGrabButtonMask);
1077 #undef DESCRIBE_EVENT_MASK
1078         stderr_out("\n");
1079 }
1080
1081 void
1082 describe_XIMStyle (XIMStyle style)
1083 {
1084 #define DESCRIBE_STYLE(bit)                     \
1085         if (bit & style) {                      \
1086                 stderr_out (#bit " ");          \
1087         }
1088
1089         DESCRIBE_STYLE (XIMPreeditArea);
1090         DESCRIBE_STYLE (XIMPreeditCallbacks);
1091         DESCRIBE_STYLE (XIMPreeditPosition);
1092         DESCRIBE_STYLE (XIMPreeditNothing);
1093         DESCRIBE_STYLE (XIMPreeditNone);
1094         DESCRIBE_STYLE (XIMStatusArea);
1095         DESCRIBE_STYLE (XIMStatusCallbacks);
1096         DESCRIBE_STYLE (XIMStatusNothing);
1097         DESCRIBE_STYLE (XIMStatusNone);
1098 #undef DESCRIBE_STYLE
1099         stderr_out("\n");
1100 }
1101
1102 void
1103 describe_XIMStyles (XIMStyles *p)
1104 {
1105         int i;
1106         stderr_out ("%d Style(s):\n", p->count_styles);
1107         for (i=0; i<p->count_styles ; i++) {
1108                 describe_XIMStyle (p->supported_styles[i]);
1109         }
1110 }
1111
1112 #endif /* DEBUG_SXEMACS */
1113
1114 /* Random cruft follows */
1115
1116 #if 0
1117 static void
1118 Unit_Test (struct frame *f, char * s)
1119 /* mrb unit testing */
1120 {
1121         XrmValue fromVal, toVal;
1122
1123         fromVal.addr = s;
1124         fromVal.size = strlen (s);
1125         toVal.addr = (XtPointer) &user_preferred_XIMStyles;
1126         toVal.size = sizeof (XIMStyles);
1127
1128         if (XtConvertAndStore (FRAME_X_TEXT_WIDGET (f), XtRString, &fromVal,
1129                                XtRXimStyles, &toVal) != False) {
1130                 stderr_out ("Unit_Test: fromVal.addr=0x%x\n",fromVal.addr);
1131                 stderr_out ("Unit_Test: fromVal.size=%d\n",  fromVal.size);
1132                 stderr_out ("Unit_Test:   toVal.addr=0x%x\n",  toVal.addr);
1133                 stderr_out ("Unit_Test:   toVal.size=%d\n",    toVal.size);
1134                 describe_XIMStyles ((XIMStyles *) toVal.addr);
1135         }
1136 }
1137 #endif
1138 #endif /* XIM_XLIB only */
1139
1140 #if 0
1141 /* Get a fontset for IM to use */
1142 void
1143 x_init_fontset (struct device *d)
1144 {
1145         Display *dpy = DEVICE_X_DISPLAY (d);
1146         XFontSet fontset;
1147         char ** missing_charsets;
1148         int num_missing_charsets;
1149         char * default_string;
1150         /*  char * font_set_string = "-dt-interface user-medium-r-normal\
1151             -s*-*-*-*-*-*-*-*-*";*/
1152         char *font_set_string =
1153                 "-dt-interface user-medium-r-normal-s*-*-*-*-*-*-*-*-*,"
1154                 "-misc-fixed-medium-r-normal--14-130-75-75-c-70-jisx0201.1976-0,"
1155                 "-misc-fixed-medium-r-normal--14-130-75-75-c-140-jisx0208.1983-0,"
1156                 "-misc-fixed-medium-r-normal--14-130-75-75-c-70-jisx0201.1976-0";
1157
1158         DEVICE_X_FONTSET (d) = fontset =
1159                 XCreateFontSet (dpy, font_set_string,
1160                                 &missing_charsets,
1161                                 &num_missing_charsets,
1162                                 &default_string);
1163
1164         if (fontset == NULL) {
1165                 stderr_out("Unable to create fontset from string:\n%s\n",
1166                            font_set_string);
1167                 return;
1168         }
1169         if (num_missing_charsets > 0) {
1170                 int i;
1171                 stderr_out ("\nMissing charsets for fontset %s:\n",
1172                             font_set_string);
1173                 for (i=0; i < num_missing_charsets; i++) {
1174                         stderr_out ("%s\n", missing_charsets[i]);
1175                 }
1176                 XFreeStringList (missing_charsets);
1177                 stderr_out ("Default string: %s\n", default_string);
1178         }
1179
1180 #ifdef DEBUG_XIM
1181         describe_XFontSet (fontset);
1182 #endif
1183 }
1184 #endif /* 0 */