[1/1] input: Add support for zwp_text_input_v3 and zwp_input_method_v2

Submitted by Andre Moreira Magalhaes (andrunko) on March 20, 2019, 9:51 p.m.

Details

Message ID 20190320215145.6779-2-andrunko@gmail.com
State RFC
Headers show
Series "input: Add support for zwp_text_input_v3 and zwp_input_method_v2" ( rev: 1 ) in Wayland

Not browsing as part of any series.

Commit Message

Andre Moreira Magalhaes (andrunko) March 20, 2019, 9:51 p.m.
From: "Andre Moreira Magalhaes (andrunko)" <andre.magalhaes@collabora.com>

Some clients such as gtk+3 require the zwp_text_input_v3 protocol for
input-method handling. This patch adds this protocol support to text-backend and also
modifies weston-keyboard to support zwp_input_method_v2, which is an update
for the v1 protocol and aligned with the updated text-input protocol.

The patch also adds support for zwp_virtual_keyboard_v1, used to emulate the
behaviour of a physical keyboard.

Note that the updated input-method v2 protocol does not support the input-panel interface
as it did on v1. The input panel interface is implemented by desktop-shell to properly
display the virtual keyboard interface.
To avoid depending on both input method v2 and v1 (for input-panel only), this patch adds
a new weston-input-panel internal protocol (a copy of the old interface from input-method v1),
which is then implemented by desktop-shell and used by weston-keyboard when displaying
its window.

Signed-off-by: Andre Moreira Magalhaes (andrunko) <andre.magalhaes@collabora.co.uk>
---
 Makefile.am                     |   28 +-
 clients/keyboard.c              |  528 ++++++++-------
 clients/meson.build             |   12 +-
 clients/window.c                |    9 +
 clients/window.h                |    3 +
 compositor/meson.build          |   12 +-
 compositor/text-backend.c       | 1104 +++++++++++++++++--------------
 desktop-shell/input-panel.c     |   12 +-
 desktop-shell/meson.build       |    4 +-
 protocol/meson.build            |    4 +
 protocol/weston-input-panel.xml |   63 ++
 tests/meson.build               |    6 +-
 tests/text-test.c               |  151 ++---
 13 files changed, 1056 insertions(+), 880 deletions(-)
 create mode 100644 protocol/weston-input-panel.xml

Patch hide | download patch | download mbox

diff --git a/Makefile.am b/Makefile.am
index b2bb61f6..5faec4c0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -171,8 +171,12 @@  nodist_libweston_@LIBWESTON_MAJOR@_la_SOURCES =				\
 	protocol/text-cursor-position-server-protocol.h	\
 	protocol/text-input-unstable-v1-protocol.c			\
 	protocol/text-input-unstable-v1-server-protocol.h		\
+	protocol/text-input-unstable-v3-protocol.c			\
+	protocol/text-input-unstable-v3-server-protocol.h		\
 	protocol/input-method-unstable-v1-protocol.c			\
 	protocol/input-method-unstable-v1-server-protocol.h		\
+	protocol/input-method-unstable-v2-protocol.c			\
+	protocol/input-method-unstable-v2-server-protocol.h		\
 	protocol/presentation-time-protocol.c		\
 	protocol/presentation-time-server-protocol.h	\
 	protocol/viewporter-protocol.c			\
@@ -185,6 +189,10 @@  nodist_libweston_@LIBWESTON_MAJOR@_la_SOURCES =				\
 	protocol/pointer-constraints-unstable-v1-server-protocol.h      \
 	protocol/input-timestamps-unstable-v1-protocol.c		\
 	protocol/input-timestamps-unstable-v1-server-protocol.h		\
+	protocol/virtual-keyboard-unstable-v1-protocol.c		\
+	protocol/virtual-keyboard-unstable-v1-server-protocol.h		\
+	protocol/weston-input-panel-protocol.c				\
+	protocol/weston-input-panel-server-protocol.h			\
 	protocol/weston-touch-calibration-protocol.c			\
 	protocol/weston-touch-calibration-server-protocol.h		\
 	protocol/linux-explicit-synchronization-unstable-v1-protocol.c	\
@@ -879,8 +887,12 @@  weston_keyboard_SOURCES = clients/keyboard.c
 nodist_weston_keyboard_SOURCES =				\
 	protocol/weston-desktop-shell-client-protocol.h		\
 	protocol/weston-desktop-shell-protocol.c		\
-	protocol/input-method-unstable-v1-protocol.c		\
-	protocol/input-method-unstable-v1-client-protocol.h
+	protocol/input-method-unstable-v2-protocol.c		\
+	protocol/input-method-unstable-v2-client-protocol.h	\
+	protocol/virtual-keyboard-unstable-v1-protocol.c	\
+	protocol/virtual-keyboard-unstable-v1-client-protocol.h	\
+	protocol/weston-input-panel-protocol.c		\
+	protocol/weston-input-panel-client-protocol.h
 weston_keyboard_LDADD = libtoytoolkit.la
 weston_keyboard_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS)
 
@@ -957,10 +969,18 @@  BUILT_SOURCES +=					\
 	protocol/text-cursor-position-protocol.c	\
 	protocol/text-input-unstable-v1-protocol.c			\
 	protocol/text-input-unstable-v1-client-protocol.h		\
+	protocol/text-input-unstable-v3-protocol.c			\
+	protocol/text-input-unstable-v3-client-protocol.h		\
 	protocol/input-method-unstable-v1-protocol.c			\
 	protocol/input-method-unstable-v1-client-protocol.h		\
+	protocol/input-method-unstable-v2-protocol.c			\
+	protocol/input-method-unstable-v2-client-protocol.h		\
+	protocol/virtual-keyboard-unstable-v1-protocol.c	\
+	protocol/virtual-keyboard-unstable-v1-client-protocol.h	\
 	protocol/weston-desktop-shell-client-protocol.h			\
 	protocol/weston-desktop-shell-protocol.c			\
+	protocol/weston-input-panel-client-protocol.h			\
+	protocol/weston-input-panel-protocol.c				\
 	protocol/viewporter-client-protocol.h		\
 	protocol/viewporter-protocol.c			\
 	protocol/presentation-time-protocol.c				\
@@ -1628,9 +1648,7 @@  EXTRA_DIST +=							\
 BUILT_SOURCES +=				\
 	protocol/weston-test-protocol.c	\
 	protocol/weston-test-server-protocol.h	\
-	protocol/weston-test-client-protocol.h	\
-	protocol/text-input-unstable-v1-protocol.c		\
-	protocol/text-input-unstable-v1-client-protocol.h
+	protocol/weston-test-client-protocol.h
 
 EXTRA_DIST +=					\
 	protocol/weston-desktop-shell.xml	\
diff --git a/clients/keyboard.c b/clients/keyboard.c
index c9f6f28e..2ba036fb 100644
--- a/clients/keyboard.c
+++ b/clients/keyboard.c
@@ -1,6 +1,7 @@ 
 /*
  * Copyright © 2012 Openismus GmbH
  * Copyright © 2012 Intel Corporation
+ * Copyright © 2019 Collabora Ltd.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -34,29 +35,39 @@ 
 #include <cairo.h>
 
 #include "window.h"
-#include "input-method-unstable-v1-client-protocol.h"
-#include "text-input-unstable-v1-client-protocol.h"
+#include "input-method-unstable-v2-client-protocol.h"
+#include "text-input-unstable-v3-client-protocol.h"
+#include "virtual-keyboard-unstable-v1-client-protocol.h"
+#include "weston-input-panel-client-protocol.h"
 #include "shared/xalloc.h"
 
 struct keyboard;
 
+struct text_input_state {
+	uint32_t content_hint;
+	uint32_t content_purpose;
+	uint32_t change_cause;
+	char *surrounding_text;
+	uint32_t surrounding_cursor;
+	bool active;
+};
+
 struct virtual_keyboard {
-	struct zwp_input_panel_v1 *input_panel;
-	struct zwp_input_method_v1 *input_method;
-	struct zwp_input_method_context_v1 *context;
+	struct weston_input_panel *input_panel;
+	struct zwp_input_method_manager_v2 *input_method_manager;
+	struct zwp_input_method_v2 *input_method;
+	struct zwp_virtual_keyboard_manager_v1 *virtual_keyboard_manager;
+	struct zwp_virtual_keyboard_v1 *virtual_keyboard;
 	struct display *display;
 	struct output *output;
-	char *preedit_string;
-	uint32_t preedit_style;
-	struct {
-		xkb_mod_mask_t shift_mask;
-	} keysym;
+	struct wl_seat *seat;
+
 	uint32_t serial;
-	uint32_t content_hint;
-	uint32_t content_purpose;
+	char *preedit_string;
+	struct text_input_state pending;
+	struct text_input_state current;
+	// TODO - add support to change language (e.g. via config)
 	char *preferred_language;
-	char *surrounding_text;
-	uint32_t surrounding_cursor;
 	struct keyboard *keyboard;
 	bool toplevel;
 };
@@ -73,7 +84,6 @@  enum key_type {
 	keytype_arrow_left,
 	keytype_arrow_right,
 	keytype_arrow_down,
-	keytype_style
 };
 
 struct key {
@@ -94,7 +104,6 @@  struct layout {
 	uint32_t rows;
 
 	const char *language;
-	uint32_t text_direction;
 };
 
 static const struct key normal_keys[] = {
@@ -140,7 +149,6 @@  static const struct key normal_keys[] = {
 	{ keytype_arrow_left, "<", "<", "<", 1},
 	{ keytype_arrow_right, ">", ">", ">", 1},
 	{ keytype_arrow_down, "\\/", "\\/", "\\/", 1},
-	{ keytype_style, "", "", "", 2}
 };
 
 static const struct key numeric_keys[] = {
@@ -162,7 +170,6 @@  static const struct key numeric_keys[] = {
 	{ keytype_arrow_left, "<", "<", "<", 1},
 	{ keytype_arrow_right, ">", ">", ">", 1},
 	{ keytype_arrow_down, "\\/", "\\/", "\\/", 1},
-	{ keytype_style, "", "", "", 2}
 };
 
 static const struct key arabic_keys[] = {
@@ -210,7 +217,6 @@  static const struct key arabic_keys[] = {
 	{ keytype_space, "", "", "", 6},
 	{ keytype_default, ".", "ذ", "]", 1},
 	{ keytype_default, "ط", "ﺝ", "[", 1},
-	{ keytype_style, "", "", "", 2}
 };
 
 
@@ -219,8 +225,7 @@  static const struct layout normal_layout = {
 	sizeof(normal_keys) / sizeof(*normal_keys),
 	12,
 	4,
-	"en",
-	ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_LTR
+	"en"
 };
 
 static const struct layout numeric_layout = {
@@ -228,8 +233,7 @@  static const struct layout numeric_layout = {
 	sizeof(numeric_keys) / sizeof(*numeric_keys),
 	12,
 	2,
-	"en",
-	ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_LTR
+	"en"
 };
 
 static const struct layout arabic_layout = {
@@ -237,19 +241,7 @@  static const struct layout arabic_layout = {
 	sizeof(arabic_keys) / sizeof(*arabic_keys),
 	13,
 	4,
-	"ar",
-	ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_RTL
-};
-
-static const char *style_labels[] = {
-	"default",
-	"none",
-	"active",
-	"inactive",
-	"highlight",
-	"underline",
-	"selection",
-	"incorrect"
+	"ar"
 };
 
 static const double key_width = 60;
@@ -269,6 +261,9 @@  struct keyboard {
 	enum keyboard_state state;
 };
 
+static void keyboard_set_visibility(struct virtual_keyboard *virtual_keyboard,
+				    bool visible);
+
 static void __attribute__ ((format (printf, 1, 2)))
 dbg(const char *fmt, ...)
 {
@@ -285,9 +280,6 @@  static const char *
 label_from_key(struct keyboard *keyboard,
 	       const struct key *key)
 {
-	if (key->key_type == keytype_style)
-		return style_labels[keyboard->keyboard->preedit_style];
-
 	switch(keyboard->state) {
 	case KEYBOARD_STATE_DEFAULT:
 		return key->label;
@@ -341,9 +333,13 @@  draw_key(struct keyboard *keyboard,
 static const struct layout *
 get_current_layout(struct virtual_keyboard *keyboard)
 {
-	switch (keyboard->content_purpose) {
-		case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_DIGITS:
-		case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NUMBER:
+	if (!keyboard->current.active) {
+		return &normal_layout;
+	}
+
+	switch (keyboard->current.content_purpose) {
+		case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DIGITS:
+		case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER:
 			return &numeric_layout;
 		default:
 			if (keyboard->preferred_language &&
@@ -430,22 +426,21 @@  virtual_keyboard_commit_preedit(struct virtual_keyboard *keyboard)
 	    strlen(keyboard->preedit_string) == 0)
 		return;
 
-	zwp_input_method_context_v1_cursor_position(keyboard->context,
-						    0, 0);
-	zwp_input_method_context_v1_commit_string(keyboard->context,
-						  keyboard->serial,
-						  keyboard->preedit_string);
+	zwp_input_method_v2_commit_string(keyboard->input_method,
+					  keyboard->preedit_string);
+	zwp_input_method_v2_commit(keyboard->input_method,
+				   keyboard->serial);
 
-	if (keyboard->surrounding_text) {
-		surrounding_text = insert_text(keyboard->surrounding_text,
-					       keyboard->surrounding_cursor,
+	if (keyboard->current.surrounding_text) {
+		surrounding_text = insert_text(keyboard->current.surrounding_text,
+					       keyboard->current.surrounding_cursor,
 					       keyboard->preedit_string);
-		free(keyboard->surrounding_text);
-		keyboard->surrounding_text = surrounding_text;
-		keyboard->surrounding_cursor += strlen(keyboard->preedit_string);
+		free(keyboard->current.surrounding_text);
+		keyboard->current.surrounding_text = surrounding_text;
+		keyboard->current.surrounding_cursor += strlen(keyboard->preedit_string);
 	} else {
-		keyboard->surrounding_text = strdup(keyboard->preedit_string);
-		keyboard->surrounding_cursor = strlen(keyboard->preedit_string);
+		keyboard->current.surrounding_text = strdup(keyboard->preedit_string);
+		keyboard->current.surrounding_cursor = strlen(keyboard->preedit_string);
 	}
 
 	free(keyboard->preedit_string);
@@ -458,19 +453,11 @@  virtual_keyboard_send_preedit(struct virtual_keyboard *keyboard,
 {
 	uint32_t index = strlen(keyboard->preedit_string);
 
-	if (keyboard->preedit_style)
-		zwp_input_method_context_v1_preedit_styling(keyboard->context,
-							    0,
-							    strlen(keyboard->preedit_string),
-							    keyboard->preedit_style);
 	if (cursor > 0)
 		index = cursor;
-	zwp_input_method_context_v1_preedit_cursor(keyboard->context,
-						   index);
-	zwp_input_method_context_v1_preedit_string(keyboard->context,
-						   keyboard->serial,
-						   keyboard->preedit_string,
-						   keyboard->preedit_string);
+	zwp_input_method_v2_set_preedit_string(keyboard->input_method,
+					       keyboard->preedit_string, index, index);
+	zwp_input_method_v2_commit(keyboard->input_method, keyboard->serial);
 }
 
 static const char *
@@ -487,33 +474,33 @@  static void
 delete_before_cursor(struct virtual_keyboard *keyboard)
 {
 	const char *start, *end;
+	const char *surrounding_text = keyboard->current.surrounding_text;
+	uint32_t surrounding_cursor = keyboard->current.surrounding_cursor;
 
-	if (!keyboard->surrounding_text) {
+	if (!surrounding_text) {
 		dbg("delete_before_cursor: No surrounding text available\n");
 		return;
 	}
 
-	start = prev_utf8_char(keyboard->surrounding_text,
-			       keyboard->surrounding_text + keyboard->surrounding_cursor);
+	start = prev_utf8_char(surrounding_text,
+			       surrounding_text + surrounding_cursor);
 	if (!start) {
 		dbg("delete_before_cursor: No previous character to delete\n");
 		return;
 	}
 
-	end = keyboard->surrounding_text + keyboard->surrounding_cursor;
+	end = surrounding_text + surrounding_cursor;
 
-	zwp_input_method_context_v1_delete_surrounding_text(keyboard->context,
-							    (start - keyboard->surrounding_text) - keyboard->surrounding_cursor,
-							    end - start);
-	zwp_input_method_context_v1_commit_string(keyboard->context,
-						  keyboard->serial,
-						  "");
+	zwp_input_method_v2_delete_surrounding_text(keyboard->input_method,
+						    surrounding_cursor - (start - surrounding_text), 0);
+	zwp_input_method_v2_commit(keyboard->input_method, keyboard->serial);
 
 	/* Update surrounding text */
