2 * Copyright (c) 2010 Mike Qin <mikeandmore@gmail.com>
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
15 * NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
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.
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.
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;
60 _xim_open(XIMHandle* handle, IMOpenStruct* proto)
62 LOG("XIM_OPEN %d", proto->connect_id);
67 _xim_close(XIMHandle* handle, IMCloseStruct* proto)
69 LOG("XIM_CLOSE %d", proto->connect_id);
74 __xim_ic_events(IMChangeICStruct* proto)
76 XICAttribute* ic_attr = proto->ic_attr;
77 XICAttribute* pre_attr = proto->preedit_attr;
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;
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);
104 _xim_create_ic(XIMHandle* handle, IMChangeICStruct* proto)
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);
118 _xim_destroy_ic(XIMHandle* handle, IMChangeICStruct* proto)
120 LOG("XIM_DESTROY_IC %d", proto->icid);
121 icmgr_destroy_ic(proto->icid);
127 __move_preedit(IC* ic)
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) {
135 get_window_size(ic->client_window, NULL, &height);
139 root_x += ic->offset_x;
140 root_y += ic->offset_y;
143 preedit_move(root_x, root_y);
147 _xim_set_ic_values(XIMHandle* handle, IMChangeICStruct* proto)
149 __xim_ic_events(proto);
150 IC* ic = icmgr_get(proto->icid);
151 /* some crapy swing application will have synchronization problems */
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();
160 /* if we change the current ic position, we might wanna
161 * move it along the way
163 if (cur_ic != NULL && ic->icid == cur_ic->icid) {
170 _xim_get_ic_values(XIMHandle* handle, IMChangeICStruct* proto)
172 LOG("XIM_GET_IC_VALUES %d", proto->icid);
173 XICAttribute* ic_attr = proto->ic_attr;
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);
187 _xim_trigger_notify(XIMHandle* handle, IMTriggerNotifyStruct* proto)
189 LOG("trigger key pressed, %d", proto->icid);
190 IC* ic = icmgr_get(proto->icid);
194 icmgr_set_current(proto->icid);
195 ic->is_enabled = true;
196 xim_start_preedit(handle);
202 _xim_set_ic_focus(XIMHandle* handle, IMChangeFocusStruct* proto)
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);
216 _xim_unset_ic_focus(XIMHandle* handle, IMChangeFocusStruct* proto)
218 LOG("unset focus on ic %d", proto->icid);
220 IC* ic = icmgr_get_current();
221 if (ic != NULL && ic->icid == proto->icid && preedit_status() == false) {
222 icmgr_clear_current();
228 extern int _xim_forward_event(XIMHandle* handle,
229 IMForwardEventStruct* proto);
232 _imdkit_protocol_hanlder(XIMHandle* handle, IMProtocol* proto)
234 assert(handle != NULL);
235 assert(proto != NULL);
237 switch (proto->major_code) {
239 return _xim_open(handle, (IMOpenStruct *) proto);
241 return _xim_close(handle, (IMCloseStruct*) proto);
243 return _xim_create_ic(handle, (IMChangeICStruct*) proto);
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);
259 LOG("unhandled major code %d", proto->major_code);
265 _open_imdkit(const char* _server_name, const char* _locale)
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
275 XIMEncoding ims_encodings[] = {
280 /* this is rarely documentated, the trigger condition is
282 * keycode == keysym && (state & modifier_mask) == modifier
284 * where keycode and state is the user pressed
287 settings_get(TRIGGER_KEY, &hk);
288 XIMTriggerKey trigger = {
290 .modifier = hk.modifiers,
291 .modifier_mask = STATE_MASK
296 XIMEncodings encodings;
298 styles.count_styles =
299 sizeof (ims_styles_onspot)/sizeof (XIMStyle) - 1;
300 styles.supported_styles = ims_styles_onspot;
302 encodings.count_encodings =
303 sizeof (ims_encodings)/sizeof (XIMEncoding) - 1;
304 encodings.supported_encodings = ims_encodings;
307 keys.keylist = &trigger;
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);
317 IMModifiers, "Xi18n",
319 IMServerName, _server_name,
321 IMServerTransport, "X/",
322 IMInputStyles, &styles,
323 IMEncodingList, &encodings,
324 IMProtocolHandler, _imdkit_protocol_hanlder,
325 IMFilterEventMask, KeyPressMask | KeyReleaseMask,
328 if (handle == NULL) {
329 fprintf(stderr, "Startup xim server failed.\n");
330 fprintf(stderr, "Your locale is %s, please file a bug.", _locale);
336 create_xim_server(const char* server_name, const char* locale)
338 XIMHandle* handle = _open_imdkit(server_name, locale);
344 xim_start_preedit(XIMHandle* handle)
346 IC* ic = icmgr_get_current();
350 IMPreeditStateStruct ps;
352 ps.connect_id = ic->connect_id;
353 IMPreeditStart(handle, (XPointer) &ps);
357 xim_cancel_preedit(XIMHandle* handle)
359 IC* ic = icmgr_get_current();
363 IMPreeditStateStruct ps;
365 ps.connect_id = ic->connect_id;
366 IMPreeditEnd(handle, (XPointer) &ps);
370 xim_commit_preedit(XIMHandle* handle, const char* result_str)
372 IC* ic = icmgr_get_current();
378 Xutf8TextListToTextProperty(dpy, (char**) &result_str, 1,
379 XCompoundTextStyle, &tp);
380 memset(&cs, 0, sizeof(IMCommitStruct));
381 cs.major_code = XIM_COMMIT;
383 cs.connect_id = ic->connect_id;
384 cs.flag = XimLookupChars;
385 cs.commit_string = (char*) tp.value;
386 IMCommitString(handle, (XPointer) &cs);