2 * Copyright © 2012 Openismus GmbH
3 * Copyright © 2012 Intel Corporation
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.
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.
33 #include <linux/input.h>
36 #include <pango/pangocairo.h>
39 #include "text-client-protocol.h"
42 struct widget *widget;
43 struct window *window;
52 PangoAttrList *attr_list;
55 PangoAttrList *attr_list;
61 uint32_t delete_index;
62 uint32_t delete_length;
65 struct wl_text_input *text_input;
68 xkb_mod_mask_t shift_mask;
71 uint32_t reset_serial;
72 uint32_t content_purpose;
73 uint32_t click_to_show;
74 char *preferred_language;
79 struct wl_text_input_manager *text_input_manager;
80 struct wl_data_source *selection;
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;
91 utf8_end_char(const char *p)
93 while ((*p & 0xc0) == 0x80)
99 utf8_prev_char(const char *s, const char *p)
101 for (--p; p >= s; --p) {
102 if ((*p & 0xc0) != 0x80)
109 utf8_next_char(const char *p)
112 return utf8_end_char(++p);
117 move_up(const char *p, uint32_t *cursor)
119 const char *posr, *posr_i;
122 xkb_keysym_to_utf8(XKB_KEY_Return, text, sizeof(text));
124 posr = strstr(p, text);
126 if (*cursor > (unsigned)(posr-p)) {
127 posr_i = strstr(posr+1, text);
128 if (!posr_i || !(*cursor > (unsigned)(posr_i-p))) {
140 move_down(const char *p, uint32_t *cursor)
145 xkb_keysym_to_utf8(XKB_KEY_Return, text, sizeof(text));
147 posr = strstr(p, text);
149 if (*cursor <= (unsigned)(posr-p)) {
150 *cursor = posr-p + 1;
153 posr = strstr(posr+1, text);
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,
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,
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);
182 text_input_commit_string(void *data,
183 struct wl_text_input *text_input,
187 struct text_entry *entry = data;
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);
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);
201 text_entry_reset_preedit(entry);
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);
208 text_entry_delete_selected_text(entry);
211 text_entry_insert_at_cursor(entry, text,
212 entry->pending_commit.cursor,
213 entry->pending_commit.anchor);
215 memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
217 widget_schedule_redraw(entry->widget);
221 clear_pending_preedit(struct text_entry *entry)
223 memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
225 pango_attr_list_unref(entry->preedit_info.attr_list);
227 entry->preedit_info.cursor = 0;
228 entry->preedit_info.attr_list = NULL;
230 memset(&entry->preedit_info, 0, sizeof entry->preedit_info);
234 text_input_preedit_string(void *data,
235 struct wl_text_input *text_input,
240 struct text_entry *entry = data;
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);
249 if (entry->pending_commit.invalid_delete) {
250 fprintf(stderr, "Ignore preedit_string. Invalid previous delete_surrounding event.\n");
251 clear_pending_preedit(entry);
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);
260 text_entry_delete_selected_text(entry);
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);
267 clear_pending_preedit(entry);
269 text_entry_update(entry);
271 widget_schedule_redraw(entry->widget);
275 text_input_delete_surrounding_text(void *data,
276 struct wl_text_input *text_input,
280 struct text_entry *entry = data;
281 uint32_t text_length;
283 entry->pending_commit.delete_index = entry->cursor + index;
284 entry->pending_commit.delete_length = length;
285 entry->pending_commit.invalid_delete = false;
287 text_length = strlen(entry->text);
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;
300 text_input_cursor_position(void *data,
301 struct wl_text_input *text_input,
305 struct text_entry *entry = data;
307 entry->pending_commit.cursor = index;
308 entry->pending_commit.anchor = anchor;
312 text_input_preedit_styling(void *data,
313 struct wl_text_input *text_input,
318 struct text_entry *entry = data;
319 PangoAttribute *attr1 = NULL;
320 PangoAttribute *attr2 = NULL;
322 if (!entry->preedit_info.attr_list)
323 entry->preedit_info.attr_list = pango_attr_list_new();
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);
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);
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);
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);
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);
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);
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);
363 text_input_preedit_cursor(void *data,
364 struct wl_text_input *text_input,
367 struct text_entry *entry = data;
369 entry->preedit_info.cursor = index;
373 text_input_modifiers_map(void *data,
374 struct wl_text_input *text_input,
375 struct wl_array *map)
377 struct text_entry *entry = data;
379 entry->keysym.shift_mask = keysym_modifiers_get_mask(map, "Shift");
383 text_input_keysym(void *data,
384 struct wl_text_input *text_input,
391 struct text_entry *entry = data;
392 const char *new_char;
394 if (key == XKB_KEY_Left ||
395 key == XKB_KEY_Right) {
396 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
399 if (key == XKB_KEY_Left)
400 new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
402 new_char = utf8_next_char(entry->text + entry->cursor);
404 if (new_char != NULL) {
405 entry->cursor = new_char - entry->text;
408 if (!(modifiers & entry->keysym.shift_mask))
409 entry->anchor = entry->cursor;
410 widget_schedule_redraw(entry->widget);
415 if (key == XKB_KEY_Up ||
416 key == XKB_KEY_Down) {
417 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
420 if (key == XKB_KEY_Up)
421 move_up(entry->text, &entry->cursor);
423 move_down(entry->text, &entry->cursor);
425 if (!(modifiers & entry->keysym.shift_mask))
426 entry->anchor = entry->cursor;
427 widget_schedule_redraw(entry->widget);
432 if (key == XKB_KEY_BackSpace) {
433 const char *start, *end;
435 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
438 text_entry_commit_and_reset(entry);
440 start = utf8_prev_char(entry->text, entry->text + entry->cursor);
444 end = utf8_next_char(start);
446 text_entry_delete_text(entry,
453 if (key == XKB_KEY_Tab ||
454 key == XKB_KEY_KP_Enter ||
455 key == XKB_KEY_Return) {
458 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
461 xkb_keysym_to_utf8(key, text, sizeof(text));
463 text_entry_insert_at_cursor(entry, text, 0, 0);
470 text_input_enter(void *data,
471 struct wl_text_input *text_input,
472 struct wl_surface *surface)
474 struct text_entry *entry = data;
476 if (surface != window_get_wl_surface(entry->window))
481 text_entry_update(entry);
482 entry->reset_serial = entry->serial;
484 widget_schedule_redraw(entry->widget);
488 text_input_leave(void *data,
489 struct wl_text_input *text_input)
491 struct text_entry *entry = data;
493 text_entry_commit_and_reset(entry);
497 wl_text_input_hide_input_panel(text_input);
499 widget_schedule_redraw(entry->widget);
503 text_input_input_panel_state(void *data,
504 struct wl_text_input *text_input,
510 text_input_language(void *data,
511 struct wl_text_input *text_input,
513 const char *language)
515 fprintf(stderr, "input language is %s \n", language);
519 text_input_text_direction(void *data,
520 struct wl_text_input *text_input,
524 struct text_entry *entry = data;
525 PangoContext *context = pango_layout_get_context(entry->layout);
526 PangoDirection pango_direction;
530 case WL_TEXT_INPUT_TEXT_DIRECTION_LTR:
531 pango_direction = PANGO_DIRECTION_LTR;
533 case WL_TEXT_INPUT_TEXT_DIRECTION_RTL:
534 pango_direction = PANGO_DIRECTION_RTL;
536 case WL_TEXT_INPUT_TEXT_DIRECTION_AUTO:
538 pango_direction = PANGO_DIRECTION_NEUTRAL;
541 pango_context_set_base_dir(context, pango_direction);
544 static const struct wl_text_input_listener text_input_listener = {
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,
557 text_input_text_direction
561 data_source_target(void *data,
562 struct wl_data_source *source, const char *mime_type)
567 data_source_send(void *data,
568 struct wl_data_source *source,
569 const char *mime_type, int32_t fd)
571 struct editor *editor = data;
573 write(fd, editor->selected_text, strlen(editor->selected_text) + 1);
577 data_source_cancelled(void *data, struct wl_data_source *source)
579 wl_data_source_destroy(source);
582 static const struct wl_data_source_listener data_source_listener = {
585 data_source_cancelled
589 paste_func(void *buffer, size_t len,
590 int32_t x, int32_t y, void *data)
592 struct editor *editor = data;
593 struct text_entry *entry = editor->active_entry;
599 pasted_text = malloc(len + 1);
600 strncpy(pasted_text, buffer, len);
601 pasted_text[len] = '\0';
603 text_entry_insert_at_cursor(entry, pasted_text, 0, 0);
609 editor_copy_cut(struct editor *editor, struct input *input, bool cut)
611 struct text_entry *entry = editor->active_entry;
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;
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';
626 text_entry_delete_text(entry, start_index, len);
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));
640 editor_paste(struct editor *editor, struct input *input)
642 input_receive_selection_data(input,
643 "text/plain;charset=utf-8",
648 menu_func(void *data, struct input *input, int index)
650 struct window *window = data;
651 struct editor *editor = window_get_user_data(window);
653 fprintf(stderr, "picked entry %d\n", index);
657 editor_copy_cut(editor, input, true);
660 editor_copy_cut(editor, input, false);
663 editor_paste(editor, input);
669 show_menu(struct editor *editor, struct input *input, uint32_t time)
672 static const char *entries[] = {
673 "Cut", "Copy", "Paste"
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));
682 static struct text_entry*
683 text_entry_create(struct editor *editor, const char *text)
685 struct text_entry *entry;
687 entry = xmalloc(sizeof *entry);
688 memset(entry, 0, sizeof *entry);
690 entry->widget = widget_add_widget(editor->widget, entry);
691 entry->window = editor->window;
692 entry->text = strdup(text);
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);
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);
708 text_entry_destroy(struct text_entry *entry)
710 widget_destroy(entry->widget);
711 wl_text_input_destroy(entry->text_input);
712 g_clear_object(&entry->layout);
718 redraw_handler(struct widget *widget, void *data)
720 struct editor *editor = data;
721 cairo_surface_t *surface;
722 struct rectangle allocation;
725 surface = window_get_surface(editor->window);
726 widget_get_allocation(editor->widget, &allocation);
728 cr = cairo_create(surface);
729 cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
732 cairo_translate(cr, allocation.x, allocation.y);
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);
741 cairo_pop_group_to_source(cr);
745 cairo_surface_destroy(surface);
749 text_entry_allocate(struct text_entry *entry, int32_t x, int32_t y,
750 int32_t width, int32_t height)
752 widget_set_allocation(entry->widget, x, y, width, height);
756 resize_handler(struct widget *widget,
757 int32_t width, int32_t height, void *data)
759 struct editor *editor = data;
760 struct rectangle allocation;
762 widget_get_allocation(editor->widget, &allocation);
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);
773 text_entry_activate(struct text_entry *entry,
774 struct wl_seat *seat)
776 struct wl_surface *surface = window_get_wl_surface(entry->window);
778 if (entry->click_to_show && entry->active) {
779 wl_text_input_show_input_panel(entry->text_input);
784 if (!entry->click_to_show)
785 wl_text_input_show_input_panel(entry->text_input);
787 wl_text_input_activate(entry->text_input,
793 text_entry_deactivate(struct text_entry *entry,
794 struct wl_seat *seat)
796 wl_text_input_deactivate(entry->text_input,
801 text_entry_update_layout(struct text_entry *entry)
804 PangoAttrList *attr_list;
806 assert(entry->cursor <= (strlen(entry->text) +
807 (entry->preedit.text ? strlen(entry->preedit.text) : 0)));
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);
816 text = strdup(entry->text);
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;
824 attr_list = pango_attr_list_copy(entry->preedit.attr_list);
827 attr_list = pango_attr_list_new();
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);
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);
839 attr_list = pango_attr_list_ref(entry->preedit.attr_list);
842 if (entry->preedit.text && !entry->preedit.attr_list) {
843 PangoAttribute *attr;
846 attr_list = pango_attr_list_new();
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);
855 pango_layout_set_text(entry->layout, text, -1);
856 pango_layout_set_attributes(entry->layout, attr_list);
860 pango_attr_list_unref(attr_list);
864 text_entry_update(struct text_entry *entry)
866 struct rectangle cursor_rectangle;
868 wl_text_input_set_content_type(entry->text_input,
869 WL_TEXT_INPUT_CONTENT_HINT_NONE,
870 entry->content_purpose);
872 wl_text_input_set_surrounding_text(entry->text_input,
877 if (entry->preferred_language)
878 wl_text_input_set_preferred_language(entry->text_input,
879 entry->preferred_language);
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);
885 wl_text_input_commit_state(entry->text_input, ++entry->serial);
889 text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
890 int32_t cursor, int32_t anchor)
892 char *new_text = xmalloc(strlen(entry->text) + strlen(text) + 1);
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);
900 entry->text = new_text;
902 entry->anchor = entry->cursor + strlen(text) + anchor;
904 entry->anchor = entry->cursor + 1 + anchor;
907 entry->cursor += strlen(text) + cursor;
909 entry->cursor += 1 + cursor;
911 text_entry_update_layout(entry);
913 widget_schedule_redraw(entry->widget);
915 text_entry_update(entry);
919 text_entry_reset_preedit(struct text_entry *entry)
921 entry->preedit.cursor = 0;
923 free(entry->preedit.text);
924 entry->preedit.text = NULL;
926 free(entry->preedit.commit);
927 entry->preedit.commit = NULL;
929 pango_attr_list_unref(entry->preedit.attr_list);
930 entry->preedit.attr_list = NULL;
934 text_entry_commit_and_reset(struct text_entry *entry)
938 if (entry->preedit.commit)
939 commit = strdup(entry->preedit.commit);
941 text_entry_reset_preedit(entry);
943 text_entry_insert_at_cursor(entry, commit, 0, 0);
947 wl_text_input_reset(entry->text_input);
948 text_entry_update(entry);
949 entry->reset_serial = entry->serial;
953 text_entry_set_preedit(struct text_entry *entry,
954 const char *preedit_text,
957 text_entry_reset_preedit(entry);
962 entry->preedit.text = strdup(preedit_text);
963 entry->preedit.cursor = preedit_cursor;
965 text_entry_update_layout(entry);
967 widget_schedule_redraw(entry->widget);
971 text_entry_try_invoke_preedit_action(struct text_entry *entry,
972 int32_t x, int32_t y,
974 enum wl_pointer_button_state state)
980 if (!entry->preedit.text)
983 pango_layout_xy_to_index(entry->layout,
984 x * PANGO_SCALE, y * PANGO_SCALE,
987 text = pango_layout_get_text(entry->layout);
988 cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
990 if (cursor < entry->cursor ||
991 cursor > entry->cursor + strlen(entry->preedit.text)) {
995 if (state == WL_POINTER_BUTTON_STATE_RELEASED)
996 wl_text_input_invoke_action(entry->text_input,
998 cursor - entry->cursor);
1004 text_entry_has_preedit(struct text_entry *entry)
1006 return entry->preedit.text && (strlen(entry->preedit.text) > 0);
1010 text_entry_set_cursor_position(struct text_entry *entry,
1011 int32_t x, int32_t y,
1014 int index, trailing;
1018 pango_layout_xy_to_index(entry->layout,
1019 x * PANGO_SCALE, y * PANGO_SCALE,
1022 text = pango_layout_get_text(entry->layout);
1024 cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
1027 entry->anchor = cursor;
1029 if (text_entry_has_preedit(entry)) {
1030 text_entry_commit_and_reset(entry);
1032 assert(!text_entry_has_preedit(entry));
1035 if (entry->cursor == cursor)
1038 entry->cursor = cursor;
1040 text_entry_update_layout(entry);
1042 widget_schedule_redraw(entry->widget);
1044 text_entry_update(entry);
1048 text_entry_delete_text(struct text_entry *entry,
1049 uint32_t index, uint32_t length)
1053 assert(index <= strlen(entry->text));
1054 assert(index + length <= strlen(entry->text));
1055 assert(index + length >= length);
1057 l = strlen(entry->text + index + length);
1058 memmove(entry->text + index,
1059 entry->text + index + length,
1062 if (entry->cursor > (index + length))
1063 entry->cursor -= length;
1064 else if (entry->cursor > index)
1065 entry->cursor = index;
1067 entry->anchor = entry->cursor;
1069 text_entry_update_layout(entry);
1071 widget_schedule_redraw(entry->widget);
1073 text_entry_update(entry);
1077 text_entry_delete_selected_text(struct text_entry *entry)
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;
1082 if (entry->anchor == entry->cursor)
1085 text_entry_delete_text(entry, start_index, end_index - start_index);
1087 entry->anchor = entry->cursor;
1091 text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle)
1093 struct rectangle allocation;
1094 PangoRectangle extents;
1095 PangoRectangle cursor_pos;
1097 widget_get_allocation(entry->widget, &allocation);
1099 if (entry->preedit.text && entry->preedit.cursor < 0) {
1102 rectangle->width = 0;
1103 rectangle->height = 0;
1108 pango_layout_get_extents(entry->layout, &extents, NULL);
1109 pango_layout_get_cursor_pos(entry->layout,
1110 entry->cursor + entry->preedit.cursor,
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);
1120 text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr)
1122 PangoRectangle extents;
1123 PangoRectangle cursor_pos;
1125 if (entry->preedit.text && entry->preedit.cursor < 0)
1128 pango_layout_get_extents(entry->layout, &extents, NULL);
1129 pango_layout_get_cursor_pos(entry->layout,
1130 entry->cursor + entry->preedit.cursor,
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));
1140 text_offset_left(struct rectangle *allocation)
1146 text_offset_top(struct rectangle *allocation)
1148 return allocation->height / 2;
1152 text_entry_redraw_handler(struct widget *widget, void *data)
1154 struct text_entry *entry = data;
1155 cairo_surface_t *surface;
1156 struct rectangle allocation;
1159 surface = window_get_surface(entry->window);
1160 widget_get_allocation(entry->widget, &allocation);
1162 cr = cairo_create(surface);
1163 cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
1166 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1168 cairo_push_group(cr);
1169 cairo_translate(cr, allocation.x, allocation.y);
1171 cairo_set_source_rgba(cr, 1, 1, 1, 1);
1172 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
1175 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
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);
1184 cairo_set_source_rgba(cr, 0, 0, 0, 1);
1187 text_offset_left(&allocation),
1188 text_offset_top(&allocation));
1191 entry->layout = pango_cairo_create_layout(cr);
1193 pango_cairo_update_layout(cr, entry->layout);
1195 text_entry_update_layout(entry);
1197 pango_cairo_show_layout(cr, entry->layout);
1199 text_entry_draw_cursor(entry, cr);
1201 cairo_pop_group_to_source(cr);
1205 cairo_surface_destroy(surface);
1209 text_entry_motion_handler(struct widget *widget,
1210 struct input *input, uint32_t time,
1211 float x, float y, void *data)
1213 struct text_entry *entry = data;
1214 struct rectangle allocation;
1217 if (!entry->button_pressed) {
1218 return CURSOR_IBEAM;
1221 widget_get_allocation(entry->widget, &allocation);
1223 tx = x - allocation.x - text_offset_left(&allocation);
1224 ty = y - allocation.y - text_offset_top(&allocation);
1226 text_entry_set_cursor_position(entry, tx, ty, false);
1228 return CURSOR_IBEAM;
1232 text_entry_button_handler(struct widget *widget,
1233 struct input *input, uint32_t time,
1235 enum wl_pointer_button_state state, void *data)
1237 struct text_entry *entry = data;
1238 struct rectangle allocation;
1239 struct editor *editor;
1243 widget_get_allocation(entry->widget, &allocation);
1244 input_get_position(input, &x, &y);
1246 x -= allocation.x + text_offset_left(&allocation);
1247 y -= allocation.y + text_offset_top(&allocation);
1249 editor = window_get_user_data(entry->window);
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);
1257 input_ungrab(input);
1260 if (state == WL_POINTER_BUTTON_STATE_PRESSED)
1261 show_menu(editor, input, time);
1265 if (text_entry_has_preedit(entry)) {
1266 result = text_entry_try_invoke_preedit_action(entry, x, y, button, state);
1272 if (state == WL_POINTER_BUTTON_STATE_PRESSED &&
1273 button == BTN_LEFT) {
1274 struct wl_seat *seat = input_get_seat(input);
1276 text_entry_activate(entry, seat);
1277 editor->active_entry = entry;
1279 text_entry_set_cursor_position(entry, x, y, true);
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)
1288 struct text_entry *entry = data;
1289 struct wl_seat *seat = input_get_seat(input);
1290 struct rectangle allocation;
1291 struct editor *editor;
1294 widget_get_allocation(entry->widget, &allocation);
1296 x = tx - (allocation.x + text_offset_left(&allocation));
1297 y = ty - (allocation.y + text_offset_top(&allocation));
1299 editor = window_get_user_data(entry->window);
1300 text_entry_activate(entry, seat);
1301 editor->active_entry = entry;
1303 text_entry_set_cursor_position(entry, x, y, true);
1307 editor_button_handler(struct widget *widget,
1308 struct input *input, uint32_t time,
1310 enum wl_pointer_button_state state, void *data)
1312 struct editor *editor = data;
1314 if (button != BTN_LEFT) {
1318 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
1319 struct wl_seat *seat = input_get_seat(input);
1321 text_entry_deactivate(editor->entry, seat);
1322 text_entry_deactivate(editor->editor, seat);
1323 editor->active_entry = NULL;
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)
1332 struct editor *editor = data;
1334 struct wl_seat *seat = input_get_seat(input);
1336 text_entry_deactivate(editor->entry, seat);
1337 text_entry_deactivate(editor->editor, seat);
1338 editor->active_entry = NULL;
1342 keyboard_focus_handler(struct window *window,
1343 struct input *device, void *data)
1345 struct editor *editor = data;
1347 window_schedule_redraw(editor->window);
1351 handle_bound_key(struct editor *editor,
1352 struct input *input, uint32_t sym, uint32_t time)
1356 editor_copy_cut(editor, input, true);
1359 editor_copy_cut(editor, input, false);
1362 editor_paste(editor, input);
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,
1375 struct editor *editor = data;
1376 struct text_entry *entry;
1377 const char *new_char;
1381 if (!editor->active_entry)
1384 entry = editor->active_entry;
1386 if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
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))
1396 case XKB_KEY_BackSpace:
1397 text_entry_commit_and_reset(entry);
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);
1405 case XKB_KEY_Delete:
1406 text_entry_commit_and_reset(entry);
1408 new_char = utf8_next_char(entry->text + entry->cursor);
1409 if (new_char != NULL)
1410 text_entry_delete_text(entry,
1412 new_char - (entry->text + entry->cursor));
1415 text_entry_commit_and_reset(entry);
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);
1426 text_entry_commit_and_reset(entry);
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);
1437 text_entry_commit_and_reset(entry);
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);
1445 text_entry_commit_and_reset(entry);
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);
1452 case XKB_KEY_Escape:
1455 if (xkb_keysym_to_utf8(sym, text, sizeof(text)) <= 0)
1458 text_entry_commit_and_reset(entry);
1460 text_entry_insert_at_cursor(entry, text, 0, 0);
1464 widget_schedule_redraw(entry->widget);
1468 global_handler(struct display *display, uint32_t name,
1469 const char *interface, uint32_t version, void *data)
1471 struct editor *editor = data;
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);
1481 main(int argc, char *argv[])
1483 struct editor editor;
1485 uint32_t click_to_show = 0;
1486 const char *preferred_language = NULL;
1488 for (i = 1; i < argc; i++) {
1489 if (strcmp("--click-to-show", argv[i]) == 0)
1491 else if (strcmp("--preferred-language", argv[i]) == 0 &&
1493 preferred_language = argv[i + 1];
1496 printf("Usage: %s [OPTIONS]\n"
1497 " --click-to-show\n"
1498 " --preferred-language LANGUAGE\n",
1504 memset(&editor, 0, sizeof editor);
1510 editor.display = display_create(&argc, argv);
1511 if (editor.display == NULL) {
1512 fprintf(stderr, "failed to create display: %m\n");
1516 display_set_user_data(editor.display, &editor);
1517 display_set_global_handler(editor.display, global_handler);
1519 if (editor.text_input_manager == NULL) {
1520 fprintf(stderr, "No text input manager global\n");
1524 editor.window = window_create(editor.display);
1525 editor.widget = window_frame_create(editor.window, &editor);
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;
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);
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);
1548 window_schedule_resize(editor.window, 500, 400);
1550 display_run(editor.display);
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);