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