comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:92745d501b9a
1 /*
2 * kinput2
3 */
4
5 /*
6 * Copyright (C) 1991 by Software Research Associates, Inc.
7 *
8 * Permission to use, copy, modify, and distribute this software and its
9 * documentation for any purpose and without fee is hereby granted, provided
10 * that the above copyright notice appear in all copies and that both that
11 * copyright notice and this permission notice appear in supporting
12 * documentation, and that the name of Software Research Associates not be
13 * used in advertising or publicity pertaining to distribution of the
14 * software without specific, written prior permission. Software Research
15 * Associates makes no representations about the suitability of this software
16 * for any purpose. It is provided "as is" without express or implied
17 * warranty.
18 *
19 * Author: Makoto Ishisone, Software Research Associates, Inc., Japan
20 * ishisone@sra.co.jp
21 */
22
23 #ifndef lint
24 static char *rcsid = "$Id: kinput2.c,v 1.46 2002/01/06 15:13:38 ishisone Exp $";
25 #endif
26
27 #include <stdio.h>
28 #include <signal.h>
29 #include <X11/Intrinsic.h>
30 #include <X11/StringDefs.h>
31 #include <X11/Shell.h>
32 #include "patchlevel.h"
33 #include "AsyncErr.h"
34 #include "MyDispatch.h"
35 #include "IOECall.h"
36 #include "ConvMgr.h"
37 #include "KIProto.h"
38 #include "XimpProto.h"
39 #include "IMProto.h"
40
41 #if !defined(USE_WNN) && !defined(USE_CANNA) && !defined(USE_SJ3) && !defined(USE_ATOK)
42 #define USE_WNN /* default */
43 #endif
44
45 #ifdef USE_WNN
46 #include "CcWnn.h"
47 #include "WcharDisp.h"
48 #endif
49 #ifdef USE_CANNA
50 #include "Canna.h"
51 #include "WcharDisp.h"
52 #endif
53 #ifdef USE_SJ3
54 #include "Sj3.h"
55 #include "WcharDisp.h"
56 #endif
57 #ifdef USE_ATOK
58 #include "Atok.h"
59 #include "WcharDisp.h"
60 #endif
61
62 #include "DebugPrint.h"
63
64 int debug_all;
65
66 /* application resource */
67 typedef struct {
68 char *conversionEngine;
69 int debugLevel;
70 Boolean useKinputProtocol;
71 Boolean useXimpProtocol;
72 Boolean useXIMProtocol;
73 Boolean appdefs_loaded;
74 } AppResRec, *AppResP;
75
76 static AppResRec appres;
77
78 static XtResource app_resources[] = {
79 { "conversionEngine", "ConversionEngine", XtRString, sizeof(String),
80 XtOffset(AppResP, conversionEngine), XtRString, (XtPointer)"" },
81 { "debugLevel", "DebugLevel", XtRInt, sizeof(int),
82 XtOffset(AppResP, debugLevel), XtRImmediate, (XtPointer)0 },
83 { "useKinputProtocol", "UseKinputProtocol", XtRBoolean, sizeof(Boolean),
84 XtOffset(AppResP, useKinputProtocol), XtRImmediate, (XtPointer)True },
85 { "useXimpProtocol", "UseXimpProtocol", XtRBoolean, sizeof(Boolean),
86 XtOffset(AppResP, useXimpProtocol), XtRImmediate, (XtPointer)True },
87 { "useXIMProtocol", "UseXIMProtocol", XtRBoolean, sizeof(Boolean),
88 XtOffset(AppResP, useXIMProtocol), XtRImmediate, (XtPointer)True },
89 { "ki2AppDefsLoaded", "Ki2AppDefsLoaded", XtRBoolean, sizeof(Boolean),
90 XtOffset(AppResP, appdefs_loaded), XtRImmediate, (XtPointer)True },
91 };
92
93 static String fallback_resources[] = {
94 "*ki2AppDefsLoaded: false",
95 "Kinput2.mappedWhenManaged: false",
96 "Kinput2.width: 1",
97 "Kinput2.height: 1",
98 NULL,
99 };
100
101 static XrmOptionDescRec options[] = {
102 {"-bc", "*KinputProtocol.backwardCompatible", XrmoptionNoArg, "True"},
103 {"-font", "*JpWcharDisplay.font", XrmoptionSepArg, NULL},
104 {"-kanjifont", "*JpWcharDisplay.kanjiFont", XrmoptionSepArg, NULL},
105 {"-kanafont", "*JpWcharDisplay.kanaFont", XrmoptionSepArg, NULL},
106 {"-kinput", ".useKinputProtocol", XrmoptionNoArg, "on"},
107 {"+kinput", ".useKinputProtocol", XrmoptionNoArg, "off"},
108 {"-ximp", ".useXimpProtocol", XrmoptionNoArg, "on"},
109 {"+ximp", ".useXimpProtocol", XrmoptionNoArg, "off"},
110 {"-xim", ".useXIMProtocol", XrmoptionNoArg, "on"},
111 {"+xim", ".useXIMProtocol", XrmoptionNoArg, "off"},
112 {"-tbheight", "*ConversionControl.titlebarHeight",
113 XrmoptionSepArg, NULL},
114 #ifdef DEBUG
115 {"-debug", ".debugLevel", XrmoptionNoArg, "1"},
116 {"-trace", ".debugLevel", XrmoptionNoArg, "10"},
117 {"-debuglevel", ".debugLevel", XrmoptionSepArg, NULL},
118 #endif
119 #ifdef USE_WNN
120 {"-wnn", ".conversionEngine", XrmoptionNoArg, "wnn"},
121 {"-jserver", "*CcWnn.jserver", XrmoptionSepArg, NULL},
122 {"-ccdef", "*CcWnn.ccdef", XrmoptionSepArg, NULL},
123 {"-wnnenvname", "*CcWnn.wnnEnvname", XrmoptionSepArg, NULL},
124 {"-wnnenvrc4", "*CcWnn.wnnEnvrc4", XrmoptionSepArg, NULL},
125 {"-wnnenvrc6", "*CcWnn.wnnEnvrc6", XrmoptionSepArg, NULL},
126 {"-wnnenvrc", "*CcWnn.wnnEnvrc", XrmoptionSepArg, NULL},
127 #endif
128 #ifdef USE_CANNA
129 {"-canna", ".conversionEngine", XrmoptionNoArg, "canna"},
130 {"-cannaserver", "*Canna.cannahost", XrmoptionSepArg, NULL},
131 {"-cs", "*Canna.cannahost", XrmoptionSepArg, NULL},
132 {"-cannafile", "*Canna.cannafile", XrmoptionSepArg, NULL},
133 #endif
134 #ifdef USE_SJ3
135 {"-sj3", ".conversionEngine", XrmoptionNoArg, "sj3"},
136 {"-sj3serv", "*Sj3.sj3serv", XrmoptionSepArg, NULL},
137 {"-sj3serv2", "*Sj3.sj3serv2", XrmoptionSepArg, NULL},
138 {"-sj3user", "*Sj3.sj3user", XrmoptionSepArg, NULL},
139 {"-rcfile", "*Sj3.rcfile", XrmoptionSepArg, NULL},
140 {"-sbfile", "*Sj3.sbfile", XrmoptionSepArg, NULL},
141 {"-rkfile", "*Sj3.rkfile", XrmoptionSepArg, NULL},
142 {"-hkfile", "*Sj3.hkfile", XrmoptionSepArg, NULL},
143 {"-zhfile", "*Sj3.zhfile", XrmoptionSepArg, NULL},
144 {"-sjrc", "*Sj3.rcfile", XrmoptionSepArg, NULL},
145 {"-sjsb", "*Sj3.sbfile", XrmoptionSepArg, NULL},
146 {"-sjrk", "*Sj3.rkfile", XrmoptionSepArg, NULL},
147 {"-sjhk", "*Sj3.hkfile", XrmoptionSepArg, NULL},
148 {"-sjzh", "*Sj3.zhfile", XrmoptionSepArg, NULL},
149 #endif
150 #ifdef USE_ATOK
151 {"-atok", ".conversionEngine", XrmoptionNoArg, "atok"},
152 {"-atokserver", "*Atok.server", XrmoptionSepArg, NULL},
153 {"-as", "*Atok.server", XrmoptionSepArg, NULL},
154 {"-atokport", "*Atok.port", XrmoptionSepArg, NULL},
155 {"-atokconf", "*Atok.conf", XrmoptionSepArg, NULL},
156 {"-atokstyle", "*Atok.style", XrmoptionSepArg, NULL},
157 #endif
158 };
159
160 XtAppContext apc;
161 Widget toplevel;
162
163 static int numProtocols;
164 static int (*DefaultErrorHandler)();
165
166 #if XtSpecificationRelease > 5
167 static XtSignalId interrupt;
168 static void interruptCallback();
169 #else
170 static void exitTimer();
171 #endif
172
173 static WidgetClass getInputObjClass();
174 static int IgnoreBadWindow();
175 #ifdef SIGNALRETURNSINT
176 static int scheduleExit();
177 #else
178 static void scheduleExit();
179 #endif
180 static void Destroyed();
181 static void Exit();
182 static void realExit();
183 static void usage();
184 static void print_version();
185
186 int
187 main(ac, av)
188 int ac;
189 char **av;
190 {
191 Widget manager, protocol;
192 int i;
193 WidgetClass inputobjclass, displayobjclass;
194 Widget inputobj;
195
196 toplevel = XtAppInitialize(&apc, "Kinput2",
197 options, XtNumber(options),
198 &ac, av,
199 fallback_resources, (ArgList)NULL, 0);
200
201 /* check invalid argument */
202 if (ac > 1) {
203 int do_usage = 0;
204 for (i = 1; i < ac; i++) {
205 if (!strcmp(av[i], "-version")) {
206 print_version();
207 } else {
208 fprintf(stderr, "unknown argument: %s\n", av[i]);
209 do_usage = 1;
210 }
211 }
212 if (do_usage) usage();
213 }
214
215 /* initialize asynchronous error handler */
216 XAEInit();
217
218 /* initialize I/O error callback handler */
219 XIOEInit();
220
221 XtGetApplicationResources(toplevel, &appres,
222 app_resources, XtNumber(app_resources),
223 NULL, 0);
224
225 /*
226 * If the application-specific class resource file
227 * (the "app-defaults" file) is not found,
228 * print a warning message.
229 */
230 if (!appres.appdefs_loaded) {
231 fprintf(stderr, "Warning: Cannot load app-defaults file.\n");
232 fprintf(stderr, " Kinput2 may not work properly without it.\n");
233 fprintf(stderr, " Maybe kinput2 is not installed correctly,\n");
234 fprintf(stderr, " or your file search path (specified by\n");
235 fprintf(stderr, " environment variable 'XFILESEARCHPATH')\n");
236 fprintf(stderr, " is wrong.\n");
237 }
238
239 /* set debug level */
240 debug_all = appres.debugLevel;
241
242 #ifdef RANDOM_ID
243 /*
244 * one nasty hack here:
245 *
246 * kinput clients often use server's window ID for the only key
247 * value to identify their conversion server (kinput), and they
248 * think it is dead and take appropriate action (eg connecting to
249 * the new server) when they notice the ID has changed.
250 *
251 * but it is likely that another kinput has the same resource ID
252 * base (because X servers always choose the smallest unused ID
253 * base for new clients). and if it is the same, so is the owner
254 * window ID, and the clients don't notice the change.
255 *
256 * to get rid of the problem, we add some small random offset to
257 * the resource ID so that every time we get different owner ID
258 * even if the resource ID base is the same.
259 *
260 * of course it heavily depends on the current implementaion of
261 * the resource ID allocation in Xlib, so I call it 'nasty'.
262 */
263 XtDisplay(toplevel)->resource_id += getpid() % 1024;
264 #endif
265
266 inputobjclass = getInputObjClass();
267
268 inputobj = XtCreateWidget("inputObj", inputobjclass,
269 toplevel, 0, 0);
270 XtRealizeWidget(inputobj);
271 ICRegisterTriggerKeys(inputobj);
272 /*
273 Destroying the `inputobj' is postponed until all the widgets
274 are realized in order to prevent duplicate initialization of
275 input object, that is, to prevent making connection twice to
276 input conversion server.
277 */
278
279 displayobjclass = jpWcharDisplayObjectClass;
280
281 manager = XtVaCreateManagedWidget("convmanager",
282 conversionManagerWidgetClass,
283 toplevel,
284 XtNwidth, 1,
285 XtNheight, 1,
286 NULL);
287
288 numProtocols = 0;
289
290 if (appres.useKinputProtocol) {
291 protocol = XtVaCreateWidget("kinputprotocol",
292 kinputProtocolWidgetClass,
293 manager,
294 XtNlanguage, "JAPANESE",
295 XtNinputObjectClass, inputobjclass,
296 XtNdisplayObjectClass, displayobjclass,
297 XtNwidth, 1,
298 XtNheight, 1,
299 NULL);
300 XtAddCallback(protocol, XtNdestroyCallback,
301 Destroyed, (XtPointer)NULL);
302 numProtocols++;
303 }
304
305 if (appres.useXimpProtocol) {
306 protocol = XtVaCreateWidget("ximpprotocol",
307 ximpProtocolWidgetClass,
308 manager,
309 XtNlocaleName, "ja_JP",
310 XtNinputObjectClass, inputobjclass,
311 XtNdisplayObjectClass, displayobjclass,
312 XtNwidth, 1,
313 XtNheight, 1,
314 NULL);
315 XtAddCallback(protocol, XtNdestroyCallback,
316 Destroyed, (XtPointer)NULL);
317 numProtocols++;
318 }
319
320 if (appres.useXIMProtocol) {
321 protocol = XtVaCreateWidget("improtocol",
322 imProtocolWidgetClass,
323 manager,
324 XtNlanguage, "ja_JP",
325 XtNinputObjectClass, inputobjclass,
326 XtNdisplayObjectClass, displayobjclass,
327 XtNwidth, 1,
328 XtNheight, 1,
329 NULL);
330 XtAddCallback(protocol, XtNdestroyCallback,
331 Destroyed, (XtPointer)NULL);
332 numProtocols++;
333 }
334
335 if (numProtocols == 0) {
336 fprintf(stderr, "no protocols activated\n");
337 exit(1);
338 }
339
340 /* set signal handler */
341 if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, scheduleExit);
342 #if XtSpecificationRelease > 5
343 interrupt = XtAppAddSignal(apc, interruptCallback, (XtPointer)NULL);
344 #endif
345 signal(SIGTERM, scheduleExit);
346 #ifdef USE_WNN
347 signal(SIGPIPE, SIG_IGN);
348 #endif
349
350 /* set my error handler */
351 DefaultErrorHandler = XAESetErrorHandler(IgnoreBadWindow);
352
353 XtRealizeWidget(toplevel);
354
355 XtDestroyWidget(inputobj); /* Don't move this before XtRealizeWidget() */
356
357 for (;;) {
358 XEvent event;
359
360 XtAppNextEvent(apc, &event);
361 XtDispatchEvent(&event);
362 MyDispatchEvent(&event); /* additional dispatcher */
363 }
364 /* NOTREACHED */
365
366 return 0; /* for lint */
367 }
368
369 static WidgetClass
370 getInputObjClass()
371 {
372 WidgetClass class;
373
374 #ifdef USE_WNN
375 if (!strcmp(appres.conversionEngine, "wnn")) {
376 return ccWnnObjectClass;
377 }
378 #endif
379 #ifdef USE_CANNA
380 if (!strcmp(appres.conversionEngine, "canna") ||
381 !strcmp(appres.conversionEngine, "iroha")) {
382 return cannaObjectClass;
383 }
384 #endif
385 #ifdef USE_SJ3
386 if (!strcmp(appres.conversionEngine, "sj3")) {
387 return sj3ObjectClass;
388 }
389 #endif
390 #ifdef USE_ATOK
391 if (!strcmp(appres.conversionEngine, "atok")) {
392 return atokObjectClass;
393 }
394 #endif
395
396 /* set default input object */
397 #ifdef USE_ATOK
398 class = atokObjectClass;
399 #endif
400 #ifdef USE_SJ3
401 class = sj3ObjectClass;
402 #endif
403 #ifdef USE_CANNA
404 class = cannaObjectClass;
405 #endif
406 #ifdef USE_WNN
407 class = ccWnnObjectClass;
408 #endif
409
410 return class;
411 }
412
413 static int
414 IgnoreBadWindow(dpy, error)
415 Display *dpy;
416 XErrorEvent *error;
417 {
418 /*
419 * BadWindow events will be sent if any of the active clients die
420 * during conversion. Although I select DestroyNotify event on the
421 * client windows to detect their destruction and take appropriate
422 * actions, and I'm careful when doing critical operations, but still
423 * there is a chance of getting unexpecting BadWindow error caused by
424 * client death.
425 * There are also chances of getting BadDrawable as well.
426 * So I set the error handler to ignore BadWindow/BadDrawable errors.
427 * Of course I'd better check if the resourceid field of the error
428 * event is the window ID of a client, but I'm too lazy to do that...
429 * P.S. We should ignore BadColor also..
430 */
431 if (error->error_code != BadWindow &&
432 error->error_code != BadDrawable &&
433 error->error_code != BadColor) {
434 /* invoke default error handler */
435 (*DefaultErrorHandler)(dpy, error);
436 }
437 return 0;
438 }
439
440 #ifdef SIGNALRETURNSINT
441 static int
442 #else
443 static void
444 #endif
445 scheduleExit()
446 {
447 #if XtSpecificationRelease > 5
448 XtNoticeSignal(interrupt);
449 #else
450 /*
451 * It is unwise to do complex operation (in this case,
452 * XtDestroyWidget) in a signal handler.
453 * So postpone the real work...
454 */
455 XtAppAddTimeOut(apc, 1L, exitTimer, (XtPointer)NULL);
456 #endif
457 }
458
459 #if XtSpecificationRelease > 5
460 /* ARGSUSED */
461 static void
462 interruptCallback(cldata, sigid)
463 XtPointer cldata;
464 XtSignalId *sigid;
465 {
466 Exit();
467 }
468 #else
469 /* ARGSUSED */
470 static void
471 exitTimer(cldata, timerp)
472 XtPointer cldata;
473 XtIntervalId *timerp;
474 {
475 Exit();
476 }
477 #endif
478
479 /* ARGSUSED */
480 static void
481 Destroyed(w, client_data, call_data)
482 Widget w;
483 XtPointer client_data;
484 XtPointer call_data;
485 {
486 static int cnt = 0;
487
488 /*
489 * if all the protocol widgets are dead, kill myself.
490 */
491 if (++cnt >= numProtocols) Exit();
492 }
493
494 static void
495 Exit()
496 {
497 static int exiting = 0;
498
499 TRACE(("Exit()\n"));
500
501 if (exiting) return;
502 exiting = 1;
503
504 /*
505 * Destroy all widgets.
506 */
507 XtDestroyWidget(toplevel);
508
509 /*
510 * Postpone calling exit() until next XtNextEvent(),
511 * in order to give each widget time to execute their
512 * destroy procedure.
513 */
514 XtAppAddTimeOut(apc, 1L, realExit, (XtPointer)NULL);
515 }
516
517 /* ARGSUSED */
518 static void
519 realExit(cldata, timerp)
520 XtPointer cldata;
521 XtIntervalId *timerp;
522 {
523 exit(0);
524 }
525
526 static void
527 usage()
528 {
529 char **p;
530 static char *syntaxtable[] = {
531 #ifdef USE_WNN
532 "-wnn", "use Wnn as the conversion engine",
533 "-jserver <hostname>", "specify jserver host",
534 "-ccdef <ccdeffile>", "specify character conversion def. file",
535 "-wnnenvname <name>", "specify Wnn environment name",
536 "-wnnenvrc4 <wnnenvrcfile>", "specify Wnn environment file for Wnn4",
537 "-wnnenvrc6 <wnnenvrcfile>", "specify Wnn environment file for Wnn6",
538 "-wnnenvrc <wnnenvrcfile>", "specify Wnn environment file",
539 #endif
540 #ifdef USE_CANNA
541 "-canna", "use Canna (Iroha) as the conversion engine",
542 "{-cannaserver|-cs} <hostname>[:n]", "specify cannaserver host",
543 "-cannafile <cannafile>", "specify canna customize file",
544 #endif
545 #ifdef USE_SJ3
546 "-sj3", "use SJ3 as the conversion engine",
547 "-sj3serv <hostname>", "specify first sj3serv host",
548 "-sj3serv2 <hostname>", "specify second sj3serv host",
549 "-sj3user <user>", "specify user name connect to sj3serv",
550 "{-rcfile|-sjrc} <file>", "specify resource definition file",
551 "{-sbfile|-sjsb} <file>", "specify symbol table file",
552 "{-rkfile|-sjrk} <file>", "specify roma-kana coversion definition file",
553 "{-hkfile|-sjhk} <file>", "specify hira-kata coversion definition file",
554 "{-zhfile|-sjzh} <file>", "specify zen/han coversion definition file",
555 #endif
556 #ifdef USE_ATOK
557 "-atok", "use ATOK as the conversion engine",
558 "{-atokserver|-as} <hostname>", "specify atok server host",
559 "-atokport <port#>", "specify atok service port",
560 "-atokconf <file>", "specify atok customize file",
561 "-atokstyle <file>", "specify atok style file",
562 #endif
563 "-bc", "backward compatible mode",
564 "-font <font>", "ASCII font to be used",
565 "-kanjifont <font>", "KANJI font to be used",
566 "-kanafont <font>", "KANA font to be used",
567 "-/+kinput", "activate/deactivate kinput protocol family",
568 "-/+ximp", "activate/deactivate Ximp protocol",
569 "-/+xim", "activate/deactivate X Input Method protocol",
570 "-tbheight <number>", "specify pop-up shell's titlebar height",
571 "-background <color>", "background color",
572 "-foreground <color>", "foreground color",
573 "-rv", "reverse video mode",
574 "-display <display>", "specify display",
575 "-version", "print version information and exit",
576 #ifdef DEBUG
577 "-debug", "print debug messages (debug level 1)",
578 "-trace", "print trace messages (debug level 10)",
579 "-debuglevel <level>", "set debug level",
580 #endif
581 NULL, NULL,
582 };
583
584 fprintf(stderr, "options are:\n");
585 for (p = syntaxtable; *p != NULL; p += 2) {
586 fprintf(stderr, " %-30s %s\n", *p, *(p + 1));
587 }
588 exit(1);
589 }
590
591 static void
592 print_version()
593 {
594 char *p;
595
596 printf("kinput2 %s ", KINPUT2_VERSION);
597 if (PATCHLEVEL > 0) printf("fix %d ", PATCHLEVEL);
598 #ifdef STATUS
599 printf("-%s- ", STATUS);
600 #endif
601 printf(" (");
602 p = DATE + 7; /* skip '$Date: ' */
603 while (*p != '\0' && *p != ' ') {
604 putchar(*p); /* print date */
605 p++;
606 }
607 printf(")\n");
608
609 printf("\toptions: ");
610 #ifdef USE_WNN
611 #ifdef USE_WNN6
612 printf("[Wnn6] ");
613 #else
614 printf("[Wnn] ");
615 #endif
616 #endif
617 #ifdef USE_CANNA
618 printf("[Canna2] ");
619 #endif
620 #ifdef USE_SJ3
621 printf("[Sj3] ");
622 #endif
623 #ifdef USE_ATOK
624 printf("[Atok] ");
625 #endif
626 #ifdef DEBUG
627 printf("[DEBUG] ");
628 #endif
629 printf("\n");
630 exit(0);
631 }
632
633 #if defined(USE_WNN) && defined(NEED_Strlen)
634 /*
635 * Wnn/jlib/js.c should have this function...
636 */
637 int
638 Strlen(s)
639 unsigned short *s;
640 {
641 int n = 0;
642
643 while (*s++) n++;
644 return n;
645 }
646 #endif