Tizen 2.1 base
[platform/core/uifw/ise-engine-sunpinyin.git] / wrapper / xim / xim.c
1 /*
2  * Copyright (c) 2010 Mike Qin <mikeandmore@gmail.com>
3  *
4  * The contents of this file are subject to the terms of either the GNU Lesser
5  * General Public License Version 2.1 only ("LGPL") or the Common Development and
6  * Distribution License ("CDDL")(collectively, the "License"). You may not use this
7  * file except in compliance with the License. You can obtain a copy of the CDDL at
8  * http://www.opensource.org/licenses/cddl1.php and a copy of the LGPLv2.1 at
9  * http://www.opensource.org/licenses/lgpl-license.php. See the License for the
10  * specific language governing permissions and limitations under the License. When
11  * distributing the software, include this License Header Notice in each file and
12  * include the full text of the License in the License file as well as the
13  * following notice:
14  *
15  * NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
16  * (CDDL)
17  * For Covered Software in this distribution, this License shall be governed by the
18  * laws of the State of California (excluding conflict-of-law provisions).
19  * Any litigation relating to this License shall be subject to the jurisdiction of
20  * the Federal Courts of the Northern District of California and the state courts
21  * of the State of California, with venue lying in Santa Clara County, California.
22  *
23  * Contributor(s):
24  *
25  * If you wish your version of this file to be governed by only the CDDL or only
26  * the LGPL Version 2.1, indicate your decision by adding "[Contributor]" elects to
27  * include this software in this distribution under the [CDDL or LGPL Version 2.1]
28  * license." If you don't indicate a single choice of license, a recipient has the
29  * option to distribute your version of this file under either the CDDL or the LGPL
30  * Version 2.1, or to extend the choice of license to its licensees as provided
31  * above. However, if you add LGPL Version 2.1 code and therefore, elected the LGPL
32  * Version 2 license, then the option applies only if the new code is made subject
33  * to such option by the copyright holder.
34  */
35
36 #include <assert.h>
37 #include <stdio.h>
38 #include <stdbool.h>
39 #include <langinfo.h>
40 #include <locale.h>
41 #include <iconv.h>
42 #include <signal.h>
43 #include <stdlib.h>
44 #include <stdbool.h>
45
46 #include "xim.h"
47 #include "xmisc.h"
48 #include "ic.h"
49 #include "common.h"
50 #include "settings.h"
51
52
53 static int __preedit_x;
54 static int __preedit_y;
55 static int __input_style;
56 static Window __client_window;
57 static Window __focus_window;
58
59 static int
60 _xim_open(XIMHandle* handle, IMOpenStruct* proto)
61 {
62     LOG("XIM_OPEN %d", proto->connect_id);
63     return 1;
64 }
65
66 static int
67 _xim_close(XIMHandle* handle, IMCloseStruct* proto)
68 {
69     LOG("XIM_CLOSE %d", proto->connect_id);
70     return 1;
71 }
72
73 static int
74 __xim_ic_events(IMChangeICStruct* proto)
75 {
76     XICAttribute* ic_attr = proto->ic_attr;
77     XICAttribute* pre_attr = proto->preedit_attr;
78
79     int i = 0;
80     for (i = 0; i < (int) proto->ic_attr_num; i++) {
81         if (strcmp(XNInputStyle, ic_attr[i].name) == 0) {
82             LOG("got input style %d", __input_style);
83             __input_style = * (int*) ic_attr[i].value;
84         } else if (strcmp(XNClientWindow, ic_attr[i].name) == 0) {
85             LOG("got client window");
86             __client_window = * (Window*) ic_attr[i].value;
87         } else if (strcmp(XNFocusWindow, ic_attr[i].name) == 0) {
88             LOG("got focus window");
89             __focus_window = * (Window*) ic_attr[i].value;
90         }
91     }
92     for (i = 0; i < (int) proto->preedit_attr_num; i++) {
93         if (strcmp(XNSpotLocation, pre_attr[i].name) == 0) {
94             XPoint* point = pre_attr[i].value;
95             __preedit_x = point->x;
96             __preedit_y = point->y;
97             LOG("position (%d, %d)", __preedit_x, __preedit_y);
98         }
99     }
100     return 1;
101 }
102
103 static int
104 _xim_create_ic(XIMHandle* handle, IMChangeICStruct* proto)
105 {
106     IC* ic = icmgr_create_ic(proto->connect_id);
107     __xim_ic_events(proto);
108     proto->icid = ic->icid;
109     ic->client_window = __client_window;
110     ic->offset_x = __preedit_x;
111     ic->offset_y = __preedit_y;
112     LOG("XIM_CREATE_IC %d", proto->icid);
113
114     return 1;
115 }
116
117 static int
118 _xim_destroy_ic(XIMHandle* handle, IMChangeICStruct* proto)
119 {
120     LOG("XIM_DESTROY_IC %d", proto->icid);
121     icmgr_destroy_ic(proto->icid);
122     icmgr_refresh();
123     return 1;
124 }
125
126 void
127 __move_preedit(IC* ic)
128 {
129     int root_x, root_y;
130     get_window_position(ic->client_window, &root_x, &root_y);
131     LOG("root: %d, %d offset: %d,%d", root_x, root_y,
132         ic->offset_x, ic->offset_y);
133     if (ic->offset_x <= 0 && ic->offset_y <= 0) {
134         int height;
135         get_window_size(ic->client_window, NULL, &height);
136         root_x += 4;
137         root_y += height;
138     } else {
139         root_x += ic->offset_x;
140         root_y += ic->offset_y;
141     }
142
143     preedit_move(root_x, root_y);
144 }
145
146 static int
147 _xim_set_ic_values(XIMHandle* handle, IMChangeICStruct* proto)
148 {
149     __xim_ic_events(proto);
150     IC* ic = icmgr_get(proto->icid);
151     /* some crapy swing application will have synchronization problems */
152     if (ic == NULL)
153         return 1;
154
155     LOG("XIM_SET_IC_VALUES %d", proto->icid);
156     ic->offset_x = __preedit_x;
157     ic->offset_y = __preedit_y;
158     IC* cur_ic = icmgr_get_current();
159
160     /* if we change the current ic position, we might wanna
161      * move it along the way
162      */
163     if (cur_ic != NULL && ic->icid == cur_ic->icid) {
164         __move_preedit(ic);
165     }
166     return 1;
167 }
168
169 static int
170 _xim_get_ic_values(XIMHandle* handle, IMChangeICStruct* proto)
171 {
172     LOG("XIM_GET_IC_VALUES %d", proto->icid);
173     XICAttribute* ic_attr = proto->ic_attr;
174
175     int i;
176     for (i = 0; i < (int) proto->ic_attr_num; i++) {
177         if (strcmp(XNFilterEvents, ic_attr[i].name) == 0) {
178             ic_attr[i].value = malloc(sizeof(CARD32));
179             *((CARD32*) ic_attr[i].value) = KeyPressMask | KeyPress;
180             ic_attr[i].value_length = sizeof(CARD32);
181         }
182     }
183     return 1;
184 }
185
186 static int
187 _xim_trigger_notify(XIMHandle* handle, IMTriggerNotifyStruct* proto)
188 {
189     LOG("trigger key pressed, %d", proto->icid);
190     IC* ic = icmgr_get(proto->icid);
191     if (ic == NULL)
192         return 1;
193
194     icmgr_set_current(proto->icid);
195     ic->is_enabled = true;
196     xim_start_preedit(handle);
197     icmgr_refresh();
198     return 1;
199 }
200
201 static int
202 _xim_set_ic_focus(XIMHandle* handle, IMChangeFocusStruct* proto)
203 {
204     DEBUG("%d", proto->icid);
205     LOG("set focus on ic %d %d", proto->icid, preedit_status());
206     /* if use didn't finish typing, we won't focus to new context */
207     if (preedit_status() == false) {
208         icmgr_set_current(proto->icid);
209     }
210     icmgr_refresh();
211
212     return 1;
213 }
214
215 static int
216 _xim_unset_ic_focus(XIMHandle* handle, IMChangeFocusStruct* proto)
217 {
218     LOG("unset focus on ic %d", proto->icid);
219
220     IC* ic = icmgr_get_current();
221     if (ic != NULL && ic->icid == proto->icid && preedit_status() == false) {
222         icmgr_clear_current();
223         icmgr_refresh();
224     }
225     return 1;
226 }
227
228 extern int _xim_forward_event(XIMHandle* handle,
229                               IMForwardEventStruct* proto);
230
231 static int
232 _imdkit_protocol_hanlder(XIMHandle* handle, IMProtocol* proto)
233 {
234     assert(handle != NULL);
235     assert(proto != NULL);
236
237     switch (proto->major_code) {
238     case XIM_OPEN:
239         return _xim_open(handle, (IMOpenStruct *) proto);
240     case XIM_CLOSE:
241         return _xim_close(handle, (IMCloseStruct*) proto);
242     case XIM_CREATE_IC:
243         return _xim_create_ic(handle, (IMChangeICStruct*) proto);
244     case XIM_DESTROY_IC:
245         return _xim_destroy_ic(handle, (IMChangeICStruct*) proto);
246     case XIM_SET_IC_VALUES:
247         return _xim_set_ic_values(handle, (IMChangeICStruct*) proto);
248     case XIM_GET_IC_VALUES:
249         return _xim_get_ic_values(handle, (IMChangeICStruct*) proto);
250     case XIM_TRIGGER_NOTIFY:
251           return _xim_trigger_notify(handle, (IMTriggerNotifyStruct*) proto);
252     case XIM_FORWARD_EVENT:
253         return _xim_forward_event(handle, (IMForwardEventStruct *) proto);
254     case XIM_SET_IC_FOCUS:
255         return _xim_set_ic_focus(handle, (IMChangeFocusStruct *) proto);
256     case XIM_UNSET_IC_FOCUS:
257         return _xim_unset_ic_focus(handle, (IMChangeFocusStruct *) proto);
258     default:
259         LOG("unhandled major code %d", proto->major_code);
260         return 1;
261     }
262 }
263
264 static XIMHandle*
265 _open_imdkit(const char* _server_name, const char* _locale)
266 {
267     XIMStyle ims_styles_onspot [] = {
268         XIMPreeditPosition | XIMStatusArea,        //OverTheSpot
269         XIMPreeditPosition | XIMStatusNothing,     //OverTheSpot
270         XIMPreeditPosition | XIMStatusNone,        //OverTheSpot
271         XIMPreeditNothing  | XIMStatusNothing,     //Root
272         XIMPreeditNothing  | XIMStatusNone,        //Root
273         0
274     };
275     XIMEncoding ims_encodings[] = {
276         "COMPOUND_TEXT",
277         0
278     };
279
280     /* this is rarely documentated, the trigger condition is
281      *
282      * keycode == keysym && (state & modifier_mask) == modifier
283      *
284      * where keycode and state is the user pressed
285      */
286     hotkey_t hk;
287     settings_get(TRIGGER_KEY, &hk);
288     XIMTriggerKey trigger = {
289         .keysym = hk.keysym,
290         .modifier = hk.modifiers,
291         .modifier_mask = STATE_MASK
292     };
293
294     XIMTriggerKeys keys;
295     XIMStyles styles;
296     XIMEncodings encodings;
297
298     styles.count_styles =
299         sizeof (ims_styles_onspot)/sizeof (XIMStyle) - 1;
300     styles.supported_styles = ims_styles_onspot;
301
302     encodings.count_encodings =
303         sizeof (ims_encodings)/sizeof (XIMEncoding) - 1;
304     encodings.supported_encodings = ims_encodings;
305
306     keys.count_keys = 1;
307     keys.keylist = &trigger;
308
309     Window win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy),
310                                      0, 0, 1, 1, 1, 0, 0);
311     XSelectInput(dpy, win,
312                  ExposureMask | ButtonPressMask | ButtonReleaseMask
313                  | ButtonMotionMask | VisibilityChangeMask);
314
315     XIMHandle* handle =
316         IMOpenIM(dpy,
317                  IMModifiers, "Xi18n",
318                  IMServerWindow, win,
319                  IMServerName, _server_name,
320                  IMLocale, _locale,
321                  IMServerTransport, "X/",
322                  IMInputStyles, &styles,
323                  IMEncodingList, &encodings,
324                  IMProtocolHandler, _imdkit_protocol_hanlder,
325                  IMFilterEventMask, KeyPressMask | KeyReleaseMask,
326                  IMOnKeysList, &keys,
327                  NULL);
328     if (handle == NULL) {
329         fprintf(stderr, "Startup xim server failed.\n");
330         fprintf(stderr, "Your locale is %s, please file a bug.", _locale);
331     }
332     return handle;
333 }
334
335 XIMHandle*
336 create_xim_server(const char* server_name, const char* locale)
337 {
338     XIMHandle* handle = _open_imdkit(server_name, locale);
339     icmgr_init();
340     return handle;
341 }
342
343 void
344 xim_start_preedit(XIMHandle* handle)
345 {
346     IC* ic = icmgr_get_current();
347     if (ic == NULL)
348         return;
349
350     IMPreeditStateStruct ps;
351     ps.icid = ic->icid;
352     ps.connect_id = ic->connect_id;
353     IMPreeditStart(handle, (XPointer) &ps);
354 }
355
356 void
357 xim_cancel_preedit(XIMHandle* handle)
358 {
359     IC* ic = icmgr_get_current();
360     if (ic == NULL)
361         return;
362
363     IMPreeditStateStruct ps;
364     ps.icid = ic->icid;
365     ps.connect_id = ic->connect_id;
366     IMPreeditEnd(handle, (XPointer) &ps);
367 }
368
369 void
370 xim_commit_preedit(XIMHandle* handle, const char* result_str)
371 {
372     IC* ic = icmgr_get_current();
373     if (ic == NULL)
374         return;
375
376     XTextProperty tp;
377     IMCommitStruct cs;
378     Xutf8TextListToTextProperty(dpy, (char**) &result_str, 1,
379                                 XCompoundTextStyle, &tp);
380     memset(&cs, 0, sizeof(IMCommitStruct));
381     cs.major_code = XIM_COMMIT;
382     cs.icid = ic->icid;
383     cs.connect_id = ic->connect_id;
384     cs.flag = XimLookupChars;
385     cs.commit_string = (char*) tp.value;
386     IMCommitString(handle, (XPointer) &cs);
387     XFree(tp.value);
388 }