blob: 8f9eb6322567c0150a4edf3c6fb901c2d0279ca8 [file] [log] [blame]
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001/*
2 * Copyright © 2012 Openismus GmbH
Jan Arne Petersen4c265182012-09-09 23:08:30 +02003 * Copyright © 2012 Intel Corporation
Jan Arne Petersencba9e472012-06-21 21:52:19 +02004 *
Bryce Harrington1f6b0d12015-06-10 22:48:59 -07005 * 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:
Jan Arne Petersencba9e472012-06-21 21:52:19 +020011 *
Bryce Harrington1f6b0d12015-06-10 22:48:59 -070012 * 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.
Jan Arne Petersencba9e472012-06-21 21:52:19 +020023 */
24
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +010025#include "config.h"
26
Philipp Brüschweiler591cfca2012-07-11 22:25:29 +020027#include <assert.h>
Bryce Harrington411ffab2016-11-21 13:26:23 -080028#include <errno.h>
Jussi Kukkonen649bbce2016-07-19 14:16:27 +030029#include <stdint.h>
Jan Arne Petersencba9e472012-06-21 21:52:19 +020030#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +020033#include <stdbool.h>
Manuel Bachmann22f34302015-03-30 01:57:44 +020034#include <unistd.h>
Jan Arne Petersencba9e472012-06-21 21:52:19 +020035
36#include <linux/input.h>
37#include <cairo.h>
38
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +010039#include <pango/pangocairo.h>
40
Bryce Harrington3d90da22016-11-21 13:26:22 -080041#include "shared/config-parser.h"
Jon Cruz35b2eaa2015-06-15 15:37:08 -070042#include "shared/helpers.h"
Bryce Harringtone99e4bf2016-03-16 14:15:18 -070043#include "shared/xalloc.h"
Jan Arne Petersencba9e472012-06-21 21:52:19 +020044#include "window.h"
Jonas Ådahl3bcba342015-11-17 16:00:29 +080045#include "text-input-unstable-v1-client-protocol.h"
Jan Arne Petersencba9e472012-06-21 21:52:19 +020046
47struct text_entry {
48 struct widget *widget;
Jan Arne Petersene829adc2012-08-10 16:47:22 +020049 struct window *window;
Jan Arne Petersencba9e472012-06-21 21:52:19 +020050 char *text;
51 int active;
Joshua Watt8093fd52017-06-24 16:03:42 -050052 bool panel_visible;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +020053 uint32_t cursor;
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +020054 uint32_t anchor;
Jan Arne Petersen46535312013-01-16 21:26:38 +010055 struct {
56 char *text;
57 int32_t cursor;
58 char *commit;
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +010059 PangoAttrList *attr_list;
Jan Arne Petersen46535312013-01-16 21:26:38 +010060 } preedit;
61 struct {
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +010062 PangoAttrList *attr_list;
Jan Arne Petersen46535312013-01-16 21:26:38 +010063 int32_t cursor;
64 } preedit_info;
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +010065 struct {
66 int32_t cursor;
67 int32_t anchor;
Jan Arne Petersen919bc142013-04-18 16:47:34 +020068 uint32_t delete_index;
69 uint32_t delete_length;
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +020070 bool invalid_delete;
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +010071 } pending_commit;
Jonas Ådahl3bcba342015-11-17 16:00:29 +080072 struct zwp_text_input_v1 *text_input;
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +010073 PangoLayout *layout;
Jan Arne Petersencd997062012-11-18 19:06:44 +010074 struct {
75 xkb_mod_mask_t shift_mask;
76 } keysym;
Jan Arne Petersenc7d2a982013-01-16 21:26:39 +010077 uint32_t serial;
Jan Arne Petersen00191c72013-04-18 16:47:33 +020078 uint32_t reset_serial;
Jan Arne Petersen0558a932013-01-16 21:26:45 +010079 uint32_t content_purpose;
Jan Arne Petersen61381972013-01-31 15:52:21 +010080 uint32_t click_to_show;
Jan Arne Petersen9d419132013-04-18 16:47:16 +020081 char *preferred_language;
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +020082 bool button_pressed;
Jan Arne Petersencba9e472012-06-21 21:52:19 +020083};
84
85struct editor {
Jonas Ådahl3bcba342015-11-17 16:00:29 +080086 struct zwp_text_input_manager_v1 *text_input_manager;
Manuel Bachmann22f34302015-03-30 01:57:44 +020087 struct wl_data_source *selection;
88 char *selected_text;
Jan Arne Petersencba9e472012-06-21 21:52:19 +020089 struct display *display;
90 struct window *window;
91 struct widget *widget;
92 struct text_entry *entry;
93 struct text_entry *editor;
Rob Bradford9d1d32b2012-11-18 19:06:49 +010094 struct text_entry *active_entry;
Jan Arne Petersencba9e472012-06-21 21:52:19 +020095};
96
Jan Arne Petersen6345faa2012-11-05 03:26:39 +010097static const char *
Jan Arne Petersen6345faa2012-11-05 03:26:39 +010098utf8_end_char(const char *p)
99{
100 while ((*p & 0xc0) == 0x80)
101 p++;
102 return p;
103}
104
105static const char *
Jan Arne Petersen68516862013-04-18 16:47:42 +0200106utf8_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
115static const char *
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100116utf8_next_char(const char *p)
117{
118 if (*p != 0)
119 return utf8_end_char(++p);
120 return NULL;
121}
122
Manuel Bachmann8986c182014-04-18 12:50:14 +0200123static void
124move_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
146static void
147move_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
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200164static void text_entry_redraw_handler(struct widget *widget, void *data);
165static 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);
Kristian Høgsberg966e3ed2014-01-07 10:41:50 -0800169static 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);
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +0200172static int text_entry_motion_handler(struct widget *widget,
173 struct input *input, uint32_t time,
174 float x, float y, void *data);
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100175static void text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
176 int32_t cursor, int32_t anchor);
Jan Arne Petersen43f4aa82012-09-09 23:08:43 +0200177static void text_entry_set_preedit(struct text_entry *entry,
178 const char *preedit_text,
179 int preedit_cursor);
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200180static void text_entry_delete_text(struct text_entry *entry,
181 uint32_t index, uint32_t length);
Jan Arne Petersene386dd22012-09-17 15:28:09 +0200182static void text_entry_delete_selected_text(struct text_entry *entry);
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100183static void text_entry_reset_preedit(struct text_entry *entry);
184static void text_entry_commit_and_reset(struct text_entry *entry);
Jan Arne Petersenfe89e712013-04-18 16:47:27 +0200185static void text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle);
186static void text_entry_update(struct text_entry *entry);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200187
188static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200189text_input_commit_string(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800190 struct zwp_text_input_v1 *text_input,
Jan Arne Petersenc7d2a982013-01-16 21:26:39 +0100191 uint32_t serial,
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100192 const char *text)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200193{
194 struct text_entry *entry = data;
195
Jan Arne Petersen00191c72013-04-18 16:47:33 +0200196 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
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200202 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 }
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100207
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200208 text_entry_reset_preedit(entry);
Jan Arne Petersen919bc142013-04-18 16:47:34 +0200209
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);
Jan Arne Petersena96953d2013-05-30 13:57:02 +0200214 } else {
215 text_entry_delete_selected_text(entry);
Jan Arne Petersen919bc142013-04-18 16:47:34 +0200216 }
217
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100218 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);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200223
224 widget_schedule_redraw(entry->widget);
225}
226
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200227static void
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200228clear_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
240static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200241text_input_preedit_string(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800242 struct zwp_text_input_v1 *text_input,
Jan Arne Petersenc7d2a982013-01-16 21:26:39 +0100243 uint32_t serial,
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200244 const char *text,
Jan Arne Petersen46535312013-01-16 21:26:38 +0100245 const char *commit)
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200246{
Jan Arne Petersen43f4aa82012-09-09 23:08:43 +0200247 struct text_entry *entry = data;
248
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200249 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
Jan Arne Petersena96953d2013-05-30 13:57:02 +0200262 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 }
Jan Arne Petersena96953d2013-05-30 13:57:02 +0200269
Jan Arne Petersen46535312013-01-16 21:26:38 +0100270 text_entry_set_preedit(entry, text, entry->preedit_info.cursor);
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100271 entry->preedit.commit = strdup(commit);
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200272 entry->preedit.attr_list = pango_attr_list_ref(entry->preedit_info.attr_list);
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100273
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200274 clear_pending_preedit(entry);
Jan Arne Petersen43f4aa82012-09-09 23:08:43 +0200275
Jan Arne Petersenfe89e712013-04-18 16:47:27 +0200276 text_entry_update(entry);
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200277
Jan Arne Petersen43f4aa82012-09-09 23:08:43 +0200278 widget_schedule_redraw(entry->widget);
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200279}
280
281static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200282text_input_delete_surrounding_text(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800283 struct zwp_text_input_v1 *text_input,
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200284 int32_t index,
285 uint32_t length)
286{
287 struct text_entry *entry = data;
Jan Arne Petersen919bc142013-04-18 16:47:34 +0200288 uint32_t text_length;
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200289
Jan Arne Petersen919bc142013-04-18 16:47:34 +0200290 entry->pending_commit.delete_index = entry->cursor + index;
291 entry->pending_commit.delete_length = length;
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200292 entry->pending_commit.invalid_delete = false;
Jan Arne Petersen919bc142013-04-18 16:47:34 +0200293
Jan Arne Petersen68516862013-04-18 16:47:42 +0200294 text_length = strlen(entry->text);
Jan Arne Petersen919bc142013-04-18 16:47:34 +0200295
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200296 if (entry->pending_commit.delete_index > text_length ||
297 length > text_length ||
Jan Arne Petersen895a1282013-05-30 13:57:04 +0200298 entry->pending_commit.delete_index + length > text_length) {
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200299 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;
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200302 return;
303 }
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200304}
305
306static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200307text_input_cursor_position(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800308 struct zwp_text_input_v1 *text_input,
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100309 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
318static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200319text_input_preedit_styling(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800320 struct zwp_text_input_v1 *text_input,
Jan Arne Petersen46535312013-01-16 21:26:38 +0100321 uint32_t index,
322 uint32_t length,
323 uint32_t style)
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200324{
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100325 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) {
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800333 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_DEFAULT:
334 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_UNDERLINE:
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100335 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
336 break;
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800337 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_INCORRECT:
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100338 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_ERROR);
339 attr2 = pango_attr_underline_color_new(65535, 0, 0);
340 break;
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800341 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_SELECTION:
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100342 attr1 = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
343 attr2 = pango_attr_foreground_new(65535, 65535, 65535);
344 break;
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800345 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT:
346 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_ACTIVE:
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100347 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
348 attr2 = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
349 break;
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800350 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_INACTIVE:
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100351 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 }
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200367}
368
369static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200370text_input_preedit_cursor(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800371 struct zwp_text_input_v1 *text_input,
Jan Arne Petersen46535312013-01-16 21:26:38 +0100372 int32_t index)
373{
374 struct text_entry *entry = data;
375
376 entry->preedit_info.cursor = index;
377}
378
379static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200380text_input_modifiers_map(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800381 struct zwp_text_input_v1 *text_input,
Jan Arne Petersend9be93b2012-11-18 19:06:43 +0100382 struct wl_array *map)
383{
Jan Arne Petersencd997062012-11-18 19:06:44 +0100384 struct text_entry *entry = data;
385
386 entry->keysym.shift_mask = keysym_modifiers_get_mask(map, "Shift");
Jan Arne Petersend9be93b2012-11-18 19:06:43 +0100387}
388
389static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200390text_input_keysym(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800391 struct zwp_text_input_v1 *text_input,
Jan Arne Petersend9be93b2012-11-18 19:06:43 +0100392 uint32_t serial,
393 uint32_t time,
394 uint32_t key,
395 uint32_t state,
396 uint32_t modifiers)
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200397{
Jan Arne Petersen8aba11d2012-09-17 15:28:07 +0200398 struct text_entry *entry = data;
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100399 const char *new_char;
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200400
Jan Arne Petersencd997062012-11-18 19:06:44 +0100401 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;
Jan Arne Petersencd997062012-11-18 19:06:44 +0100413 }
414
Jan Arne Petersen68516862013-04-18 16:47:42 +0200415 if (!(modifiers & entry->keysym.shift_mask))
416 entry->anchor = entry->cursor;
417 widget_schedule_redraw(entry->widget);
418
Jan Arne Petersencd997062012-11-18 19:06:44 +0100419 return;
420 }
421
Manuel Bachmann8986c182014-04-18 12:50:14 +0200422 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
Jan Arne Petersen3fb6e712013-01-16 21:26:52 +0100439 if (key == XKB_KEY_BackSpace) {
440 const char *start, *end;
441
Jan Arne Petersendfd34462013-04-18 16:47:26 +0200442 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
443 return;
444
Jan Arne Petersen3fb6e712013-01-16 21:26:52 +0100445 text_entry_commit_and_reset(entry);
446
447 start = utf8_prev_char(entry->text, entry->text + entry->cursor);
Jan Arne Petersen3fb6e712013-01-16 21:26:52 +0100448 if (start == NULL)
449 return;
450
Daiki Uenob08b3292013-06-28 18:59:44 +0900451 end = utf8_next_char(start);
452
Jan Arne Petersen3fb6e712013-01-16 21:26:52 +0100453 text_entry_delete_text(entry,
454 start - entry->text,
455 end - start);
456
457 return;
458 }
459
Manuel Bachmann8986c182014-04-18 12:50:14 +0200460 if (key == XKB_KEY_Tab ||
461 key == XKB_KEY_KP_Enter ||
462 key == XKB_KEY_Return) {
463 char text[16];
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200464
Manuel Bachmann8986c182014-04-18 12:50:14 +0200465 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 }
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200474}
475
476static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200477text_input_enter(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800478 struct zwp_text_input_v1 *text_input,
Jan Arne Petersen680275f2012-09-24 14:51:14 +0200479 struct wl_surface *surface)
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200480{
481 struct text_entry *entry = data;
482
Jan Arne Petersen680275f2012-09-24 14:51:14 +0200483 if (surface != window_get_wl_surface(entry->window))
484 return;
485
Derek Foreman237a6842014-12-17 09:43:58 -0600486 entry->active++;
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200487
Jan Arne Petersen00191c72013-04-18 16:47:33 +0200488 text_entry_update(entry);
489 entry->reset_serial = entry->serial;
490
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200491 widget_schedule_redraw(entry->widget);
492}
493
494static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200495text_input_leave(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800496 struct zwp_text_input_v1 *text_input)
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200497{
498 struct text_entry *entry = data;
499
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100500 text_entry_commit_and_reset(entry);
Derek Foreman237a6842014-12-17 09:43:58 -0600501 entry->active--;
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100502
Joshua Watt8093fd52017-06-24 16:03:42 -0500503 if (!entry->active) {
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800504 zwp_text_input_v1_hide_input_panel(text_input);
Joshua Watt8093fd52017-06-24 16:03:42 -0500505 entry->panel_visible = false;
506 }
Jan Arne Petersen61381972013-01-31 15:52:21 +0100507
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200508 widget_schedule_redraw(entry->widget);
509}
510
Jan Arne Petersen61381972013-01-31 15:52:21 +0100511static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200512text_input_input_panel_state(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800513 struct zwp_text_input_v1 *text_input,
Jan Arne Petersen61381972013-01-31 15:52:21 +0100514 uint32_t state)
515{
516}
517
Jan Arne Petersenece6b5a2013-04-18 16:47:15 +0200518static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200519text_input_language(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800520 struct zwp_text_input_v1 *text_input,
Jan Arne Petersenece6b5a2013-04-18 16:47:15 +0200521 uint32_t serial,
522 const char *language)
523{
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200524 fprintf(stderr, "input language is %s \n", language);
Jan Arne Petersenece6b5a2013-04-18 16:47:15 +0200525}
526
527static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200528text_input_text_direction(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800529 struct zwp_text_input_v1 *text_input,
Jan Arne Petersenece6b5a2013-04-18 16:47:15 +0200530 uint32_t serial,
531 uint32_t direction)
532{
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200533 struct text_entry *entry = data;
534 PangoContext *context = pango_layout_get_context(entry->layout);
535 PangoDirection pango_direction;
536
537
538 switch (direction) {
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800539 case ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_LTR:
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200540 pango_direction = PANGO_DIRECTION_LTR;
541 break;
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800542 case ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_RTL:
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200543 pango_direction = PANGO_DIRECTION_RTL;
544 break;
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800545 case ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_AUTO:
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200546 default:
547 pango_direction = PANGO_DIRECTION_NEUTRAL;
548 }
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200549
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200550 pango_context_set_base_dir(context, pango_direction);
Jan Arne Petersenece6b5a2013-04-18 16:47:15 +0200551}
552
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800553static const struct zwp_text_input_v1_listener text_input_listener = {
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200554 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
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200567};
568
Manuel Bachmann22f34302015-03-30 01:57:44 +0200569static void
570data_source_target(void *data,
571 struct wl_data_source *source, const char *mime_type)
572{
573}
574
575static void
576data_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
Bryce Harrington7dd12ec2015-05-19 15:32:09 -0700582 if (write(fd, editor->selected_text, strlen(editor->selected_text) + 1) < 0)
583 fprintf(stderr, "write failed: %m\n");
Derek Foreman2af7e202016-07-07 10:52:17 -0500584
585 close(fd);
Manuel Bachmann22f34302015-03-30 01:57:44 +0200586}
587
588static void
589data_source_cancelled(void *data, struct wl_data_source *source)
590{
591 wl_data_source_destroy(source);
592}
593
594static const struct wl_data_source_listener data_source_listener = {
595 data_source_target,
596 data_source_send,
597 data_source_cancelled
598};
599
600static void
601paste_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
620static void
621editor_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;
Michael Vetter2a18a522015-05-15 17:17:47 +0200627
Manuel Bachmann22f34302015-03-30 01:57:44 +0200628 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);
Derek Foreman11b62422017-04-20 14:31:36 -0500642 if (!editor->selection)
643 return;
644
Manuel Bachmann22f34302015-03-30 01:57:44 +0200645 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
654static void
655editor_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
662static void
663menu_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
683static void
684show_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
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200697static struct text_entry*
698text_entry_create(struct editor *editor, const char *text)
699{
700 struct text_entry *entry;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200701
Ryo Munakata5e653ca2015-08-07 20:20:46 +0900702 entry = xzalloc(sizeof *entry);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200703
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200704 entry->widget = widget_add_widget(editor->widget, entry);
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200705 entry->window = editor->window;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200706 entry->text = strdup(text);
707 entry->active = 0;
Joshua Watt8093fd52017-06-24 16:03:42 -0500708 entry->panel_visible = false;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200709 entry->cursor = strlen(text);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200710 entry->anchor = entry->cursor;
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800711 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);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200715
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200716 widget_set_redraw_handler(entry->widget, text_entry_redraw_handler);
717 widget_set_button_handler(entry->widget, text_entry_button_handler);
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +0200718 widget_set_motion_handler(entry->widget, text_entry_motion_handler);
Kristian Høgsberg966e3ed2014-01-07 10:41:50 -0800719 widget_set_touch_down_handler(entry->widget, text_entry_touch_handler);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200720
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200721 return entry;
722}
723
724static void
725text_entry_destroy(struct text_entry *entry)
726{
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200727 widget_destroy(entry->widget);
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800728 zwp_text_input_v1_destroy(entry->text_input);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100729 g_clear_object(&entry->layout);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200730 free(entry->text);
Silvan Jegene31d95f2016-11-17 21:43:06 +0100731 free(entry->preferred_language);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200732 free(entry);
733}
734
735static void
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200736redraw_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);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200754 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200755 cairo_set_source_rgba(cr, 1, 1, 1, 1);
756 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
757 cairo_fill(cr);
758
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200759 cairo_pop_group_to_source(cr);
760 cairo_paint(cr);
761
762 cairo_destroy(cr);
763 cairo_surface_destroy(surface);
764}
765
766static void
767text_entry_allocate(struct text_entry *entry, int32_t x, int32_t y,
768 int32_t width, int32_t height)
769{
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200770 widget_set_allocation(entry->widget, x, y, width, height);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200771}
772
773static void
774resize_handler(struct widget *widget,
775 int32_t width, int32_t height, void *data)
776{
777 struct editor *editor = data;
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200778 struct rectangle allocation;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200779
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200780 widget_get_allocation(editor->widget, &allocation);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200781
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200782 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);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200788}
789
790static void
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200791text_entry_activate(struct text_entry *entry,
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200792 struct wl_seat *seat)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200793{
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200794 struct wl_surface *surface = window_get_wl_surface(entry->window);
795
Jan Arne Petersen61381972013-01-31 15:52:21 +0100796 if (entry->click_to_show && entry->active) {
Joshua Watt8093fd52017-06-24 16:03:42 -0500797 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);
Jan Arne Petersen61381972013-01-31 15:52:21 +0100803
804 return;
805 }
806
807 if (!entry->click_to_show)
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800808 zwp_text_input_v1_show_input_panel(entry->text_input);
Jan Arne Petersen61381972013-01-31 15:52:21 +0100809
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800810 zwp_text_input_v1_activate(entry->text_input,
811 seat,
812 surface);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200813}
814
815static void
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200816text_entry_deactivate(struct text_entry *entry,
817 struct wl_seat *seat)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200818{
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800819 zwp_text_input_v1_deactivate(entry->text_input,
820 seat);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200821}
822
823static void
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200824text_entry_update_layout(struct text_entry *entry)
825{
826 char *text;
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100827 PangoAttrList *attr_list;
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200828
Jan Arne Petersen68516862013-04-18 16:47:42 +0200829 assert(entry->cursor <= (strlen(entry->text) +
830 (entry->preedit.text ? strlen(entry->preedit.text) : 0)));
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200831
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100832 if (entry->preedit.text) {
Derek Foreman22044922014-11-20 15:42:35 -0600833 text = xmalloc(strlen(entry->text) + strlen(entry->preedit.text) + 1);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100834 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);
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200840 }
841
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100842 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;
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200846
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100847 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
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200882 free(text);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100883 pango_attr_list_unref(attr_list);
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200884}
885
886static void
Jan Arne Petersen0558a932013-01-16 21:26:45 +0100887text_entry_update(struct text_entry *entry)
888{
Jan Arne Petersenfe89e712013-04-18 16:47:27 +0200889 struct rectangle cursor_rectangle;
890
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800891 zwp_text_input_v1_set_content_type(entry->text_input,
892 ZWP_TEXT_INPUT_V1_CONTENT_HINT_NONE,
893 entry->content_purpose);
Jan Arne Petersen0558a932013-01-16 21:26:45 +0100894
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800895 zwp_text_input_v1_set_surrounding_text(entry->text_input,
896 entry->text,
897 entry->cursor,
898 entry->anchor);
Jan Arne Petersen0eabcaa2013-01-31 15:52:20 +0100899
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200900 if (entry->preferred_language)
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800901 zwp_text_input_v1_set_preferred_language(entry->text_input,
902 entry->preferred_language);
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200903
Jan Arne Petersenfe89e712013-04-18 16:47:27 +0200904 text_entry_get_cursor_rectangle(entry, &cursor_rectangle);
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800905 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);
Jan Arne Petersenfe89e712013-04-18 16:47:27 +0200910
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800911 zwp_text_input_v1_commit_state(entry->text_input, ++entry->serial);
Jan Arne Petersen0558a932013-01-16 21:26:45 +0100912}
913
914static void
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100915text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
916 int32_t cursor, int32_t anchor)
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200917{
Derek Foreman22044922014-11-20 15:42:35 -0600918 char *new_text = xmalloc(strlen(entry->text) + strlen(text) + 1);
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200919
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;
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100927 if (anchor >= 0)
928 entry->anchor = entry->cursor + strlen(text) + anchor;
929 else
930 entry->anchor = entry->cursor + 1 + anchor;
Jan Arne Petersen68516862013-04-18 16:47:42 +0200931
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100932 if (cursor >= 0)
933 entry->cursor += strlen(text) + cursor;
934 else
935 entry->cursor += 1 + cursor;
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200936
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200937 text_entry_update_layout(entry);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100938
939 widget_schedule_redraw(entry->widget);
940
Jan Arne Petersen0558a932013-01-16 21:26:45 +0100941 text_entry_update(entry);
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200942}
943
944static void
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100945text_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;
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100954
955 pango_attr_list_unref(entry->preedit.attr_list);
956 entry->preedit.attr_list = NULL;
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100957}
958
959static void
960text_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) {
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100969 text_entry_insert_at_cursor(entry, commit, 0, 0);
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100970 free(commit);
971 }
Jan Arne Petersen08015b62013-04-18 16:47:18 +0200972
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800973 zwp_text_input_v1_reset(entry->text_input);
Jan Arne Petersen00191c72013-04-18 16:47:33 +0200974 text_entry_update(entry);
975 entry->reset_serial = entry->serial;
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100976}
977
978static void
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200979text_entry_set_preedit(struct text_entry *entry,
980 const char *preedit_text,
981 int preedit_cursor)
982{
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100983 text_entry_reset_preedit(entry);
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200984
985 if (!preedit_text)
986 return;
987
Jan Arne Petersen46535312013-01-16 21:26:38 +0100988 entry->preedit.text = strdup(preedit_text);
989 entry->preedit.cursor = preedit_cursor;
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200990
991 text_entry_update_layout(entry);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100992
993 widget_schedule_redraw(entry->widget);
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200994}
995
Jan Arne Petersen3489ba92013-01-16 21:26:47 +0100996static uint32_t
997text_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;
Jan Arne Petersen68516862013-04-18 16:47:42 +02001004 const char *text;
Jan Arne Petersen3489ba92013-01-16 21:26:47 +01001005
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);
Jan Arne Petersen68516862013-04-18 16:47:42 +02001012
1013 text = pango_layout_get_text(entry->layout);
1014 cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
Jan Arne Petersen3489ba92013-01-16 21:26:47 +01001015
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)
Jonas Ådahl3bcba342015-11-17 16:00:29 +08001022 zwp_text_input_v1_invoke_action(entry->text_input,
1023 button,
1024 cursor - entry->cursor);
Jan Arne Petersen3489ba92013-01-16 21:26:47 +01001025
1026 return 1;
1027}
1028
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001029static bool
1030text_entry_has_preedit(struct text_entry *entry)
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001031{
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001032 return entry->preedit.text && (strlen(entry->preedit.text) > 0);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001033}
1034
1035static void
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001036text_entry_set_cursor_position(struct text_entry *entry,
1037 int32_t x, int32_t y,
1038 bool move_anchor)
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001039{
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +01001040 int index, trailing;
Jan Arne Petersen68516862013-04-18 16:47:42 +02001041 const char *text;
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001042 uint32_t cursor;
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +01001043
1044 pango_layout_xy_to_index(entry->layout,
1045 x * PANGO_SCALE, y * PANGO_SCALE,
1046 &index, &trailing);
Jan Arne Petersen68516862013-04-18 16:47:42 +02001047
1048 text = pango_layout_get_text(entry->layout);
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001049
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;
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +01001065
1066 text_entry_update_layout(entry);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001067
1068 widget_schedule_redraw(entry->widget);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +01001069
Jan Arne Petersen0558a932013-01-16 21:26:45 +01001070 text_entry_update(entry);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001071}
1072
1073static void
Jan Arne Petersene202bae2012-09-09 23:08:44 +02001074text_entry_delete_text(struct text_entry *entry,
1075 uint32_t index, uint32_t length)
1076{
Jan Arne Petersen68516862013-04-18 16:47:42 +02001077 uint32_t l;
1078
Jan Arne Petersen895a1282013-05-30 13:57:04 +02001079 assert(index <= strlen(entry->text));
1080 assert(index + length <= strlen(entry->text));
1081 assert(index + length >= length);
Jan Arne Petersen80ad1a92012-09-17 15:28:10 +02001082
Jan Arne Petersen68516862013-04-18 16:47:42 +02001083 l = strlen(entry->text + index + length);
1084 memmove(entry->text + index,
1085 entry->text + index + length,
1086 l + 1);
Jan Arne Petersene202bae2012-09-09 23:08:44 +02001087
Jan Arne Petersen9eaa8e52013-05-30 13:57:03 +02001088 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
Jan Arne Petersene202bae2012-09-09 23:08:44 +02001095 text_entry_update_layout(entry);
1096
1097 widget_schedule_redraw(entry->widget);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +01001098
Jan Arne Petersen0558a932013-01-16 21:26:45 +01001099 text_entry_update(entry);
Jan Arne Petersene202bae2012-09-09 23:08:44 +02001100}
1101
1102static void
Jan Arne Petersene386dd22012-09-17 15:28:09 +02001103text_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
1116static void
Jan Arne Petersenfe89e712013-04-18 16:47:27 +02001117text_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
Jan Arne Petersen68516862013-04-18 16:47:42 +02001133
Jan Arne Petersenfe89e712013-04-18 16:47:27 +02001134 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
1145static void
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001146text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr)
1147{
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +01001148 PangoRectangle extents;
1149 PangoRectangle cursor_pos;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001150
Jan Arne Petersen46535312013-01-16 21:26:38 +01001151 if (entry->preedit.text && entry->preedit.cursor < 0)
1152 return;
1153
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +01001154 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);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001158
1159 cairo_set_line_width(cr, 1.0);
Peter Maatmanb9a23f42013-07-06 20:55:54 +02001160 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));
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001162 cairo_stroke(cr);
1163}
1164
Ander Conselvan de Oliveira8e37d962014-05-08 14:55:50 +03001165static int
1166text_offset_left(struct rectangle *allocation)
1167{
1168 return 10;
1169}
1170
1171static int
1172text_offset_top(struct rectangle *allocation)
1173{
1174 return allocation->height / 2;
1175}
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +02001176
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +02001177static void
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001178text_entry_redraw_handler(struct widget *widget, void *data)
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001179{
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001180 struct text_entry *entry = data;
1181 cairo_surface_t *surface;
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001182 struct rectangle allocation;
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001183 cairo_t *cr;
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001184
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001185 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);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001211
Ander Conselvan de Oliveira8e37d962014-05-08 14:55:50 +03001212 cairo_translate(cr,
1213 text_offset_left(&allocation),
1214 text_offset_top(&allocation));
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001215
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +01001216 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);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001224
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001225 text_entry_draw_cursor(entry, cr);
1226
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001227 cairo_pop_group_to_source(cr);
1228 cairo_paint(cr);
1229
1230 cairo_destroy(cr);
1231 cairo_surface_destroy(surface);
1232}
1233
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001234static int
1235text_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;
Ander Conselvan de Oliveira8e37d962014-05-08 14:55:50 +03001241 int tx, ty;
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001242
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001243 if (!entry->button_pressed) {
1244 return CURSOR_IBEAM;
1245 }
1246
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001247 widget_get_allocation(entry->widget, &allocation);
1248
Ander Conselvan de Oliveira8e37d962014-05-08 14:55:50 +03001249 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);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001253
1254 return CURSOR_IBEAM;
1255}
1256
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001257static void
1258text_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;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001264 struct rectangle allocation;
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001265 struct editor *editor;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001266 int32_t x, y;
Jan Arne Petersen3489ba92013-01-16 21:26:47 +01001267 uint32_t result;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001268
1269 widget_get_allocation(entry->widget, &allocation);
1270 input_get_position(input, &x, &y);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001271
Ander Conselvan de Oliveira8e37d962014-05-08 14:55:50 +03001272 x -= allocation.x + text_offset_left(&allocation);
1273 y -= allocation.y + text_offset_top(&allocation);
Jan Arne Petersen3489ba92013-01-16 21:26:47 +01001274
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001275 editor = window_get_user_data(entry->window);
1276
Manuel Bachmann22f34302015-03-30 01:57:44 +02001277 switch (button) {
1278 case BTN_LEFT:
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001279 entry->button_pressed = (state == WL_POINTER_BUTTON_STATE_PRESSED);
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001280 if (state == WL_POINTER_BUTTON_STATE_PRESSED)
1281 input_grab(input, entry->widget, button);
1282 else
1283 input_ungrab(input);
Manuel Bachmann22f34302015-03-30 01:57:44 +02001284 break;
1285 case BTN_RIGHT:
1286 if (state == WL_POINTER_BUTTON_STATE_PRESSED)
1287 show_menu(editor, input, time);
1288 break;
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001289 }
1290
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001291 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 }
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001297
Manuel Bachmann22f34302015-03-30 01:57:44 +02001298 if (state == WL_POINTER_BUTTON_STATE_PRESSED &&
1299 button == BTN_LEFT) {
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001300 struct wl_seat *seat = input_get_seat(input);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001301
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001302 text_entry_activate(entry, seat);
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001303 editor->active_entry = entry;
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001304
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001305 text_entry_set_cursor_position(entry, x, y, true);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001306 }
1307}
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001308
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001309static void
Kristian Høgsberg966e3ed2014-01-07 10:41:50 -08001310text_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
Ander Conselvan de Oliveira8e37d962014-05-08 14:55:50 +03001322 x = tx - (allocation.x + text_offset_left(&allocation));
1323 y = ty - (allocation.y + text_offset_top(&allocation));
Kristian Høgsberg966e3ed2014-01-07 10:41:50 -08001324
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
1332static void
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001333editor_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;
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001339
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001340 if (button != BTN_LEFT) {
1341 return;
1342 }
Jan Arne Petersene829adc2012-08-10 16:47:22 +02001343
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001344 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
1345 struct wl_seat *seat = input_get_seat(input);
1346
Jan Arne Petersene829adc2012-08-10 16:47:22 +02001347 text_entry_deactivate(editor->entry, seat);
1348 text_entry_deactivate(editor->editor, seat);
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001349 editor->active_entry = NULL;
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001350 }
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001351}
1352
Kristian Høgsberg966e3ed2014-01-07 10:41:50 -08001353static void
1354editor_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}
Kristian Høgsberg78858902014-01-01 23:57:42 -08001366
1367static void
1368keyboard_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
Manuel Bachmann22f34302015-03-30 01:57:44 +02001376static int
1377handle_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
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001395static void
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001396key_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;
Jan Arne Petersen68516862013-04-18 16:47:42 +02001403 const char *new_char;
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001404 char text[16];
Manuel Bachmann22f34302015-03-30 01:57:44 +02001405 uint32_t modifiers;
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001406
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
Manuel Bachmann22f34302015-03-30 01:57:44 +02001415 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
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001421 switch (sym) {
1422 case XKB_KEY_BackSpace:
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +01001423 text_entry_commit_and_reset(entry);
1424
Jan Arne Petersen68516862013-04-18 16:47:42 +02001425 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);
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001430 break;
1431 case XKB_KEY_Delete:
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +01001432 text_entry_commit_and_reset(entry);
1433
Jan Arne Petersen68516862013-04-18 16:47:42 +02001434 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));
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001439 break;
1440 case XKB_KEY_Left:
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +01001441 text_entry_commit_and_reset(entry);
1442
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001443 new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
1444 if (new_char != NULL) {
1445 entry->cursor = new_char - entry->text;
Rob Bradford70002832013-07-11 16:00:00 +01001446 if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1447 entry->anchor = entry->cursor;
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001448 widget_schedule_redraw(entry->widget);
1449 }
1450 break;
1451 case XKB_KEY_Right:
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +01001452 text_entry_commit_and_reset(entry);
1453
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001454 new_char = utf8_next_char(entry->text + entry->cursor);
1455 if (new_char != NULL) {
1456 entry->cursor = new_char - entry->text;
Rob Bradford70002832013-07-11 16:00:00 +01001457 if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1458 entry->anchor = entry->cursor;
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001459 widget_schedule_redraw(entry->widget);
1460 }
1461 break;
Manuel Bachmann8986c182014-04-18 12:50:14 +02001462 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;
Peter Maatman08c38d42013-07-06 20:42:59 +02001478 case XKB_KEY_Escape:
1479 break;
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001480 default:
1481 if (xkb_keysym_to_utf8(sym, text, sizeof(text)) <= 0)
1482 break;
1483
Peter Maatman08c38d42013-07-06 20:42:59 +02001484 text_entry_commit_and_reset(entry);
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +01001485
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +01001486 text_entry_insert_at_cursor(entry, text, 0, 0);
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001487 break;
1488 }
1489
1490 widget_schedule_redraw(entry->widget);
1491}
1492
1493static void
Kristian Høgsbergfa80e112012-10-10 21:34:26 -04001494global_handler(struct display *display, uint32_t name,
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001495 const char *interface, uint32_t version, void *data)
1496{
1497 struct editor *editor = data;
1498
Jonas Ådahl3bcba342015-11-17 16:00:29 +08001499 if (!strcmp(interface, "zwp_text_input_manager_v1")) {
Jan Arne Petersen78d00e42013-04-18 16:47:24 +02001500 editor->text_input_manager =
Kristian Høgsbergfa80e112012-10-10 21:34:26 -04001501 display_bind(display, name,
Jonas Ådahl3bcba342015-11-17 16:00:29 +08001502 &zwp_text_input_manager_v1_interface, 1);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001503 }
1504}
1505
Bryce Harrington3d90da22016-11-21 13:26:22 -08001506/** Display help for command line options, and exit */
1507static uint32_t opt_help = 0;
1508
1509/** Require a distinct click to show the input panel (virtual keyboard) */
1510static uint32_t opt_click_to_show = 0;
1511
1512/** Set a specific (RFC-3066) language. Used for the virtual keyboard, etc. */
1513static const char *opt_preferred_language = NULL;
1514
1515/**
1516 * \brief command line options for editor
1517 */
1518static 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
1524static void
1525usage(const char *program_name, int exit_code)
1526{
1527 unsigned k;
1528
Bryce Harrington411ffab2016-11-21 13:26:23 -08001529 fprintf(stderr, "Usage: %s [OPTIONS] [FILENAME]\n\n", program_name);
Bryce Harrington3d90da22016-11-21 13:26:22 -08001530 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
Bryce Harrington411ffab2016-11-21 13:26:23 -08001548/* 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 */
1553static char *
1554read_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
1585error:
1586 errsv = errno;
1587 if (fin)
1588 fclose(fin);
1589 free(buffer);
Peter Huttererc8b46452017-01-19 08:21:45 +10001590 errno = errsv ? errsv : EINVAL;
Bryce Harrington411ffab2016-11-21 13:26:23 -08001591
1592 return NULL;
1593}
1594
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001595int
1596main(int argc, char *argv[])
1597{
1598 struct editor editor;
Bryce Harrington411ffab2016-11-21 13:26:23 -08001599 char *text_buffer = NULL;
Jan Arne Petersen61381972013-01-31 15:52:21 +01001600
Bryce Harrington3d90da22016-11-21 13:26:22 -08001601 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) {
Bryce Harrington411ffab2016-11-21 13:26:23 -08001607 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': %m\n", argv[1]);
1613 return -1;
1614 }
Jan Arne Petersen9d419132013-04-18 16:47:16 +02001615 }
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001616
Jan Arne Petersen25f6db52012-11-05 03:26:40 +01001617 memset(&editor, 0, sizeof editor);
1618
Kristian Høgsberg4172f662013-02-20 15:27:49 -05001619 editor.display = display_create(&argc, argv);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001620 if (editor.display == NULL) {
1621 fprintf(stderr, "failed to create display: %m\n");
Raúl Peñacoba745e6472017-03-29 18:16:46 +02001622 free(text_buffer);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001623 return -1;
1624 }
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001625
Kristian Høgsbergfa80e112012-10-10 21:34:26 -04001626 display_set_user_data(editor.display, &editor);
1627 display_set_global_handler(editor.display, global_handler);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001628
Olivier Blin30e1f3c2014-09-16 19:13:17 +02001629 if (editor.text_input_manager == NULL) {
1630 fprintf(stderr, "No text input manager global\n");
Raúl Peñacoba745e6472017-03-29 18:16:46 +02001631 display_destroy(editor.display);
1632 free(text_buffer);
Olivier Blin30e1f3c2014-09-16 19:13:17 +02001633 return -1;
1634 }
1635
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001636 editor.window = window_create(editor.display);
Jason Ekstrandee7fefc2013-10-13 19:08:38 -05001637 editor.widget = window_frame_create(editor.window, &editor);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001638
Bryce Harrington411ffab2016-11-21 13:26:23 -08001639 if (text_buffer)
1640 editor.entry = text_entry_create(&editor, text_buffer);
1641 else
1642 editor.entry = text_entry_create(&editor, "Entry");
Bryce Harrington3d90da22016-11-21 13:26:22 -08001643 editor.entry->click_to_show = opt_click_to_show;
1644 if (opt_preferred_language)
1645 editor.entry->preferred_language = strdup(opt_preferred_language);
Jan Arne Petersen0558a932013-01-16 21:26:45 +01001646 editor.editor = text_entry_create(&editor, "Numeric");
Jonas Ådahl3bcba342015-11-17 16:00:29 +08001647 editor.editor->content_purpose = ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NUMBER;
Bryce Harrington3d90da22016-11-21 13:26:22 -08001648 editor.editor->click_to_show = opt_click_to_show;
Manuel Bachmann22f34302015-03-30 01:57:44 +02001649 editor.selection = NULL;
1650 editor.selected_text = NULL;
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001651
1652 window_set_title(editor.window, "Text Editor");
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001653 window_set_key_handler(editor.window, key_handler);
Kristian Høgsberg78858902014-01-01 23:57:42 -08001654 window_set_keyboard_focus_handler(editor.window,
1655 keyboard_focus_handler);
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001656 window_set_user_data(editor.window, &editor);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001657
1658 widget_set_redraw_handler(editor.widget, redraw_handler);
1659 widget_set_resize_handler(editor.widget, resize_handler);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001660 widget_set_button_handler(editor.widget, editor_button_handler);
Kristian Høgsberg966e3ed2014-01-07 10:41:50 -08001661 widget_set_touch_down_handler(editor.widget, editor_touch_handler);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001662
1663 window_schedule_resize(editor.window, 500, 400);
1664
1665 display_run(editor.display);
1666
Manuel Bachmann22f34302015-03-30 01:57:44 +02001667 if (editor.selected_text)
1668 free(editor.selected_text);
1669 if (editor.selection)
1670 wl_data_source_destroy(editor.selection);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001671 text_entry_destroy(editor.entry);
1672 text_entry_destroy(editor.editor);
vivek31732f72014-05-15 18:58:16 +05301673 widget_destroy(editor.widget);
1674 window_destroy(editor.window);
1675 display_destroy(editor.display);
Bryce Harrington411ffab2016-11-21 13:26:23 -08001676 free(text_buffer);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001677
1678 return 0;
1679}