/* xim.c - XIM handlers, for multiple locale input methods. Copyright (C) 1996-2000 Paul Sheer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ /* #define DEBUG */ /* #define DEBUG_ENTRY */ #include #include #include #include #include #include #include "lkeysym.h" #include "stringtools.h" #include "app_glob.c" #include "coolwidget.h" #include "coollocal.h" #include "font.h" #include "mad.h" /* A lot of this stuff is hatched from rxvt-2.6.1: */ /*--------------------------------*-C-*---------------------------------* * File: command.c *----------------------------------------------------------------------* * $Id: command.c,v 1.85.2.23 1999/08/12 16:32:39 mason Exp $ * * All portions of code are copyright by their respective author/s. * Copyright (C) 1992 John Bovey, University of Kent at Canterbury * - original version * Copyright (C) 1994 Robert Nation * - extensive modifications * Copyright (C) 1995 Garrett D'Amore * - vt100 printing * Copyright (C) 1995 Steven Hirsch * - X11 mouse report mode and support for * DEC "private mode" save/restore functions. * Copyright (C) 1995 Jakub Jelinek * - key-related changes to handle Shift+function * keys properly. * Copyright (C) 1997 MJ Olesen * - extensive modifications * Copyright (C) 1997 Raul Garcia Garcia * - modification and cleanups for Solaris 2.x * and Linux 1.2.x * Copyright (C) 1997,1998 Oezguer Kesim * Copyright (C) 1998 Geoff Wing * Copyright (C) 1998 Alfredo K. Kojima * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *----------------------------------------------------------------------*/ #ifndef HAVE_XPOINTER typedef char *XPointer; #endif #ifdef USE_XIM static void xim_print_error (char *s,...) { char k[1024]; va_list ap; va_start (ap, s); vsprintf (k, s, ap); fprintf (stderr, "%s: %s\n", CAppName, k); va_end (ap); } int option_use_xim = 1; static void IMInstantiateCallback (Display * display, XPointer client_data, XPointer call_data); void init_xlocale (void) { #ifdef LC_CTYPE char *xim_locale = NULL; xim_locale = setlocale (LC_CTYPE, 0); CPushFont ("editor", 0); if (!xim_locale) { xim_print_error ("Setting locale failed."); } else if (!current_font->font_set) { xim_print_error ("Font set not loaded - cannot create input method."); } else { if (option_use_xim) XRegisterIMInstantiateCallback (CDisplay, NULL, NULL, NULL, IMInstantiateCallback, NULL); } CPopFont (); #else option_use_xim = 0; #endif } void setPosition (CWidget * child, CWidget * w, XPoint * pos) { #ifdef dont_forget_this /* FIXME */ XWindowAttributes xwa; XGetWindowAttributes (CDisplay, TermWin.vt, &xwa); pos->x = Col2Pixel (screen.cur.col) + xwa.x; pos->y = Height2Pixel ((screen.cur.row + 1)) + xwa.y; #else pos->x = w->width / 2; pos->y = w->height / 2; #endif } static void IMSendSpot (Window window) { XPoint spot; XVaNestedList preedit_attr; XIMStyle input_style; CWidget *child, *w; if (!window) return; if (!(child = CWidgetOfWindow (window))) return; if (child->mainid) { w = CWidgetOfWindow (child->mainid); } else { w = child; } if (!w->input_context) return; XGetICValues (w->input_context, XNInputStyle, &input_style, NULL); if (!(input_style & XIMPreeditPosition)) return; setPosition (child, w, &spot); preedit_attr = XVaCreateNestedList (0, XNSpotLocation, &spot, NULL); XSetICValues (w->input_context, XNPreeditAttributes, preedit_attr, NULL); XFree (preedit_attr); } KeySym key_sym_xlat (XEvent * ev, char *x_lat) { static KeySym r; static int len = 0; static KeySym keysym = 0; static XComposeStatus compose = {NULL, 0}; static unsigned char kbuf[512] = ""; static int valid_keysym = 1; Status status_return = 0; if (x_lat) *x_lat = '\0'; if (ev->type != KeyPress && ev->type != KeyRelease) return 0; /* we mustn't call this twice with the same event */ if (ev->xkey.x_root == 31234) goto no_repeat_call; ev->xkey.x_root = 31234; keysym = 0; len = 0; if (ev->type == KeyRelease) { len = XLookupString (&ev->xkey, (char *) kbuf, sizeof (kbuf), &keysym, 0); if (!len && (keysym >= 0x0100) && (keysym < 0x0800)) { len = 1; kbuf[0] = (keysym & 0xFF); } } else if (CIC) { CWidget *w; IMSendSpot (ev->xkey.window); w = CWidgetOfWindow (ev->xkey.window); if (!w) return 0; if (w->mainid) w = CWidgetOfWindow (w->mainid); if (w->input_context != CIC) { printf ("w->input_context != CIC --> Huh?\n"); /* FIXME:this seems to actually happen */ return 0; } len = XmbLookupString (CIC, &ev->xkey, (char *) kbuf, sizeof (kbuf), &keysym, &status_return); valid_keysym = ((status_return == XLookupKeySym) || (status_return == XLookupBoth)); } else { keysym = 0; len = XLookupString (&ev->xkey, (char *) kbuf, sizeof (kbuf), &keysym, &compose); if (!len && (keysym >= 0x0100) && (keysym < 0x0800)) { len = 1; kbuf[0] = (keysym & 0xFF); } } no_repeat_call: if (x_lat) { if (len > 0) { if (len > 7) len = 7; memcpy (x_lat, kbuf, len); x_lat[len] = '\0'; } } r = ((keysym >= 0x0100) && (keysym < 0x0800)) ? (valid_keysym ? kbuf[0] : 0) : (valid_keysym ? keysym : 0); return r; } void setSize (CWidget * w, XRectangle * size) { #ifdef RXVT_SOURCE /* FIXME */ size->x = TermWin_internalBorder; size->y = TermWin_internalBorder; size->width = Width2Pixel (TermWin.ncol); size->height = Height2Pixel (TermWin.nrow); #else size->x = 0; size->y = 0; size->width = w->width; size->height = w->height; #endif } void setColor (CWidget * w, unsigned long *fg, unsigned long *bg) { #ifdef dont_forget_this /* FIXME */ *fg = PixColors[Color_fg]; *bg = PixColors[Color_bg]; #else *fg = COLOR_BLACK; *bg = COLOR_WHITE; #endif } void setPreeditArea (CWidget * w, XRectangle * preedit_rect, XRectangle * status_rect, XRectangle * needed_rect) { #ifdef RXVT_SOURCE /* FIXME */ preedit_rect->x = needed_rect->width + (scrollbar_visible () && !(Options & Opt_scrollBar_right) ? (SB_WIDTH + sb_shadow * 2) : 0); preedit_rect->y = Height2Pixel (TermWin.nrow - 1) + ((menuBar.state == 1) ? menuBar_TotalHeight () : 0); preedit_rect->width = Width2Pixel (TermWin.ncol + 1) - needed_rect->width + (!(Options & Opt_scrollBar_right) ? (SB_WIDTH + sb_shadow * 2) : 0); preedit_rect->height = Height2Pixel (1); status_rect->x = (scrollbar_visible () && !(Options & Opt_scrollBar_right)) ? (SB_WIDTH + sb_shadow * 2) : 0; status_rect->y = Height2Pixel (TermWin.nrow - 1) + ((menuBar.state == 1) ? menuBar_TotalHeight () : 0); status_rect->width = needed_rect->width ? needed_rect->width : Width2Pixel (TermWin.ncol + 1); status_rect->height = Height2Pixel (1); #else preedit_rect->x = 0; preedit_rect->y = 0; preedit_rect->width = w->width; preedit_rect->height = w->height - 20; status_rect->x = 0; status_rect->y = w->height - 20; status_rect->width = w->width; status_rect->height = 20; #endif } static long destroy_input_context (CWidget * w) { w->input_context = 0; return 0; } void IMDestroyCallback (XIM xim, XPointer client_data, XPointer call_data) { XRegisterIMInstantiateCallback (CDisplay, NULL, NULL, NULL, IMInstantiateCallback, NULL); for_all_widgets ((void *) destroy_input_context, 0, 0); CIC = 0; } /* returns zero on error */ XIMStyle get_input_style (void) { int found = 0; char tmp[1024] = "", *s = 0; char *end = 0, *next_s = 0; XIMStyle input_style = 0; XIMStyles *xim_styles = NULL; if (!CIM) { if (option_use_xim) xim_print_error ("Trying to get input_style, but Input Method is null."); return 0; } if (XGetIMValues (CIM, XNQueryInputStyle, &xim_styles, NULL) || !xim_styles) { xim_print_error ("input method doesn't support any style"); return 0; } #ifdef RXVT_SOURCE strncpy (tmp, (rs[Rs_preeditType] ? rs[Rs_preeditType] : "OverTheSpot,OffTheSpot,Root"), sizeof (tmp) - 1); #else strncpy (tmp, "OverTheSpot,OffTheSpot,Root", sizeof (tmp) - 1); #endif for (found = 0, s = tmp; *s && !found; s = next_s + 1) { unsigned short i; for (; *s && isspace (*s); s++); if (!*s) break; for (end = s; (*end && (*end != ',')); end++); for (next_s = end--; ((end >= s) && isspace (*end)); end--); *(end + 1) = '\0'; if (!strcmp (s, "OverTheSpot")) input_style = (XIMPreeditPosition | XIMStatusNothing); else if (!strcmp (s, "OffTheSpot")) input_style = (XIMPreeditArea | XIMStatusArea); else if (!strcmp (s, "Root")) input_style = (XIMPreeditNothing | XIMStatusNothing); for (i = 0; i < xim_styles->count_styles; i++) if (input_style == xim_styles->supported_styles[i]) { found = 1; break; } } XFree (xim_styles); if (found == 0) { xim_print_error ("input method doesn't support my preedit type"); return 0; } if ((input_style != (XIMPreeditNothing | XIMStatusNothing)) && (input_style != (XIMPreeditArea | XIMStatusArea)) && (input_style != (XIMPreeditPosition | XIMStatusNothing))) { xim_print_error ("This program does not support the preedit type"); return 0; } return input_style; } long create_input_context (CWidget * w, XIMStyle input_style) { XVaNestedList preedit_attr = 0; XVaNestedList status_attr = 0; XPoint spot; XRectangle rect, status_rect, needed_rect; XIMCallback ximcallback; unsigned long fg, bg; if (w->kind != C_WINDOW_WIDGET) return 0; if (w->mainid) return 0; if (w->input_context) return 0; if (!CIM) return 1; if (!input_style) return 1; ximcallback.callback = IMDestroyCallback; ximcallback.client_data = NULL; if (input_style & XIMPreeditPosition) { setSize (w, &rect); setPosition (0, w, &spot); setColor (w, &fg, &bg); preedit_attr = XVaCreateNestedList (0, XNArea, &rect, XNSpotLocation, &spot, XNForeground, fg, XNBackground, bg, XNFontSet, current_font->font_set, NULL); } else if (input_style & XIMPreeditArea) { setColor (w, &fg, &bg); /* * The necessary width of preedit area is unknown * until create input context. */ needed_rect.width = 0; setPreeditArea (w, &rect, &status_rect, &needed_rect); preedit_attr = XVaCreateNestedList (0, XNArea, &rect, XNForeground, fg, XNBackground, bg, XNFontSet, current_font->font_set, NULL); status_attr = XVaCreateNestedList (0, XNArea, &status_rect, XNForeground, fg, XNBackground, bg, XNFontSet, current_font->font_set, NULL); } w->input_context = XCreateIC (CIM, XNInputStyle, input_style, XNClientWindow, w->winid, XNFocusWindow, w->winid, XNDestroyCallback, &ximcallback, preedit_attr ? XNPreeditAttributes : NULL, preedit_attr, status_attr ? XNStatusAttributes : NULL, status_attr, NULL); if (preedit_attr) XFree (preedit_attr); if (status_attr) XFree (status_attr); if (!w->input_context) { xim_print_error ("Failed to create input context for widget %s", w->ident); return 1; } return 0; } long set_status_position (CWidget * w) { XIMStyle input_style; XRectangle preedit_rect, status_rect, *needed_rect = 0; XVaNestedList preedit_attr, status_attr; if (!w->input_context) return 0; XGetICValues (w->input_context, XNInputStyle, &input_style, NULL); if (input_style & XIMPreeditArea) { /* Getting the necessary width of preedit area */ status_attr = XVaCreateNestedList (0, XNAreaNeeded, &needed_rect, NULL); XGetICValues (w->input_context, XNStatusAttributes, status_attr, NULL); XFree (status_attr); setPreeditArea (w, &preedit_rect, &status_rect, needed_rect); preedit_attr = XVaCreateNestedList (0, XNArea, &preedit_rect, NULL); status_attr = XVaCreateNestedList (0, XNArea, &status_rect, NULL); XSetICValues (w->input_context, XNPreeditAttributes, preedit_attr, XNStatusAttributes, status_attr, NULL); XFree (preedit_attr); XFree (status_attr); } return 0; } static void IMInstantiateCallback (Display * display, XPointer client_data, XPointer call_data) { char *p; XIMStyle input_style = 0; XIMCallback ximcallback; if (CIC) return; ximcallback.callback = IMDestroyCallback; ximcallback.client_data = NULL; #ifdef RXVT_SOURCE if (rs[Rs_inputMethod] && *rs[Rs_inputMethod]) { strncpy (tmp, option_imput_method, sizeof (tmp) - 1); for (s = tmp; *s; s = next_s + 1) { for (; *s && isspace (*s); s++); if (!*s) break; for (end = s; (*end && (*end != ',')); end++); for (next_s = end--; ((end >= s) && isspace (*end)); end--); *(end + 1) = '\0'; if (*s) { strcpy (buf, "@im="); strncat (buf, s, sizeof (buf) - 4 - 1); if ((p = XSetLocaleModifiers (buf)) != NULL && *p && (CIM = XOpenIM (CDisplay, NULL, NULL, NULL)) != NULL) break; } if (!*next_s) break; } } #endif /* try with XMODIFIERS env. var. */ if (CIM == NULL && (p = XSetLocaleModifiers ("")) != NULL && *p) CIM = XOpenIM (CDisplay, NULL, NULL, NULL); /* try with XMODIFIERS env. var. */ if (CIM == NULL && (p = XSetLocaleModifiers ("@im=control")) != NULL && *p) CIM = XOpenIM (CDisplay, NULL, NULL, NULL); /* try with no modifiers base */ if (CIM == NULL && (p = XSetLocaleModifiers ("@im=none")) != NULL && *p) CIM = XOpenIM (CDisplay, NULL, NULL, NULL); if (!CIM) return; /* got the Input Method, now set up all dialogs */ XSetIMValues (CIM, XNDestroyCallback, &ximcallback, NULL); if (!(input_style = get_input_style ())) { XCloseIM (CIM); CIM = 0; } CPushFont ("editor", 0); if (for_all_widgets ((void *) create_input_context, (void *) input_style, 0)) { input_style = 0; XCloseIM (CIM); CIM = 0; } CPopFont (); if (input_style & XIMPreeditArea) for_all_widgets ((void *) set_status_position, 0, 0); } #else int option_use_xim = 0; KeySym key_sym_xlat (XEvent * ev, char *x_lat) { static int len = 0; static KeySym keysym = 0; static XComposeStatus compose = {NULL, 0}; static unsigned char kbuf[512] = ""; static int valid_keysym = 1; Status status_return = 0; if (x_lat) *x_lat = '\0'; if (ev->type != KeyPress && ev->type != KeyRelease) return 0; /* we mustn't call this twice with the same event */ if (ev->xkey.x_root == 31234) goto no_repeat_call; ev->xkey.x_root = 31234; keysym = 0; len = 0; if (ev->type == KeyRelease) { len = XLookupString (&ev->xkey, (char *) kbuf, sizeof (kbuf), &keysym, 0); if (!len && (keysym >= 0x0100) && (keysym < 0x0800)) { len = 1; kbuf[0] = (keysym & 0xFF); } } else { keysym = 0; len = XLookupString (&ev->xkey, (char *) kbuf, sizeof (kbuf), &keysym, &compose); if (!len && (keysym >= 0x0100) && (keysym < 0x0800)) { len = 1; kbuf[0] = (keysym & 0xFF); } } no_repeat_call: if (x_lat) { if (len > 0) { if (len > 7) len = 7; memcpy (x_lat, kbuf, len); x_lat[len] = '\0'; } } return valid_keysym ? keysym : 0; } #endif