Mercurial > kinput2.yaz
comparison lib/IMProto.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 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:92745d501b9a |
---|---|
1 #ifndef lint | |
2 static char *rcsid = "$Id: IMProto.c,v 1.20 1999/04/12 08:52:23 ishisone Exp $"; | |
3 #endif | |
4 /*- | |
5 * Copyright (c) 1991, 1994 Software Research Associates, Inc. | |
6 * | |
7 * Permission to use, copy, modify, and distribute this software and its | |
8 * documentation for any purpose and without fee is hereby granted, provided | |
9 * that the above copyright notice appear in all copies and that both that | |
10 * copyright notice and this permission notice appear in supporting | |
11 * documentation, and that the name of Software Research Associates not be | |
12 * used in advertising or publicity pertaining to distribution of the | |
13 * software without specific, written prior permission. Software Research | |
14 * Associates makes no representations about the suitability of this software | |
15 * for any purpose. It is provided "as is" without express or implied | |
16 * warranty. | |
17 * | |
18 * Author: Makoto Ishisone, Software Research Associates, Inc., Japan | |
19 */ | |
20 | |
21 /* | |
22 * X Input Method Protocol handler is considered to be still in | |
23 * beta testing phase. Current version has the following | |
24 * restrictions. | |
25 * - it does not support on-demand-synchronous method. | |
26 * - it does not support front-end model. | |
27 * - it does not SetIMValues operation. | |
28 * - it supports only X, local and TCP transports. | |
29 * Also, there might be various bugs. | |
30 */ | |
31 | |
32 #define DEBUG_VAR debug_IMProtocol | |
33 | |
34 #include <ctype.h> | |
35 #include <X11/Xos.h> | |
36 #include <sys/stat.h> | |
37 #include <X11/IntrinsicP.h> | |
38 #include <X11/StringDefs.h> | |
39 #include <X11/Xatom.h> | |
40 #include <X11/Xmu/SysUtil.h> | |
41 #include "IMProtoP.h" | |
42 #include "ParseKey.h" | |
43 #include "InputConv.h" | |
44 #include "im.h" | |
45 | |
46 | |
47 #define SERVER_NAME "kinput2" | |
48 #define UNIX_SOCKET_DIR "/tmp/.ki2-unix" | |
49 | |
50 /*- resource table -*/ | |
51 static XtResource resources[] = { | |
52 #define offset(field) XtOffset(IMProtocolWidget, imp.field) | |
53 { XtNserverName, XtCServerName, XtRString, sizeof(String), | |
54 offset(server_name), XtRString, (XtPointer)SERVER_NAME }, | |
55 { XtNlanguage, XtCLanguage, XtRString, sizeof(String), | |
56 offset(language), XtRImmediate, (XtPointer)NULL }, | |
57 { XtNlocales, XtCLocales, XtRString, sizeof(String), | |
58 offset(locales), XtRImmediate, (XtPointer)NULL }, | |
59 { XtNinputObjectClass, XtCClass, XtRPointer, sizeof(WidgetClass), | |
60 offset(input_object_class), XtRImmediate, (XtPointer)NULL }, | |
61 { XtNdisplayObjectClass, XtCClass, XtRPointer, sizeof(WidgetClass), | |
62 offset(display_object_class), XtRImmediate, (XtPointer)NULL }, | |
63 { XtNdefaultFontList, XtCFontList, XtRString, sizeof(String), | |
64 offset(default_fontlist), XtRImmediate, (XtPointer)NULL }, | |
65 { XtNconversionStartKeys, XtCConversionStartKeys, XtRString, sizeof(String), | |
66 offset(conversion_start_keys), XtRImmediate, (XtPointer)NULL }, | |
67 { XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel), | |
68 offset(foreground), XtRString, XtDefaultForeground }, | |
69 { XtNstatusWidth, XtCStatusWidth, XtRDimension, sizeof(Dimension), | |
70 offset(status_width), XtRImmediate, (XtPointer)0 }, | |
71 { XtNtransports, XtCTransports, XtRString, sizeof(String), | |
72 offset(transport_list), XtRString, (XtPointer)"tcp,unix,x" }, | |
73 #undef offset | |
74 }; | |
75 | |
76 static void Initialize _Pt_((Widget req, Widget new, | |
77 ArgList args, Cardinal *num_args)); | |
78 static void Destroy _Pt_((Widget w)); | |
79 static void Realize _Pt_((Widget w, XtValueMask *mask, | |
80 XSetWindowAttributes *value)); | |
81 | |
82 /*- IMProtocolClassRec -*/ | |
83 IMProtocolClassRec imProtocolClassRec = { | |
84 { /* core fields */ | |
85 /* superclass */ (WidgetClass) &widgetClassRec, | |
86 /* class_name */ "IMProtocol", | |
87 /* widget_size */ sizeof(IMProtocolRec), | |
88 /* class_initialize */ NULL, | |
89 /* class_part_initialize */ NULL, | |
90 /* class_inited */ FALSE, | |
91 /* initialize */ Initialize, | |
92 /* initialize_hook */ NULL, | |
93 /* realize */ Realize, | |
94 /* actions */ NULL, | |
95 /* num_actions */ 0, | |
96 /* resources */ resources, | |
97 /* num_resources */ XtNumber(resources), | |
98 /* xrm_class */ NULLQUARK, | |
99 /* compress_motion */ TRUE, | |
100 /* compress_exposure */ TRUE, | |
101 /* compress_enterleave */ TRUE, | |
102 /* visible_interest */ FALSE, | |
103 /* destroy */ Destroy, | |
104 /* resize */ NULL, | |
105 /* expose */ NULL, | |
106 /* set_values */ NULL, | |
107 /* set_values_hook */ NULL, | |
108 /* set_values_almost */ XtInheritSetValuesAlmost, | |
109 /* get_values_hook */ NULL, | |
110 /* accept_focus */ NULL, | |
111 /* version */ XtVersion, | |
112 /* callback_private */ NULL, | |
113 /* tm_table */ NULL, | |
114 /* query_geometry */ XtInheritQueryGeometry, | |
115 /* display_accelerator */ XtInheritDisplayAccelerator, | |
116 /* extension */ NULL | |
117 }, | |
118 { /* imProtocol fields */ | |
119 /* dummy */ 0 | |
120 } | |
121 }; | |
122 | |
123 WidgetClass imProtocolWidgetClass = (WidgetClass)&imProtocolClassRec; | |
124 | |
125 static void getAtoms _Pt_((IMProtocolWidget ipw)); | |
126 static void setProperty _Pt_((IMProtocolWidget ipw)); | |
127 static int ownSelection _Pt_((IMProtocolWidget ipw)); | |
128 static Boolean convertSelection _Pt_((Widget w, Atom *selectionp, | |
129 Atom *targetp, Atom *typep, | |
130 XtPointer *valuep, | |
131 unsigned long *lengthp, int *formatp)); | |
132 static void loseSelection _Pt_((Widget w, Atom *selectionp)); | |
133 #ifdef IM_TCP_TRANSPORT | |
134 static void acceptTCPService _Pt_((XtPointer client_data, | |
135 int *sourcep, XtInputId *idp)); | |
136 #endif | |
137 #ifdef IM_UNIX_TRANSPORT | |
138 static void acceptUnixService _Pt_((XtPointer client_data, | |
139 int *sourcep, XtInputId *idp)); | |
140 #endif | |
141 #ifdef IM_X_TRANSPORT | |
142 static void acceptXService _Pt_((Widget w, XtPointer client_data, | |
143 XEvent *event, Boolean *continuep)); | |
144 #endif | |
145 static void initializeError _Pt_((Widget w, String resname)); | |
146 static char *compactList _Pt_((char *s)); | |
147 static void setTransport _Pt_((Widget w)); | |
148 static int makeConverter _Pt_((Widget w)); | |
149 static void getTriggerKeys _Pt_((Widget w)); | |
150 static void ioeCallback _Pt_((XPointer cldata)); | |
151 | |
152 | |
153 /* | |
154 *+ Core class methods | |
155 */ | |
156 | |
157 /*- Initialize: intern Atoms, get default fonts, etc. -*/ | |
158 /* ARGSUSED */ | |
159 static void | |
160 Initialize(req, new, args, num_args) | |
161 Widget req; | |
162 Widget new; | |
163 ArgList args; | |
164 Cardinal *num_args; | |
165 { | |
166 IMProtocolWidget ipw = (IMProtocolWidget)new; | |
167 | |
168 TRACE(("IMProtocolWidget:Initialize()\n")); | |
169 | |
170 /* | |
171 * Check resources which must be specified at the initialization. | |
172 */ | |
173 #define NULLorEMPTY(p) ((p) == NULL || (p)[0] == '\0') | |
174 if (NULLorEMPTY(ipw->imp.server_name)) { | |
175 initializeError(new, XtNserverName); | |
176 } | |
177 | |
178 ipw->imp.server_name = XtNewString(ipw->imp.server_name); | |
179 | |
180 if (NULLorEMPTY(ipw->imp.language)) { | |
181 initializeError(new, XtNlanguage); | |
182 } else if (NULLorEMPTY(ipw->imp.locales)) { | |
183 initializeError(new, XtNlocales); | |
184 } else if (ipw->imp.input_object_class == NULL) { | |
185 initializeError(new, XtNinputObjectClass); | |
186 } else if (ipw->imp.display_object_class == NULL) { | |
187 initializeError(new, XtNdisplayObjectClass); | |
188 } | |
189 ipw->imp.locales = compactList(XtNewString(ipw->imp.locales)); | |
190 #undef NULLorEMPTY | |
191 | |
192 /* | |
193 * Initialize converter info. | |
194 */ | |
195 if (makeConverter(new) < 0) { | |
196 /* | |
197 * locales is empty. | |
198 */ | |
199 String params[1]; | |
200 Cardinal num_params; | |
201 | |
202 params[0] = XtClass(new)->core_class.class_name; | |
203 num_params = 1; | |
204 XtAppErrorMsg(XtWidgetToApplicationContext(new), | |
205 "initializeError", "invalidValue", "WidgetError", | |
206 "%s: locale list is empty", | |
207 params, &num_params); | |
208 } | |
209 | |
210 /* | |
211 * Create font bank (a bank of cached fonts) and enter | |
212 * default fonts. | |
213 */ | |
214 ipw->imp.font_bank = FontBankCreate(XtDisplay(new), ipw->imp.language); | |
215 if (ipw->imp.font_bank == NULL) { | |
216 /* | |
217 * The specified language is not supported. | |
218 */ | |
219 String params[2]; | |
220 Cardinal num_params; | |
221 | |
222 params[0] = XtClass(new)->core_class.class_name; | |
223 params[1] = ipw->imp.language; | |
224 num_params = 2; | |
225 XtAppErrorMsg(XtWidgetToApplicationContext(new), | |
226 "initializeError", "invalidValue", "WidgetError", | |
227 "%s: language %s not supported", | |
228 params, &num_params); | |
229 } | |
230 | |
231 if (ipw->imp.default_fontlist != NULL) { | |
232 ipw->imp.default_fontlist = XtNewString(ipw->imp.default_fontlist); | |
233 | |
234 DDPRINT(2, ("cache default fonts: %s\n", ipw->imp.default_fontlist)); | |
235 ipw->imp.default_fonts = FontBankGet(ipw->imp.font_bank, | |
236 ipw->imp.default_fontlist, | |
237 &ipw->imp.num_default_fonts); | |
238 } else { | |
239 ipw->imp.default_fonts = NULL; | |
240 ipw->imp.num_default_fonts = 0; | |
241 } | |
242 | |
243 /* | |
244 * Initialize private data. | |
245 */ | |
246 ipw->imp.connection_list = NULL; | |
247 ipw->imp.no_more_connections = False; | |
248 ipw->imp.scheduler_queue = NULL; | |
249 setTransport(new); | |
250 getTriggerKeys(new); | |
251 IMInitHash(new); | |
252 getAtoms(ipw); | |
253 | |
254 /* | |
255 * Initialilze transport layer. | |
256 */ | |
257 /* 1. TCP/IP */ | |
258 ipw->imp.tcp_sock = -1; | |
259 #ifdef IM_TCP_TRANSPORT | |
260 if (ipw->imp.use_tcp_transport) { | |
261 ipw->imp.tcp_port = 0; /* let the system choose the port number */ | |
262 ipw->imp.tcp_sock = IMCreateTCPService(&ipw->imp.tcp_port); | |
263 } | |
264 if (ipw->imp.tcp_sock >= 0) { | |
265 TRACE(("call XtAppAddInput for tcp socket(%d)\n", ipw->imp.tcp_sock)); | |
266 ipw->imp.tcp_id = XtAppAddInput(XtWidgetToApplicationContext(new), | |
267 ipw->imp.tcp_sock, | |
268 (XtPointer)XtInputReadMask, | |
269 acceptTCPService, (XtPointer)ipw); | |
270 } | |
271 #endif /* IM_TCP_TRANSPORT */ | |
272 | |
273 /* 2. UNIX domain */ | |
274 ipw->imp.unix_sock = -1; | |
275 #ifdef IM_UNIX_TRANSPORT | |
276 if (ipw->imp.use_unix_transport) { | |
277 char path[1024]; | |
278 char *p; | |
279 | |
280 /* | |
281 * The unix domain socket pathname has the following form: | |
282 * <UNIX_SOCKET_DIR>/<Display Name>-<Language> | |
283 */ | |
284 (void)mkdir(UNIX_SOCKET_DIR, 0777); | |
285 #ifdef S_IFLNK | |
286 { | |
287 /* | |
288 * This system has symbolic links. Make sure UNIX_SOCKET_DIR | |
289 * is not a symbolic link but a directory before calling | |
290 * chmod(). | |
291 */ | |
292 struct stat st; | |
293 if (lstat(UNIX_SOCKET_DIR, &st) == 0 && | |
294 (st.st_mode & S_IFMT) == S_IFDIR) { | |
295 (void)chmod(UNIX_SOCKET_DIR, 0777); | |
296 } | |
297 } | |
298 #else | |
299 (void)chmod(UNIX_SOCKET_DIR, 0777); | |
300 #endif | |
301 (void)sprintf(path, "%s/%s", UNIX_SOCKET_DIR, | |
302 DisplayString(XtDisplay(new))); | |
303 /* | |
304 * Omit screen number and the preceding period. | |
305 */ | |
306 for (p = path + strlen(path) - 1; p > path && *p != ':'; p--) { | |
307 if (*p == '.') { | |
308 *p = '\0'; | |
309 break; | |
310 } | |
311 } | |
312 /* | |
313 * Append language part. | |
314 */ | |
315 (void)strcat(path, "-"); | |
316 (void)strcat(path, ipw->imp.language); | |
317 /* | |
318 * Change every ':' in the path name to '_', since ':' is not | |
319 * included in POSIX Portable Filename Character Set. | |
320 */ | |
321 for (p = path; *p != '\0'; p++) { | |
322 if (*p == ':') *p = '_'; | |
323 } | |
324 ipw->imp.unix_path = XtNewString(path); | |
325 ipw->imp.unix_sock = IMCreateUnixService(ipw->imp.unix_path); | |
326 } | |
327 if (ipw->imp.unix_sock >= 0) { | |
328 TRACE(("call XtAppAddInput for unix socket(%d)\n", ipw->imp.unix_sock)); | |
329 ipw->imp.unix_id = XtAppAddInput(XtWidgetToApplicationContext(new), | |
330 ipw->imp.unix_sock, | |
331 (XtPointer)XtInputReadMask, | |
332 acceptUnixService, (XtPointer)ipw); | |
333 ipw->imp.ioe_handle = XIOESet(ioeCallback, (XPointer)ipw); | |
334 } | |
335 #endif /* IM_UNIX_TRANSPORT */ | |
336 | |
337 #ifdef IM_X_TRANSPORT | |
338 if (ipw->imp.use_x_transport) { | |
339 TRACE(("call XtAddEventHandler for X transport\n")); | |
340 XtAddEventHandler(new, NoEventMask, True, acceptXService, | |
341 (XtPointer)NULL); | |
342 } | |
343 #endif /* IM_X_TRANSPORT */ | |
344 | |
345 /* | |
346 * Compile request dispatching table. | |
347 */ | |
348 IMCompileReq(); | |
349 } | |
350 | |
351 /*- Destroy: free allocated memory -*/ | |
352 static void | |
353 Destroy(w) | |
354 Widget w; | |
355 { | |
356 IMProtocolWidget ipw = (IMProtocolWidget)w; | |
357 IMConnection *conn; | |
358 int i; | |
359 | |
360 TRACE(("IMProtocolWidget:Destroy()\n")); | |
361 | |
362 XtFree(ipw->imp.server_name); | |
363 XtFree(ipw->imp.locales); | |
364 if (ipw->imp.default_fontlist != NULL) XtFree(ipw->imp.default_fontlist); | |
365 if (ipw->imp.trigger_keys != NULL) XtFree((char *)ipw->imp.trigger_keys); | |
366 | |
367 for (i = 0; i < ipw->imp.converter.num_locales; i++) { | |
368 XtFree(ipw->imp.converter.supported_locales[i]); | |
369 } | |
370 XtFree((char *)ipw->imp.converter.supported_locales); | |
371 | |
372 /* | |
373 * Close down all connections. | |
374 */ | |
375 conn = ipw->imp.connection_list; | |
376 while (conn != NULL) { | |
377 IMConnection *next = conn->next; | |
378 | |
379 IMCloseConnection(conn); | |
380 conn = next; | |
381 } | |
382 | |
383 /* | |
384 * Close down TCP/Unix service sockets. | |
385 */ | |
386 if (ipw->imp.tcp_sock >= 0) { | |
387 XtRemoveInput(ipw->imp.tcp_id); | |
388 (void)close(ipw->imp.tcp_sock); | |
389 } | |
390 if (ipw->imp.unix_sock >= 0) { | |
391 XIOEUnset(ipw->imp.ioe_handle); | |
392 (void)unlink(ipw->imp.unix_path); | |
393 XtRemoveInput(ipw->imp.unix_id); | |
394 (void)close(ipw->imp.unix_sock); | |
395 XtFree(ipw->imp.unix_path); | |
396 } | |
397 | |
398 /* | |
399 * Unload default fonts. | |
400 */ | |
401 if (ipw->imp.num_default_fonts > 0) { | |
402 FontBankFreeFonts(ipw->imp.font_bank, | |
403 ipw->imp.default_fonts, | |
404 ipw->imp.num_default_fonts); | |
405 } | |
406 | |
407 /* | |
408 * Free font bank. | |
409 */ | |
410 FontBankDestroy(ipw->imp.font_bank); | |
411 } | |
412 | |
413 /*- Realize: own selection -*/ | |
414 static void | |
415 Realize(w, mask, value) | |
416 Widget w; | |
417 XtValueMask *mask; | |
418 XSetWindowAttributes *value; | |
419 { | |
420 IMProtocolWidget ipw = (IMProtocolWidget)w; | |
421 CoreWidgetClass super = (CoreWidgetClass)XtClass(w)->core_class.superclass; | |
422 | |
423 TRACE(("IMProtocolWidget:Realize()\n")); | |
424 | |
425 (*super->core_class.realize)(w, mask, value); | |
426 | |
427 if (ownSelection(ipw) < 0) { | |
428 String params[1]; | |
429 Cardinal num_params; | |
430 | |
431 params[0] = XtClass(w)->core_class.class_name; | |
432 num_params = 1; | |
433 XtAppWarningMsg(XtWidgetToApplicationContext(w), | |
434 "selectionError", "ownSelection", "WidgetError", | |
435 "%s: can't own selection", params, &num_params); | |
436 | |
437 XtDestroyWidget(w); | |
438 } | |
439 | |
440 setProperty(ipw); | |
441 } | |
442 | |
443 /* | |
444 *+ Atom, property and selection handling | |
445 */ | |
446 | |
447 /*- getAtoms: intern atoms -*/ | |
448 static void | |
449 getAtoms(ipw) | |
450 IMProtocolWidget ipw; | |
451 { | |
452 Display *dpy = XtDisplay((Widget)ipw); | |
453 char buf[256]; | |
454 | |
455 TRACE(("IMProtocolWidget:getAtoms()\n")); | |
456 | |
457 (void)strcpy(buf, "@server="); | |
458 (void)strcat(buf, ipw->imp.server_name); | |
459 #define MAKEATOM(s) XInternAtom(dpy, s, False) | |
460 ipw->imp.server_atom = MAKEATOM(buf); | |
461 ipw->imp.ctext_atom = MAKEATOM("COMPOUND_TEXT"); | |
462 ipw->imp.locales_atom = MAKEATOM("LOCALES"); | |
463 ipw->imp.transport_atom = MAKEATOM("TRANSPORT"); | |
464 ipw->imp.ki2comm_atom = MAKEATOM("_KINPUT2_COMM"); | |
465 ipw->imp.xim_xconnect = MAKEATOM("_XIM_XCONNECT"); | |
466 ipw->imp.xim_protocol = MAKEATOM("_XIM_PROTOCOL"); | |
467 ipw->imp.xim_moredata = MAKEATOM("_XIM_MOREDATA"); | |
468 #undef MAKEATOM | |
469 } | |
470 | |
471 /*- setProperty: set XIM_SERVERS property -*/ | |
472 static void | |
473 setProperty(ipw) | |
474 IMProtocolWidget ipw; | |
475 { | |
476 Display *dpy = XtDisplay((Widget)ipw); | |
477 Atom xim_servers = XInternAtom(dpy, "XIM_SERVERS", False); | |
478 Atom server_atom = ipw->imp.server_atom; | |
479 Window root0 = RootWindow(dpy, 0); | |
480 int op_mode = PropModePrepend; | |
481 int no_registration = 0; | |
482 Atom type; | |
483 int format; | |
484 unsigned long nitems; | |
485 unsigned long bytes_after; | |
486 unsigned char *value; | |
487 unsigned long data; | |
488 | |
489 TRACE(("IMProtocolWidget:setProperty()\n")); | |
490 | |
491 /* | |
492 * For atomic operation, grab the server. | |
493 */ | |
494 #ifndef DEBUG | |
495 XGrabServer(dpy); | |
496 #endif | |
497 | |
498 /* | |
499 * First, check the XIM_SERVERS property's existance. | |
500 * If it exists, examine the contents. | |
501 */ | |
502 if (XGetWindowProperty(dpy, root0, xim_servers, 0L, 1024L, False, | |
503 AnyPropertyType, &type, &format, &nitems, | |
504 &bytes_after, &value) == Success) { | |
505 if (type != XA_ATOM || format != 32) { | |
506 /* | |
507 * The contents of the property is invalid. | |
508 */ | |
509 DDPRINT(2, ("XIM_SERVERS is corrupted (type=%ld, format=%d)\n", | |
510 type, format)); | |
511 op_mode = PropModeReplace; | |
512 } else { | |
513 int i; | |
514 unsigned long *atoms = (unsigned long *)value; | |
515 | |
516 for (i = 0; i < nitems; i++) { | |
517 if (atoms[i] == server_atom) { | |
518 /* | |
519 * Already registered. | |
520 */ | |
521 TRACE(("server is already registered in XIM_SERVERS\n")); | |
522 no_registration = 1; | |
523 break; | |
524 } | |
525 } | |
526 } | |
527 if (value != NULL) XFree((char *)value); | |
528 } | |
529 | |
530 if (!no_registration) { | |
531 TRACE(("changing XIM_SERVERS property\n")); | |
532 data = ipw->imp.server_atom; | |
533 XChangeProperty(dpy, root0, xim_servers, XA_ATOM, 32, op_mode, | |
534 (unsigned char *)&data, 1); | |
535 } else { | |
536 TRACE(("touching XIM_SERVERS property to generate PropertyNotify\n")); | |
537 XChangeProperty(dpy, root0, xim_servers, XA_ATOM, 32, PropModeAppend, | |
538 (unsigned char *)&data, 0); | |
539 } | |
540 | |
541 #ifndef DEBUG | |
542 XUngrabServer(dpy); | |
543 #endif | |
544 } | |
545 | |
546 /*- ownSelection: own conversion selection -*/ | |
547 static int | |
548 ownSelection(ipw) | |
549 IMProtocolWidget ipw; | |
550 { | |
551 Display *dpy = XtDisplay((Widget)ipw); | |
552 Time time = XtLastTimestampProcessed(dpy); | |
553 | |
554 | |
555 TRACE(("IMProtocolWidget:ownSelection()\n")); | |
556 | |
557 if (!XtOwnSelection((Widget)ipw, ipw->imp.server_atom, time, | |
558 convertSelection, loseSelection, | |
559 (XtSelectionDoneProc)NULL)) { | |
560 DPRINT(("cannot own selection")); | |
561 return -1; | |
562 } | |
563 DPRINT(("selection atom:%ld owner: %08lx (%ld)\n", ipw->imp.server_atom, | |
564 XtWindow((Widget)ipw), XtWindow((Widget)ipw))); | |
565 return 0; | |
566 } | |
567 | |
568 /*- convertSelection: convert selections -*/ | |
569 /* ARGSUSED */ | |
570 static Boolean | |
571 convertSelection(w, selectionp, targetp, typep, valuep, lengthp, formatp) | |
572 Widget w; | |
573 Atom *selectionp; | |
574 Atom *targetp; | |
575 Atom *typep; | |
576 XtPointer *valuep; | |
577 unsigned long *lengthp; | |
578 int *formatp; | |
579 { | |
580 Display *dpy = XtDisplay(w); | |
581 IMProtocolWidget ipw = (IMProtocolWidget)w; | |
582 | |
583 TRACE(("IMProtocolWidget:convertSelection()\n")); | |
584 | |
585 if (*targetp == XInternAtom(dpy, "TARGETS", False)) { | |
586 Atom *targets; | |
587 | |
588 TRACE(("target is \"TARGETS\"\n")); | |
589 targets = (Atom *)XtMalloc(sizeof(Atom) * 2); | |
590 targets[0] = ATOM_LOCALES(w); | |
591 targets[1] = ATOM_TRANSPORT(w); | |
592 | |
593 *typep = XA_ATOM; | |
594 *valuep = (XtPointer)targets; | |
595 *lengthp = 2; | |
596 *formatp = 32; | |
597 return True; | |
598 } else if (*targetp == ATOM_LOCALES(w)) { | |
599 char buf[1024]; | |
600 | |
601 TRACE(("target is \"LOCALES\"\n")); | |
602 (void)strcpy(buf, "@locale="); | |
603 (void)strcat(buf, ipw->imp.locales); | |
604 TRACE(("\ttype: STRING, value: %s\n", buf)); | |
605 /* | |
606 * The protocol spec is unclear on the type of the | |
607 * selection value. Since R6 sample implementation | |
608 * uses LOCALES, use it. | |
609 */ | |
610 *typep = *targetp; | |
611 /* *typep = XA_STRING; */ | |
612 *valuep = (XtPointer)XtNewString(buf); | |
613 *lengthp = strlen(buf); | |
614 *formatp = 8; | |
615 return True; | |
616 } else if (*targetp == ATOM_TRANSPORT(w)) { | |
617 char buf[1024]; | |
618 char hostname[256]; | |
619 | |
620 TRACE(("target is \"TRANSPORT\"\n")); | |
621 | |
622 XmuGetHostname(hostname, 256); | |
623 | |
624 (void)strcpy(buf, "@transport="); | |
625 | |
626 #ifdef IM_X_TRANSPORT | |
627 if (ipw->imp.use_x_transport) { | |
628 (void)strcat(buf, "X/,"); | |
629 } | |
630 #endif /* IM_X_TRANSPORT */ | |
631 | |
632 #ifdef IM_TCP_TRANSPORT | |
633 if (ipw->imp.use_tcp_transport) { | |
634 char t_buf[1024]; | |
635 (void)sprintf(t_buf, "tcp/%s:%d,", hostname, ipw->imp.tcp_port); | |
636 (void)strcat(buf, t_buf); | |
637 } | |
638 #endif /* IM_TCP_TRANSPORT */ | |
639 | |
640 #ifdef IM_UNIX_TRANSPORT | |
641 if (ipw->imp.use_unix_transport) { | |
642 char u_buf[1024]; | |
643 (void)sprintf(u_buf, "local/%s:%s,", hostname, ipw->imp.unix_path); | |
644 (void)strcat(buf, u_buf); | |
645 } | |
646 #endif /* IM_UNIX_TRANSPORT */ | |
647 | |
648 /* delete trailing comma */ | |
649 if (buf[strlen(buf) - 1] == ',') buf[strlen(buf) - 1] = '\0'; | |
650 TRACE(("\ttype: STRING, value: %s\n", buf)); | |
651 | |
652 *typep = *targetp; /* -- see the comment on LOCALES above */ | |
653 *valuep = (XtPointer)XtNewString(buf); | |
654 *lengthp = strlen(buf); | |
655 *formatp = 8; | |
656 return True; | |
657 } else { | |
658 DDPRINT(2, ("unknown target atom (%ld)\n", *targetp)); | |
659 return False; | |
660 } | |
661 } | |
662 | |
663 /*- loseSelection: disable IM protocol handling -*/ | |
664 /* ARGSUSED */ | |
665 static void | |
666 loseSelection(w, selectionp) | |
667 Widget w; | |
668 Atom *selectionp; | |
669 { | |
670 IMProtocolWidget ipw = (IMProtocolWidget)w; | |
671 | |
672 TRACE(("IMProtocolWidget:loseSelection()\n")); | |
673 | |
674 /* | |
675 * Someone takes over the selection. That means | |
676 * another kinput2 process has been started. | |
677 * Let the newly process handle new clients, but | |
678 * as long as existing clients are remained, we have | |
679 * to maintain them. | |
680 */ | |
681 | |
682 if (ipw->imp.connection_list == NULL) { | |
683 /* | |
684 * There are no clients. It is OK to destroy protocol handler. | |
685 */ | |
686 XtDestroyWidget(w); | |
687 return; | |
688 } | |
689 | |
690 ipw->imp.no_more_connections = True; | |
691 | |
692 /* | |
693 * Close down TCP/Unix service sockets. | |
694 */ | |
695 if (ipw->imp.tcp_sock >= 0) { | |
696 TRACE(("\tclose tcp socket\n")); | |
697 XtRemoveInput(ipw->imp.tcp_id); | |
698 (void)close(ipw->imp.tcp_sock); | |
699 ipw->imp.tcp_sock = -1; | |
700 } | |
701 if (ipw->imp.unix_sock >= 0) { | |
702 TRACE(("\tclose unix socket\n")); | |
703 XtRemoveInput(ipw->imp.unix_id); | |
704 (void)close(ipw->imp.unix_sock); | |
705 ipw->imp.unix_sock = -1; | |
706 } | |
707 } | |
708 | |
709 | |
710 /* | |
711 *+ Connection acceptance | |
712 */ | |
713 | |
714 #ifdef IM_TCP_TRANSPORT | |
715 /*- acceptTCPService: establish connection via TCP transport -*/ | |
716 /* ARGSUSED */ | |
717 static void | |
718 acceptTCPService(client_data, sourcep, idp) | |
719 XtPointer client_data; | |
720 int *sourcep; | |
721 XtInputId *idp; | |
722 { | |
723 IMProtocolWidget ipw = (IMProtocolWidget)client_data; | |
724 IMConnection *conn; | |
725 | |
726 TRACE(("IMProtocolWidget:acceptTCPService()\n")); | |
727 | |
728 /* | |
729 * Accept connection request. | |
730 */ | |
731 conn = IMTCPConnection((Widget)ipw, *sourcep); | |
732 | |
733 /* | |
734 * Set dispatcher. | |
735 */ | |
736 if (conn != NULL) IMSetInitialDispatcher(conn); | |
737 | |
738 /* | |
739 * Enter to the connections list. | |
740 */ | |
741 if (conn != NULL) IMRegisterConnection(conn); | |
742 } | |
743 #endif /* IM_TCP_TRANSPORT */ | |
744 | |
745 #ifdef IM_UNIX_TRANSPORT | |
746 /*- acceptUnixService: establish connection via UNIX domain transport -*/ | |
747 /* ARGSUSED */ | |
748 static void | |
749 acceptUnixService(client_data, sourcep, idp) | |
750 XtPointer client_data; | |
751 int *sourcep; | |
752 XtInputId *idp; | |
753 { | |
754 IMProtocolWidget ipw = (IMProtocolWidget)client_data; | |
755 IMConnection *conn; | |
756 | |
757 TRACE(("IMProtocolWidget:acceptUnixService()\n")); | |
758 | |
759 /* | |
760 * Accept connection request. | |
761 */ | |
762 conn = IMUnixConnection((Widget)ipw, *sourcep); | |
763 | |
764 /* | |
765 * Set dispatcher. | |
766 */ | |
767 if (conn != NULL) IMSetInitialDispatcher(conn); | |
768 | |
769 /* | |
770 * Enter to the connections list. | |
771 */ | |
772 if (conn != NULL) IMRegisterConnection(conn); | |
773 } | |
774 #endif /* IM_UNIX_TRANSPORT */ | |
775 | |
776 #ifdef IM_X_TRANSPORT | |
777 /*- acceptXService: establish connection via X transport -*/ | |
778 /* ARGSUSED */ | |
779 static void | |
780 acceptXService(w, client_data, event, continuep) | |
781 Widget w; | |
782 XtPointer client_data; | |
783 XEvent *event; | |
784 Boolean *continuep; | |
785 { | |
786 IMConnection *conn; | |
787 | |
788 TRACE(("IMProtocolWidget:acceptXService()\n")); | |
789 | |
790 /* | |
791 * Check if the event is really a connection request. | |
792 */ | |
793 if (event->type != ClientMessage) return; | |
794 conn = IMXConnection(w, event); | |
795 | |
796 /* | |
797 * Set dispatcher. | |
798 */ | |
799 if (conn != NULL) IMSetInitialDispatcher(conn); | |
800 | |
801 /* | |
802 * Enter to the connections list. | |
803 */ | |
804 if (conn != NULL) IMRegisterConnection(conn); | |
805 } | |
806 #endif /* IM_X_TRANSPORT */ | |
807 | |
808 | |
809 /* | |
810 *+ utility functions | |
811 */ | |
812 | |
813 /*- initializeError: display error message when resource isn't specified -*/ | |
814 static void | |
815 initializeError(w, resname) | |
816 Widget w; | |
817 String resname; | |
818 { | |
819 String params[2]; | |
820 Cardinal num_params; | |
821 | |
822 params[0] = XtClass(w)->core_class.class_name; | |
823 params[1] = resname; | |
824 num_params = 2; | |
825 XtAppErrorMsg(XtWidgetToApplicationContext(w), | |
826 "initializeError", "noResource", "WidgetError", | |
827 "%s: resource %s must be specified at widget creation", | |
828 params, &num_params); | |
829 } | |
830 | |
831 /*- compactList: remove unnecessary spaces in a comma-separated list -*/ | |
832 static char * | |
833 compactList(s) | |
834 char *s; | |
835 { | |
836 char *src, *dst; | |
837 int c; | |
838 | |
839 src = dst = s; | |
840 for (;;) { | |
841 /* skip leading space */ | |
842 while (isspace(*src)) src++; | |
843 | |
844 if (*src == '\0') { | |
845 *dst = '\0'; | |
846 return s; | |
847 } | |
848 | |
849 /* copy string until comma or NUL appears */ | |
850 while ((c = *dst++ = *src++) != ',') { | |
851 if (c == '\0') return s; | |
852 } | |
853 } | |
854 } | |
855 | |
856 /*- setTransport: determine which transport to be used -*/ | |
857 static void | |
858 setTransport(w) | |
859 Widget w; | |
860 { | |
861 IMProtocolWidget ipw = (IMProtocolWidget)w; | |
862 char *p; | |
863 | |
864 TRACE(("IMProtocolWidget:setTransport(%s)\n", ipw->imp.transport_list)); | |
865 | |
866 ipw->imp.use_tcp_transport = False; | |
867 ipw->imp.use_unix_transport = False; | |
868 ipw->imp.use_x_transport = False; | |
869 | |
870 p = ipw->imp.transport_list; | |
871 while (*p != '\0') { | |
872 char lower[256]; | |
873 char *q; | |
874 | |
875 while (isspace(*p)) p++; | |
876 if (*p == '\0') break; | |
877 | |
878 q = lower; | |
879 while (*p != '\0' && *p != ',' && !isspace(*p)) { | |
880 if (isupper(*p)) { | |
881 *q++ = tolower(*p); | |
882 } else { | |
883 *q++ = *p; | |
884 } | |
885 p++; | |
886 } | |
887 *q = '\0'; | |
888 while (isspace(*p)) p++; | |
889 if (*p == ',') p++; | |
890 | |
891 if (!strcmp(lower, "tcp")) { | |
892 TRACE(("\tTCP transport\n")); | |
893 ipw->imp.use_tcp_transport = True; | |
894 } else if (!strcmp(lower, "unix")) { | |
895 TRACE(("\tUNIX domain transport\n")); | |
896 ipw->imp.use_unix_transport = True; | |
897 } else if (!strcmp(lower, "x")) { | |
898 TRACE(("\tX transport\n")); | |
899 ipw->imp.use_x_transport = True; | |
900 } | |
901 } | |
902 } | |
903 | |
904 /*- makeConverter: create converter record -*/ | |
905 static int | |
906 makeConverter(w) | |
907 Widget w; | |
908 { | |
909 IMProtocolWidget ipw = (IMProtocolWidget)w; | |
910 char *locales[100]; | |
911 int num_locales; | |
912 int size; | |
913 char *p; | |
914 | |
915 TRACE(("IMProtocolWidget:makeConverter()\n")); | |
916 | |
917 ipw->imp.converter.input_object_class = ipw->imp.input_object_class; | |
918 ipw->imp.converter.display_object_class = ipw->imp.display_object_class; | |
919 | |
920 p = ipw->imp.locales; | |
921 num_locales = 0; | |
922 do { | |
923 char buf[256]; | |
924 char *q = buf; | |
925 | |
926 while (isspace(*p)) p++; | |
927 if (*p == '\0') break; | |
928 | |
929 while (*p != '\0' && *p != ',' && !isspace(*p)) *q++ = *p++; | |
930 *q = '\0'; | |
931 TRACE(("\tsupported locale: %s\n", buf)); | |
932 locales[num_locales++] = XtNewString(buf); | |
933 while (isspace(*p)) p++; | |
934 if (*p == ',') p++; | |
935 } while (*p != '\0' && num_locales < 100); | |
936 TRACE(("\tnumber of supported locales: %d\n", num_locales)); | |
937 | |
938 if (num_locales == 0) return -1; | |
939 ipw->imp.converter.num_locales = num_locales; | |
940 | |
941 size = sizeof(char *) * num_locales; | |
942 ipw->imp.converter.supported_locales = (char **)XtMalloc(size); | |
943 bcopy((char *)locales, (char *)ipw->imp.converter.supported_locales, size); | |
944 return 0; | |
945 } | |
946 | |
947 /*- getTriggerKeys: parse conversion trigger key specification -*/ | |
948 static void | |
949 getTriggerKeys(w) | |
950 Widget w; | |
951 { | |
952 IMProtocolWidget ipw = (IMProtocolWidget)w; | |
953 char *key_str; | |
954 IMTriggerKey keys[100]; | |
955 int num_keys; | |
956 int c, n; | |
957 ICTriggerKey *ckeys, *ekeys; | |
958 | |
959 TRACE(("IMProtocolWidget:getTriggerKeys()\n")); | |
960 | |
961 key_str = ipw->imp.conversion_start_keys; | |
962 num_keys = 0; | |
963 TRACE(("\tstart keys: %s\n", key_str)); | |
964 | |
965 if (key_str != NULL) { | |
966 do { | |
967 char buf[256]; | |
968 char *p = buf; | |
969 KeySym keysym; | |
970 long mods, chk_mods; | |
971 | |
972 while ((c = *key_str++) != '\0' && c != '\n') { | |
973 *p++ = c; | |
974 } | |
975 *p = '\0'; | |
976 if (ParseKeyEvent(buf, &keysym, &mods, &chk_mods)) { | |
977 TRACE(("\tkeysym: %08lx, modifiers: %04lx, check: %04lx\n", | |
978 keysym, mods, chk_mods)); | |
979 keys[num_keys].keysym = keysym; | |
980 keys[num_keys].modifiers = mods; | |
981 keys[num_keys].check_modifiers = chk_mods; | |
982 num_keys++; | |
983 } | |
984 } while (c != '\0' && num_keys < 100); | |
985 } | |
986 | |
987 n = ICGetTriggerKeysOfInputObjectClass(ipw->imp.input_object_class, | |
988 &ckeys); | |
989 for (ekeys = ckeys + n ; | |
990 ckeys < ekeys && num_keys < (sizeof(keys) / sizeof(IMTriggerKey)) ; | |
991 ckeys++) { | |
992 keys[num_keys].keysym = ckeys->keysym; | |
993 keys[num_keys].modifiers = ckeys->modifiers; | |
994 keys[num_keys].check_modifiers = ckeys->modifiermask; | |
995 num_keys++; | |
996 } | |
997 | |
998 TRACE(("\tnumber of trigger keys: %d\n", num_keys)); | |
999 ipw->imp.num_trigger_keys = num_keys; | |
1000 | |
1001 if (num_keys > 0) { | |
1002 int size; | |
1003 | |
1004 size = sizeof(IMTriggerKey) * num_keys; | |
1005 ipw->imp.trigger_keys = (IMTriggerKey *)XtMalloc(size); | |
1006 bcopy((char *)keys, (char *)ipw->imp.trigger_keys, size); | |
1007 } else { | |
1008 ipw->imp.trigger_keys = NULL; | |
1009 } | |
1010 } | |
1011 | |
1012 /*- ioeCallback: callback procedure for X I/O error -*/ | |
1013 static void | |
1014 ioeCallback(cldata) | |
1015 XPointer cldata; | |
1016 { | |
1017 IMProtocolWidget ipw = (IMProtocolWidget)cldata; | |
1018 | |
1019 if (ipw->imp.unix_sock >= 0 && ipw->imp.unix_path != NULL) { | |
1020 (void)unlink(ipw->imp.unix_path); | |
1021 } | |
1022 } |