blob: b34aafa9d7c232e7c694ff28d87320df31b7f605 [file] [log] [blame]
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001/*
2 * Copyright © 2012 Openismus GmbH
Jan Arne Petersen4c265182012-09-09 23:08:30 +02003 * Copyright © 2012 Intel Corporation
Jan Arne Petersencba9e472012-06-21 21:52:19 +02004 *
5 * Permission to use, copy, modify, distribute, and sell this software and
6 * its documentation for any purpose is hereby granted without fee, provided
7 * that the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation, and that the name of the copyright holders not be used in
10 * advertising or publicity pertaining to distribution of the software
11 * without specific, written prior permission. The copyright holders make
12 * no representations about the suitability of this software for any
13 * purpose. It is provided "as is" without express or implied warranty.
14 *
15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
16 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
19 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
20 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 */
23
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +010024#include "config.h"
25
Philipp Brüschweiler591cfca2012-07-11 22:25:29 +020026#include <assert.h>
Jan Arne Petersencba9e472012-06-21 21:52:19 +020027#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +020030#include <stdbool.h>
Jan Arne Petersencba9e472012-06-21 21:52:19 +020031
32#include <linux/input.h>
33#include <cairo.h>
34
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +010035#include <pango/pangocairo.h>
36
Jan Arne Petersencba9e472012-06-21 21:52:19 +020037#include "window.h"
38#include "text-client-protocol.h"
39
40struct text_entry {
41 struct widget *widget;
Jan Arne Petersene829adc2012-08-10 16:47:22 +020042 struct window *window;
Jan Arne Petersencba9e472012-06-21 21:52:19 +020043 char *text;
44 int active;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +020045 uint32_t cursor;
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +020046 uint32_t anchor;
Jan Arne Petersen46535312013-01-16 21:26:38 +010047 struct {
48 char *text;
49 int32_t cursor;
50 char *commit;
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +010051 PangoAttrList *attr_list;
Jan Arne Petersen46535312013-01-16 21:26:38 +010052 } preedit;
53 struct {
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +010054 PangoAttrList *attr_list;
Jan Arne Petersen46535312013-01-16 21:26:38 +010055 int32_t cursor;
56 } preedit_info;
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +010057 struct {
58 int32_t cursor;
59 int32_t anchor;
Jan Arne Petersen919bc142013-04-18 16:47:34 +020060 uint32_t delete_index;
61 uint32_t delete_length;
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +020062 bool invalid_delete;
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +010063 } pending_commit;
Jan Arne Petersen62ece762013-04-18 16:47:36 +020064 struct wl_text_input *text_input;
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +010065 PangoLayout *layout;
Jan Arne Petersencd997062012-11-18 19:06:44 +010066 struct {
67 xkb_mod_mask_t shift_mask;
68 } keysym;
Jan Arne Petersenc7d2a982013-01-16 21:26:39 +010069 uint32_t serial;
Jan Arne Petersen00191c72013-04-18 16:47:33 +020070 uint32_t reset_serial;
Jan Arne Petersen0558a932013-01-16 21:26:45 +010071 uint32_t content_purpose;
Jan Arne Petersen61381972013-01-31 15:52:21 +010072 uint32_t click_to_show;
Jan Arne Petersen9d419132013-04-18 16:47:16 +020073 char *preferred_language;
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +020074 bool button_pressed;
Jan Arne Petersencba9e472012-06-21 21:52:19 +020075};
76
77struct editor {
Jan Arne Petersen62ece762013-04-18 16:47:36 +020078 struct wl_text_input_manager *text_input_manager;
Jan Arne Petersencba9e472012-06-21 21:52:19 +020079 struct display *display;
80 struct window *window;
81 struct widget *widget;
82 struct text_entry *entry;
83 struct text_entry *editor;
Rob Bradford9d1d32b2012-11-18 19:06:49 +010084 struct text_entry *active_entry;
Jan Arne Petersencba9e472012-06-21 21:52:19 +020085};
86
Jan Arne Petersen6345faa2012-11-05 03:26:39 +010087static const char *
Jan Arne Petersen6345faa2012-11-05 03:26:39 +010088utf8_end_char(const char *p)
89{
90 while ((*p & 0xc0) == 0x80)
91 p++;
92 return p;
93}
94
95static const char *
Jan Arne Petersen68516862013-04-18 16:47:42 +020096utf8_prev_char(const char *s, const char *p)
97{
98 for (--p; p >= s; --p) {
99 if ((*p & 0xc0) != 0x80)
100 return p;
101 }
102 return NULL;
103}
104
105static const char *
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100106utf8_next_char(const char *p)
107{
108 if (*p != 0)
109 return utf8_end_char(++p);
110 return NULL;
111}
112
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200113static void text_entry_redraw_handler(struct widget *widget, void *data);
114static void text_entry_button_handler(struct widget *widget,
115 struct input *input, uint32_t time,
116 uint32_t button,
117 enum wl_pointer_button_state state, void *data);
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +0200118static int text_entry_motion_handler(struct widget *widget,
119 struct input *input, uint32_t time,
120 float x, float y, void *data);
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100121static void text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
122 int32_t cursor, int32_t anchor);
Jan Arne Petersen43f4aa82012-09-09 23:08:43 +0200123static void text_entry_set_preedit(struct text_entry *entry,
124 const char *preedit_text,
125 int preedit_cursor);
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200126static void text_entry_delete_text(struct text_entry *entry,
127 uint32_t index, uint32_t length);
Jan Arne Petersene386dd22012-09-17 15:28:09 +0200128static void text_entry_delete_selected_text(struct text_entry *entry);
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100129static void text_entry_reset_preedit(struct text_entry *entry);
130static void text_entry_commit_and_reset(struct text_entry *entry);
Jan Arne Petersenfe89e712013-04-18 16:47:27 +0200131static void text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle);
132static void text_entry_update(struct text_entry *entry);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200133
134static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200135text_input_commit_string(void *data,
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200136 struct wl_text_input *text_input,
Jan Arne Petersenc7d2a982013-01-16 21:26:39 +0100137 uint32_t serial,
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100138 const char *text)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200139{
140 struct text_entry *entry = data;
141
Jan Arne Petersen00191c72013-04-18 16:47:33 +0200142 if ((entry->serial - serial) > (entry->serial - entry->reset_serial)) {
143 fprintf(stderr, "Ignore commit. Serial: %u, Current: %u, Reset: %u\n",
144 serial, entry->serial, entry->reset_serial);
145 return;
146 }
147
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200148 if (entry->pending_commit.invalid_delete) {
149 fprintf(stderr, "Ignore commit. Invalid previous delete_surrounding event.\n");
150 memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
151 return;
152 }
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100153
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200154 text_entry_reset_preedit(entry);
Jan Arne Petersen919bc142013-04-18 16:47:34 +0200155
156 if (entry->pending_commit.delete_length) {
157 text_entry_delete_text(entry,
158 entry->pending_commit.delete_index,
159 entry->pending_commit.delete_length);
Jan Arne Petersena96953d2013-05-30 13:57:02 +0200160 } else {
161 text_entry_delete_selected_text(entry);
Jan Arne Petersen919bc142013-04-18 16:47:34 +0200162 }
163
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100164 text_entry_insert_at_cursor(entry, text,
165 entry->pending_commit.cursor,
166 entry->pending_commit.anchor);
167
168 memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200169
170 widget_schedule_redraw(entry->widget);
171}
172
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200173static void
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200174clear_pending_preedit(struct text_entry *entry)
175{
176 memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
177
178 pango_attr_list_unref(entry->preedit_info.attr_list);
179
180 entry->preedit_info.cursor = 0;
181 entry->preedit_info.attr_list = NULL;
182
183 memset(&entry->preedit_info, 0, sizeof entry->preedit_info);
184}
185
186static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200187text_input_preedit_string(void *data,
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200188 struct wl_text_input *text_input,
Jan Arne Petersenc7d2a982013-01-16 21:26:39 +0100189 uint32_t serial,
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200190 const char *text,
Jan Arne Petersen46535312013-01-16 21:26:38 +0100191 const char *commit)
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200192{
Jan Arne Petersen43f4aa82012-09-09 23:08:43 +0200193 struct text_entry *entry = data;
194
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200195 if ((entry->serial - serial) > (entry->serial - entry->reset_serial)) {
196 fprintf(stderr, "Ignore preedit_string. Serial: %u, Current: %u, Reset: %u\n",
197 serial, entry->serial, entry->reset_serial);
198 clear_pending_preedit(entry);
199 return;
200 }
201
202 if (entry->pending_commit.invalid_delete) {
203 fprintf(stderr, "Ignore preedit_string. Invalid previous delete_surrounding event.\n");
204 clear_pending_preedit(entry);
205 return;
206 }
207
Jan Arne Petersena96953d2013-05-30 13:57:02 +0200208 if (entry->pending_commit.delete_length) {
209 text_entry_delete_text(entry,
210 entry->pending_commit.delete_index,
211 entry->pending_commit.delete_length);
212 } else {
213 text_entry_delete_selected_text(entry);
214 }
Jan Arne Petersena96953d2013-05-30 13:57:02 +0200215
Jan Arne Petersen46535312013-01-16 21:26:38 +0100216 text_entry_set_preedit(entry, text, entry->preedit_info.cursor);
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100217 entry->preedit.commit = strdup(commit);
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200218 entry->preedit.attr_list = pango_attr_list_ref(entry->preedit_info.attr_list);
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100219
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200220 clear_pending_preedit(entry);
Jan Arne Petersen43f4aa82012-09-09 23:08:43 +0200221
Jan Arne Petersenfe89e712013-04-18 16:47:27 +0200222 text_entry_update(entry);
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200223
Jan Arne Petersen43f4aa82012-09-09 23:08:43 +0200224 widget_schedule_redraw(entry->widget);
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200225}
226
227static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200228text_input_delete_surrounding_text(void *data,
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200229 struct wl_text_input *text_input,
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200230 int32_t index,
231 uint32_t length)
232{
233 struct text_entry *entry = data;
Jan Arne Petersen919bc142013-04-18 16:47:34 +0200234 uint32_t text_length;
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200235
Jan Arne Petersen919bc142013-04-18 16:47:34 +0200236 entry->pending_commit.delete_index = entry->cursor + index;
237 entry->pending_commit.delete_length = length;
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200238 entry->pending_commit.invalid_delete = false;
Jan Arne Petersen919bc142013-04-18 16:47:34 +0200239
Jan Arne Petersen68516862013-04-18 16:47:42 +0200240 text_length = strlen(entry->text);
Jan Arne Petersen919bc142013-04-18 16:47:34 +0200241
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200242 if (entry->pending_commit.delete_index > text_length ||
243 length > text_length ||
Jan Arne Petersen895a1282013-05-30 13:57:04 +0200244 entry->pending_commit.delete_index + length > text_length) {
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200245 fprintf(stderr, "delete_surrounding_text: Invalid index: %d," \
246 "length %u'; cursor: %u text length: %u\n", index, length, entry->cursor, text_length);
247 entry->pending_commit.invalid_delete = true;
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200248 return;
249 }
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200250}
251
252static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200253text_input_cursor_position(void *data,
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200254 struct wl_text_input *text_input,
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100255 int32_t index,
256 int32_t anchor)
257{
258 struct text_entry *entry = data;
259
260 entry->pending_commit.cursor = index;
261 entry->pending_commit.anchor = anchor;
262}
263
264static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200265text_input_preedit_styling(void *data,
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200266 struct wl_text_input *text_input,
Jan Arne Petersen46535312013-01-16 21:26:38 +0100267 uint32_t index,
268 uint32_t length,
269 uint32_t style)
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200270{
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100271 struct text_entry *entry = data;
272 PangoAttribute *attr1 = NULL;
273 PangoAttribute *attr2 = NULL;
274
275 if (!entry->preedit_info.attr_list)
276 entry->preedit_info.attr_list = pango_attr_list_new();
277
278 switch (style) {
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200279 case WL_TEXT_INPUT_PREEDIT_STYLE_DEFAULT:
280 case WL_TEXT_INPUT_PREEDIT_STYLE_UNDERLINE:
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100281 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
282 break;
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200283 case WL_TEXT_INPUT_PREEDIT_STYLE_INCORRECT:
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100284 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_ERROR);
285 attr2 = pango_attr_underline_color_new(65535, 0, 0);
286 break;
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200287 case WL_TEXT_INPUT_PREEDIT_STYLE_SELECTION:
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100288 attr1 = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
289 attr2 = pango_attr_foreground_new(65535, 65535, 65535);
290 break;
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200291 case WL_TEXT_INPUT_PREEDIT_STYLE_HIGHLIGHT:
292 case WL_TEXT_INPUT_PREEDIT_STYLE_ACTIVE:
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100293 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
294 attr2 = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
295 break;
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200296 case WL_TEXT_INPUT_PREEDIT_STYLE_INACTIVE:
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100297 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
298 attr2 = pango_attr_foreground_new(0.3 * 65535, 0.3 * 65535, 0.3 * 65535);
299 break;
300 }
301
302 if (attr1) {
303 attr1->start_index = entry->cursor + index;
304 attr1->end_index = entry->cursor + index + length;
305 pango_attr_list_insert(entry->preedit_info.attr_list, attr1);
306 }
307
308 if (attr2) {
309 attr2->start_index = entry->cursor + index;
310 attr2->end_index = entry->cursor + index + length;
311 pango_attr_list_insert(entry->preedit_info.attr_list, attr2);
312 }
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200313}
314
315static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200316text_input_preedit_cursor(void *data,
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200317 struct wl_text_input *text_input,
Jan Arne Petersen46535312013-01-16 21:26:38 +0100318 int32_t index)
319{
320 struct text_entry *entry = data;
321
322 entry->preedit_info.cursor = index;
323}
324
325static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200326text_input_modifiers_map(void *data,
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200327 struct wl_text_input *text_input,
Jan Arne Petersend9be93b2012-11-18 19:06:43 +0100328 struct wl_array *map)
329{
Jan Arne Petersencd997062012-11-18 19:06:44 +0100330 struct text_entry *entry = data;
331
332 entry->keysym.shift_mask = keysym_modifiers_get_mask(map, "Shift");
Jan Arne Petersend9be93b2012-11-18 19:06:43 +0100333}
334
335static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200336text_input_keysym(void *data,
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200337 struct wl_text_input *text_input,
Jan Arne Petersend9be93b2012-11-18 19:06:43 +0100338 uint32_t serial,
339 uint32_t time,
340 uint32_t key,
341 uint32_t state,
342 uint32_t modifiers)
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200343{
Jan Arne Petersen8aba11d2012-09-17 15:28:07 +0200344 struct text_entry *entry = data;
Jan Arne Petersencd997062012-11-18 19:06:44 +0100345 const char *state_label = "release";
346 const char *key_label = "Unknown";
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100347 const char *new_char;
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200348
349 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
350 state_label = "pressed";
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200351 }
352
Jan Arne Petersencd997062012-11-18 19:06:44 +0100353 if (key == XKB_KEY_Left ||
354 key == XKB_KEY_Right) {
355 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
356 return;
357
358 if (key == XKB_KEY_Left)
359 new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
360 else
361 new_char = utf8_next_char(entry->text + entry->cursor);
362
363 if (new_char != NULL) {
364 entry->cursor = new_char - entry->text;
Jan Arne Petersencd997062012-11-18 19:06:44 +0100365 }
366
Jan Arne Petersen68516862013-04-18 16:47:42 +0200367 if (!(modifiers & entry->keysym.shift_mask))
368 entry->anchor = entry->cursor;
369 widget_schedule_redraw(entry->widget);
370
Jan Arne Petersencd997062012-11-18 19:06:44 +0100371 return;
372 }
373
Jan Arne Petersen3fb6e712013-01-16 21:26:52 +0100374 if (key == XKB_KEY_BackSpace) {
375 const char *start, *end;
376
Jan Arne Petersendfd34462013-04-18 16:47:26 +0200377 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
378 return;
379
Jan Arne Petersen3fb6e712013-01-16 21:26:52 +0100380 text_entry_commit_and_reset(entry);
381
382 start = utf8_prev_char(entry->text, entry->text + entry->cursor);
Jan Arne Petersen3fb6e712013-01-16 21:26:52 +0100383 if (start == NULL)
384 return;
385
Daiki Uenob08b3292013-06-28 18:59:44 +0900386 end = utf8_next_char(start);
387
Jan Arne Petersen3fb6e712013-01-16 21:26:52 +0100388 text_entry_delete_text(entry,
389 start - entry->text,
390 end - start);
391
392 return;
393 }
394
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200395 switch (key) {
396 case XKB_KEY_Tab:
397 key_label = "Tab";
398 break;
399 case XKB_KEY_KP_Enter:
Jan Arne Petersencd997062012-11-18 19:06:44 +0100400 case XKB_KEY_Return:
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200401 key_label = "Enter";
402 break;
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200403 }
404
405 fprintf(stderr, "%s key was %s.\n", key_label, state_label);
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200406}
407
408static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200409text_input_enter(void *data,
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200410 struct wl_text_input *text_input,
Jan Arne Petersen680275f2012-09-24 14:51:14 +0200411 struct wl_surface *surface)
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200412{
413 struct text_entry *entry = data;
414
Jan Arne Petersen680275f2012-09-24 14:51:14 +0200415 if (surface != window_get_wl_surface(entry->window))
416 return;
417
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200418 entry->active = 1;
419
Jan Arne Petersen00191c72013-04-18 16:47:33 +0200420 text_entry_update(entry);
421 entry->reset_serial = entry->serial;
422
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200423 widget_schedule_redraw(entry->widget);
424}
425
426static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200427text_input_leave(void *data,
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200428 struct wl_text_input *text_input)
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200429{
430 struct text_entry *entry = data;
431
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100432 text_entry_commit_and_reset(entry);
433
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200434 entry->active = 0;
435
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200436 wl_text_input_hide_input_panel(text_input);
Jan Arne Petersen61381972013-01-31 15:52:21 +0100437
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200438 widget_schedule_redraw(entry->widget);
439}
440
Jan Arne Petersen61381972013-01-31 15:52:21 +0100441static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200442text_input_input_panel_state(void *data,
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200443 struct wl_text_input *text_input,
Jan Arne Petersen61381972013-01-31 15:52:21 +0100444 uint32_t state)
445{
446}
447
Jan Arne Petersenece6b5a2013-04-18 16:47:15 +0200448static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200449text_input_language(void *data,
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200450 struct wl_text_input *text_input,
Jan Arne Petersenece6b5a2013-04-18 16:47:15 +0200451 uint32_t serial,
452 const char *language)
453{
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200454 fprintf(stderr, "input language is %s \n", language);
Jan Arne Petersenece6b5a2013-04-18 16:47:15 +0200455}
456
457static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200458text_input_text_direction(void *data,
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200459 struct wl_text_input *text_input,
Jan Arne Petersenece6b5a2013-04-18 16:47:15 +0200460 uint32_t serial,
461 uint32_t direction)
462{
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200463 struct text_entry *entry = data;
464 PangoContext *context = pango_layout_get_context(entry->layout);
465 PangoDirection pango_direction;
466
467
468 switch (direction) {
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200469 case WL_TEXT_INPUT_TEXT_DIRECTION_LTR:
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200470 pango_direction = PANGO_DIRECTION_LTR;
471 break;
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200472 case WL_TEXT_INPUT_TEXT_DIRECTION_RTL:
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200473 pango_direction = PANGO_DIRECTION_RTL;
474 break;
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200475 case WL_TEXT_INPUT_TEXT_DIRECTION_AUTO:
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200476 default:
477 pango_direction = PANGO_DIRECTION_NEUTRAL;
478 }
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200479
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200480 pango_context_set_base_dir(context, pango_direction);
Jan Arne Petersenece6b5a2013-04-18 16:47:15 +0200481}
482
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200483static const struct wl_text_input_listener text_input_listener = {
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200484 text_input_enter,
485 text_input_leave,
486 text_input_modifiers_map,
487 text_input_input_panel_state,
488 text_input_preedit_string,
489 text_input_preedit_styling,
490 text_input_preedit_cursor,
491 text_input_commit_string,
492 text_input_cursor_position,
493 text_input_delete_surrounding_text,
494 text_input_keysym,
495 text_input_language,
496 text_input_text_direction
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200497};
498
499static struct text_entry*
500text_entry_create(struct editor *editor, const char *text)
501{
502 struct text_entry *entry;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200503
Brian Lovinbc919262013-08-07 15:34:59 -0700504 entry = xmalloc(sizeof *entry);
505 memset(entry, 0, sizeof *entry);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200506
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200507 entry->widget = widget_add_widget(editor->widget, entry);
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200508 entry->window = editor->window;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200509 entry->text = strdup(text);
510 entry->active = 0;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200511 entry->cursor = strlen(text);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200512 entry->anchor = entry->cursor;
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200513 entry->text_input = wl_text_input_manager_create_text_input(editor->text_input_manager);
514 wl_text_input_add_listener(entry->text_input, &text_input_listener, entry);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200515
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200516 widget_set_redraw_handler(entry->widget, text_entry_redraw_handler);
517 widget_set_button_handler(entry->widget, text_entry_button_handler);
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +0200518 widget_set_motion_handler(entry->widget, text_entry_motion_handler);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200519
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200520 return entry;
521}
522
523static void
524text_entry_destroy(struct text_entry *entry)
525{
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200526 widget_destroy(entry->widget);
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200527 wl_text_input_destroy(entry->text_input);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100528 g_clear_object(&entry->layout);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200529 free(entry->text);
530 free(entry);
531}
532
533static void
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200534redraw_handler(struct widget *widget, void *data)
535{
536 struct editor *editor = data;
537 cairo_surface_t *surface;
538 struct rectangle allocation;
539 cairo_t *cr;
540
541 surface = window_get_surface(editor->window);
542 widget_get_allocation(editor->widget, &allocation);
543
544 cr = cairo_create(surface);
545 cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
546 cairo_clip(cr);
547
548 cairo_translate(cr, allocation.x, allocation.y);
549
550 /* Draw background */
551 cairo_push_group(cr);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200552 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200553 cairo_set_source_rgba(cr, 1, 1, 1, 1);
554 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
555 cairo_fill(cr);
556
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200557 cairo_pop_group_to_source(cr);
558 cairo_paint(cr);
559
560 cairo_destroy(cr);
561 cairo_surface_destroy(surface);
562}
563
564static void
565text_entry_allocate(struct text_entry *entry, int32_t x, int32_t y,
566 int32_t width, int32_t height)
567{
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200568 widget_set_allocation(entry->widget, x, y, width, height);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200569}
570
571static void
572resize_handler(struct widget *widget,
573 int32_t width, int32_t height, void *data)
574{
575 struct editor *editor = data;
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200576 struct rectangle allocation;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200577
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200578 widget_get_allocation(editor->widget, &allocation);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200579
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200580 text_entry_allocate(editor->entry,
581 allocation.x + 20, allocation.y + 20,
582 width - 40, height / 2 - 40);
583 text_entry_allocate(editor->editor,
584 allocation.x + 20, allocation.y + height / 2 + 20,
585 width - 40, height / 2 - 40);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200586}
587
588static void
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200589text_entry_activate(struct text_entry *entry,
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200590 struct wl_seat *seat)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200591{
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200592 struct wl_surface *surface = window_get_wl_surface(entry->window);
593
Jan Arne Petersen61381972013-01-31 15:52:21 +0100594 if (entry->click_to_show && entry->active) {
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200595 wl_text_input_show_input_panel(entry->text_input);
Jan Arne Petersen61381972013-01-31 15:52:21 +0100596
597 return;
598 }
599
600 if (!entry->click_to_show)
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200601 wl_text_input_show_input_panel(entry->text_input);
Jan Arne Petersen61381972013-01-31 15:52:21 +0100602
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200603 wl_text_input_activate(entry->text_input,
604 seat,
605 surface);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200606}
607
608static void
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200609text_entry_deactivate(struct text_entry *entry,
610 struct wl_seat *seat)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200611{
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200612 wl_text_input_deactivate(entry->text_input,
613 seat);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200614}
615
616static void
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200617text_entry_update_layout(struct text_entry *entry)
618{
619 char *text;
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100620 PangoAttrList *attr_list;
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200621
Jan Arne Petersen68516862013-04-18 16:47:42 +0200622 assert(entry->cursor <= (strlen(entry->text) +
623 (entry->preedit.text ? strlen(entry->preedit.text) : 0)));
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200624
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100625 if (entry->preedit.text) {
626 text = malloc(strlen(entry->text) + strlen(entry->preedit.text) + 1);
627 strncpy(text, entry->text, entry->cursor);
628 strcpy(text + entry->cursor, entry->preedit.text);
629 strcpy(text + entry->cursor + strlen(entry->preedit.text),
630 entry->text + entry->cursor);
631 } else {
632 text = strdup(entry->text);
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200633 }
634
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100635 if (entry->cursor != entry->anchor) {
636 int start_index = MIN(entry->cursor, entry->anchor);
637 int end_index = MAX(entry->cursor, entry->anchor);
638 PangoAttribute *attr;
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200639
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100640 attr_list = pango_attr_list_copy(entry->preedit.attr_list);
641
642 if (!attr_list)
643 attr_list = pango_attr_list_new();
644
645 attr = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
646 attr->start_index = start_index;
647 attr->end_index = end_index;
648 pango_attr_list_insert(attr_list, attr);
649
650 attr = pango_attr_foreground_new(65535, 65535, 65535);
651 attr->start_index = start_index;
652 attr->end_index = end_index;
653 pango_attr_list_insert(attr_list, attr);
654 } else {
655 attr_list = pango_attr_list_ref(entry->preedit.attr_list);
656 }
657
658 if (entry->preedit.text && !entry->preedit.attr_list) {
659 PangoAttribute *attr;
660
661 if (!attr_list)
662 attr_list = pango_attr_list_new();
663
664 attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
665 attr->start_index = entry->cursor;
666 attr->end_index = entry->cursor + strlen(entry->preedit.text);
667 pango_attr_list_insert(attr_list, attr);
668 }
669
670 if (entry->layout) {
671 pango_layout_set_text(entry->layout, text, -1);
672 pango_layout_set_attributes(entry->layout, attr_list);
673 }
674
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200675 free(text);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100676 pango_attr_list_unref(attr_list);
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200677}
678
679static void
Jan Arne Petersen0558a932013-01-16 21:26:45 +0100680text_entry_update(struct text_entry *entry)
681{
Jan Arne Petersenfe89e712013-04-18 16:47:27 +0200682 struct rectangle cursor_rectangle;
683
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200684 wl_text_input_set_content_type(entry->text_input,
685 WL_TEXT_INPUT_CONTENT_HINT_NONE,
686 entry->content_purpose);
Jan Arne Petersen0558a932013-01-16 21:26:45 +0100687
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200688 wl_text_input_set_surrounding_text(entry->text_input,
689 entry->text,
690 entry->cursor,
691 entry->anchor);
Jan Arne Petersen0eabcaa2013-01-31 15:52:20 +0100692
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200693 if (entry->preferred_language)
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200694 wl_text_input_set_preferred_language(entry->text_input,
695 entry->preferred_language);
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200696
Jan Arne Petersenfe89e712013-04-18 16:47:27 +0200697 text_entry_get_cursor_rectangle(entry, &cursor_rectangle);
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200698 wl_text_input_set_cursor_rectangle(entry->text_input, cursor_rectangle.x, cursor_rectangle.y,
699 cursor_rectangle.width, cursor_rectangle.height);
Jan Arne Petersenfe89e712013-04-18 16:47:27 +0200700
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200701 wl_text_input_commit_state(entry->text_input, ++entry->serial);
Jan Arne Petersen0558a932013-01-16 21:26:45 +0100702}
703
704static void
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100705text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
706 int32_t cursor, int32_t anchor)
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200707{
708 char *new_text = malloc(strlen(entry->text) + strlen(text) + 1);
709
710 strncpy(new_text, entry->text, entry->cursor);
711 strcpy(new_text + entry->cursor, text);
712 strcpy(new_text + entry->cursor + strlen(text),
713 entry->text + entry->cursor);
714
715 free(entry->text);
716 entry->text = new_text;
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100717 if (anchor >= 0)
718 entry->anchor = entry->cursor + strlen(text) + anchor;
719 else
720 entry->anchor = entry->cursor + 1 + anchor;
Jan Arne Petersen68516862013-04-18 16:47:42 +0200721
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100722 if (cursor >= 0)
723 entry->cursor += strlen(text) + cursor;
724 else
725 entry->cursor += 1 + cursor;
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200726
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200727 text_entry_update_layout(entry);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100728
729 widget_schedule_redraw(entry->widget);
730
Jan Arne Petersen0558a932013-01-16 21:26:45 +0100731 text_entry_update(entry);
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200732}
733
734static void
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100735text_entry_reset_preedit(struct text_entry *entry)
736{
737 entry->preedit.cursor = 0;
738
739 free(entry->preedit.text);
740 entry->preedit.text = NULL;
741
742 free(entry->preedit.commit);
743 entry->preedit.commit = NULL;
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100744
745 pango_attr_list_unref(entry->preedit.attr_list);
746 entry->preedit.attr_list = NULL;
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100747}
748
749static void
750text_entry_commit_and_reset(struct text_entry *entry)
751{
752 char *commit = NULL;
753
754 if (entry->preedit.commit)
755 commit = strdup(entry->preedit.commit);
756
757 text_entry_reset_preedit(entry);
758 if (commit) {
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100759 text_entry_insert_at_cursor(entry, commit, 0, 0);
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100760 free(commit);
761 }
Jan Arne Petersen08015b62013-04-18 16:47:18 +0200762
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200763 wl_text_input_reset(entry->text_input);
Jan Arne Petersen00191c72013-04-18 16:47:33 +0200764 text_entry_update(entry);
765 entry->reset_serial = entry->serial;
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100766}
767
768static void
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200769text_entry_set_preedit(struct text_entry *entry,
770 const char *preedit_text,
771 int preedit_cursor)
772{
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100773 text_entry_reset_preedit(entry);
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200774
775 if (!preedit_text)
776 return;
777
Jan Arne Petersen46535312013-01-16 21:26:38 +0100778 entry->preedit.text = strdup(preedit_text);
779 entry->preedit.cursor = preedit_cursor;
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200780
781 text_entry_update_layout(entry);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100782
783 widget_schedule_redraw(entry->widget);
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200784}
785
Jan Arne Petersen3489ba92013-01-16 21:26:47 +0100786static uint32_t
787text_entry_try_invoke_preedit_action(struct text_entry *entry,
788 int32_t x, int32_t y,
789 uint32_t button,
790 enum wl_pointer_button_state state)
791{
792 int index, trailing;
793 uint32_t cursor;
Jan Arne Petersen68516862013-04-18 16:47:42 +0200794 const char *text;
Jan Arne Petersen3489ba92013-01-16 21:26:47 +0100795
796 if (!entry->preedit.text)
797 return 0;
798
799 pango_layout_xy_to_index(entry->layout,
800 x * PANGO_SCALE, y * PANGO_SCALE,
801 &index, &trailing);
Jan Arne Petersen68516862013-04-18 16:47:42 +0200802
803 text = pango_layout_get_text(entry->layout);
804 cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
Jan Arne Petersen3489ba92013-01-16 21:26:47 +0100805
806 if (cursor < entry->cursor ||
807 cursor > entry->cursor + strlen(entry->preedit.text)) {
808 return 0;
809 }
810
811 if (state == WL_POINTER_BUTTON_STATE_RELEASED)
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200812 wl_text_input_invoke_action(entry->text_input,
813 button,
814 cursor - entry->cursor);
Jan Arne Petersen3489ba92013-01-16 21:26:47 +0100815
816 return 1;
817}
818
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +0200819static bool
820text_entry_has_preedit(struct text_entry *entry)
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200821{
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +0200822 return entry->preedit.text && (strlen(entry->preedit.text) > 0);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200823}
824
825static void
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +0200826text_entry_set_cursor_position(struct text_entry *entry,
827 int32_t x, int32_t y,
828 bool move_anchor)
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200829{
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100830 int index, trailing;
Jan Arne Petersen68516862013-04-18 16:47:42 +0200831 const char *text;
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +0200832 uint32_t cursor;
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100833
834 pango_layout_xy_to_index(entry->layout,
835 x * PANGO_SCALE, y * PANGO_SCALE,
836 &index, &trailing);
Jan Arne Petersen68516862013-04-18 16:47:42 +0200837
838 text = pango_layout_get_text(entry->layout);
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +0200839
840 cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
841
842 if (move_anchor)
843 entry->anchor = cursor;
844
845 if (text_entry_has_preedit(entry)) {
846 text_entry_commit_and_reset(entry);
847
848 assert(!text_entry_has_preedit(entry));
849 }
850
851 if (entry->cursor == cursor)
852 return;
853
854 entry->cursor = cursor;
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100855
856 text_entry_update_layout(entry);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200857
858 widget_schedule_redraw(entry->widget);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100859
Jan Arne Petersen0558a932013-01-16 21:26:45 +0100860 text_entry_update(entry);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200861}
862
863static void
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200864text_entry_delete_text(struct text_entry *entry,
865 uint32_t index, uint32_t length)
866{
Jan Arne Petersen68516862013-04-18 16:47:42 +0200867 uint32_t l;
868
Jan Arne Petersen895a1282013-05-30 13:57:04 +0200869 assert(index <= strlen(entry->text));
870 assert(index + length <= strlen(entry->text));
871 assert(index + length >= length);
Jan Arne Petersen80ad1a92012-09-17 15:28:10 +0200872
Jan Arne Petersen68516862013-04-18 16:47:42 +0200873 l = strlen(entry->text + index + length);
874 memmove(entry->text + index,
875 entry->text + index + length,
876 l + 1);
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200877
Jan Arne Petersen9eaa8e52013-05-30 13:57:03 +0200878 if (entry->cursor > (index + length))
879 entry->cursor -= length;
880 else if (entry->cursor > index)
881 entry->cursor = index;
882
883 entry->anchor = entry->cursor;
884
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200885 text_entry_update_layout(entry);
886
887 widget_schedule_redraw(entry->widget);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100888
Jan Arne Petersen0558a932013-01-16 21:26:45 +0100889 text_entry_update(entry);
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200890}
891
892static void
Jan Arne Petersene386dd22012-09-17 15:28:09 +0200893text_entry_delete_selected_text(struct text_entry *entry)
894{
895 uint32_t start_index = entry->anchor < entry->cursor ? entry->anchor : entry->cursor;
896 uint32_t end_index = entry->anchor < entry->cursor ? entry->cursor : entry->anchor;
897
898 if (entry->anchor == entry->cursor)
899 return;
900
901 text_entry_delete_text(entry, start_index, end_index - start_index);
902
903 entry->anchor = entry->cursor;
904}
905
906static void
Jan Arne Petersenfe89e712013-04-18 16:47:27 +0200907text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle)
908{
909 struct rectangle allocation;
910 PangoRectangle extents;
911 PangoRectangle cursor_pos;
912
913 widget_get_allocation(entry->widget, &allocation);
914
915 if (entry->preedit.text && entry->preedit.cursor < 0) {
916 rectangle->x = 0;
917 rectangle->y = 0;
918 rectangle->width = 0;
919 rectangle->height = 0;
920 return;
921 }
922
Jan Arne Petersen68516862013-04-18 16:47:42 +0200923
Jan Arne Petersenfe89e712013-04-18 16:47:27 +0200924 pango_layout_get_extents(entry->layout, &extents, NULL);
925 pango_layout_get_cursor_pos(entry->layout,
926 entry->cursor + entry->preedit.cursor,
927 &cursor_pos, NULL);
928
929 rectangle->x = allocation.x + (allocation.height / 2) + PANGO_PIXELS(cursor_pos.x);
930 rectangle->y = allocation.y + 10 + PANGO_PIXELS(cursor_pos.y);
931 rectangle->width = PANGO_PIXELS(cursor_pos.width);
932 rectangle->height = PANGO_PIXELS(cursor_pos.height);
933}
934
935static void
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200936text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr)
937{
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100938 PangoRectangle extents;
939 PangoRectangle cursor_pos;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200940
Jan Arne Petersen46535312013-01-16 21:26:38 +0100941 if (entry->preedit.text && entry->preedit.cursor < 0)
942 return;
943
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100944 pango_layout_get_extents(entry->layout, &extents, NULL);
945 pango_layout_get_cursor_pos(entry->layout,
946 entry->cursor + entry->preedit.cursor,
947 &cursor_pos, NULL);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200948
949 cairo_set_line_width(cr, 1.0);
Peter Maatmanb9a23f42013-07-06 20:55:54 +0200950 cairo_move_to(cr, PANGO_PIXELS(cursor_pos.x), PANGO_PIXELS(cursor_pos.y));
951 cairo_line_to(cr, PANGO_PIXELS(cursor_pos.x), PANGO_PIXELS(cursor_pos.y) + PANGO_PIXELS(cursor_pos.height));
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200952 cairo_stroke(cr);
953}
954
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +0200955static const int text_offset_left = 10;
956
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200957static void
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200958text_entry_redraw_handler(struct widget *widget, void *data)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200959{
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200960 struct text_entry *entry = data;
961 cairo_surface_t *surface;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200962 struct rectangle allocation;
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200963 cairo_t *cr;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200964
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200965 surface = window_get_surface(entry->window);
966 widget_get_allocation(entry->widget, &allocation);
967
968 cr = cairo_create(surface);
969 cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
970 cairo_clip(cr);
971
972 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
973
974 cairo_push_group(cr);
975 cairo_translate(cr, allocation.x, allocation.y);
976
977 cairo_set_source_rgba(cr, 1, 1, 1, 1);
978 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
979 cairo_fill(cr);
980
981 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
982
983 if (entry->active) {
984 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
985 cairo_set_line_width (cr, 3);
986 cairo_set_source_rgba(cr, 0, 0, 1, 1.0);
987 cairo_stroke(cr);
988 }
989
990 cairo_set_source_rgba(cr, 0, 0, 0, 1);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200991
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +0200992 cairo_translate(cr, text_offset_left, allocation.height / 2);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200993
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100994 if (!entry->layout)
995 entry->layout = pango_cairo_create_layout(cr);
996 else
997 pango_cairo_update_layout(cr, entry->layout);
998
999 text_entry_update_layout(entry);
1000
1001 pango_cairo_show_layout(cr, entry->layout);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001002
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001003 text_entry_draw_cursor(entry, cr);
1004
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001005 cairo_pop_group_to_source(cr);
1006 cairo_paint(cr);
1007
1008 cairo_destroy(cr);
1009 cairo_surface_destroy(surface);
1010}
1011
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001012static int
1013text_entry_motion_handler(struct widget *widget,
1014 struct input *input, uint32_t time,
1015 float x, float y, void *data)
1016{
1017 struct text_entry *entry = data;
1018 struct rectangle allocation;
1019
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001020 if (!entry->button_pressed) {
1021 return CURSOR_IBEAM;
1022 }
1023
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001024 widget_get_allocation(entry->widget, &allocation);
1025
1026 text_entry_set_cursor_position(entry,
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +02001027 x - allocation.x - text_offset_left,
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001028 y - allocation.y - text_offset_left,
1029 false);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001030
1031 return CURSOR_IBEAM;
1032}
1033
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001034static void
1035text_entry_button_handler(struct widget *widget,
1036 struct input *input, uint32_t time,
1037 uint32_t button,
1038 enum wl_pointer_button_state state, void *data)
1039{
1040 struct text_entry *entry = data;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001041 struct rectangle allocation;
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001042 struct editor *editor;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001043 int32_t x, y;
Jan Arne Petersen3489ba92013-01-16 21:26:47 +01001044 uint32_t result;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001045
1046 widget_get_allocation(entry->widget, &allocation);
1047 input_get_position(input, &x, &y);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001048
Jan Arne Petersen3489ba92013-01-16 21:26:47 +01001049 x -= allocation.x + text_offset_left;
1050 y -= allocation.y + text_offset_left;
1051
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001052 editor = window_get_user_data(entry->window);
1053
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001054 if (button == BTN_LEFT) {
1055 entry->button_pressed = (state == WL_POINTER_BUTTON_STATE_PRESSED);
Jan Arne Petersen3489ba92013-01-16 21:26:47 +01001056
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001057 if (state == WL_POINTER_BUTTON_STATE_PRESSED)
1058 input_grab(input, entry->widget, button);
1059 else
1060 input_ungrab(input);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001061 }
1062
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001063 if (text_entry_has_preedit(entry)) {
1064 result = text_entry_try_invoke_preedit_action(entry, x, y, button, state);
1065
1066 if (result)
1067 return;
1068 }
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001069
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001070 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
1071 struct wl_seat *seat = input_get_seat(input);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001072
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001073 text_entry_activate(entry, seat);
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001074 editor->active_entry = entry;
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001075
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001076 text_entry_set_cursor_position(entry, x, y, true);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001077 }
1078}
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001079
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001080static void
1081editor_button_handler(struct widget *widget,
1082 struct input *input, uint32_t time,
1083 uint32_t button,
1084 enum wl_pointer_button_state state, void *data)
1085{
1086 struct editor *editor = data;
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001087
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001088 if (button != BTN_LEFT) {
1089 return;
1090 }
Jan Arne Petersene829adc2012-08-10 16:47:22 +02001091
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001092 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
1093 struct wl_seat *seat = input_get_seat(input);
1094
Jan Arne Petersene829adc2012-08-10 16:47:22 +02001095 text_entry_deactivate(editor->entry, seat);
1096 text_entry_deactivate(editor->editor, seat);
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001097 editor->active_entry = NULL;
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001098 }
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001099}
1100
Kristian Høgsberg78858902014-01-01 23:57:42 -08001101
1102static void
1103keyboard_focus_handler(struct window *window,
1104 struct input *device, void *data)
1105{
1106 struct editor *editor = data;
1107
1108 window_schedule_redraw(editor->window);
1109}
1110
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001111static void
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001112key_handler(struct window *window,
1113 struct input *input, uint32_t time,
1114 uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
1115 void *data)
1116{
1117 struct editor *editor = data;
1118 struct text_entry *entry;
Jan Arne Petersen68516862013-04-18 16:47:42 +02001119 const char *new_char;
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001120 char text[16];
1121
1122 if (!editor->active_entry)
1123 return;
1124
1125 entry = editor->active_entry;
1126
1127 if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
1128 return;
1129
1130 switch (sym) {
1131 case XKB_KEY_BackSpace:
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +01001132 text_entry_commit_and_reset(entry);
1133
Jan Arne Petersen68516862013-04-18 16:47:42 +02001134 new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
1135 if (new_char != NULL)
1136 text_entry_delete_text(entry,
1137 new_char - entry->text,
1138 (entry->text + entry->cursor) - new_char);
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001139 break;
1140 case XKB_KEY_Delete:
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +01001141 text_entry_commit_and_reset(entry);
1142
Jan Arne Petersen68516862013-04-18 16:47:42 +02001143 new_char = utf8_next_char(entry->text + entry->cursor);
1144 if (new_char != NULL)
1145 text_entry_delete_text(entry,
1146 entry->cursor,
1147 new_char - (entry->text + entry->cursor));
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001148 break;
1149 case XKB_KEY_Left:
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +01001150 text_entry_commit_and_reset(entry);
1151
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001152 new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
1153 if (new_char != NULL) {
1154 entry->cursor = new_char - entry->text;
Rob Bradford70002832013-07-11 16:00:00 +01001155 if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1156 entry->anchor = entry->cursor;
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001157 widget_schedule_redraw(entry->widget);
1158 }
1159 break;
1160 case XKB_KEY_Right:
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +01001161 text_entry_commit_and_reset(entry);
1162
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001163 new_char = utf8_next_char(entry->text + entry->cursor);
1164 if (new_char != NULL) {
1165 entry->cursor = new_char - entry->text;
Rob Bradford70002832013-07-11 16:00:00 +01001166 if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1167 entry->anchor = entry->cursor;
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001168 widget_schedule_redraw(entry->widget);
1169 }
1170 break;
Peter Maatman08c38d42013-07-06 20:42:59 +02001171 case XKB_KEY_Escape:
1172 break;
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001173 default:
1174 if (xkb_keysym_to_utf8(sym, text, sizeof(text)) <= 0)
1175 break;
1176
Peter Maatman08c38d42013-07-06 20:42:59 +02001177 text_entry_commit_and_reset(entry);
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +01001178
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +01001179 text_entry_insert_at_cursor(entry, text, 0, 0);
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001180 break;
1181 }
1182
1183 widget_schedule_redraw(entry->widget);
1184}
1185
1186static void
Kristian Høgsbergfa80e112012-10-10 21:34:26 -04001187global_handler(struct display *display, uint32_t name,
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001188 const char *interface, uint32_t version, void *data)
1189{
1190 struct editor *editor = data;
1191
Jan Arne Petersen62ece762013-04-18 16:47:36 +02001192 if (!strcmp(interface, "wl_text_input_manager")) {
Jan Arne Petersen78d00e42013-04-18 16:47:24 +02001193 editor->text_input_manager =
Kristian Høgsbergfa80e112012-10-10 21:34:26 -04001194 display_bind(display, name,
Jan Arne Petersen62ece762013-04-18 16:47:36 +02001195 &wl_text_input_manager_interface, 1);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001196 }
1197}
1198
1199int
1200main(int argc, char *argv[])
1201{
1202 struct editor editor;
Jan Arne Petersen61381972013-01-31 15:52:21 +01001203 int i;
1204 uint32_t click_to_show = 0;
Jan Arne Petersen9d419132013-04-18 16:47:16 +02001205 const char *preferred_language = NULL;
Jan Arne Petersen61381972013-01-31 15:52:21 +01001206
Jan Arne Petersen9d419132013-04-18 16:47:16 +02001207 for (i = 1; i < argc; i++) {
Jan Arne Petersen61381972013-01-31 15:52:21 +01001208 if (strcmp("--click-to-show", argv[i]) == 0)
1209 click_to_show = 1;
Jan Arne Petersen9d419132013-04-18 16:47:16 +02001210 else if (strcmp("--preferred-language", argv[i]) == 0) {
1211 if (i + 1 < argc) {
1212 preferred_language = argv[i + 1];
1213 i++;
1214 }
1215 }
1216 }
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001217
Jan Arne Petersen25f6db52012-11-05 03:26:40 +01001218 memset(&editor, 0, sizeof editor);
1219
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +01001220#ifdef HAVE_PANGO
1221 g_type_init();
1222#endif
1223
Kristian Høgsberg4172f662013-02-20 15:27:49 -05001224 editor.display = display_create(&argc, argv);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001225 if (editor.display == NULL) {
1226 fprintf(stderr, "failed to create display: %m\n");
1227 return -1;
1228 }
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001229
Kristian Høgsbergfa80e112012-10-10 21:34:26 -04001230 display_set_user_data(editor.display, &editor);
1231 display_set_global_handler(editor.display, global_handler);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001232
1233 editor.window = window_create(editor.display);
Jason Ekstrandee7fefc2013-10-13 19:08:38 -05001234 editor.widget = window_frame_create(editor.window, &editor);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001235
1236 editor.entry = text_entry_create(&editor, "Entry");
Jan Arne Petersen61381972013-01-31 15:52:21 +01001237 editor.entry->click_to_show = click_to_show;
Jan Arne Petersen9d419132013-04-18 16:47:16 +02001238 if (preferred_language)
1239 editor.entry->preferred_language = strdup(preferred_language);
Jan Arne Petersen0558a932013-01-16 21:26:45 +01001240 editor.editor = text_entry_create(&editor, "Numeric");
Jan Arne Petersen62ece762013-04-18 16:47:36 +02001241 editor.editor->content_purpose = WL_TEXT_INPUT_CONTENT_PURPOSE_NUMBER;
Jan Arne Petersen61381972013-01-31 15:52:21 +01001242 editor.editor->click_to_show = click_to_show;
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001243
1244 window_set_title(editor.window, "Text Editor");
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001245 window_set_key_handler(editor.window, key_handler);
Kristian Høgsberg78858902014-01-01 23:57:42 -08001246 window_set_keyboard_focus_handler(editor.window,
1247 keyboard_focus_handler);
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001248 window_set_user_data(editor.window, &editor);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001249
1250 widget_set_redraw_handler(editor.widget, redraw_handler);
1251 widget_set_resize_handler(editor.widget, resize_handler);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001252 widget_set_button_handler(editor.widget, editor_button_handler);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001253
1254 window_schedule_resize(editor.window, 500, 400);
1255
1256 display_run(editor.display);
1257
1258 text_entry_destroy(editor.entry);
1259 text_entry_destroy(editor.editor);
1260
1261 return 0;
1262}