-	keyboard->surrounding_cursor = start - keyboard->surrounding_text;
-	keyboard->surrounding_text[keyboard->surrounding_cursor] = '\0';
+	keyboard->current.surrounding_cursor = start - surrounding_text;
+	keyboard->current.surrounding_text[surrounding_cursor] = '\0';
 	if (*end)
-		memmove(keyboard->surrounding_text + keyboard->surrounding_cursor, end, strlen(end));
+		memmove(keyboard->current.surrounding_text + keyboard->current.surrounding_cursor,
+			end, strlen(end));
 }
 
 static char *
@@ -548,7 +535,6 @@  keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key *
 		break;
 	}
 
-	xkb_mod_mask_t mod_mask = keyboard->state == KEYBOARD_STATE_DEFAULT ? 0 : keyboard->keyboard->keysym.shift_mask;
 	uint32_t key_state = (state == WL_POINTER_BUTTON_STATE_PRESSED) ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED;
 
 	switch (key->key_type) {
@@ -562,6 +548,16 @@  keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key *
 			virtual_keyboard_send_preedit(keyboard->keyboard, -1);
 			break;
 		case keytype_backspace:
+			if (!keyboard->keyboard->current.active ||
+			    ((!keyboard->keyboard->preedit_string ||
+			      strlen(keyboard->keyboard->preedit_string) == 0) &&
+			     (!keyboard->keyboard->current.surrounding_text ||
+			      strlen(keyboard->keyboard->current.surrounding_text) == 0))) {
+				zwp_virtual_keyboard_v1_key(keyboard->keyboard->virtual_keyboard,
+							    time, KEY_BACKSPACE, key_state);
+				break;
+			}
+
 			if (state != WL_POINTER_BUTTON_STATE_PRESSED)
 				break;
 
@@ -573,13 +569,18 @@  keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key *
 			}
 			break;
 		case keytype_enter:
-			virtual_keyboard_commit_preedit(keyboard->keyboard);
-			zwp_input_method_context_v1_keysym(keyboard->keyboard->context,
-							   display_get_serial(keyboard->keyboard->display),
-							   time,
-							   XKB_KEY_Return, key_state, mod_mask);
+			if (state == WL_POINTER_BUTTON_STATE_PRESSED &&
+			    keyboard->keyboard->current.active)
+				virtual_keyboard_commit_preedit(keyboard->keyboard);
+			zwp_virtual_keyboard_v1_key(keyboard->keyboard->virtual_keyboard,
+						    time, KEY_ENTER, key_state);
 			break;
 		case keytype_space:
+			if (!keyboard->keyboard->current.active) {
+				zwp_virtual_keyboard_v1_key(keyboard->keyboard->virtual_keyboard,
+							    time, KEY_SPACE, key_state);
+				break;
+			}
 			if (state != WL_POINTER_BUTTON_STATE_PRESSED)
 				break;
 			keyboard->keyboard->preedit_string =
@@ -617,45 +618,39 @@  keyboard_handle_key(struct keyboard *keyboard, uint32_t time, const struct key *
 			}
 			break;
 		case keytype_tab:
-			virtual_keyboard_commit_preedit(keyboard->keyboard);
-			zwp_input_method_context_v1_keysym(keyboard->keyboard->context,
-							   display_get_serial(keyboard->keyboard->display),
-							   time,
-							   XKB_KEY_Tab, key_state, mod_mask);
+			if (state == WL_POINTER_BUTTON_STATE_PRESSED &&
+			    keyboard->keyboard->current.active)
+				virtual_keyboard_commit_preedit(keyboard->keyboard);
+			zwp_virtual_keyboard_v1_key(keyboard->keyboard->virtual_keyboard,
+						    time, KEY_TAB, key_state);
 			break;
 		case keytype_arrow_up:
-			virtual_keyboard_commit_preedit(keyboard->keyboard);
-			zwp_input_method_context_v1_keysym(keyboard->keyboard->context,
-							   display_get_serial(keyboard->keyboard->display),
-							   time,
-							   XKB_KEY_Up, key_state, mod_mask);
+			if (state == WL_POINTER_BUTTON_STATE_PRESSED &&
+			    keyboard->keyboard->current.active)
+				virtual_keyboard_commit_preedit(keyboard->keyboard);
+			zwp_virtual_keyboard_v1_key(keyboard->keyboard->virtual_keyboard,
+						    time, KEY_UP, key_state);
 			break;
 		case keytype_arrow_left:
-			virtual_keyboard_commit_preedit(keyboard->keyboard);
-			zwp_input_method_context_v1_keysym(keyboard->keyboard->context,
-							   display_get_serial(keyboard->keyboard->display),
-							   time,
-							   XKB_KEY_Left, key_state, mod_mask);
+			if (state == WL_POINTER_BUTTON_STATE_PRESSED &&
+			    keyboard->keyboard->current.active)
+				virtual_keyboard_commit_preedit(keyboard->keyboard);
+			zwp_virtual_keyboard_v1_key(keyboard->keyboard->virtual_keyboard,
+						    time, KEY_LEFT, key_state);
 			break;
 		case keytype_arrow_right:
-			virtual_keyboard_commit_preedit(keyboard->keyboard);
-			zwp_input_method_context_v1_keysym(keyboard->keyboard->context,
-							   display_get_serial(keyboard->keyboard->display),
-							   time,
-							   XKB_KEY_Right, key_state, mod_mask);
+			if (state == WL_POINTER_BUTTON_STATE_PRESSED &&
+			    keyboard->keyboard->current.active)
+				virtual_keyboard_commit_preedit(keyboard->keyboard);
+			zwp_virtual_keyboard_v1_key(keyboard->keyboard->virtual_keyboard,
+						    time, KEY_RIGHT, key_state);
 			break;
 		case keytype_arrow_down:
-			virtual_keyboard_commit_preedit(keyboard->keyboard);
-			zwp_input_method_context_v1_keysym(keyboard->keyboard->context,
-							   display_get_serial(keyboard->keyboard->display),
-							   time,
-							   XKB_KEY_Down, key_state, mod_mask);
-			break;
-		case keytype_style:
-			if (state != WL_POINTER_BUTTON_STATE_PRESSED)
-				break;
-			keyboard->keyboard->preedit_style = (keyboard->keyboard->preedit_style + 1) % 8; /* TODO */
-			virtual_keyboard_send_preedit(keyboard->keyboard, -1);
+			if (state == WL_POINTER_BUTTON_STATE_PRESSED &&
+			    keyboard->keyboard->current.active)
+				virtual_keyboard_commit_preedit(keyboard->keyboard);
+			zwp_virtual_keyboard_v1_key(keyboard->keyboard->virtual_keyboard,
+						    time, KEY_DOWN, key_state);
 			break;
 	}
 }
@@ -751,248 +746,191 @@  touch_up_handler(struct widget *widget, struct input *input,
 		WL_POINTER_BUTTON_STATE_RELEASED, data);
 }
 
+static void
+input_method_activate(void *data,
+		      struct zwp_input_method_v2 *input_method)
+{
+	struct virtual_keyboard *virtual_keyboard = data;
+	struct text_input_state defaults = {0};
+
+	if (virtual_keyboard->pending.surrounding_text)
+		free(virtual_keyboard->pending.surrounding_text);
+	virtual_keyboard->pending.surrounding_text = NULL;
+	virtual_keyboard->pending = defaults;
+	if (virtual_keyboard->preedit_string)
+		free(virtual_keyboard->preedit_string);
+	virtual_keyboard->preedit_string = strdup("");
+	virtual_keyboard->pending.active = true;
+}
+
+static void
+input_method_deactivate(void *data,
+			struct zwp_input_method_v2 *input_method)
+{
+	struct virtual_keyboard *virtual_keyboard = data;
+
+	virtual_keyboard->pending.active = false;
+}
+
 static void
 handle_surrounding_text(void *data,
-			struct zwp_input_method_context_v1 *context,
+			struct zwp_input_method_v2 *input_method,
 			const char *text,
 			uint32_t cursor,
 			uint32_t anchor)
 {
 	struct virtual_keyboard *keyboard = data;
 
-	free(keyboard->surrounding_text);
-	keyboard->surrounding_text = strdup(text);
+	if (keyboard->pending.surrounding_text)
+		free(keyboard->pending.surrounding_text);
+	keyboard->pending.surrounding_text = strdup(text);
 
-	keyboard->surrounding_cursor = cursor;
+	keyboard->pending.surrounding_cursor = cursor;
 }
 
 static void
