text: Add simple compose input method
[profile/ivi/weston-ivi-shell.git] / clients / weston-simple-im.c
1 /*
2  * Copyright © 2012 Intel Corporation
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and
5  * its documentation for any purpose is hereby granted without fee, provided
6  * that the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of the copyright holders not be used in
9  * advertising or publicity pertaining to distribution of the software
10  * without specific, written prior permission.  The copyright holders make
11  * no representations about the suitability of this software for any
12  * purpose.  It is provided "as is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include <linux/input.h>
28
29 #include "window.h"
30 #include "keyboard-utils.h"
31 #include "input-method-client-protocol.h"
32
33 enum compose_state {
34         state_normal,
35         state_compose
36 };
37
38 struct compose_seq {
39         uint32_t keys[4];
40
41         const char *text;
42 };
43
44 struct simple_im {
45         struct input_method *input_method;
46         struct input_method_context *context;
47         struct display *display;
48         struct wl_keyboard *keyboard;
49         struct keyboard_input *keyboard_input;
50         enum compose_state compose_state;
51         struct compose_seq compose_seq;
52 };
53
54 static const struct compose_seq compose_seqs[] = {
55         { { XKB_KEY_quotedbl, XKB_KEY_A, 0 },  "Ä" },
56         { { XKB_KEY_quotedbl, XKB_KEY_O, 0 },  "Ö" },
57         { { XKB_KEY_quotedbl, XKB_KEY_U, 0 },  "Ü" },
58         { { XKB_KEY_quotedbl, XKB_KEY_a, 0 },  "ä" },
59         { { XKB_KEY_quotedbl, XKB_KEY_o, 0 },  "ö" },
60         { { XKB_KEY_quotedbl, XKB_KEY_u, 0 },  "ü" },
61         { { XKB_KEY_apostrophe, XKB_KEY_A, 0 },  "Á" },
62         { { XKB_KEY_apostrophe, XKB_KEY_a, 0 },  "á" },
63         { { XKB_KEY_O, XKB_KEY_C, 0 },  "©" },
64         { { XKB_KEY_O, XKB_KEY_R, 0 },  "®" },
65         { { XKB_KEY_s, XKB_KEY_s, 0 },  "ß" },
66 };
67
68 static const uint32_t ignore_keys_on_compose[] = {
69         XKB_KEY_Shift_L,
70         XKB_KEY_Shift_R
71 };
72
73 static void
74 input_method_context_surrounding_text(void *data,
75                                       struct input_method_context *context,
76                                       const char *text,
77                                       uint32_t cursor,
78                                       uint32_t anchor)
79 {
80         fprintf(stderr, "Surrounding text updated: %s\n", text);
81 }
82
83 static void
84 input_method_context_reset(void *data,
85                            struct input_method_context *context)
86 {
87         struct simple_im *keyboard = data;
88
89         fprintf(stderr, "Reset pre-edit buffer\n");
90
91         keyboard->compose_state = state_normal;
92 }
93
94 static const struct input_method_context_listener input_method_context_listener = {
95         input_method_context_surrounding_text,
96         input_method_context_reset
97 };
98
99 static void
100 input_method_keyboard_keymap(void *data,
101                              struct wl_keyboard *wl_keyboard,
102                              uint32_t format,
103                              int32_t fd,
104                              uint32_t size)
105 {
106         struct simple_im *keyboard = data;
107
108         keyboard_input_handle_keymap(keyboard->keyboard_input, format, fd, size);
109 }
110
111 static void
112 input_method_keyboard_key(void *data,
113                           struct wl_keyboard *wl_keyboard,
114                           uint32_t serial,
115                           uint32_t time,
116                           uint32_t key,
117                           uint32_t state_w)
118 {
119         struct simple_im *keyboard = data;
120
121         keyboard_input_handle_key(keyboard->keyboard_input, serial,
122                                   time, key, state_w);
123 }
124
125 static void
126 input_method_keyboard_modifiers(void *data,
127                                 struct wl_keyboard *wl_keyboard,
128                                 uint32_t serial,
129                                 uint32_t mods_depressed,
130                                 uint32_t mods_latched,
131                                 uint32_t mods_locked,
132                                 uint32_t group)
133 {
134         struct simple_im *keyboard = data;
135         struct input_method_context *context = keyboard->context;
136
137         keyboard_input_handle_modifiers(keyboard->keyboard_input, serial,
138                                         mods_depressed, mods_latched, mods_locked, group);
139
140         input_method_context_modifiers(context, serial,
141                                        mods_depressed, mods_depressed,
142                                        mods_latched, group);
143 }
144
145 static const struct wl_keyboard_listener input_method_keyboard_listener = {
146         input_method_keyboard_keymap,
147         NULL, /* enter */
148         NULL, /* leave */
149         input_method_keyboard_key,
150         input_method_keyboard_modifiers
151 };
152
153 static void
154 input_method_activate(void *data,
155                       struct input_method *input_method,
156                       struct input_method_context *context)
157 {
158         struct simple_im *keyboard = data;
159
160         if (keyboard->context)
161                 input_method_context_destroy(keyboard->context);
162
163         keyboard->compose_state = state_normal;
164
165         keyboard->context = context;
166         input_method_context_add_listener(context,
167                                           &input_method_context_listener,
168                                           keyboard);
169         keyboard->keyboard = input_method_context_grab_keyboard(context);
170         wl_keyboard_add_listener(keyboard->keyboard,
171                                  &input_method_keyboard_listener,
172                                  keyboard);
173 }
174
175 static void
176 input_method_deactivate(void *data,
177                         struct input_method *input_method,
178                         struct input_method_context *context)
179 {
180         struct simple_im *keyboard = data;
181
182         if (!keyboard->context)
183                 return;
184
185         input_method_context_destroy(keyboard->context);
186         keyboard->context = NULL;
187 }
188
189 static const struct input_method_listener input_method_listener = {
190         input_method_activate,
191         input_method_deactivate
192 };
193
194 static void
195 global_handler(struct display *display, uint32_t name,
196                const char *interface, uint32_t version, void *data)
197 {
198         struct simple_im *keyboard = data;
199
200         if (!strcmp(interface, "input_method")) {
201                 keyboard->input_method =
202                         display_bind(display, name,
203                                      &input_method_interface, 1);
204                 input_method_add_listener(keyboard->input_method, &input_method_listener, keyboard);
205         }
206 }
207
208 static int
209 compare_compose_keys(const void *c1, const void *c2)
210 {
211         const struct compose_seq *cs1 = c1;
212         const struct compose_seq *cs2 = c2;
213         int i;
214
215         for (i = 0; cs1->keys[i] != 0 && cs2->keys[i] != 0; i++) {
216                 if (cs1->keys[i] != cs2->keys[i])
217                         return cs1->keys[i] - cs2->keys[i];
218         }
219
220         if (cs1->keys[i] == cs2->keys[i]
221             || cs1->keys[i] == 0)
222                 return 0;
223
224         return cs1->keys[i] - cs2->keys[i];
225 }
226
227 static void
228 simple_im_key_handler(struct keyboard_input *keyboard_input,
229                       uint32_t time, uint32_t key, uint32_t sym,
230                       enum wl_keyboard_key_state state, void *data)
231 {
232         struct simple_im *keyboard = data;
233         struct input_method_context *context = keyboard->context;
234         char text[64];
235
236         if (sym == XKB_KEY_Multi_key &&
237             state == WL_KEYBOARD_KEY_STATE_RELEASED &&
238             keyboard->compose_state == state_normal) {
239                 keyboard->compose_state = state_compose;
240                 memset(&keyboard->compose_seq, 0, sizeof(struct compose_seq));
241                 return;
242         }
243
244         if (keyboard->compose_state == state_compose) {
245                 uint32_t i = 0;
246                 struct compose_seq *cs;
247
248                 if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
249                         return;
250
251                 for (i = 0; i < sizeof(ignore_keys_on_compose) / sizeof(ignore_keys_on_compose[0]); i++) {
252                         if (sym == ignore_keys_on_compose[i]) {
253                                 input_method_context_key(context, display_get_serial(keyboard->display), time, key, state);
254                                 return;
255                         }
256                 }
257
258                 for (i = 0; keyboard->compose_seq.keys[i] != 0; i++);
259
260                 keyboard->compose_seq.keys[i] = sym;
261
262                 cs = bsearch (&keyboard->compose_seq, compose_seqs,
263                               sizeof(compose_seqs) / sizeof(compose_seqs[0]),
264                               sizeof(compose_seqs[0]), compare_compose_keys);
265
266                 if (cs) {
267                         if (cs->keys[i + 1] == 0) {
268                                 input_method_context_preedit_string(keyboard->context,
269                                                                     "", 0);
270                                 input_method_context_commit_string(keyboard->context,
271                                                                    cs->text,
272                                                                    strlen(cs->text));
273                                 keyboard->compose_state = state_normal;
274                         } else {
275                                 uint32_t j = 0, idx = 0;
276
277                                 for (; j <= i; j++) {
278                                         idx += xkb_keysym_to_utf8(cs->keys[j], text + idx, sizeof(text) - idx);
279                                 }
280
281                                 input_method_context_preedit_string(keyboard->context,
282                                                                     text,
283                                                                     strlen(text));
284                         }
285                 } else {
286                         uint32_t j = 0, idx = 0;
287
288                         for (; j <= i; j++) {
289                                 idx += xkb_keysym_to_utf8(keyboard->compose_seq.keys[j], text + idx, sizeof(text) - idx);
290                         }
291                         input_method_context_preedit_string(keyboard->context,
292                                                             "", 0);
293                         input_method_context_commit_string(keyboard->context,
294                                                            text,
295                                                            strlen(text));
296                         keyboard->compose_state = state_normal;
297                 }
298                 return;
299         }
300
301         if (xkb_keysym_to_utf8(sym, text, sizeof(text)) <= 0) {
302                 input_method_context_key(context, display_get_serial(keyboard->display), time, key, state);
303                 return;
304         }
305
306         if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
307                 return;
308
309         input_method_context_commit_string(keyboard->context,
310                                            text,
311                                            strlen(text));
312 }
313
314 int
315 main(int argc, char *argv[])
316 {
317         struct simple_im simple_im;
318
319         memset(&simple_im, 0, sizeof(simple_im));
320
321         simple_im.display = display_create(argc, argv);
322         if (simple_im.display == NULL) {
323                 fprintf(stderr, "failed to create display: %m\n");
324                 return -1;
325         }
326
327         simple_im.context = NULL;
328         simple_im.keyboard_input = keyboard_input_create(display_get_xkb_context(simple_im.display));
329         keyboard_input_set_user_data(simple_im.keyboard_input, &simple_im);
330         keyboard_input_set_key_handler(simple_im.keyboard_input, simple_im_key_handler);
331
332         display_set_user_data(simple_im.display, &simple_im);
333         display_set_global_handler(simple_im.display, global_handler);
334
335         display_run(simple_im.display);
336
337         return 0;
338 }