1 /*
2 
3   Copyright (c) 2003-2013 uim Project https://github.com/uim/uim
4 
5   All rights reserved.
6 
7   Redistribution and use in source and binary forms, with or without
8   modification, are permitted provided that the following conditions
9   are met:
10 
11   1. Redistributions of source code must retain the above copyright
12      notice, this list of conditions and the following disclaimer.
13   2. Redistributions in binary form must reproduce the above copyright
14      notice, this list of conditions and the following disclaimer in the
15      documentation and/or other materials provided with the distribution.
16   3. Neither the name of authors nor the names of its contributors
17      may be used to endorse or promote products derived from this software
18      without specific prior written permission.
19 
20   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
21   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
24   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30   SUCH DAMAGE.
31 */
32 
33 // Handle IM procedure defined in XIM protocol
34 
35 #ifdef HAVE_CONFIG_H
36 # include <config.h>
37 #endif
38 
39 #include <cstdio>
40 #include <cstdlib>
41 #include <cstring>
42 #include <map>
43 
44 #include "xim.h"
45 #include <X11/Xutil.h>
46 #define NEED_EVENTS	// for declaration of xEvent
47 #include <X11/Xproto.h>
48 
49 #ifdef HAVE_ALLOCA_H
50 # include <alloca.h>
51 #endif
52 
53 static std::map<C16, XimIM *> g_ims;
54 
55 // tables
56 static input_style input_style_tab_with_over_the_spot[] = {
57     {XIMPreeditNothing|XIMStatusNothing, IS_ROOT_WINDOW},
58     //{XIMPreeditPosition|XIMStatusArea, IS_OVER_THE_SPOT},// emacs
59     {XIMPreeditPosition|XIMStatusNothing, IS_OVER_THE_SPOT},
60     //{XIMPreeditCallbacks|XIMStatusCallbacks, IS_ON_THE_SPOT},// OOo
61     //{XIMPreeditArea|XIMStatusArea, IS_ROOT_WINDOW},
62     {XIMPreeditCallbacks|XIMStatusNothing, IS_ON_THE_SPOT},
63     {0, 0},
64 };
65 static input_style input_style_tab_without_over_the_spot[] = {
66     {XIMPreeditNothing|XIMStatusNothing, IS_ROOT_WINDOW},
67     //{XIMPreeditPosition|XIMStatusArea, IS_OVER_THE_SPOT},// emacs
68     //{XIMPreeditPosition|XIMStatusNothing, IS_OVER_THE_SPOT},
69     //{XIMPreeditCallbacks|XIMStatusCallbacks, IS_ON_THE_SPOT},// OOo
70     //{XIMPreeditArea|XIMStatusArea, IS_ROOT_WINDOW},
71     {XIMPreeditCallbacks|XIMStatusNothing, IS_ON_THE_SPOT},
72     {0, 0},
73 };
74 // XIMPreeditArea,XIMPreeditCallbacks,XIMPreeditPosition
75 // XIMPreeditNothing,XIMPreeditNone
76 // XIMStatusArea,XIMStatusCallbacks
77 // XIMStatusNothing,XIMStatusNone
78 
79 
80 class XimIM_impl : public XimIM {
81 public:
82     XimIM_impl(Connection *c, C16 id);
83     virtual ~XimIM_impl();
84     virtual void create_ic(RxPacket *);
85     virtual void destroy_ic(C16);
86     virtual void set_ic_focus(C16 icid);
87     virtual void set_ic_values(RxPacket *);
88     virtual void get_ic_values(RxPacket *);
89     virtual void unset_ic_focus(C16 icid);
90     virtual void forward_event(RxPacket *);
91     virtual void send_sync_reply(C16 icid);
92     virtual void send_sync(C16 icid);
93     virtual XimIC *get_ic_by_id(C16 id);
94     virtual void onSendPacket();
95     virtual void changeContext(const char *);
96 
97 private:
98     C16 unused_ic_id();
99     void free_all_ic();
100     void delete_ic(XimIC *);
101     char *mEngineName;
102     std::map<C16, XimIC *> m_ics;
103 };
104 
XimIM_impl(Connection * c,C16 id)105 XimIM_impl::XimIM_impl(Connection *c, C16 id) : XimIM(c, id)
106 {
107     mEngineName = strdup(mConn->getXimServer()->getIMName());
108 }
109 
~XimIM_impl()110 XimIM_impl::~XimIM_impl()
111 {
112     free_all_ic();
113     free(mEngineName);
114 }
115 
create_ic(RxPacket * p)116 void XimIM_impl::create_ic(RxPacket *p)
117 {
118     XimIC *ic;
119     C16 icid= unused_ic_id();
120 
121     // create compose table with the first ic
122     if (icid == 1)
123 	create_compose_tree();
124 
125     ic = ::create_ic(mConn, p, mID, icid, mEngineName);
126     if (!ic) {
127 	mConn->push_error_packet(mID, icid,
128 				 ERR_Style, "invalid im style");
129 	return;
130     }
131     std::pair<C16, XimIC *> n(ic->get_icid(), ic);
132     m_ics.insert(n);
133 
134     TxPacket *t;
135     t = createTxPacket(XIM_CREATE_IC_REPLY, 0);
136     t->pushC16(mID);
137     t->pushC16(ic->get_icid());
138     mConn->push_packet(t);
139 }
140 
destroy_ic(C16 icid)141 void XimIM_impl::destroy_ic(C16 icid)
142 {
143     TxPacket *t;
144     t = createTxPacket(XIM_DESTROY_IC_REPLY, 0);
145     t->pushC16(mID);
146     t->pushC16(icid);
147     mConn->push_packet(t);
148     // destruct IC
149     XimIC *ic;
150     ic = get_ic_by_id(icid);
151     delete_ic(ic);
152 }
153 
changeContext(const char * engine)154 void XimIM_impl::changeContext(const char *engine)
155 {
156     std::map<C16, XimIC *>::iterator i;
157     for (i = m_ics.begin(); i != m_ics.end(); ++i) {
158 	(*i).second->changeContext(engine);
159     }
160     free(mEngineName);
161     mEngineName = strdup(engine);
162 }
163 
set_ic_values(RxPacket * p)164 void XimIM_impl::set_ic_values(RxPacket *p)
165 {
166     C16 imid, icid;
167     XimIC *ic;
168     p->rewind();
169     imid = p->getC16();
170     icid = p->getC16();
171     ic = get_ic_by_id(icid);
172 
173     int atr_len;
174     atr_len = p->getC16();
175     p->getC16();
176 
177     unsigned char *v;
178     v = (unsigned char *)alloca(atr_len);
179 
180     int i;
181     for (i = 0; i < atr_len; i++) {
182 	v[i] = p->getC8();
183     }
184 
185     ic->setICAttrs((void *)v, atr_len);
186 
187     TxPacket *t = createTxPacket(XIM_SET_IC_VALUES_REPLY, 0);
188     t->pushC16(imid);
189     t->pushC16(icid);
190     mConn->push_packet(t);
191 }
192 
get_ic_values(RxPacket * p)193 void XimIM_impl::get_ic_values(RxPacket *p)
194 {
195     C16 icid;
196     XimIC *ic;
197     p->rewind();
198     p->getC16();
199     icid = p->getC16();
200     ic = get_ic_by_id(icid);
201 
202     int len;
203     len = p->getC16();
204 
205     TxPacket *t = createTxPacket(XIM_GET_IC_VALUES_REPLY, 0);
206     t->pushC16(mID);
207     t->pushC16(icid);
208     int i, l;
209     C16 id;
210     l = 0;
211     for (i = 0; i < len / 2; i++) {
212 	id = p->getC16();
213 	l += ic->get_ic_atr(id, 0);
214 	l += 4;
215     }
216     t->pushC16((C16)l);
217     t->pushC16(0);
218 
219     p->rewind();
220     p->getC16(); // imid
221     p->getC16(); // icid
222     p->getC16(); // n
223 
224     for (i = 0; i < len / 2; i++) {
225 	id = p->getC16();
226 	t->pushC16(id);
227 	t->pushC16(ic->get_ic_atr(id, 0));
228 	l += ic->get_ic_atr(id, t);
229     }
230     mConn->push_packet(t);
231 }
232 
unused_ic_id()233 C16 XimIM_impl::unused_ic_id()
234 {
235     std::map<C16, XimIC *>::iterator i;
236     C16 max_id = 1; // Does ID of input-context start with 1?
237     for (i = m_ics.begin(); i != m_ics.end(); ++i) {
238 	if (max_id <= (*i).first)
239 	    max_id = (C16)((*i).first + 1);
240     }
241     return max_id;
242 }
243 
set_ic_focus(C16 icid)244 void XimIM_impl::set_ic_focus(C16 icid)
245 {
246     XimIC *ic = get_ic_by_id(icid);
247     if (ic)
248 	ic->setFocus();
249 }
250 
unset_ic_focus(C16 icid)251 void XimIM_impl::unset_ic_focus(C16 icid)
252 {
253     XimIC *ic = get_ic_by_id(icid);
254     if (ic)
255 	ic->unsetFocus();
256 }
257 
get_ic_by_id(C16 icid)258 XimIC *XimIM_impl::get_ic_by_id(C16 icid)
259 {
260     std::map<C16, XimIC *>::iterator it;
261     it = m_ics.find(icid);
262     if (it == m_ics.end())
263 	return 0;
264 
265     return it->second;
266 }
267 
forward_event(RxPacket * p)268 void XimIM_impl::forward_event(RxPacket *p)
269 {
270     unsigned char *c;
271     int i;
272     keyEventX k;
273     xEvent ev_raw;
274 
275     C16 imid, icid;
276     int flag;
277 
278     XimIC *ic;
279     imid = p->getC16();
280     icid = p->getC16();
281     flag = p->getC16();
282     ic = get_ic_by_id(icid);
283     k.serial = p->getC16();
284 
285     // keep copy of the xEvent
286     c = (unsigned char *)&ev_raw;
287     for (i = 0; i < (int)sizeof(ev_raw); i++) {
288 	*c = p->getC8();
289 	c++;
290     }
291 
292     k.ev.type = ev_raw.u.u.type & 0x7f;
293     k.ev.xany.serial = (k.serial << 16) | mConn->to_hs(ev_raw.u.u.sequenceNumber);
294     k.ev.xany.display = XimServer::gDpy;
295     k.ev.xany.send_event = ev_raw.u.u.type > 127;
296 
297     switch (k.ev.type) {
298     case KeyPress:
299     case KeyRelease:
300 	k.ev.xkey.window =
301 	    mConn->to_hl(ev_raw.u.keyButtonPointer.event);
302 	k.ev.xkey.root =
303 	    mConn->to_hl(ev_raw.u.keyButtonPointer.root);
304 	k.ev.xkey.subwindow =
305 	    mConn->to_hl(ev_raw.u.keyButtonPointer.child);
306 	k.ev.xkey.time =
307 	    mConn->to_hl(ev_raw.u.keyButtonPointer.time);
308 	k.ev.xkey.x =
309 	    mConn->to_hs(ev_raw.u.keyButtonPointer.eventX);
310 	k.ev.xkey.y =
311 	    mConn->to_hs(ev_raw.u.keyButtonPointer.eventY);
312 	k.ev.xkey.x_root =
313 	    mConn->to_hs(ev_raw.u.keyButtonPointer.rootX);
314 	k.ev.xkey.y_root =
315 	    mConn->to_hs(ev_raw.u.keyButtonPointer.rootY);
316 	k.ev.xkey.state = mConn->to_hs(ev_raw.u.keyButtonPointer.state);
317 	k.ev.xkey.keycode = ev_raw.u.u.detail;
318 	k.ev.xkey.same_screen = ev_raw.u.keyButtonPointer.sameScreen;
319 	char  buf[10];
320 	KeySym ks;
321 	XLookupString(&k.ev.xkey, buf, 10, &ks, 0);
322 	k.state = mConn->to_hs(ev_raw.u.keyButtonPointer.state);
323 	k.press = (k.ev.type == KeyPress);
324 	k.key_sym = ks;
325 
326 	if (ic) {
327 	    InputContext *focusedContext = InputContext::focusedContext();
328 	    if (!focusedContext)
329 		ic->setFocus(); // workaround for some buggy applications
330 	    ic->OnKeyEvent(k);
331 	}
332 	if (!(g_option_mask & OPT_ON_DEMAND_SYNC))
333 	    send_sync_reply(icid);
334 	break;
335     default:
336 	printf("unknown type of forwarded event.(%d)\n", k.ev.type);
337 	if (!(g_option_mask & OPT_ON_DEMAND_SYNC))
338 	    send_sync_reply(icid);
339 	break;
340     }
341 }
342 
free_all_ic()343 void XimIM_impl::free_all_ic()
344 {
345     std::map<C16, XimIC *>::iterator i;
346     for (i = m_ics.begin(); i != m_ics.end(); ++i) {
347 	(*i).second->unsetFocus();
348 	delete (*i).second;
349     }
350     m_ics.erase(m_ics.begin(), m_ics.end());
351 }
352 
delete_ic(XimIC * ic)353 void XimIM_impl::delete_ic(XimIC *ic)
354 {
355     std::map<C16, XimIC *>::iterator it;
356     for (it = m_ics.begin(); it != m_ics.end(); ++it) {
357 	if (it->second == ic) {
358 	    it->second->unsetFocus();
359 	    delete it->second;
360 	    m_ics.erase(it);
361 	    return;
362 	}
363     }
364 }
365 
send_sync_reply(C16 icid)366 void XimIM_impl::send_sync_reply(C16 icid)
367 {
368     TxPacket *t = createTxPacket(XIM_SYNC_REPLY, 0);
369     t->pushC16(mID);
370     t->pushC16(icid);
371     mConn->push_packet(t);
372 }
373 
send_sync(C16 icid)374 void XimIM_impl::send_sync(C16 icid)
375 {
376     TxPacket *t = createTxPacket(XIM_SYNC, 0);
377     t->pushC16(mID);
378     t->pushC16(icid);
379     mConn->push_packet(t);
380 }
381 
onSendPacket()382 void XimIM_impl::onSendPacket()
383 {
384     std::map<C16, XimIC *>::iterator i;
385     for (i = m_ics.begin(); i != m_ics.end(); ++i) {
386 	(*i).second->onSendPacket();
387     }
388 }
389 
XimIM(Connection * c,C16 id)390 XimIM::XimIM(Connection *c, C16 id)
391 {
392     mConn = c;
393     mID = id;
394     mEncoding = NULL;
395     mLangRegion = NULL;
396     mTreeTop = NULL;
397     mLocale = NULL;
398 }
399 
~XimIM()400 XimIM::~XimIM()
401 {
402     free(mEncoding);
403     free(mLangRegion);
404     FreeComposeTree(mTreeTop);
405     delete mLocale;
406 }
407 
FreeComposeTree(DefTree * top)408 void XimIM::FreeComposeTree(DefTree *top)
409 {
410    if (!top)
411 	return;
412 
413    if (top->succession)
414 	FreeComposeTree(top->succession);
415    if (top->next)
416 	FreeComposeTree(top->next);
417    free(top->mb);
418    free(top->utf8);
419    free(top);
420 }
421 
set_encoding(const char * encoding)422 void XimIM::set_encoding(const char *encoding)
423 {
424     free(mEncoding);
425     mEncoding = strdup(encoding);
426 
427     // set iconv environment
428     if (mLocale)
429 	delete mLocale;
430     // workaround for Solaris 10 (bug #7558)
431     char *p;
432     if (!strcasecmp(encoding, "EUC") && mLangRegion &&
433 	(p = strchr(mLangRegion, '_'))) {
434 	char *iconv_encoding = (char *)malloc(3 + strlen(p + 1) + 1);
435 	if (iconv_encoding) {
436 	    sprintf(iconv_encoding, "euc%s", p + 1);
437 	    mLocale = createLocale(iconv_encoding);
438 	    free(iconv_encoding);
439 	} else {
440 	    mLocale = createLocale(mEncoding);
441 	}
442     } else {
443 	mLocale = createLocale(mEncoding);
444     }
445 }
446 
get_encoding()447 const char *XimIM::get_encoding()
448 {
449     return mEncoding;
450 }
451 
set_lang_region(const char * lang_and_region)452 void XimIM::set_lang_region(const char *lang_and_region)
453 {
454     free(mLangRegion);
455     mLangRegion = strdup(lang_and_region);
456 }
457 
get_lang_region()458 const char *XimIM::get_lang_region()
459 {
460     return mLangRegion;
461 }
462 
getInputStyles()463 struct input_style *XimIM::getInputStyles()
464 {
465     if (mLocale && mLocale->supportOverTheSpot())
466 	return input_style_tab_with_over_the_spot;
467 
468     return input_style_tab_without_over_the_spot;
469 }
470 
uStringToCtext(uString * us)471 char *XimIM::uStringToCtext(uString *us)
472 {
473     char *ret = NULL;
474     if (mLocale)
475 	ret = mLocale->uStringToCtext(us);
476 
477     return ret;
478 }
479 
utf8_to_native_str(char * str)480 char *XimIM::utf8_to_native_str(char *str)
481 {
482     char *ret = NULL;
483     if (mLocale)
484 	ret = mLocale->utf8_to_native_str(str);
485 
486     return ret;
487 }
488 
unused_im_id()489 C16 unused_im_id()
490 {
491     C16 max_id;
492     std::map<C16, XimIM *>::iterator i;
493     max_id = 1;
494     for (i = g_ims.begin(); i != g_ims.end(); ++i) {
495 	if ((*i).first == max_id)
496 	    max_id = (C16)((*i).first + 1);
497     }
498     return max_id;
499 }
500 
create_im(Connection * c,C16 id)501 XimIM *create_im(Connection *c, C16 id)
502 {
503     XimIM *im;
504     im = new XimIM_impl(c, id);
505     std::pair<C16, XimIM *> p(id, im);
506     g_ims.insert(p);
507     return im;
508 }
509 
get_im_by_id(C16 id)510 XimIM *get_im_by_id(C16 id)
511 {
512     std::map<C16, XimIM *>::iterator it;
513     it = g_ims.find(id);
514     if (it == g_ims.end())
515 	return NULL;
516 
517     return it->second;
518 }
519 
close_im(C16 id)520 void close_im(C16 id)
521 {
522     XimIM *im;
523 
524     im = get_im_by_id(id);
525     if (im)
526 	delete im;
527 
528     std::map<C16, XimIM *>::iterator it;
529     it = g_ims.find(id);
530     if (it != g_ims.end())
531 	g_ims.erase(it);
532 }
533 /*
534  * Local variables:
535  *  c-indent-level: 4
536  *  c-basic-offset: 4
537  * End:
538  */
539