1 /*
2 * Copyright © 2012 Openismus GmbH
3 * Copyright © 2012 Intel Corporation
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25 #include "config.h"
26
27 #include <assert.h>
28 #include <errno.h>
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdbool.h>
34 #include <unistd.h>
35
36 #include <linux/input.h>
37 #include <cairo.h>
38
39 #include <pango/pangocairo.h>
40
41 #include <libweston/config-parser.h>
42 #include "shared/helpers.h"
43 #include "shared/xalloc.h"
44 #include "window.h"
45 #include "text-input-unstable-v1-client-protocol.h"
46
47 struct text_entry {
48 struct widget *widget;
49 struct window *window;
50 char *text;
51 int active;
52 bool panel_visible;
53 uint32_t cursor;
54 uint32_t anchor;
55 struct {
56 char *text;
57 int32_t cursor;
58 char *commit;
59 PangoAttrList *attr_list;
60 } preedit;
61 struct {
62 PangoAttrList *attr_list;
63 int32_t cursor;
64 } preedit_info;
65 struct {
66 int32_t cursor;
67 int32_t anchor;
68 uint32_t delete_index;
69 uint32_t delete_length;
70 bool invalid_delete;
71 } pending_commit;
72 struct zwp_text_input_v1 *text_input;
73 PangoLayout *layout;
74 struct {
75 xkb_mod_mask_t shift_mask;
76 } keysym;
77 uint32_t serial;
78 uint32_t reset_serial;
79 uint32_t content_purpose;
80 bool click_to_show;
81 char *preferred_language;
82 bool button_pressed;
83 };
84
85 struct editor {
86 struct zwp_text_input_manager_v1 *text_input_manager;
87 struct wl_data_source *selection;
88 char *selected_text;
89 struct display *display;
90 struct window *window;
91 struct widget *widget;
92 struct text_entry *entry;
93 struct text_entry *editor;
94 struct text_entry *active_entry;
95 };
96
97 static const char *
utf8_end_char(const char * p)98 utf8_end_char(const char *p)
99 {
100 while ((*p & 0xc0) == 0x80)
101 p++;
102 return p;
103 }
104
105 static const char *
utf8_prev_char(const char * s,const char * p)106 utf8_prev_char(const char *s, const char *p)
107 {
108 for (--p; p >= s; --p) {
109 if ((*p & 0xc0) != 0x80)
110 return p;
111 }
112 return NULL;
113 }
114
115 static const char *
utf8_next_char(const char * p)116 utf8_next_char(const char *p)
117 {
118 if (*p != 0)
119 return utf8_end_char(++p);
120 return NULL;
121 }
122
123 static void
move_up(const char * p,uint32_t * cursor)124 move_up(const char *p, uint32_t *cursor)
125 {
126 const char *posr, *posr_i;
127 char text[16];
128
129 xkb_keysym_to_utf8(XKB_KEY_Return, text, sizeof(text));
130
131 posr = strstr(p, text);
132 while (posr) {
133 if (*cursor > (unsigned)(posr-p)) {
134 posr_i = strstr(posr+1, text);
135 if (!posr_i || !(*cursor > (unsigned)(posr_i-p))) {
136 *cursor = posr-p;
137 break;
138 }
139 posr = posr_i;
140 } else {
141 break;
142 }
143 }
144 }
145
146 static void
move_down(const char * p,uint32_t * cursor)147 move_down(const char *p, uint32_t *cursor)
148 {
149 const char *posr;
150 char text[16];
151
152 xkb_keysym_to_utf8(XKB_KEY_Return, text, sizeof(text));
153
154 posr = strstr(p, text);
155 while (posr) {
156 if (*cursor <= (unsigned)(posr-p)) {
157 *cursor = posr-p + 1;
158 break;
159 }
160 posr = strstr(posr+1, text);
161 }
162 }
163
164 static void text_entry_redraw_handler(struct widget *widget, void *data);
165 static void text_entry_button_handler(struct widget *widget,
166 struct input *input, uint32_t time,
167 uint32_t button,
168 enum wl_pointer_button_state state, void *data);
169 static void text_entry_touch_handler(struct widget *widget, struct input *input,
170 uint32_t serial, uint32_t time, int32_t id,
171 float tx, float ty, void *data);
172 static int text_entry_motion_handler(struct widget *widget,
173 struct input *input, uint32_t time,
174 float x, float y, void *data);
175 static void text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
176 int32_t cursor, int32_t anchor);
177 static void text_entry_set_preedit(struct text_entry *entry,
178 const char *preedit_text,
179 int preedit_cursor);
180 static void text_entry_delete_text(struct text_entry *entry,
181 uint32_t index, uint32_t length);
182 static void text_entry_delete_selected_text(struct text_entry *entry);
183 static void text_entry_reset_preedit(struct text_entry *entry);
184 static void text_entry_commit_and_reset(struct text_entry *entry);
185 static void text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle);
186 static void text_entry_update(struct text_entry *entry);
187
188 static void
text_input_commit_string(void * data,struct zwp_text_input_v1 * text_input,uint32_t serial,const char * text)189 text_input_commit_string(void *data,
190 struct zwp_text_input_v1 *text_input,
191 uint32_t serial,
192 const char *text)
193 {
194 struct text_entry *entry = data;
195
196 if ((entry->serial - serial) > (entry->serial - entry->reset_serial)) {
197 fprintf(stderr, "Ignore commit. Serial: %u, Current: %u, Reset: %u\n",
198 serial, entry->serial, entry->reset_serial);
199 return;
200 }
201
202 if (entry->pending_commit.invalid_delete) {
203 fprintf(stderr, "Ignore commit. Invalid previous delete_surrounding event.\n");
204 memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
205 return;
206 }
207
208 text_entry_reset_preedit(entry);
209
210 if (entry->pending_commit.delete_length) {
211 text_entry_delete_text(entry,
212 entry->pending_commit.delete_index,
213 entry->pending_commit.delete_length);
214 } else {
215 text_entry_delete_selected_text(entry);
216 }
217
218 text_entry_insert_at_cursor(entry, text,
219 entry->pending_commit.cursor,
220 entry->pending_commit.anchor);
221
222 memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
223
224 widget_schedule_redraw(entry->widget);
225 }
226
227 static void
clear_pending_preedit(struct text_entry * entry)228 clear_pending_preedit(struct text_entry *entry)
229 {
230 memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
231
232 pango_attr_list_unref(entry->preedit_info.attr_list);
233
234 entry->preedit_info.cursor = 0;
235 entry->preedit_info.attr_list = NULL;
236
237 memset(&entry->preedit_info, 0, sizeof entry->preedit_info);
238 }
239
240 static void
text_input_preedit_string(void * data,struct zwp_text_input_v1 * text_input,uint32_t serial,const char * text,const char * commit)241 text_input_preedit_string(void *data,
242 struct zwp_text_input_v1 *text_input,
243 uint32_t serial,
244 const char *text,
245 const char *commit)
246 {
247 struct text_entry *entry = data;
248
249 if ((entry->serial - serial) > (entry->serial - entry->reset_serial)) {
250 fprintf(stderr, "Ignore preedit_string. Serial: %u, Current: %u, Reset: %u\n",
251 serial, entry->serial, entry->reset_serial);
252 clear_pending_preedit(entry);
253 return;
254 }
255
256 if (entry->pending_commit.invalid_delete) {
257 fprintf(stderr, "Ignore preedit_string. Invalid previous delete_surrounding event.\n");
258 clear_pending_preedit(entry);
259 return;
260 }
261
262 if (entry->pending_commit.delete_length) {
263 text_entry_delete_text(entry,
264 entry->pending_commit.delete_index,
265 entry->pending_commit.delete_length);
266 } else {
267 text_entry_delete_selected_text(entry);
268 }
269
270 text_entry_set_preedit(entry, text, entry->preedit_info.cursor);
271 entry->preedit.commit = strdup(commit);
272 entry->preedit.attr_list = pango_attr_list_ref(entry->preedit_info.attr_list);
273
274 clear_pending_preedit(entry);
275
276 text_entry_update(entry);
277
278 widget_schedule_redraw(entry->widget);
279 }
280
281 static void
text_input_delete_surrounding_text(void * data,struct zwp_text_input_v1 * text_input,int32_t index,uint32_t length)282 text_input_delete_surrounding_text(void *data,
283 struct zwp_text_input_v1 *text_input,
284 int32_t index,
285 uint32_t length)
286 {
287 struct text_entry *entry = data;
288 uint32_t text_length;
289
290 entry->pending_commit.delete_index = entry->cursor + index;
291 entry->pending_commit.delete_length = length;
292 entry->pending_commit.invalid_delete = false;
293
294 text_length = strlen(entry->text);
295
296 if (entry->pending_commit.delete_index > text_length ||
297 length > text_length ||
298 entry->pending_commit.delete_index + length > text_length) {
299 fprintf(stderr, "delete_surrounding_text: Invalid index: %d," \
300 "length %u'; cursor: %u text length: %u\n", index, length, entry->cursor, text_length);
301 entry->pending_commit.invalid_delete = true;
302 return;
303 }
304 }
305
306 static void
text_input_cursor_position(void * data,struct zwp_text_input_v1 * text_input,int32_t index,int32_t anchor)307 text_input_cursor_position(void *data,
308 struct zwp_text_input_v1 *text_input,
309 int32_t index,
310 int32_t anchor)
311 {
312 struct text_entry *entry = data;
313
314 entry->pending_commit.cursor = index;
315 entry->pending_commit.anchor = anchor;
316 }
317
318 static void
text_input_preedit_styling(void * data,struct zwp_text_input_v1 * text_input,uint32_t index,uint32_t length,uint32_t style)319 text_input_preedit_styling(void *data,
320 struct zwp_text_input_v1 *text_input,
321 uint32_t index,
322 uint32_t length,
323 uint32_t style)
324 {
325 struct text_entry *entry = data;
326 PangoAttribute *attr1 = NULL;
327 PangoAttribute *attr2 = NULL;
328
329 if (!entry->preedit_info.attr_list)
330 entry->preedit_info.attr_list = pango_attr_list_new();
331
332 switch (style) {
333 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_DEFAULT:
334 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_UNDERLINE:
335 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
336 break;
337 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_INCORRECT:
338 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_ERROR);
339 attr2 = pango_attr_underline_color_new(65535, 0, 0);
340 break;
341 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_SELECTION:
342 attr1 = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
343 attr2 = pango_attr_foreground_new(65535, 65535, 65535);
344 break;
345 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT:
346 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_ACTIVE:
347 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
348 attr2 = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
349 break;
350 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_INACTIVE:
351 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
352 attr2 = pango_attr_foreground_new(0.3 * 65535, 0.3 * 65535, 0.3 * 65535);
353 break;
354 }
355
356 if (attr1) {
357 attr1->start_index = entry->cursor + index;
358 attr1->end_index = entry->cursor + index + length;
359 pango_attr_list_insert(entry->preedit_info.attr_list, attr1);
360 }
361
362 if (attr2) {
363 attr2->start_index = entry->cursor + index;
364 attr2->end_index = entry->cursor + index + length;
365 pango_attr_list_insert(entry->preedit_info.attr_list, attr2);
366 }
367 }
368
369 static void
text_input_preedit_cursor(void * data,struct zwp_text_input_v1 * text_input,int32_t index)370 text_input_preedit_cursor(void *data,
371 struct zwp_text_input_v1 *text_input,
372 int32_t index)
373 {
374 struct text_entry *entry = data;
375
376 entry->preedit_info.cursor = index;
377 }
378
379 static void
text_input_modifiers_map(void * data,struct zwp_text_input_v1 * text_input,struct wl_array * map)380 text_input_modifiers_map(void *data,
381 struct zwp_text_input_v1 *text_input,
382 struct wl_array *map)
383 {
384 struct text_entry *entry = data;
385
386 entry->keysym.shift_mask = keysym_modifiers_get_mask(map, "Shift");
387 }
388
389 static void
text_input_keysym(void * data,struct zwp_text_input_v1 * text_input,uint32_t serial,uint32_t time,uint32_t key,uint32_t state,uint32_t modifiers)390 text_input_keysym(void *data,
391 struct zwp_text_input_v1 *text_input,
392 uint32_t serial,
393 uint32_t time,
394 uint32_t key,
395 uint32_t state,
396 uint32_t modifiers)
397 {
398 struct text_entry *entry = data;
399 const char *new_char;
400
401 if (key == XKB_KEY_Left ||
402 key == XKB_KEY_Right) {
403 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
404 return;
405
406 if (key == XKB_KEY_Left)
407 new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
408 else
409 new_char = utf8_next_char(entry->text + entry->cursor);
410
411 if (new_char != NULL) {
412 entry->cursor = new_char - entry->text;
413 }
414
415 if (!(modifiers & entry->keysym.shift_mask))
416 entry->anchor = entry->cursor;
417 widget_schedule_redraw(entry->widget);
418
419 return;
420 }
421
422 if (key == XKB_KEY_Up ||
423 key == XKB_KEY_Down) {
424 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
425 return;
426
427 if (key == XKB_KEY_Up)
428 move_up(entry->text, &entry->cursor);
429 else
430 move_down(entry->text, &entry->cursor);
431
432 if (!(modifiers & entry->keysym.shift_mask))
433 entry->anchor = entry->cursor;
434 widget_schedule_redraw(entry->widget);
435
436 return;
437 }
438
439 if (key == XKB_KEY_BackSpace) {
440 const char *start, *end;
441
442 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
443 return;
444
445 text_entry_commit_and_reset(entry);
446
447 start = utf8_prev_char(entry->text, entry->text + entry->cursor);
448 if (start == NULL)
449 return;
450
451 end = utf8_next_char(start);
452
453 text_entry_delete_text(entry,
454 start - entry->text,
455 end - start);
456
457 return;
458 }
459
460 if (key == XKB_KEY_Tab ||
461 key == XKB_KEY_KP_Enter ||
462 key == XKB_KEY_Return) {
463 char text[16];
464
465 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
466 return;
467
468 xkb_keysym_to_utf8(key, text, sizeof(text));
469
470 text_entry_insert_at_cursor(entry, text, 0, 0);
471
472 return;
473 }
474 }
475
476 static void
text_input_enter(void * data,struct zwp_text_input_v1 * text_input,struct wl_surface * surface)477 text_input_enter(void *data,
478 struct zwp_text_input_v1 *text_input,
479 struct wl_surface *surface)
480 {
481 struct text_entry *entry = data;
482
483 if (surface != window_get_wl_surface(entry->window))
484 return;
485
486 entry->active++;
487
488 text_entry_update(entry);
489 entry->reset_serial = entry->serial;
490
491 widget_schedule_redraw(entry->widget);
492 }
493
494 static void
text_input_leave(void * data,struct zwp_text_input_v1 * text_input)495 text_input_leave(void *data,
496 struct zwp_text_input_v1 *text_input)
497 {
498 struct text_entry *entry = data;
499
500 text_entry_commit_and_reset(entry);
501 entry->active--;
502
503 if (!entry->active) {
504 zwp_text_input_v1_hide_input_panel(text_input);
505 entry->panel_visible = false;
506 }
507
508 widget_schedule_redraw(entry->widget);
509 }
510
511 static void
text_input_input_panel_state(void * data,struct zwp_text_input_v1 * text_input,uint32_t state)512 text_input_input_panel_state(void *data,
513 struct zwp_text_input_v1 *text_input,
514 uint32_t state)
515 {
516 }
517
518 static void
text_input_language(void * data,struct zwp_text_input_v1 * text_input,uint32_t serial,const char * language)519 text_input_language(void *data,
520 struct zwp_text_input_v1 *text_input,
521 uint32_t serial,
522 const char *language)
523 {
524 fprintf(stderr, "input language is %s \n", language);
525 }
526
527 static void
text_input_text_direction(void * data,struct zwp_text_input_v1 * text_input,uint32_t serial,uint32_t direction)528 text_input_text_direction(void *data,
529 struct zwp_text_input_v1 *text_input,
530 uint32_t serial,
531 uint32_t direction)
532 {
533 struct text_entry *entry = data;
534 PangoContext *context = pango_layout_get_context(entry->layout);
535 PangoDirection pango_direction;
536
537
538 switch (direction) {
539 case ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_LTR:
540 pango_direction = PANGO_DIRECTION_LTR;
541 break;
542 case ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_RTL:
543 pango_direction = PANGO_DIRECTION_RTL;
544 break;
545 case ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_AUTO:
546 default:
547 pango_direction = PANGO_DIRECTION_NEUTRAL;
548 }
549
550 pango_context_set_base_dir(context, pango_direction);
551 }
552
553 static const struct zwp_text_input_v1_listener text_input_listener = {
554 text_input_enter,
555 text_input_leave,
556 text_input_modifiers_map,
557 text_input_input_panel_state,
558 text_input_preedit_string,
559 text_input_preedit_styling,
560 text_input_preedit_cursor,
561 text_input_commit_string,
562 text_input_cursor_position,
563 text_input_delete_surrounding_text,
564 text_input_keysym,
565 text_input_language,
566 text_input_text_direction
567 };
568
569 static void
data_source_target(void * data,struct wl_data_source * source,const char * mime_type)570 data_source_target(void *data,
571 struct wl_data_source *source, const char *mime_type)
572 {
573 }
574
575 static void
data_source_send(void * data,struct wl_data_source * source,const char * mime_type,int32_t fd)576 data_source_send(void *data,
577 struct wl_data_source *source,
578 const char *mime_type, int32_t fd)
579 {
580 struct editor *editor = data;
581
582 if (write(fd, editor->selected_text, strlen(editor->selected_text) + 1) < 0)
583 fprintf(stderr, "write failed: %s\n", strerror(errno));
584
585 close(fd);
586 }
587
588 static void
data_source_cancelled(void * data,struct wl_data_source * source)589 data_source_cancelled(void *data, struct wl_data_source *source)
590 {
591 wl_data_source_destroy(source);
592 }
593
594 static const struct wl_data_source_listener data_source_listener = {
595 data_source_target,
596 data_source_send,
597 data_source_cancelled
598 };
599
600 static void
paste_func(void * buffer,size_t len,int32_t x,int32_t y,void * data)601 paste_func(void *buffer, size_t len,
602 int32_t x, int32_t y, void *data)
603 {
604 struct editor *editor = data;
605 struct text_entry *entry = editor->active_entry;
606 char *pasted_text;
607
608 if (!entry)
609 return;
610
611 pasted_text = malloc(len + 1);
612 strncpy(pasted_text, buffer, len);
613 pasted_text[len] = '\0';
614
615 text_entry_insert_at_cursor(entry, pasted_text, 0, 0);
616
617 free(pasted_text);
618 }
619
620 static void
editor_copy_cut(struct editor * editor,struct input * input,bool cut)621 editor_copy_cut(struct editor *editor, struct input *input, bool cut)
622 {
623 struct text_entry *entry = editor->active_entry;
624
625 if (!entry)
626 return;
627
628 if (entry->cursor != entry->anchor) {
629 int start_index = MIN(entry->cursor, entry->anchor);
630 int end_index = MAX(entry->cursor, entry->anchor);
631 int len = end_index - start_index;
632
633 editor->selected_text = realloc(editor->selected_text, len + 1);
634 strncpy(editor->selected_text, &entry->text[start_index], len);
635 editor->selected_text[len] = '\0';
636
637 if (cut)
638 text_entry_delete_text(entry, start_index, len);
639
640 editor->selection =
641 display_create_data_source(editor->display);
642 if (!editor->selection)
643 return;
644
645 wl_data_source_offer(editor->selection,
646 "text/plain;charset=utf-8");
647 wl_data_source_add_listener(editor->selection,
648 &data_source_listener, editor);
649 input_set_selection(input, editor->selection,
650 display_get_serial(editor->display));
651 }
652 }
653
654 static void
editor_paste(struct editor * editor,struct input * input)655 editor_paste(struct editor *editor, struct input *input)
656 {
657 input_receive_selection_data(input,
658 "text/plain;charset=utf-8",
659 paste_func, editor);
660 }
661
662 static void
menu_func(void * data,struct input * input,int index)663 menu_func(void *data, struct input *input, int index)
664 {
665 struct window *window = data;
666 struct editor *editor = window_get_user_data(window);
667
668 fprintf(stderr, "picked entry %d\n", index);
669
670 switch (index) {
671 case 0:
672 editor_copy_cut(editor, input, true);
673 break;
674 case 1:
675 editor_copy_cut(editor, input, false);
676 break;
677 case 2:
678 editor_paste(editor, input);
679 break;
680 }
681 }
682
683 static void
show_menu(struct editor * editor,struct input * input,uint32_t time)684 show_menu(struct editor *editor, struct input *input, uint32_t time)
685 {
686 int32_t x, y;
687 static const char *entries[] = {
688 "Cut", "Copy", "Paste"
689 };
690
691 input_get_position(input, &x, &y);
692 window_show_menu(editor->display, input, time, editor->window,
693 x + 10, y + 20, menu_func,
694 entries, ARRAY_LENGTH(entries));
695 }
696
697 static struct text_entry*
text_entry_create(struct editor * editor,const char * text)698 text_entry_create(struct editor *editor, const char *text)
699 {
700 struct text_entry *entry;
701
702 entry = xzalloc(sizeof *entry);
703
704 entry->widget = widget_add_widget(editor->widget, entry);
705 entry->window = editor->window;
706 entry->text = strdup(text);
707 entry->active = 0;
708 entry->panel_visible = false;
709 entry->cursor = strlen(text);
710 entry->anchor = entry->cursor;
711 entry->text_input =
712 zwp_text_input_manager_v1_create_text_input(editor->text_input_manager);
713 zwp_text_input_v1_add_listener(entry->text_input,
714 &text_input_listener, entry);
715
716 widget_set_redraw_handler(entry->widget, text_entry_redraw_handler);
717 widget_set_button_handler(entry->widget, text_entry_button_handler);
718 widget_set_motion_handler(entry->widget, text_entry_motion_handler);
719 widget_set_touch_down_handler(entry->widget, text_entry_touch_handler);
720
721 return entry;
722 }
723
724 static void
text_entry_destroy(struct text_entry * entry)725 text_entry_destroy(struct text_entry *entry)
726 {
727 widget_destroy(entry->widget);
728 zwp_text_input_v1_destroy(entry->text_input);
729 g_clear_object(&entry->layout);
730 free(entry->text);
731 free(entry->preferred_language);
732 free(entry);
733 }
734
735 static void
redraw_handler(struct widget * widget,void * data)736 redraw_handler(struct widget *widget, void *data)
737 {
738 struct editor *editor = data;
739 cairo_surface_t *surface;
740 struct rectangle allocation;
741 cairo_t *cr;
742
743 surface = window_get_surface(editor->window);
744 widget_get_allocation(editor->widget, &allocation);
745
746 cr = cairo_create(surface);
747 cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
748 cairo_clip(cr);
749
750 cairo_translate(cr, allocation.x, allocation.y);
751
752 /* Draw background */
753 cairo_push_group(cr);
754 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
755 cairo_set_source_rgba(cr, 1, 1, 1, 1);
756 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
757 cairo_fill(cr);
758
759 cairo_pop_group_to_source(cr);
760 cairo_paint(cr);
761
762 cairo_destroy(cr);
763 cairo_surface_destroy(surface);
764 }
765
766 static void
text_entry_allocate(struct text_entry * entry,int32_t x,int32_t y,int32_t width,int32_t height)767 text_entry_allocate(struct text_entry *entry, int32_t x, int32_t y,
768 int32_t width, int32_t height)
769 {
770 widget_set_allocation(entry->widget, x, y, width, height);
771 }
772
773 static void
resize_handler(struct widget * widget,int32_t width,int32_t height,void * data)774 resize_handler(struct widget *widget,
775 int32_t width, int32_t height, void *data)
776 {
777 struct editor *editor = data;
778 struct rectangle allocation;
779
780 widget_get_allocation(editor->widget, &allocation);
781
782 text_entry_allocate(editor->entry,
783 allocation.x + 20, allocation.y + 20,
784 width - 40, height / 2 - 40);
785 text_entry_allocate(editor->editor,
786 allocation.x + 20, allocation.y + height / 2 + 20,
787 width - 40, height / 2 - 40);
788 }
789
790 static void
text_entry_activate(struct text_entry * entry,struct wl_seat * seat)791 text_entry_activate(struct text_entry *entry,
792 struct wl_seat *seat)
793 {
794 struct wl_surface *surface = window_get_wl_surface(entry->window);
795
796 if (entry->click_to_show && entry->active) {
797 entry->panel_visible = !entry->panel_visible;
798
799 if (entry->panel_visible)
800 zwp_text_input_v1_show_input_panel(entry->text_input);
801 else
802 zwp_text_input_v1_hide_input_panel(entry->text_input);
803
804 return;
805 }
806
807 if (!entry->click_to_show)
808 zwp_text_input_v1_show_input_panel(entry->text_input);
809
810 zwp_text_input_v1_activate(entry->text_input,
811 seat,
812 surface);
813 }
814
815 static void
text_entry_deactivate(struct text_entry * entry,struct wl_seat * seat)816 text_entry_deactivate(struct text_entry *entry,
817 struct wl_seat *seat)
818 {
819 zwp_text_input_v1_deactivate(entry->text_input,
820 seat);
821 }
822
823 static void
text_entry_update_layout(struct text_entry * entry)824 text_entry_update_layout(struct text_entry *entry)
825 {
826 char *text;
827 PangoAttrList *attr_list;
828
829 assert(entry->cursor <= (strlen(entry->text) +
830 (entry->preedit.text ? strlen(entry->preedit.text) : 0)));
831
832 if (entry->preedit.text) {
833 text = xmalloc(strlen(entry->text) + strlen(entry->preedit.text) + 1);
834 strncpy(text, entry->text, entry->cursor);
835 strcpy(text + entry->cursor, entry->preedit.text);
836 strcpy(text + entry->cursor + strlen(entry->preedit.text),
837 entry->text + entry->cursor);
838 } else {
839 text = strdup(entry->text);
840 }
841
842 if (entry->cursor != entry->anchor) {
843 int start_index = MIN(entry->cursor, entry->anchor);
844 int end_index = MAX(entry->cursor, entry->anchor);
845 PangoAttribute *attr;
846
847 attr_list = pango_attr_list_copy(entry->preedit.attr_list);
848
849 if (!attr_list)
850 attr_list = pango_attr_list_new();
851
852 attr = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
853 attr->start_index = start_index;
854 attr->end_index = end_index;
855 pango_attr_list_insert(attr_list, attr);
856
857 attr = pango_attr_foreground_new(65535, 65535, 65535);
858 attr->start_index = start_index;
859 attr->end_index = end_index;
860 pango_attr_list_insert(attr_list, attr);
861 } else {
862 attr_list = pango_attr_list_ref(entry->preedit.attr_list);
863 }
864
865 if (entry->preedit.text && !entry->preedit.attr_list) {
866 PangoAttribute *attr;
867
868 if (!attr_list)
869 attr_list = pango_attr_list_new();
870
871 attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
872 attr->start_index = entry->cursor;
873 attr->end_index = entry->cursor + strlen(entry->preedit.text);
874 pango_attr_list_insert(attr_list, attr);
875 }
876
877 if (entry->layout) {
878 pango_layout_set_text(entry->layout, text, -1);
879 pango_layout_set_attributes(entry->layout, attr_list);
880 }
881
882 free(text);
883 pango_attr_list_unref(attr_list);
884 }
885
886 static void
text_entry_update(struct text_entry * entry)887 text_entry_update(struct text_entry *entry)
888 {
889 struct rectangle cursor_rectangle;
890
891 zwp_text_input_v1_set_content_type(entry->text_input,
892 ZWP_TEXT_INPUT_V1_CONTENT_HINT_NONE,
893 entry->content_purpose);
894
895 zwp_text_input_v1_set_surrounding_text(entry->text_input,
896 entry->text,
897 entry->cursor,
898 entry->anchor);
899
900 if (entry->preferred_language)
901 zwp_text_input_v1_set_preferred_language(entry->text_input,
902 entry->preferred_language);
903
904 text_entry_get_cursor_rectangle(entry, &cursor_rectangle);
905 zwp_text_input_v1_set_cursor_rectangle(entry->text_input,
906 cursor_rectangle.x,
907 cursor_rectangle.y,
908 cursor_rectangle.width,
909 cursor_rectangle.height);
910
911 zwp_text_input_v1_commit_state(entry->text_input, ++entry->serial);
912 }
913
914 static void
text_entry_insert_at_cursor(struct text_entry * entry,const char * text,int32_t cursor,int32_t anchor)915 text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
916 int32_t cursor, int32_t anchor)
917 {
918 char *new_text = xmalloc(strlen(entry->text) + strlen(text) + 1);
919
920 strncpy(new_text, entry->text, entry->cursor);
921 strcpy(new_text + entry->cursor, text);
922 strcpy(new_text + entry->cursor + strlen(text),
923 entry->text + entry->cursor);
924
925 free(entry->text);
926 entry->text = new_text;
927 if (anchor >= 0)
928 entry->anchor = entry->cursor + strlen(text) + anchor;
929 else
930 entry->anchor = entry->cursor + 1 + anchor;
931
932 if (cursor >= 0)
933 entry->cursor += strlen(text) + cursor;
934 else
935 entry->cursor += 1 + cursor;
936
937 text_entry_update_layout(entry);
938
939 widget_schedule_redraw(entry->widget);
940
941 text_entry_update(entry);
942 }
943
944 static void
text_entry_reset_preedit(struct text_entry * entry)945 text_entry_reset_preedit(struct text_entry *entry)
946 {
947 entry->preedit.cursor = 0;
948
949 free(entry->preedit.text);
950 entry->preedit.text = NULL;
951
952 free(entry->preedit.commit);
953 entry->preedit.commit = NULL;
954
955 pango_attr_list_unref(entry->preedit.attr_list);
956 entry->preedit.attr_list = NULL;
957 }
958
959 static void
text_entry_commit_and_reset(struct text_entry * entry)960 text_entry_commit_and_reset(struct text_entry *entry)
961 {
962 char *commit = NULL;
963
964 if (entry->preedit.commit)
965 commit = strdup(entry->preedit.commit);
966
967 text_entry_reset_preedit(entry);
968 if (commit) {
969 text_entry_insert_at_cursor(entry, commit, 0, 0);
970 free(commit);
971 }
972
973 zwp_text_input_v1_reset(entry->text_input);
974 text_entry_update(entry);
975 entry->reset_serial = entry->serial;
976 }
977
978 static void
text_entry_set_preedit(struct text_entry * entry,const char * preedit_text,int preedit_cursor)979 text_entry_set_preedit(struct text_entry *entry,
980 const char *preedit_text,
981 int preedit_cursor)
982 {
983 text_entry_reset_preedit(entry);
984
985 if (!preedit_text)
986 return;
987
988 entry->preedit.text = strdup(preedit_text);
989 entry->preedit.cursor = preedit_cursor;
990
991 text_entry_update_layout(entry);
992
993 widget_schedule_redraw(entry->widget);
994 }
995
996 static uint32_t
text_entry_try_invoke_preedit_action(struct text_entry * entry,int32_t x,int32_t y,uint32_t button,enum wl_pointer_button_state state)997 text_entry_try_invoke_preedit_action(struct text_entry *entry,
998 int32_t x, int32_t y,
999 uint32_t button,
1000 enum wl_pointer_button_state state)
1001 {
1002 int index, trailing;
1003 uint32_t cursor;
1004 const char *text;
1005
1006 if (!entry->preedit.text)
1007 return 0;
1008
1009 pango_layout_xy_to_index(entry->layout,
1010 x * PANGO_SCALE, y * PANGO_SCALE,
1011 &index, &trailing);
1012
1013 text = pango_layout_get_text(entry->layout);
1014 cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
1015
1016 if (cursor < entry->cursor ||
1017 cursor > entry->cursor + strlen(entry->preedit.text)) {
1018 return 0;
1019 }
1020
1021 if (state == WL_POINTER_BUTTON_STATE_RELEASED)
1022 zwp_text_input_v1_invoke_action(entry->text_input,
1023 button,
1024 cursor - entry->cursor);
1025
1026 return 1;
1027 }
1028
1029 static bool
text_entry_has_preedit(struct text_entry * entry)1030 text_entry_has_preedit(struct text_entry *entry)
1031 {
1032 return entry->preedit.text && (strlen(entry->preedit.text) > 0);
1033 }
1034
1035 static void
text_entry_set_cursor_position(struct text_entry * entry,int32_t x,int32_t y,bool move_anchor)1036 text_entry_set_cursor_position(struct text_entry *entry,
1037 int32_t x, int32_t y,
1038 bool move_anchor)
1039 {
1040 int index, trailing;
1041 const char *text;
1042 uint32_t cursor;
1043
1044 pango_layout_xy_to_index(entry->layout,
1045 x * PANGO_SCALE, y * PANGO_SCALE,
1046 &index, &trailing);
1047
1048 text = pango_layout_get_text(entry->layout);
1049
1050 cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
1051
1052 if (move_anchor)
1053 entry->anchor = cursor;
1054
1055 if (text_entry_has_preedit(entry)) {
1056 text_entry_commit_and_reset(entry);
1057
1058 assert(!text_entry_has_preedit(entry));
1059 }
1060
1061 if (entry->cursor == cursor)
1062 return;
1063
1064 entry->cursor = cursor;
1065
1066 text_entry_update_layout(entry);
1067
1068 widget_schedule_redraw(entry->widget);
1069
1070 text_entry_update(entry);
1071 }
1072
1073 static void
text_entry_delete_text(struct text_entry * entry,uint32_t index,uint32_t length)1074 text_entry_delete_text(struct text_entry *entry,
1075 uint32_t index, uint32_t length)
1076 {
1077 uint32_t l;
1078
1079 assert(index <= strlen(entry->text));
1080 assert(index + length <= strlen(entry->text));
1081 assert(index + length >= length);
1082
1083 l = strlen(entry->text + index + length);
1084 memmove(entry->text + index,
1085 entry->text + index + length,
1086 l + 1);
1087
1088 if (entry->cursor > (index + length))
1089 entry->cursor -= length;
1090 else if (entry->cursor > index)
1091 entry->cursor = index;
1092
1093 entry->anchor = entry->cursor;
1094
1095 text_entry_update_layout(entry);
1096
1097 widget_schedule_redraw(entry->widget);
1098
1099 text_entry_update(entry);
1100 }
1101
1102 static void
text_entry_delete_selected_text(struct text_entry * entry)1103 text_entry_delete_selected_text(struct text_entry *entry)
1104 {
1105 uint32_t start_index = entry->anchor < entry->cursor ? entry->anchor : entry->cursor;
1106 uint32_t end_index = entry->anchor < entry->cursor ? entry->cursor : entry->anchor;
1107
1108 if (entry->anchor == entry->cursor)
1109 return;
1110
1111 text_entry_delete_text(entry, start_index, end_index - start_index);
1112
1113 entry->anchor = entry->cursor;
1114 }
1115
1116 static void
text_entry_get_cursor_rectangle(struct text_entry * entry,struct rectangle * rectangle)1117 text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle)
1118 {
1119 struct rectangle allocation;
1120 PangoRectangle extents;
1121 PangoRectangle cursor_pos;
1122
1123 widget_get_allocation(entry->widget, &allocation);
1124
1125 if (entry->preedit.text && entry->preedit.cursor < 0) {
1126 rectangle->x = 0;
1127 rectangle->y = 0;
1128 rectangle->width = 0;
1129 rectangle->height = 0;
1130 return;
1131 }
1132
1133
1134 pango_layout_get_extents(entry->layout, &extents, NULL);
1135 pango_layout_get_cursor_pos(entry->layout,
1136 entry->cursor + entry->preedit.cursor,
1137 &cursor_pos, NULL);
1138
1139 rectangle->x = allocation.x + (allocation.height / 2) + PANGO_PIXELS(cursor_pos.x);
1140 rectangle->y = allocation.y + 10 + PANGO_PIXELS(cursor_pos.y);
1141 rectangle->width = PANGO_PIXELS(cursor_pos.width);
1142 rectangle->height = PANGO_PIXELS(cursor_pos.height);
1143 }
1144
1145 static void
text_entry_draw_cursor(struct text_entry * entry,cairo_t * cr)1146 text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr)
1147 {
1148 PangoRectangle extents;
1149 PangoRectangle cursor_pos;
1150
1151 if (entry->preedit.text && entry->preedit.cursor < 0)
1152 return;
1153
1154 pango_layout_get_extents(entry->layout, &extents, NULL);
1155 pango_layout_get_cursor_pos(entry->layout,
1156 entry->cursor + entry->preedit.cursor,
1157 &cursor_pos, NULL);
1158
1159 cairo_set_line_width(cr, 1.0);
1160 cairo_move_to(cr, PANGO_PIXELS(cursor_pos.x), PANGO_PIXELS(cursor_pos.y));
1161 cairo_line_to(cr, PANGO_PIXELS(cursor_pos.x), PANGO_PIXELS(cursor_pos.y) + PANGO_PIXELS(cursor_pos.height));
1162 cairo_stroke(cr);
1163 }
1164
1165 static int
text_offset_left(struct rectangle * allocation)1166 text_offset_left(struct rectangle *allocation)
1167 {
1168 return 10;
1169 }
1170
1171 static int
text_offset_top(struct rectangle * allocation)1172 text_offset_top(struct rectangle *allocation)
1173 {
1174 return allocation->height / 2;
1175 }
1176
1177 static void
text_entry_redraw_handler(struct widget * widget,void * data)1178 text_entry_redraw_handler(struct widget *widget, void *data)
1179 {
1180 struct text_entry *entry = data;
1181 cairo_surface_t *surface;
1182 struct rectangle allocation;
1183 cairo_t *cr;
1184
1185 surface = window_get_surface(entry->window);
1186 widget_get_allocation(entry->widget, &allocation);
1187
1188 cr = cairo_create(surface);
1189 cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
1190 cairo_clip(cr);
1191
1192 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1193
1194 cairo_push_group(cr);
1195 cairo_translate(cr, allocation.x, allocation.y);
1196
1197 cairo_set_source_rgba(cr, 1, 1, 1, 1);
1198 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
1199 cairo_fill(cr);
1200
1201 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1202
1203 if (entry->active) {
1204 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
1205 cairo_set_line_width (cr, 3);
1206 cairo_set_source_rgba(cr, 0, 0, 1, 1.0);
1207 cairo_stroke(cr);
1208 }
1209
1210 cairo_set_source_rgba(cr, 0, 0, 0, 1);
1211
1212 cairo_translate(cr,
1213 text_offset_left(&allocation),
1214 text_offset_top(&allocation));
1215
1216 if (!entry->layout)
1217 entry->layout = pango_cairo_create_layout(cr);
1218 else
1219 pango_cairo_update_layout(cr, entry->layout);
1220
1221 text_entry_update_layout(entry);
1222
1223 pango_cairo_show_layout(cr, entry->layout);
1224
1225 text_entry_draw_cursor(entry, cr);
1226
1227 cairo_pop_group_to_source(cr);
1228 cairo_paint(cr);
1229
1230 cairo_destroy(cr);
1231 cairo_surface_destroy(surface);
1232 }
1233
1234 static int
text_entry_motion_handler(struct widget * widget,struct input * input,uint32_t time,float x,float y,void * data)1235 text_entry_motion_handler(struct widget *widget,
1236 struct input *input, uint32_t time,
1237 float x, float y, void *data)
1238 {
1239 struct text_entry *entry = data;
1240 struct rectangle allocation;
1241 int tx, ty;
1242
1243 if (!entry->button_pressed) {
1244 return CURSOR_IBEAM;
1245 }
1246
1247 widget_get_allocation(entry->widget, &allocation);
1248
1249 tx = x - allocation.x - text_offset_left(&allocation);
1250 ty = y - allocation.y - text_offset_top(&allocation);
1251
1252 text_entry_set_cursor_position(entry, tx, ty, false);
1253
1254 return CURSOR_IBEAM;
1255 }
1256
1257 static void
text_entry_button_handler(struct widget * widget,struct input * input,uint32_t time,uint32_t button,enum wl_pointer_button_state state,void * data)1258 text_entry_button_handler(struct widget *widget,
1259 struct input *input, uint32_t time,
1260 uint32_t button,
1261 enum wl_pointer_button_state state, void *data)
1262 {
1263 struct text_entry *entry = data;
1264 struct rectangle allocation;
1265 struct editor *editor;
1266 int32_t x, y;
1267 uint32_t result;
1268
1269 widget_get_allocation(entry->widget, &allocation);
1270 input_get_position(input, &x, &y);
1271
1272 x -= allocation.x + text_offset_left(&allocation);
1273 y -= allocation.y + text_offset_top(&allocation);
1274
1275 editor = window_get_user_data(entry->window);
1276
1277 switch (button) {
1278 case BTN_LEFT:
1279 entry->button_pressed = (state == WL_POINTER_BUTTON_STATE_PRESSED);
1280 if (state == WL_POINTER_BUTTON_STATE_PRESSED)
1281 input_grab(input, entry->widget, button);
1282 else
1283 input_ungrab(input);
1284 break;
1285 case BTN_RIGHT:
1286 if (state == WL_POINTER_BUTTON_STATE_PRESSED)
1287 show_menu(editor, input, time);
1288 break;
1289 }
1290
1291 if (text_entry_has_preedit(entry)) {
1292 result = text_entry_try_invoke_preedit_action(entry, x, y, button, state);
1293
1294 if (result)
1295 return;
1296 }
1297
1298 if (state == WL_POINTER_BUTTON_STATE_PRESSED &&
1299 button == BTN_LEFT) {
1300 struct wl_seat *seat = input_get_seat(input);
1301
1302 text_entry_activate(entry, seat);
1303 editor->active_entry = entry;
1304
1305 text_entry_set_cursor_position(entry, x, y, true);
1306 }
1307 }
1308
1309 static void
text_entry_touch_handler(struct widget * widget,struct input * input,uint32_t serial,uint32_t time,int32_t id,float tx,float ty,void * data)1310 text_entry_touch_handler(struct widget *widget, struct input *input,
1311 uint32_t serial, uint32_t time, int32_t id,
1312 float tx, float ty, void *data)
1313 {
1314 struct text_entry *entry = data;
1315 struct wl_seat *seat = input_get_seat(input);
1316 struct rectangle allocation;
1317 struct editor *editor;
1318 int32_t x, y;
1319
1320 widget_get_allocation(entry->widget, &allocation);
1321
1322 x = tx - (allocation.x + text_offset_left(&allocation));
1323 y = ty - (allocation.y + text_offset_top(&allocation));
1324
1325 editor = window_get_user_data(entry->window);
1326 text_entry_activate(entry, seat);
1327 editor->active_entry = entry;
1328
1329 text_entry_set_cursor_position(entry, x, y, true);
1330 }
1331
1332 static void
editor_button_handler(struct widget * widget,struct input * input,uint32_t time,uint32_t button,enum wl_pointer_button_state state,void * data)1333 editor_button_handler(struct widget *widget,
1334 struct input *input, uint32_t time,
1335 uint32_t button,
1336 enum wl_pointer_button_state state, void *data)
1337 {
1338 struct editor *editor = data;
1339
1340 if (button != BTN_LEFT) {
1341 return;
1342 }
1343
1344 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
1345 struct wl_seat *seat = input_get_seat(input);
1346
1347 text_entry_deactivate(editor->entry, seat);
1348 text_entry_deactivate(editor->editor, seat);
1349 editor->active_entry = NULL;
1350 }
1351 }
1352
1353 static void
editor_touch_handler(struct widget * widget,struct input * input,uint32_t serial,uint32_t time,int32_t id,float tx,float ty,void * data)1354 editor_touch_handler(struct widget *widget, struct input *input,
1355 uint32_t serial, uint32_t time, int32_t id,
1356 float tx, float ty, void *data)
1357 {
1358 struct editor *editor = data;
1359
1360 struct wl_seat *seat = input_get_seat(input);
1361
1362 text_entry_deactivate(editor->entry, seat);
1363 text_entry_deactivate(editor->editor, seat);
1364 editor->active_entry = NULL;
1365 }
1366
1367 static void
keyboard_focus_handler(struct window * window,struct input * device,void * data)1368 keyboard_focus_handler(struct window *window,
1369 struct input *device, void *data)
1370 {
1371 struct editor *editor = data;
1372
1373 window_schedule_redraw(editor->window);
1374 }
1375
1376 static int
handle_bound_key(struct editor * editor,struct input * input,uint32_t sym,uint32_t time)1377 handle_bound_key(struct editor *editor,
1378 struct input *input, uint32_t sym, uint32_t time)
1379 {
1380 switch (sym) {
1381 case XKB_KEY_X:
1382 editor_copy_cut(editor, input, true);
1383 return 1;
1384 case XKB_KEY_C:
1385 editor_copy_cut(editor, input, false);
1386 return 1;
1387 case XKB_KEY_V:
1388 editor_paste(editor, input);
1389 return 1;
1390 default:
1391 return 0;
1392 }
1393 }
1394
1395 static void
key_handler(struct window * window,struct input * input,uint32_t time,uint32_t key,uint32_t sym,enum wl_keyboard_key_state state,void * data)1396 key_handler(struct window *window,
1397 struct input *input, uint32_t time,
1398 uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
1399 void *data)
1400 {
1401 struct editor *editor = data;
1402 struct text_entry *entry;
1403 const char *new_char;
1404 char text[16];
1405 uint32_t modifiers;
1406
1407 if (!editor->active_entry)
1408 return;
1409
1410 entry = editor->active_entry;
1411
1412 if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
1413 return;
1414
1415 modifiers = input_get_modifiers(input);
1416 if ((modifiers & MOD_CONTROL_MASK) &&
1417 (modifiers & MOD_SHIFT_MASK) &&
1418 handle_bound_key(editor, input, sym, time))
1419 return;
1420
1421 switch (sym) {
1422 case XKB_KEY_BackSpace:
1423 text_entry_commit_and_reset(entry);
1424
1425 new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
1426 if (new_char != NULL)
1427 text_entry_delete_text(entry,
1428 new_char - entry->text,
1429 (entry->text + entry->cursor) - new_char);
1430 break;
1431 case XKB_KEY_Delete:
1432 text_entry_commit_and_reset(entry);
1433
1434 new_char = utf8_next_char(entry->text + entry->cursor);
1435 if (new_char != NULL)
1436 text_entry_delete_text(entry,
1437 entry->cursor,
1438 new_char - (entry->text + entry->cursor));
1439 break;
1440 case XKB_KEY_Left:
1441 text_entry_commit_and_reset(entry);
1442
1443 new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
1444 if (new_char != NULL) {
1445 entry->cursor = new_char - entry->text;
1446 if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1447 entry->anchor = entry->cursor;
1448 widget_schedule_redraw(entry->widget);
1449 }
1450 break;
1451 case XKB_KEY_Right:
1452 text_entry_commit_and_reset(entry);
1453
1454 new_char = utf8_next_char(entry->text + entry->cursor);
1455 if (new_char != NULL) {
1456 entry->cursor = new_char - entry->text;
1457 if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1458 entry->anchor = entry->cursor;
1459 widget_schedule_redraw(entry->widget);
1460 }
1461 break;
1462 case XKB_KEY_Up:
1463 text_entry_commit_and_reset(entry);
1464
1465 move_up(entry->text, &entry->cursor);
1466 if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1467 entry->anchor = entry->cursor;
1468 widget_schedule_redraw(entry->widget);
1469 break;
1470 case XKB_KEY_Down:
1471 text_entry_commit_and_reset(entry);
1472
1473 move_down(entry->text, &entry->cursor);
1474 if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1475 entry->anchor = entry->cursor;
1476 widget_schedule_redraw(entry->widget);
1477 break;
1478 case XKB_KEY_Escape:
1479 break;
1480 default:
1481 if (xkb_keysym_to_utf8(sym, text, sizeof(text)) <= 0)
1482 break;
1483
1484 text_entry_commit_and_reset(entry);
1485
1486 text_entry_insert_at_cursor(entry, text, 0, 0);
1487 break;
1488 }
1489
1490 widget_schedule_redraw(entry->widget);
1491 }
1492
1493 static void
global_handler(struct display * display,uint32_t name,const char * interface,uint32_t version,void * data)1494 global_handler(struct display *display, uint32_t name,
1495 const char *interface, uint32_t version, void *data)
1496 {
1497 struct editor *editor = data;
1498
1499 if (!strcmp(interface, "zwp_text_input_manager_v1")) {
1500 editor->text_input_manager =
1501 display_bind(display, name,
1502 &zwp_text_input_manager_v1_interface, 1);
1503 }
1504 }
1505
1506 /** Display help for command line options, and exit */
1507 static bool opt_help = false;
1508
1509 /** Require a distinct click to show the input panel (virtual keyboard) */
1510 static bool opt_click_to_show = false;
1511
1512 /** Set a specific (RFC-3066) language. Used for the virtual keyboard, etc. */
1513 static const char *opt_preferred_language = NULL;
1514
1515 /**
1516 * \brief command line options for editor
1517 */
1518 static const struct weston_option editor_options[] = {
1519 { WESTON_OPTION_BOOLEAN, "help", 'h', &opt_help },
1520 { WESTON_OPTION_BOOLEAN, "click-to-show", 'C', &opt_click_to_show },
1521 { WESTON_OPTION_STRING, "preferred-language", 'L', &opt_preferred_language },
1522 };
1523
1524 static void
usage(const char * program_name,int exit_code)1525 usage(const char *program_name, int exit_code)
1526 {
1527 unsigned k;
1528
1529 fprintf(stderr, "Usage: %s [OPTIONS] [FILENAME]\n\n", program_name);
1530 for (k = 0; k < ARRAY_LENGTH(editor_options); k++) {
1531 const struct weston_option *p = &editor_options[k];
1532 if (p->name) {
1533 fprintf(stderr, " --%s", p->name);
1534 if (p->type != WESTON_OPTION_BOOLEAN)
1535 fprintf(stderr, "=VALUE");
1536 fprintf(stderr, "\n");
1537 }
1538 if (p->short_name) {
1539 fprintf(stderr, " -%c", p->short_name);
1540 if (p->type != WESTON_OPTION_BOOLEAN)
1541 fprintf(stderr, "VALUE");
1542 fprintf(stderr, "\n");
1543 }
1544 }
1545 exit(exit_code);
1546 }
1547
1548 /* Load the contents of a file into a UTF-8 text buffer and return it.
1549 *
1550 * Caller is responsible for freeing the buffer when done.
1551 * On error, returns NULL.
1552 */
1553 static char *
read_file(char * filename)1554 read_file(char *filename)
1555 {
1556 char *buffer = NULL;
1557 int buf_size, read_size;
1558 FILE *fin;
1559 int errsv;
1560
1561 fin = fopen(filename, "r");
1562 if (fin == NULL)
1563 goto error;
1564
1565 /* Determine required buffer size */
1566 if (fseek(fin, 0, SEEK_END) != 0)
1567 goto error;
1568 buf_size = ftell(fin);
1569 if (buf_size < 0)
1570 goto error;
1571 rewind(fin);
1572
1573 /* Create buffer and read in the text */
1574 buffer = (char*) malloc(sizeof(char) * (buf_size + 1));
1575 if (buffer == NULL)
1576 goto error;
1577 read_size = fread(buffer, sizeof(char), buf_size, fin);
1578 fclose(fin);
1579 if (buf_size != read_size)
1580 goto error;
1581 buffer[buf_size] = '\0';
1582
1583 return buffer;
1584
1585 error:
1586 errsv = errno;
1587 if (fin)
1588 fclose(fin);
1589 free(buffer);
1590 errno = errsv ? errsv : EINVAL;
1591
1592 return NULL;
1593 }
1594
1595 int
main(int argc,char * argv[])1596 main(int argc, char *argv[])
1597 {
1598 struct editor editor;
1599 char *text_buffer = NULL;
1600
1601 parse_options(editor_options, ARRAY_LENGTH(editor_options),
1602 &argc, argv);
1603 if (opt_help)
1604 usage(argv[0], EXIT_SUCCESS);
1605
1606 if (argc > 1) {
1607 if (argv[1][0] == '-')
1608 usage(argv[0], EXIT_FAILURE);
1609
1610 text_buffer = read_file(argv[1]);
1611 if (text_buffer == NULL) {
1612 fprintf(stderr, "could not read file '%s': %s\n",
1613 argv[1], strerror(errno));
1614 return -1;
1615 }
1616 }
1617
1618 memset(&editor, 0, sizeof editor);
1619
1620 editor.display = display_create(&argc, argv);
1621 if (editor.display == NULL) {
1622 fprintf(stderr, "failed to create display: %s\n",
1623 strerror(errno));
1624 free(text_buffer);
1625 return -1;
1626 }
1627
1628 display_set_user_data(editor.display, &editor);
1629 display_set_global_handler(editor.display, global_handler);
1630
1631 if (editor.text_input_manager == NULL) {
1632 fprintf(stderr, "No text input manager global\n");
1633 display_destroy(editor.display);
1634 free(text_buffer);
1635 return -1;
1636 }
1637
1638 editor.window = window_create(editor.display);
1639 editor.widget = window_frame_create(editor.window, &editor);
1640
1641 if (text_buffer)
1642 editor.entry = text_entry_create(&editor, text_buffer);
1643 else
1644 editor.entry = text_entry_create(&editor, "Entry");
1645 editor.entry->click_to_show = opt_click_to_show;
1646 if (opt_preferred_language)
1647 editor.entry->preferred_language = strdup(opt_preferred_language);
1648 editor.editor = text_entry_create(&editor, "Numeric");
1649 editor.editor->content_purpose = ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NUMBER;
1650 editor.editor->click_to_show = opt_click_to_show;
1651 editor.selection = NULL;
1652 editor.selected_text = NULL;
1653
1654 window_set_title(editor.window, "Text Editor");
1655 window_set_key_handler(editor.window, key_handler);
1656 window_set_keyboard_focus_handler(editor.window,
1657 keyboard_focus_handler);
1658 window_set_user_data(editor.window, &editor);
1659
1660 widget_set_redraw_handler(editor.widget, redraw_handler);
1661 widget_set_resize_handler(editor.widget, resize_handler);
1662 widget_set_button_handler(editor.widget, editor_button_handler);
1663 widget_set_touch_down_handler(editor.widget, editor_touch_handler);
1664
1665 window_schedule_resize(editor.window, 500, 400);
1666
1667 display_run(editor.display);
1668
1669 if (editor.selected_text)
1670 free(editor.selected_text);
1671 if (editor.selection)
1672 wl_data_source_destroy(editor.selection);
1673 text_entry_destroy(editor.entry);
1674 text_entry_destroy(editor.editor);
1675 widget_destroy(editor.widget);
1676 window_destroy(editor.window);
1677 display_destroy(editor.display);
1678 free(text_buffer);
1679
1680 return 0;
1681 }
1682