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.
32 #include <linux/input.h>
35 #include <pango/pangocairo.h>
38 #include "text-client-protocol.h"
41 struct widget *widget;
42 struct window *window;
51 PangoAttrList *attr_list;
54 PangoAttrList *attr_list;
60 uint32_t delete_index;
61 uint32_t delete_length;
64 struct wl_text_input *text_input;
67 xkb_mod_mask_t shift_mask;
70 uint32_t reset_serial;
71 uint32_t content_purpose;
72 uint32_t click_to_show;
73 char *preferred_language;
78 struct wl_text_input_manager *text_input_manager;
79 struct display *display;
80 struct window *window;
81 struct widget *widget;
82 struct text_entry *entry;
83 struct text_entry *editor;
84 struct text_entry *active_entry;
88 utf8_end_char(const char *p)
90 while ((*p & 0xc0) == 0x80)
96 utf8_prev_char(const char *s, const char *p)
98 for (--p; p >= s; --p) {
99 if ((*p & 0xc0) != 0x80)
106 utf8_next_char(const char *p)
109 return utf8_end_char(++p);
114 move_up(const char *p, uint32_t *cursor)
116 const char *posr, *posr_i;
119 xkb_keysym_to_utf8(XKB_KEY_Return, text, sizeof(text));
121 posr = strstr(p, text);
123 if (*cursor > (unsigned)(posr-p)) {
124 posr_i = strstr(posr+1, text);
125 if (!posr_i || !(*cursor > (unsigned)(posr_i-p))) {
137 move_down(const char *p, uint32_t *cursor)
142 xkb_keysym_to_utf8(XKB_KEY_Return, text, sizeof(text));
144 posr = strstr(p, text);
146 if (*cursor <= (unsigned)(posr-p)) {
147 *cursor = posr-p + 1;
150 posr = strstr(posr+1, text);
154 static void text_entry_redraw_handler(struct widget *widget, void *data);
155 static void text_entry_button_handler(struct widget *widget,
156 struct input *input, uint32_t time,
158 enum wl_pointer_button_state state, void *data);
159 static void text_entry_touch_handler(struct widget *widget, struct input *input,
160 uint32_t serial, uint32_t time, int32_t id,
161 float tx, float ty, void *data);
162 static int text_entry_motion_handler(struct widget *widget,
163 struct input *input, uint32_t time,
164 float x, float y, void *data);
165 static void text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
166 int32_t cursor, int32_t anchor);
167 static void text_entry_set_preedit(struct text_entry *entry,
168 const char *preedit_text,
170 static void text_entry_delete_text(struct text_entry *entry,
171 uint32_t index, uint32_t length);
172 static void text_entry_delete_selected_text(struct text_entry *entry);
173 static void text_entry_reset_preedit(struct text_entry *entry);
174 static void text_entry_commit_and_reset(struct text_entry *entry);
175 static void text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle);
176 static void text_entry_update(struct text_entry *entry);
179 text_input_commit_string(void *data,
180 struct wl_text_input *text_input,
184 struct text_entry *entry = data;
186 if ((entry->serial - serial) > (entry->serial - entry->reset_serial)) {
187 fprintf(stderr, "Ignore commit. Serial: %u, Current: %u, Reset: %u\n",
188 serial, entry->serial, entry->reset_serial);
192 if (entry->pending_commit.invalid_delete) {
193 fprintf(stderr, "Ignore commit. Invalid previous delete_surrounding event.\n");
194 memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
198 text_entry_reset_preedit(entry);
200 if (entry->pending_commit.delete_length) {
201 text_entry_delete_text(entry,
202 entry->pending_commit.delete_index,
203 entry->pending_commit.delete_length);
205 text_entry_delete_selected_text(entry);
208 text_entry_insert_at_cursor(entry, text,
209 entry->pending_commit.cursor,
210 entry->pending_commit.anchor);
212 memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
214 widget_schedule_redraw(entry->widget);
218 clear_pending_preedit(struct text_entry *entry)
220 memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
222 pango_attr_list_unref(entry->preedit_info.attr_list);
224 entry->preedit_info.cursor = 0;
225 entry->preedit_info.attr_list = NULL;
227 memset(&entry->preedit_info, 0, sizeof entry->preedit_info);
231 text_input_preedit_string(void *data,
232 struct wl_text_input *text_input,
237 struct text_entry *entry = data;
239 if ((entry->serial - serial) > (entry->serial - entry->reset_serial)) {
240 fprintf(stderr, "Ignore preedit_string. Serial: %u, Current: %u, Reset: %u\n",
241 serial, entry->serial, entry->reset_serial);
242 clear_pending_preedit(entry);
246 if (entry->pending_commit.invalid_delete) {
247 fprintf(stderr, "Ignore preedit_string. Invalid previous delete_surrounding event.\n");
248 clear_pending_preedit(entry);
252 if (entry->pending_commit.delete_length) {
253 text_entry_delete_text(entry,
254 entry->pending_commit.delete_index,
255 entry->pending_commit.delete_length);
257 text_entry_delete_selected_text(entry);
260 text_entry_set_preedit(entry, text, entry->preedit_info.cursor);
261 entry->preedit.commit = strdup(commit);
262 entry->preedit.attr_list = pango_attr_list_ref(entry->preedit_info.attr_list);
264 clear_pending_preedit(entry);
266 text_entry_update(entry);
268 widget_schedule_redraw(entry->widget);
272 text_input_delete_surrounding_text(void *data,
273 struct wl_text_input *text_input,
277 struct text_entry *entry = data;
278 uint32_t text_length;
280 entry->pending_commit.delete_index = entry->cursor + index;
281 entry->pending_commit.delete_length = length;
282 entry->pending_commit.invalid_delete = false;
284 text_length = strlen(entry->text);
286 if (entry->pending_commit.delete_index > text_length ||
287 length > text_length ||
288 entry->pending_commit.delete_index + length > text_length) {
289 fprintf(stderr, "delete_surrounding_text: Invalid index: %d," \
290 "length %u'; cursor: %u text length: %u\n", index, length, entry->cursor, text_length);
291 entry->pending_commit.invalid_delete = true;
297 text_input_cursor_position(void *data,
298 struct wl_text_input *text_input,
302 struct text_entry *entry = data;
304 entry->pending_commit.cursor = index;
305 entry->pending_commit.anchor = anchor;
309 text_input_preedit_styling(void *data,
310 struct wl_text_input *text_input,
315 struct text_entry *entry = data;
316 PangoAttribute *attr1 = NULL;
317 PangoAttribute *attr2 = NULL;
319 if (!entry->preedit_info.attr_list)
320 entry->preedit_info.attr_list = pango_attr_list_new();
323 case WL_TEXT_INPUT_PREEDIT_STYLE_DEFAULT:
324 case WL_TEXT_INPUT_PREEDIT_STYLE_UNDERLINE:
325 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
327 case WL_TEXT_INPUT_PREEDIT_STYLE_INCORRECT:
328 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_ERROR);
329 attr2 = pango_attr_underline_color_new(65535, 0, 0);
331 case WL_TEXT_INPUT_PREEDIT_STYLE_SELECTION:
332 attr1 = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
333 attr2 = pango_attr_foreground_new(65535, 65535, 65535);
335 case WL_TEXT_INPUT_PREEDIT_STYLE_HIGHLIGHT:
336 case WL_TEXT_INPUT_PREEDIT_STYLE_ACTIVE:
337 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
338 attr2 = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
340 case WL_TEXT_INPUT_PREEDIT_STYLE_INACTIVE:
341 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
342 attr2 = pango_attr_foreground_new(0.3 * 65535, 0.3 * 65535, 0.3 * 65535);
347 attr1->start_index = entry->cursor + index;
348 attr1->end_index = entry->cursor + index + length;
349 pango_attr_list_insert(entry->preedit_info.attr_list, attr1);
353 attr2->start_index = entry->cursor + index;
354 attr2->end_index = entry->cursor + index + length;
355 pango_attr_list_insert(entry->preedit_info.attr_list, attr2);
360 text_input_preedit_cursor(void *data,
361 struct wl_text_input *text_input,
364 struct text_entry *entry = data;
366 entry->preedit_info.cursor = index;
370 text_input_modifiers_map(void *data,
371 struct wl_text_input *text_input,
372 struct wl_array *map)
374 struct text_entry *entry = data;
376 entry->keysym.shift_mask = keysym_modifiers_get_mask(map, "Shift");
380 text_input_keysym(void *data,
381 struct wl_text_input *text_input,
388 struct text_entry *entry = data;
389 const char *new_char;
391 if (key == XKB_KEY_Left ||
392 key == XKB_KEY_Right) {
393 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
396 if (key == XKB_KEY_Left)
397 new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
399 new_char = utf8_next_char(entry->text + entry->cursor);
401 if (new_char != NULL) {
402 entry->cursor = new_char - entry->text;
405 if (!(modifiers & entry->keysym.shift_mask))
406 entry->anchor = entry->cursor;
407 widget_schedule_redraw(entry->widget);
412 if (key == XKB_KEY_Up ||
413 key == XKB_KEY_Down) {
414 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
417 if (key == XKB_KEY_Up)
418 move_up(entry->text, &entry->cursor);
420 move_down(entry->text, &entry->cursor);
422 if (!(modifiers & entry->keysym.shift_mask))
423 entry->anchor = entry->cursor;
424 widget_schedule_redraw(entry->widget);
429 if (key == XKB_KEY_BackSpace) {
430 const char *start, *end;
432 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
435 text_entry_commit_and_reset(entry);
437 start = utf8_prev_char(entry->text, entry->text + entry->cursor);
441 end = utf8_next_char(start);
443 text_entry_delete_text(entry,
450 if (key == XKB_KEY_Tab ||
451 key == XKB_KEY_KP_Enter ||
452 key == XKB_KEY_Return) {
455 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
458 xkb_keysym_to_utf8(key, text, sizeof(text));
460 text_entry_insert_at_cursor(entry, text, 0, 0);
467 text_input_enter(void *data,
468 struct wl_text_input *text_input,
469 struct wl_surface *surface)
471 struct text_entry *entry = data;
473 if (surface != window_get_wl_surface(entry->window))
478 text_entry_update(entry);
479 entry->reset_serial = entry->serial;
481 widget_schedule_redraw(entry->widget);
485 text_input_leave(void *data,
486 struct wl_text_input *text_input)
488 struct text_entry *entry = data;
490 text_entry_commit_and_reset(entry);
494 wl_text_input_hide_input_panel(text_input);
496 widget_schedule_redraw(entry->widget);
500 text_input_input_panel_state(void *data,
501 struct wl_text_input *text_input,
507 text_input_language(void *data,
508 struct wl_text_input *text_input,
510 const char *language)
512 fprintf(stderr, "input language is %s \n", language);
516 text_input_text_direction(void *data,
517 struct wl_text_input *text_input,
521 struct text_entry *entry = data;
522 PangoContext *context = pango_layout_get_context(entry->layout);
523 PangoDirection pango_direction;
527 case WL_TEXT_INPUT_TEXT_DIRECTION_LTR:
528 pango_direction = PANGO_DIRECTION_LTR;
530 case WL_TEXT_INPUT_TEXT_DIRECTION_RTL:
531 pango_direction = PANGO_DIRECTION_RTL;
533 case WL_TEXT_INPUT_TEXT_DIRECTION_AUTO:
535 pango_direction = PANGO_DIRECTION_NEUTRAL;
538 pango_context_set_base_dir(context, pango_direction);
541 static const struct wl_text_input_listener text_input_listener = {
544 text_input_modifiers_map,
545 text_input_input_panel_state,
546 text_input_preedit_string,
547 text_input_preedit_styling,
548 text_input_preedit_cursor,
549 text_input_commit_string,
550 text_input_cursor_position,
551 text_input_delete_surrounding_text,
554 text_input_text_direction
557 static struct text_entry*
558 text_entry_create(struct editor *editor, const char *text)
560 struct text_entry *entry;
562 entry = xmalloc(sizeof *entry);
563 memset(entry, 0, sizeof *entry);
565 entry->widget = widget_add_widget(editor->widget, entry);
566 entry->window = editor->window;
567 entry->text = strdup(text);
569 entry->cursor = strlen(text);
570 entry->anchor = entry->cursor;
571 entry->text_input = wl_text_input_manager_create_text_input(editor->text_input_manager);
572 wl_text_input_add_listener(entry->text_input, &text_input_listener, entry);
574 widget_set_redraw_handler(entry->widget, text_entry_redraw_handler);
575 widget_set_button_handler(entry->widget, text_entry_button_handler);
576 widget_set_motion_handler(entry->widget, text_entry_motion_handler);
577 widget_set_touch_down_handler(entry->widget, text_entry_touch_handler);
583 text_entry_destroy(struct text_entry *entry)
585 widget_destroy(entry->widget);
586 wl_text_input_destroy(entry->text_input);
587 g_clear_object(&entry->layout);
593 redraw_handler(struct widget *widget, void *data)
595 struct editor *editor = data;
596 cairo_surface_t *surface;
597 struct rectangle allocation;
600 surface = window_get_surface(editor->window);
601 widget_get_allocation(editor->widget, &allocation);
603 cr = cairo_create(surface);
604 cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
607 cairo_translate(cr, allocation.x, allocation.y);
609 /* Draw background */
610 cairo_push_group(cr);
611 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
612 cairo_set_source_rgba(cr, 1, 1, 1, 1);
613 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
616 cairo_pop_group_to_source(cr);
620 cairo_surface_destroy(surface);
624 text_entry_allocate(struct text_entry *entry, int32_t x, int32_t y,
625 int32_t width, int32_t height)
627 widget_set_allocation(entry->widget, x, y, width, height);
631 resize_handler(struct widget *widget,
632 int32_t width, int32_t height, void *data)
634 struct editor *editor = data;
635 struct rectangle allocation;
637 widget_get_allocation(editor->widget, &allocation);
639 text_entry_allocate(editor->entry,
640 allocation.x + 20, allocation.y + 20,
641 width - 40, height / 2 - 40);
642 text_entry_allocate(editor->editor,
643 allocation.x + 20, allocation.y + height / 2 + 20,
644 width - 40, height / 2 - 40);
648 text_entry_activate(struct text_entry *entry,
649 struct wl_seat *seat)
651 struct wl_surface *surface = window_get_wl_surface(entry->window);
653 if (entry->click_to_show && entry->active) {
654 wl_text_input_show_input_panel(entry->text_input);
659 if (!entry->click_to_show)
660 wl_text_input_show_input_panel(entry->text_input);
662 wl_text_input_activate(entry->text_input,
668 text_entry_deactivate(struct text_entry *entry,
669 struct wl_seat *seat)
671 wl_text_input_deactivate(entry->text_input,
676 text_entry_update_layout(struct text_entry *entry)
679 PangoAttrList *attr_list;
681 assert(entry->cursor <= (strlen(entry->text) +
682 (entry->preedit.text ? strlen(entry->preedit.text) : 0)));
684 if (entry->preedit.text) {
685 text = malloc(strlen(entry->text) + strlen(entry->preedit.text) + 1);
686 strncpy(text, entry->text, entry->cursor);
687 strcpy(text + entry->cursor, entry->preedit.text);
688 strcpy(text + entry->cursor + strlen(entry->preedit.text),
689 entry->text + entry->cursor);
691 text = strdup(entry->text);
694 if (entry->cursor != entry->anchor) {
695 int start_index = MIN(entry->cursor, entry->anchor);
696 int end_index = MAX(entry->cursor, entry->anchor);
697 PangoAttribute *attr;
699 attr_list = pango_attr_list_copy(entry->preedit.attr_list);
702 attr_list = pango_attr_list_new();
704 attr = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
705 attr->start_index = start_index;
706 attr->end_index = end_index;
707 pango_attr_list_insert(attr_list, attr);
709 attr = pango_attr_foreground_new(65535, 65535, 65535);
710 attr->start_index = start_index;
711 attr->end_index = end_index;
712 pango_attr_list_insert(attr_list, attr);
714 attr_list = pango_attr_list_ref(entry->preedit.attr_list);
717 if (entry->preedit.text && !entry->preedit.attr_list) {
718 PangoAttribute *attr;
721 attr_list = pango_attr_list_new();
723 attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
724 attr->start_index = entry->cursor;
725 attr->end_index = entry->cursor + strlen(entry->preedit.text);
726 pango_attr_list_insert(attr_list, attr);
730 pango_layout_set_text(entry->layout, text, -1);
731 pango_layout_set_attributes(entry->layout, attr_list);
735 pango_attr_list_unref(attr_list);
739 text_entry_update(struct text_entry *entry)
741 struct rectangle cursor_rectangle;
743 wl_text_input_set_content_type(entry->text_input,
744 WL_TEXT_INPUT_CONTENT_HINT_NONE,
745 entry->content_purpose);
747 wl_text_input_set_surrounding_text(entry->text_input,
752 if (entry->preferred_language)
753 wl_text_input_set_preferred_language(entry->text_input,
754 entry->preferred_language);
756 text_entry_get_cursor_rectangle(entry, &cursor_rectangle);
757 wl_text_input_set_cursor_rectangle(entry->text_input, cursor_rectangle.x, cursor_rectangle.y,
758 cursor_rectangle.width, cursor_rectangle.height);
760 wl_text_input_commit_state(entry->text_input, ++entry->serial);
764 text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
765 int32_t cursor, int32_t anchor)
767 char *new_text = malloc(strlen(entry->text) + strlen(text) + 1);
769 strncpy(new_text, entry->text, entry->cursor);
770 strcpy(new_text + entry->cursor, text);
771 strcpy(new_text + entry->cursor + strlen(text),
772 entry->text + entry->cursor);
775 entry->text = new_text;
777 entry->anchor = entry->cursor + strlen(text) + anchor;
779 entry->anchor = entry->cursor + 1 + anchor;
782 entry->cursor += strlen(text) + cursor;
784 entry->cursor += 1 + cursor;
786 text_entry_update_layout(entry);
788 widget_schedule_redraw(entry->widget);
790 text_entry_update(entry);
794 text_entry_reset_preedit(struct text_entry *entry)
796 entry->preedit.cursor = 0;
798 free(entry->preedit.text);
799 entry->preedit.text = NULL;
801 free(entry->preedit.commit);
802 entry->preedit.commit = NULL;
804 pango_attr_list_unref(entry->preedit.attr_list);
805 entry->preedit.attr_list = NULL;
809 text_entry_commit_and_reset(struct text_entry *entry)
813 if (entry->preedit.commit)
814 commit = strdup(entry->preedit.commit);
816 text_entry_reset_preedit(entry);
818 text_entry_insert_at_cursor(entry, commit, 0, 0);
822 wl_text_input_reset(entry->text_input);
823 text_entry_update(entry);
824 entry->reset_serial = entry->serial;
828 text_entry_set_preedit(struct text_entry *entry,
829 const char *preedit_text,
832 text_entry_reset_preedit(entry);
837 entry->preedit.text = strdup(preedit_text);
838 entry->preedit.cursor = preedit_cursor;
840 text_entry_update_layout(entry);
842 widget_schedule_redraw(entry->widget);
846 text_entry_try_invoke_preedit_action(struct text_entry *entry,
847 int32_t x, int32_t y,
849 enum wl_pointer_button_state state)
855 if (!entry->preedit.text)
858 pango_layout_xy_to_index(entry->layout,
859 x * PANGO_SCALE, y * PANGO_SCALE,
862 text = pango_layout_get_text(entry->layout);
863 cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
865 if (cursor < entry->cursor ||
866 cursor > entry->cursor + strlen(entry->preedit.text)) {
870 if (state == WL_POINTER_BUTTON_STATE_RELEASED)
871 wl_text_input_invoke_action(entry->text_input,
873 cursor - entry->cursor);
879 text_entry_has_preedit(struct text_entry *entry)
881 return entry->preedit.text && (strlen(entry->preedit.text) > 0);
885 text_entry_set_cursor_position(struct text_entry *entry,
886 int32_t x, int32_t y,
893 pango_layout_xy_to_index(entry->layout,
894 x * PANGO_SCALE, y * PANGO_SCALE,
897 text = pango_layout_get_text(entry->layout);
899 cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
902 entry->anchor = cursor;
904 if (text_entry_has_preedit(entry)) {
905 text_entry_commit_and_reset(entry);
907 assert(!text_entry_has_preedit(entry));
910 if (entry->cursor == cursor)
913 entry->cursor = cursor;
915 text_entry_update_layout(entry);
917 widget_schedule_redraw(entry->widget);
919 text_entry_update(entry);
923 text_entry_delete_text(struct text_entry *entry,
924 uint32_t index, uint32_t length)
928 assert(index <= strlen(entry->text));
929 assert(index + length <= strlen(entry->text));
930 assert(index + length >= length);
932 l = strlen(entry->text + index + length);
933 memmove(entry->text + index,
934 entry->text + index + length,
937 if (entry->cursor > (index + length))
938 entry->cursor -= length;
939 else if (entry->cursor > index)
940 entry->cursor = index;
942 entry->anchor = entry->cursor;
944 text_entry_update_layout(entry);
946 widget_schedule_redraw(entry->widget);
948 text_entry_update(entry);
952 text_entry_delete_selected_text(struct text_entry *entry)
954 uint32_t start_index = entry->anchor < entry->cursor ? entry->anchor : entry->cursor;
955 uint32_t end_index = entry->anchor < entry->cursor ? entry->cursor : entry->anchor;
957 if (entry->anchor == entry->cursor)
960 text_entry_delete_text(entry, start_index, end_index - start_index);
962 entry->anchor = entry->cursor;
966 text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle)
968 struct rectangle allocation;
969 PangoRectangle extents;
970 PangoRectangle cursor_pos;
972 widget_get_allocation(entry->widget, &allocation);
974 if (entry->preedit.text && entry->preedit.cursor < 0) {
977 rectangle->width = 0;
978 rectangle->height = 0;
983 pango_layout_get_extents(entry->layout, &extents, NULL);
984 pango_layout_get_cursor_pos(entry->layout,
985 entry->cursor + entry->preedit.cursor,
988 rectangle->x = allocation.x + (allocation.height / 2) + PANGO_PIXELS(cursor_pos.x);
989 rectangle->y = allocation.y + 10 + PANGO_PIXELS(cursor_pos.y);
990 rectangle->width = PANGO_PIXELS(cursor_pos.width);
991 rectangle->height = PANGO_PIXELS(cursor_pos.height);
995 text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr)
997 PangoRectangle extents;
998 PangoRectangle cursor_pos;
1000 if (entry->preedit.text && entry->preedit.cursor < 0)
1003 pango_layout_get_extents(entry->layout, &extents, NULL);
1004 pango_layout_get_cursor_pos(entry->layout,
1005 entry->cursor + entry->preedit.cursor,
1008 cairo_set_line_width(cr, 1.0);
1009 cairo_move_to(cr, PANGO_PIXELS(cursor_pos.x), PANGO_PIXELS(cursor_pos.y));
1010 cairo_line_to(cr, PANGO_PIXELS(cursor_pos.x), PANGO_PIXELS(cursor_pos.y) + PANGO_PIXELS(cursor_pos.height));
1015 text_offset_left(struct rectangle *allocation)
1021 text_offset_top(struct rectangle *allocation)
1023 return allocation->height / 2;
1027 text_entry_redraw_handler(struct widget *widget, void *data)
1029 struct text_entry *entry = data;
1030 cairo_surface_t *surface;
1031 struct rectangle allocation;
1034 surface = window_get_surface(entry->window);
1035 widget_get_allocation(entry->widget, &allocation);
1037 cr = cairo_create(surface);
1038 cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
1041 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1043 cairo_push_group(cr);
1044 cairo_translate(cr, allocation.x, allocation.y);
1046 cairo_set_source_rgba(cr, 1, 1, 1, 1);
1047 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
1050 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1052 if (entry->active) {
1053 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
1054 cairo_set_line_width (cr, 3);
1055 cairo_set_source_rgba(cr, 0, 0, 1, 1.0);
1059 cairo_set_source_rgba(cr, 0, 0, 0, 1);
1062 text_offset_left(&allocation),
1063 text_offset_top(&allocation));
1066 entry->layout = pango_cairo_create_layout(cr);
1068 pango_cairo_update_layout(cr, entry->layout);
1070 text_entry_update_layout(entry);
1072 pango_cairo_show_layout(cr, entry->layout);
1074 text_entry_draw_cursor(entry, cr);
1076 cairo_pop_group_to_source(cr);
1080 cairo_surface_destroy(surface);
1084 text_entry_motion_handler(struct widget *widget,
1085 struct input *input, uint32_t time,
1086 float x, float y, void *data)
1088 struct text_entry *entry = data;
1089 struct rectangle allocation;
1092 if (!entry->button_pressed) {
1093 return CURSOR_IBEAM;
1096 widget_get_allocation(entry->widget, &allocation);
1098 tx = x - allocation.x - text_offset_left(&allocation);
1099 ty = y - allocation.y - text_offset_top(&allocation);
1101 text_entry_set_cursor_position(entry, tx, ty, false);
1103 return CURSOR_IBEAM;
1107 text_entry_button_handler(struct widget *widget,
1108 struct input *input, uint32_t time,
1110 enum wl_pointer_button_state state, void *data)
1112 struct text_entry *entry = data;
1113 struct rectangle allocation;
1114 struct editor *editor;
1118 widget_get_allocation(entry->widget, &allocation);
1119 input_get_position(input, &x, &y);
1121 x -= allocation.x + text_offset_left(&allocation);
1122 y -= allocation.y + text_offset_top(&allocation);
1124 editor = window_get_user_data(entry->window);
1126 if (button == BTN_LEFT) {
1127 entry->button_pressed = (state == WL_POINTER_BUTTON_STATE_PRESSED);
1129 if (state == WL_POINTER_BUTTON_STATE_PRESSED)
1130 input_grab(input, entry->widget, button);
1132 input_ungrab(input);
1135 if (text_entry_has_preedit(entry)) {
1136 result = text_entry_try_invoke_preedit_action(entry, x, y, button, state);
1142 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
1143 struct wl_seat *seat = input_get_seat(input);
1145 text_entry_activate(entry, seat);
1146 editor->active_entry = entry;
1148 text_entry_set_cursor_position(entry, x, y, true);
1153 text_entry_touch_handler(struct widget *widget, struct input *input,
1154 uint32_t serial, uint32_t time, int32_t id,
1155 float tx, float ty, void *data)
1157 struct text_entry *entry = data;
1158 struct wl_seat *seat = input_get_seat(input);
1159 struct rectangle allocation;
1160 struct editor *editor;
1163 widget_get_allocation(entry->widget, &allocation);
1165 x = tx - (allocation.x + text_offset_left(&allocation));
1166 y = ty - (allocation.y + text_offset_top(&allocation));
1168 editor = window_get_user_data(entry->window);
1169 text_entry_activate(entry, seat);
1170 editor->active_entry = entry;
1172 text_entry_set_cursor_position(entry, x, y, true);
1176 editor_button_handler(struct widget *widget,
1177 struct input *input, uint32_t time,
1179 enum wl_pointer_button_state state, void *data)
1181 struct editor *editor = data;
1183 if (button != BTN_LEFT) {
1187 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
1188 struct wl_seat *seat = input_get_seat(input);
1190 text_entry_deactivate(editor->entry, seat);
1191 text_entry_deactivate(editor->editor, seat);
1192 editor->active_entry = NULL;
1197 editor_touch_handler(struct widget *widget, struct input *input,
1198 uint32_t serial, uint32_t time, int32_t id,
1199 float tx, float ty, void *data)
1201 struct editor *editor = data;
1203 struct wl_seat *seat = input_get_seat(input);
1205 text_entry_deactivate(editor->entry, seat);
1206 text_entry_deactivate(editor->editor, seat);
1207 editor->active_entry = NULL;
1211 keyboard_focus_handler(struct window *window,
1212 struct input *device, void *data)
1214 struct editor *editor = data;
1216 window_schedule_redraw(editor->window);
1220 key_handler(struct window *window,
1221 struct input *input, uint32_t time,
1222 uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
1225 struct editor *editor = data;
1226 struct text_entry *entry;
1227 const char *new_char;
1230 if (!editor->active_entry)
1233 entry = editor->active_entry;
1235 if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
1239 case XKB_KEY_BackSpace:
1240 text_entry_commit_and_reset(entry);
1242 new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
1243 if (new_char != NULL)
1244 text_entry_delete_text(entry,
1245 new_char - entry->text,
1246 (entry->text + entry->cursor) - new_char);
1248 case XKB_KEY_Delete:
1249 text_entry_commit_and_reset(entry);
1251 new_char = utf8_next_char(entry->text + entry->cursor);
1252 if (new_char != NULL)
1253 text_entry_delete_text(entry,
1255 new_char - (entry->text + entry->cursor));
1258 text_entry_commit_and_reset(entry);
1260 new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
1261 if (new_char != NULL) {
1262 entry->cursor = new_char - entry->text;
1263 if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1264 entry->anchor = entry->cursor;
1265 widget_schedule_redraw(entry->widget);
1269 text_entry_commit_and_reset(entry);
1271 new_char = utf8_next_char(entry->text + entry->cursor);
1272 if (new_char != NULL) {
1273 entry->cursor = new_char - entry->text;
1274 if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1275 entry->anchor = entry->cursor;
1276 widget_schedule_redraw(entry->widget);
1280 text_entry_commit_and_reset(entry);
1282 move_up(entry->text, &entry->cursor);
1283 if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1284 entry->anchor = entry->cursor;
1285 widget_schedule_redraw(entry->widget);
1288 text_entry_commit_and_reset(entry);
1290 move_down(entry->text, &entry->cursor);
1291 if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1292 entry->anchor = entry->cursor;
1293 widget_schedule_redraw(entry->widget);
1295 case XKB_KEY_Escape:
1298 if (xkb_keysym_to_utf8(sym, text, sizeof(text)) <= 0)
1301 text_entry_commit_and_reset(entry);
1303 text_entry_insert_at_cursor(entry, text, 0, 0);
1307 widget_schedule_redraw(entry->widget);
1311 global_handler(struct display *display, uint32_t name,
1312 const char *interface, uint32_t version, void *data)
1314 struct editor *editor = data;
1316 if (!strcmp(interface, "wl_text_input_manager")) {
1317 editor->text_input_manager =
1318 display_bind(display, name,
1319 &wl_text_input_manager_interface, 1);
1324 main(int argc, char *argv[])
1326 struct editor editor;
1328 uint32_t click_to_show = 0;
1329 const char *preferred_language = NULL;
1331 for (i = 1; i < argc; i++) {
1332 if (strcmp("--click-to-show", argv[i]) == 0)
1334 else if (strcmp("--preferred-language", argv[i]) == 0) {
1336 preferred_language = argv[i + 1];
1342 memset(&editor, 0, sizeof editor);
1348 editor.display = display_create(&argc, argv);
1349 if (editor.display == NULL) {
1350 fprintf(stderr, "failed to create display: %m\n");
1354 display_set_user_data(editor.display, &editor);
1355 display_set_global_handler(editor.display, global_handler);
1357 editor.window = window_create(editor.display);
1358 editor.widget = window_frame_create(editor.window, &editor);
1360 editor.entry = text_entry_create(&editor, "Entry");
1361 editor.entry->click_to_show = click_to_show;
1362 if (preferred_language)
1363 editor.entry->preferred_language = strdup(preferred_language);
1364 editor.editor = text_entry_create(&editor, "Numeric");
1365 editor.editor->content_purpose = WL_TEXT_INPUT_CONTENT_PURPOSE_NUMBER;
1366 editor.editor->click_to_show = click_to_show;
1368 window_set_title(editor.window, "Text Editor");
1369 window_set_key_handler(editor.window, key_handler);
1370 window_set_keyboard_focus_handler(editor.window,
1371 keyboard_focus_handler);
1372 window_set_user_data(editor.window, &editor);
1374 widget_set_redraw_handler(editor.widget, redraw_handler);
1375 widget_set_resize_handler(editor.widget, resize_handler);
1376 widget_set_button_handler(editor.widget, editor_button_handler);
1377 widget_set_touch_down_handler(editor.widget, editor_touch_handler);
1379 window_schedule_resize(editor.window, 500, 400);
1381 display_run(editor.display);
1383 text_entry_destroy(editor.entry);
1384 text_entry_destroy(editor.editor);
1385 widget_destroy(editor.widget);
1386 window_destroy(editor.window);
1387 display_destroy(editor.display);