Mercurial > kinput2.yaz
diff cmd/kinput2.c @ 0:92745d501b9a
initial import from kinput2-v3.1
author | Yoshiki Yazawa <yaz@honeyplanet.jp> |
---|---|
date | Mon, 08 Mar 2010 04:44:30 +0900 |
parents | |
children | 115737ec0041 598fcbe482b5 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cmd/kinput2.c Mon Mar 08 04:44:30 2010 +0900 @@ -0,0 +1,646 @@ +/* + * kinput2 + */ + +/* + * Copyright (C) 1991 by Software Research Associates, Inc. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Software Research Associates not be + * used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. Software Research + * Associates makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied + * warranty. + * + * Author: Makoto Ishisone, Software Research Associates, Inc., Japan + * ishisone@sra.co.jp + */ + +#ifndef lint +static char *rcsid = "$Id: kinput2.c,v 1.46 2002/01/06 15:13:38 ishisone Exp $"; +#endif + +#include <stdio.h> +#include <signal.h> +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#include <X11/Shell.h> +#include "patchlevel.h" +#include "AsyncErr.h" +#include "MyDispatch.h" +#include "IOECall.h" +#include "ConvMgr.h" +#include "KIProto.h" +#include "XimpProto.h" +#include "IMProto.h" + +#if !defined(USE_WNN) && !defined(USE_CANNA) && !defined(USE_SJ3) && !defined(USE_ATOK) +#define USE_WNN /* default */ +#endif + +#ifdef USE_WNN +#include "CcWnn.h" +#include "WcharDisp.h" +#endif +#ifdef USE_CANNA +#include "Canna.h" +#include "WcharDisp.h" +#endif +#ifdef USE_SJ3 +#include "Sj3.h" +#include "WcharDisp.h" +#endif +#ifdef USE_ATOK +#include "Atok.h" +#include "WcharDisp.h" +#endif + +#include "DebugPrint.h" + +int debug_all; + +/* application resource */ +typedef struct { + char *conversionEngine; + int debugLevel; + Boolean useKinputProtocol; + Boolean useXimpProtocol; + Boolean useXIMProtocol; + Boolean appdefs_loaded; +} AppResRec, *AppResP; + +static AppResRec appres; + +static XtResource app_resources[] = { + { "conversionEngine", "ConversionEngine", XtRString, sizeof(String), + XtOffset(AppResP, conversionEngine), XtRString, (XtPointer)"" }, + { "debugLevel", "DebugLevel", XtRInt, sizeof(int), + XtOffset(AppResP, debugLevel), XtRImmediate, (XtPointer)0 }, + { "useKinputProtocol", "UseKinputProtocol", XtRBoolean, sizeof(Boolean), + XtOffset(AppResP, useKinputProtocol), XtRImmediate, (XtPointer)True }, + { "useXimpProtocol", "UseXimpProtocol", XtRBoolean, sizeof(Boolean), + XtOffset(AppResP, useXimpProtocol), XtRImmediate, (XtPointer)True }, + { "useXIMProtocol", "UseXIMProtocol", XtRBoolean, sizeof(Boolean), + XtOffset(AppResP, useXIMProtocol), XtRImmediate, (XtPointer)True }, + { "ki2AppDefsLoaded", "Ki2AppDefsLoaded", XtRBoolean, sizeof(Boolean), + XtOffset(AppResP, appdefs_loaded), XtRImmediate, (XtPointer)True }, +}; + +static String fallback_resources[] = { + "*ki2AppDefsLoaded: false", + "Kinput2.mappedWhenManaged: false", + "Kinput2.width: 1", + "Kinput2.height: 1", + NULL, +}; + +static XrmOptionDescRec options[] = { + {"-bc", "*KinputProtocol.backwardCompatible", XrmoptionNoArg, "True"}, + {"-font", "*JpWcharDisplay.font", XrmoptionSepArg, NULL}, + {"-kanjifont", "*JpWcharDisplay.kanjiFont", XrmoptionSepArg, NULL}, + {"-kanafont", "*JpWcharDisplay.kanaFont", XrmoptionSepArg, NULL}, + {"-kinput", ".useKinputProtocol", XrmoptionNoArg, "on"}, + {"+kinput", ".useKinputProtocol", XrmoptionNoArg, "off"}, + {"-ximp", ".useXimpProtocol", XrmoptionNoArg, "on"}, + {"+ximp", ".useXimpProtocol", XrmoptionNoArg, "off"}, + {"-xim", ".useXIMProtocol", XrmoptionNoArg, "on"}, + {"+xim", ".useXIMProtocol", XrmoptionNoArg, "off"}, + {"-tbheight", "*ConversionControl.titlebarHeight", + XrmoptionSepArg, NULL}, +#ifdef DEBUG + {"-debug", ".debugLevel", XrmoptionNoArg, "1"}, + {"-trace", ".debugLevel", XrmoptionNoArg, "10"}, + {"-debuglevel", ".debugLevel", XrmoptionSepArg, NULL}, +#endif +#ifdef USE_WNN + {"-wnn", ".conversionEngine", XrmoptionNoArg, "wnn"}, + {"-jserver", "*CcWnn.jserver", XrmoptionSepArg, NULL}, + {"-ccdef", "*CcWnn.ccdef", XrmoptionSepArg, NULL}, + {"-wnnenvname", "*CcWnn.wnnEnvname", XrmoptionSepArg, NULL}, + {"-wnnenvrc4", "*CcWnn.wnnEnvrc4", XrmoptionSepArg, NULL}, + {"-wnnenvrc6", "*CcWnn.wnnEnvrc6", XrmoptionSepArg, NULL}, + {"-wnnenvrc", "*CcWnn.wnnEnvrc", XrmoptionSepArg, NULL}, +#endif +#ifdef USE_CANNA + {"-canna", ".conversionEngine", XrmoptionNoArg, "canna"}, + {"-cannaserver", "*Canna.cannahost", XrmoptionSepArg, NULL}, + {"-cs", "*Canna.cannahost", XrmoptionSepArg, NULL}, + {"-cannafile", "*Canna.cannafile", XrmoptionSepArg, NULL}, +#endif +#ifdef USE_SJ3 + {"-sj3", ".conversionEngine", XrmoptionNoArg, "sj3"}, + {"-sj3serv", "*Sj3.sj3serv", XrmoptionSepArg, NULL}, + {"-sj3serv2", "*Sj3.sj3serv2", XrmoptionSepArg, NULL}, + {"-sj3user", "*Sj3.sj3user", XrmoptionSepArg, NULL}, + {"-rcfile", "*Sj3.rcfile", XrmoptionSepArg, NULL}, + {"-sbfile", "*Sj3.sbfile", XrmoptionSepArg, NULL}, + {"-rkfile", "*Sj3.rkfile", XrmoptionSepArg, NULL}, + {"-hkfile", "*Sj3.hkfile", XrmoptionSepArg, NULL}, + {"-zhfile", "*Sj3.zhfile", XrmoptionSepArg, NULL}, + {"-sjrc", "*Sj3.rcfile", XrmoptionSepArg, NULL}, + {"-sjsb", "*Sj3.sbfile", XrmoptionSepArg, NULL}, + {"-sjrk", "*Sj3.rkfile", XrmoptionSepArg, NULL}, + {"-sjhk", "*Sj3.hkfile", XrmoptionSepArg, NULL}, + {"-sjzh", "*Sj3.zhfile", XrmoptionSepArg, NULL}, +#endif +#ifdef USE_ATOK + {"-atok", ".conversionEngine", XrmoptionNoArg, "atok"}, + {"-atokserver", "*Atok.server", XrmoptionSepArg, NULL}, + {"-as", "*Atok.server", XrmoptionSepArg, NULL}, + {"-atokport", "*Atok.port", XrmoptionSepArg, NULL}, + {"-atokconf", "*Atok.conf", XrmoptionSepArg, NULL}, + {"-atokstyle", "*Atok.style", XrmoptionSepArg, NULL}, +#endif +}; + +XtAppContext apc; +Widget toplevel; + +static int numProtocols; +static int (*DefaultErrorHandler)(); + +#if XtSpecificationRelease > 5 +static XtSignalId interrupt; +static void interruptCallback(); +#else +static void exitTimer(); +#endif + +static WidgetClass getInputObjClass(); +static int IgnoreBadWindow(); +#ifdef SIGNALRETURNSINT +static int scheduleExit(); +#else +static void scheduleExit(); +#endif +static void Destroyed(); +static void Exit(); +static void realExit(); +static void usage(); +static void print_version(); + +int +main(ac, av) +int ac; +char **av; +{ + Widget manager, protocol; + int i; + WidgetClass inputobjclass, displayobjclass; + Widget inputobj; + + toplevel = XtAppInitialize(&apc, "Kinput2", + options, XtNumber(options), + &ac, av, + fallback_resources, (ArgList)NULL, 0); + + /* check invalid argument */ + if (ac > 1) { + int do_usage = 0; + for (i = 1; i < ac; i++) { + if (!strcmp(av[i], "-version")) { + print_version(); + } else { + fprintf(stderr, "unknown argument: %s\n", av[i]); + do_usage = 1; + } + } + if (do_usage) usage(); + } + + /* initialize asynchronous error handler */ + XAEInit(); + + /* initialize I/O error callback handler */ + XIOEInit(); + + XtGetApplicationResources(toplevel, &appres, + app_resources, XtNumber(app_resources), + NULL, 0); + + /* + * If the application-specific class resource file + * (the "app-defaults" file) is not found, + * print a warning message. + */ + if (!appres.appdefs_loaded) { + fprintf(stderr, "Warning: Cannot load app-defaults file.\n"); + fprintf(stderr, " Kinput2 may not work properly without it.\n"); + fprintf(stderr, " Maybe kinput2 is not installed correctly,\n"); + fprintf(stderr, " or your file search path (specified by\n"); + fprintf(stderr, " environment variable 'XFILESEARCHPATH')\n"); + fprintf(stderr, " is wrong.\n"); + } + + /* set debug level */ + debug_all = appres.debugLevel; + +#ifdef RANDOM_ID + /* + * one nasty hack here: + * + * kinput clients often use server's window ID for the only key + * value to identify their conversion server (kinput), and they + * think it is dead and take appropriate action (eg connecting to + * the new server) when they notice the ID has changed. + * + * but it is likely that another kinput has the same resource ID + * base (because X servers always choose the smallest unused ID + * base for new clients). and if it is the same, so is the owner + * window ID, and the clients don't notice the change. + * + * to get rid of the problem, we add some small random offset to + * the resource ID so that every time we get different owner ID + * even if the resource ID base is the same. + * + * of course it heavily depends on the current implementaion of + * the resource ID allocation in Xlib, so I call it 'nasty'. + */ + XtDisplay(toplevel)->resource_id += getpid() % 1024; +#endif + + inputobjclass = getInputObjClass(); + + inputobj = XtCreateWidget("inputObj", inputobjclass, + toplevel, 0, 0); + XtRealizeWidget(inputobj); + ICRegisterTriggerKeys(inputobj); + /* + Destroying the `inputobj' is postponed until all the widgets + are realized in order to prevent duplicate initialization of + input object, that is, to prevent making connection twice to + input conversion server. + */ + + displayobjclass = jpWcharDisplayObjectClass; + + manager = XtVaCreateManagedWidget("convmanager", + conversionManagerWidgetClass, + toplevel, + XtNwidth, 1, + XtNheight, 1, + NULL); + + numProtocols = 0; + + if (appres.useKinputProtocol) { + protocol = XtVaCreateWidget("kinputprotocol", + kinputProtocolWidgetClass, + manager, + XtNlanguage, "JAPANESE", + XtNinputObjectClass, inputobjclass, + XtNdisplayObjectClass, displayobjclass, + XtNwidth, 1, + XtNheight, 1, + NULL); + XtAddCallback(protocol, XtNdestroyCallback, + Destroyed, (XtPointer)NULL); + numProtocols++; + } + + if (appres.useXimpProtocol) { + protocol = XtVaCreateWidget("ximpprotocol", + ximpProtocolWidgetClass, + manager, + XtNlocaleName, "ja_JP", + XtNinputObjectClass, inputobjclass, + XtNdisplayObjectClass, displayobjclass, + XtNwidth, 1, + XtNheight, 1, + NULL); + XtAddCallback(protocol, XtNdestroyCallback, + Destroyed, (XtPointer)NULL); + numProtocols++; + } + + if (appres.useXIMProtocol) { + protocol = XtVaCreateWidget("improtocol", + imProtocolWidgetClass, + manager, + XtNlanguage, "ja_JP", + XtNinputObjectClass, inputobjclass, + XtNdisplayObjectClass, displayobjclass, + XtNwidth, 1, + XtNheight, 1, + NULL); + XtAddCallback(protocol, XtNdestroyCallback, + Destroyed, (XtPointer)NULL); + numProtocols++; + } + + if (numProtocols == 0) { + fprintf(stderr, "no protocols activated\n"); + exit(1); + } + + /* set signal handler */ + if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, scheduleExit); +#if XtSpecificationRelease > 5 + interrupt = XtAppAddSignal(apc, interruptCallback, (XtPointer)NULL); +#endif + signal(SIGTERM, scheduleExit); +#ifdef USE_WNN + signal(SIGPIPE, SIG_IGN); +#endif + + /* set my error handler */ + DefaultErrorHandler = XAESetErrorHandler(IgnoreBadWindow); + + XtRealizeWidget(toplevel); + + XtDestroyWidget(inputobj); /* Don't move this before XtRealizeWidget() */ + + for (;;) { + XEvent event; + + XtAppNextEvent(apc, &event); + XtDispatchEvent(&event); + MyDispatchEvent(&event); /* additional dispatcher */ + } + /* NOTREACHED */ + + return 0; /* for lint */ +} + +static WidgetClass +getInputObjClass() +{ + WidgetClass class; + +#ifdef USE_WNN + if (!strcmp(appres.conversionEngine, "wnn")) { + return ccWnnObjectClass; + } +#endif +#ifdef USE_CANNA + if (!strcmp(appres.conversionEngine, "canna") || + !strcmp(appres.conversionEngine, "iroha")) { + return cannaObjectClass; + } +#endif +#ifdef USE_SJ3 + if (!strcmp(appres.conversionEngine, "sj3")) { + return sj3ObjectClass; + } +#endif +#ifdef USE_ATOK + if (!strcmp(appres.conversionEngine, "atok")) { + return atokObjectClass; + } +#endif + + /* set default input object */ +#ifdef USE_ATOK + class = atokObjectClass; +#endif +#ifdef USE_SJ3 + class = sj3ObjectClass; +#endif +#ifdef USE_CANNA + class = cannaObjectClass; +#endif +#ifdef USE_WNN + class = ccWnnObjectClass; +#endif + + return class; +} + +static int +IgnoreBadWindow(dpy, error) +Display *dpy; +XErrorEvent *error; +{ + /* + * BadWindow events will be sent if any of the active clients die + * during conversion. Although I select DestroyNotify event on the + * client windows to detect their destruction and take appropriate + * actions, and I'm careful when doing critical operations, but still + * there is a chance of getting unexpecting BadWindow error caused by + * client death. + * There are also chances of getting BadDrawable as well. + * So I set the error handler to ignore BadWindow/BadDrawable errors. + * Of course I'd better check if the resourceid field of the error + * event is the window ID of a client, but I'm too lazy to do that... + * P.S. We should ignore BadColor also.. + */ + if (error->error_code != BadWindow && + error->error_code != BadDrawable && + error->error_code != BadColor) { + /* invoke default error handler */ + (*DefaultErrorHandler)(dpy, error); + } + return 0; +} + +#ifdef SIGNALRETURNSINT +static int +#else +static void +#endif +scheduleExit() +{ +#if XtSpecificationRelease > 5 + XtNoticeSignal(interrupt); +#else + /* + * It is unwise to do complex operation (in this case, + * XtDestroyWidget) in a signal handler. + * So postpone the real work... + */ + XtAppAddTimeOut(apc, 1L, exitTimer, (XtPointer)NULL); +#endif +} + +#if XtSpecificationRelease > 5 +/* ARGSUSED */ +static void +interruptCallback(cldata, sigid) +XtPointer cldata; +XtSignalId *sigid; +{ + Exit(); +} +#else +/* ARGSUSED */ +static void +exitTimer(cldata, timerp) +XtPointer cldata; +XtIntervalId *timerp; +{ + Exit(); +} +#endif + +/* ARGSUSED */ +static void +Destroyed(w, client_data, call_data) +Widget w; +XtPointer client_data; +XtPointer call_data; +{ + static int cnt = 0; + + /* + * if all the protocol widgets are dead, kill myself. + */ + if (++cnt >= numProtocols) Exit(); +} + +static void +Exit() +{ + static int exiting = 0; + + TRACE(("Exit()\n")); + + if (exiting) return; + exiting = 1; + + /* + * Destroy all widgets. + */ + XtDestroyWidget(toplevel); + + /* + * Postpone calling exit() until next XtNextEvent(), + * in order to give each widget time to execute their + * destroy procedure. + */ + XtAppAddTimeOut(apc, 1L, realExit, (XtPointer)NULL); +} + +/* ARGSUSED */ +static void +realExit(cldata, timerp) +XtPointer cldata; +XtIntervalId *timerp; +{ + exit(0); +} + +static void +usage() +{ + char **p; + static char *syntaxtable[] = { +#ifdef USE_WNN + "-wnn", "use Wnn as the conversion engine", + "-jserver <hostname>", "specify jserver host", + "-ccdef <ccdeffile>", "specify character conversion def. file", + "-wnnenvname <name>", "specify Wnn environment name", + "-wnnenvrc4 <wnnenvrcfile>", "specify Wnn environment file for Wnn4", + "-wnnenvrc6 <wnnenvrcfile>", "specify Wnn environment file for Wnn6", + "-wnnenvrc <wnnenvrcfile>", "specify Wnn environment file", +#endif +#ifdef USE_CANNA + "-canna", "use Canna (Iroha) as the conversion engine", + "{-cannaserver|-cs} <hostname>[:n]", "specify cannaserver host", + "-cannafile <cannafile>", "specify canna customize file", +#endif +#ifdef USE_SJ3 + "-sj3", "use SJ3 as the conversion engine", + "-sj3serv <hostname>", "specify first sj3serv host", + "-sj3serv2 <hostname>", "specify second sj3serv host", + "-sj3user <user>", "specify user name connect to sj3serv", + "{-rcfile|-sjrc} <file>", "specify resource definition file", + "{-sbfile|-sjsb} <file>", "specify symbol table file", + "{-rkfile|-sjrk} <file>", "specify roma-kana coversion definition file", + "{-hkfile|-sjhk} <file>", "specify hira-kata coversion definition file", + "{-zhfile|-sjzh} <file>", "specify zen/han coversion definition file", +#endif +#ifdef USE_ATOK + "-atok", "use ATOK as the conversion engine", + "{-atokserver|-as} <hostname>", "specify atok server host", + "-atokport <port#>", "specify atok service port", + "-atokconf <file>", "specify atok customize file", + "-atokstyle <file>", "specify atok style file", +#endif + "-bc", "backward compatible mode", + "-font <font>", "ASCII font to be used", + "-kanjifont <font>", "KANJI font to be used", + "-kanafont <font>", "KANA font to be used", + "-/+kinput", "activate/deactivate kinput protocol family", + "-/+ximp", "activate/deactivate Ximp protocol", + "-/+xim", "activate/deactivate X Input Method protocol", + "-tbheight <number>", "specify pop-up shell's titlebar height", + "-background <color>", "background color", + "-foreground <color>", "foreground color", + "-rv", "reverse video mode", + "-display <display>", "specify display", + "-version", "print version information and exit", +#ifdef DEBUG + "-debug", "print debug messages (debug level 1)", + "-trace", "print trace messages (debug level 10)", + "-debuglevel <level>", "set debug level", +#endif + NULL, NULL, + }; + + fprintf(stderr, "options are:\n"); + for (p = syntaxtable; *p != NULL; p += 2) { + fprintf(stderr, " %-30s %s\n", *p, *(p + 1)); + } + exit(1); +} + +static void +print_version() +{ + char *p; + + printf("kinput2 %s ", KINPUT2_VERSION); + if (PATCHLEVEL > 0) printf("fix %d ", PATCHLEVEL); +#ifdef STATUS + printf("-%s- ", STATUS); +#endif + printf(" ("); + p = DATE + 7; /* skip '$Date: ' */ + while (*p != '\0' && *p != ' ') { + putchar(*p); /* print date */ + p++; + } + printf(")\n"); + + printf("\toptions: "); +#ifdef USE_WNN +#ifdef USE_WNN6 + printf("[Wnn6] "); +#else + printf("[Wnn] "); +#endif +#endif +#ifdef USE_CANNA + printf("[Canna2] "); +#endif +#ifdef USE_SJ3 + printf("[Sj3] "); +#endif +#ifdef USE_ATOK + printf("[Atok] "); +#endif +#ifdef DEBUG + printf("[DEBUG] "); +#endif + printf("\n"); + exit(0); +} + +#if defined(USE_WNN) && defined(NEED_Strlen) +/* + * Wnn/jlib/js.c should have this function... + */ +int +Strlen(s) +unsigned short *s; +{ + int n = 0; + + while (*s++) n++; + return n; +} +#endif