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