/* * i18n_input.c * * Written by David Flanagan. Copyright 1991, O'Reilly && Associates. * This program is freely distributable without licensing fees and * is provided without guarantee or warranty expressed or implied. * This program is -not- in the public domain. * * This program demonstrates some of the X11R5 internationalized text * input functions. It creates a very simple window, connects to an * input method, and displays composed text obtained by calling * XwcLookupString. It backspaces when it receives the Backspace or * Delete keysyms. * * To run this program successfully, you must have an input method running. * Because there are no input methods as part of the core X11R5 * distribution, this may be difficult. If your Xlib uses the Xsi * implementation of the X11R5 internationalization features, you can * use the input method in contrib/im/Xsi. In order to run this program, * I had to do the following: * * 1) build everything in contrib/im/Xsi. * 2) install everything in contrib/im/Xsi. This involved * installing a number of files under /usr/local/lib/wnn, and * adding a new user "wnn" to the /etc/passwd file. * 3) start the "translation server" contrib/im/Xsi/Wnn/jserver/jserver * 4) start the "input manager" contrib/im/Xsi/Xwnmo/xwnmo/xwnmo * which was also installed in /usr/bin/X11. * 5) set the XMODIFIERS environment variable to "@im=_XWNMO". * 6) set the LANG environment variable to something appropriate, * ja_JP.ujis, for example. * * With these steps accomplished, I was able to run the program and type * Latin characters, but I was never able to figure out how to actually * make use of the input method to input Japanese. Since the Xsi input * method is contributed software, it may have been updated since this * program was written, and the above list may no longer be correct. * * This program has not been tested with the Ximp implementation. * * Finally, note that this program contains a work-around for a bug * in the Xsi implementation of XwcLookupString. If you are using * the Ximp implementation, or if the bug has been fixed in your Xlib, * you will need to undo the workaround. See the comment below, near * the call to XwcLookupString. */ #include #include #include #include /* * include or the non-standard X substitutes * depending on the X_LOCALE compilation flag */ #include /* * This function chooses the "more desirable" of two input styles. The * style with the more complicated PreEdit style is returned, and if the * styles have the same PreEdit styles, then the style with the more * complicated Status style is returned. There is no "official" way to * order interaction styles. This one makes the most sense to me. * This is a long procedure for a simple heuristic. */ XIMStyle ChooseBetterStyle(style1,style2) XIMStyle style1, style2; { XIMStyle s,t; XIMStyle preedit = XIMPreeditArea | XIMPreeditCallbacks | XIMPreeditPosition | XIMPreeditNothing | XIMPreeditNone; XIMStyle status = XIMStatusArea | XIMStatusCallbacks | XIMStatusNothing | XIMStatusNone; if (style1 == 0) return style2; if (style2 == 0) return style1; if ((style1 & (preedit | status)) == (style2 & (preedit | status))) return style1; s = style1 & preedit; t = style2 & preedit; if (s != t) { if (s | t | XIMPreeditCallbacks) return (s == XIMPreeditCallbacks)?style1:style2; else if (s | t | XIMPreeditPosition) return (s == XIMPreeditPosition)?style1:style2; else if (s | t | XIMPreeditArea) return (s == XIMPreeditArea)?style1:style2; else if (s | t | XIMPreeditNothing) return (s == XIMPreeditNothing)?style1:style2; } else { /* if preedit flags are the same, compare status flags */ s = style1 & status; t = style2 & status; if (s | t | XIMStatusCallbacks) return (s == XIMStatusCallbacks)?style1:style2; else if (s | t | XIMStatusArea) return (s == XIMStatusArea)?style1:style2; else if (s | t | XIMStatusNothing) return (s == XIMStatusNothing)?style1:style2; } } void GetPreferredGeometry(ic, name, area) XIC ic; char *name; /* XNPreEditAttributes or XNStatusAttributes */ XRectangle *area; /* the constraints on the area */ { XVaNestedList list; list = XVaCreateNestedList(0, XNAreaNeeded, area, NULL); /* set the constraints */ XSetICValues(ic, name, list, NULL); /* Now query the preferred size */ /* The Xsi input method, Xwnmo, seems to ignore the constraints, */ /* but we're not going to try to enforce them here. */ XGetICValues(ic, name, list, NULL); XFree(list); } void SetGeometry(ic, name, area) XIC ic; char *name; /* XNPreEditAttributes or XNStatusAttributes */ XRectangle *area; /* the actual area to set */ { XVaNestedList list; list = XVaCreateNestedList(0, XNArea, area, NULL); XSetICValues(ic, name, list, NULL); XFree(list); } main(argc, argv) int argc; char *argv[]; { Display *dpy; int screen; Window win; GC gc; XGCValues gcv; XEvent event; XFontSet fontset; XIM im; XIC ic; XIMStyles *im_supported_styles; XIMStyle app_supported_styles; XIMStyle style; XIMStyle best_style; XVaNestedList list; long im_event_mask; XRectangle preedit_area; XRectangle status_area; char *program_name = argv[0]; char **missing_charsets; int num_missing_charsets = 0; char *default_string; wchar_t string[200]; int str_len = 0; int i; /* * The error messages in this program are all in English. * In a truely internationalized program, they would not * be hardcoded; they would be looked up in a database of * some sort. */ if (setlocale(LC_ALL, "") == NULL) { (void) fprintf(stderr, "%s: cannot set locale.\n",program_name); exit(1); } if ((dpy = XOpenDisplay(NULL)) == NULL) { (void) fprintf(stderr, "%s: cannot open Display.\n", program_name); exit(1); } if (!XSupportsLocale()) { (void) fprintf(stderr, "%s: X does not support locale \"%s\".\n", program_name, setlocale(LC_ALL, NULL)); exit(1); } if (XSetLocaleModifiers("") == NULL) { (void) fprintf(stderr, "%s: Warning: cannot set locale modifiers.\n", argv[0]); } /* * Create the fontset. */ fontset = XCreateFontSet(dpy, "-adobe-helvetica-*-r-*-*-*-120-*-*-*-*-*-*,\ -misc-fixed-*-r-*-*-*-130-*-*-*-*-*-*", &missing_charsets, &num_missing_charsets, &default_string); /* * if there are charsets for which no fonts can * be found, print a warning message. */ if (num_missing_charsets > 0) { (void)fprintf(stderr, "%s: The following charsets are missing:\n", program_name); for(i=0; i < num_missing_charsets; i++) (void)fprintf(stderr, "%s: \t%s\n", program_name, missing_charsets[i]); XFreeStringList(missing_charsets); (void)fprintf(stderr, "%s: The string \"%s\" will be used in place\n", program_name, default_string); (void)fprintf(stderr, "%s: of any characters from those sets.\n", program_name); } screen = DefaultScreen(dpy); win = XCreateSimpleWindow(dpy, RootWindow(dpy, screen), 0, 0, 400, 100, 2, WhitePixel(dpy,screen),BlackPixel(dpy,screen)); gc = XCreateGC(dpy,win,0,&gcv); XSetForeground(dpy,gc,WhitePixel(dpy,screen)); XSetBackground(dpy,gc,BlackPixel(dpy,screen)); /* Connect to an input method. */ /* In this example, we don't pass a resource database */ if ((im = XOpenIM(dpy, NULL, NULL, NULL)) == NULL) { (void)fprintf(stderr, "Couldn't open input method\n"); exit(1); } /* set flags for the styles our application can support */ app_supported_styles = XIMPreeditNone | XIMPreeditNothing | XIMPreeditArea; app_supported_styles |= XIMStatusNone | XIMStatusNothing | XIMStatusArea; /* figure out which styles the IM can support */ XGetIMValues(im, XNQueryInputStyle, &im_supported_styles, NULL); /* * now look at each of the IM supported styles, and * chose the "best" one that we can support. */ best_style = 0; for(i=0; i < im_supported_styles->count_styles; i++) { style = im_supported_styles->supported_styles[i]; if ((style & app_supported_styles) == style) /* if we can handle it */ best_style = ChooseBetterStyle(style, best_style); } /* if we couldn't support any of them, print an error and exit */ if (best_style == 0) { (void)fprintf(stderr, "%s: application and program do not share a\n", argv[0]); (void)fprintf(stderr, "%s: commonly supported interaction style.\n", argv[0]); exit(1); } /* * Now go create an IC using the style we chose. * Also set the window and fontset attributes now. */ list = XVaCreateNestedList(0,XNFontSet,fontset,NULL); ic = XCreateIC(im, XNInputStyle, best_style, XNClientWindow, win, XNPreeditAttributes, list, XNStatusAttributes, list, NULL); XFree(list); if (ic == NULL) { (void) fprintf(stderr, "Couldn't create input context\n"); exit(1); } XGetICValues(ic, XNFilterEvents, &im_event_mask, NULL); XSelectInput(dpy,win, ExposureMask | KeyPressMask | StructureNotifyMask | im_event_mask); XSetICFocus(ic); XMapWindow(dpy,win); while(1) { int buf_len = 10; wchar_t *buffer = (wchar_t *)malloc(buf_len * sizeof(wchar_t)); int len; KeySym keysym; Status status; Bool redraw = False; XNextEvent(dpy, &event); if (XFilterEvent(&event, None)) continue; switch (event.type) { case Expose: /* draw the string at a hard-coded location */ if (event.xexpose.count == 0) XwcDrawString(dpy, win, fontset, gc, 10, 50, string, str_len); break; case KeyPress: len = XwcLookupString(ic, &event, buffer, buf_len, &keysym, &status); /* * Workaround: the Xsi implementation of XwcLookupString * returns a length that is 4 times too big. If this bug * does not exist in your version of Xlib, remove the * following line, and the similar line below. */ len = len / 4; if (status == XBufferOverflow) { buf_len = len; buffer = (wchar_t *)realloc((char *)buffer, buf_len * sizeof(wchar_t)); len = XwcLookupString(ic, &event, buffer, buf_len, &keysym, &status); /* Workaround */ len = len / 4; } redraw = False; switch (status) { case XLookupNone: break; case XLookupKeySym: case XLookupBoth: /* Handle backspacing, and to exit */ if ((keysym == XK_Delete) || (keysym == XK_BackSpace)) { if (str_len > 0) str_len--; redraw = True; break; } if (keysym == XK_Return) exit(0); if (status == XLookupKeySym) break; case XLookupChars: for(i=0; i < len; i++) string[str_len++] = buffer[i]; redraw = True; break; } /* do a very simple-minded redraw, if needed */ if (redraw) { XClearWindow(dpy, win); XwcDrawString(dpy, win, fontset, gc, 10, 50, string, str_len); } break; case ConfigureNotify: /* * When the window is resized, we should re-negotiate the * geometry of the Preedit and Status area, if they are used * in the interaction style. */ if (best_style & XIMPreeditArea) { preedit_area.width = event.xconfigure.width*4/5; preedit_area.height = 0; GetPreferredGeometry(ic, XNPreeditAttributes, &preedit_area); preedit_area.x = event.xconfigure.width - preedit_area.width; preedit_area.y = event.xconfigure.height - preedit_area.height; SetGeometry(ic, XNPreeditAttributes, &preedit_area); } if (best_style & XIMStatusArea) { status_area.width = event.xconfigure.width/5; status_area.height = 0; GetPreferredGeometry(ic, XNStatusAttributes, &status_area); status_area.x = 0; status_area.y = event.xconfigure.height - status_area.height; SetGeometry(ic, XNStatusAttributes, &status_area); } break; } } }