compositor-x11: Rename the output make to "weston-X11"
[platform/upstream/weston.git] / clients / editor.c
1 /*
2  * Copyright © 2012 Openismus GmbH
3  * Copyright © 2012 Intel Corporation
4  *
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
24 #include "config.h"
25
26 #include <assert.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdbool.h>
31
32 #include <linux/input.h>
33 #include <cairo.h>
34
35 #include <pango/pangocairo.h>
36
37 #include "window.h"
38 #include "text-client-protocol.h"
39
40 struct text_entry {
41         struct widget *widget;
42         struct window *window;
43         char *text;
44         int active;
45         uint32_t cursor;
46         uint32_t anchor;
47         struct {
48                 char *text;
49                 int32_t cursor;
50                 char *commit;
51                 PangoAttrList *attr_list;
52         } preedit;
53         struct {
54                 PangoAttrList *attr_list;
55                 int32_t cursor;
56         } preedit_info;
57         struct {
58                 int32_t cursor;
59                 int32_t anchor;
60                 uint32_t delete_index;
61                 uint32_t delete_length;
62                 bool invalid_delete;
63         } pending_commit;
64         struct wl_text_input *text_input;
65         PangoLayout *layout;
66         struct {
67                 xkb_mod_mask_t shift_mask;
68         } keysym;
69         uint32_t serial;
70         uint32_t reset_serial;
71         uint32_t content_purpose;
72         uint32_t click_to_show;
73         char *preferred_language;
74         bool button_pressed;
75 };
76
77 struct editor {
78         struct wl_text_input_manager *text_input_manager;
79         struct display *display;
80         struct window *window;
81         struct widget *widget;
82         struct text_entry *entry;
83         struct text_entry *editor;
84         struct text_entry *active_entry;
85 };
86
87 static const char *
88 utf8_end_char(const char *p)
89 {
90         while ((*p & 0xc0) == 0x80)
91                 p++;
92         return p;
93 }
94
95 static const char *
96 utf8_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
105 static const char *
106 utf8_next_char(const char *p)
107 {
108         if (*p != 0)
109                 return utf8_end_char(++p);
110         return NULL;
111 }
112
113 static void
114 move_up(const char *p, uint32_t *cursor)
115 {
116         const char *posr, *posr_i;
117         char text[16];
118
119         xkb_keysym_to_utf8(XKB_KEY_Return, text, sizeof(text));
120
121         posr = strstr(p, text);
122         while (posr) {
123                 if (*cursor > (unsigned)(posr-p)) {
124                         posr_i = strstr(posr+1, text);
125                         if (!posr_i || !(*cursor > (unsigned)(posr_i-p))) {
126                                 *cursor = posr-p;
127                                 break;
128                         }
129                         posr = posr_i;
130                 } else {
131                         break;
132                 }
133         }
134 }
135
136 static void
137 move_down(const char *p, uint32_t *cursor)
138 {
139         const char *posr;
140         char text[16];
141
142         xkb_keysym_to_utf8(XKB_KEY_Return, text, sizeof(text));
143
144         posr = strstr(p, text);
145         while (posr) {
146                 if (*cursor <= (unsigned)(posr-p)) {
147                         *cursor = posr-p + 1;
148                         break;
149                 }
150                 posr = strstr(posr+1, text);
151         }
152 }
153
154 static void text_entry_redraw_handler(struct widget *widget, void *data);
155 static void text_entry_button_handler(struct widget *widget,
156                                       struct input *input, uint32_t time,
157                                       uint32_t button,
158                                       enum wl_pointer_button_state state, void *data);
159 static void text_entry_touch_handler(struct widget *widget, struct input *input,
160                                      uint32_t serial, uint32_t time, int32_t id,
161                                      float tx, float ty, void *data);
162 static int text_entry_motion_handler(struct widget *widget,
163                                      struct input *input, uint32_t time,
164                                      float x, float y, void *data);
165 static void text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
166                                         int32_t cursor, int32_t anchor);
167 static void text_entry_set_preedit(struct text_entry *entry,
168                                    const char *preedit_text,
169                                    int preedit_cursor);
170 static void text_entry_delete_text(struct text_entry *entry,
171                                    uint32_t index, uint32_t length);
172 static void text_entry_delete_selected_text(struct text_entry *entry);
173 static void text_entry_reset_preedit(struct text_entry *entry);
174 static void text_entry_commit_and_reset(struct text_entry *entry);
175 static void text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle);
176 static void text_entry_update(struct text_entry *entry);
177
178 static void
179 text_input_commit_string(void *data,
180                          struct wl_text_input *text_input,
181                          uint32_t serial,
182                          const char *text)
183 {
184         struct text_entry *entry = data;
185
186         if ((entry->serial - serial) > (entry->serial - entry->reset_serial)) {
187                 fprintf(stderr, "Ignore commit. Serial: %u, Current: %u, Reset: %u\n",
188                         serial, entry->serial, entry->reset_serial);
189                 return;
190         }
191
192         if (entry->pending_commit.invalid_delete) {
193                 fprintf(stderr, "Ignore commit. Invalid previous delete_surrounding event.\n");
194                 memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
195                 return;
196         }
197
198         text_entry_reset_preedit(entry);
199
200         if (entry->pending_commit.delete_length) {
201                 text_entry_delete_text(entry,
202                                        entry->pending_commit.delete_index,
203                                        entry->pending_commit.delete_length);
204         } else {
205                 text_entry_delete_selected_text(entry);
206         }
207
208         text_entry_insert_at_cursor(entry, text,
209                                     entry->pending_commit.cursor,
210                                     entry->pending_commit.anchor);
211
212         memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
213
214         widget_schedule_redraw(entry->widget);
215 }
216
217 static void
218 clear_pending_preedit(struct text_entry *entry)
219 {
220         memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
221
222         pango_attr_list_unref(entry->preedit_info.attr_list);
223
224         entry->preedit_info.cursor = 0;
225         entry->preedit_info.attr_list = NULL;
226
227         memset(&entry->preedit_info, 0, sizeof entry->preedit_info);
228 }
229
230 static void
231 text_input_preedit_string(void *data,
232                           struct wl_text_input *text_input,
233                           uint32_t serial,
234                           const char *text,
235                           const char *commit)
236 {
237         struct text_entry *entry = data;
238
239         if ((entry->serial - serial) > (entry->serial - entry->reset_serial)) {
240                 fprintf(stderr, "Ignore preedit_string. Serial: %u, Current: %u, Reset: %u\n",
241                         serial, entry->serial, entry->reset_serial);
242                 clear_pending_preedit(entry);
243                 return;
244         }
245
246         if (entry->pending_commit.invalid_delete) {
247                 fprintf(stderr, "Ignore preedit_string. Invalid previous delete_surrounding event.\n");
248                 clear_pending_preedit(entry);
249                 return;
250         }
251
252         if (entry->pending_commit.delete_length) {
253                 text_entry_delete_text(entry,
254                                        entry->pending_commit.delete_index,
255                                        entry->pending_commit.delete_length);
256         } else {
257                 text_entry_delete_selected_text(entry);
258         }
259
260         text_entry_set_preedit(entry, text, entry->preedit_info.cursor);
261         entry->preedit.commit = strdup(commit);
262         entry->preedit.attr_list = pango_attr_list_ref(entry->preedit_info.attr_list);
263
264         clear_pending_preedit(entry);
265
266         text_entry_update(entry);
267
268         widget_schedule_redraw(entry->widget);
269 }
270
271 static void
272 text_input_delete_surrounding_text(void *data,
273                                    struct wl_text_input *text_input,
274                                    int32_t index,
275                                    uint32_t length)
276 {
277         struct text_entry *entry = data;
278         uint32_t text_length;
279
280         entry->pending_commit.delete_index = entry->cursor + index;
281         entry->pending_commit.delete_length = length;
282         entry->pending_commit.invalid_delete = false;
283
284         text_length = strlen(entry->text);
285
286         if (entry->pending_commit.delete_index > text_length ||
287             length > text_length ||
288             entry->pending_commit.delete_index + length > text_length) {
289                 fprintf(stderr, "delete_surrounding_text: Invalid index: %d," \
290                         "length %u'; cursor: %u text length: %u\n", index, length, entry->cursor, text_length);
291                 entry->pending_commit.invalid_delete = true;
292                 return;
293         }
294 }
295
296 static void
297 text_input_cursor_position(void *data,
298                            struct wl_text_input *text_input,
299                            int32_t index,
300                            int32_t anchor)
301 {
302         struct text_entry *entry = data;
303
304         entry->pending_commit.cursor = index;
305         entry->pending_commit.anchor = anchor;
306 }
307
308 static void
309 text_input_preedit_styling(void *data,
310                            struct wl_text_input *text_input,
311                            uint32_t index,
312                            uint32_t length,
313                            uint32_t style)
314 {
315         struct text_entry *entry = data;
316         PangoAttribute *attr1 = NULL;
317         PangoAttribute *attr2 = NULL;
318
319         if (!entry->preedit_info.attr_list)
320                 entry->preedit_info.attr_list = pango_attr_list_new();
321
322         switch (style) {
323                 case WL_TEXT_INPUT_PREEDIT_STYLE_DEFAULT:
324                 case WL_TEXT_INPUT_PREEDIT_STYLE_UNDERLINE:
325                         attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
326                         break;
327                 case WL_TEXT_INPUT_PREEDIT_STYLE_INCORRECT:
328                         attr1 = pango_attr_underline_new(PANGO_UNDERLINE_ERROR);
329                         attr2 = pango_attr_underline_color_new(65535, 0, 0);
330                         break;
331                 case WL_TEXT_INPUT_PREEDIT_STYLE_SELECTION:
332                         attr1 = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
333                         attr2 = pango_attr_foreground_new(65535, 65535, 65535);
334                         break;
335                 case WL_TEXT_INPUT_PREEDIT_STYLE_HIGHLIGHT:
336                 case WL_TEXT_INPUT_PREEDIT_STYLE_ACTIVE:
337                         attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
338                         attr2 = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
339                         break;
340                 case WL_TEXT_INPUT_PREEDIT_STYLE_INACTIVE:
341                         attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
342                         attr2 = pango_attr_foreground_new(0.3 * 65535, 0.3 * 65535, 0.3 * 65535);
343                         break;
344         }
345
346         if (attr1) {
347                 attr1->start_index = entry->cursor + index;
348                 attr1->end_index = entry->cursor + index + length;
349                 pango_attr_list_insert(entry->preedit_info.attr_list, attr1);
350         }
351
352         if (attr2) {
353                 attr2->start_index = entry->cursor + index;
354                 attr2->end_index = entry->cursor + index + length;
355                 pango_attr_list_insert(entry->preedit_info.attr_list, attr2);
356         }
357 }
358
359 static void
360 text_input_preedit_cursor(void *data,
361                           struct wl_text_input *text_input,
362                           int32_t index)
363 {
364         struct text_entry *entry = data;
365
366         entry->preedit_info.cursor = index;
367 }
368
369 static void
370 text_input_modifiers_map(void *data,
371                          struct wl_text_input *text_input,
372                          struct wl_array *map)
373 {
374         struct text_entry *entry = data;
375
376         entry->keysym.shift_mask = keysym_modifiers_get_mask(map, "Shift");
377 }
378
379 static void
380 text_input_keysym(void *data,
381                   struct wl_text_input *text_input,
382                   uint32_t serial,
383                   uint32_t time,
384                   uint32_t key,
385                   uint32_t state,
386                   uint32_t modifiers)
387 {
388         struct text_entry *entry = data;
389         const char *new_char;
390
391         if (key == XKB_KEY_Left ||
392             key == XKB_KEY_Right) {
393                 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
394                         return;
395
396                 if (key == XKB_KEY_Left)
397                         new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
398                 else
399                         new_char = utf8_next_char(entry->text + entry->cursor);
400
401                 if (new_char != NULL) {
402                         entry->cursor = new_char - entry->text;
403                 }
404
405                 if (!(modifiers & entry->keysym.shift_mask))
406                         entry->anchor = entry->cursor;
407                 widget_schedule_redraw(entry->widget);
408
409                 return;
410         }
411
412         if (key == XKB_KEY_Up ||
413             key == XKB_KEY_Down) {
414                 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
415                         return;
416
417                 if (key == XKB_KEY_Up)
418                         move_up(entry->text, &entry->cursor);
419                 else
420                         move_down(entry->text, &entry->cursor);
421
422                 if (!(modifiers & entry->keysym.shift_mask))
423                         entry->anchor = entry->cursor;
424                 widget_schedule_redraw(entry->widget);
425
426                 return;
427         }
428
429         if (key == XKB_KEY_BackSpace) {
430                 const char *start, *end;
431
432                 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
433                         return;
434
435                 text_entry_commit_and_reset(entry);
436
437                 start = utf8_prev_char(entry->text, entry->text + entry->cursor);
438                 if (start == NULL)
439                         return;
440
441                 end = utf8_next_char(start);
442
443                 text_entry_delete_text(entry,
444                                        start - entry->text,
445                                        end - start);
446
447                 return;
448         }
449
450         if (key == XKB_KEY_Tab ||
451             key == XKB_KEY_KP_Enter ||
452             key == XKB_KEY_Return) {
453                 char text[16];
454
455                 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
456                         return;
457
458                 xkb_keysym_to_utf8(key, text, sizeof(text));
459
460                 text_entry_insert_at_cursor(entry, text, 0, 0);
461
462                 return;
463         }
464 }
465
466 static void
467 text_input_enter(void *data,
468                  struct wl_text_input *text_input,
469                  struct wl_surface *surface)
470 {
471         struct text_entry *entry = data;
472
473         if (surface != window_get_wl_surface(entry->window))
474                 return;
475
476         entry->active = 1;
477
478         text_entry_update(entry);
479         entry->reset_serial = entry->serial;
480
481         widget_schedule_redraw(entry->widget);
482 }
483
484 static void
485 text_input_leave(void *data,
486                  struct wl_text_input *text_input)
487 {
488         struct text_entry *entry = data;
489
490         text_entry_commit_and_reset(entry);
491
492         entry->active = 0;
493
494         wl_text_input_hide_input_panel(text_input);
495
496         widget_schedule_redraw(entry->widget);
497 }
498
499 static void
500 text_input_input_panel_state(void *data,
501                              struct wl_text_input *text_input,
502                              uint32_t state)
503 {
504 }
505
506 static void
507 text_input_language(void *data,
508                     struct wl_text_input *text_input,
509                     uint32_t serial,
510                     const char *language)
511 {
512         fprintf(stderr, "input language is %s \n", language);
513 }
514
515 static void
516 text_input_text_direction(void *data,
517                           struct wl_text_input *text_input,
518                           uint32_t serial,
519                           uint32_t direction)
520 {
521         struct text_entry *entry = data;
522         PangoContext *context = pango_layout_get_context(entry->layout);
523         PangoDirection pango_direction;
524
525
526         switch (direction) {
527                 case WL_TEXT_INPUT_TEXT_DIRECTION_LTR:
528                         pango_direction = PANGO_DIRECTION_LTR;
529                         break;
530                 case WL_TEXT_INPUT_TEXT_DIRECTION_RTL:
531                         pango_direction = PANGO_DIRECTION_RTL;
532                         break;
533                 case WL_TEXT_INPUT_TEXT_DIRECTION_AUTO:
534                 default:
535                         pango_direction = PANGO_DIRECTION_NEUTRAL;
536         }
537
538         pango_context_set_base_dir(context, pango_direction);
539 }
540
541 static const struct wl_text_input_listener text_input_listener = {
542         text_input_enter,
543         text_input_leave,
544         text_input_modifiers_map,
545         text_input_input_panel_state,
546         text_input_preedit_string,
547         text_input_preedit_styling,
548         text_input_preedit_cursor,
549         text_input_commit_string,
550         text_input_cursor_position,
551         text_input_delete_surrounding_text,
552         text_input_keysym,
553         text_input_language,
554         text_input_text_direction
555 };
556
557 static struct text_entry*
558 text_entry_create(struct editor *editor, const char *text)
559 {
560         struct text_entry *entry;
561
562         entry = xmalloc(sizeof *entry);
563         memset(entry, 0, sizeof *entry);
564
565         entry->widget = widget_add_widget(editor->widget, entry);
566         entry->window = editor->window;
567         entry->text = strdup(text);
568         entry->active = 0;
569         entry->cursor = strlen(text);
570         entry->anchor = entry->cursor;
571         entry->text_input = wl_text_input_manager_create_text_input(editor->text_input_manager);
572         wl_text_input_add_listener(entry->text_input, &text_input_listener, entry);
573
574         widget_set_redraw_handler(entry->widget, text_entry_redraw_handler);
575         widget_set_button_handler(entry->widget, text_entry_button_handler);
576         widget_set_motion_handler(entry->widget, text_entry_motion_handler);
577         widget_set_touch_down_handler(entry->widget, text_entry_touch_handler);
578
579         return entry;
580 }
581
582 static void
583 text_entry_destroy(struct text_entry *entry)
584 {
585         widget_destroy(entry->widget);
586         wl_text_input_destroy(entry->text_input);
587         g_clear_object(&entry->layout);
588         free(entry->text);
589         free(entry);
590 }
591
592 static void
593 redraw_handler(struct widget *widget, void *data)
594 {
595         struct editor *editor = data;
596         cairo_surface_t *surface;
597         struct rectangle allocation;
598         cairo_t *cr;
599
600         surface = window_get_surface(editor->window);
601         widget_get_allocation(editor->widget, &allocation);
602
603         cr = cairo_create(surface);
604         cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
605         cairo_clip(cr);
606
607         cairo_translate(cr, allocation.x, allocation.y);
608
609         /* Draw background */
610         cairo_push_group(cr);
611         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
612         cairo_set_source_rgba(cr, 1, 1, 1, 1);
613         cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
614         cairo_fill(cr);
615
616         cairo_pop_group_to_source(cr);
617         cairo_paint(cr);
618
619         cairo_destroy(cr);
620         cairo_surface_destroy(surface);
621 }
622
623 static void
624 text_entry_allocate(struct text_entry *entry, int32_t x, int32_t y,
625                     int32_t width, int32_t height)
626 {
627         widget_set_allocation(entry->widget, x, y, width, height);
628 }
629
630 static void
631 resize_handler(struct widget *widget,
632                int32_t width, int32_t height, void *data)
633 {
634         struct editor *editor = data;
635         struct rectangle allocation;
636
637         widget_get_allocation(editor->widget, &allocation);
638
639         text_entry_allocate(editor->entry,
640                             allocation.x + 20, allocation.y + 20,
641                             width - 40, height / 2 - 40);
642         text_entry_allocate(editor->editor,
643                             allocation.x + 20, allocation.y + height / 2 + 20,
644                             width - 40, height / 2 - 40);
645 }
646
647 static void
648 text_entry_activate(struct text_entry *entry,
649                     struct wl_seat *seat)
650 {
651         struct wl_surface *surface = window_get_wl_surface(entry->window);
652
653         if (entry->click_to_show && entry->active) {
654                 wl_text_input_show_input_panel(entry->text_input);
655
656                 return;
657         }
658
659         if (!entry->click_to_show)
660                 wl_text_input_show_input_panel(entry->text_input);
661
662         wl_text_input_activate(entry->text_input,
663                                seat,
664                                surface);
665 }
666
667 static void
668 text_entry_deactivate(struct text_entry *entry,
669                       struct wl_seat *seat)
670 {
671         wl_text_input_deactivate(entry->text_input,
672                                  seat);
673 }
674
675 static void
676 text_entry_update_layout(struct text_entry *entry)
677 {
678         char *text;
679         PangoAttrList *attr_list;
680
681         assert(entry->cursor <= (strlen(entry->text) +
682                (entry->preedit.text ? strlen(entry->preedit.text) : 0)));
683
684         if (entry->preedit.text) {
685                 text = malloc(strlen(entry->text) + strlen(entry->preedit.text) + 1);
686                 strncpy(text, entry->text, entry->cursor);
687                 strcpy(text + entry->cursor, entry->preedit.text);
688                 strcpy(text + entry->cursor + strlen(entry->preedit.text),
689                        entry->text + entry->cursor);
690         } else {
691                 text = strdup(entry->text);
692         }
693
694         if (entry->cursor != entry->anchor) {
695                 int start_index = MIN(entry->cursor, entry->anchor);
696                 int end_index = MAX(entry->cursor, entry->anchor);
697                 PangoAttribute *attr;
698
699                 attr_list = pango_attr_list_copy(entry->preedit.attr_list);
700
701                 if (!attr_list)
702                         attr_list = pango_attr_list_new();
703
704                 attr = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
705                 attr->start_index = start_index;
706                 attr->end_index = end_index;
707                 pango_attr_list_insert(attr_list, attr);
708
709                 attr = pango_attr_foreground_new(65535, 65535, 65535);
710                 attr->start_index = start_index;
711                 attr->end_index = end_index;
712                 pango_attr_list_insert(attr_list, attr);
713         } else {
714                 attr_list = pango_attr_list_ref(entry->preedit.attr_list);
715         }
716
717         if (entry->preedit.text && !entry->preedit.attr_list) {
718                 PangoAttribute *attr;
719
720                 if (!attr_list)
721                         attr_list = pango_attr_list_new();
722
723                 attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
724                 attr->start_index = entry->cursor;
725                 attr->end_index = entry->cursor + strlen(entry->preedit.text);
726                 pango_attr_list_insert(attr_list, attr);
727         }
728
729         if (entry->layout) {
730                 pango_layout_set_text(entry->layout, text, -1);
731                 pango_layout_set_attributes(entry->layout, attr_list);
732         }
733
734         free(text);
735         pango_attr_list_unref(attr_list);
736 }
737
738 static void
739 text_entry_update(struct text_entry *entry)
740 {
741         struct rectangle cursor_rectangle;
742
743         wl_text_input_set_content_type(entry->text_input,
744                                        WL_TEXT_INPUT_CONTENT_HINT_NONE,
745                                        entry->content_purpose);
746
747         wl_text_input_set_surrounding_text(entry->text_input,
748                                            entry->text,
749                                            entry->cursor,
750                                            entry->anchor);
751
752         if (entry->preferred_language)
753                 wl_text_input_set_preferred_language(entry->text_input,
754                                                      entry->preferred_language);
755
756         text_entry_get_cursor_rectangle(entry, &cursor_rectangle);
757         wl_text_input_set_cursor_rectangle(entry->text_input, cursor_rectangle.x, cursor_rectangle.y,
758                                            cursor_rectangle.width, cursor_rectangle.height);
759
760         wl_text_input_commit_state(entry->text_input, ++entry->serial);
761 }
762
763 static void
764 text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
765                             int32_t cursor, int32_t anchor)
766 {
767         char *new_text = malloc(strlen(entry->text) + strlen(text) + 1);
768
769         strncpy(new_text, entry->text, entry->cursor);
770         strcpy(new_text + entry->cursor, text);
771         strcpy(new_text + entry->cursor + strlen(text),
772                entry->text + entry->cursor);
773
774         free(entry->text);
775         entry->text = new_text;
776         if (anchor >= 0)
777                 entry->anchor = entry->cursor + strlen(text) + anchor;
778         else
779                 entry->anchor = entry->cursor + 1 + anchor;
780
781         if (cursor >= 0)
782                 entry->cursor += strlen(text) + cursor;
783         else
784                 entry->cursor += 1 + cursor;
785
786         text_entry_update_layout(entry);
787
788         widget_schedule_redraw(entry->widget);
789
790         text_entry_update(entry);
791 }
792
793 static void
794 text_entry_reset_preedit(struct text_entry *entry)
795 {
796         entry->preedit.cursor = 0;
797
798         free(entry->preedit.text);
799         entry->preedit.text = NULL;
800
801         free(entry->preedit.commit);
802         entry->preedit.commit = NULL;
803
804         pango_attr_list_unref(entry->preedit.attr_list);
805         entry->preedit.attr_list = NULL;
806 }
807
808 static void
809 text_entry_commit_and_reset(struct text_entry *entry)
810 {
811         char *commit = NULL;
812
813         if (entry->preedit.commit)
814                 commit = strdup(entry->preedit.commit);
815
816         text_entry_reset_preedit(entry);
817         if (commit) {
818                 text_entry_insert_at_cursor(entry, commit, 0, 0);
819                 free(commit);
820         }
821
822         wl_text_input_reset(entry->text_input);
823         text_entry_update(entry);
824         entry->reset_serial = entry->serial;
825 }
826
827 static void
828 text_entry_set_preedit(struct text_entry *entry,
829                        const char *preedit_text,
830                        int preedit_cursor)
831 {
832         text_entry_reset_preedit(entry);
833
834         if (!preedit_text)
835                 return;
836
837         entry->preedit.text = strdup(preedit_text);
838         entry->preedit.cursor = preedit_cursor;
839
840         text_entry_update_layout(entry);
841
842         widget_schedule_redraw(entry->widget);
843 }
844
845 static uint32_t
846 text_entry_try_invoke_preedit_action(struct text_entry *entry,
847                                      int32_t x, int32_t y,
848                                      uint32_t button,
849                                      enum wl_pointer_button_state state)
850 {
851         int index, trailing;
852         uint32_t cursor;
853         const char *text;
854
855         if (!entry->preedit.text)
856                 return 0;
857
858         pango_layout_xy_to_index(entry->layout,
859                                  x * PANGO_SCALE, y * PANGO_SCALE,
860                                  &index, &trailing);
861
862         text = pango_layout_get_text(entry->layout);
863         cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
864
865         if (cursor < entry->cursor ||
866             cursor > entry->cursor + strlen(entry->preedit.text)) {
867                 return 0;
868         }
869
870         if (state == WL_POINTER_BUTTON_STATE_RELEASED)
871                 wl_text_input_invoke_action(entry->text_input,
872                                             button,
873                                             cursor - entry->cursor);
874
875         return 1;
876 }
877
878 static bool
879 text_entry_has_preedit(struct text_entry *entry)
880 {
881         return entry->preedit.text && (strlen(entry->preedit.text) > 0);
882 }
883
884 static void
885 text_entry_set_cursor_position(struct text_entry *entry,
886                                int32_t x, int32_t y,
887                                bool move_anchor)
888 {
889         int index, trailing;
890         const char *text;
891         uint32_t cursor;
892
893         pango_layout_xy_to_index(entry->layout,
894                                  x * PANGO_SCALE, y * PANGO_SCALE,
895                                  &index, &trailing);
896
897         text = pango_layout_get_text(entry->layout);
898
899         cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
900
901         if (move_anchor)
902                 entry->anchor = cursor;
903
904         if (text_entry_has_preedit(entry)) {
905                 text_entry_commit_and_reset(entry);
906
907                 assert(!text_entry_has_preedit(entry));
908         }
909
910         if (entry->cursor == cursor)
911                 return;
912
913         entry->cursor = cursor;
914
915         text_entry_update_layout(entry);
916
917         widget_schedule_redraw(entry->widget);
918
919         text_entry_update(entry);
920 }
921
922 static void
923 text_entry_delete_text(struct text_entry *entry,
924                        uint32_t index, uint32_t length)
925 {
926         uint32_t l;
927
928         assert(index <= strlen(entry->text));
929         assert(index + length <= strlen(entry->text));
930         assert(index + length >= length);
931
932         l = strlen(entry->text + index + length);
933         memmove(entry->text + index,
934                 entry->text + index + length,
935                 l + 1);
936
937         if (entry->cursor > (index + length))
938                 entry->cursor -= length;
939         else if (entry->cursor > index)
940                 entry->cursor = index;
941
942         entry->anchor = entry->cursor;
943
944         text_entry_update_layout(entry);
945
946         widget_schedule_redraw(entry->widget);
947
948         text_entry_update(entry);
949 }
950
951 static void
952 text_entry_delete_selected_text(struct text_entry *entry)
953 {
954         uint32_t start_index = entry->anchor < entry->cursor ? entry->anchor : entry->cursor;
955         uint32_t end_index = entry->anchor < entry->cursor ? entry->cursor : entry->anchor;
956
957         if (entry->anchor == entry->cursor)
958                 return;
959
960         text_entry_delete_text(entry, start_index, end_index - start_index);
961
962         entry->anchor = entry->cursor;
963 }
964
965 static void
966 text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle)
967 {
968         struct rectangle allocation;
969         PangoRectangle extents;
970         PangoRectangle cursor_pos;
971
972         widget_get_allocation(entry->widget, &allocation);
973
974         if (entry->preedit.text && entry->preedit.cursor < 0) {
975                 rectangle->x = 0;
976                 rectangle->y = 0;
977                 rectangle->width = 0;
978                 rectangle->height = 0;
979                 return;
980         }
981
982
983         pango_layout_get_extents(entry->layout, &extents, NULL);
984         pango_layout_get_cursor_pos(entry->layout,
985                                     entry->cursor + entry->preedit.cursor,
986                                     &cursor_pos, NULL);
987
988         rectangle->x = allocation.x + (allocation.height / 2) + PANGO_PIXELS(cursor_pos.x);
989         rectangle->y = allocation.y + 10 + PANGO_PIXELS(cursor_pos.y);
990         rectangle->width = PANGO_PIXELS(cursor_pos.width);
991         rectangle->height = PANGO_PIXELS(cursor_pos.height);
992 }
993
994 static void
995 text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr)
996 {
997         PangoRectangle extents;
998         PangoRectangle cursor_pos;
999
1000         if (entry->preedit.text && entry->preedit.cursor < 0)
1001                 return;
1002
1003         pango_layout_get_extents(entry->layout, &extents, NULL);
1004         pango_layout_get_cursor_pos(entry->layout,
1005                                     entry->cursor + entry->preedit.cursor,
1006                                     &cursor_pos, NULL);
1007
1008         cairo_set_line_width(cr, 1.0);
1009         cairo_move_to(cr, PANGO_PIXELS(cursor_pos.x), PANGO_PIXELS(cursor_pos.y));
1010         cairo_line_to(cr, PANGO_PIXELS(cursor_pos.x), PANGO_PIXELS(cursor_pos.y) + PANGO_PIXELS(cursor_pos.height));
1011         cairo_stroke(cr);
1012 }
1013
1014 static int
1015 text_offset_left(struct rectangle *allocation)
1016 {
1017         return 10;
1018 }
1019
1020 static int
1021 text_offset_top(struct rectangle *allocation)
1022 {
1023         return allocation->height / 2;
1024 }
1025
1026 static void
1027 text_entry_redraw_handler(struct widget *widget, void *data)
1028 {
1029         struct text_entry *entry = data;
1030         cairo_surface_t *surface;
1031         struct rectangle allocation;
1032         cairo_t *cr;
1033
1034         surface = window_get_surface(entry->window);
1035         widget_get_allocation(entry->widget, &allocation);
1036
1037         cr = cairo_create(surface);
1038         cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
1039         cairo_clip(cr);
1040
1041         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1042
1043         cairo_push_group(cr);
1044         cairo_translate(cr, allocation.x, allocation.y);
1045
1046         cairo_set_source_rgba(cr, 1, 1, 1, 1);
1047         cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
1048         cairo_fill(cr);
1049
1050         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1051
1052         if (entry->active) {
1053                 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
1054                 cairo_set_line_width (cr, 3);
1055                 cairo_set_source_rgba(cr, 0, 0, 1, 1.0);
1056                 cairo_stroke(cr);
1057         }
1058
1059         cairo_set_source_rgba(cr, 0, 0, 0, 1);
1060
1061         cairo_translate(cr,
1062                         text_offset_left(&allocation),
1063                         text_offset_top(&allocation));
1064
1065         if (!entry->layout)
1066                 entry->layout = pango_cairo_create_layout(cr);
1067         else
1068                 pango_cairo_update_layout(cr, entry->layout);
1069
1070         text_entry_update_layout(entry);
1071
1072         pango_cairo_show_layout(cr, entry->layout);
1073
1074         text_entry_draw_cursor(entry, cr);
1075
1076         cairo_pop_group_to_source(cr);
1077         cairo_paint(cr);
1078
1079         cairo_destroy(cr);
1080         cairo_surface_destroy(surface);
1081 }
1082
1083 static int
1084 text_entry_motion_handler(struct widget *widget,
1085                           struct input *input, uint32_t time,
1086                           float x, float y, void *data)
1087 {
1088         struct text_entry *entry = data;
1089         struct rectangle allocation;
1090         int tx, ty;
1091
1092         if (!entry->button_pressed) {
1093                 return CURSOR_IBEAM;
1094         }
1095
1096         widget_get_allocation(entry->widget, &allocation);
1097
1098         tx = x - allocation.x - text_offset_left(&allocation);
1099         ty = y - allocation.y - text_offset_top(&allocation);
1100
1101         text_entry_set_cursor_position(entry, tx, ty, false);
1102
1103         return CURSOR_IBEAM;
1104 }
1105
1106 static void
1107 text_entry_button_handler(struct widget *widget,
1108                           struct input *input, uint32_t time,
1109                           uint32_t button,
1110                           enum wl_pointer_button_state state, void *data)
1111 {
1112         struct text_entry *entry = data;
1113         struct rectangle allocation;
1114         struct editor *editor;
1115         int32_t x, y;
1116         uint32_t result;
1117
1118         widget_get_allocation(entry->widget, &allocation);
1119         input_get_position(input, &x, &y);
1120
1121         x -= allocation.x + text_offset_left(&allocation);
1122         y -= allocation.y + text_offset_top(&allocation);
1123
1124         editor = window_get_user_data(entry->window);
1125
1126         if (button == BTN_LEFT) {
1127                 entry->button_pressed = (state == WL_POINTER_BUTTON_STATE_PRESSED);
1128
1129                 if (state == WL_POINTER_BUTTON_STATE_PRESSED)
1130                         input_grab(input, entry->widget, button);
1131                 else
1132                         input_ungrab(input);
1133         }
1134
1135         if (text_entry_has_preedit(entry)) {
1136                 result = text_entry_try_invoke_preedit_action(entry, x, y, button, state);
1137
1138                 if (result)
1139                         return;
1140         }
1141
1142         if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
1143                 struct wl_seat *seat = input_get_seat(input);
1144
1145                 text_entry_activate(entry, seat);
1146                 editor->active_entry = entry;
1147
1148                 text_entry_set_cursor_position(entry, x, y, true);
1149         }
1150 }
1151
1152 static void
1153 text_entry_touch_handler(struct widget *widget, struct input *input,
1154                          uint32_t serial, uint32_t time, int32_t id,
1155                          float tx, float ty, void *data)
1156 {
1157         struct text_entry *entry = data;
1158         struct wl_seat *seat = input_get_seat(input);
1159         struct rectangle allocation;
1160         struct editor *editor;
1161         int32_t x, y;
1162
1163         widget_get_allocation(entry->widget, &allocation);
1164
1165         x = tx - (allocation.x + text_offset_left(&allocation));
1166         y = ty - (allocation.y + text_offset_top(&allocation));
1167
1168         editor = window_get_user_data(entry->window);
1169         text_entry_activate(entry, seat);
1170         editor->active_entry = entry;
1171
1172         text_entry_set_cursor_position(entry, x, y, true);
1173 }
1174
1175 static void
1176 editor_button_handler(struct widget *widget,
1177                       struct input *input, uint32_t time,
1178                       uint32_t button,
1179                       enum wl_pointer_button_state state, void *data)
1180 {
1181         struct editor *editor = data;
1182
1183         if (button != BTN_LEFT) {
1184                 return;
1185         }
1186
1187         if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
1188                 struct wl_seat *seat = input_get_seat(input);
1189
1190                 text_entry_deactivate(editor->entry, seat);
1191                 text_entry_deactivate(editor->editor, seat);
1192                 editor->active_entry = NULL;
1193         }
1194 }
1195
1196 static void
1197 editor_touch_handler(struct widget *widget, struct input *input,
1198                      uint32_t serial, uint32_t time, int32_t id,
1199                      float tx, float ty, void *data)
1200 {
1201         struct editor *editor = data;
1202
1203         struct wl_seat *seat = input_get_seat(input);
1204
1205         text_entry_deactivate(editor->entry, seat);
1206         text_entry_deactivate(editor->editor, seat);
1207         editor->active_entry = NULL;
1208 }
1209
1210 static void
1211 keyboard_focus_handler(struct window *window,
1212                        struct input *device, void *data)
1213 {
1214         struct editor *editor = data;
1215
1216         window_schedule_redraw(editor->window);
1217 }
1218
1219 static void
1220 key_handler(struct window *window,
1221             struct input *input, uint32_t time,
1222             uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
1223             void *data)
1224 {
1225         struct editor *editor = data;
1226         struct text_entry *entry;
1227         const char *new_char;
1228         char text[16];
1229
1230         if (!editor->active_entry)
1231                 return;
1232
1233         entry = editor->active_entry;
1234
1235         if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
1236                 return;
1237
1238         switch (sym) {
1239                 case XKB_KEY_BackSpace:
1240                         text_entry_commit_and_reset(entry);
1241
1242                         new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
1243                         if (new_char != NULL)
1244                                 text_entry_delete_text(entry,
1245                                                        new_char - entry->text,
1246                                                        (entry->text + entry->cursor) - new_char);
1247                         break;
1248                 case XKB_KEY_Delete:
1249                         text_entry_commit_and_reset(entry);
1250
1251                         new_char = utf8_next_char(entry->text + entry->cursor);
1252                         if (new_char != NULL)
1253                                 text_entry_delete_text(entry,
1254                                                        entry->cursor,
1255                                                        new_char - (entry->text + entry->cursor));
1256                         break;
1257                 case XKB_KEY_Left:
1258                         text_entry_commit_and_reset(entry);
1259
1260                         new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
1261                         if (new_char != NULL) {
1262                                 entry->cursor = new_char - entry->text;
1263                                 if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1264                                         entry->anchor = entry->cursor;
1265                                 widget_schedule_redraw(entry->widget);
1266                         }
1267                         break;
1268                 case XKB_KEY_Right:
1269                         text_entry_commit_and_reset(entry);
1270
1271                         new_char = utf8_next_char(entry->text + entry->cursor);
1272                         if (new_char != NULL) {
1273                                 entry->cursor = new_char - entry->text;
1274                                 if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1275                                         entry->anchor = entry->cursor;
1276                                 widget_schedule_redraw(entry->widget);
1277                         }
1278                         break;
1279                 case XKB_KEY_Up:
1280                         text_entry_commit_and_reset(entry);
1281
1282                         move_up(entry->text, &entry->cursor);
1283                         if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1284                                 entry->anchor = entry->cursor;
1285                         widget_schedule_redraw(entry->widget);
1286                         break;
1287                 case XKB_KEY_Down:
1288                         text_entry_commit_and_reset(entry);
1289
1290                         move_down(entry->text, &entry->cursor);
1291                         if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1292                                 entry->anchor = entry->cursor;
1293                         widget_schedule_redraw(entry->widget);
1294                         break;
1295                 case XKB_KEY_Escape:
1296                         break;
1297                 default:
1298                         if (xkb_keysym_to_utf8(sym, text, sizeof(text)) <= 0)
1299                                 break;
1300
1301                         text_entry_commit_and_reset(entry);
1302
1303                         text_entry_insert_at_cursor(entry, text, 0, 0);
1304                         break;
1305         }
1306
1307         widget_schedule_redraw(entry->widget);
1308 }
1309
1310 static void
1311 global_handler(struct display *display, uint32_t name,
1312                const char *interface, uint32_t version, void *data)
1313 {
1314         struct editor *editor = data;
1315
1316         if (!strcmp(interface, "wl_text_input_manager")) {
1317                 editor->text_input_manager =
1318                         display_bind(display, name,
1319                                      &wl_text_input_manager_interface, 1);
1320         }
1321 }
1322
1323 int
1324 main(int argc, char *argv[])
1325 {
1326         struct editor editor;
1327         int i;
1328         uint32_t click_to_show = 0;
1329         const char *preferred_language = NULL;
1330
1331         for (i = 1; i < argc; i++) {
1332                 if (strcmp("--click-to-show", argv[i]) == 0)
1333                         click_to_show = 1;
1334                 else if (strcmp("--preferred-language", argv[i]) == 0 &&
1335                          i + 1 < argc) {
1336                         preferred_language = argv[i + 1];
1337                         i++;
1338                 } else {
1339                         printf("Usage: %s [OPTIONS]\n"
1340                                "  --click-to-show\n"
1341                                "  --preferred-language LANGUAGE\n",
1342                                argv[0]);
1343                         return 1;
1344                 }
1345         }
1346
1347         memset(&editor, 0, sizeof editor);
1348
1349 #ifdef HAVE_PANGO
1350         g_type_init();
1351 #endif
1352
1353         editor.display = display_create(&argc, argv);
1354         if (editor.display == NULL) {
1355                 fprintf(stderr, "failed to create display: %m\n");
1356                 return -1;
1357         }
1358
1359         display_set_user_data(editor.display, &editor);
1360         display_set_global_handler(editor.display, global_handler);
1361
1362         editor.window = window_create(editor.display);
1363         editor.widget = window_frame_create(editor.window, &editor);
1364
1365         editor.entry = text_entry_create(&editor, "Entry");
1366         editor.entry->click_to_show = click_to_show;
1367         if (preferred_language)
1368                 editor.entry->preferred_language = strdup(preferred_language);
1369         editor.editor = text_entry_create(&editor, "Numeric");
1370         editor.editor->content_purpose = WL_TEXT_INPUT_CONTENT_PURPOSE_NUMBER;
1371         editor.editor->click_to_show = click_to_show;
1372
1373         window_set_title(editor.window, "Text Editor");
1374         window_set_key_handler(editor.window, key_handler);
1375         window_set_keyboard_focus_handler(editor.window,
1376                                           keyboard_focus_handler);
1377         window_set_user_data(editor.window, &editor);
1378
1379         widget_set_redraw_handler(editor.widget, redraw_handler);
1380         widget_set_resize_handler(editor.widget, resize_handler);
1381         widget_set_button_handler(editor.widget, editor_button_handler);
1382         widget_set_touch_down_handler(editor.widget, editor_touch_handler);
1383
1384         window_schedule_resize(editor.window, 500, 400);
1385
1386         display_run(editor.display);
1387
1388         text_entry_destroy(editor.entry);
1389         text_entry_destroy(editor.editor);
1390         widget_destroy(editor.widget);
1391         window_destroy(editor.window);
1392         display_destroy(editor.display);
1393
1394         return 0;
1395 }