-handle_reset(void *data,
-	     struct zwp_input_method_context_v1 *context)
+handle_text_change_cause(void *data,
+			 struct zwp_input_method_v2 *zwp_input_method_v2,
+			 uint32_t cause)
 {
 	struct virtual_keyboard *keyboard = data;
 
-	dbg("Reset pre-edit buffer\n");
-
-	if (strlen(keyboard->preedit_string)) {
-		free(keyboard->preedit_string);
-		keyboard->preedit_string = strdup("");
-	}
+	keyboard->pending.change_cause = cause;
 }
 
 static void
 handle_content_type(void *data,
-		    struct zwp_input_method_context_v1 *context,
+		    struct zwp_input_method_v2 *input_method,
 		    uint32_t hint,
 		    uint32_t purpose)
 {
 	struct virtual_keyboard *keyboard = data;
 
-	keyboard->content_hint = hint;
-	keyboard->content_purpose = purpose;
-}
-
-static void
-handle_invoke_action(void *data,
-		     struct zwp_input_method_context_v1 *context,
-		     uint32_t button,
-		     uint32_t index)
-{
-	struct virtual_keyboard *keyboard = data;
-
-	if (button != BTN_LEFT)
-		return;
-
-	virtual_keyboard_send_preedit(keyboard, index);
+	keyboard->pending.content_hint = hint;
+	keyboard->pending.content_purpose = purpose;
 }
 
 static void
 handle_commit_state(void *data,
-		    struct zwp_input_method_context_v1 *context,
-		    uint32_t serial)
+		    struct zwp_input_method_v2 *input_method)
 {
-	struct virtual_keyboard *keyboard = data;
-	const struct layout *layout;
-
-	keyboard->serial = serial;
+	struct virtual_keyboard *virtual_keyboard = data;
+	struct text_input_state defaults = {0};
 
-	layout = get_current_layout(keyboard);
+	virtual_keyboard->serial++;
 
-	if (keyboard->surrounding_text)
-		dbg("Surrounding text updated: %s\n", keyboard->surrounding_text);
+	if (virtual_keyboard->pending.surrounding_text)
+		free(virtual_keyboard->pending.surrounding_text);
+	virtual_keyboard->pending.surrounding_text = NULL;
+	virtual_keyboard->current = virtual_keyboard->pending;
+	virtual_keyboard->pending = defaults;
+	virtual_keyboard->pending.active = virtual_keyboard->current.active;
 
-	window_schedule_resize(keyboard->keyboard->window,
-			       layout->columns * key_width,
-			       layout->rows * key_height);
-
-	zwp_input_method_context_v1_language(context,
-					     keyboard->serial,
-					     layout->language);
-	zwp_input_method_context_v1_text_direction(context,
-						   keyboard->serial,
-						   layout->text_direction);
-
-	widget_schedule_redraw(keyboard->keyboard->widget);
+	keyboard_set_visibility(virtual_keyboard,
+				virtual_keyboard->current.active);
 }
 
 static void
-handle_preferred_language(void *data,
-			  struct zwp_input_method_context_v1 *context,
-			  const char *language)
+handle_unavailable(void *data,
+		   struct zwp_input_method_v2 *zwp_input_method_v2)
 {
-	struct virtual_keyboard *keyboard = data;
-
-	if (keyboard->preferred_language)
-		free(keyboard->preferred_language);
+	struct virtual_keyboard *virtual_keyboard = data;
 
-	keyboard->preferred_language = NULL;
-
-	if (language)
-		keyboard->preferred_language = strdup(language);
+	virtual_keyboard->current.active = false;
+	zwp_input_method_v2_destroy(virtual_keyboard->input_method);
+	virtual_keyboard->input_method = NULL;
+	keyboard_set_visibility(virtual_keyboard, false);
 }
 
