1 /******************************************************************
2 
3          Copyright 1994, 1995 by Sun Microsystems, Inc.
4          Copyright 1993, 1994 by Hewlett-Packard Company
5 
6 Permission to use, copy, modify, distribute, and sell this software
7 and its documentation for any purpose is hereby granted without fee,
8 provided that the above copyright notice appear in all copies and
9 that both that copyright notice and this permission notice appear
10 in supporting documentation, and that the name of Sun Microsystems, Inc.
11 and Hewlett-Packard not be used in advertising or publicity pertaining to
12 distribution of the software without specific, written prior permission.
13 Sun Microsystems, Inc. and Hewlett-Packard make no representations about
14 the suitability of this software for any purpose.  It is provided "as is"
15 without express or implied warranty.
16 
17 SUN MICROSYSTEMS INC. AND HEWLETT-PACKARD COMPANY DISCLAIMS ALL
18 WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
20 SUN MICROSYSTEMS, INC. AND HEWLETT-PACKARD COMPANY BE LIABLE FOR ANY
21 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
22 RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
23 CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
24 IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 
26   Author: Hidetoshi Tajima(tajima@Eng.Sun.COM) Sun Microsystems, Inc.
27 
28 ******************************************************************/
29 #include <stdio.h>
30 #include <X11/Xlocale.h>
31 #include <X11/Xlib.h>
32 #include <X11/Xutil.h>
33 #include <X11/keysym.h>
34 #include <X11/Ximd/IMdkit.h>
35 #include <X11/Ximd/Xi18n.h>
36 
37 #define DEFAULT_IMNAME "sampleIM"
38 #define DEFAULT_LOCALE "zh_TW,ja_JP"
39 
40 /* flags for debugging */
41 Bool use_trigger = True;	/* Dynamic Event Flow is default */
42 Bool use_offkey = False;	/* Register OFF Key for Dynamic Event Flow */
43 Bool use_tcp = False;		/* Using TCP/IP Transport or not */
44 Bool use_local = False;		/* Using Unix domain Tranport or not */
45 long filter_mask = KeyPressMask;
46 
47 /* Supported Inputstyles */
48 static XIMStyle Styles[] = {
49     XIMPreeditCallbacks|XIMStatusCallbacks,
50     XIMPreeditPosition|XIMStatusArea,
51     XIMPreeditPosition|XIMStatusNothing,
52     XIMPreeditArea|XIMStatusArea,
53     XIMPreeditNothing|XIMStatusNothing,
54     0
55 };
56 
57 /* Trigger Keys List */
58 static XIMTriggerKey Trigger_Keys[] = {
59     {XK_space, ShiftMask, ShiftMask},
60     {0L, 0L, 0L}
61 };
62 
63 /* Conversion Keys List */
64 static XIMTriggerKey Conversion_Keys[] = {
65     {XK_k, ControlMask, ControlMask},
66     {0L, 0L, 0L}
67 };
68 
69 /* Forward Keys List */
70 static XIMTriggerKey Forward_Keys[] = {
71     {XK_Return, 0, 0},
72     {XK_Tab, 0, 0},
73     {0L, 0L, 0L}
74 };
75 
76 /* Supported Taiwanese Encodings */
77 static XIMEncoding zhEncodings[] = {
78     "COMPOUND_TEXT",
79     NULL
80 };
81 
MyGetICValuesHandler(ims,call_data)82 MyGetICValuesHandler(ims, call_data)
83 XIMS ims;
84 IMChangeICStruct *call_data;
85 {
86     GetIC(call_data);
87     return True;
88 }
89 
MySetICValuesHandler(ims,call_data)90 MySetICValuesHandler(ims, call_data)
91 XIMS ims;
92 IMChangeICStruct *call_data;
93 {
94     SetIC(call_data);
95     return True;
96 }
97 
MyOpenHandler(ims,call_data)98 MyOpenHandler(ims, call_data)
99 XIMS ims;
100 IMOpenStruct *call_data;
101 {
102 #ifdef DEBUG
103     printf("new_client lang = %s\n", call_data->lang.name);
104     printf("     connect_id = 0x%x\n", (int)call_data->connect_id);
105 #endif
106     return True;
107 }
108 
MyCloseHandler(ims,call_data)109 MyCloseHandler(ims, call_data)
110 XIMS ims;
111 IMOpenStruct *call_data;
112 {
113 #ifdef DEBUG
114     printf("closing connect_id 0x%x\n", (int)call_data->connect_id);
115 #endif
116     return True;
117 }
118 
MyCreateICHandler(ims,call_data)119 MyCreateICHandler(ims, call_data)
120 XIMS ims;
121 IMChangeICStruct *call_data;
122 {
123     CreateIC(call_data);
124     return True;
125 }
126 
MyDestroyICHandler(ims,call_data)127 MyDestroyICHandler(ims, call_data)
128 XIMS ims;
129 IMChangeICStruct *call_data;
130 {
131     DestroyIC(call_data);
132     return True;
133 }
134 
135 #define STRBUFLEN 64
IsKey(ims,call_data,trigger)136 IsKey(ims, call_data, trigger)
137 XIMS ims;
138 IMForwardEventStruct *call_data;
139 XIMTriggerKey *trigger;		       /* Searching for these keys */
140 {
141     char strbuf[STRBUFLEN];
142     KeySym keysym;
143     int i;
144     int modifier;
145     int modifier_mask;
146     XKeyEvent *kev;
147 
148     memset(strbuf, 0, STRBUFLEN);
149     kev = (XKeyEvent*)&call_data->event;
150     XLookupString(kev, strbuf, STRBUFLEN, &keysym, NULL);
151 
152     for (i = 0; trigger[i].keysym != 0; i++) {
153 	modifier      = trigger[i].modifier;
154 	modifier_mask = trigger[i].modifier_mask;
155 	if (((KeySym)trigger[i].keysym == keysym)
156 	    && ((kev->state & modifier_mask) == modifier))
157 	  return True;
158     }
159     return False;
160 }
161 
ProcessKey(ims,call_data)162 ProcessKey(ims, call_data)
163 XIMS ims;
164 IMForwardEventStruct *call_data;
165 {
166     char strbuf[STRBUFLEN];
167     KeySym keysym;
168     XKeyEvent *kev;
169     int count;
170 
171     fprintf(stderr, "Processing \n");
172     memset(strbuf, 0, STRBUFLEN);
173     kev = (XKeyEvent*)&call_data->event;
174     count = XLookupString(kev, strbuf, STRBUFLEN, &keysym, NULL);
175 
176     if (count > 0) {
177 	fprintf(stdout, "[%s] ", strbuf);
178     }
179 }
180 
MyForwardEventHandler(ims,call_data)181 MyForwardEventHandler(ims, call_data)
182 XIMS ims;
183 IMForwardEventStruct *call_data;
184 {
185     /* Lookup KeyPress Events only */
186     fprintf(stderr, "ForwardEventHandler\n");
187     if (call_data->event.type != KeyPress) {
188         fprintf(stderr, "bogus event type, ignored\n");
189     	return True;
190     }
191 
192     /* In case of Static Event Flow */
193     if (!use_trigger) {
194 	static Bool preedit_state_flag = False;
195 	if (IsKey(ims, call_data, Trigger_Keys)) {
196 	    preedit_state_flag = !preedit_state_flag;
197 	    return True;
198 	}
199     }
200 
201     /* In case of Dynamic Event Flow without registering OFF keys,
202        the end of preediting must be notified from IMserver to
203        IMlibrary. */
204     if (use_trigger && !use_offkey) {
205 	if (IsKey(ims, call_data, Trigger_Keys)) {
206 	    return IMPreeditEnd(ims, (XPointer)call_data);
207 	}
208     }
209     if (IsKey(ims, call_data, Conversion_Keys)) {
210 	XTextProperty tp;
211 	Display *display = ims->core.display;
212 	/* char *text = "�o�O�@�� IM ���A��������"; */
213 	char *text = "���üy";
214 	char **list_return; /* [20]; */
215 	int count_return; /* [20]; */
216 
217 	fprintf(stderr, "matching ctrl-k...\n");
218 	XmbTextListToTextProperty(display, (char **)&text, 1,
219 				  XCompoundTextStyle,
220 				  &tp);
221 
222 	((IMCommitStruct*)call_data)->flag |= XimLookupChars;
223 	((IMCommitStruct*)call_data)->commit_string = (char *)tp.value;
224 	fprintf(stderr, "commiting string...(%s)\n", tp.value);
225 	IMCommitString(ims, (XPointer)call_data);
226 #if 0
227 	XmbTextPropertyToTextList(display, &tp, &list_return, &count_return);
228 	fprintf(stderr, "converted back: %s\n", *list_return);
229 #endif
230 	XFree(tp.value);
231 	fprintf(stderr, "survived so far..\n");
232     }
233     else if (IsKey(ims, call_data, Forward_Keys)) {
234         IMForwardEventStruct forward_ev = *((IMForwardEventStruct *)call_data);
235 
236 	fprintf(stderr, "TAB and RETURN forwarded...\n");
237 	IMForwardEvent(ims, (XPointer)&forward_ev);
238     } else {
239 	ProcessKey(ims, call_data);
240     }
241     return True;
242 }
243 
MyTriggerNotifyHandler(ims,call_data)244 MyTriggerNotifyHandler(ims, call_data)
245 XIMS ims;
246 IMTriggerNotifyStruct *call_data;
247 {
248     if (call_data->flag == 0) {	/* on key */
249 	/* Here, the start of preediting is notified from IMlibrary, which
250 	   is the only way to start preediting in case of Dynamic Event
251 	   Flow, because ON key is mandatary for Dynamic Event Flow. */
252 	return True;
253     } else if (use_offkey && call_data->flag == 1) {	/* off key */
254 	/* Here, the end of preediting is notified from the IMlibrary, which
255 	   happens only if OFF key, which is optional for Dynamic Event Flow,
256 	   has been registered by IMOpenIM or IMSetIMValues, otherwise,
257 	   the end of preediting must be notified from the IMserver to the
258 	   IMlibrary. */
259 	return True;
260     } else {
261 	/* never happens */
262 	return False;
263     }
264 }
265 
MyPreeditStartReplyHandler(ims,call_data)266 MyPreeditStartReplyHandler(ims, call_data)
267 XIMS ims;
268 IMPreeditCBStruct *call_data;
269 {
270 }
271 
MyPreeditCaretReplyHandler(ims,call_data)272 MyPreeditCaretReplyHandler(ims, call_data)
273 XIMS ims;
274 IMPreeditCBStruct *call_data;
275 {
276 }
277 
MyProtoHandler(ims,call_data)278 MyProtoHandler(ims, call_data)
279 XIMS ims;
280 IMProtocol *call_data;
281 {
282     switch (call_data->major_code) {
283       case XIM_OPEN:
284         fprintf(stderr, "XIM_OPEN:\n");
285 	return MyOpenHandler(ims, call_data);
286       case XIM_CLOSE:
287         fprintf(stderr, "XIM_CLOSE:\n");
288 	return MyCloseHandler(ims, call_data);
289       case XIM_CREATE_IC:
290         fprintf(stderr, "XIM_CREATE_IC:\n");
291 	return MyCreateICHandler(ims, call_data);
292       case XIM_DESTROY_IC:
293         fprintf(stderr, "XIM_DESTROY_IC.\n");
294         return MyDestroyICHandler(ims, call_data);
295       case XIM_SET_IC_VALUES:
296         fprintf(stderr, "XIM_SET_IC_VALUES:\n");
297 	return MySetICValuesHandler(ims, call_data);
298       case XIM_GET_IC_VALUES:
299         fprintf(stderr, "XIM_GET_IC_VALUES:\n");
300 	return MyGetICValuesHandler(ims, call_data);
301       case XIM_FORWARD_EVENT:
302 	return MyForwardEventHandler(ims, call_data);
303       case XIM_SET_IC_FOCUS:
304         fprintf(stderr, "XIM_SET_IC_FOCUS()\n");
305 	return True;
306       case XIM_UNSET_IC_FOCUS:
307         fprintf(stderr, "XIM_UNSET_IC_FOCUS:\n");
308 	return True;
309       case XIM_RESET_IC:
310         fprintf(stderr, "XIM_RESET_IC_FOCUS:\n");
311 	return True;
312       case XIM_TRIGGER_NOTIFY:
313         fprintf(stderr, "XIM_TRIGGER_NOTIFY:\n");
314 	return MyTriggerNotifyHandler(ims, call_data);
315       case XIM_PREEDIT_START_REPLY:
316         fprintf(stderr, "XIM_PREEDIT_START_REPLY:\n");
317 	return MyPreeditStartReplyHandler(ims, call_data);
318       case XIM_PREEDIT_CARET_REPLY:
319         fprintf(stderr, "XIM_PREEDIT_CARET_REPLY:\n");
320 	return MyPreeditCaretReplyHandler(ims, call_data);
321       default:
322 	fprintf(stderr, "Unknown IMDKit Protocol message type\n");
323 	break;
324     }
325 }
326 
MyXEventHandler(im_window,event)327 void MyXEventHandler(im_window, event)
328 Window im_window;
329 XEvent *event;
330 {
331     fprintf(stderr, "Local Event\n");
332     switch (event->type) {
333       case DestroyNotify:
334 	break;
335       case ButtonPress:
336 	switch (event->xbutton.button) {
337 	  case Button3:
338 	    if (event->xbutton.window == im_window)
339 		goto Exit;
340 	    break;
341 	}
342       default:
343 	break;
344     }
345     return;
346   Exit:
347     XDestroyWindow(event->xbutton.display, im_window);
348     exit(0);
349 }
350 
main(argc,argv)351 main(argc, argv)
352 int argc;
353 char **argv;
354 {
355     char *display_name = NULL;
356     Display *dpy;
357     char *imname = NULL;
358     XIMS ims;
359     XIMStyles *input_styles, *styles2;
360     XIMTriggerKeys *on_keys, *trigger2;
361     XIMEncodings *encodings, *encoding2;
362     Window im_window;
363     register int i;
364     char transport[80];		/* enough */
365 
366     for (i = 1; i < argc; i++) {
367 	if (!strcmp(argv[i], "-name")) {
368 	    imname = argv[++i];
369 	} else if (!strcmp(argv[i], "-display")) {
370 	    display_name = argv[++i];
371 	} else if (!strcmp(argv[i], "-dynamic")) {
372 	    use_trigger = True;
373 	} else if (!strcmp(argv[i], "-static")) {
374 	    use_trigger = False;
375 	} else if (!strcmp(argv[i], "-tcp")) {
376 	    use_tcp = True;
377 	} else if (!strcmp(argv[i], "-local")) {
378 	    use_local = True;
379 	} else if (!strcmp(argv[i], "-offkey")) {
380 	    use_offkey = True;
381 	} else if (!strcmp(argv[i], "-kl")) {
382 	    filter_mask = (KeyPressMask|KeyReleaseMask);
383 	}
384     }
385     if (!imname) imname = DEFAULT_IMNAME;
386 
387 	setlocale(LC_CTYPE, "zh_TW");
388     if ((dpy = XOpenDisplay(display_name)) == NULL) {
389 	fprintf(stderr, "Can't Open Display: %s\n", display_name);
390 	exit(1);
391     }
392     im_window = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy),
393 				    0, 700, 400, 800-700,
394 				    0, WhitePixel(dpy, DefaultScreen(dpy)),
395 				    WhitePixel(dpy, DefaultScreen(dpy)));
396 
397     if (im_window == (Window)NULL) {
398 	fprintf(stderr, "Can't Create Window\n");
399 	exit(1);
400     }
401     XStoreName(dpy, im_window, "sampleIM");
402     XSetTransientForHint(dpy, im_window, im_window);
403 
404     if ((input_styles = (XIMStyles *)malloc(sizeof(XIMStyles))) == NULL) {
405 	fprintf(stderr, "Can't allocate\n");
406 	exit(1);
407     }
408     input_styles->count_styles = sizeof(Styles)/sizeof(XIMStyle) - 1;
409     input_styles->supported_styles = Styles;
410 
411     if ((on_keys = (XIMTriggerKeys *)
412 	 malloc(sizeof(XIMTriggerKeys))) == NULL) {
413 	fprintf(stderr, "Can't allocate\n");
414 	exit(1);
415     }
416     on_keys->count_keys = sizeof(Trigger_Keys)/sizeof(XIMTriggerKey) - 1;
417     on_keys->keylist = Trigger_Keys;
418 
419     if ((encodings = (XIMEncodings *)malloc(sizeof(XIMEncodings))) == NULL) {
420 	fprintf(stderr, "Can't allocate\n");
421 	exit(1);
422     }
423     encodings->count_encodings = sizeof(zhEncodings)/sizeof(XIMEncoding) - 1;
424     encodings->supported_encodings = zhEncodings;
425 
426     if (use_local) {
427 	char hostname[64];
428 	char *address = "/tmp/.ximsock";
429 
430 	gethostname(hostname, 64);
431 	sprintf(transport, "local/%s:%s", hostname, address);
432     } else if (use_tcp) {
433 	char hostname[64];
434 	int port_number = 9010;
435 
436 	gethostname(hostname, 64);
437 	sprintf(transport, "tcp/%s:%d", hostname, port_number);
438     } else {
439 	strcpy(transport, "X/");
440     }
441 
442     ims = IMOpenIM(dpy,
443 		   IMModifiers, "Xi18n",
444 		   IMServerWindow, im_window,
445 		   IMServerName, imname,
446 		   IMLocale, DEFAULT_LOCALE,
447 		   IMServerTransport, transport,
448 		   IMInputStyles, input_styles,
449 		   NULL);
450     if (ims == (XIMS)NULL) {
451 	fprintf(stderr, "Can't Open Input Method Service:\n");
452 	fprintf(stderr, "\tInput Method Name :%s\n", imname);
453 	fprintf(stderr, "\tTranport Address:%s\n", transport);
454 	exit(1);
455     }
456     if (use_trigger) {
457 	if (use_offkey)
458 	  IMSetIMValues(ims,
459 			IMOnKeysList, on_keys,
460 			IMOffKeysList, on_keys,
461 			NULL);
462 	else
463 	  IMSetIMValues(ims,
464 			IMOnKeysList, on_keys,
465 			NULL);
466     }
467     IMSetIMValues(ims,
468 		  IMEncodingList, encodings,
469 		  IMProtocolHandler, MyProtoHandler,
470 		  IMFilterEventMask, filter_mask,
471 		  NULL);
472     IMGetIMValues(ims,
473 		  IMInputStyles, &styles2,
474 		  IMOnKeysList, &trigger2,
475 		  IMOffKeysList, &trigger2,
476 		  IMEncodingList, &encoding2,
477 		  NULL);
478     XSelectInput(dpy, im_window, StructureNotifyMask|ButtonPressMask);
479     XMapWindow(dpy, im_window);
480     XFlush(dpy);		/* necessary flush for tcp/ip connection */
481 
482     for (;;) {
483 	XEvent event;
484 	XNextEvent(dpy, &event);
485 	if (XFilterEvent(&event, None) == True) {
486 		fprintf(stderr, "window %ld\n",event.xany.window);
487 		continue;
488 	}
489 	MyXEventHandler(im_window, &event);
490     }
491 }
492