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