-static const struct zwp_input_method_context_v1_listener input_method_context_listener = {
+static const struct zwp_input_method_v2_listener input_method_listener = {
+	input_method_activate,
+	input_method_deactivate,
 	handle_surrounding_text,
-	handle_reset,
+	handle_text_change_cause,
 	handle_content_type,
-	handle_invoke_action,
 	handle_commit_state,
-	handle_preferred_language
+	handle_unavailable
 };
 
 static void
-input_method_activate(void *data,
-		      struct zwp_input_method_v1 *input_method,
-		      struct zwp_input_method_context_v1 *context)
+make_input_method(struct virtual_keyboard *virtual_keyboard)
 {
-	struct virtual_keyboard *keyboard = data;
-	struct wl_array modifiers_map;
-	const struct layout *layout;
-
-	keyboard->keyboard->state = KEYBOARD_STATE_DEFAULT;
-
-	if (keyboard->context)
-		zwp_input_method_context_v1_destroy(keyboard->context);
-
-	if (keyboard->preedit_string)
-		free(keyboard->preedit_string);
-
-	keyboard->preedit_string = strdup("");
-	keyboard->content_hint = 0;
-	keyboard->content_purpose = 0;
-	free(keyboard->preferred_language);
-	keyboard->preferred_language = NULL;
-	free(keyboard->surrounding_text);
-	keyboard->surrounding_text = NULL;
-
-	keyboard->serial = 0;
-
-	keyboard->context = context;
-	zwp_input_method_context_v1_add_listener(context,
-						 &input_method_context_listener,
-						 keyboard);
-
-	wl_array_init(&modifiers_map);
-	keysym_modifiers_add(&modifiers_map, "Shift");
-	keysym_modifiers_add(&modifiers_map, "Control");
-	keysym_modifiers_add(&modifiers_map, "Mod1");
-	zwp_input_method_context_v1_modifiers_map(context, &modifiers_map);
-	keyboard->keysym.shift_mask = keysym_modifiers_get_mask(&modifiers_map, "Shift");
-	wl_array_release(&modifiers_map);
-
-	layout = get_current_layout(keyboard);
-
-	window_schedule_resize(keyboard->keyboard->window,
-			       layout->columns * key_width,
-			       layout->rows * key_height);
-
-	zwp_input_method_context_v1_language(context,
-					     keyboard->serial,
-					     layout->language);
-	zwp_input_method_context_v1_text_direction(context,
-						   keyboard->serial,
-						   layout->text_direction);
-
-	widget_schedule_redraw(keyboard->keyboard->widget);
+	virtual_keyboard->input_method =
+		zwp_input_method_manager_v2_get_input_method(
+			virtual_keyboard->input_method_manager, virtual_keyboard->seat);
+	zwp_input_method_v2_add_listener(virtual_keyboard->input_method,
+		&input_method_listener, virtual_keyboard);
 }
 
 static void
-input_method_deactivate(void *data,
-			struct zwp_input_method_v1 *input_method,
-			struct zwp_input_method_context_v1 *context)
+make_virtual_keyboard(struct virtual_keyboard *virtual_keyboard)
 {
-	struct virtual_keyboard *keyboard = data;
-
-	if (!keyboard->context)
-		return;
-
-	zwp_input_method_context_v1_destroy(keyboard->context);
-	keyboard->context = NULL;
+	virtual_keyboard->virtual_keyboard =
+		zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(
+			virtual_keyboard->virtual_keyboard_manager, virtual_keyboard->seat);
 }
 
-static const struct zwp_input_method_v1_listener input_method_listener = {
-	input_method_activate,
-	input_method_deactivate
-};
-
 static void
 global_handler(struct display *display, uint32_t name,
 	       const char *interface, uint32_t version, void *data)
 {
 	struct virtual_keyboard *keyboard = data;
 
-	if (!strcmp(interface, "zwp_input_panel_v1")) {
+	if (!strcmp(interface, "weston_input_panel")) {
 		keyboard->input_panel =
-			display_bind(display, name, &zwp_input_panel_v1_interface, 1);
-	} else if (!strcmp(interface, "zwp_input_method_v1")) {
-		keyboard->input_method =
 			display_bind(display, name,
-				     &zwp_input_method_v1_interface, 1);
-		zwp_input_method_v1_add_listener(keyboard->input_method,
-						 &input_method_listener,
-						 keyboard);
+				     &weston_input_panel_interface, 1);
+	} else if (!strcmp(interface, "zwp_input_method_manager_v2")) {
+		keyboard->input_method_manager =
+			display_bind(display, name,
+				     &zwp_input_method_manager_v2_interface, 1);
+	} else if (!strcmp(interface, "zwp_virtual_keyboard_manager_v1")) {
+		keyboard->virtual_keyboard_manager =
+			display_bind(display, name,
+				     &zwp_virtual_keyboard_manager_v1_interface, 1);
 	}
+
 }
 
 static void
 set_toplevel(struct output *output, struct virtual_keyboard *virtual_keyboard)
 {
-	struct zwp_input_panel_surface_v1 *ips;
+	struct weston_input_panel_surface *ips;
 	struct keyboard *keyboard = virtual_keyboard->keyboard;
 
-	ips = zwp_input_panel_v1_get_input_panel_surface(virtual_keyboard->input_panel,
+	ips = weston_input_panel_get_input_panel_surface(virtual_keyboard->input_panel,
 							 window_get_wl_surface(keyboard->window));
 
-	zwp_input_panel_surface_v1_set_toplevel(ips,
+	weston_input_panel_surface_set_toplevel(ips,
 						output_get_wl_output(output),
-						ZWP_INPUT_PANEL_SURFACE_V1_POSITION_CENTER_BOTTOM);
+						WESTON_INPUT_PANEL_SURFACE_POSITION_CENTER_BOTTOM);
 
 	virtual_keyboard->toplevel = true;
 }
 
 static void
-display_output_handler(struct output *output, void *data) {
+display_output_handler(struct output *output, void *data)
+{
 	struct virtual_keyboard *keyboard = data;
 
-	if (!keyboard->toplevel)
+	if (!keyboard->toplevel && keyboard->keyboard->window)
 		set_toplevel(output, keyboard);
 }
 
 static void
-keyboard_create(struct virtual_keyboard *virtual_keyboard)
+keyboard_window_create(struct virtual_keyboard *virtual_keyboard)
 {
-	struct keyboard *keyboard;
+	struct keyboard *keyboard = virtual_keyboard->keyboard;
 	const struct layout *layout;
 
+	if (keyboard->window)
+		return;
+
 	layout = get_current_layout(virtual_keyboard);
 
-	keyboard = xzalloc(sizeof *keyboard);
-	keyboard->keyboard = virtual_keyboard;
 	keyboard->window = window_create_custom(virtual_keyboard->display);
 	keyboard->widget = window_add_widget(keyboard->window, keyboard);
 
-	virtual_keyboard->keyboard = keyboard;
-
 	window_set_title(keyboard->window, "Virtual keyboard");
 	window_set_user_data(keyboard->window, keyboard);
 
@@ -1005,6 +943,38 @@  keyboard_create(struct virtual_keyboard *virtual_keyboard)
 	window_schedule_resize(keyboard->window,
 			       layout->columns * key_width,
 			       layout->rows * key_height);
+}
+
+static void
+keyboard_window_destroy(struct virtual_keyboard *virtual_keyboard)
+{
+	if (!virtual_keyboard->keyboard->window)
+		return;
+
+	widget_destroy(virtual_keyboard->keyboard->widget);
+	virtual_keyboard->keyboard->widget = NULL;
+	window_destroy(virtual_keyboard->keyboard->window);
+	virtual_keyboard->keyboard->window = NULL;
+}
+
+static void
+keyboard_set_visibility(struct virtual_keyboard *virtual_keyboard,
+			bool visible)
+{
+	if (visible)
+		keyboard_window_create(virtual_keyboard);
+	else
+		keyboard_window_destroy(virtual_keyboard);
+}
+
+static void
+keyboard_create(struct virtual_keyboard *virtual_keyboard)
+{
+	struct keyboard *keyboard;
+
+	keyboard = xzalloc(sizeof *keyboard);
+	keyboard->keyboard = virtual_keyboard;
+	virtual_keyboard->keyboard = keyboard;
 
 	display_set_output_configure_handler(virtual_keyboard->display,
 					     display_output_handler);
@@ -1026,13 +996,35 @@  main(int argc, char *argv[])
 	display_set_user_data(virtual_keyboard.display, &virtual_keyboard);
 	display_set_global_handler(virtual_keyboard.display, global_handler);
 
+	virtual_keyboard.seat = display_get_seat(virtual_keyboard.display);
+
+	if (virtual_keyboard.seat == NULL) {
+		fprintf(stderr, "No seat available\n");
+		return -1;
+	}
+
+	if (virtual_keyboard.input_method_manager == NULL) {
+		fprintf(stderr, "No input method manager global\n");
+		return -1;
+	}
+
+	if (virtual_keyboard.virtual_keyboard_manager == NULL) {
+		fprintf(stderr, "No virtual keyboard manager global\n");
+		return -1;
+	}
+
 	if (virtual_keyboard.input_panel == NULL) {
 		fprintf(stderr, "No input panel global\n");
 		return -1;
 	}
 
+	make_input_method(&virtual_keyboard);
+	make_virtual_keyboard(&virtual_keyboard);
+
 	keyboard_create(&virtual_keyboard);
 
+	keyboard_set_visibility(&virtual_keyboard, true);
+
 	display_run(virtual_keyboard.display);
 
 	return 0;
diff --git a/clients/meson.build b/clients/meson.build
index 47e9e8ce..c1885c50 100644
--- a/clients/meson.build
+++ b/clients/meson.build
@@ -342,10 +342,14 @@  if get_option('shell-desktop')
 	exe_keyboard = executable(
 		'weston-keyboard',
 		'keyboard.c',
-		text_input_unstable_v1_client_protocol_h,
-		text_input_unstable_v1_protocol_c,
-		input_method_unstable_v1_client_protocol_h,
-		input_method_unstable_v1_protocol_c,
+		input_method_unstable_v2_client_protocol_h,
+		input_method_unstable_v2_protocol_c,
+		text_input_unstable_v3_client_protocol_h,
+		text_input_unstable_v3_protocol_c,
+		virtual_keyboard_unstable_v1_client_protocol_h,
+		virtual_keyboard_unstable_v1_protocol_c,
+		weston_input_panel_client_protocol_h,
+		weston_input_panel_protocol_c,
 		include_directories: include_directories('..'),
 		dependencies: dep_toytoolkit,
 		install_dir: get_option('libexecdir'),
diff --git a/clients/window.c b/clients/window.c
index bb9c708f..8e933f9b 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -6317,6 +6317,15 @@  display_get_compositor(struct display *display)
 	return display->compositor;
 }
 
+struct wl_seat *
+display_get_seat(struct display *display)
+{
+	if (wl_list_empty(&display->input_list))
+		return NULL;
+
+	return container_of(display->input_list.next, struct input, link)->seat;
+}
+
 uint32_t
 display_get_serial(struct display *display)
 {
diff --git a/clients/window.h b/clients/window.h
index fde5c2f0..13ceddee 100644
--- a/clients/window.h
+++ b/clients/window.h
@@ -77,6 +77,9 @@  display_get_cairo_device(struct display *display);
 struct wl_compositor *
 display_get_compositor(struct display *display);
 
+struct wl_seat *
+display_get_seat(struct display *display);
+
 struct output *
 display_get_output(struct display *display);
 
diff --git a/compositor/meson.build b/compositor/meson.build
index d5d7282f..7dba3373 100644
--- a/compositor/meson.build
+++ b/compositor/meson.build
@@ -3,10 +3,14 @@  srcs_weston = [
 	'main.c',
 	'text-backend.c',
 	'weston-screenshooter.c',
-	text_input_unstable_v1_server_protocol_h,
-	text_input_unstable_v1_protocol_c,
-	input_method_unstable_v1_server_protocol_h,
-	input_method_unstable_v1_protocol_c,
+	input_method_unstable_v2_server_protocol_h,
+	input_method_unstable_v2_protocol_c,
+	text_input_unstable_v3_server_protocol_h,
+	text_input_unstable_v3_protocol_c,
+	virtual_keyboard_unstable_v1_server_protocol_h,
+	virtual_keyboard_unstable_v1_protocol_c,
+	weston_input_panel_server_protocol_h,
+	weston_input_panel_protocol_c,
 	weston_screenshooter_server_protocol_h,
 	weston_screenshooter_protocol_c,
 ]
diff --git a/compositor/text-backend.c b/compositor/text-backend.c
index 03019584..3dd2f09f 100644
--- a/compositor/text-backend.c
+++ b/compositor/text-backend.c
@@ -1,6 +1,7 @@ 
 /*
  * Copyright © 2012 Openismus GmbH
  * Copyright © 2012 Intel Corporation
+ * Copyright © 2019 Collabora Ltd.
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files (the
@@ -33,32 +34,63 @@ 
 #include <unistd.h>
 #include <time.h>
 
+#include <linux/input.h>
+
 #include "compositor.h"
 #include "weston.h"
-#include "text-input-unstable-v1-server-protocol.h"
-#include "input-method-unstable-v1-server-protocol.h"
+#include "input-method-unstable-v2-server-protocol.h"
+#include "text-input-unstable-v3-server-protocol.h"
+#include "virtual-keyboard-unstable-v1-server-protocol.h"
 #include "shared/helpers.h"
 #include "shared/timespec-util.h"
 
-struct text_input_manager;
 struct input_method;
-struct input_method_context;
+struct input_method_manager;
+struct virtual_keyboard;
+struct virtual_keyboard_manager;
 struct text_backend;
+struct text_input_manager;
+
+struct text_input_state {
+	struct {
+		char *text; // NULL is allowed and equivalent to empty string
+		uint32_t cursor;
+		uint32_t anchor;
+	} surrounding;
+
+	uint32_t text_change_cause;
+
+	struct {
+		uint32_t hint;
+		uint32_t purpose;
+	} content_type;
+
+	pixman_box32_t cursor_rectangle;
+};
 
 struct text_input {
 	struct wl_resource *resource;
 
 	struct weston_compositor *ec;
 
-	struct wl_list input_methods;
+	struct weston_seat *seat;
 
-	struct weston_surface *surface;
+	struct input_method *input_method;
 
-	pixman_box32_t cursor_rectangle;
+	struct weston_surface *surface;
 
 	bool input_panel_visible;
 
 	struct text_input_manager *manager;
+
+	uint32_t current_serial;
+
+	bool pending_enabled;
+	bool current_enabled;
+	struct text_input_state pending;
+	struct text_input_state current;
+
+	struct wl_list link;
 };
 
 struct text_input_manager {
@@ -68,34 +100,77 @@  struct text_input_manager {
 	struct text_input *current_text_input;
 
 	struct weston_compositor *ec;
+
+	struct wl_list text_inputs;
+};
+
+struct input_method_state {
+	struct {
+		char *text;
+		int32_t cursor_begin;
+		int32_t cursor_end;
+	} preedit;
+
+	char *commit_text;
+
+	struct {
+		uint32_t before_length;
+		uint32_t after_length;
+	} delete;
 };
 
 struct input_method {
-	struct wl_resource *input_method_binding;
-	struct wl_global *input_method_global;
-	struct wl_listener destroy_listener;
+	struct wl_resource *resource;
+
+	struct weston_compositor *ec;
 
 	struct weston_seat *seat;
 	struct text_input *input;
 
-	struct wl_list link;
-
 	struct wl_listener keyboard_focus_listener;
 
 	bool focus_listener_initialized;
 
-	struct input_method_context *context;
+	struct wl_resource *keyboard;
 
-	struct text_backend *text_backend;
+	struct input_method_manager *manager;
+
+	struct weston_surface *pending_focused_surface;
+
+	struct input_method_state pending;
+	struct input_method_state current;
+
+	struct wl_list link;
+};
+
+struct input_method_manager {
+	struct wl_global *input_method_manager_global;
+	struct wl_listener destroy_listener;
+
+	struct weston_compositor *ec;
+
+	struct wl_list input_methods;
 };
 
-struct input_method_context {
+struct virtual_keyboard {
 	struct wl_resource *resource;
 
-	struct text_input *input;
-	struct input_method *input_method;
+	struct weston_compositor *ec;
 
-	struct wl_resource *keyboard;
+	struct weston_seat *seat;
+
+	struct virtual_keyboard_manager *manager;
+
+	struct wl_list link;
+};
+
+struct virtual_keyboard_manager {
+	struct wl_global *virtual_keyboard_manager_global;
+	struct wl_listener destroy_listener;
+
+	struct weston_compositor *ec;
+
+	struct wl_list virtual_keyboards;
 };
 
 struct text_backend {
@@ -110,37 +185,36 @@  struct text_backend {
 	} input_method;
 
 	struct wl_listener client_listener;
-	struct wl_listener seat_created_listener;
 };
 
 static void
-input_method_context_create(struct text_input *input,
-			    struct input_method *input_method);
-static void
-input_method_context_end_keyboard_grab(struct input_method_context *context);
+input_method_end_keyboard_grab(struct input_method *input_method);
 
 static void
 input_method_init_seat(struct weston_seat *seat);
 
+static void
+text_input_show_input_panel(struct text_input *text_input);
+
 static void
 deactivate_input_method(struct input_method *input_method)
 {
-	struct text_input *text_input = input_method->input;
-	struct weston_compositor *ec = text_input->ec;
-
-	if (input_method->context && input_method->input_method_binding) {
-		input_method_context_end_keyboard_grab(input_method->context);
-		zwp_input_method_v1_send_deactivate(
-			input_method->input_method_binding,
-			input_method->context->resource);
-		input_method->context->input = NULL;
+	if (input_method->resource) {
+		input_method_end_keyboard_grab(input_method);
+		zwp_input_method_v2_send_deactivate(input_method->resource);
 	}
+	if (input_method->input) {
+		input_method->input->input_method = NULL;
+		input_method->input = NULL;
+	}
+}
 
-	wl_list_remove(&input_method->link);
-	input_method->input = NULL;
-	input_method->context = NULL;
+static void
+deactivate_text_input(struct text_input *text_input)
+{
+	struct weston_compositor *ec = text_input->ec;
 
-	if (wl_list_empty(&text_input->input_methods) &&
+	if (text_input->input_method &&
 	    text_input->input_panel_visible &&
 	    text_input->manager->current_text_input == text_input) {
 		wl_signal_emit(&ec->hide_input_panel_signal, ec);
@@ -150,18 +224,30 @@  deactivate_input_method(struct input_method *input_method)
 	if (text_input->manager->current_text_input == text_input)
 		text_input->manager->current_text_input = NULL;
 
-	zwp_text_input_v1_send_leave(text_input->resource);
+	if (text_input->input_method)
+		text_input->input_method->input = NULL;
+	text_input->input_method = NULL;
+
+	if (text_input->surface) {
+		zwp_text_input_v3_send_leave(text_input->resource,
+					     text_input->surface->resource);
+	}
+	text_input->surface = NULL;
 }
 
 static void
 destroy_text_input(struct wl_resource *resource)
 {
 	struct text_input *text_input = wl_resource_get_user_data(resource);
-	struct input_method *input_method, *next;
 
-	wl_list_for_each_safe(input_method, next,
-			      &text_input->input_methods, link)
-		deactivate_input_method(input_method);
+	deactivate_text_input(text_input);
+
+	if (text_input->current.surrounding.text)
+		free(text_input->current.surrounding.text);
+	if (text_input->pending.surrounding.text)
+		free(text_input->pending.surrounding.text);
+
+	wl_list_remove(&text_input->link);
 
 	free(text_input);
 }
@@ -170,95 +256,69 @@  static void
 text_input_set_surrounding_text(struct wl_client *client,
 				struct wl_resource *resource,
 				const char *text,
-				uint32_t cursor,
-				uint32_t anchor)
+				int32_t cursor,
+				int32_t anchor)
 {
 	struct text_input *text_input = wl_resource_get_user_data(resource);
-	struct input_method *input_method, *next;
-
-	wl_list_for_each_safe(input_method, next,
-			      &text_input->input_methods, link) {
-		if (!input_method->context)
-			continue;
-		zwp_input_method_context_v1_send_surrounding_text(
-			input_method->context->resource, text, cursor, anchor);
-	}
+
+	if (text_input->pending.surrounding.text)
+		free(text_input->pending.surrounding.text);
+	text_input->pending.surrounding.text = strdup(text);
+	text_input->pending.surrounding.cursor = cursor;
+	text_input->pending.surrounding.anchor = anchor;
 }
 
 static void
-text_input_activate(struct wl_client *client,
-		    struct wl_resource *resource,
-		    struct wl_resource *seat,
-		    struct wl_resource *surface)
+activate_text_input(struct text_input *text_input)
 {
-	struct text_input *text_input = wl_resource_get_user_data(resource);
-	struct weston_seat *weston_seat = wl_resource_get_user_data(seat);
+	struct weston_seat *weston_seat = text_input->seat;
 	struct input_method *input_method;
-	struct weston_compositor *ec = text_input->ec;
-	struct text_input *current;
 
 	if (!weston_seat)
 		return;
 
 	input_method = weston_seat->input_method;
-	if (input_method->input == text_input)
+	if (!input_method || input_method->input == text_input)
 		return;
 
-	if (input_method->input)
-		deactivate_input_method(input_method);
+	if (!input_method->pending_focused_surface)
+		return;
 
 	input_method->input = text_input;
-	wl_list_insert(&text_input->input_methods, &input_method->link);
-	input_method_init_seat(weston_seat);
-
-	text_input->surface = wl_resource_get_user_data(surface);
-
-	input_method_context_create(text_input, input_method);
+	text_input->input_method = input_method;
 
-	current = text_input->manager->current_text_input;
+	text_input->surface = input_method->pending_focused_surface;
 
-	if (current && current != text_input) {
-		current->input_panel_visible = false;
-		wl_signal_emit(&ec->hide_input_panel_signal, ec);
-	}
+	zwp_input_method_v2_send_activate(input_method->resource);
 
-	if (text_input->input_panel_visible) {
-		wl_signal_emit(&ec->show_input_panel_signal,
-			       text_input->surface);
-		wl_signal_emit(&ec->update_input_panel_signal,
-			       &text_input->cursor_rectangle);
-	}
 	text_input->manager->current_text_input = text_input;
 
-	zwp_text_input_v1_send_enter(text_input->resource,
+	text_input_show_input_panel(text_input);
+
+	zwp_text_input_v3_send_enter(text_input->resource,
 				     text_input->surface->resource);
 }
 
 static void
-text_input_deactivate(struct wl_client *client,
-		      struct wl_resource *resource,
-		      struct wl_resource *seat)
+text_input_enable(struct wl_client *client,
+		  struct wl_resource *resource)
 {
-	struct weston_seat *weston_seat = wl_resource_get_user_data(seat);
+	struct text_input *text_input = wl_resource_get_user_data(resource);
+	struct text_input_state defaults = {0};
 
-	if (weston_seat && weston_seat->input_method->input)
-		deactivate_input_method(weston_seat->input_method);
+	if (text_input->pending.surrounding.text)
+		free(text_input->pending.surrounding.text);
+	text_input->pending = defaults;
+	text_input->pending_enabled = true;
 }
 
 static void
-text_input_reset(struct wl_client *client,
-		 struct wl_resource *resource)
+text_input_disable(struct wl_client *client,
+		   struct wl_resource *resource)
 {
 	struct text_input *text_input = wl_resource_get_user_data(resource);
-	struct input_method *input_method, *next;
-
-	wl_list_for_each_safe(input_method, next,
-			      &text_input->input_methods, link) {
-		if (!input_method->context)
-			continue;
-		zwp_input_method_context_v1_send_reset(
-			input_method->context->resource);
-	}
+
+	text_input->pending_enabled = false;
 }
 
 static void
@@ -270,15 +330,11 @@  text_input_set_cursor_rectangle(struct wl_client *client,
 				int32_t height)
 {
 	struct text_input *text_input = wl_resource_get_user_data(resource);
-	struct weston_compositor *ec = text_input->ec;
 
-	text_input->cursor_rectangle.x1 = x;
-	text_input->cursor_rectangle.y1 = y;
-	text_input->cursor_rectangle.x2 = x + width;
-	text_input->cursor_rectangle.y2 = y + height;
-
-	wl_signal_emit(&ec->update_input_panel_signal,
-		       &text_input->cursor_rectangle);
+	text_input->pending.cursor_rectangle.x1 = x;
+	text_input->pending.cursor_rectangle.y1 = y;
+	text_input->pending.cursor_rectangle.x2 = x + width;
+	text_input->pending.cursor_rectangle.y2 = y + height;
 }
 
 static void
@@ -288,141 +344,139 @@  text_input_set_content_type(struct wl_client *client,
 			    uint32_t purpose)
 {
 	struct text_input *text_input = wl_resource_get_user_data(resource);
-	struct input_method *input_method, *next;
-
-	wl_list_for_each_safe(input_method, next,
-			      &text_input->input_methods, link) {
-		if (!input_method->context)
-			continue;
-		zwp_input_method_context_v1_send_content_type(
-			input_method->context->resource, hint, purpose);
-	}
+
+	text_input->pending.content_type.hint = hint;
+	text_input->pending.content_type.purpose = purpose;
 }
 
 static void
-text_input_invoke_action(struct wl_client *client,
-			 struct wl_resource *resource,
-			 uint32_t button,
-			 uint32_t index)
+text_input_commit(struct wl_client *client,
+		  struct wl_resource *resource)
 {
 	struct text_input *text_input = wl_resource_get_user_data(resource);
-	struct input_method *input_method, *next;
-
-	wl_list_for_each_safe(input_method, next,
-			      &text_input->input_methods, link) {
-		if (!input_method->context)
-			continue;
-		zwp_input_method_context_v1_send_invoke_action(
-			input_method->context->resource, button, index);
+	struct weston_compositor *ec = text_input->ec;
+	struct input_method *input_method;
+	bool old_enabled;
+
+	text_input->current_serial++;
+	text_input->current = text_input->pending;
+	if (text_input->pending.surrounding.text)
+		text_input->current.surrounding.text =
+			strdup(text_input->pending.surrounding.text);
+
+	old_enabled = text_input->current_enabled;
+	text_input->current_enabled = text_input->pending_enabled;
+
+	input_method = text_input->input_method;
+	if (!old_enabled && text_input->current_enabled)
+		activate_text_input(text_input);
+	else if (old_enabled && !text_input->current_enabled) {
+		deactivate_text_input(text_input);
 	}
-}
 
-static void
-text_input_commit_state(struct wl_client *client,
-			struct wl_resource *resource,
-			uint32_t serial)
-{
-	struct text_input *text_input = wl_resource_get_user_data(resource);
-	struct input_method *input_method, *next;
-
-	wl_list_for_each_safe(input_method, next,
-			      &text_input->input_methods, link) {
-		if (!input_method->context)
-			continue;
-		zwp_input_method_context_v1_send_commit_state(
-			input_method->context->resource, serial);
+	if (input_method) {
+		if (text_input->current.surrounding.text) {
+			zwp_input_method_v2_send_surrounding_text(
+						input_method->resource,
+						text_input->current.surrounding.text,
+						text_input->current.surrounding.cursor,
+						text_input->current.surrounding.anchor);
+		}
+		zwp_input_method_v2_send_text_change_cause(
+						input_method->resource,
+						text_input->current.text_change_cause);
+		zwp_input_method_v2_send_content_type(
+						input_method->resource,
+						text_input->current.content_type.hint,
+						text_input->current.content_type.purpose);
+		wl_signal_emit(&ec->update_input_panel_signal,
+			       &text_input->current.cursor_rectangle);
+		zwp_input_method_v2_send_done(input_method->resource);
 	}
 }
 
 static void
-text_input_show_input_panel(struct wl_client *client,
-			    struct wl_resource *resource)
+text_input_show_input_panel(struct text_input *text_input)
 {
-	struct text_input *text_input = wl_resource_get_user_data(resource);
 	struct weston_compositor *ec = text_input->ec;
 
 	text_input->input_panel_visible = true;
 
-	if (!wl_list_empty(&text_input->input_methods) &&
+	if (text_input->input_method &&
 	    text_input == text_input->manager->current_text_input) {
 		wl_signal_emit(&ec->show_input_panel_signal,
 			       text_input->surface);
 		wl_signal_emit(&ec->update_input_panel_signal,
-			       &text_input->cursor_rectangle);
+			       &text_input->current.cursor_rectangle);
 	}
 }
 
 static void
-text_input_hide_input_panel(struct wl_client *client,
-			    struct wl_resource *resource)
+text_input_destroy(struct wl_client *client,
+		   struct wl_resource *resource)
 {
-	struct text_input *text_input = wl_resource_get_user_data(resource);
-	struct weston_compositor *ec = text_input->ec;
-
-	text_input->input_panel_visible = false;
-
-	if (!wl_list_empty(&text_input->input_methods) &&
-	    text_input == text_input->manager->current_text_input)
-		wl_signal_emit(&ec->hide_input_panel_signal, ec);
+	wl_resource_destroy(resource);
 }
 
 static void
-text_input_set_preferred_language(struct wl_client *client,
-				  struct wl_resource *resource,
-				  const char *language)
+text_input_set_text_change_cause(struct wl_client *client,
+				 struct wl_resource *resource,
+				 uint32_t cause)
 {
 	struct text_input *text_input = wl_resource_get_user_data(resource);
-	struct input_method *input_method, *next;
-
-	wl_list_for_each_safe(input_method, next,
-			      &text_input->input_methods, link) {
-		if (!input_method->context)
-			continue;
-		zwp_input_method_context_v1_send_preferred_language(
-			input_method->context->resource, language);
-	}
+
+	text_input->pending.text_change_cause = cause;
 }
 
-static const struct zwp_text_input_v1_interface text_input_implementation = {
-	text_input_activate,
-	text_input_deactivate,
-	text_input_show_input_panel,
-	text_input_hide_input_panel,
-	text_input_reset,
+static const struct zwp_text_input_v3_interface text_input_implementation = {
+	text_input_destroy,
+	text_input_enable,
+	text_input_disable,
 	text_input_set_surrounding_text,
+	text_input_set_text_change_cause,
 	text_input_set_content_type,
 	text_input_set_cursor_rectangle,
-	text_input_set_preferred_language,
-	text_input_commit_state,
-	text_input_invoke_action
+	text_input_commit,
 };
 
-static void text_input_manager_create_text_input(struct wl_client *client,
-						 struct wl_resource *resource,
-						 uint32_t id)
+static void
+text_input_manager_destroy(struct wl_client *client,
+			   struct wl_resource *resource)
+{
+	wl_resource_destroy(resource);
+}
+
+static void text_input_manager_get_text_input(struct wl_client *client,
+					      struct wl_resource *resource,
+					      uint32_t id,
+					      struct wl_resource *seat)
 {
 	struct text_input_manager *text_input_manager =
 		wl_resource_get_user_data(resource);
 	struct text_input *text_input;
+	struct weston_seat *weston_seat = wl_resource_get_user_data(seat);
 
 	text_input = zalloc(sizeof *text_input);
 	if (text_input == NULL)
 		return;
 
 	text_input->resource =
-		wl_resource_create(client, &zwp_text_input_v1_interface, 1, id);
+		wl_resource_create(client, &zwp_text_input_v3_interface, 1, id);
 	wl_resource_set_implementation(text_input->resource,
 				       &text_input_implementation,
 				       text_input, destroy_text_input);
 
 	text_input->ec = text_input_manager->ec;
 	text_input->manager = text_input_manager;
+	text_input->seat = weston_seat;
+	text_input->current_serial = 0;
 
-	wl_list_init(&text_input->input_methods);
+	wl_list_insert(&text_input_manager->text_inputs, &text_input->link);
 };
 
-static const struct zwp_text_input_manager_v1_interface manager_implementation = {
-	text_input_manager_create_text_input
+static const struct zwp_text_input_manager_v3_interface text_input_manager_implementation = {
+	text_input_manager_destroy,
+	text_input_manager_get_text_input
 };
 
 static void
@@ -437,10 +491,10 @@  bind_text_input_manager(struct wl_client *client,
 	/* No checking for duplicate binding necessary.  */
 	resource =
 		wl_resource_create(client,
-				   &zwp_text_input_manager_v1_interface, 1, id);
+				   &zwp_text_input_manager_v3_interface, 1, id);
 	if (resource)
 		wl_resource_set_implementation(resource,
-					       &manager_implementation,
+					       &text_input_manager_implementation,
 					       text_input_manager, NULL);
 }
 
@@ -451,6 +505,12 @@  text_input_manager_notifier_destroy(struct wl_listener *listener, void *data)
 		container_of(listener,
 			     struct text_input_manager,
 			     destroy_listener);
+	struct text_input *text_input, *text_input_tmp;
+
+	wl_list_for_each_safe(text_input, text_input_tmp,
+			      &text_input_manager->text_inputs, link) {
+		wl_resource_destroy(text_input->resource);
+	}
 
 	wl_list_remove(&text_input_manager->destroy_listener.link);
 	wl_global_destroy(text_input_manager->text_input_manager_global);
@@ -471,151 +531,113 @@  text_input_manager_create(struct weston_compositor *ec)
 
 	text_input_manager->text_input_manager_global =
 		wl_global_create(ec->wl_display,
-				 &zwp_text_input_manager_v1_interface, 1,
+				 &zwp_text_input_manager_v3_interface, 1,
 				 text_input_manager, bind_text_input_manager);
 
 	text_input_manager->destroy_listener.notify =
 		text_input_manager_notifier_destroy;
 	wl_signal_add(&ec->destroy_signal,
 		      &text_input_manager->destroy_listener);
-}
-
-static void
-input_method_context_destroy(struct wl_client *client,
-			     struct wl_resource *resource)
-{
-	wl_resource_destroy(resource);
-}
-
-static void
-input_method_context_commit_string(struct wl_client *client,
-				   struct wl_resource *resource,
-				   uint32_t serial,
-				   const char *text)
-{
-	struct input_method_context *context =
-		wl_resource_get_user_data(resource);
 
-	if (context->input)
-		zwp_text_input_v1_send_commit_string(context->input->resource,
-						     serial, text);
+	wl_list_init(&text_input_manager->text_inputs);
 }
 
 static void
-input_method_context_preedit_string(struct wl_client *client,
-				    struct wl_resource *resource,
-				    uint32_t serial,
-				    const char *text,
-				    const char *commit)
+input_method_destroy(struct wl_client *client,
+		     struct wl_resource *resource)
 {
-	struct input_method_context *context =
-		wl_resource_get_user_data(resource);
-
-	if (context->input)
-		zwp_text_input_v1_send_preedit_string(context->input->resource,
-						      serial, text, commit);
-}
-
-static void
-input_method_context_preedit_styling(struct wl_client *client,
-				     struct wl_resource *resource,
-				     uint32_t index,
-				     uint32_t length,
-				     uint32_t style)
-{
-	struct input_method_context *context =
-		wl_resource_get_user_data(resource);
-
-	if (context->input)
-		zwp_text_input_v1_send_preedit_styling(context->input->resource,
-						       index, length, style);
+	wl_resource_destroy(resource);
 }
 
 static void
-input_method_context_preedit_cursor(struct wl_client *client,
-				    struct wl_resource *resource,
-				    int32_t cursor)
+input_method_commit_string(struct wl_client *client,
+			   struct wl_resource *resource,
+			   const char *text)
 {
-	struct input_method_context *context =
-		wl_resource_get_user_data(resource);
+	struct input_method *input_method = wl_resource_get_user_data(resource);
 
-	if (context->input)
-		zwp_text_input_v1_send_preedit_cursor(context->input->resource,
-						      cursor);
+	if (input_method->pending.commit_text)
+		free(input_method->pending.commit_text);
+	input_method->pending.commit_text = strdup(text);
 }
 
 static void
-input_method_context_delete_surrounding_text(struct wl_client *client,
-					     struct wl_resource *resource,
-					     int32_t index,
-					     uint32_t length)
+input_method_set_preedit_string(struct wl_client *client,
+				struct wl_resource *resource,
+				const char *text,
+				int32_t cursor_begin,
+				int32_t cursor_end)
 {
-	struct input_method_context *context =
-		wl_resource_get_user_data(resource);
+	struct input_method *input_method = wl_resource_get_user_data(resource);
 
-	if (context->input)
-		zwp_text_input_v1_send_delete_surrounding_text(
-			context->input->resource, index, length);
+	if (input_method->pending.preedit.text)
+		free(input_method->pending.preedit.text);
+	input_method->pending.preedit.text = strdup(text);
+	input_method->pending.preedit.cursor_begin = cursor_begin;
+	input_method->pending.preedit.cursor_end = cursor_end;
 }
 
 static void
-input_method_context_cursor_position(struct wl_client *client,
+input_method_delete_surrounding_text(struct wl_client *client,
 				     struct wl_resource *resource,
-				     int32_t index,
-				     int32_t anchor)
+				     uint32_t before_length,
+				     uint32_t after_length)
 {
-	struct input_method_context *context =
-		wl_resource_get_user_data(resource);
+	struct input_method *input_method = wl_resource_get_user_data(resource);
 
-	if (context->input)
-		zwp_text_input_v1_send_cursor_position(context->input->resource,
-						       index, anchor);
+	input_method->pending.delete.before_length = before_length;
+	input_method->pending.delete.after_length = after_length;
 }
 
 static void
-input_method_context_modifiers_map(struct wl_client *client,
-				   struct wl_resource *resource,
-				   struct wl_array *map)
+input_method_commit(struct wl_client *client,
+		    struct wl_resource *resource,
+		    uint32_t serial)
 {
-	struct input_method_context *context =
-		wl_resource_get_user_data(resource);
+	struct input_method *input_method = wl_resource_get_user_data(resource);
 
-	if (context->input)
-		zwp_text_input_v1_send_modifiers_map(context->input->resource,
-						     map);
-}
+	if (!input_method->input) {
+		return;
+	}
 
-static void
-input_method_context_keysym(struct wl_client *client,
-			    struct wl_resource *resource,
-			    uint32_t serial,
-			    uint32_t time,
-			    uint32_t sym,
-			    uint32_t state,
-			    uint32_t modifiers)
-{
-	struct input_method_context *context =
-		wl_resource_get_user_data(resource);
+	input_method->current = input_method->pending;
+	struct input_method_state default_state = {0};
+	input_method->pending = default_state;
 
-	if (context->input)
-		zwp_text_input_v1_send_keysym(context->input->resource,
-					      serial, time,
-					      sym, state, modifiers);
+	if (input_method->current.preedit.text) {
+		zwp_text_input_v3_send_preedit_string(input_method->input->resource,
+						input_method->current.preedit.text,
+						input_method->current.preedit.cursor_begin,
+						input_method->current.preedit.cursor_end);
+	}
+	if (input_method->current.commit_text) {
+		zwp_text_input_v3_send_commit_string(input_method->input->resource,
+						input_method->current.commit_text);
+	}
+	if (input_method->current.delete.before_length ||
+		input_method->current.delete.after_length) {
+		zwp_text_input_v3_send_delete_surrounding_text(input_method->input->resource,
+						input_method->current.delete.before_length,
+						input_method->current.delete.after_length);
+	}
+	zwp_text_input_v3_send_done(input_method->input->resource,
+						input_method->input->current_serial);
 }
 
 static void
 unbind_keyboard(struct wl_resource *resource)
 {
-	struct input_method_context *context =
+	struct input_method *input_method =
 		wl_resource_get_user_data(resource);
 
-	input_method_context_end_keyboard_grab(context);
-	context->keyboard = NULL;
+	input_method_end_keyboard_grab(input_method);
+	input_method->keyboard = NULL;
 }
 
 static void
 input_method_context_grab_key(struct weston_keyboard_grab *grab,
-			      const struct timespec *time, uint32_t key,
+			      const struct timespec *time,
+			      uint32_t key,
 			      uint32_t state_w)
 {
 	struct weston_keyboard *keyboard = grab->keyboard;
@@ -665,20 +687,20 @@  static const struct weston_keyboard_grab_interface input_method_context_grab = {
 };
 
 static void
-input_method_context_grab_keyboard(struct wl_client *client,
-				   struct wl_resource *resource,
-				   uint32_t id)
+input_method_grab_keyboard(struct wl_client *client,
+			   struct wl_resource *resource,
+			   uint32_t id)
 {
-	struct input_method_context *context =
+	struct input_method *input_method =
 		wl_resource_get_user_data(resource);
 	struct wl_resource *cr;
-	struct weston_seat *seat = context->input_method->seat;
+	struct weston_seat *seat = input_method->seat;
 	struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
 
 	cr = wl_resource_create(client, &wl_keyboard_interface, 1, id);
-	wl_resource_set_implementation(cr, NULL, context, unbind_keyboard);
+	wl_resource_set_implementation(cr, NULL, input_method, unbind_keyboard);
 
-	context->keyboard = cr;
+	input_method->keyboard = cr;
 
 	weston_keyboard_send_keymap(keyboard, cr);
 
@@ -690,16 +712,25 @@  input_method_context_grab_keyboard(struct wl_client *client,
 }
 
 static void
-input_method_context_key(struct wl_client *client,
-			 struct wl_resource *resource,
-			 uint32_t serial,
-			 uint32_t time,
-			 uint32_t key,
-			 uint32_t state_w)
+virtual_keyboard_keymap(struct wl_client *client,
+			struct wl_resource *resource,
+			uint32_t format,
+			int32_t fd,
+			uint32_t size)
 {
-	struct input_method_context *context =
+	weston_log("stub: zwp_virtual_keyboard_v1_interface:keymap\n");
+}
+
+static void
+virtual_keyboard_key(struct wl_client *client,
+		     struct wl_resource *resource,
+		     uint32_t time,
+		     uint32_t key,
+		     uint32_t state_w)
+{
+	struct virtual_keyboard *virtual_keyboard =
 		wl_resource_get_user_data(resource);
-	struct weston_seat *seat = context->input_method->seat;
+	struct weston_seat *seat = virtual_keyboard->seat;
 	struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
 	struct weston_keyboard_grab *default_grab = &keyboard->default_grab;
 	struct timespec ts;
@@ -710,20 +741,20 @@  input_method_context_key(struct wl_client *client,
 }
 
 static void
-input_method_context_modifiers(struct wl_client *client,
-			       struct wl_resource *resource,
-			       uint32_t serial,
-			       uint32_t mods_depressed,
-			       uint32_t mods_latched,
-			       uint32_t mods_locked,
-			       uint32_t group)
+virtual_keyboard_modifiers(struct wl_client *client,
+			   struct wl_resource *resource,
+			   uint32_t mods_depressed,
+			   uint32_t mods_latched,
+			   uint32_t mods_locked,
+			   uint32_t group)
 {
-	struct input_method_context *context =
+	struct virtual_keyboard *virtual_keyboard =
 		wl_resource_get_user_data(resource);
 
-	struct weston_seat *seat = context->input_method->seat;
+	struct weston_seat *seat = virtual_keyboard->seat;
 	struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
 	struct weston_keyboard_grab *default_grab = &keyboard->default_grab;
+	int serial = wl_display_next_serial(virtual_keyboard->ec->wl_display);
 
 	default_grab->interface->modifiers(default_grab,
 					   serial, mods_depressed,
@@ -732,104 +763,12 @@  input_method_context_modifiers(struct wl_client *client,
 }
 
 static void
-input_method_context_language(struct wl_client *client,
-			      struct wl_resource *resource,
-			      uint32_t serial,
-			      const char *language)
-{
-	struct input_method_context *context =
-		wl_resource_get_user_data(resource);
-
-	if (context->input)
-		zwp_text_input_v1_send_language(context->input->resource,
-						serial, language);
-}
-
-static void
-input_method_context_text_direction(struct wl_client *client,
-				    struct wl_resource *resource,
-				    uint32_t serial,
-				    uint32_t direction)
-{
-	struct input_method_context *context =
-		wl_resource_get_user_data(resource);
-
-	if (context->input)
-		zwp_text_input_v1_send_text_direction(context->input->resource,
-						      serial, direction);
-}
-
-
-static const struct zwp_input_method_context_v1_interface context_implementation = {
-	input_method_context_destroy,
-	input_method_context_commit_string,
-	input_method_context_preedit_string,
-	input_method_context_preedit_styling,
-	input_method_context_preedit_cursor,
-	input_method_context_delete_surrounding_text,
-	input_method_context_cursor_position,
-	input_method_context_modifiers_map,
-	input_method_context_keysym,
-	input_method_context_grab_keyboard,
-	input_method_context_key,
-	input_method_context_modifiers,
-	input_method_context_language,
-	input_method_context_text_direction
-};
-
-static void
-destroy_input_method_context(struct wl_resource *resource)
-{
-	struct input_method_context *context =
-		wl_resource_get_user_data(resource);
-
-	if (context->keyboard)
-		wl_resource_destroy(context->keyboard);
-
-	if (context->input_method && context->input_method->context == context)
-		context->input_method->context = NULL;
-
-	free(context);
-}
-
-static void
-input_method_context_create(struct text_input *input,
-			    struct input_method *input_method)
-{
-	struct input_method_context *context;
-	struct wl_resource *binding;
-
-	if (!input_method->input_method_binding)
-		return;
-
-	context = zalloc(sizeof *context);
-	if (context == NULL)
-		return;
-
-	binding = input_method->input_method_binding;
-	context->resource =
-		wl_resource_create(wl_resource_get_client(binding),
-				   &zwp_input_method_context_v1_interface,
-				   1, 0);
-	wl_resource_set_implementation(context->resource,
-				       &context_implementation,
-				       context, destroy_input_method_context);
-
-	context->input = input;
-	context->input_method = input_method;
-	input_method->context = context;
-
-
-	zwp_input_method_v1_send_activate(binding, context->resource);
-}
-
-static void
-input_method_context_end_keyboard_grab(struct input_method_context *context)
+input_method_end_keyboard_grab(struct input_method *input_method)
 {
 	struct weston_keyboard_grab *grab;
 	struct weston_keyboard *keyboard;
 
-	keyboard = weston_seat_get_keyboard(context->input_method->seat);
+	keyboard = weston_seat_get_keyboard(input_method->seat);
 	if (!keyboard)
 		return;
 
@@ -844,64 +783,6 @@  input_method_context_end_keyboard_grab(struct input_method_context *context)
 	keyboard->input_method_resource = NULL;
 }
 
-static void
-unbind_input_method(struct wl_resource *resource)
-{
-	struct input_method *input_method = wl_resource_get_user_data(resource);
-
-	input_method->input_method_binding = NULL;
-	input_method->context = NULL;
-}
-
-static void
-bind_input_method(struct wl_client *client,
-		  void *data,
-		  uint32_t version,
-		  uint32_t id)
-{
-	struct input_method *input_method = data;
-	struct text_backend *text_backend = input_method->text_backend;
-	struct wl_resource *resource;
-
-	resource =
-		wl_resource_create(client,
-				   &zwp_input_method_v1_interface, 1, id);
-
-	if (input_method->input_method_binding != NULL) {
-		wl_resource_post_error(resource,
-				       WL_DISPLAY_ERROR_INVALID_OBJECT,
-				       "interface object already bound");
-		return;
-	}
-
-	if (text_backend->input_method.client != client) {
-		wl_resource_post_error(resource,
-				       WL_DISPLAY_ERROR_INVALID_OBJECT,
-				       "permission to bind "
-				       "input_method denied");
-		return;
-	}
-
-	wl_resource_set_implementation(resource, NULL, input_method,
-				       unbind_input_method);
-	input_method->input_method_binding = resource;
-}
-
-static void
-input_method_notifier_destroy(struct wl_listener *listener, void *data)
-{
-	struct input_method *input_method =
-		container_of(listener, struct input_method, destroy_listener);
-
-	if (input_method->input)
-		deactivate_input_method(input_method);
-
-	wl_global_destroy(input_method->input_method_global);
-	wl_list_remove(&input_method->destroy_listener.link);
-
-	free(input_method);
-}
-
 static void
 handle_keyboard_focus(struct wl_listener *listener, void *data)
 {
@@ -911,11 +792,16 @@  handle_keyboard_focus(struct wl_listener *listener, void *data)
 			     keyboard_focus_listener);
 	struct weston_surface *surface = keyboard->focus;
 
-	if (!input_method->input)
+	if (!input_method->input) {
+		input_method->pending_focused_surface = surface;
 		return;
+	}
 
-	if (!surface || input_method->input->surface != surface)
-		deactivate_input_method(input_method);
+	if (!surface || input_method->input->surface != surface) {
+		deactivate_text_input(input_method->input);
+	}
+
+	input_method->pending_focused_surface = surface;
 }
 
 static void
@@ -1001,43 +887,282 @@  launch_input_method(struct text_backend *text_backend)
 				       &text_backend->client_listener);
 }
 
+static void input_method_get_input_popup_surface(struct wl_client *client,
+						 struct wl_resource *resource,
+						 uint32_t id,
+						 struct wl_resource *surface)
+{
+	weston_log("stub: zwp_input_method_v2_interface:get_input_popup_surface\n");
+}
+
+static const struct zwp_input_method_v2_interface input_method_implementation = {
+	input_method_commit_string,
+	input_method_set_preedit_string,
+	input_method_delete_surrounding_text,
+	input_method_commit,
+	input_method_get_input_popup_surface,
+	input_method_grab_keyboard,
+	input_method_destroy,
+};
+
 static void
-text_backend_seat_created(struct text_backend *text_backend,
-			  struct weston_seat *seat)
+destroy_input_method(struct wl_resource *resource)
 {
+	struct input_method *input_method =
+		wl_resource_get_user_data(resource);
+
+	if (input_method->keyboard)
+		wl_resource_destroy(input_method->keyboard);
+
+	if (input_method->input)
+		deactivate_input_method(input_method);
+
+	if (input_method->pending.commit_text)
+		free(input_method->pending.commit_text);
+	if (input_method->pending.preedit.text)
+		free(input_method->pending.preedit.text);
+	if (input_method->current.commit_text)
+		free(input_method->current.commit_text);
+	if (input_method->current.preedit.text)
+		free(input_method->current.preedit.text);
+
+	wl_list_remove(&input_method->link);
+
+	free(input_method);
+}
+
+static void
+input_method_manager_get_input_method(struct wl_client *client,
+				      struct wl_resource *resource,
+				      struct wl_resource *seat,
+				      uint32_t id)
+{
+	struct input_method_manager *input_method_manager =
+		wl_resource_get_user_data(resource);
+	struct weston_seat *weston_seat = wl_resource_get_user_data(seat);
 	struct input_method *input_method;
-	struct weston_compositor *ec = seat->compositor;
 
 	input_method = zalloc(sizeof *input_method);
 	if (input_method == NULL)
 		return;
 
-	input_method->seat = seat;
+	input_method->resource =
+		wl_resource_create(client, &zwp_input_method_v2_interface, 1, id);
+	wl_resource_set_implementation(input_method->resource,
+				       &input_method_implementation,
+				       input_method, destroy_input_method);
+
+	input_method->seat = weston_seat;
 	input_method->input = NULL;
 	input_method->focus_listener_initialized = false;
-	input_method->context = NULL;
-	input_method->text_backend = text_backend;
+	input_method->manager = input_method_manager;
+	input_method->pending_focused_surface = NULL;
+
+	weston_seat->input_method = input_method;
+
+	input_method_init_seat(weston_seat);
+
+	wl_list_insert(&input_method_manager->input_methods, &input_method->link);
+};
+
+static void
+input_method_manager_destroy(struct wl_client *client,
+			     struct wl_resource *resource)
+{
+	wl_resource_destroy(resource);
+}
+
+static const struct zwp_input_method_manager_v2_interface input_method_manager_implementation = {
+	input_method_manager_get_input_method,
+	input_method_manager_destroy
+};
+
+static void
+bind_input_method_manager(struct wl_client *client,
+			  void *data,
+			  uint32_t version,
+			  uint32_t id)
+{
+	struct input_method_manager *input_method_manager = data;
+	struct wl_resource *resource;
+
+	resource =
+		wl_resource_create(client,
+				   &zwp_input_method_manager_v2_interface, 1, id);
+	if (resource)
+		wl_resource_set_implementation(resource,
+					       &input_method_manager_implementation,
+					       input_method_manager, NULL);
+}
+
+static void
+input_method_manager_notifier_destroy(struct wl_listener *listener, void *data)
+{
+	struct input_method_manager *input_method_manager =
+		container_of(listener,
+			     struct input_method_manager,
+			     destroy_listener);
+	struct input_method *input_method, *input_method_tmp;
+
+	wl_list_for_each_safe(input_method, input_method_tmp,
+			      &input_method_manager->input_methods, link) {
+		wl_resource_destroy(input_method->resource);
+	}
+
+	wl_list_remove(&input_method_manager->destroy_listener.link);
+	wl_global_destroy(input_method_manager->input_method_manager_global);
+
+	free(input_method_manager);
+}
 
-	input_method->input_method_global =
+static void
+input_method_manager_create(struct weston_compositor *ec)
+{
+	struct input_method_manager *input_method_manager;
+
+	input_method_manager = zalloc(sizeof *input_method_manager);
+	if (input_method_manager == NULL)
+		return;
+
+	input_method_manager->ec = ec;
+
+	input_method_manager->input_method_manager_global =
 		wl_global_create(ec->wl_display,
-				 &zwp_input_method_v1_interface, 1,
-				 input_method, bind_input_method);
+				 &zwp_input_method_manager_v2_interface, 1,
+				 input_method_manager, bind_input_method_manager);
 
-	input_method->destroy_listener.notify = input_method_notifier_destroy;
-	wl_signal_add(&seat->destroy_signal, &input_method->destroy_listener);
+	input_method_manager->destroy_listener.notify =
+		input_method_manager_notifier_destroy;
+	wl_signal_add(&ec->destroy_signal,
+		      &input_method_manager->destroy_listener);
+
+	wl_list_init(&input_method_manager->input_methods);
+}
 
-	seat->input_method = input_method;
+static void
+virtual_keyboard_destroy(struct wl_client *client,
+			 struct wl_resource *resource)
+{
+	wl_resource_destroy(resource);
 }
 
+static const struct zwp_virtual_keyboard_v1_interface virtual_keyboard_implementation = {
+	virtual_keyboard_keymap,
+	virtual_keyboard_key,
+	virtual_keyboard_modifiers,
+	virtual_keyboard_destroy,
+};
+
 static void
-handle_seat_created(struct wl_listener *listener, void *data)
+destroy_virtual_keyboard(struct wl_resource *resource)
 {
-	struct weston_seat *seat = data;
-	struct text_backend *text_backend =
-		container_of(listener, struct text_backend,
-			     seat_created_listener);
+	struct virtual_keyboard *virtual_keyboard =
+		wl_resource_get_user_data(resource);
 
-	text_backend_seat_created(text_backend, seat);
+	wl_list_remove(&virtual_keyboard->link);
+
+	free(virtual_keyboard);
+}
+
+static void
+virtual_keyboard_manager_create_virtual_keyboard(struct wl_client *client,
+						 struct wl_resource *resource,
+						 struct wl_resource *seat,
+						 uint32_t id)
+{
+	struct virtual_keyboard_manager *virtual_keyboard_manager =
+		wl_resource_get_user_data(resource);
+	struct weston_seat *weston_seat = wl_resource_get_user_data(seat);
+	struct virtual_keyboard *virtual_keyboard;
+
+	virtual_keyboard = zalloc(sizeof *virtual_keyboard);
+	if (virtual_keyboard == NULL)
+		return;
+
+	virtual_keyboard->resource =
+		wl_resource_create(client, &zwp_virtual_keyboard_v1_interface, 1, id);
+	wl_resource_set_implementation(virtual_keyboard->resource,
+				       &virtual_keyboard_implementation,
+				       virtual_keyboard, destroy_virtual_keyboard);
+
+	virtual_keyboard->seat = weston_seat;
+	virtual_keyboard->manager = virtual_keyboard_manager;
+
+	wl_list_insert(&virtual_keyboard_manager->virtual_keyboards, &virtual_keyboard->link);
+};
+
+static void
+virtual_keyboard_manager_destroy(struct wl_client *client,
+				 struct wl_resource *resource)
+{
+	wl_resource_destroy(resource);
+}
+
+static const struct zwp_virtual_keyboard_manager_v1_interface virtual_keyboard_manager_implementation = {
+	virtual_keyboard_manager_create_virtual_keyboard,
+	virtual_keyboard_manager_destroy,
+};
+
+static void
+bind_virtual_keyboard_manager(struct wl_client *client,
+			      void *data,
+			      uint32_t version,
+			      uint32_t id)
+{
+	struct virtual_keyboard_manager *virtual_keyboard_manager = data;
+	struct wl_resource *resource;
+
+	resource =
+		wl_resource_create(client,
+				   &zwp_virtual_keyboard_manager_v1_interface, 1, id);
+	if (resource)
+		wl_resource_set_implementation(resource,
+					       &virtual_keyboard_manager_implementation,
+					       virtual_keyboard_manager, NULL);
+}
+
+static void
+virtual_keyboard_manager_notifier_destroy(struct wl_listener *listener, void *data)
+{
+	struct virtual_keyboard_manager *virtual_keyboard_manager =
+		container_of(listener,
+			     struct virtual_keyboard_manager,
+			     destroy_listener);
+	struct virtual_keyboard *virtual_keyboard, *virtual_keyboard_tmp;
+
+	wl_list_for_each_safe(virtual_keyboard, virtual_keyboard_tmp,
+			      &virtual_keyboard_manager->virtual_keyboards, link) {
+		wl_resource_destroy(virtual_keyboard->resource);
+	}
+
+	wl_list_remove(&virtual_keyboard_manager->destroy_listener.link);
+	wl_global_destroy(virtual_keyboard_manager->virtual_keyboard_manager_global);
+
+	free(virtual_keyboard_manager);
+}
+
+static void
+virtual_keyboard_manager_create(struct weston_compositor *ec)
+{
+	struct virtual_keyboard_manager *virtual_keyboard_manager;
+
+	virtual_keyboard_manager = zalloc(sizeof *virtual_keyboard_manager);
+	if (virtual_keyboard_manager == NULL)
+		return;
+
+	virtual_keyboard_manager->ec = ec;
+
+	virtual_keyboard_manager->virtual_keyboard_manager_global =
+		wl_global_create(ec->wl_display,
+				 &zwp_virtual_keyboard_manager_v1_interface, 1,
+				 virtual_keyboard_manager, bind_virtual_keyboard_manager);
+
+	virtual_keyboard_manager->destroy_listener.notify =
+		virtual_keyboard_manager_notifier_destroy;
+	wl_signal_add(&ec->destroy_signal,
+		      &virtual_keyboard_manager->destroy_listener);
+
+	wl_list_init(&virtual_keyboard_manager->virtual_keyboards);
 }
 
 static void
@@ -1059,8 +1184,6 @@  text_backend_configuration(struct text_backend *text_backend)
 WL_EXPORT void
 text_backend_destroy(struct text_backend *text_backend)
 {
-	wl_list_remove(&text_backend->seat_created_listener.link);
-
 	if (text_backend->input_method.client) {
 		/* disable respawn */
 		wl_list_remove(&text_backend->client_listener.link);
@@ -1075,7 +1198,6 @@  WL_EXPORT struct text_backend *
 text_backend_init(struct weston_compositor *ec)
 {
 	struct text_backend *text_backend;
-	struct weston_seat *seat;
 
 	text_backend = zalloc(sizeof(*text_backend));
 	if (text_backend == NULL)
@@ -1085,13 +1207,9 @@  text_backend_init(struct weston_compositor *ec)
 
 	text_backend_configuration(text_backend);
 
-	wl_list_for_each(seat, &ec->seat_list, link)
-		text_backend_seat_created(text_backend, seat);
-	text_backend->seat_created_listener.notify = handle_seat_created;
-	wl_signal_add(&ec->seat_created_signal,
-		      &text_backend->seat_created_listener);
-
+	input_method_manager_create(ec);
 	text_input_manager_create(ec);
+	virtual_keyboard_manager_create(ec);
 
 	launch_input_method(text_backend);
 
diff --git a/desktop-shell/input-panel.c b/desktop-shell/input-panel.c
index 8292f20a..f7a60809 100644
--- a/desktop-shell/input-panel.c
+++ b/desktop-shell/input-panel.c
@@ -31,7 +31,7 @@ 
 #include <string.h>
 
 #include "shell.h"
-#include "input-method-unstable-v1-server-protocol.h"
+#include "weston-input-panel-server-protocol.h"
 #include "shared/helpers.h"
 
 struct input_panel_surface {
@@ -294,7 +294,7 @@  input_panel_surface_set_overlay_panel(struct wl_client *client,
 	input_panel_surface->panel = 1;
 }
 
-static const struct zwp_input_panel_surface_v1_interface input_panel_surface_implementation = {
+static const struct weston_input_panel_surface_interface input_panel_surface_implementation = {
 	input_panel_surface_set_toplevel,
 	input_panel_surface_set_overlay_panel
 };
@@ -336,7 +336,7 @@  input_panel_get_input_panel_surface(struct wl_client *client,
 
 	ipsurf->resource =
 		wl_resource_create(client,
-				   &zwp_input_panel_surface_v1_interface,
+				   &weston_input_panel_surface_interface,
 				   1,
 				   id);
 	wl_resource_set_implementation(ipsurf->resource,
@@ -345,7 +345,7 @@  input_panel_get_input_panel_surface(struct wl_client *client,
 				       destroy_input_panel_surface_resource);
 }
 
-static const struct zwp_input_panel_v1_interface input_panel_implementation = {
+static const struct weston_input_panel_interface input_panel_implementation = {
 	input_panel_get_input_panel_surface
 };
 
@@ -365,7 +365,7 @@  bind_input_panel(struct wl_client *client,
 	struct wl_resource *resource;
 
 	resource = wl_resource_create(client,
-				      &zwp_input_panel_v1_interface, 1, id);
+				      &weston_input_panel_interface, 1, id);
 
 	if (shell->input_panel.binding == NULL) {
 		wl_resource_set_implementation(resource,
@@ -404,7 +404,7 @@  input_panel_setup(struct desktop_shell *shell)
 	wl_list_init(&shell->input_panel.surfaces);
 
 	if (wl_global_create(shell->compositor->wl_display,
-			     &zwp_input_panel_v1_interface, 1,
+			     &weston_input_panel_interface, 1,
 			     shell, bind_input_panel) == NULL)
 		return -1;
 
diff --git a/desktop-shell/meson.build b/desktop-shell/meson.build
index 07013437..a785648b 100644
--- a/desktop-shell/meson.build
+++ b/desktop-shell/meson.build
@@ -8,8 +8,8 @@  if get_option('shell-desktop')
 		'../shared/matrix.c',
 		weston_desktop_shell_server_protocol_h,
 		weston_desktop_shell_protocol_c,
-		input_method_unstable_v1_server_protocol_h,
-		input_method_unstable_v1_protocol_c,
+		weston_input_panel_server_protocol_h,
+		weston_input_panel_protocol_c,
 	]
 	deps_shell_desktop = [
 		dep_libshared,
diff --git a/protocol/meson.build b/protocol/meson.build
index 34026ff9..b9c02376 100644
--- a/protocol/meson.build
+++ b/protocol/meson.build
@@ -16,6 +16,7 @@  install_data(
 
 generated_protocols = [
 	[ 'input-method', 'v1' ],
+	[ 'input-method', 'v2' ],
 	[ 'input-timestamps', 'v1' ],
 	[ 'ivi-application', 'internal' ],
 	[ 'ivi-hmi-controller', 'internal' ],
@@ -28,9 +29,12 @@  generated_protocols = [
 	[ 'tablet', 'v2' ],
 	[ 'text-cursor-position', 'internal' ],
 	[ 'text-input', 'v1' ],
+	[ 'text-input', 'v3' ],
 	[ 'viewporter', 'stable' ],
+	[ 'virtual-keyboard', 'v1' ],
 	[ 'weston-debug', 'internal' ],
 	[ 'weston-desktop-shell', 'internal' ],
+	[ 'weston-input-panel', 'internal' ],
 	[ 'weston-screenshooter', 'internal' ],
 	[ 'weston-test', 'internal' ],
 	[ 'weston-touch-calibration', 'internal' ],
diff --git a/protocol/weston-input-panel.xml b/protocol/weston-input-panel.xml
new file mode 100644
index 00000000..9a68a441
--- /dev/null
+++ b/protocol/weston-input-panel.xml
@@ -0,0 +1,63 @@ 
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="weston_input_panel">
+
+  <copyright>
+    Copyright © 2012, 2013 Intel Corporation
+
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the "Software"),
+    to deal in the Software without restriction, including without limitation
+    the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice (including the next
+    paragraph) shall be included in all copies or substantial portions of the
+    Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
+  </copyright>
+
+  <interface name="weston_input_panel" version="1">
+    <description summary="interface for implementing keyboards">
+      Only one client can bind this interface at a time.
+    </description>
+
+    <request name="get_input_panel_surface">
+      <arg name="id" type="new_id" interface="weston_input_panel_surface"/>
+      <arg name="surface" type="object" interface="wl_surface"/>
+    </request>
+  </interface>
+
+  <interface name="weston_input_panel_surface" version="1">
+    <enum name="position">
+      <entry name="center_bottom" value="0"/>
+    </enum>
+
+    <request name="set_toplevel">
+      <description summary="set the surface type as a keyboard">
+	Set the input_panel_surface type to keyboard.
+
+	A keyboard surface is only shown when a text input is active.
+      </description>
+      <arg name="output" type="object" interface="wl_output"/>
+      <arg name="position" type="uint"/>
+    </request>
+
+    <request name="set_overlay_panel">
+      <description summary="set the surface type as an overlay panel">
+	Set the input_panel_surface to be an overlay panel.
+
+	This is shown near the input cursor above the application window when
+	a text input is active.
+      </description>
+    </request>
+  </interface>
+
+</protocol>
diff --git a/tests/meson.build b/tests/meson.build
index 03692f47..ac65183d 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -156,8 +156,10 @@  tests_weston = [
 	[
 		'text',
 		[
-			text_input_unstable_v1_client_protocol_h,
-			text_input_unstable_v1_protocol_c,
+			text_input_unstable_v3_client_protocol_h,
+			text_input_unstable_v3_protocol_c,
+			input_method_unstable_v2_client_protocol_h,
+			input_method_unstable_v2_protocol_c,
 		]
 	],
 	[
diff --git a/tests/text-test.c b/tests/text-test.c
index 685a28dc..079a5d8e 100644
--- a/tests/text-test.c
+++ b/tests/text-test.c
@@ -30,7 +30,8 @@ 
 #include <stdio.h>
 
 #include "weston-test-client-helper.h"
-#include "text-input-unstable-v1-client-protocol.h"
+#include "text-input-unstable-v3-client-protocol.h"
+#include "input-method-unstable-v2-client-protocol.h"
 
 struct text_input_state {
 	int activated;
@@ -39,74 +40,38 @@  struct text_input_state {
 
 static void
 text_input_commit_string(void *data,
-			 struct zwp_text_input_v1 *text_input,
-			 uint32_t serial,
+			 struct zwp_text_input_v3 *text_input,
 			 const char *text)
 {
 }
 
 static void
 text_input_preedit_string(void *data,
-			  struct zwp_text_input_v1 *text_input,
-			  uint32_t serial,
+			  struct zwp_text_input_v3 *text_input,
 			  const char *text,
-			  const char *commit)
+			  int32_t cursor_begin,
+			  int32_t cursor_end)
 {
 }
 
 static void
 text_input_delete_surrounding_text(void *data,
-				   struct zwp_text_input_v1 *text_input,
-				   int32_t index,
-				   uint32_t length)
+				   struct zwp_text_input_v3 *text_input,
+				   uint32_t before_length,
+				   uint32_t after_length)
 {
 }
 
 static void
-text_input_cursor_position(void *data,
-			   struct zwp_text_input_v1 *text_input,
-			   int32_t index,
-			   int32_t anchor)
-{
-}
-
-static void
-text_input_preedit_styling(void *data,
-			   struct zwp_text_input_v1 *text_input,
-			   uint32_t index,
-			   uint32_t length,
-			   uint32_t style)
-{
-}
-
-static void
-text_input_preedit_cursor(void *data,
-			  struct zwp_text_input_v1 *text_input,
-			  int32_t index)
-{
-}
-
-static void
-text_input_modifiers_map(void *data,
-			 struct zwp_text_input_v1 *text_input,
-			 struct wl_array *map)
-{
-}
-
-static void
-text_input_keysym(void *data,
-		  struct zwp_text_input_v1 *text_input,
-		  uint32_t serial,
-		  uint32_t time,
-		  uint32_t sym,
-		  uint32_t state,
-		  uint32_t modifiers)
+text_input_done(void *data,
+		struct zwp_text_input_v3 *zwp_text_input_v3,
+		uint32_t serial)
 {
 }
 
 static void
 text_input_enter(void *data,
-		 struct zwp_text_input_v1 *text_input,
+		 struct zwp_text_input_v3 *text_input,
 		 struct wl_surface *surface)
 
 {
@@ -119,76 +84,69 @@  text_input_enter(void *data,
 
 static void
 text_input_leave(void *data,
-		 struct zwp_text_input_v1 *text_input)
+		 struct zwp_text_input_v3 *text_input,
+		 struct wl_surface *surface)
 {
 	struct text_input_state *state = data;
 
 	state->deactivated += 1;
 }
 
-static void
-text_input_input_panel_state(void *data,
-			     struct zwp_text_input_v1 *text_input,
-			     uint32_t state)
-{
-}
-
-static void
-text_input_language(void *data,
-		    struct zwp_text_input_v1 *text_input,
-		    uint32_t serial,
-		    const char *language)
-{
-}
-
-static void
-text_input_text_direction(void *data,
-			  struct zwp_text_input_v1 *text_input,
-			  uint32_t serial,
-			  uint32_t direction)
-{
-}
-
-static const struct zwp_text_input_v1_listener text_input_listener = {
+static const struct zwp_text_input_v3_listener text_input_listener = {
 	text_input_enter,
 	text_input_leave,
-	text_input_modifiers_map,
-	text_input_input_panel_state,
 	text_input_preedit_string,
-	text_input_preedit_styling,
-	text_input_preedit_cursor,
 	text_input_commit_string,
-	text_input_cursor_position,
 	text_input_delete_surrounding_text,
-	text_input_keysym,
-	text_input_language,
-	text_input_text_direction
+	text_input_done,
 };
 
 TEST(text_test)
 {
 	struct client *client;
 	struct global *global;
-	struct zwp_text_input_manager_v1 *factory;
-	struct zwp_text_input_v1 *text_input;
+	struct zwp_input_method_manager_v2 *input_method_factory;
+	struct zwp_input_method_v2 *input_method;
+	struct zwp_text_input_manager_v3 *text_input_factory;
+	struct zwp_text_input_v3 *text_input;
 	struct text_input_state state;
 
 	client = create_client_and_test_surface(100, 100, 100, 100);
 	assert(client);
 
-	factory = NULL;
+	input_method_factory = NULL;
+	text_input_factory = NULL;
 	wl_list_for_each(global, &client->global_list, link) {
-		if (strcmp(global->interface, "zwp_text_input_manager_v1") == 0)
-			factory = wl_registry_bind(client->wl_registry,
-						   global->name,
-						   &zwp_text_input_manager_v1_interface, 1);
+		if (strcmp(global->interface, "zwp_input_method_manager_v2") == 0) {
+			input_method_factory = wl_registry_bind(client->wl_registry,
+								global->name,
+								&zwp_input_method_manager_v2_interface, 1);
+		}
+		else if (strcmp(global->interface, "zwp_text_input_manager_v3") == 0) {
+			text_input_factory = wl_registry_bind(client->wl_registry,
+							      global->name,
+							      &zwp_text_input_manager_v3_interface, 1);
+		}
 	}
 
-	assert(factory);
+	assert(input_method_factory);
+	assert(text_input_factory);
 
 	memset(&state, 0, sizeof state);
-	text_input = zwp_text_input_manager_v1_create_text_input(factory);
-	zwp_text_input_v1_add_listener(text_input,
+
+	/* Initialize input method for seat.
+	 * text-input will only receive enter/leave events if there is
+	 * an input method available.
+	 */
+	input_method = zwp_input_method_manager_v2_get_input_method(input_method_factory,
+								    client->input->wl_seat);
+	assert(input_method);
+
+	/* Initialize text input for seat. */
+	text_input = zwp_text_input_manager_v3_get_text_input(text_input_factory,
+							      client->input->wl_seat);
+	assert(text_input);
+	zwp_text_input_v3_add_listener(text_input,
 				       &text_input_listener,
 				       &state);
 
@@ -199,19 +157,20 @@  TEST(text_test)
 	assert(client->input->keyboard->focus == client->surface);
 
 	/* Activate test model and make sure we get enter event. */
-	zwp_text_input_v1_activate(text_input, client->input->wl_seat,
-				   client->surface->wl_surface);
+	zwp_text_input_v3_enable(text_input);
+	zwp_text_input_v3_commit(text_input);
 	client_roundtrip(client);
 	assert(state.activated == 1 && state.deactivated == 0);
 
 	/* Deactivate test model and make sure we get leave event. */
-	zwp_text_input_v1_deactivate(text_input, client->input->wl_seat);
+	zwp_text_input_v3_disable(text_input);
+	zwp_text_input_v3_commit(text_input);
 	client_roundtrip(client);
 	assert(state.activated == 1 && state.deactivated == 1);
 
 	/* Activate test model again. */
-	zwp_text_input_v1_activate(text_input, client->input->wl_seat,
-				   client->surface->wl_surface);
+	zwp_text_input_v3_enable(text_input);
+	zwp_text_input_v3_commit(text_input);
 	client_roundtrip(client);
 	assert(state.activated == 2 && state.deactivated == 1);