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