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 IC (Input Context) part of XIM protocol.
34 // Intermediate key handling between backend IM engine.
35 
36 #ifdef HAVE_CONFIG_H
37 # include <config.h>
38 #endif
39 
40 #include <cctype>
41 #include <cstdio>
42 #include <cstdlib>
43 #include <cstring>
44 
45 #include "xim.h"
46 #include "convdisp.h"
47 #include "ximserver.h"
48 #include "connection.h"
49 #include "util.h"
50 
51 #include "uim/uim-scm.h"
52 
53 #ifdef HAVE_ALLOCA_H
54 # include <alloca.h>
55 #endif
56 
57 char invalid_style_msg[]=
58 "Client requested unsupported input style";
59 
60 XimIC *XimIC::current_ic;
61 int XimIC::nrActiveIC;
62 
63 struct FSCache {
64     int refc;
65     XFontSet fs;
66     char *name;
67     char *locale;
68 };
69 
70 static std::list<FSCache> fs_cache;
71 
72 XFontSet
get_font_set(const char * name,const char * locale)73 get_font_set(const char *name, const char *locale)
74 {
75     std::list<FSCache>::iterator it;
76     for (it = fs_cache.begin(); it != fs_cache.end(); ++it) {
77 	if (!strcmp(it->name, name) && !strcmp(it->locale, locale)) {
78 	    it->refc++;
79 	    return it->fs;
80 	}
81     }
82     struct FSCache fc;
83     char **missing, *def;
84     int nr_missing;
85     fc.fs = XCreateFontSet(XimServer::gDpy, name,
86 			   &missing, &nr_missing, &def);
87     if (missing)
88 	XFreeStringList(missing);
89 
90     if (fc.fs == NULL) {
91 	fprintf(stderr, "Critical: XCreateFontSet failed!\n");
92 	return NULL;
93     }
94 
95     fc.refc = 1;
96     fc.name = strdup(name);
97     fc.locale = strdup(locale);
98     fs_cache.push_back(fc);
99     return fc.fs;
100 }
101 
102 static void
release_font_set(const char * name,const char * locale)103 release_font_set(const char *name, const char *locale)
104 {
105     std::list<FSCache>::iterator it;
106     for (it = fs_cache.begin(); it != fs_cache.end(); ++it) {
107 	if (!strcmp(it->name, name) && !strcmp(it->locale, locale)) {
108 	    it->refc--;
109 	    if (!it->refc) {
110 		XFreeFontSet(XimServer::gDpy, it->fs);
111 		free(it->name);
112 		free(it->locale);
113 		fs_cache.erase(it);
114 	    }
115 	    return;
116 	}
117     }
118 }
119 
pe_stat(InputContext * c)120 pe_stat::pe_stat(InputContext *c)
121 {
122     cont = c;
123     clear();
124 }
125 
clear()126 void pe_stat::clear()
127 {
128     ustrings.erase(ustrings.begin(), ustrings.end());
129     caret_pos = 0;
130 }
131 
new_segment(int s)132 void pe_stat::new_segment(int s)
133 {
134     pe_ustring p;
135     p.stat = s;
136     ustrings.push_back(p);
137 }
138 
push_uchar(uchar c)139 void pe_stat::push_uchar(uchar c)
140 {
141     std::list<pe_ustring>::reverse_iterator i = ustrings.rbegin();
142     (*i).s.push_back(c);
143 }
144 
get_char_count()145 int pe_stat::get_char_count()
146 {
147     std::list<pe_ustring>::iterator i;
148     uString::iterator j;
149     int k = 0;
150     for (i = ustrings.begin(); i != ustrings.end(); ++i) {
151 	for (j = (*i).s.begin(); j != (*i).s.end(); ++j) {
152 	    k++;
153 	}
154     }
155     return k;
156 }
157 
icxatr()158 icxatr::icxatr()
159 {
160     atr_mask = 0;
161     change_mask = 0;
162     font_set_name = NULL;
163     font_set = NULL;
164     m_locale = NULL;
165     foreground_pixel = static_cast<C32>(BlackPixel(XimServer::gDpy,
166 			                DefaultScreen(XimServer::gDpy)));
167     background_pixel = static_cast<C32>(WhitePixel(XimServer::gDpy,
168 			                DefaultScreen(XimServer::gDpy)));
169     line_space = 0;
170 #if HAVE_XFT_UTF8_STRING
171     m_use_xft = uim_scm_symbol_value_bool("uim-xim-use-xft-font?");
172 #else
173     m_use_xft = false;
174 #endif
175 }
176 
~icxatr()177 icxatr::~icxatr()
178 {
179     if (font_set_name) {
180 	if (use_xft() == false)
181 	    release_font_set(font_set_name, m_locale);
182 	free((void *)font_set_name);
183     }
184     free(m_locale);
185 }
186 
has_atr(C16 id)187 bool icxatr::has_atr(C16 id)
188 {
189     return atr_mask & (1 << id);
190 }
191 
set_atr(C16 id,C8 * val,int o)192 void icxatr::set_atr(C16 id, C8 *val, int o)
193 {
194     switch (id) {
195     case ICA_InputStyle:
196 	input_style = readC32(val, o);
197 	break;
198     case ICA_ClientWindow:
199 	client_window = readC32(val, o);
200 	break;
201     case ICA_FocusWindow:
202 	focus_window = readC32(val, o);
203 	break;
204     case ICA_Foreground:
205 	foreground_pixel = readC32(val, o);
206 	break;
207     case ICA_Background:
208 	background_pixel = readC32(val, o);
209 	break;
210     case ICA_SpotLocation:
211 	spot_location.x = readC16(val, o);
212 	spot_location.y = readC16(&val[2], o);
213 	break;
214     case ICA_FontSet:
215     {
216 	int len = readC16(val, o);
217 	char *new_fsn = (char *)alloca(len + 1);
218 	new_fsn[len] = '\0';
219 	memcpy(new_fsn, &val[2], len);
220 	if (font_set_name && !strcmp(font_set_name, new_fsn))
221 	    break;
222 
223 	free(font_set_name);
224 	font_set_name = strdup(new_fsn);
225 	if (use_xft() == false) {
226 	    font_set = get_font_set(font_set_name, m_locale);
227 	    if (!font_set) {
228 		free(font_set_name);
229 		font_set_name = NULL;
230 		return; // Don't set atr mask
231 	    }
232 	}
233     }
234     break;
235     case ICA_Area:
236     {
237 	area.x = readC16(&val[0], o);
238 	area.y = readC16(&val[2], o);
239 	area.width = readC16(&val[4], o);
240 	area.height = readC16(&val[6], o);
241     }
242     break;
243     case ICA_LineSpace:
244 	line_space = readC16(val, o);
245 	break;
246     default:
247 	// unknown attribute
248 	printf("try to set unknown ic attribute %d.\n", id);
249 	return;
250     }
251     atr_mask |= (1 << id);
252     change_mask |= (1 << id);
253 }
254 
is_changed(C16 id)255 bool icxatr::is_changed(C16 id)
256 {
257     if (change_mask & (1 << id))
258 	return true;
259 
260     return false;
261 }
262 
unset_change_mask(C16 id)263 void icxatr::unset_change_mask(C16 id)
264 {
265     change_mask &= (~(1 << id));
266 }
267 
print()268 void icxatr::print()
269 {
270     if (has_atr(ICA_InputStyle))
271 	printf("input-style %ld.\n", input_style);
272     else
273 	printf("input-style undefined.\n");
274 
275     if (has_atr(ICA_ClientWindow))
276 	printf("client-window id %ld.\n", client_window);
277     else
278 	printf("client-window id undefined.\n");
279 
280     if (has_atr(ICA_FocusWindow))
281 	printf("focus-window id %ld.\n", focus_window);
282     else
283 	printf("focus-window id undefined.\n");
284 
285     if (has_atr(ICA_Foreground))
286 	printf("foreground-pixel %d.\n", foreground_pixel);
287     else
288 	printf("foreground-pixel undefined.\n");
289 
290     if (has_atr(ICA_Background))
291 	printf("background-pixel %d.\n", background_pixel);
292     else
293 	printf("background-pixel undefined.\n");
294 
295     if (has_atr(ICA_SpotLocation))
296 	printf("spot location x=%d,y=%d.\n",
297 	       spot_location.x, spot_location.y);
298     else
299 	printf("spot location undefined.\n");
300 
301     if (has_atr(ICA_FontSet))
302 	printf("font-set-name %s\n", font_set_name);
303     else
304 	printf("font-set-name undefined.\n");
305 
306     if (has_atr(ICA_Area))
307 	printf("area = x=%d y=%d width=%d height=%d.\n",
308 	       area.x, area.y, area.width, area.height);
309     else
310 	printf("area undefined.\n");
311 
312     if (has_atr(ICA_LineSpace))
313 	printf("line-space %d.\n", line_space);
314     else
315 	printf("line-space undefined.\n");
316 
317 }
318 
getSize(C16 id)319 C16 icxatr::getSize(C16 id)
320 {
321     switch (id) {
322     case ICA_FocusWindow:
323 	return 4;
324     case ICA_FilterEvents:
325 	return 4;
326     case ICA_InputStyle:
327 	return 4;
328     }
329     return 0;
330 }
331 
set_locale_name(const char * locale)332 void icxatr::set_locale_name(const char *locale)
333 {
334     free(m_locale);
335     m_locale = strdup(locale);
336 }
337 
use_xft()338 bool icxatr::use_xft() {
339     return m_use_xft;
340 }
341 
XimIC(Connection * c,C16 imid,C16 icid,const char * engine)342 XimIC::XimIC(Connection *c, C16 imid, C16 icid, const char *engine)
343 {
344     mConn = c;
345     mIMid = imid;
346     mICid = icid;
347     mIsActive = false;
348 
349     XimServer *svr = mConn->getXimServer();
350     m_kkContext = svr->createContext(this, engine);
351 
352     const char *locale = m_kkContext->get_locale_name();
353     m_xatr.set_locale_name(locale);
354 
355     mConvdisp = NULL;
356     m_keyState = new keyState(this);
357     if (g_option_mask & OPT_TRACE)
358 	printf("imid=%d, icid=%d ic created.\n", mIMid, mICid);
359 }
360 
~XimIC()361 XimIC::~XimIC()
362 {
363     if (g_option_mask & OPT_TRACE)
364 	printf("imid=%d, icid=%d ic deleted.\n", mIMid , mICid);
365 
366     unsetFocus();
367     if (current_ic == this)
368 	current_ic = 0;
369 
370     // The sequence is important.
371     delete m_kkContext;
372     if (mConvdisp)
373 	delete mConvdisp;
374     delete m_keyState;
375 }
376 
isActive()377 bool XimIC::isActive()
378 {
379     return mIsActive;
380 }
381 
get_icid()382 C16 XimIC::get_icid()
383 {
384     return mICid;
385 }
386 
get_imid()387 C16 XimIC::get_imid()
388 {
389     return mIMid;
390 }
391 
setFocus()392 void XimIC::setFocus()
393 {
394     if (!mIsActive)
395 	nrActiveIC++;
396 
397     current_ic = this;
398     mIsActive = true;
399     m_kkContext->focusIn();
400 }
401 
402 // Note that the sequence of XIM_SET_IC_FOCUS and XIM_UNSET_FOCUS
403 // events is not consistent.  Be careful about hiding caret state and
404 // candidate window.
unsetFocus()405 void XimIC::unsetFocus()
406 {
407     if (!mIsActive)
408 	return;
409 
410     mIsActive = false;
411     nrActiveIC--;
412     m_kkContext->focusOut();
413 }
414 
OnKeyEvent(keyEventX e)415 void XimIC::OnKeyEvent(keyEventX e)
416 {
417     int s;
418     m_keyState->check_key(&e);
419 
420     s = m_kkContext->pushKey(m_keyState);
421     if (s & COMMIT_RAW)
422 	send_key_event(&e.ev.xkey);
423 
424     if (s & UPDATE_MODE)
425 	onSendPacket(); // send XIM_COMMIT
426 }
427 
changeContext(const char * engine)428 void XimIC::changeContext(const char *engine)
429 {
430    m_kkContext->changeContext(engine);
431 }
432 
send_key_event(XKeyEvent * e)433 void XimIC::send_key_event(XKeyEvent *e)
434 {
435     TxPacket *t;
436     t = createTxPacket(XIM_FORWARD_EVENT, 0);
437     t->pushC16(mIMid);
438     t->pushC16(mICid);
439     t->pushC16(1); // flag, synchronous
440     t->pushC16((C16)((e->serial >> 16) & 0xffff));
441 
442     t->pushC8((C8)e->type);
443     t->pushC8((C8)e->keycode);
444     t->pushC16((C16)e->serial & 0xffff);
445     t->pushC32(static_cast<C32>(e->time));
446     t->pushC32(static_cast<C32>(e->root));
447     t->pushC32(static_cast<C32>(e->window));
448     t->pushC32(static_cast<C32>(e->subwindow));
449     t->pushC16((C16)e->x_root);
450     t->pushC16((C16)e->y_root);
451     t->pushC16((C16)e->x);
452     t->pushC16((C16)e->y);
453     t->pushC16((C16)e->state);
454     t->pushC8((C8)e->same_screen);
455     t->pushC8(0);
456     mConn->push_packet(t);
457 }
458 
commit_string(const char * str)459 void XimIC::commit_string(const char *str)
460 {
461     uString us;
462     mConn->getXimServer()->strToUstring(&us, str);
463     append_ustring(&mPending, &us);
464 }
465 
extra_input(char * s)466 void XimIC::extra_input(char *s)
467 {
468     if (s == NULL)
469 	return;
470 
471     commit_string(s);
472     onSendPacket();
473     send_sync();
474     force_send_packet();
475 }
476 
send_sync()477 void XimIC::send_sync() {
478 
479     XimIM *im = get_im_by_id(mIMid);
480     im->send_sync(mICid);
481 }
482 
force_send_packet(void)483 void XimIC::force_send_packet(void) {
484     (dynamic_cast<XConnection *>(mConn))->writeProc();
485 }
486 
setICAttrs(void * val,int len)487 void XimIC::setICAttrs(void *val, int len)
488 {
489     unsigned char *p = (unsigned char *)val;
490     int byte_order = mConn->byte_order();
491     int i;
492     for (i = 0; i < len;) {
493 	C16 atr_id, atr_len;
494 	atr_id = readC16(&p[i], byte_order);
495 	i += 2;
496 
497 	atr_len = readC16(&p[i], byte_order);
498 	i += 2;
499 
500 	unsigned char *q;
501 	q = (unsigned char *)alloca(atr_len + pad4(atr_len));
502 
503 	int j;
504 	for (j = 0; j < atr_len + pad4(atr_len); j++, i++) {
505 	    q[j] = p[i];
506 	}
507 
508 	set_ic_attr(atr_id, (C8 *)q, atr_len);
509     }
510 }
511 
get_ic_atr(C16 id,TxPacket * t)512 C16 XimIC::get_ic_atr(C16 id, TxPacket *t)
513 {
514     C16 l = m_xatr.getSize(id);
515     if (!t)
516 	return l;
517 
518     switch (id) {
519     case ICA_FocusWindow:
520 	t->pushC32(static_cast<C32>(m_xatr.focus_window));
521 	break;
522     case ICA_FilterEvents:
523 	if (g_option_mask & OPT_ON_DEMAND_SYNC)
524 	    t->pushC32(KeyPressMask|KeyReleaseMask);
525 	else // Filtering KeyRelease event with full-synchronous method
526 	     //	causes problem with mozilla (1.7.3) gtk2 on navigation
527 	     //	toolbar's auto text completion...
528 	    t->pushC32(KeyPressMask);
529 	break;
530     case ICA_InputStyle:
531 	t->pushC32(static_cast<C32>(m_xatr.input_style));
532   	break;
533     default:
534 	printf("try to get unknown ic attribute %d.\n", id);
535 	break;
536     }
537     return l;
538 }
539 
lookup_style(unsigned long s)540 int XimIC::lookup_style(unsigned long s)
541 {
542     int i;
543     struct input_style *is = get_im_by_id(mIMid)->getInputStyles();
544     for (i = 0; is[i].x_style; i++) {
545 	if (is[i].x_style == (int)s)
546 	    return is[i].style;
547     }
548     return IS_INVALID;
549 }
550 
set_ic_attr(C16 id,C8 * val,int len)551 void XimIC::set_ic_attr(C16 id, C8 *val, int len)
552 {
553     if (id == ICA_PreeditAttribute || id == ICA_StatusAttributes)
554 	setICAttrs(val, len); // list of attribute
555     else
556 	m_xatr.set_atr(id, val, mConn->byte_order());
557 
558     if (mConvdisp)
559 	mConvdisp->update_icxatr();
560     else {
561 	if (m_xatr.has_atr(ICA_InputStyle)) {
562 	    mConvdisp = create_convdisp(lookup_style(m_xatr.input_style),
563 				    m_kkContext, &m_xatr, mConn);
564 	    m_kkContext->setConvdisp(mConvdisp);
565 
566 	    if (mConvdisp)
567 		mConvdisp->update_icxatr();
568 	}
569     }
570 }
571 
reset_ic()572 void XimIC::reset_ic()
573 {
574     TxPacket *t;
575     t = createTxPacket(XIM_RESET_IC_REPLY, 0);
576     t->pushC16(mIMid);
577     t->pushC16(mICid);
578 
579     uString s;
580 
581     // m_kkContext->get_preedit_string() returns uncommitted preedit
582     // strings, which will be committed in client applications.
583     s = m_kkContext->get_preedit_string();
584     if (!s.empty()) {
585 	char *p;
586 	C16 i;
587 	int len = 0;
588 	p = get_im_by_id(mIMid)->uStringToCtext(&s);
589 	if (p) {
590 	    len = static_cast<int>(strlen(p));
591 	    t->pushC16((C16)len); // length of committed strings
592 	    for (i = 0; i < len; i++) {
593 		t->pushC8(p[i]); // put string here
594 	    }
595 	    len = pad4(len + 2);
596 	    for (i = 0; i < len; i++) {
597 		t->pushC8(0); // padding (len + 2bytes)
598 	    }
599 	    free(p);
600 	} else {
601 	    t->pushC16(0);
602 	    t->pushC16(0);
603 	}
604     } else {
605 	t->pushC16(0);
606 	t->pushC16(0);
607     }
608 
609     mConn->push_packet(t);
610 
611     // After sending RESET_IC_REPLY, uim-xim needs to clear and reset
612     // the input context.
613     m_kkContext->clear();
614 
615     // Also reset key state
616     m_keyState->reset();
617 }
618 
get_convdisp()619 Convdisp *XimIC::get_convdisp()
620 {
621     return mConvdisp;
622 }
623 
onSendPacket()624 void XimIC::onSendPacket()
625 {
626     if (mPending.empty())
627 	return;
628 
629     char *p;
630     p = get_im_by_id(mIMid)->uStringToCtext(&mPending);
631 
632     if (!p) {
633 	erase_ustring(&mPending);
634 	return;
635     }
636 
637     TxPacket *t;
638     t = createTxPacket(XIM_COMMIT, 0);
639     t->pushC16(mIMid);
640     t->pushC16(mICid);
641 
642     t->pushC16(3); // XLookupChars|synchronous
643 
644     int i, len;
645     len = static_cast<int>(strlen(p));
646 
647     t->pushC16((C16)len);
648     for (i = 0; i < len; i++) {
649 	t->pushC8(p[i]);
650     }
651     len = pad4(len);
652     for (i = 0; i < len; i++) {
653 	t->pushC8(0);
654     }
655     free(p);
656 
657     mConn->push_passive_packet(t);
658 
659     erase_ustring(&mPending);
660 }
661 
get_current_ic()662 XimIC *XimIC::get_current_ic()
663 {
664     return current_ic;
665 }
666 
isAnyActive()667 bool XimIC::isAnyActive()
668 {
669     if (nrActiveIC)
670 	return true;
671 
672     return false;
673 }
674 
get_encoding()675 const char *XimIC::get_encoding()
676 {
677     XimIM *im = get_im_by_id(mIMid);
678     return im->get_encoding();
679 }
680 
get_lang_region()681 const char *XimIC::get_lang_region()
682 {
683     XimIM *im = get_im_by_id(mIMid);
684     return im->get_lang_region();
685 }
686 
create_ic(Connection * c,RxPacket * p,C16 imid,C16 icid,const char * engine)687 XimIC *create_ic(Connection *c, RxPacket *p, C16 imid, C16 icid, const char *engine)
688 {
689     XimIC *ic;
690     ic = new XimIC(c, imid, icid, engine);
691     p->rewind();
692     p->getC16(); // discard
693     int atr_len = p->getC16();
694     unsigned char *v;
695     v = (unsigned char *)alloca(atr_len);
696     int i;
697     for (i = 0; i < atr_len; i++) {
698 	v[i] = p->getC8();
699     }
700     ic->setICAttrs((void *)v, atr_len);
701     if (!ic->get_convdisp()) {
702 	delete ic;
703 	return 0;
704     }
705     return ic;
706 }
707 /*
708  * Local variables:
709  *  c-indent-level: 4
710  *  c-basic-offset: 4
711  * End:
712  */
713