[PATCH 1/5] text: Fix serials handling and use Unicode offsets

Jan Arne Petersen jpetersen at openismus.com
Mon Apr 15 04:37:15 PDT 2013


From: Jan Arne Petersen <jpetersen at openismus.com>

Simplify the serial handling and document it better.

Also use Unicode character offsets instead of byte offsets.

Signed-off-by: Jan Arne Petersen <jpetersen at openismus.com>
---
 clients/editor.c           | 241 ++++++++++++++++++++++++---------------------
 clients/keyboard.c         |  42 +++-----
 clients/weston-simple-im.c |  28 +++---
 protocol/input-method.xml  |  45 +++++----
 protocol/text.xml          |  77 +++++++++------
 src/input-panel.c          |   9 --
 src/text-backend.c         |  33 +++----
 tests/text-test.c          |   8 +-
 8 files changed, 237 insertions(+), 246 deletions(-)

diff --git a/clients/editor.c b/clients/editor.c
index e61eda0..84f24e9 100644
--- a/clients/editor.c
+++ b/clients/editor.c
@@ -56,6 +56,8 @@ struct text_entry {
 	struct {
 		int32_t cursor;
 		int32_t anchor;
+		uint32_t delete_index;
+		uint32_t delete_length;
 	} pending_commit;
 	struct text_input *text_input;
 	PangoLayout *layout;
@@ -63,6 +65,7 @@ struct text_entry {
 		xkb_mod_mask_t shift_mask;
 	} keysym;
 	uint32_t serial;
+	uint32_t reset_serial;
 	uint32_t content_purpose;
 	uint32_t click_to_show;
 	char *preferred_language;
@@ -79,24 +82,6 @@ struct editor {
 };
 
 static const char *
-utf8_start_char(const char *text, const char *p)
-{
-	for (; p >= text; --p) {
-		if ((*p & 0xc0) != 0x80)
-			return p;
-	}
-	return NULL;
-}
-
-static const char *
-utf8_prev_char(const char *text, const char *p)
-{
-	if (p > text)
-		return utf8_start_char(text, --p);
-	return NULL;
-}
-
-static const char *
 utf8_end_char(const char *p)
 {
 	while ((*p & 0xc0) == 0x80)
@@ -112,6 +97,39 @@ utf8_next_char(const char *p)
 	return NULL;
 }
 
+static uint32_t
+utf8_offset_to_byte_offset(const char *p,
+			   uint32_t offset)
+{
+	const char *q = p;
+	while (offset--) {
+		q = utf8_next_char(q);
+	}
+
+	return q - p;
+}
+
+static uint32_t
+utf8_pointer_to_offset(const char *s,
+		       const char *p)
+{
+	uint32_t offset;
+	for (offset = 0; s < p; offset++) {
+		s = utf8_next_char(s);
+	}
+	return offset;
+}
+
+static uint32_t
+utf8_characters(const char *p)
+{
+	uint32_t offset;
+	for (offset = 0; *p != 0; offset++) {
+		p = utf8_next_char(p);
+	}
+	return offset;
+}
+
 static void text_entry_redraw_handler(struct widget *widget, void *data);
 static void text_entry_button_handler(struct widget *widget,
 				      struct input *input, uint32_t time,
@@ -138,9 +156,22 @@ text_input_commit_string(void *data,
 {
 	struct text_entry *entry = data;
 
+	if ((entry->serial - serial) > (entry->serial - entry->reset_serial)) {
+		fprintf(stderr, "Ignore commit. Serial: %u, Current: %u, Reset: %u\n",
+			serial, entry->serial, entry->reset_serial);
+		return;
+	}
+
 	text_entry_reset_preedit(entry);
 
 	text_entry_delete_selected_text(entry);
+
+	if (entry->pending_commit.delete_length) {
+		text_entry_delete_text(entry,
+				       entry->pending_commit.delete_index,
+				       entry->pending_commit.delete_length);
+	}
+
 	text_entry_insert_at_cursor(entry, text,
 				    entry->pending_commit.cursor,
 				    entry->pending_commit.anchor);
@@ -175,39 +206,33 @@ text_input_preedit_string(void *data,
 static void
 text_input_delete_surrounding_text(void *data,
 				   struct text_input *text_input,
-				   uint32_t serial,
 				   int32_t index,
 				   uint32_t length)
 {
 	struct text_entry *entry = data;
-	uint32_t cursor_index = index + entry->cursor;
-	const char *start, *end;
+	uint32_t text_length;
+
+	entry->pending_commit.delete_index = entry->cursor + index;
+	entry->pending_commit.delete_length = length;
 
-	if (cursor_index > strlen(entry->text)) {
+	text_length = utf8_characters(entry->text);
+
+	if (entry->pending_commit.delete_index > text_length) {
 		fprintf(stderr, "Invalid cursor index %d\n", index);
+		entry->pending_commit.delete_length = 0;
 		return;
 	}
 
-	if (cursor_index + length > strlen(entry->text)) {
+	if (entry->pending_commit.delete_index + length > text_length) {
 		fprintf(stderr, "Invalid length %d\n", length);
+		entry->pending_commit.delete_length = 0;
 		return;
 	}
-
-	if (length == 0)
-		return;
-
-	start = utf8_start_char(entry->text, entry->text + cursor_index);
-	end = utf8_end_char(entry->text + cursor_index + length);
-
-	text_entry_delete_text(entry,
-			       start - entry->text,
-			       end - start);
 }
 
 static void
 text_input_cursor_position(void *data,
 			   struct text_input *text_input,
-			   uint32_t serial,
 			   int32_t index,
 			   int32_t anchor)
 {
@@ -220,7 +245,6 @@ text_input_cursor_position(void *data,
 static void
 text_input_preedit_styling(void *data,
 			   struct text_input *text_input,
-			   uint32_t serial,
 			   uint32_t index,
 			   uint32_t length,
 			   uint32_t style)
@@ -257,14 +281,14 @@ text_input_preedit_styling(void *data,
 	}
 
 	if (attr1) {
-		attr1->start_index = entry->cursor + index;
-		attr1->end_index = entry->cursor + index + length;
+		attr1->start_index = utf8_offset_to_byte_offset(entry->text, entry->cursor + index);
+		attr1->end_index = utf8_offset_to_byte_offset(entry->text, entry->cursor + index + length);
 		pango_attr_list_insert(entry->preedit_info.attr_list, attr1);
 	}
 
 	if (attr2) {
-		attr2->start_index = entry->cursor + index;
-		attr2->end_index = entry->cursor + index + length;
+		attr2->start_index = utf8_offset_to_byte_offset(entry->text, entry->cursor + index);
+		attr2->end_index = utf8_offset_to_byte_offset(entry->text, entry->cursor + index + length);
 		pango_attr_list_insert(entry->preedit_info.attr_list, attr2);
 	}
 }
@@ -272,7 +296,6 @@ text_input_preedit_styling(void *data,
 static void
 text_input_preedit_cursor(void *data,
 			  struct text_input *text_input,
-			  uint32_t serial,
 			  int32_t index)
 {
 	struct text_entry *entry = data;
@@ -302,7 +325,6 @@ text_input_keysym(void *data,
 	struct text_entry *entry = data;
 	const char *state_label = "release";
 	const char *key_label = "Unknown";
-	const char *new_char;
 
 	if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
 		state_label = "pressed";
@@ -314,37 +336,26 @@ text_input_keysym(void *data,
 			return;
 
 		if (key == XKB_KEY_Left)
-			new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
+			entry->cursor = entry->cursor > 0 ? entry->cursor - 1 : entry->cursor;
 		else
-			new_char = utf8_next_char(entry->text + entry->cursor);
+			entry->cursor = entry->cursor < utf8_characters(entry->text) ? entry->cursor + 1 : entry->cursor;
 
-		if (new_char != NULL) {
-			entry->cursor = new_char - entry->text;
-			if (!(modifiers & entry->keysym.shift_mask))
-				entry->anchor = entry->cursor;
-			widget_schedule_redraw(entry->widget);
-		}
+		if (!(modifiers & entry->keysym.shift_mask))
+			entry->anchor = entry->cursor;
+		widget_schedule_redraw(entry->widget);
 
 		return;
 	}
 
 	if (key == XKB_KEY_BackSpace) {
-		const char *start, *end;
-
 		if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
 			return;
 
 		text_entry_commit_and_reset(entry);
 
-		start = utf8_prev_char(entry->text, entry->text + entry->cursor);
-
-		if (start == NULL)
-			return;
-
-		end = utf8_end_char(entry->text + entry->cursor);
 		text_entry_delete_text(entry,
-				       start - entry->text,
-				       end - start);
+				       entry->cursor - 1,
+				       1);
 
 		return;
 	}
@@ -374,6 +385,9 @@ text_input_enter(void *data,
 
 	entry->active = 1;
 
+	text_entry_update(entry);
+	entry->reset_serial = entry->serial;
+
 	widget_schedule_redraw(entry->widget);
 }
 
@@ -461,7 +475,7 @@ text_entry_create(struct editor *editor, const char *text)
 	entry->window = editor->window;
 	entry->text = strdup(text);
 	entry->active = 0;
-	entry->cursor = strlen(text);
+	entry->cursor = utf8_characters(text);
 	entry->anchor = entry->cursor;
 	entry->text_input = text_input_manager_create_text_input(editor->text_input_manager);
 	text_input_add_listener(entry->text_input, &text_input_listener, entry);
@@ -552,10 +566,7 @@ text_entry_activate(struct text_entry *entry,
 	if (!entry->click_to_show)
 		text_input_show_input_panel(entry->text_input);
 
-	entry->serial++;
-
 	text_input_activate(entry->text_input,
-			    entry->serial,
 			    seat,
 			    surface);
 }
@@ -574,15 +585,16 @@ text_entry_update_layout(struct text_entry *entry)
 	char *text;
 	PangoAttrList *attr_list;
 
-	assert(((unsigned int)entry->cursor) <= strlen(entry->text) +
-	       (entry->preedit.text ? strlen(entry->preedit.text) : 0));
+	assert(entry->cursor <= (utf8_characters(entry->text) +
+	       (entry->preedit.text ? utf8_characters(entry->preedit.text) : 0)));
 
 	if (entry->preedit.text) {
+		uint32_t old_cursor = utf8_offset_to_byte_offset(entry->text, entry->cursor);
 		text = malloc(strlen(entry->text) + strlen(entry->preedit.text) + 1);
-		strncpy(text, entry->text, entry->cursor);
-		strcpy(text + entry->cursor, entry->preedit.text);
-		strcpy(text + entry->cursor + strlen(entry->preedit.text),
-		       entry->text + entry->cursor);
+		strncpy(text, entry->text, old_cursor);
+		strcpy(text + old_cursor, entry->preedit.text);
+		strcpy(text + old_cursor + strlen(entry->preedit.text),
+		       entry->text + old_cursor);
 	} else {
 		text = strdup(entry->text);
 	}
@@ -592,6 +604,9 @@ text_entry_update_layout(struct text_entry *entry)
 		int end_index = MAX(entry->cursor, entry->anchor);
 		PangoAttribute *attr;
 
+		start_index = utf8_offset_to_byte_offset(entry->text, start_index);
+		end_index = utf8_offset_to_byte_offset(entry->text, end_index);
+
 		attr_list = pango_attr_list_copy(entry->preedit.attr_list);
 
 		if (!attr_list)
@@ -617,7 +632,7 @@ text_entry_update_layout(struct text_entry *entry)
 			attr_list = pango_attr_list_new();
 
 		attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
-		attr->start_index = entry->cursor;
+		attr->start_index = utf8_offset_to_byte_offset(entry->text, entry->cursor);
 		attr->end_index = entry->cursor + strlen(entry->preedit.text);
 		pango_attr_list_insert(attr_list, attr);
 	}
@@ -653,7 +668,7 @@ text_entry_update(struct text_entry *entry)
 	text_input_set_cursor_rectangle(entry->text_input, cursor_rectangle.x, cursor_rectangle.y,
 					cursor_rectangle.width, cursor_rectangle.height);
 
-	text_input_commit_state(entry->text_input);
+	text_input_commit_state(entry->text_input, ++entry->serial);
 }
 
 static void
@@ -662,19 +677,21 @@ text_entry_insert_at_cursor(struct text_entry *entry, const char *text,
 {
 	char *new_text = malloc(strlen(entry->text) + strlen(text) + 1);
 
-	strncpy(new_text, entry->text, entry->cursor);
-	strcpy(new_text + entry->cursor, text);
-	strcpy(new_text + entry->cursor + strlen(text),
-	       entry->text + entry->cursor);
+	uint32_t old_cursor = utf8_offset_to_byte_offset(entry->text, entry->cursor);
+
+	strncpy(new_text, entry->text, old_cursor);
+	strcpy(new_text + old_cursor, text);
+	strcpy(new_text + old_cursor + strlen(text),
+	       entry->text + old_cursor);
 
 	free(entry->text);
 	entry->text = new_text;
 	if (anchor >= 0)
-		entry->anchor = entry->cursor + strlen(text) + anchor;
+		entry->anchor = entry->cursor + utf8_characters(text) + anchor;
 	else
 		entry->anchor = entry->cursor + 1 + anchor;
 	if (cursor >= 0)
-		entry->cursor += strlen(text) + cursor;
+		entry->cursor += utf8_characters(text) + cursor;
 	else
 		entry->cursor += 1 + cursor;
 
@@ -714,8 +731,9 @@ text_entry_commit_and_reset(struct text_entry *entry)
 		free(commit);
 	}
 
-	entry->serial++;
-	text_input_reset(entry->text_input, entry->serial);
+	text_input_reset(entry->text_input);
+	text_entry_update(entry);
+	entry->reset_serial = entry->serial;
 }
 
 static void
@@ -754,7 +772,7 @@ text_entry_try_invoke_preedit_action(struct text_entry *entry,
 	cursor = index + trailing;
 
 	if (cursor < entry->cursor ||
-	    cursor > entry->cursor + strlen(entry->preedit.text)) {
+	    cursor > entry->cursor + utf8_characters(entry->preedit.text)) {
 		return 0;
 	}
 
@@ -777,7 +795,7 @@ text_entry_set_cursor_position(struct text_entry *entry,
 	pango_layout_xy_to_index(entry->layout,
 				 x * PANGO_SCALE, y * PANGO_SCALE,
 				 &index, &trailing);
-	entry->cursor = index + trailing;
+	entry->cursor = utf8_pointer_to_offset(entry->text, entry->text + index + trailing);
 
 	text_entry_update_layout(entry);
 
@@ -795,7 +813,7 @@ text_entry_set_anchor_position(struct text_entry *entry,
 	pango_layout_xy_to_index(entry->layout,
 				 x * PANGO_SCALE, y * PANGO_SCALE,
 				 &index, &trailing);
-	entry->anchor = index + trailing;
+	entry->anchor = utf8_pointer_to_offset(entry->text, entry->text + index + trailing);
 
 	text_entry_update_layout(entry);
 
@@ -808,13 +826,18 @@ static void
 text_entry_delete_text(struct text_entry *entry,
 		       uint32_t index, uint32_t length)
 {
+	uint32_t s, e;
+
 	if (entry->cursor > index)
 		entry->cursor -= length;
 
 	entry->anchor = entry->cursor;
 
-	entry->text[index] = '\0';
-	strcat(entry->text, entry->text + index + length);
+	s = utf8_offset_to_byte_offset(entry->text, index);
+	e = utf8_offset_to_byte_offset(entry->text, index + length);
+
+	entry->text[s] = '\0';
+	strcat(entry->text, entry->text + e);
 
 	text_entry_update_layout(entry);
 
@@ -843,6 +866,7 @@ text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rect
 	struct rectangle allocation;
 	PangoRectangle extents;
 	PangoRectangle cursor_pos;
+	const char *text;
 
 	widget_get_allocation(entry->widget, &allocation);
 
@@ -854,9 +878,11 @@ text_entry_get_cursor_rectangle(struct text_entry *entry, struct rectangle *rect
 		return;
 	}
 
+
+	text = pango_layout_get_text(entry->layout);
 	pango_layout_get_extents(entry->layout, &extents, NULL);
 	pango_layout_get_cursor_pos(entry->layout,
-				    entry->cursor + entry->preedit.cursor,
+				    utf8_offset_to_byte_offset(text, entry->cursor + entry->preedit.cursor),
 				    &cursor_pos, NULL);
 
 	rectangle->x = allocation.x + (allocation.height / 2) + PANGO_PIXELS(cursor_pos.x);
@@ -870,13 +896,15 @@ text_entry_draw_cursor(struct text_entry *entry, cairo_t *cr)
 {
 	PangoRectangle extents;
 	PangoRectangle cursor_pos;
+	const char *text;
 
 	if (entry->preedit.text && entry->preedit.cursor < 0)
 		return;
 
+	text = pango_layout_get_text(entry->layout);
 	pango_layout_get_extents(entry->layout, &extents, NULL);
 	pango_layout_get_cursor_pos(entry->layout,
-				    entry->cursor + entry->preedit.cursor,
+				    utf8_offset_to_byte_offset(text, entry->cursor + entry->preedit.cursor),
 				    &cursor_pos, NULL);
 
 	cairo_set_line_width(cr, 1.0);
@@ -1033,7 +1061,6 @@ key_handler(struct window *window,
 {
 	struct editor *editor = data;
 	struct text_entry *entry;
-	const char *start, *end, *new_char;
 	char text[16];
 
 	if (!editor->active_entry)
@@ -1048,39 +1075,24 @@ key_handler(struct window *window,
 		case XKB_KEY_BackSpace:
 			text_entry_commit_and_reset(entry);
 
-			start = utf8_prev_char(entry->text, entry->text + entry->cursor);
-
-			if (start == NULL)
-				break;
-
-			end = utf8_end_char(entry->text + entry->cursor);
-			text_entry_delete_text(entry,
-					       start - entry->text,
-					       end - start);
+			if (entry->cursor > 0)
+				text_entry_delete_text(entry,
+						       entry->cursor - 1,
+						       1);
 			break;
 		case XKB_KEY_Delete:
 			text_entry_commit_and_reset(entry);
 
-			start = utf8_start_char(entry->text, entry->text + entry->cursor);
-
-			if (start == NULL)
-				break;
-
-			end = utf8_next_char(start);
-
-			if (end == NULL)
-				break;
-
-			text_entry_delete_text(entry,
-					       start - entry->text,
-					       end - start);
+			if (entry->cursor < utf8_characters(entry->text))
+				text_entry_delete_text(entry,
+						       entry->cursor,
+						       1);
 			break;
 		case XKB_KEY_Left:
 			text_entry_commit_and_reset(entry);
 
-			new_char = utf8_prev_char(entry->text, entry->text + entry->cursor);
-			if (new_char != NULL) {
-				entry->cursor = new_char - entry->text;
+			if (entry->cursor > 0) {
+				entry->cursor--;
 				entry->anchor = entry->cursor;
 				widget_schedule_redraw(entry->widget);
 			}
@@ -1088,9 +1100,8 @@ key_handler(struct window *window,
 		case XKB_KEY_Right:
 			text_entry_commit_and_reset(entry);
 
-			new_char = utf8_next_char(entry->text + entry->cursor);
-			if (new_char != NULL) {
-				entry->cursor = new_char - entry->text;
+			if (entry->cursor < utf8_characters(entry->text)) {
+				entry->cursor++;
 				entry->anchor = entry->cursor;
 				widget_schedule_redraw(entry->widget);
 			}
diff --git a/clients/keyboard.c b/clients/keyboard.c
index 17fb683..4fc8e73 100644
--- a/clients/keyboard.c
+++ b/clients/keyboard.c
@@ -387,15 +387,7 @@ virtual_keyboard_commit_preedit(struct virtual_keyboard *keyboard)
 	    strlen(keyboard->preedit_string) == 0)
 		return;
 
-	input_method_context_preedit_cursor(keyboard->context,
-					    keyboard->serial,
-					    0);
-	input_method_context_preedit_string(keyboard->context,
-					    keyboard->serial,
-					    "",
-					    "");
 	input_method_context_cursor_position(keyboard->context,
-					     keyboard->serial,
 					     0, 0);
 	input_method_context_commit_string(keyboard->context,
 					   keyboard->serial,
@@ -412,14 +404,12 @@ virtual_keyboard_send_preedit(struct virtual_keyboard *keyboard,
 
 	if (keyboard->preedit_style)
 		input_method_context_preedit_styling(keyboard->context,
-						     keyboard->serial,
 						     0,
 						     strlen(keyboard->preedit_string),
 						     keyboard->preedit_style);
 	if (cursor > 0)
 		index = cursor;
 	input_method_context_preedit_cursor(keyboard->context,
-					    keyboard->serial,
 					    index);
 	input_method_context_preedit_string(keyboard->context,
 					    keyboard->serial,
@@ -449,8 +439,10 @@ keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key *
 
 			if (strlen(keyboard->keyboard->preedit_string) == 0) {
 				input_method_context_delete_surrounding_text(keyboard->keyboard->context,
-									     keyboard->keyboard->serial,
 									     -1, 1);
+				input_method_context_commit_string(keyboard->keyboard->context,
+								   keyboard->keyboard->serial,
+								   "");
 			} else {
 				keyboard->keyboard->preedit_string[strlen(keyboard->keyboard->preedit_string) - 1] = '\0';
 				virtual_keyboard_send_preedit(keyboard->keyboard, -1);
@@ -579,26 +571,16 @@ handle_surrounding_text(void *data,
 
 static void
 handle_reset(void *data,
-	     struct input_method_context *context,
-	     uint32_t serial)
+	     struct input_method_context *context)
 {
 	struct virtual_keyboard *keyboard = data;
 
 	fprintf(stderr, "Reset pre-edit buffer\n");
 
 	if (strlen(keyboard->preedit_string)) {
-		input_method_context_preedit_cursor(context,
-						    serial,
-						    0);
-		input_method_context_preedit_string(context,
-						    serial,
-						    "",
-						    "");
 		free(keyboard->preedit_string);
 		keyboard->preedit_string = strdup("");
 	}
-
-	keyboard->serial = serial;
 }
 
 static void
@@ -628,12 +610,15 @@ handle_invoke_action(void *data,
 }
 
 static void
-handle_commit(void *data,
-	      struct input_method_context *context)
+handle_commit_state(void *data,
+		    struct input_method_context *context,
+		    uint32_t serial)
 {
 	struct virtual_keyboard *keyboard = data;
 	const struct layout *layout;
 
+	keyboard->serial = serial;
+
 	layout = get_current_layout(keyboard);
 
 	if (keyboard->surrounding_text)
@@ -670,15 +655,14 @@ static const struct input_method_context_listener input_method_context_listener
 	handle_reset,
 	handle_content_type,
 	handle_invoke_action,
-	handle_commit,
+	handle_commit_state,
 	handle_preferred_language
 };
 
 static void
 input_method_activate(void *data,
 		      struct input_method *input_method,
-		      struct input_method_context *context,
-		      uint32_t serial)
+		      struct input_method_context *context)
 {
 	struct virtual_keyboard *keyboard = data;
 	struct wl_array modifiers_map;
@@ -700,7 +684,7 @@ input_method_activate(void *data,
 	free(keyboard->surrounding_text);
 	keyboard->surrounding_text = NULL;
 
-	keyboard->serial = serial;
+	keyboard->serial = 0;
 
 	keyboard->context = context;
 	input_method_context_add_listener(context,
@@ -799,8 +783,6 @@ keyboard_create(struct output *output, struct virtual_keyboard *virtual_keyboard
 	input_panel_surface_set_toplevel(ips,
 					 output_get_wl_output(output),
 					 INPUT_PANEL_SURFACE_POSITION_CENTER_BOTTOM);
-
-	fprintf(stderr, "%s, %p\n", __FUNCTION__, output_get_wl_output(output));
 }
 
 int
diff --git a/clients/weston-simple-im.c b/clients/weston-simple-im.c
index 9d31e3b..a532003 100644
--- a/clients/weston-simple-im.c
+++ b/clients/weston-simple-im.c
@@ -112,16 +112,13 @@ handle_surrounding_text(void *data,
 
 static void
 handle_reset(void *data,
-	     struct input_method_context *context,
-	     uint32_t serial)
+	     struct input_method_context *context)
 {
 	struct simple_im *keyboard = data;
 
 	fprintf(stderr, "Reset pre-edit buffer\n");
 
 	keyboard->compose_state = state_normal;
-
-	keyboard->serial = serial;
 }
 
 static void
@@ -141,9 +138,13 @@ handle_invoke_action(void *data,
 }
 
 static void
-handle_commit(void *data,
-	      struct input_method_context *context)
+handle_commit_state(void *data,
+		    struct input_method_context *context,
+		    uint32_t serial)
 {
+	struct simple_im *keyboard = data;
+
+	keyboard->serial = serial;
 }
 
 static void
@@ -158,7 +159,7 @@ static const struct input_method_context_listener input_method_context_listener
 	handle_reset,
 	handle_content_type,
 	handle_invoke_action,
-	handle_commit,
+	handle_commit_state,
 	handle_preferred_language
 };
 
@@ -285,8 +286,7 @@ static const struct wl_keyboard_listener input_method_keyboard_listener = {
 static void
 input_method_activate(void *data,
 		      struct input_method *input_method,
-		      struct input_method_context *context,
-		      uint32_t serial)
+		      struct input_method_context *context)
 {
 	struct simple_im *keyboard = data;
 
@@ -295,7 +295,7 @@ input_method_activate(void *data,
 
 	keyboard->compose_state = state_normal;
 
-	keyboard->serial = serial;
+	keyboard->serial = 0;
 
 	keyboard->context = context;
 	input_method_context_add_listener(context,
@@ -396,7 +396,7 @@ simple_im_key_handler(struct simple_im *keyboard,
 
 		for (i = 0; i < sizeof(ignore_keys_on_compose) / sizeof(ignore_keys_on_compose[0]); i++) {
 			if (sym == ignore_keys_on_compose[i]) {
-				input_method_context_key(context, serial, time, key, state);
+				input_method_context_key(context, keyboard->serial, time, key, state);
 				return;
 			}
 		}
@@ -412,13 +412,11 @@ simple_im_key_handler(struct simple_im *keyboard,
 		if (cs) {
 			if (cs->keys[i + 1] == 0) {
 				input_method_context_preedit_cursor(keyboard->context,
-								    keyboard->serial,
 								    0);
 				input_method_context_preedit_string(keyboard->context,
 								    keyboard->serial,
 								    "", "");
 				input_method_context_cursor_position(keyboard->context,
-								     keyboard->serial,
 								     0, 0);
 				input_method_context_commit_string(keyboard->context,
 								   keyboard->serial,
@@ -432,7 +430,6 @@ simple_im_key_handler(struct simple_im *keyboard,
 				}
 
 				input_method_context_preedit_cursor(keyboard->context,
-								    keyboard->serial,
 								    strlen(text));
 				input_method_context_preedit_string(keyboard->context,
 								    keyboard->serial,
@@ -446,13 +443,11 @@ simple_im_key_handler(struct simple_im *keyboard,
 				idx += xkb_keysym_to_utf8(keyboard->compose_seq.keys[j], text + idx, sizeof(text) - idx);
 			}
 			input_method_context_preedit_cursor(keyboard->context,
-							    keyboard->serial,
 							    0);
 			input_method_context_preedit_string(keyboard->context,
 							    keyboard->serial,
 							    "", "");
 			input_method_context_cursor_position(keyboard->context,
-							     keyboard->serial,
 							     0, 0);
 			input_method_context_commit_string(keyboard->context,
 							   keyboard->serial,
@@ -471,7 +466,6 @@ simple_im_key_handler(struct simple_im *keyboard,
 		return;
 
 	input_method_context_cursor_position(keyboard->context,
-					     keyboard->serial,
 					     0, 0);
 	input_method_context_commit_string(keyboard->context,
 					   keyboard->serial,
diff --git a/protocol/input-method.xml b/protocol/input-method.xml
index d9ae4a9..d483087 100644
--- a/protocol/input-method.xml
+++ b/protocol/input-method.xml
@@ -33,13 +33,23 @@
       receive information about the text model from the application via events.
       Input method contexts do not keep state after deactivation and should be
       destroyed after deactivation is handled.
+
+      Text is generally UTF-8 encoded, indices and lengths are in Unicode
+      characters.
+
+      Serials are used to synchronize the state between the text input and
+      an input method. New serials are sent by the text input in the
+      commit_state request and are used by the input method to indicate
+      the known text input state in events like preedit_string, commit_string,
+      and keysym. The text input can then ignore events from the input method
+      which are based on an outdated state (for example after a reset).
     </description>
     <request name="destroy" type="destructor"/>
     <request name="commit_string">
       <description summary="commit string">
         Send the commit string text to the applications text model and
-        set the cursor at index (as byte index) relative to the
-        beginning of inserted text.
+	set the cursor at index (as Unicode character offset) relative to 
+	the beginning of inserted text.
       </description>
       <arg name="serial" type="uint"/>
       <arg name="text" type="string"/>
@@ -57,34 +67,32 @@
     <request name="preedit_styling">
       <description summary="pre-edit styling">
         Sets styling information on composing text. The style is applied for
-        length (in bytes) characters from index relative to the beginning of the
-        composing text (as byte index). Multiple styles can be applied to a
-        composing text.
+	length in Unicode characters from index relative to the beginning of
+	the composing text (as Unicode characters offset). Multiple styles can
+	be applied to a composing text.
 
         This request should be sent before sending preedit_string request.
       </description>
-      <arg name="serial" type="uint"/>
       <arg name="index" type="uint"/>
       <arg name="length" type="uint"/>
       <arg name="style" type="uint"/>
     </request>
     <request name="preedit_cursor">
       <description summary="pre-edit cursor">
-        Sets the cursor position inside the composing text (as byte index)
-        relative to the start of the composing text.
+        Sets the cursor position inside the composing text (as Unicode
+        characters offset) relative to the start of the composing text.
+
+        When index is negative no cursor should be displayed.
 
         This request should be sent before sending preedit_string request.
       </description>
-      <arg name="serial" type="uint"/>
       <arg name="index" type="int"/>
     </request>
     <request name="delete_surrounding_text">
-      <arg name="serial" type="uint"/>
       <arg name="index" type="int"/>
       <arg name="length" type="uint"/>
     </request>
     <request name="cursor_position">
-      <arg name="serial" type="uint"/>
       <arg name="index" type="int"/>
       <arg name="anchor" type="int"/>
     </request>
@@ -137,17 +145,17 @@
     <event name="surrounding_text">
       <description summary="surrounding text event">
         The plain surrounding text around the input position. Cursor is the
-        position in bytes within the surrounding text relative to the beginning
-        of the text. Anchor is the position in bytes of the selection anchor
-        within the surrounding text relative to the beginning of the text. If
-        there is no selected text anchor is the same as cursor.
+        position in Unicode characters within the surrounding text relative to
+        the beginning of the text. Anchor is the position in Unicode characters
+        of the selection anchor within the surrounding text relative to the
+        beginning of the text. If there is no selected text anchor is the same
+        as cursor.
       </description>
       <arg name="text" type="string"/>
       <arg name="cursor" type="uint"/>
       <arg name="anchor" type="uint"/>
     </event>
     <event name="reset">
-      <arg name="serial" type="uint"/>
     </event>
     <event name="content_type">
       <arg name="hint" type="uint"/>
@@ -157,7 +165,9 @@
       <arg name="button" type="uint"/>
       <arg name="index" type="uint"/>
     </event>
-    <event name="commit"/>
+    <event name="commit_state">
+      <arg name="serial" type="uint"/>
+    </event>
     <event name="preferred_language">
       <arg name="language" type="string"/>
     </event>
@@ -176,7 +186,6 @@
         which allows communication with the text model.
       </description>
       <arg name="id" type="new_id" interface="input_method_context"/>
-      <arg name="serial" type="uint"/>
     </event>
     <event name="deactivate">
       <description summary="activate event">
diff --git a/protocol/text.xml b/protocol/text.xml
index 02c5041..ba372bf 100644
--- a/protocol/text.xml
+++ b/protocol/text.xml
@@ -2,7 +2,7 @@
 <protocol name="text">
 
   <copyright>
-    Copyright © 2012 Intel Corporation
+    Copyright © 2012, 2013 Intel Corporation
 
     Permission to use, copy, modify, distribute, and sell this
     software and its documentation for any purpose is hereby granted
@@ -38,6 +38,16 @@
       the pre-edit and commit events. Using this interface removes the need
       for applications to directly process hardware key events and compose text
       out of them.
+
+      Text is generally UTF-8 encoded, indices and lengths are in Unicode
+      characters.
+
+      Serials are used to synchronize the state between the text input and
+      an input method. New serials are sent by the text input in the
+      commit_state request and are used by the input method to indicate
+      the known text input state in events like preedit_string, commit_string,
+      and keysym. The text input can then ignore events from the input method
+      which are based on an outdated state (for example after a reset).
     </description>
     <request name="activate">
       <description summary="request activation">
@@ -48,7 +58,6 @@
         text-input object and tracked for focus lost. The enter event
         is emitted on successful activation.
       </description>
-      <arg name="serial" type="uint"/>
       <arg name="seat" type="object" interface="wl_seat"/>
       <arg name="surface" type="object" interface="wl_surface"/>
     </request>
@@ -76,14 +85,14 @@
         reset, for example after the text was changed outside of the normal
         input method flow.
       </description>
-      <arg name="serial" type="uint"/>
     </request>
     <request name="set_surrounding_text">
       <description summary="sets the surrounding text">
-        Sets the plain surrounding text around the input position. Cursor is the
-        byte index within the surrounding text. Anchor is the byte index of the
+        Sets the plain surrounding text around the input position. Text is
+        UTF-8 encoded. Cursor is the Unicode character offset within the
+        surrounding text. Anchor is the Unicode character offset of the
         selection anchor within the surrounding text. If there is no selected
-        text anchor then it is the same as cursor.
+        text anchor is the same as cursor.
       </description>
       <arg name="text" type="string"/>
       <arg name="cursor" type="uint"/>
@@ -162,6 +171,7 @@
       <arg name="language" type="string"/>
     </request>
     <request name="commit_state">
+      <arg name="serial" type="uint" summary="used to identify the known state"/>
     </request>
     <request name="invoke_action">
       <arg name="button" type="uint"/>
@@ -202,9 +212,12 @@
         be removed.
 
         The commit text can be used to replace the preedit text on reset
-        (for example on unfocus).
+	(for example on unfocus).
+
+	The text input should also handle all preedit_style and preedit_cursor
+	events occuring directly before preedit_string.
       </description>
-      <arg name="serial" type="uint"/>
+      <arg name="serial" type="uint" summary="serial of the latest known text input state"/>
       <arg name="text" type="string"/>
       <arg name="commit" type="string"/>
     </event>
@@ -221,57 +234,59 @@
     <event name="preedit_styling">
       <description summary="pre-edit styling">
         Sets styling information on composing text. The style is applied for
-        length (in bytes) characters from index relative to the beginning of
-        the composing text (as byte index). Multiple styles can be applied
-        to a composing text.
+        length Unicode characters from index relative to the beginning of
+        the composing text (as Unicode character offset). Multiple styles can
+        be applied to a composing text by sending multiple preedit_styling
+        events.
 
-        This event should be handled as part of a following preedit_string
-        event.
+        This event is handled as part of a following preedit_string event.
       </description>
-      <arg name="serial" type="uint"/>
       <arg name="index" type="uint"/>
       <arg name="length" type="uint"/>
       <arg name="style" type="uint"/>
     </event>
     <event name="preedit_cursor">
       <description summary="pre-edit cursor">
-        Sets the cursor position inside the composing text (as byte index)
-        relative to the start of the composing text.
+        Sets the cursor position inside the composing text (as Unicode character
+        offset) relative to the start of the composing text. When index is a
+        negative number no cursor is shown.
 
-        This event should be handled as part of a following preedit_string
-        event.
+        This event is handled as part of a following preedit_string event.
       </description>
-      <arg name="serial" type="uint"/>
       <arg name="index" type="int"/>
     </event>
     <event name="commit_string">
       <description summary="commit">
         Notify when text should be inserted into the editor widget. The text to
         commit could be either just a single character after a key press or the
-        result of some composing (pre-edit). It also sets the new cursor
-        position (as byte index) relative to the beginning of inserted text.
+        result of some composing (pre-edit).
 
         Any previously set composing text should be removed.
       </description>
-      <arg name="serial" type="uint"/>
+      <arg name="serial" type="uint" summary="serial of the latest known text input state"/>
       <arg name="text" type="string"/>
     </event>
     <event name="cursor_position">
       <description summary="set cursor to new position">
-        Notify when the cursor or anchor position should be modified. It
-        should take effect after the next commit_string event.
+        Notify when the cursor or anchor position should be modified.
+
+        This event should be handled as part of a following commit_string
+        event.
       </description>
-      <arg name="serial" type="uint"/>
       <arg name="index" type="int"/>
       <arg name="anchor" type="int"/>
     </event>
     <event name="delete_surrounding_text">
       <description summary="delete surrounding text">
         Notify when the text around the current cursor position should be
-        deleted. Index is relative to the current cursor (as byte index).
-        Length is the length of deleted text (in bytes).
+        deleted.
+
+        Index is relative to the current cursor (in Unicode characters).
+        Length is the length of deleted text (in Unicode characters).
+
+        This event should be handled as part of a following commit_string
+        event.
       </description>
-      <arg name="serial" type="uint"/>
       <arg name="index" type="int"/>
       <arg name="length" type="uint"/>
     </event>
@@ -284,7 +299,7 @@
         wl_keyboard key_state. Modifiers are a mask for effective modifiers
         (where the modifier indices are set by the modifiers_map event)
       </description>
-      <arg name="serial" type="uint"/>
+      <arg name="serial" type="uint" summary="serial of the latest known text input state"/>
       <arg name="time" type="uint"/>
       <arg name="sym" type="uint"/>
       <arg name="state" type="uint"/>
@@ -295,7 +310,7 @@
         Sets the language of the input text. The "language" argument is a RFC-3066 
         format language tag.
       </description>
-      <arg name="serial" type="uint"/>
+      <arg name="serial" type="uint" summary="serial of the latest known text input state"/>
       <arg name="language" type="string"/>
     </event>
     <enum name="text_direction">
@@ -311,7 +326,7 @@
         editor when there is no input yet done and making sure neutral
         direction text is laid out properly.
       </description>
-      <arg name="serial" type="uint"/>
+      <arg name="serial" type="uint" summary="serial of the latest known text input state"/>
       <arg name="direction" type="uint"/>
     </event>
   </interface>
diff --git a/src/input-panel.c b/src/input-panel.c
index 78dfe21..81a82f8 100644
--- a/src/input-panel.c
+++ b/src/input-panel.c
@@ -97,12 +97,6 @@ show_input_panels(struct wl_listener *listener, void *data)
 
 	input_panel->text_input.surface = (struct weston_surface*)data;
 
-	fprintf(stderr,
-		"%s layer visible: %d input panel visible: %d\n",
-		__FUNCTION__,
-		input_panel->layer.visible,
-		input_panel->showing_input_panels);
-
 	if (input_panel->showing_input_panels)
 		return;
 
@@ -150,7 +144,6 @@ overlay_panel_get_position(struct input_panel_surface *surface,
 	struct input_panel *input_panel = surface->input_panel;
 
 	if (!surface->overlay_panel) {
-		fprintf(stderr, "%s: surface is not overlay panel\n", __FUNCTION__);
 		return;
 	}
 
@@ -434,7 +427,6 @@ input_panel_show_layer(struct input_panel *input_panel,
 		       struct weston_layer *next)
 {
 	if (input_panel->layer.visible) {
-		fprintf(stderr, "%s - input panel layer is already shown.\n", __FUNCTION__);
 		return;
 	}
 
@@ -458,7 +450,6 @@ WL_EXPORT void
 input_panel_hide_layer(struct input_panel *input_panel)
 {
 	if (!input_panel->layer.visible) {
-		fprintf(stderr, "%s - input panel layer is already hidden.\n", __FUNCTION__);
 		return;
 	}
 
diff --git a/src/text-backend.c b/src/text-backend.c
index a14e048..e965a0d 100644
--- a/src/text-backend.c
+++ b/src/text-backend.c
@@ -104,8 +104,7 @@ struct text_backend {
 };
 
 static void input_method_context_create(struct text_input *model,
-					struct input_method *input_method,
-					uint32_t serial);
+					struct input_method *input_method);
 static void input_method_context_end_keyboard_grab(struct input_method_context *context);
 
 static void input_method_init_seat(struct weston_seat *seat);
@@ -168,7 +167,6 @@ text_input_set_surrounding_text(struct wl_client *client,
 static void
 text_input_activate(struct wl_client *client,
 	            struct wl_resource *resource,
-		    uint32_t serial,
 		    struct wl_resource *seat,
 		    struct wl_resource *surface)
 {
@@ -192,7 +190,7 @@ text_input_activate(struct wl_client *client,
 
 	text_input->surface = surface->data;
 
-	input_method_context_create(text_input, input_method, serial);
+	input_method_context_create(text_input, input_method);
 
 	if (text_input->input_panel_visible) {
 		wl_signal_emit(&ec->show_input_panel_signal, text_input->surface);
@@ -216,8 +214,7 @@ text_input_deactivate(struct wl_client *client,
 
 static void
 text_input_reset(struct wl_client *client,
-		 struct wl_resource *resource,
-		 uint32_t serial)
+		 struct wl_resource *resource)
 {
 	struct text_input *text_input = resource->data;
 	struct input_method *input_method, *next;
@@ -225,7 +222,7 @@ text_input_reset(struct wl_client *client,
 	wl_list_for_each_safe(input_method, next, &text_input->input_methods, link) {
 		if (!input_method->context)
 			continue;
-		input_method_context_send_reset(&input_method->context->resource, serial);
+		input_method_context_send_reset(&input_method->context->resource);
 	}
 }
 
@@ -282,7 +279,8 @@ text_input_invoke_action(struct wl_client *client,
 
 static void
 text_input_commit_state(struct wl_client *client,
-			struct wl_resource *resource)
+			struct wl_resource *resource,
+			uint32_t serial)
 {
 	struct text_input *text_input = resource->data;
 	struct input_method *input_method, *next;
@@ -290,7 +288,7 @@ text_input_commit_state(struct wl_client *client,
 	wl_list_for_each_safe(input_method, next, &text_input->input_methods, link) {
 		if (!input_method->context)
 			continue;
-		input_method_context_send_commit(&input_method->context->resource);
+		input_method_context_send_commit_state(&input_method->context->resource, serial);
 	}
 }
 
@@ -458,49 +456,45 @@ input_method_context_preedit_string(struct wl_client *client,
 static void
 input_method_context_preedit_styling(struct wl_client *client,
 				     struct wl_resource *resource,
-				     uint32_t serial,
 				     uint32_t index,
 				     uint32_t length,
 				     uint32_t style)
 {
 	struct input_method_context *context = resource->data;
 
-	text_input_send_preedit_styling(&context->model->resource, serial, index, length, style);
+	text_input_send_preedit_styling(&context->model->resource, index, length, style);
 }
 
 static void
 input_method_context_preedit_cursor(struct wl_client *client,
 				    struct wl_resource *resource,
-				    uint32_t serial,
 				    int32_t cursor)
 {
 	struct input_method_context *context = resource->data;
 
-	text_input_send_preedit_cursor(&context->model->resource, serial, cursor);
+	text_input_send_preedit_cursor(&context->model->resource, cursor);
 }
 
 static void
 input_method_context_delete_surrounding_text(struct wl_client *client,
 					     struct wl_resource *resource,
-					     uint32_t serial,
 					     int32_t index,
 					     uint32_t length)
 {
 	struct input_method_context *context = resource->data;
 
-	text_input_send_delete_surrounding_text(&context->model->resource, serial, index, length);
+	text_input_send_delete_surrounding_text(&context->model->resource, index, length);
 }
 
 static void
 input_method_context_cursor_position(struct wl_client *client,
 				     struct wl_resource *resource,
-				     uint32_t serial,
 				     int32_t index,
 				     int32_t anchor)
 {
 	struct input_method_context *context = resource->data;
 
-	text_input_send_cursor_position(&context->model->resource, serial, index, anchor);
+	text_input_send_cursor_position(&context->model->resource, index, anchor);
 }
 
 static void
@@ -694,8 +688,7 @@ destroy_input_method_context(struct wl_resource *resource)
 
 static void
 input_method_context_create(struct text_input *model,
-			    struct input_method *input_method,
-			    uint32_t serial)
+			    struct input_method *input_method)
 {
 	struct input_method_context *context;
 
@@ -720,7 +713,7 @@ input_method_context_create(struct text_input *model,
 
 	wl_client_add_resource(input_method->input_method_binding->client, &context->resource);
 
-	input_method_send_activate(input_method->input_method_binding, &context->resource, serial);
+	input_method_send_activate(input_method->input_method_binding, &context->resource);
 }
 
 static void
diff --git a/tests/text-test.c b/tests/text-test.c
index 1feae66..d8ffc38 100644
--- a/tests/text-test.c
+++ b/tests/text-test.c
@@ -51,7 +51,6 @@ text_input_preedit_string(void *data,
 static void
 text_input_delete_surrounding_text(void *data,
 				   struct text_input *text_input,
-				   uint32_t serial,
 				   int32_t index,
 				   uint32_t length)
 {
@@ -60,7 +59,6 @@ text_input_delete_surrounding_text(void *data,
 static void
 text_input_cursor_position(void *data,
 			   struct text_input *text_input,
-			   uint32_t serial,
 			   int32_t index,
 			   int32_t anchor)
 {
@@ -69,7 +67,6 @@ text_input_cursor_position(void *data,
 static void
 text_input_preedit_styling(void *data,
 			   struct text_input *text_input,
-			   uint32_t serial,
 			   uint32_t index,
 			   uint32_t length,
 			   uint32_t style)
@@ -79,7 +76,6 @@ text_input_preedit_styling(void *data,
 static void
 text_input_preedit_cursor(void *data,
 			  struct text_input *text_input,
-			  uint32_t serial,
 			  int32_t index)
 {
 }
@@ -195,7 +191,7 @@ TEST(text_test)
 	assert(client->input->keyboard->focus == client->surface);
 
 	/* Activate test model and make sure we get enter event. */
-	text_input_activate(text_input, 0, client->input->wl_seat,
+	text_input_activate(text_input, client->input->wl_seat,
 			    client->surface->wl_surface);
 	client_roundtrip(client);
 	assert(state.activated == 1 && state.deactivated == 0);
@@ -206,7 +202,7 @@ TEST(text_test)
 	assert(state.activated == 1 && state.deactivated == 1);
 
 	/* Activate test model again. */
-	text_input_activate(text_input, 0, client->input->wl_seat,
+	text_input_activate(text_input, client->input->wl_seat,
 			    client->surface->wl_surface);
 	client_roundtrip(client);
 	assert(state.activated == 2 && state.deactivated == 1);
-- 
1.8.1.4



More information about the wayland-devel mailing list