1/*
2 * Copyright (c) 1992, Oracle and/or its affiliates.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23/******************************************************************
24
25           Copyright 1992, 1993, 1994 by FUJITSU LIMITED
26
27Permission to use, copy, modify, distribute, and sell this software
28and its documentation for any purpose is hereby granted without fee,
29provided that the above copyright notice appear in all copies and
30that both that copyright notice and this permission notice appear
31in supporting documentation, and that the name of FUJITSU LIMITED
32not be used in advertising or publicity pertaining to distribution
33of the software without specific, written prior permission.
34FUJITSU LIMITED makes no representations about the suitability of
35this software for any purpose.
36It is provided "as is" without express or implied warranty.
37
38FUJITSU LIMITED DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
39INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
40EVENT SHALL FUJITSU LIMITED BE LIABLE FOR ANY SPECIAL, INDIRECT OR
41CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
42USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
43OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
44PERFORMANCE OF THIS SOFTWARE.
45
46  Author: Hideki Hiura (hhiura@Sun.COM) Sun Microsystems, Inc.
47          Takashi Fujiwara     FUJITSU LIMITED
48                               fujiwara@a80.tech.yk.fujitsu.co.jp
49
50******************************************************************/
51
52#ifdef HAVE_CONFIG_H
53#include <config.h>
54#endif
55#include <string.h>
56#include <X11/Xatom.h>
57#include "Xlibint.h"
58#include "Xlcint.h"
59#include "Ximint.h"
60#include "XimTrInt.h"
61#include "XimTrX.h"
62
63static Bool
64_XimXRegisterDispatcher(
65    Xim			 im,
66    Bool		 (*callback)(
67				     Xim, INT16, XPointer, XPointer
68				     ),
69    XPointer		 call_data)
70{
71    XIntrCallbackPtr	 rec;
72    XSpecRec		*spec = (XSpecRec *)im->private.proto.spec;
73
74    if (!(rec = Xmalloc(sizeof(XIntrCallbackRec))))
75        return False;
76
77    rec->func       = callback;
78    rec->call_data  = call_data;
79    rec->next       = spec->intr_cb;
80    spec->intr_cb   = rec;
81    return True;
82}
83
84static void
85_XimXFreeIntrCallback(
86    Xim			 im)
87{
88    XSpecRec		*spec = (XSpecRec *)im->private.proto.spec;
89    register XIntrCallbackPtr rec, next;
90
91    for (rec = spec->intr_cb; rec;) {
92	next = rec->next;
93	Xfree(rec);
94	rec = next;
95    }
96    spec->intr_cb = NULL;
97    return;
98}
99
100static Bool
101_XimXCallDispatcher(Xim im, INT16 len, XPointer data)
102{
103    register XIntrCallbackRec	*rec;
104    XSpecRec		*spec = (XSpecRec *)im->private.proto.spec;
105
106    for (rec = spec->intr_cb; rec; rec = rec->next) {
107	if ((*rec->func)(im, len, data, rec->call_data))
108	    return True;
109    }
110    return False;
111}
112
113static Bool
114_XimXFilterWaitEvent(
115    Display	*d,
116    Window	 w,
117    XEvent	*ev,
118    XPointer	 arg)
119{
120    Xim		 im = (Xim)arg;
121    XSpecRec	*spec = (XSpecRec *)im->private.proto.spec;
122    Bool ret;
123
124    spec->ev = (XPointer)ev;
125    ret = _XimFilterWaitEvent(im);
126
127    /*
128     * If ev is a pointer to a stack variable, there could be
129     * a coredump later on if the pointer is dereferenced.
130     * Therefore, reset to NULL to force reinitialization in
131     * _XimXRead().
132     *
133     * Keep in mind _XimXRead may be called again when the stack
134     * is very different.
135     */
136     spec->ev = (XPointer)NULL;
137
138     return ret;
139}
140
141static Bool
142_CheckConnect(
143    Display	*display,
144    XEvent	*event,
145    XPointer	 xim)
146{
147    Xim		 im = (Xim)xim;
148    XSpecRec	*spec = (XSpecRec *)im->private.proto.spec;
149
150    if ((event->type == ClientMessage)
151     && (event->xclient.message_type == spec->imconnectid)) {
152	return True;
153    }
154    return False;
155}
156
157static Bool
158_XimXConnect(Xim im)
159{
160    XEvent	 event;
161    XSpecRec	*spec = (XSpecRec *)im->private.proto.spec;
162    CARD32	 major_code;
163    CARD32	 minor_code;
164
165    if (!(spec->lib_connect_wid = XCreateSimpleWindow(im->core.display,
166		DefaultRootWindow(im->core.display), 0, 0, 1, 1, 1, 0, 0))) {
167	return False;
168    }
169
170    event.xclient.type         = ClientMessage;
171    event.xclient.display      = im->core.display;
172    event.xclient.window       = im->private.proto.im_window;
173    event.xclient.message_type = spec->imconnectid;
174    event.xclient.format       = 32;
175    event.xclient.data.l[0]    = (CARD32)spec->lib_connect_wid;
176    event.xclient.data.l[1]    = spec->major_code;
177    event.xclient.data.l[2]    = spec->minor_code;
178    event.xclient.data.l[3]    = 0;
179    event.xclient.data.l[4]    = 0;
180
181    if(event.xclient.data.l[1] == 1 || event.xclient.data.l[1] == 2) {
182	XWindowAttributes	 atr;
183	long			 event_mask;
184
185	XGetWindowAttributes(im->core.display, spec->lib_connect_wid, &atr);
186	event_mask = atr.your_event_mask | PropertyChangeMask;
187	XSelectInput(im->core.display, spec->lib_connect_wid, event_mask);
188	_XRegisterFilterByType(im->core.display, spec->lib_connect_wid,
189			PropertyNotify, PropertyNotify,
190			 _XimXFilterWaitEvent, (XPointer)im);
191    }
192
193    XSendEvent(im->core.display, im->private.proto.im_window,
194		 False, NoEventMask, &event);
195    XFlush(im->core.display);
196
197    for (;;) {
198	XIfEvent(im->core.display, &event, _CheckConnect, (XPointer)im);
199	if (event.xclient.type != ClientMessage) {
200	    return False;
201	}
202	if (event.xclient.message_type == spec->imconnectid)
203	    break;
204    }
205
206    spec->ims_connect_wid = (Window)event.xclient.data.l[0];
207    major_code = (CARD32)event.xclient.data.l[1];
208    minor_code = (CARD32)event.xclient.data.l[2];
209
210    if (((major_code == 0) && (minor_code <= 2)) ||
211	((major_code == 1) && (minor_code == 0)) ||
212	((major_code == 2) && (minor_code <= 1))) {
213	spec->major_code = major_code;
214	spec->minor_code = minor_code;
215    }
216    if (((major_code == 0) && (minor_code == 2)) ||
217	((major_code == 2) && (minor_code == 1))) {
218	spec->BoundarySize = (CARD32)event.xclient.data.l[3];
219    }
220
221    /* ClientMessage Event Filter */
222    _XRegisterFilterByType(im->core.display, spec->lib_connect_wid,
223			ClientMessage, ClientMessage,
224			 _XimXFilterWaitEvent, (XPointer)im);
225    return True;
226}
227
228static Bool
229_XimXShutdown(Xim im)
230{
231    XSpecRec	*spec = (XSpecRec *)im->private.proto.spec;
232
233    if (!spec)
234	return True;
235
236    /* ClientMessage Event Filter */
237    _XUnregisterFilter(im->core.display,
238	    ((XSpecRec *)im->private.proto.spec)->lib_connect_wid,
239	    _XimXFilterWaitEvent, (XPointer)im);
240    XDestroyWindow(im->core.display,
241	    ((XSpecRec *)im->private.proto.spec)->lib_connect_wid);
242    _XimXFreeIntrCallback(im);
243    Xfree(spec);
244    im->private.proto.spec = 0;
245    return True;
246}
247
248static char *
249_NewAtom(
250    char	*atomName)
251{
252    static int	 sequence = 0;
253
254    (void)sprintf(atomName, "_client%d", sequence);
255    sequence = ((sequence < 20) ? sequence + 1 : 0);
256    return atomName;
257}
258
259static Bool
260_XimXWrite(Xim im, INT16 len, XPointer data)
261{
262    Atom	 atom;
263    char	 atomName[16];
264    XSpecRec	*spec = (XSpecRec *)im->private.proto.spec;
265    XEvent	 event;
266    CARD8	*p;
267    CARD32	 major_code = spec->major_code;
268    CARD32	 minor_code = spec->minor_code;
269    int		 BoundSize;
270
271    bzero(&event,sizeof(XEvent));
272    event.xclient.type         = ClientMessage;
273    event.xclient.display      = im->core.display;
274    event.xclient.window       = spec->ims_connect_wid;
275    if(major_code == 1 && minor_code == 0) {
276        BoundSize = 0;
277    } else if((major_code == 0 && minor_code == 2) ||
278              (major_code == 2 && minor_code == 1)) {
279        BoundSize = spec->BoundarySize;
280    } else if(major_code == 0 && minor_code == 1) {
281        BoundSize = len;
282    } else {
283        BoundSize = XIM_CM_DATA_SIZE;
284    }
285    if (len > BoundSize) {
286	event.xclient.message_type = spec->improtocolid;
287	atom = XInternAtom(im->core.display, _NewAtom(atomName), False);
288	XChangeProperty(im->core.display, spec->ims_connect_wid,
289			atom, XA_STRING, 8, PropModeAppend,
290			(unsigned char *)data, len);
291	if(major_code == 0) {
292	    event.xclient.format = 32;
293	    event.xclient.data.l[0] = (long)len;
294	    event.xclient.data.l[1] = (long)atom;
295	    XSendEvent(im->core.display, spec->ims_connect_wid,
296					False, NoEventMask, &event);
297	}
298    } else {
299	int		 length;
300
301	event.xclient.format = 8;
302	for(length = 0 ; length < len ; length += XIM_CM_DATA_SIZE) {
303	    p = (CARD8 *)&event.xclient.data.b[0];
304	    if((length + XIM_CM_DATA_SIZE) >= len) {
305		event.xclient.message_type = spec->improtocolid;
306		bzero(p, XIM_CM_DATA_SIZE);
307		memcpy((char *)p, (data + length), (len - length));
308	    } else {
309		event.xclient.message_type = spec->immoredataid;
310		memcpy((char *)p, (data + length), XIM_CM_DATA_SIZE);
311	    }
312	    XSendEvent(im->core.display, spec->ims_connect_wid,
313					False, NoEventMask, &event);
314	}
315    }
316
317    return True;
318}
319
320static Bool
321_XimXGetReadData(
322    Xim			  im,
323    char		 *buf,
324    int			  buf_len,
325    int			 *ret_len,
326    XEvent		 *event)
327{
328    char		 *data;
329    int			  len;
330
331    char		  tmp_buf[XIM_CM_DATA_SIZE];
332    XSpecRec		 *spec = (XSpecRec *)im->private.proto.spec;
333    unsigned long	  length;
334    Atom		  prop;
335    int			  return_code;
336    Atom		  type_ret;
337    int			  format_ret;
338    unsigned long	  nitems;
339    unsigned long	  bytes_after_ret;
340    unsigned char	 *prop_ret;
341
342    if ((event->type == ClientMessage) &&
343        !((event->xclient.message_type == spec->improtocolid) ||
344          (event->xclient.message_type == spec->immoredataid))) {
345         /* This event has nothing to do with us,
346          * FIXME should not have gotten here then...
347          */
348         return False;
349    } else if ((event->type == ClientMessage) && (event->xclient.format == 8)) {
350        data = event->xclient.data.b;
351	if (buf_len >= XIM_CM_DATA_SIZE) {
352	    (void)memcpy(buf, data, XIM_CM_DATA_SIZE);
353	    *ret_len = XIM_CM_DATA_SIZE;
354	} else {
355	    (void)memcpy(buf, data, buf_len);
356	    len = XIM_CM_DATA_SIZE - buf_len;
357	    (void)memcpy(tmp_buf, &data[buf_len], len);
358	    bzero(data, XIM_CM_DATA_SIZE);
359	    (void)memcpy(data, tmp_buf, len);
360	    XPutBackEvent(im->core.display, event);
361	    *ret_len = buf_len;
362	}
363    } else if ((event->type == ClientMessage)
364				&& (event->xclient.format == 32)) {
365	length = (unsigned long)event->xclient.data.l[0];
366	prop   = (Atom)event->xclient.data.l[1];
367	return_code = XGetWindowProperty(im->core.display,
368		spec->lib_connect_wid, prop, 0L,
369		(long)((length + 3)/ 4), True, AnyPropertyType,
370		&type_ret, &format_ret, &nitems, &bytes_after_ret, &prop_ret);
371	if (return_code != Success || format_ret == 0 || nitems == 0) {
372	    if (return_code == Success)
373		XFree(prop_ret);
374	    return False;
375	}
376	if (buf_len >= (int)nitems) {
377	    (void)memcpy(buf, prop_ret, (int)nitems);
378	    *ret_len  = (int)nitems;
379	    if (bytes_after_ret > 0) {
380		XFree(prop_ret);
381		if (XGetWindowProperty(im->core.display,
382				       spec->lib_connect_wid, prop, 0L,
383				       ((length + bytes_after_ret + 3)/ 4),
384				       True, AnyPropertyType,
385				       &type_ret, &format_ret, &nitems,
386				       &bytes_after_ret,
387				       &prop_ret) == Success) {
388		    XChangeProperty(im->core.display, spec->lib_connect_wid, prop,
389				    XA_STRING, 8, PropModePrepend, &prop_ret[length],
390				    (nitems - length));
391		} else {
392		    return False;
393		}
394	    }
395	} else {
396	    (void)memcpy(buf, prop_ret, buf_len);
397	    *ret_len  = buf_len;
398	    len = nitems - buf_len;
399
400	    if (bytes_after_ret > 0) {
401		XFree(prop_ret);
402		if (XGetWindowProperty(im->core.display,
403				       spec->lib_connect_wid, prop, 0L,
404				       ((length + bytes_after_ret + 3)/ 4),
405				       True, AnyPropertyType,
406				       &type_ret, &format_ret, &nitems,
407				       &bytes_after_ret, &prop_ret) != Success) {
408		    return False;
409		}
410	    }
411	    XChangeProperty(im->core.display, spec->lib_connect_wid, prop,
412		    XA_STRING, 8, PropModePrepend, &prop_ret[buf_len], len);
413	    event->xclient.data.l[0] = (long)len;
414	    event->xclient.data.l[1] = (long)prop;
415	    XPutBackEvent(im->core.display, event);
416	}
417	XFree(prop_ret);
418    } else if (event->type == PropertyNotify) {
419	prop = event->xproperty.atom;
420	return_code = XGetWindowProperty(im->core.display,
421		spec->lib_connect_wid, prop, 0L,
422		1000000L, True, AnyPropertyType,
423		&type_ret, &format_ret, &nitems, &bytes_after_ret, &prop_ret);
424	if (return_code != Success || format_ret == 0 || nitems == 0) {
425	    if (return_code == Success)
426		XFree(prop_ret);
427	    return False;
428	}
429	if (buf_len >= nitems) {
430	    (void)memcpy(buf, prop_ret, (int)nitems);
431	    *ret_len  = (int)nitems;
432	} else {
433	    (void)memcpy(buf, prop_ret, buf_len);
434	    *ret_len  = buf_len;
435	    len = nitems - buf_len;
436	    XChangeProperty(im->core.display, spec->lib_connect_wid, prop,
437		XA_STRING, 8, PropModePrepend, &prop_ret[buf_len], len);
438	}
439	XFree(prop_ret);
440    }
441    return True;
442}
443
444static Bool
445_CheckCMEvent(
446    Display	*display,
447    XEvent	*event,
448    XPointer	 xim)
449{
450    Xim		 im = (Xim)xim;
451    XSpecRec	*spec = (XSpecRec *)im->private.proto.spec;
452    CARD32	 major_code = spec->major_code;
453
454    if ((event->type == ClientMessage)
455     &&((event->xclient.message_type == spec->improtocolid) ||
456        (event->xclient.message_type == spec->immoredataid)))
457	return True;
458    if((major_code == 1 || major_code == 2) &&
459       (event->type == PropertyNotify) &&
460       (event->xproperty.state == PropertyNewValue))
461	return True;
462    return False;
463}
464
465static Bool
466_XimXRead(Xim im, XPointer recv_buf, int buf_len, int *ret_len)
467{
468    XEvent	*ev;
469    XEvent	 event;
470    int		 len = 0;
471    XSpecRec	*spec = (XSpecRec *)im->private.proto.spec;
472    XPointer	  arg = spec->ev;
473
474    if (!arg) {
475	bzero(&event, sizeof(XEvent));
476	ev = &event;
477	XIfEvent(im->core.display, ev, _CheckCMEvent, (XPointer)im);
478    } else {
479	ev = (XEvent *)arg;
480	spec->ev = (XPointer)NULL;
481    }
482    if (!(_XimXGetReadData(im, recv_buf, buf_len, &len, ev)))
483	return False;
484    *ret_len = len;
485    return True;
486}
487
488static void
489_XimXFlush(Xim im)
490{
491    XFlush(im->core.display);
492    return;
493}
494
495Bool
496_XimXConf(Xim im, char *address)
497{
498    XSpecRec	*spec;
499
500    if (!(spec = Xcalloc(1, sizeof(XSpecRec))))
501	return False;
502
503    spec->improtocolid = XInternAtom(im->core.display, _XIM_PROTOCOL, False);
504    spec->imconnectid  = XInternAtom(im->core.display, _XIM_XCONNECT, False);
505    spec->immoredataid = XInternAtom(im->core.display, _XIM_MOREDATA, False);
506    spec->major_code = MAJOR_TRANSPORT_VERSION;
507    spec->minor_code = MINOR_TRANSPORT_VERSION;
508
509    im->private.proto.spec     = (XPointer)spec;
510    im->private.proto.connect  = _XimXConnect;
511    im->private.proto.shutdown = _XimXShutdown;
512    im->private.proto.write    = _XimXWrite;
513    im->private.proto.read     = _XimXRead;
514    im->private.proto.flush    = _XimXFlush;
515    im->private.proto.register_dispatcher  = _XimXRegisterDispatcher;
516    im->private.proto.call_dispatcher = _XimXCallDispatcher;
517
518    return True;
519}
520