blob: 42c7f52d7d5abea061a007c8374df398958bd47b [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;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +020052 uint32_t cursor;
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +020053 uint32_t anchor;
Jan Arne Petersen46535312013-01-16 21:26:38 +010054 struct {
55 char *text;
56 int32_t cursor;
57 char *commit;
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +010058 PangoAttrList *attr_list;
Jan Arne Petersen46535312013-01-16 21:26:38 +010059 } preedit;
60 struct {
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +010061 PangoAttrList *attr_list;
Jan Arne Petersen46535312013-01-16 21:26:38 +010062 int32_t cursor;
63 } preedit_info;
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +010064 struct {
65 int32_t cursor;
66 int32_t anchor;
Jan Arne Petersen919bc142013-04-18 16:47:34 +020067 uint32_t delete_index;
68 uint32_t delete_length;
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +020069 bool invalid_delete;
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +010070 } pending_commit;
Jonas Ådahl3bcba342015-11-17 16:00:29 +080071 struct zwp_text_input_v1 *text_input;
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +010072 PangoLayout *layout;
Jan Arne Petersencd997062012-11-18 19:06:44 +010073 struct {
74 xkb_mod_mask_t shift_mask;
75 } keysym;
Jan Arne Petersenc7d2a982013-01-16 21:26:39 +010076 uint32_t serial;
Jan Arne Petersen00191c72013-04-18 16:47:33 +020077 uint32_t reset_serial;
Jan Arne Petersen0558a932013-01-16 21:26:45 +010078 uint32_t content_purpose;
Jan Arne Petersen61381972013-01-31 15:52:21 +010079 uint32_t click_to_show;
Jan Arne Petersen9d419132013-04-18 16:47:16 +020080 char *preferred_language;
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +020081 bool button_pressed;
Jan Arne Petersencba9e472012-06-21 21:52:19 +020082};
83
84struct editor {
Jonas Ådahl3bcba342015-11-17 16:00:29 +080085 struct zwp_text_input_manager_v1 *text_input_manager;
Manuel Bachmann22f34302015-03-30 01:57:44 +020086 struct wl_data_source *selection;
87 char *selected_text;
Jan Arne Petersencba9e472012-06-21 21:52:19 +020088 struct display *display;
89 struct window *window;
90 struct widget *widget;
91 struct text_entry *entry;
92 struct text_entry *editor;
Rob Bradford9d1d32b2012-11-18 19:06:49 +010093 struct text_entry *active_entry;
Jan Arne Petersencba9e472012-06-21 21:52:19 +020094};
95
Jan Arne Petersen6345faa2012-11-05 03:26:39 +010096static const char *
Jan Arne Petersen6345faa2012-11-05 03:26:39 +010097utf8_end_char(const char *p)
98{
99 while ((*p & 0xc0) == 0x80)
100 p++;
101 return p;
102}
103
104static const char *
Jan Arne Petersen68516862013-04-18 16:47:42 +0200105utf8_prev_char(const char *s, const char *p)
106{
107 for (--p; p >= s; --p) {
108 if ((*p & 0xc0) != 0x80)
109 return p;
110 }
111 return NULL;
112}
113
114static const char *
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100115utf8_next_char(const char *p)
116{
117 if (*p != 0)
118 return utf8_end_char(++p);
119 return NULL;
120}
121
Manuel Bachmann8986c182014-04-18 12:50:14 +0200122static void
123move_up(const char *p, uint32_t *cursor)
124{
125 const char *posr, *posr_i;
126 char text[16];
127
128 xkb_keysym_to_utf8(XKB_KEY_Return, text, sizeof(text));
129
130 posr = strstr(p, text);
131 while (posr) {
132 if (*cursor > (unsigned)(posr-p)) {
133 posr_i = strstr(posr+1, text);
134 if (!posr_i || !(*cursor > (unsigned)(posr_i-p))) {
135 *cursor = posr-p;
136 break;
137 }
138 posr = posr_i;
139 } else {
140 break;
141 }
142 }
143}
144
145static void
146move_down(const char *p, uint32_t *cursor)
147{
148 const char *posr;
149 char text[16];
150
151 xkb_keysym_to_utf8(XKB_KEY_Return, text, sizeof(text));
152
153 posr = strstr(p, text);
154 while (posr) {
155 if (*cursor <= (unsigned)(posr-p)) {
156 *cursor = posr-p + 1;
157 break;
158 }
159 posr = strstr(posr+1, text);
160 }
161}
162
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200163static void text_entry_redraw_handler(struct widget *widget, void *data);
164static void text_entry_button_handler(struct widget *widget,
165 struct input *input, uint32_t time,
166 uint32_t button,
167 enum wl_pointer_button_state state, void *data);
Kristian Høgsberg966e3ed2014-01-07 10:41:50 -0800168static void text_entry_touch_handler(struct widget *widget, struct input *input,
169 uint32_t serial, uint32_t time, int32_t id,
170 float tx, float ty, void *data);
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +0200171static int text_entry_motion_handler(struct widget *widget,
172 struct input *input, uint32_t time,
173 float x, float y, void *data);
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100174static void text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
175 int32_t cursor, int32_t anchor);
Jan Arne Petersen43f4aa82012-09-09 23:08:43 +0200176static void text_entry_set_preedit(struct text_entry *entry,
177 const char *preedit_text,
178 int preedit_cursor);
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200179static void text_entry_delete_text(struct text_entry *entry,
180 uint32_t index, uint32_t length);
Jan Arne Petersene386dd22012-09-17 15:28:09 +0200181static void text_entry_delete_selected_text(struct text_entry *entry);
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100182static void text_entry_reset_preedit(struct text_entry *entry);
183static void text_entry_commit_and_reset(struct text_entry *entry);
Jan Arne Petersenfe89e712013-04-18 16:47:27 +0200184static void text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle);
185static void text_entry_update(struct text_entry *entry);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200186
187static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200188text_input_commit_string(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800189 struct zwp_text_input_v1 *text_input,
Jan Arne Petersenc7d2a982013-01-16 21:26:39 +0100190 uint32_t serial,
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100191 const char *text)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200192{
193 struct text_entry *entry = data;
194
Jan Arne Petersen00191c72013-04-18 16:47:33 +0200195 if ((entry->serial - serial) > (entry->serial - entry->reset_serial)) {
196 fprintf(stderr, "Ignore commit. Serial: %u, Current: %u, Reset: %u\n",
197 serial, entry->serial, entry->reset_serial);
198 return;
199 }
200
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200201 if (entry->pending_commit.invalid_delete) {
202 fprintf(stderr, "Ignore commit. Invalid previous delete_surrounding event.\n");
203 memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
204 return;
205 }
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100206
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200207 text_entry_reset_preedit(entry);
Jan Arne Petersen919bc142013-04-18 16:47:34 +0200208
209 if (entry->pending_commit.delete_length) {
210 text_entry_delete_text(entry,
211 entry->pending_commit.delete_index,
212 entry->pending_commit.delete_length);
Jan Arne Petersena96953d2013-05-30 13:57:02 +0200213 } else {
214 text_entry_delete_selected_text(entry);
Jan Arne Petersen919bc142013-04-18 16:47:34 +0200215 }
216
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100217 text_entry_insert_at_cursor(entry, text,
218 entry->pending_commit.cursor,
219 entry->pending_commit.anchor);
220
221 memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200222
223 widget_schedule_redraw(entry->widget);
224}
225
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200226static void
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200227clear_pending_preedit(struct text_entry *entry)
228{
229 memset(&entry->pending_commit, 0, sizeof entry->pending_commit);
230
231 pango_attr_list_unref(entry->preedit_info.attr_list);
232
233 entry->preedit_info.cursor = 0;
234 entry->preedit_info.attr_list = NULL;
235
236 memset(&entry->preedit_info, 0, sizeof entry->preedit_info);
237}
238
239static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200240text_input_preedit_string(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800241 struct zwp_text_input_v1 *text_input,
Jan Arne Petersenc7d2a982013-01-16 21:26:39 +0100242 uint32_t serial,
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200243 const char *text,
Jan Arne Petersen46535312013-01-16 21:26:38 +0100244 const char *commit)
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200245{
Jan Arne Petersen43f4aa82012-09-09 23:08:43 +0200246 struct text_entry *entry = data;
247
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200248 if ((entry->serial - serial) > (entry->serial - entry->reset_serial)) {
249 fprintf(stderr, "Ignore preedit_string. Serial: %u, Current: %u, Reset: %u\n",
250 serial, entry->serial, entry->reset_serial);
251 clear_pending_preedit(entry);
252 return;
253 }
254
255 if (entry->pending_commit.invalid_delete) {
256 fprintf(stderr, "Ignore preedit_string. Invalid previous delete_surrounding event.\n");
257 clear_pending_preedit(entry);
258 return;
259 }
260
Jan Arne Petersena96953d2013-05-30 13:57:02 +0200261 if (entry->pending_commit.delete_length) {
262 text_entry_delete_text(entry,
263 entry->pending_commit.delete_index,
264 entry->pending_commit.delete_length);
265 } else {
266 text_entry_delete_selected_text(entry);
267 }
Jan Arne Petersena96953d2013-05-30 13:57:02 +0200268
Jan Arne Petersen46535312013-01-16 21:26:38 +0100269 text_entry_set_preedit(entry, text, entry->preedit_info.cursor);
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100270 entry->preedit.commit = strdup(commit);
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200271 entry->preedit.attr_list = pango_attr_list_ref(entry->preedit_info.attr_list);
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100272
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200273 clear_pending_preedit(entry);
Jan Arne Petersen43f4aa82012-09-09 23:08:43 +0200274
Jan Arne Petersenfe89e712013-04-18 16:47:27 +0200275 text_entry_update(entry);
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200276
Jan Arne Petersen43f4aa82012-09-09 23:08:43 +0200277 widget_schedule_redraw(entry->widget);
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200278}
279
280static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200281text_input_delete_surrounding_text(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800282 struct zwp_text_input_v1 *text_input,
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200283 int32_t index,
284 uint32_t length)
285{
286 struct text_entry *entry = data;
Jan Arne Petersen919bc142013-04-18 16:47:34 +0200287 uint32_t text_length;
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200288
Jan Arne Petersen919bc142013-04-18 16:47:34 +0200289 entry->pending_commit.delete_index = entry->cursor + index;
290 entry->pending_commit.delete_length = length;
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200291 entry->pending_commit.invalid_delete = false;
Jan Arne Petersen919bc142013-04-18 16:47:34 +0200292
Jan Arne Petersen68516862013-04-18 16:47:42 +0200293 text_length = strlen(entry->text);
Jan Arne Petersen919bc142013-04-18 16:47:34 +0200294
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200295 if (entry->pending_commit.delete_index > text_length ||
296 length > text_length ||
Jan Arne Petersen895a1282013-05-30 13:57:04 +0200297 entry->pending_commit.delete_index + length > text_length) {
Jan Arne Petersen8ccb7cc2013-05-30 13:57:05 +0200298 fprintf(stderr, "delete_surrounding_text: Invalid index: %d," \
299 "length %u'; cursor: %u text length: %u\n", index, length, entry->cursor, text_length);
300 entry->pending_commit.invalid_delete = true;
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200301 return;
302 }
Jan Arne Petersene202bae2012-09-09 23:08:44 +0200303}
304
305static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200306text_input_cursor_position(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800307 struct zwp_text_input_v1 *text_input,
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100308 int32_t index,
309 int32_t anchor)
310{
311 struct text_entry *entry = data;
312
313 entry->pending_commit.cursor = index;
314 entry->pending_commit.anchor = anchor;
315}
316
317static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200318text_input_preedit_styling(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800319 struct zwp_text_input_v1 *text_input,
Jan Arne Petersen46535312013-01-16 21:26:38 +0100320 uint32_t index,
321 uint32_t length,
322 uint32_t style)
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200323{
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100324 struct text_entry *entry = data;
325 PangoAttribute *attr1 = NULL;
326 PangoAttribute *attr2 = NULL;
327
328 if (!entry->preedit_info.attr_list)
329 entry->preedit_info.attr_list = pango_attr_list_new();
330
331 switch (style) {
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800332 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_DEFAULT:
333 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_UNDERLINE:
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100334 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
335 break;
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800336 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_INCORRECT:
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100337 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_ERROR);
338 attr2 = pango_attr_underline_color_new(65535, 0, 0);
339 break;
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800340 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_SELECTION:
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100341 attr1 = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
342 attr2 = pango_attr_foreground_new(65535, 65535, 65535);
343 break;
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800344 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT:
345 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_ACTIVE:
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100346 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
347 attr2 = pango_attr_weight_new(PANGO_WEIGHT_BOLD);
348 break;
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800349 case ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_INACTIVE:
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100350 attr1 = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
351 attr2 = pango_attr_foreground_new(0.3 * 65535, 0.3 * 65535, 0.3 * 65535);
352 break;
353 }
354
355 if (attr1) {
356 attr1->start_index = entry->cursor + index;
357 attr1->end_index = entry->cursor + index + length;
358 pango_attr_list_insert(entry->preedit_info.attr_list, attr1);
359 }
360
361 if (attr2) {
362 attr2->start_index = entry->cursor + index;
363 attr2->end_index = entry->cursor + index + length;
364 pango_attr_list_insert(entry->preedit_info.attr_list, attr2);
365 }
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200366}
367
368static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200369text_input_preedit_cursor(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800370 struct zwp_text_input_v1 *text_input,
Jan Arne Petersen46535312013-01-16 21:26:38 +0100371 int32_t index)
372{
373 struct text_entry *entry = data;
374
375 entry->preedit_info.cursor = index;
376}
377
378static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200379text_input_modifiers_map(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800380 struct zwp_text_input_v1 *text_input,
Jan Arne Petersend9be93b2012-11-18 19:06:43 +0100381 struct wl_array *map)
382{
Jan Arne Petersencd997062012-11-18 19:06:44 +0100383 struct text_entry *entry = data;
384
385 entry->keysym.shift_mask = keysym_modifiers_get_mask(map, "Shift");
Jan Arne Petersend9be93b2012-11-18 19:06:43 +0100386}
387
388static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200389text_input_keysym(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800390 struct zwp_text_input_v1 *text_input,
Jan Arne Petersend9be93b2012-11-18 19:06:43 +0100391 uint32_t serial,
392 uint32_t time,
393 uint32_t key,
394 uint32_t state,
395 uint32_t modifiers)
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200396{
Jan Arne Petersen8aba11d2012-09-17 15:28:07 +0200397 struct text_entry *entry = data;
Jan Arne Petersen6345faa2012-11-05 03:26:39 +0100398 const char *new_char;
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200399
Jan Arne Petersencd997062012-11-18 19:06:44 +0100400 if (key == XKB_KEY_Left ||
401 key == XKB_KEY_Right) {
402 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
403 return;
404
405 if (key == XKB_KEY_Left)
406 new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
407 else
408 new_char = utf8_next_char(entry->text + entry->cursor);
409
410 if (new_char != NULL) {
411 entry->cursor = new_char - entry->text;
Jan Arne Petersencd997062012-11-18 19:06:44 +0100412 }
413
Jan Arne Petersen68516862013-04-18 16:47:42 +0200414 if (!(modifiers & entry->keysym.shift_mask))
415 entry->anchor = entry->cursor;
416 widget_schedule_redraw(entry->widget);
417
Jan Arne Petersencd997062012-11-18 19:06:44 +0100418 return;
419 }
420
Manuel Bachmann8986c182014-04-18 12:50:14 +0200421 if (key == XKB_KEY_Up ||
422 key == XKB_KEY_Down) {
423 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
424 return;
425
426 if (key == XKB_KEY_Up)
427 move_up(entry->text, &entry->cursor);
428 else
429 move_down(entry->text, &entry->cursor);
430
431 if (!(modifiers & entry->keysym.shift_mask))
432 entry->anchor = entry->cursor;
433 widget_schedule_redraw(entry->widget);
434
435 return;
436 }
437
Jan Arne Petersen3fb6e712013-01-16 21:26:52 +0100438 if (key == XKB_KEY_BackSpace) {
439 const char *start, *end;
440
Jan Arne Petersendfd34462013-04-18 16:47:26 +0200441 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
442 return;
443
Jan Arne Petersen3fb6e712013-01-16 21:26:52 +0100444 text_entry_commit_and_reset(entry);
445
446 start = utf8_prev_char(entry->text, entry->text + entry->cursor);
Jan Arne Petersen3fb6e712013-01-16 21:26:52 +0100447 if (start == NULL)
448 return;
449
Daiki Uenob08b3292013-06-28 18:59:44 +0900450 end = utf8_next_char(start);
451
Jan Arne Petersen3fb6e712013-01-16 21:26:52 +0100452 text_entry_delete_text(entry,
453 start - entry->text,
454 end - start);
455
456 return;
457 }
458
Manuel Bachmann8986c182014-04-18 12:50:14 +0200459 if (key == XKB_KEY_Tab ||
460 key == XKB_KEY_KP_Enter ||
461 key == XKB_KEY_Return) {
462 char text[16];
Jan Arne Petersence8a4432012-09-09 23:08:45 +0200463
Manuel Bachmann8986c182014-04-18 12:50:14 +0200464 if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
465 return;
466
467 xkb_keysym_to_utf8(key, text, sizeof(text));
468
469 text_entry_insert_at_cursor(entry, text, 0, 0);
470
471 return;
472 }
Jan Arne Petersen72f60822012-08-10 16:47:19 +0200473}
474
475static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200476text_input_enter(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800477 struct zwp_text_input_v1 *text_input,
Jan Arne Petersen680275f2012-09-24 14:51:14 +0200478 struct wl_surface *surface)
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200479{
480 struct text_entry *entry = data;
481
Jan Arne Petersen680275f2012-09-24 14:51:14 +0200482 if (surface != window_get_wl_surface(entry->window))
483 return;
484
Derek Foreman237a6842014-12-17 09:43:58 -0600485 entry->active++;
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200486
Jan Arne Petersen00191c72013-04-18 16:47:33 +0200487 text_entry_update(entry);
488 entry->reset_serial = entry->serial;
489
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200490 widget_schedule_redraw(entry->widget);
491}
492
493static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200494text_input_leave(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800495 struct zwp_text_input_v1 *text_input)
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200496{
497 struct text_entry *entry = data;
498
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100499 text_entry_commit_and_reset(entry);
Derek Foreman237a6842014-12-17 09:43:58 -0600500 entry->active--;
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100501
Derek Foreman237a6842014-12-17 09:43:58 -0600502 if (!entry->active)
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800503 zwp_text_input_v1_hide_input_panel(text_input);
Jan Arne Petersen61381972013-01-31 15:52:21 +0100504
Jan Arne Petersende3b6a12012-08-10 16:47:21 +0200505 widget_schedule_redraw(entry->widget);
506}
507
Jan Arne Petersen61381972013-01-31 15:52:21 +0100508static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200509text_input_input_panel_state(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800510 struct zwp_text_input_v1 *text_input,
Jan Arne Petersen61381972013-01-31 15:52:21 +0100511 uint32_t state)
512{
513}
514
Jan Arne Petersenece6b5a2013-04-18 16:47:15 +0200515static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200516text_input_language(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800517 struct zwp_text_input_v1 *text_input,
Jan Arne Petersenece6b5a2013-04-18 16:47:15 +0200518 uint32_t serial,
519 const char *language)
520{
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200521 fprintf(stderr, "input language is %s \n", language);
Jan Arne Petersenece6b5a2013-04-18 16:47:15 +0200522}
523
524static void
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200525text_input_text_direction(void *data,
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800526 struct zwp_text_input_v1 *text_input,
Jan Arne Petersenece6b5a2013-04-18 16:47:15 +0200527 uint32_t serial,
528 uint32_t direction)
529{
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200530 struct text_entry *entry = data;
531 PangoContext *context = pango_layout_get_context(entry->layout);
532 PangoDirection pango_direction;
533
534
535 switch (direction) {
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800536 case ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_LTR:
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200537 pango_direction = PANGO_DIRECTION_LTR;
538 break;
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800539 case ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_RTL:
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200540 pango_direction = PANGO_DIRECTION_RTL;
541 break;
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800542 case ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_AUTO:
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200543 default:
544 pango_direction = PANGO_DIRECTION_NEUTRAL;
545 }
Jan Arne Petersen62ece762013-04-18 16:47:36 +0200546
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200547 pango_context_set_base_dir(context, pango_direction);
Jan Arne Petersenece6b5a2013-04-18 16:47:15 +0200548}
549
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800550static const struct zwp_text_input_v1_listener text_input_listener = {
Jan Arne Petersen78d00e42013-04-18 16:47:24 +0200551 text_input_enter,
552 text_input_leave,
553 text_input_modifiers_map,
554 text_input_input_panel_state,
555 text_input_preedit_string,
556 text_input_preedit_styling,
557 text_input_preedit_cursor,
558 text_input_commit_string,
559 text_input_cursor_position,
560 text_input_delete_surrounding_text,
561 text_input_keysym,
562 text_input_language,
563 text_input_text_direction
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200564};
565
Manuel Bachmann22f34302015-03-30 01:57:44 +0200566static void
567data_source_target(void *data,
568 struct wl_data_source *source, const char *mime_type)
569{
570}
571
572static void
573data_source_send(void *data,
574 struct wl_data_source *source,
575 const char *mime_type, int32_t fd)
576{
577 struct editor *editor = data;
578
Bryce Harrington7dd12ec2015-05-19 15:32:09 -0700579 if (write(fd, editor->selected_text, strlen(editor->selected_text) + 1) < 0)
580 fprintf(stderr, "write failed: %m\n");
Derek Foreman2af7e202016-07-07 10:52:17 -0500581
582 close(fd);
Manuel Bachmann22f34302015-03-30 01:57:44 +0200583}
584
585static void
586data_source_cancelled(void *data, struct wl_data_source *source)
587{
588 wl_data_source_destroy(source);
589}
590
591static const struct wl_data_source_listener data_source_listener = {
592 data_source_target,
593 data_source_send,
594 data_source_cancelled
595};
596
597static void
598paste_func(void *buffer, size_t len,
599 int32_t x, int32_t y, void *data)
600{
601 struct editor *editor = data;
602 struct text_entry *entry = editor->active_entry;
603 char *pasted_text;
604
605 if (!entry)
606 return;
607
608 pasted_text = malloc(len + 1);
609 strncpy(pasted_text, buffer, len);
610 pasted_text[len] = '\0';
611
612 text_entry_insert_at_cursor(entry, pasted_text, 0, 0);
613
614 free(pasted_text);
615}
616
617static void
618editor_copy_cut(struct editor *editor, struct input *input, bool cut)
619{
620 struct text_entry *entry = editor->active_entry;
621
622 if (!entry)
623 return;
Michael Vetter2a18a522015-05-15 17:17:47 +0200624
Manuel Bachmann22f34302015-03-30 01:57:44 +0200625 if (entry->cursor != entry->anchor) {
626 int start_index = MIN(entry->cursor, entry->anchor);
627 int end_index = MAX(entry->cursor, entry->anchor);
628 int len = end_index - start_index;
629
630 editor->selected_text = realloc(editor->selected_text, len + 1);
631 strncpy(editor->selected_text, &entry->text[start_index], len);
632 editor->selected_text[len] = '\0';
633
634 if (cut)
635 text_entry_delete_text(entry, start_index, len);
636
637 editor->selection =
638 display_create_data_source(editor->display);
639 wl_data_source_offer(editor->selection,
640 "text/plain;charset=utf-8");
641 wl_data_source_add_listener(editor->selection,
642 &data_source_listener, editor);
643 input_set_selection(input, editor->selection,
644 display_get_serial(editor->display));
645 }
646}
647
648static void
649editor_paste(struct editor *editor, struct input *input)
650{
651 input_receive_selection_data(input,
652 "text/plain;charset=utf-8",
653 paste_func, editor);
654}
655
656static void
657menu_func(void *data, struct input *input, int index)
658{
659 struct window *window = data;
660 struct editor *editor = window_get_user_data(window);
661
662 fprintf(stderr, "picked entry %d\n", index);
663
664 switch (index) {
665 case 0:
666 editor_copy_cut(editor, input, true);
667 break;
668 case 1:
669 editor_copy_cut(editor, input, false);
670 break;
671 case 2:
672 editor_paste(editor, input);
673 break;
674 }
675}
676
677static void
678show_menu(struct editor *editor, struct input *input, uint32_t time)
679{
680 int32_t x, y;
681 static const char *entries[] = {
682 "Cut", "Copy", "Paste"
683 };
684
685 input_get_position(input, &x, &y);
686 window_show_menu(editor->display, input, time, editor->window,
687 x + 10, y + 20, menu_func,
688 entries, ARRAY_LENGTH(entries));
689}
690
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200691static struct text_entry*
692text_entry_create(struct editor *editor, const char *text)
693{
694 struct text_entry *entry;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200695
Ryo Munakata5e653ca2015-08-07 20:20:46 +0900696 entry = xzalloc(sizeof *entry);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200697
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200698 entry->widget = widget_add_widget(editor->widget, entry);
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200699 entry->window = editor->window;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200700 entry->text = strdup(text);
701 entry->active = 0;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +0200702 entry->cursor = strlen(text);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +0200703 entry->anchor = entry->cursor;
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800704 entry->text_input =
705 zwp_text_input_manager_v1_create_text_input(editor->text_input_manager);
706 zwp_text_input_v1_add_listener(entry->text_input,
707 &text_input_listener, entry);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200708
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200709 widget_set_redraw_handler(entry->widget, text_entry_redraw_handler);
710 widget_set_button_handler(entry->widget, text_entry_button_handler);
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +0200711 widget_set_motion_handler(entry->widget, text_entry_motion_handler);
Kristian Høgsberg966e3ed2014-01-07 10:41:50 -0800712 widget_set_touch_down_handler(entry->widget, text_entry_touch_handler);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200713
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200714 return entry;
715}
716
717static void
718text_entry_destroy(struct text_entry *entry)
719{
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200720 widget_destroy(entry->widget);
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800721 zwp_text_input_v1_destroy(entry->text_input);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100722 g_clear_object(&entry->layout);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200723 free(entry->text);
Silvan Jegene31d95f2016-11-17 21:43:06 +0100724 free(entry->preferred_language);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200725 free(entry);
726}
727
728static void
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200729redraw_handler(struct widget *widget, void *data)
730{
731 struct editor *editor = data;
732 cairo_surface_t *surface;
733 struct rectangle allocation;
734 cairo_t *cr;
735
736 surface = window_get_surface(editor->window);
737 widget_get_allocation(editor->widget, &allocation);
738
739 cr = cairo_create(surface);
740 cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
741 cairo_clip(cr);
742
743 cairo_translate(cr, allocation.x, allocation.y);
744
745 /* Draw background */
746 cairo_push_group(cr);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200747 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200748 cairo_set_source_rgba(cr, 1, 1, 1, 1);
749 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
750 cairo_fill(cr);
751
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200752 cairo_pop_group_to_source(cr);
753 cairo_paint(cr);
754
755 cairo_destroy(cr);
756 cairo_surface_destroy(surface);
757}
758
759static void
760text_entry_allocate(struct text_entry *entry, int32_t x, int32_t y,
761 int32_t width, int32_t height)
762{
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200763 widget_set_allocation(entry->widget, x, y, width, height);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200764}
765
766static void
767resize_handler(struct widget *widget,
768 int32_t width, int32_t height, void *data)
769{
770 struct editor *editor = data;
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200771 struct rectangle allocation;
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200772
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200773 widget_get_allocation(editor->widget, &allocation);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200774
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200775 text_entry_allocate(editor->entry,
776 allocation.x + 20, allocation.y + 20,
777 width - 40, height / 2 - 40);
778 text_entry_allocate(editor->editor,
779 allocation.x + 20, allocation.y + height / 2 + 20,
780 width - 40, height / 2 - 40);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200781}
782
783static void
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200784text_entry_activate(struct text_entry *entry,
Jan Arne Petersenf80bc062012-09-09 23:08:34 +0200785 struct wl_seat *seat)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200786{
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200787 struct wl_surface *surface = window_get_wl_surface(entry->window);
788
Jan Arne Petersen61381972013-01-31 15:52:21 +0100789 if (entry->click_to_show && entry->active) {
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800790 zwp_text_input_v1_show_input_panel(entry->text_input);
Jan Arne Petersen61381972013-01-31 15:52:21 +0100791
792 return;
793 }
794
795 if (!entry->click_to_show)
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800796 zwp_text_input_v1_show_input_panel(entry->text_input);
Jan Arne Petersen61381972013-01-31 15:52:21 +0100797
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800798 zwp_text_input_v1_activate(entry->text_input,
799 seat,
800 surface);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200801}
802
803static void
Jan Arne Petersene829adc2012-08-10 16:47:22 +0200804text_entry_deactivate(struct text_entry *entry,
805 struct wl_seat *seat)
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200806{
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800807 zwp_text_input_v1_deactivate(entry->text_input,
808 seat);
Jan Arne Petersencba9e472012-06-21 21:52:19 +0200809}
810
811static void
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200812text_entry_update_layout(struct text_entry *entry)
813{
814 char *text;
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100815 PangoAttrList *attr_list;
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200816
Jan Arne Petersen68516862013-04-18 16:47:42 +0200817 assert(entry->cursor <= (strlen(entry->text) +
818 (entry->preedit.text ? strlen(entry->preedit.text) : 0)));
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200819
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100820 if (entry->preedit.text) {
Derek Foreman22044922014-11-20 15:42:35 -0600821 text = xmalloc(strlen(entry->text) + strlen(entry->preedit.text) + 1);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100822 strncpy(text, entry->text, entry->cursor);
823 strcpy(text + entry->cursor, entry->preedit.text);
824 strcpy(text + entry->cursor + strlen(entry->preedit.text),
825 entry->text + entry->cursor);
826 } else {
827 text = strdup(entry->text);
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200828 }
829
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100830 if (entry->cursor != entry->anchor) {
831 int start_index = MIN(entry->cursor, entry->anchor);
832 int end_index = MAX(entry->cursor, entry->anchor);
833 PangoAttribute *attr;
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200834
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100835 attr_list = pango_attr_list_copy(entry->preedit.attr_list);
836
837 if (!attr_list)
838 attr_list = pango_attr_list_new();
839
840 attr = pango_attr_background_new(0.3 * 65535, 0.3 * 65535, 65535);
841 attr->start_index = start_index;
842 attr->end_index = end_index;
843 pango_attr_list_insert(attr_list, attr);
844
845 attr = pango_attr_foreground_new(65535, 65535, 65535);
846 attr->start_index = start_index;
847 attr->end_index = end_index;
848 pango_attr_list_insert(attr_list, attr);
849 } else {
850 attr_list = pango_attr_list_ref(entry->preedit.attr_list);
851 }
852
853 if (entry->preedit.text && !entry->preedit.attr_list) {
854 PangoAttribute *attr;
855
856 if (!attr_list)
857 attr_list = pango_attr_list_new();
858
859 attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
860 attr->start_index = entry->cursor;
861 attr->end_index = entry->cursor + strlen(entry->preedit.text);
862 pango_attr_list_insert(attr_list, attr);
863 }
864
865 if (entry->layout) {
866 pango_layout_set_text(entry->layout, text, -1);
867 pango_layout_set_attributes(entry->layout, attr_list);
868 }
869
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200870 free(text);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100871 pango_attr_list_unref(attr_list);
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200872}
873
874static void
Jan Arne Petersen0558a932013-01-16 21:26:45 +0100875text_entry_update(struct text_entry *entry)
876{
Jan Arne Petersenfe89e712013-04-18 16:47:27 +0200877 struct rectangle cursor_rectangle;
878
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800879 zwp_text_input_v1_set_content_type(entry->text_input,
880 ZWP_TEXT_INPUT_V1_CONTENT_HINT_NONE,
881 entry->content_purpose);
Jan Arne Petersen0558a932013-01-16 21:26:45 +0100882
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800883 zwp_text_input_v1_set_surrounding_text(entry->text_input,
884 entry->text,
885 entry->cursor,
886 entry->anchor);
Jan Arne Petersen0eabcaa2013-01-31 15:52:20 +0100887
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200888 if (entry->preferred_language)
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800889 zwp_text_input_v1_set_preferred_language(entry->text_input,
890 entry->preferred_language);
Jan Arne Petersen9d419132013-04-18 16:47:16 +0200891
Jan Arne Petersenfe89e712013-04-18 16:47:27 +0200892 text_entry_get_cursor_rectangle(entry, &cursor_rectangle);
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800893 zwp_text_input_v1_set_cursor_rectangle(entry->text_input,
894 cursor_rectangle.x,
895 cursor_rectangle.y,
896 cursor_rectangle.width,
897 cursor_rectangle.height);
Jan Arne Petersenfe89e712013-04-18 16:47:27 +0200898
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800899 zwp_text_input_v1_commit_state(entry->text_input, ++entry->serial);
Jan Arne Petersen0558a932013-01-16 21:26:45 +0100900}
901
902static void
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100903text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
904 int32_t cursor, int32_t anchor)
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200905{
Derek Foreman22044922014-11-20 15:42:35 -0600906 char *new_text = xmalloc(strlen(entry->text) + strlen(text) + 1);
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200907
908 strncpy(new_text, entry->text, entry->cursor);
909 strcpy(new_text + entry->cursor, text);
910 strcpy(new_text + entry->cursor + strlen(text),
911 entry->text + entry->cursor);
912
913 free(entry->text);
914 entry->text = new_text;
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100915 if (anchor >= 0)
916 entry->anchor = entry->cursor + strlen(text) + anchor;
917 else
918 entry->anchor = entry->cursor + 1 + anchor;
Jan Arne Petersen68516862013-04-18 16:47:42 +0200919
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100920 if (cursor >= 0)
921 entry->cursor += strlen(text) + cursor;
922 else
923 entry->cursor += 1 + cursor;
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200924
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200925 text_entry_update_layout(entry);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100926
927 widget_schedule_redraw(entry->widget);
928
Jan Arne Petersen0558a932013-01-16 21:26:45 +0100929 text_entry_update(entry);
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200930}
931
932static void
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100933text_entry_reset_preedit(struct text_entry *entry)
934{
935 entry->preedit.cursor = 0;
936
937 free(entry->preedit.text);
938 entry->preedit.text = NULL;
939
940 free(entry->preedit.commit);
941 entry->preedit.commit = NULL;
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100942
943 pango_attr_list_unref(entry->preedit.attr_list);
944 entry->preedit.attr_list = NULL;
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100945}
946
947static void
948text_entry_commit_and_reset(struct text_entry *entry)
949{
950 char *commit = NULL;
951
952 if (entry->preedit.commit)
953 commit = strdup(entry->preedit.commit);
954
955 text_entry_reset_preedit(entry);
956 if (commit) {
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +0100957 text_entry_insert_at_cursor(entry, commit, 0, 0);
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100958 free(commit);
959 }
Jan Arne Petersen08015b62013-04-18 16:47:18 +0200960
Jonas Ådahl3bcba342015-11-17 16:00:29 +0800961 zwp_text_input_v1_reset(entry->text_input);
Jan Arne Petersen00191c72013-04-18 16:47:33 +0200962 text_entry_update(entry);
963 entry->reset_serial = entry->serial;
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100964}
965
966static void
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200967text_entry_set_preedit(struct text_entry *entry,
968 const char *preedit_text,
969 int preedit_cursor)
970{
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +0100971 text_entry_reset_preedit(entry);
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200972
973 if (!preedit_text)
974 return;
975
Jan Arne Petersen46535312013-01-16 21:26:38 +0100976 entry->preedit.text = strdup(preedit_text);
977 entry->preedit.cursor = preedit_cursor;
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +0200978
979 text_entry_update_layout(entry);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +0100980
981 widget_schedule_redraw(entry->widget);
Jan Arne Petersen09e7c962012-09-09 23:08:37 +0200982}
983
Jan Arne Petersen3489ba92013-01-16 21:26:47 +0100984static uint32_t
985text_entry_try_invoke_preedit_action(struct text_entry *entry,
986 int32_t x, int32_t y,
987 uint32_t button,
988 enum wl_pointer_button_state state)
989{
990 int index, trailing;
991 uint32_t cursor;
Jan Arne Petersen68516862013-04-18 16:47:42 +0200992 const char *text;
Jan Arne Petersen3489ba92013-01-16 21:26:47 +0100993
994 if (!entry->preedit.text)
995 return 0;
996
997 pango_layout_xy_to_index(entry->layout,
998 x * PANGO_SCALE, y * PANGO_SCALE,
999 &index, &trailing);
Jan Arne Petersen68516862013-04-18 16:47:42 +02001000
1001 text = pango_layout_get_text(entry->layout);
1002 cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
Jan Arne Petersen3489ba92013-01-16 21:26:47 +01001003
1004 if (cursor < entry->cursor ||
1005 cursor > entry->cursor + strlen(entry->preedit.text)) {
1006 return 0;
1007 }
1008
1009 if (state == WL_POINTER_BUTTON_STATE_RELEASED)
Jonas Ådahl3bcba342015-11-17 16:00:29 +08001010 zwp_text_input_v1_invoke_action(entry->text_input,
1011 button,
1012 cursor - entry->cursor);
Jan Arne Petersen3489ba92013-01-16 21:26:47 +01001013
1014 return 1;
1015}
1016
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001017static bool
1018text_entry_has_preedit(struct text_entry *entry)
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001019{
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001020 return entry->preedit.text && (strlen(entry->preedit.text) > 0);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001021}
1022
1023static void
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001024text_entry_set_cursor_position(struct text_entry *entry,
1025 int32_t x, int32_t y,
1026 bool move_anchor)
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001027{
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +01001028 int index, trailing;
Jan Arne Petersen68516862013-04-18 16:47:42 +02001029 const char *text;
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001030 uint32_t cursor;
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +01001031
1032 pango_layout_xy_to_index(entry->layout,
1033 x * PANGO_SCALE, y * PANGO_SCALE,
1034 &index, &trailing);
Jan Arne Petersen68516862013-04-18 16:47:42 +02001035
1036 text = pango_layout_get_text(entry->layout);
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001037
1038 cursor = g_utf8_offset_to_pointer(text + index, trailing) - text;
1039
1040 if (move_anchor)
1041 entry->anchor = cursor;
1042
1043 if (text_entry_has_preedit(entry)) {
1044 text_entry_commit_and_reset(entry);
1045
1046 assert(!text_entry_has_preedit(entry));
1047 }
1048
1049 if (entry->cursor == cursor)
1050 return;
1051
1052 entry->cursor = cursor;
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +01001053
1054 text_entry_update_layout(entry);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001055
1056 widget_schedule_redraw(entry->widget);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +01001057
Jan Arne Petersen0558a932013-01-16 21:26:45 +01001058 text_entry_update(entry);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001059}
1060
1061static void
Jan Arne Petersene202bae2012-09-09 23:08:44 +02001062text_entry_delete_text(struct text_entry *entry,
1063 uint32_t index, uint32_t length)
1064{
Jan Arne Petersen68516862013-04-18 16:47:42 +02001065 uint32_t l;
1066
Jan Arne Petersen895a1282013-05-30 13:57:04 +02001067 assert(index <= strlen(entry->text));
1068 assert(index + length <= strlen(entry->text));
1069 assert(index + length >= length);
Jan Arne Petersen80ad1a92012-09-17 15:28:10 +02001070
Jan Arne Petersen68516862013-04-18 16:47:42 +02001071 l = strlen(entry->text + index + length);
1072 memmove(entry->text + index,
1073 entry->text + index + length,
1074 l + 1);
Jan Arne Petersene202bae2012-09-09 23:08:44 +02001075
Jan Arne Petersen9eaa8e52013-05-30 13:57:03 +02001076 if (entry->cursor > (index + length))
1077 entry->cursor -= length;
1078 else if (entry->cursor > index)
1079 entry->cursor = index;
1080
1081 entry->anchor = entry->cursor;
1082
Jan Arne Petersene202bae2012-09-09 23:08:44 +02001083 text_entry_update_layout(entry);
1084
1085 widget_schedule_redraw(entry->widget);
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +01001086
Jan Arne Petersen0558a932013-01-16 21:26:45 +01001087 text_entry_update(entry);
Jan Arne Petersene202bae2012-09-09 23:08:44 +02001088}
1089
1090static void
Jan Arne Petersene386dd22012-09-17 15:28:09 +02001091text_entry_delete_selected_text(struct text_entry *entry)
1092{
1093 uint32_t start_index = entry->anchor < entry->cursor ? entry->anchor : entry->cursor;
1094 uint32_t end_index = entry->anchor < entry->cursor ? entry->cursor : entry->anchor;
1095
1096 if (entry->anchor == entry->cursor)
1097 return;
1098
1099 text_entry_delete_text(entry, start_index, end_index - start_index);
1100
1101 entry->anchor = entry->cursor;
1102}
1103
1104static void
Jan Arne Petersenfe89e712013-04-18 16:47:27 +02001105text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rectangle)
1106{
1107 struct rectangle allocation;
1108 PangoRectangle extents;
1109 PangoRectangle cursor_pos;
1110
1111 widget_get_allocation(entry->widget, &allocation);
1112
1113 if (entry->preedit.text && entry->preedit.cursor < 0) {
1114 rectangle->x = 0;
1115 rectangle->y = 0;
1116 rectangle->width = 0;
1117 rectangle->height = 0;
1118 return;
1119 }
1120
Jan Arne Petersen68516862013-04-18 16:47:42 +02001121
Jan Arne Petersenfe89e712013-04-18 16:47:27 +02001122 pango_layout_get_extents(entry->layout, &extents, NULL);
1123 pango_layout_get_cursor_pos(entry->layout,
1124 entry->cursor + entry->preedit.cursor,
1125 &cursor_pos, NULL);
1126
1127 rectangle->x = allocation.x + (allocation.height / 2) + PANGO_PIXELS(cursor_pos.x);
1128 rectangle->y = allocation.y + 10 + PANGO_PIXELS(cursor_pos.y);
1129 rectangle->width = PANGO_PIXELS(cursor_pos.width);
1130 rectangle->height = PANGO_PIXELS(cursor_pos.height);
1131}
1132
1133static void
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001134text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr)
1135{
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +01001136 PangoRectangle extents;
1137 PangoRectangle cursor_pos;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001138
Jan Arne Petersen46535312013-01-16 21:26:38 +01001139 if (entry->preedit.text && entry->preedit.cursor < 0)
1140 return;
1141
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +01001142 pango_layout_get_extents(entry->layout, &extents, NULL);
1143 pango_layout_get_cursor_pos(entry->layout,
1144 entry->cursor + entry->preedit.cursor,
1145 &cursor_pos, NULL);
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001146
1147 cairo_set_line_width(cr, 1.0);
Peter Maatmanb9a23f42013-07-06 20:55:54 +02001148 cairo_move_to(cr, PANGO_PIXELS(cursor_pos.x), PANGO_PIXELS(cursor_pos.y));
1149 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 +02001150 cairo_stroke(cr);
1151}
1152
Ander Conselvan de Oliveira8e37d962014-05-08 14:55:50 +03001153static int
1154text_offset_left(struct rectangle *allocation)
1155{
1156 return 10;
1157}
1158
1159static int
1160text_offset_top(struct rectangle *allocation)
1161{
1162 return allocation->height / 2;
1163}
Philipp Brüschweiler9f897c72012-10-02 11:06:53 +02001164
Jan Arne Petersenc1fbcb72012-09-09 23:08:39 +02001165static void
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001166text_entry_redraw_handler(struct widget *widget, void *data)
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001167{
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001168 struct text_entry *entry = data;
1169 cairo_surface_t *surface;
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001170 struct rectangle allocation;
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001171 cairo_t *cr;
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001172
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001173 surface = window_get_surface(entry->window);
1174 widget_get_allocation(entry->widget, &allocation);
1175
1176 cr = cairo_create(surface);
1177 cairo_rectangle(cr, allocation.x, allocation.y, allocation.width, allocation.height);
1178 cairo_clip(cr);
1179
1180 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1181
1182 cairo_push_group(cr);
1183 cairo_translate(cr, allocation.x, allocation.y);
1184
1185 cairo_set_source_rgba(cr, 1, 1, 1, 1);
1186 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
1187 cairo_fill(cr);
1188
1189 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1190
1191 if (entry->active) {
1192 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
1193 cairo_set_line_width (cr, 3);
1194 cairo_set_source_rgba(cr, 0, 0, 1, 1.0);
1195 cairo_stroke(cr);
1196 }
1197
1198 cairo_set_source_rgba(cr, 0, 0, 0, 1);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001199
Ander Conselvan de Oliveira8e37d962014-05-08 14:55:50 +03001200 cairo_translate(cr,
1201 text_offset_left(&allocation),
1202 text_offset_top(&allocation));
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001203
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +01001204 if (!entry->layout)
1205 entry->layout = pango_cairo_create_layout(cr);
1206 else
1207 pango_cairo_update_layout(cr, entry->layout);
1208
1209 text_entry_update_layout(entry);
1210
1211 pango_cairo_show_layout(cr, entry->layout);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001212
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001213 text_entry_draw_cursor(entry, cr);
1214
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001215 cairo_pop_group_to_source(cr);
1216 cairo_paint(cr);
1217
1218 cairo_destroy(cr);
1219 cairo_surface_destroy(surface);
1220}
1221
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001222static int
1223text_entry_motion_handler(struct widget *widget,
1224 struct input *input, uint32_t time,
1225 float x, float y, void *data)
1226{
1227 struct text_entry *entry = data;
1228 struct rectangle allocation;
Ander Conselvan de Oliveira8e37d962014-05-08 14:55:50 +03001229 int tx, ty;
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001230
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001231 if (!entry->button_pressed) {
1232 return CURSOR_IBEAM;
1233 }
1234
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001235 widget_get_allocation(entry->widget, &allocation);
1236
Ander Conselvan de Oliveira8e37d962014-05-08 14:55:50 +03001237 tx = x - allocation.x - text_offset_left(&allocation);
1238 ty = y - allocation.y - text_offset_top(&allocation);
1239
1240 text_entry_set_cursor_position(entry, tx, ty, false);
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001241
1242 return CURSOR_IBEAM;
1243}
1244
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001245static void
1246text_entry_button_handler(struct widget *widget,
1247 struct input *input, uint32_t time,
1248 uint32_t button,
1249 enum wl_pointer_button_state state, void *data)
1250{
1251 struct text_entry *entry = data;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001252 struct rectangle allocation;
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001253 struct editor *editor;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001254 int32_t x, y;
Jan Arne Petersen3489ba92013-01-16 21:26:47 +01001255 uint32_t result;
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001256
1257 widget_get_allocation(entry->widget, &allocation);
1258 input_get_position(input, &x, &y);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001259
Ander Conselvan de Oliveira8e37d962014-05-08 14:55:50 +03001260 x -= allocation.x + text_offset_left(&allocation);
1261 y -= allocation.y + text_offset_top(&allocation);
Jan Arne Petersen3489ba92013-01-16 21:26:47 +01001262
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001263 editor = window_get_user_data(entry->window);
1264
Manuel Bachmann22f34302015-03-30 01:57:44 +02001265 switch (button) {
1266 case BTN_LEFT:
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001267 entry->button_pressed = (state == WL_POINTER_BUTTON_STATE_PRESSED);
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001268 if (state == WL_POINTER_BUTTON_STATE_PRESSED)
1269 input_grab(input, entry->widget, button);
1270 else
1271 input_ungrab(input);
Manuel Bachmann22f34302015-03-30 01:57:44 +02001272 break;
1273 case BTN_RIGHT:
1274 if (state == WL_POINTER_BUTTON_STATE_PRESSED)
1275 show_menu(editor, input, time);
1276 break;
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001277 }
1278
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001279 if (text_entry_has_preedit(entry)) {
1280 result = text_entry_try_invoke_preedit_action(entry, x, y, button, state);
1281
1282 if (result)
1283 return;
1284 }
Jan Arne Petersen7e634a02012-09-09 23:08:36 +02001285
Manuel Bachmann22f34302015-03-30 01:57:44 +02001286 if (state == WL_POINTER_BUTTON_STATE_PRESSED &&
1287 button == BTN_LEFT) {
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001288 struct wl_seat *seat = input_get_seat(input);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001289
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001290 text_entry_activate(entry, seat);
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001291 editor->active_entry = entry;
Jan Arne Petersen0e5bd452012-09-09 23:08:38 +02001292
Jan Arne Petersen1c45b4a2013-05-30 13:57:01 +02001293 text_entry_set_cursor_position(entry, x, y, true);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001294 }
1295}
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001296
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001297static void
Kristian Høgsberg966e3ed2014-01-07 10:41:50 -08001298text_entry_touch_handler(struct widget *widget, struct input *input,
1299 uint32_t serial, uint32_t time, int32_t id,
1300 float tx, float ty, void *data)
1301{
1302 struct text_entry *entry = data;
1303 struct wl_seat *seat = input_get_seat(input);
1304 struct rectangle allocation;
1305 struct editor *editor;
1306 int32_t x, y;
1307
1308 widget_get_allocation(entry->widget, &allocation);
1309
Ander Conselvan de Oliveira8e37d962014-05-08 14:55:50 +03001310 x = tx - (allocation.x + text_offset_left(&allocation));
1311 y = ty - (allocation.y + text_offset_top(&allocation));
Kristian Høgsberg966e3ed2014-01-07 10:41:50 -08001312
1313 editor = window_get_user_data(entry->window);
1314 text_entry_activate(entry, seat);
1315 editor->active_entry = entry;
1316
1317 text_entry_set_cursor_position(entry, x, y, true);
1318}
1319
1320static void
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001321editor_button_handler(struct widget *widget,
1322 struct input *input, uint32_t time,
1323 uint32_t button,
1324 enum wl_pointer_button_state state, void *data)
1325{
1326 struct editor *editor = data;
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001327
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001328 if (button != BTN_LEFT) {
1329 return;
1330 }
Jan Arne Petersene829adc2012-08-10 16:47:22 +02001331
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001332 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
1333 struct wl_seat *seat = input_get_seat(input);
1334
Jan Arne Petersene829adc2012-08-10 16:47:22 +02001335 text_entry_deactivate(editor->entry, seat);
1336 text_entry_deactivate(editor->editor, seat);
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001337 editor->active_entry = NULL;
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001338 }
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001339}
1340
Kristian Høgsberg966e3ed2014-01-07 10:41:50 -08001341static void
1342editor_touch_handler(struct widget *widget, struct input *input,
1343 uint32_t serial, uint32_t time, int32_t id,
1344 float tx, float ty, void *data)
1345{
1346 struct editor *editor = data;
1347
1348 struct wl_seat *seat = input_get_seat(input);
1349
1350 text_entry_deactivate(editor->entry, seat);
1351 text_entry_deactivate(editor->editor, seat);
1352 editor->active_entry = NULL;
1353}
Kristian Høgsberg78858902014-01-01 23:57:42 -08001354
1355static void
1356keyboard_focus_handler(struct window *window,
1357 struct input *device, void *data)
1358{
1359 struct editor *editor = data;
1360
1361 window_schedule_redraw(editor->window);
1362}
1363
Manuel Bachmann22f34302015-03-30 01:57:44 +02001364static int
1365handle_bound_key(struct editor *editor,
1366 struct input *input, uint32_t sym, uint32_t time)
1367{
1368 switch (sym) {
1369 case XKB_KEY_X:
1370 editor_copy_cut(editor, input, true);
1371 return 1;
1372 case XKB_KEY_C:
1373 editor_copy_cut(editor, input, false);
1374 return 1;
1375 case XKB_KEY_V:
1376 editor_paste(editor, input);
1377 return 1;
1378 default:
1379 return 0;
1380 }
1381}
1382
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001383static void
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001384key_handler(struct window *window,
1385 struct input *input, uint32_t time,
1386 uint32_t key, uint32_t sym, enum wl_keyboard_key_state state,
1387 void *data)
1388{
1389 struct editor *editor = data;
1390 struct text_entry *entry;
Jan Arne Petersen68516862013-04-18 16:47:42 +02001391 const char *new_char;
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001392 char text[16];
Manuel Bachmann22f34302015-03-30 01:57:44 +02001393 uint32_t modifiers;
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001394
1395 if (!editor->active_entry)
1396 return;
1397
1398 entry = editor->active_entry;
1399
1400 if (state != WL_KEYBOARD_KEY_STATE_PRESSED)
1401 return;
1402
Manuel Bachmann22f34302015-03-30 01:57:44 +02001403 modifiers = input_get_modifiers(input);
1404 if ((modifiers & MOD_CONTROL_MASK) &&
1405 (modifiers & MOD_SHIFT_MASK) &&
1406 handle_bound_key(editor, input, sym, time))
1407 return;
1408
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001409 switch (sym) {
1410 case XKB_KEY_BackSpace:
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +01001411 text_entry_commit_and_reset(entry);
1412
Jan Arne Petersen68516862013-04-18 16:47:42 +02001413 new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
1414 if (new_char != NULL)
1415 text_entry_delete_text(entry,
1416 new_char - entry->text,
1417 (entry->text + entry->cursor) - new_char);
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001418 break;
1419 case XKB_KEY_Delete:
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +01001420 text_entry_commit_and_reset(entry);
1421
Jan Arne Petersen68516862013-04-18 16:47:42 +02001422 new_char = utf8_next_char(entry->text + entry->cursor);
1423 if (new_char != NULL)
1424 text_entry_delete_text(entry,
1425 entry->cursor,
1426 new_char - (entry->text + entry->cursor));
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001427 break;
1428 case XKB_KEY_Left:
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +01001429 text_entry_commit_and_reset(entry);
1430
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001431 new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
1432 if (new_char != NULL) {
1433 entry->cursor = new_char - entry->text;
Rob Bradford70002832013-07-11 16:00:00 +01001434 if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1435 entry->anchor = entry->cursor;
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001436 widget_schedule_redraw(entry->widget);
1437 }
1438 break;
1439 case XKB_KEY_Right:
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +01001440 text_entry_commit_and_reset(entry);
1441
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001442 new_char = utf8_next_char(entry->text + entry->cursor);
1443 if (new_char != NULL) {
1444 entry->cursor = new_char - entry->text;
Rob Bradford70002832013-07-11 16:00:00 +01001445 if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1446 entry->anchor = entry->cursor;
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001447 widget_schedule_redraw(entry->widget);
1448 }
1449 break;
Manuel Bachmann8986c182014-04-18 12:50:14 +02001450 case XKB_KEY_Up:
1451 text_entry_commit_and_reset(entry);
1452
1453 move_up(entry->text, &entry->cursor);
1454 if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1455 entry->anchor = entry->cursor;
1456 widget_schedule_redraw(entry->widget);
1457 break;
1458 case XKB_KEY_Down:
1459 text_entry_commit_and_reset(entry);
1460
1461 move_down(entry->text, &entry->cursor);
1462 if (!(input_get_modifiers(input) & MOD_SHIFT_MASK))
1463 entry->anchor = entry->cursor;
1464 widget_schedule_redraw(entry->widget);
1465 break;
Peter Maatman08c38d42013-07-06 20:42:59 +02001466 case XKB_KEY_Escape:
1467 break;
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001468 default:
1469 if (xkb_keysym_to_utf8(sym, text, sizeof(text)) <= 0)
1470 break;
1471
Peter Maatman08c38d42013-07-06 20:42:59 +02001472 text_entry_commit_and_reset(entry);
Jan Arne Petersen4a17fae2013-01-16 21:26:40 +01001473
Jan Arne Petersen1cc9e082013-01-31 15:52:23 +01001474 text_entry_insert_at_cursor(entry, text, 0, 0);
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001475 break;
1476 }
1477
1478 widget_schedule_redraw(entry->widget);
1479}
1480
1481static void
Kristian Høgsbergfa80e112012-10-10 21:34:26 -04001482global_handler(struct display *display, uint32_t name,
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001483 const char *interface, uint32_t version, void *data)
1484{
1485 struct editor *editor = data;
1486
Jonas Ådahl3bcba342015-11-17 16:00:29 +08001487 if (!strcmp(interface, "zwp_text_input_manager_v1")) {
Jan Arne Petersen78d00e42013-04-18 16:47:24 +02001488 editor->text_input_manager =
Kristian Høgsbergfa80e112012-10-10 21:34:26 -04001489 display_bind(display, name,
Jonas Ådahl3bcba342015-11-17 16:00:29 +08001490 &zwp_text_input_manager_v1_interface, 1);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001491 }
1492}
1493
Bryce Harrington3d90da22016-11-21 13:26:22 -08001494/** Display help for command line options, and exit */
1495static uint32_t opt_help = 0;
1496
1497/** Require a distinct click to show the input panel (virtual keyboard) */
1498static uint32_t opt_click_to_show = 0;
1499
1500/** Set a specific (RFC-3066) language. Used for the virtual keyboard, etc. */
1501static const char *opt_preferred_language = NULL;
1502
1503/**
1504 * \brief command line options for editor
1505 */
1506static const struct weston_option editor_options[] = {
1507 { WESTON_OPTION_BOOLEAN, "help", 'h', &opt_help },
1508 { WESTON_OPTION_BOOLEAN, "click-to-show", 'C', &opt_click_to_show },
1509 { WESTON_OPTION_STRING, "preferred-language", 'L', &opt_preferred_language },
1510};
1511
1512static void
1513usage(const char *program_name, int exit_code)
1514{
1515 unsigned k;
1516
Bryce Harrington411ffab2016-11-21 13:26:23 -08001517 fprintf(stderr, "Usage: %s [OPTIONS] [FILENAME]\n\n", program_name);
Bryce Harrington3d90da22016-11-21 13:26:22 -08001518 for (k = 0; k < ARRAY_LENGTH(editor_options); k++) {
1519 const struct weston_option *p = &editor_options[k];
1520 if (p->name) {
1521 fprintf(stderr, " --%s", p->name);
1522 if (p->type != WESTON_OPTION_BOOLEAN)
1523 fprintf(stderr, "=VALUE");
1524 fprintf(stderr, "\n");
1525 }
1526 if (p->short_name) {
1527 fprintf(stderr, " -%c", p->short_name);
1528 if (p->type != WESTON_OPTION_BOOLEAN)
1529 fprintf(stderr, "VALUE");
1530 fprintf(stderr, "\n");
1531 }
1532 }
1533 exit(exit_code);
1534}
1535
Bryce Harrington411ffab2016-11-21 13:26:23 -08001536/* Load the contents of a file into a UTF-8 text buffer and return it.
1537 *
1538 * Caller is responsible for freeing the buffer when done.
1539 * On error, returns NULL.
1540 */
1541static char *
1542read_file(char *filename)
1543{
1544 char *buffer = NULL;
1545 int buf_size, read_size;
1546 FILE *fin;
1547 int errsv;
1548
1549 fin = fopen(filename, "r");
1550 if (fin == NULL)
1551 goto error;
1552
1553 /* Determine required buffer size */
1554 if (fseek(fin, 0, SEEK_END) != 0)
1555 goto error;
1556 buf_size = ftell(fin);
1557 if (buf_size < 0)
1558 goto error;
1559 rewind(fin);
1560
1561 /* Create buffer and read in the text */
1562 buffer = (char*) malloc(sizeof(char) * (buf_size + 1));
1563 if (buffer == NULL)
1564 goto error;
1565 read_size = fread(buffer, sizeof(char), buf_size, fin);
1566 fclose(fin);
1567 if (buf_size != read_size)
1568 goto error;
1569 buffer[buf_size] = '\0';
1570
1571 return buffer;
1572
1573error:
1574 errsv = errno;
1575 if (fin)
1576 fclose(fin);
1577 free(buffer);
1578 errno = errsv || EINVAL;
1579
1580 return NULL;
1581}
1582
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001583int
1584main(int argc, char *argv[])
1585{
1586 struct editor editor;
Bryce Harrington411ffab2016-11-21 13:26:23 -08001587 char *text_buffer = NULL;
Jan Arne Petersen61381972013-01-31 15:52:21 +01001588
Bryce Harrington3d90da22016-11-21 13:26:22 -08001589 parse_options(editor_options, ARRAY_LENGTH(editor_options),
1590 &argc, argv);
1591 if (opt_help)
1592 usage(argv[0], EXIT_SUCCESS);
1593
1594 if (argc > 1) {
Bryce Harrington411ffab2016-11-21 13:26:23 -08001595 if (argv[1][0] == '-')
1596 usage(argv[0], EXIT_FAILURE);
1597
1598 text_buffer = read_file(argv[1]);
1599 if (text_buffer == NULL) {
1600 fprintf(stderr, "could not read file '%s': %m\n", argv[1]);
1601 return -1;
1602 }
Jan Arne Petersen9d419132013-04-18 16:47:16 +02001603 }
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001604
Jan Arne Petersen25f6db52012-11-05 03:26:40 +01001605 memset(&editor, 0, sizeof editor);
1606
Jan Arne Petersen0a1cf392013-01-16 21:26:42 +01001607#ifdef HAVE_PANGO
1608 g_type_init();
1609#endif
1610
Kristian Høgsberg4172f662013-02-20 15:27:49 -05001611 editor.display = display_create(&argc, argv);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001612 if (editor.display == NULL) {
1613 fprintf(stderr, "failed to create display: %m\n");
1614 return -1;
1615 }
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001616
Kristian Høgsbergfa80e112012-10-10 21:34:26 -04001617 display_set_user_data(editor.display, &editor);
1618 display_set_global_handler(editor.display, global_handler);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001619
Olivier Blin30e1f3c2014-09-16 19:13:17 +02001620 if (editor.text_input_manager == NULL) {
1621 fprintf(stderr, "No text input manager global\n");
1622 return -1;
1623 }
1624
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001625 editor.window = window_create(editor.display);
Jason Ekstrandee7fefc2013-10-13 19:08:38 -05001626 editor.widget = window_frame_create(editor.window, &editor);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001627
Bryce Harrington411ffab2016-11-21 13:26:23 -08001628 if (text_buffer)
1629 editor.entry = text_entry_create(&editor, text_buffer);
1630 else
1631 editor.entry = text_entry_create(&editor, "Entry");
Bryce Harrington3d90da22016-11-21 13:26:22 -08001632 editor.entry->click_to_show = opt_click_to_show;
1633 if (opt_preferred_language)
1634 editor.entry->preferred_language = strdup(opt_preferred_language);
Jan Arne Petersen0558a932013-01-16 21:26:45 +01001635 editor.editor = text_entry_create(&editor, "Numeric");
Jonas Ådahl3bcba342015-11-17 16:00:29 +08001636 editor.editor->content_purpose = ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NUMBER;
Bryce Harrington3d90da22016-11-21 13:26:22 -08001637 editor.editor->click_to_show = opt_click_to_show;
Manuel Bachmann22f34302015-03-30 01:57:44 +02001638 editor.selection = NULL;
1639 editor.selected_text = NULL;
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001640
1641 window_set_title(editor.window, "Text Editor");
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001642 window_set_key_handler(editor.window, key_handler);
Kristian Høgsberg78858902014-01-01 23:57:42 -08001643 window_set_keyboard_focus_handler(editor.window,
1644 keyboard_focus_handler);
Rob Bradford9d1d32b2012-11-18 19:06:49 +01001645 window_set_user_data(editor.window, &editor);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001646
1647 widget_set_redraw_handler(editor.widget, redraw_handler);
1648 widget_set_resize_handler(editor.widget, resize_handler);
Jan Arne Petersenf80bc062012-09-09 23:08:34 +02001649 widget_set_button_handler(editor.widget, editor_button_handler);
Kristian Høgsberg966e3ed2014-01-07 10:41:50 -08001650 widget_set_touch_down_handler(editor.widget, editor_touch_handler);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001651
1652 window_schedule_resize(editor.window, 500, 400);
1653
1654 display_run(editor.display);
1655
Manuel Bachmann22f34302015-03-30 01:57:44 +02001656 if (editor.selected_text)
1657 free(editor.selected_text);
1658 if (editor.selection)
1659 wl_data_source_destroy(editor.selection);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001660 text_entry_destroy(editor.entry);
1661 text_entry_destroy(editor.editor);
vivek31732f72014-05-15 18:58:16 +05301662 widget_destroy(editor.widget);
1663 window_destroy(editor.window);
1664 display_destroy(editor.display);
Bryce Harrington411ffab2016-11-21 13:26:23 -08001665 free(text_buffer);
Jan Arne Petersencba9e472012-06-21 21:52:19 +02001666
1667 return 0;
1668}