From 85e5b160ee7dbeec474c3f6877ddbd5310f0fbe8 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Mon, 13 Aug 2018 18:59:02 +0200 Subject: [PATCH] wayland: Implement text-input from wayland-protocols This protocol supersedes the internal gtk_text_input protocol that was in place. Functionally it is very similar, with just some more verbosity in both ways (text_change_cause, .done event), and some improvements wrt the pre-edit text styling. --- .gitignore | 2 + configure.ac | 2 +- src/Makefile.am | 4 + src/wayland/meta-wayland-seat.c | 6 + src/wayland/meta-wayland-seat.h | 2 + src/wayland/meta-wayland-text-input.c | 682 ++++++++++++++++++++++++++ src/wayland/meta-wayland-text-input.h | 46 ++ src/wayland/meta-wayland-versions.h | 1 + src/wayland/meta-wayland.c | 1 + 9 files changed, 745 insertions(+), 1 deletion(-) create mode 100644 src/wayland/meta-wayland-text-input.c create mode 100644 src/wayland/meta-wayland-text-input.h diff --git a/.gitignore b/.gitignore index 7836c1c85..f75f852f9 100644 --- a/.gitignore +++ b/.gitignore @@ -94,6 +94,8 @@ src/xwayland-keyboard-grab-unstable-v1-protocol.c src/xwayland-keyboard-grab-unstable-v1-server-protocol.h src/tablet-unstable-v*-protocol.c src/tablet-unstable-v*-server-protocol.h +src/text-input-unstable-v*-protocol.c +src/text-input-unstable-v*-server-protocol.h src/keyboard-shortcuts-inhibit-unstable-v*-protocol.c src/keyboard-shortcuts-inhibit-unstable-v*-server-protocol.h src/linux-dmabuf-unstable-v*-protocol.c diff --git a/configure.ac b/configure.ac index a404fa232..b39fccd26 100644 --- a/configure.ac +++ b/configure.ac @@ -325,7 +325,7 @@ AS_IF([test "$have_wayland" = "yes"], [ AC_SUBST([WAYLAND_SCANNER]) AC_DEFINE([HAVE_WAYLAND],[1],[Define if you want to enable Wayland support]) - PKG_CHECK_MODULES(WAYLAND_PROTOCOLS, [wayland-protocols >= 1.12], + PKG_CHECK_MODULES(WAYLAND_PROTOCOLS, [wayland-protocols >= 1.16], [ac_wayland_protocols_pkgdatadir=`$PKG_CONFIG --variable=pkgdatadir wayland-protocols`]) AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, $ac_wayland_protocols_pkgdatadir) ]) diff --git a/src/Makefile.am b/src/Makefile.am index 1824f8b38..811e2b86b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -88,6 +88,8 @@ mutter_built_sources += \ xdg-output-unstable-v1-server-protocol.h \ xwayland-keyboard-grab-unstable-v1-protocol.c \ xwayland-keyboard-grab-unstable-v1-server-protocol.h \ + text-input-unstable-v3-protocol.c \ + text-input-unstable-v3-server-protocol.h \ gtk-text-input-protocol.c \ gtk-text-input-server-protocol.h \ $(NULL) @@ -461,6 +463,8 @@ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES += \ wayland/meta-wayland-shell-surface.h \ wayland/meta-wayland-text-input-legacy.c \ wayland/meta-wayland-text-input-legacy.h \ + wayland/meta-wayland-text-input.c \ + wayland/meta-wayland-text-input.h \ wayland/meta-wayland-types.h \ wayland/meta-wayland-versions.h \ wayland/meta-wayland-outputs.c \ diff --git a/src/wayland/meta-wayland-seat.c b/src/wayland/meta-wayland-seat.c index b676d7e17..42af635c1 100644 --- a/src/wayland/meta-wayland-seat.c +++ b/src/wayland/meta-wayland-seat.c @@ -225,6 +225,7 @@ meta_wayland_seat_new (MetaWaylandCompositor *compositor, "seat", seat, NULL); + seat->text_input = meta_wayland_text_input_new (seat); seat->gtk_text_input = meta_wayland_gtk_text_input_new (seat); meta_wayland_data_device_init (&seat->data_device); @@ -263,6 +264,7 @@ meta_wayland_seat_free (MetaWaylandSeat *seat) g_object_unref (seat->keyboard); g_object_unref (seat->touch); meta_wayland_gtk_text_input_destroy (seat->gtk_text_input); + meta_wayland_text_input_destroy (seat->text_input); g_slice_free (MetaWaylandSeat, seat); } @@ -382,6 +384,9 @@ meta_wayland_seat_handle_event (MetaWaylandSeat *seat, break; case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: + if (meta_wayland_text_input_handle_event (seat->text_input, event)) + return TRUE; + if (meta_wayland_gtk_text_input_handle_event (seat->gtk_text_input, event)) return TRUE; @@ -429,6 +434,7 @@ meta_wayland_seat_set_input_focus (MetaWaylandSeat *seat, tablet_seat = meta_wayland_tablet_manager_ensure_seat (compositor->tablet_manager, seat); meta_wayland_tablet_seat_set_pad_focus (tablet_seat, surface); + meta_wayland_text_input_set_focus (seat->text_input, surface); meta_wayland_gtk_text_input_set_focus (seat->gtk_text_input, surface); } diff --git a/src/wayland/meta-wayland-seat.h b/src/wayland/meta-wayland-seat.h index 970363195..b0fc9bce5 100644 --- a/src/wayland/meta-wayland-seat.h +++ b/src/wayland/meta-wayland-seat.h @@ -32,6 +32,7 @@ #include "meta-wayland-touch.h" #include "meta-wayland-data-device.h" #include "meta-wayland-tablet-tool.h" +#include "meta-wayland-text-input.h" #include "meta-wayland-text-input-legacy.h" struct _MetaWaylandSeat @@ -46,6 +47,7 @@ struct _MetaWaylandSeat MetaWaylandDataDevice data_device; MetaWaylandGtkTextInput *gtk_text_input; + MetaWaylandTextInput *text_input; guint capabilities; }; diff --git a/src/wayland/meta-wayland-text-input.c b/src/wayland/meta-wayland-text-input.c new file mode 100644 index 000000000..868143021 --- /dev/null +++ b/src/wayland/meta-wayland-text-input.c @@ -0,0 +1,682 @@ +/* + * Copyright (C) 2017, 2018 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Carlos Garnacho + */ + +#include "config.h" + +#include + +#include "text-input-unstable-v3-server-protocol.h" +#include "wayland/meta-wayland-private.h" +#include "wayland/meta-wayland-seat.h" +#include "wayland/meta-wayland-text-input.h" +#include "wayland/meta-wayland-versions.h" + +#define META_TYPE_WAYLAND_TEXT_INPUT_FOCUS (meta_wayland_text_input_focus_get_type ()) + +typedef enum +{ + META_WAYLAND_PENDING_STATE_NONE = 0, + META_WAYLAND_PENDING_STATE_INPUT_RECT = 1 << 0, + META_WAYLAND_PENDING_STATE_CONTENT_TYPE = 1 << 1, + META_WAYLAND_PENDING_STATE_SURROUNDING_TEXT = 1 << 2, + META_WAYLAND_PENDING_STATE_CHANGE_CAUSE = 1 << 3, + META_WAYLAND_PENDING_STATE_ENABLED = 1 << 4, +} MetaWaylandTextInputPendingState; + +typedef struct _MetaWaylandTextInput MetaWaylandTextInput; + +struct _MetaWaylandTextInput +{ + MetaWaylandSeat *seat; + ClutterInputFocus *input_focus; + + struct wl_list resource_list; + struct wl_list focus_resource_list; + MetaWaylandSurface *surface; + struct wl_listener surface_listener; + + MetaWaylandTextInputPendingState pending_state; + + GHashTable *resource_serials; + + struct + { + char *text; + uint32_t cursor; + uint32_t anchor; + } surrounding; + + cairo_rectangle_int_t cursor_rect; + + uint32_t content_type_hint; + uint32_t content_type_purpose; + uint32_t text_change_cause; + gboolean enabled; +}; + +struct _MetaWaylandTextInputFocus +{ + ClutterInputFocus parent_instance; + MetaWaylandTextInput *text_input; +}; + +G_DECLARE_FINAL_TYPE (MetaWaylandTextInputFocus, meta_wayland_text_input_focus, + META, WAYLAND_TEXT_INPUT_FOCUS, ClutterInputFocus) +G_DEFINE_TYPE (MetaWaylandTextInputFocus, meta_wayland_text_input_focus, + CLUTTER_TYPE_INPUT_FOCUS) + +static void +meta_wayland_text_input_focus_request_surrounding (ClutterInputFocus *focus) +{ + MetaWaylandTextInput *text_input; + + text_input = META_WAYLAND_TEXT_INPUT_FOCUS (focus)->text_input; + clutter_input_focus_set_surrounding (focus, + text_input->surrounding.text, + text_input->surrounding.cursor, + text_input->surrounding.anchor); +} + +static uint32_t +lookup_serial (MetaWaylandTextInput *text_input, + struct wl_resource *resource) +{ + return GPOINTER_TO_UINT (g_hash_table_lookup (text_input->resource_serials, + resource)); +} + +static void +increment_serial (MetaWaylandTextInput *text_input, + struct wl_resource *resource) +{ + uint32_t serial; + + serial = lookup_serial (text_input, resource); + g_hash_table_insert (text_input->resource_serials, resource, + GUINT_TO_POINTER (serial + 1)); +} + +static void +meta_wayland_text_input_focus_delete_surrounding (ClutterInputFocus *focus, + guint cursor, + guint len) +{ + MetaWaylandTextInput *text_input; + struct wl_resource *resource; + + text_input = META_WAYLAND_TEXT_INPUT_FOCUS (focus)->text_input; + + wl_resource_for_each (resource, &text_input->focus_resource_list) + { + zwp_text_input_v3_send_delete_surrounding_text (resource, cursor, len); + zwp_text_input_v3_send_done (resource, + lookup_serial (text_input, resource)); + } +} + +static void +meta_wayland_text_input_focus_commit_text (ClutterInputFocus *focus, + const gchar *text) +{ + MetaWaylandTextInput *text_input; + struct wl_resource *resource; + + text_input = META_WAYLAND_TEXT_INPUT_FOCUS (focus)->text_input; + + wl_resource_for_each (resource, &text_input->focus_resource_list) + { + zwp_text_input_v3_send_preedit_string (resource, NULL, 0, 0); + zwp_text_input_v3_send_commit_string (resource, text); + zwp_text_input_v3_send_done (resource, + lookup_serial (text_input, resource)); + } +} + +static void +meta_wayland_text_input_focus_set_preedit_text (ClutterInputFocus *focus, + const gchar *text, + guint cursor) +{ + MetaWaylandTextInput *text_input; + struct wl_resource *resource; + + text_input = META_WAYLAND_TEXT_INPUT_FOCUS (focus)->text_input; + + wl_resource_for_each (resource, &text_input->focus_resource_list) + { + zwp_text_input_v3_send_preedit_string (resource, text, cursor, cursor); + zwp_text_input_v3_send_done (resource, + lookup_serial (text_input, resource)); + } +} + +static void +meta_wayland_text_input_focus_class_init (MetaWaylandTextInputFocusClass *klass) +{ + ClutterInputFocusClass *focus_class = CLUTTER_INPUT_FOCUS_CLASS (klass); + + focus_class->request_surrounding = meta_wayland_text_input_focus_request_surrounding; + focus_class->delete_surrounding = meta_wayland_text_input_focus_delete_surrounding; + focus_class->commit_text = meta_wayland_text_input_focus_commit_text; + focus_class->set_preedit_text = meta_wayland_text_input_focus_set_preedit_text; +} + +static void +meta_wayland_text_input_focus_init (MetaWaylandTextInputFocus *focus) +{ +} + +static ClutterInputFocus * +meta_wayland_text_input_focus_new (MetaWaylandTextInput *text_input) +{ + MetaWaylandTextInputFocus *focus; + + focus = g_object_new (META_TYPE_WAYLAND_TEXT_INPUT_FOCUS, NULL); + focus->text_input = text_input; + + return CLUTTER_INPUT_FOCUS (focus); +} + +static void +text_input_handle_focus_surface_destroy (struct wl_listener *listener, + void *data) +{ + MetaWaylandTextInput *text_input = wl_container_of (listener, text_input, + surface_listener); + + meta_wayland_text_input_set_focus (text_input, NULL); +} + +static void +move_resources (struct wl_list *destination, struct wl_list *source) +{ + wl_list_insert_list (destination, source); + wl_list_init (source); +} + +static void +move_resources_for_client (struct wl_list *destination, + struct wl_list *source, + struct wl_client *client) +{ + struct wl_resource *resource, *tmp; + wl_resource_for_each_safe (resource, tmp, source) + { + if (wl_resource_get_client (resource) == client) + { + wl_list_remove (wl_resource_get_link (resource)); + wl_list_insert (destination, wl_resource_get_link (resource)); + } + } +} + +void +meta_wayland_text_input_set_focus (MetaWaylandTextInput *text_input, + MetaWaylandSurface *surface) +{ + if (text_input->surface == surface) + return; + + text_input->pending_state = META_WAYLAND_PENDING_STATE_NONE; + + if (text_input->surface) + { + if (!wl_list_empty (&text_input->focus_resource_list)) + { + ClutterInputFocus *focus = text_input->input_focus; + ClutterInputMethod *input_method; + struct wl_resource *resource; + + if (clutter_input_focus_is_focused (focus)) + { + input_method = clutter_backend_get_input_method (clutter_get_default_backend ()); + clutter_input_method_focus_out (input_method); + } + + wl_resource_for_each (resource, &text_input->focus_resource_list) + { + zwp_text_input_v3_send_leave (resource, + text_input->surface->resource); + } + + move_resources (&text_input->resource_list, + &text_input->focus_resource_list); + } + + wl_list_remove (&text_input->surface_listener.link); + text_input->surface = NULL; + } + + if (surface) + { + struct wl_resource *focus_surface_resource; + + text_input->surface = surface; + focus_surface_resource = text_input->surface->resource; + wl_resource_add_destroy_listener (focus_surface_resource, + &text_input->surface_listener); + + move_resources_for_client (&text_input->focus_resource_list, + &text_input->resource_list, + wl_resource_get_client (focus_surface_resource)); + + if (!wl_list_empty (&text_input->focus_resource_list)) + { + struct wl_resource *resource; + + wl_resource_for_each (resource, &text_input->focus_resource_list) + { + zwp_text_input_v3_send_enter (resource, surface->resource); + } + } + } +} + +static void +text_input_destructor (struct wl_resource *resource) +{ + MetaWaylandTextInput *text_input = wl_resource_get_user_data (resource); + + g_hash_table_remove (text_input->resource_serials, resource); + wl_list_remove (wl_resource_get_link (resource)); +} + +static void +text_input_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +text_input_enable (struct wl_client *client, + struct wl_resource *resource) +{ + MetaWaylandTextInput *text_input = wl_resource_get_user_data (resource); + + text_input->enabled = TRUE; + text_input->pending_state |= META_WAYLAND_PENDING_STATE_ENABLED; +} + +static void +text_input_disable (struct wl_client *client, + struct wl_resource *resource) +{ + MetaWaylandTextInput *text_input = wl_resource_get_user_data (resource); + + text_input->enabled = FALSE; + text_input->pending_state |= META_WAYLAND_PENDING_STATE_ENABLED; +} + +static void +text_input_set_surrounding_text (struct wl_client *client, + struct wl_resource *resource, + const char *text, + int32_t cursor, + int32_t anchor) +{ + MetaWaylandTextInput *text_input = wl_resource_get_user_data (resource); + + g_free (text_input->surrounding.text); + text_input->surrounding.text = g_strdup (text); + text_input->surrounding.cursor = cursor; + text_input->surrounding.anchor = anchor; + text_input->pending_state |= META_WAYLAND_PENDING_STATE_SURROUNDING_TEXT; +} + +static void +text_input_set_text_change_cause (struct wl_client *client, + struct wl_resource *resource, + uint32_t cause) +{ + MetaWaylandTextInput *text_input = wl_resource_get_user_data (resource); + + text_input->text_change_cause = cause; + text_input->pending_state |= META_WAYLAND_PENDING_STATE_CHANGE_CAUSE; +} + +static ClutterInputContentHintFlags +translate_hints (uint32_t hints) +{ + ClutterInputContentHintFlags clutter_hints = 0; + + if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_COMPLETION) + clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_COMPLETION; + if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_SPELLCHECK) + clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_SPELLCHECK; + if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_AUTO_CAPITALIZATION) + clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_AUTO_CAPITALIZATION; + if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_LOWERCASE) + clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_LOWERCASE; + if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_UPPERCASE) + clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_UPPERCASE; + if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_TITLECASE) + clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_TITLECASE; + if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_HIDDEN_TEXT) + clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_HIDDEN_TEXT; + if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA) + clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_SENSITIVE_DATA; + if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_LATIN) + clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_LATIN; + if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_MULTILINE) + clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_MULTILINE; + + return clutter_hints; +} + +static ClutterInputContentPurpose +translate_purpose (uint32_t purpose) +{ + switch (purpose) + { + case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL: + return CLUTTER_INPUT_CONTENT_PURPOSE_NORMAL; + case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_ALPHA: + return CLUTTER_INPUT_CONTENT_PURPOSE_ALPHA; + case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DIGITS: + return CLUTTER_INPUT_CONTENT_PURPOSE_DIGITS; + case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER: + return CLUTTER_INPUT_CONTENT_PURPOSE_NUMBER; + case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PHONE: + return CLUTTER_INPUT_CONTENT_PURPOSE_PHONE; + case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_URL: + return CLUTTER_INPUT_CONTENT_PURPOSE_URL; + case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_EMAIL: + return CLUTTER_INPUT_CONTENT_PURPOSE_EMAIL; + case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NAME: + return CLUTTER_INPUT_CONTENT_PURPOSE_NAME; + case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PASSWORD: + return CLUTTER_INPUT_CONTENT_PURPOSE_PASSWORD; + case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DATE: + return CLUTTER_INPUT_CONTENT_PURPOSE_DATE; + case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TIME: + return CLUTTER_INPUT_CONTENT_PURPOSE_TIME; + case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DATETIME: + return CLUTTER_INPUT_CONTENT_PURPOSE_DATETIME; + case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL: + return CLUTTER_INPUT_CONTENT_PURPOSE_TERMINAL; + } + + g_warn_if_reached (); + return CLUTTER_INPUT_CONTENT_PURPOSE_NORMAL; +} + +static void +text_input_set_content_type (struct wl_client *client, + struct wl_resource *resource, + uint32_t hint, + uint32_t purpose) +{ + MetaWaylandTextInput *text_input = wl_resource_get_user_data (resource); + + if (!text_input->surface) + return; + + text_input->content_type_hint = hint; + text_input->content_type_purpose = purpose; + text_input->pending_state |= META_WAYLAND_PENDING_STATE_CONTENT_TYPE; +} + +static void +text_input_set_cursor_rectangle (struct wl_client *client, + struct wl_resource *resource, + int32_t x, + int32_t y, + int32_t width, + int32_t height) +{ + MetaWaylandTextInput *text_input = wl_resource_get_user_data (resource); + + if (!text_input->surface) + return; + + text_input->cursor_rect = (cairo_rectangle_int_t) { x, y, width, height }; + text_input->pending_state |= META_WAYLAND_PENDING_STATE_INPUT_RECT; +} + +static void +meta_wayland_text_input_reset (MetaWaylandTextInput *text_input) +{ + g_clear_pointer (&text_input->surrounding.text, g_free); + text_input->content_type_hint = ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE; + text_input->content_type_purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL; + text_input->text_change_cause = ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_INPUT_METHOD; + text_input->cursor_rect = (cairo_rectangle_int_t) { 0, 0, 0, 0 }; +} + +static void +text_input_commit_state (struct wl_client *client, + struct wl_resource *resource) +{ + MetaWaylandTextInput *text_input = wl_resource_get_user_data (resource); + ClutterInputFocus *focus = text_input->input_focus; + gboolean toggle_panel = FALSE; + + increment_serial (text_input, resource); + + if (text_input->surface == NULL) + return; + + if (text_input->pending_state & META_WAYLAND_PENDING_STATE_ENABLED) + { + ClutterInputMethod *input_method; + + input_method = clutter_backend_get_input_method (clutter_get_default_backend ()); + + if (text_input->enabled) + { + meta_wayland_text_input_reset (text_input); + + if (!clutter_input_focus_is_focused (focus)) + { + if (input_method) + clutter_input_method_focus_in (input_method, focus); + else + return; + } + + clutter_input_focus_set_can_show_preedit (focus, TRUE); + toggle_panel = TRUE; + } + else if (clutter_input_focus_is_focused (focus)) + { + text_input->pending_state = META_WAYLAND_PENDING_STATE_NONE; + clutter_input_focus_reset (text_input->input_focus); + clutter_input_method_focus_out (input_method); + } + } + else if (!clutter_input_focus_is_focused (focus)) + return; + + if (text_input->pending_state & META_WAYLAND_PENDING_STATE_CONTENT_TYPE) + { + clutter_input_focus_set_content_hints (text_input->input_focus, + translate_hints (text_input->content_type_hint)); + clutter_input_focus_set_content_purpose (text_input->input_focus, + translate_purpose (text_input->content_type_purpose)); + } + + if (text_input->pending_state & META_WAYLAND_PENDING_STATE_SURROUNDING_TEXT) + { + clutter_input_focus_set_surrounding (text_input->input_focus, + text_input->surrounding.text, + text_input->surrounding.cursor, + text_input->surrounding.anchor); + } + + if (text_input->pending_state & META_WAYLAND_PENDING_STATE_INPUT_RECT) + { + ClutterRect cursor_rect; + float x1, y1, x2, y2; + cairo_rectangle_int_t rect; + + rect = text_input->cursor_rect; + meta_wayland_surface_get_absolute_coordinates (text_input->surface, + rect.x, rect.y, &x1, &y1); + meta_wayland_surface_get_absolute_coordinates (text_input->surface, + rect.x + rect.width, + rect.y + rect.height, + &x2, &y2); + + clutter_rect_init (&cursor_rect, x1, y1, x2 - x1, y2 - y1); + clutter_input_focus_set_cursor_location (text_input->input_focus, + &cursor_rect); + } + + text_input->pending_state = META_WAYLAND_PENDING_STATE_NONE; + + if (toggle_panel) + clutter_input_focus_request_toggle_input_panel (focus); +} + +static struct zwp_text_input_v3_interface meta_text_input_interface = { + 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_commit_state, +}; + +MetaWaylandTextInput * +meta_wayland_text_input_new (MetaWaylandSeat *seat) +{ + MetaWaylandTextInput *text_input; + + text_input = g_new0 (MetaWaylandTextInput, 1); + text_input->input_focus = meta_wayland_text_input_focus_new (text_input); + text_input->seat = seat; + + wl_list_init (&text_input->resource_list); + wl_list_init (&text_input->focus_resource_list); + text_input->surface_listener.notify = text_input_handle_focus_surface_destroy; + + text_input->resource_serials = g_hash_table_new (NULL, NULL); + + return text_input; +} + +void +meta_wayland_text_input_destroy (MetaWaylandTextInput *text_input) +{ + meta_wayland_text_input_set_focus (text_input, NULL); + g_object_unref (text_input->input_focus); + g_hash_table_destroy (text_input->resource_serials); + g_free (text_input); +} + +static void +meta_wayland_text_input_create_new_resource (MetaWaylandTextInput *text_input, + struct wl_client *client, + struct wl_resource *seat_resource, + uint32_t id) +{ + struct wl_resource *text_input_resource; + + text_input_resource = wl_resource_create (client, + &zwp_text_input_v3_interface, + META_ZWP_TEXT_INPUT_V3_VERSION, + id); + + wl_resource_set_implementation (text_input_resource, + &meta_text_input_interface, + text_input, text_input_destructor); + + if (text_input->surface && + wl_resource_get_client (text_input->surface->resource) == client) + { + wl_list_insert (&text_input->focus_resource_list, + wl_resource_get_link (text_input_resource)); + + zwp_text_input_v3_send_enter (text_input_resource, + text_input->surface->resource); + } + else + { + wl_list_insert (&text_input->resource_list, + wl_resource_get_link (text_input_resource)); + } +} + +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_resource) +{ + MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); + + meta_wayland_text_input_create_new_resource (seat->text_input, client, + seat_resource, id); +} + +static struct zwp_text_input_manager_v3_interface meta_text_input_manager_interface = { + text_input_manager_destroy, + text_input_manager_get_text_input, +}; + +static void +bind_text_input (struct wl_client *client, + void *data, + uint32_t version, + uint32_t id) +{ + struct wl_resource *resource; + + resource = wl_resource_create (client, + &zwp_text_input_manager_v3_interface, + META_ZWP_TEXT_INPUT_V3_VERSION, + id); + wl_resource_set_implementation (resource, + &meta_text_input_manager_interface, + NULL, NULL); +} + +gboolean +meta_wayland_text_input_init (MetaWaylandCompositor *compositor) +{ + return (wl_global_create (compositor->wayland_display, + &zwp_text_input_manager_v3_interface, + META_ZWP_TEXT_INPUT_V3_VERSION, + compositor->seat->text_input, + bind_text_input) != NULL); +} + +gboolean +meta_wayland_text_input_handle_event (MetaWaylandTextInput *text_input, + const ClutterEvent *event) +{ + if (!text_input->surface || + !clutter_input_focus_is_focused (text_input->input_focus)) + return FALSE; + + return clutter_input_focus_filter_key_event (text_input->input_focus, + (const ClutterKeyEvent *) event); +} diff --git a/src/wayland/meta-wayland-text-input.h b/src/wayland/meta-wayland-text-input.h new file mode 100644 index 000000000..d82db91a0 --- /dev/null +++ b/src/wayland/meta-wayland-text-input.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Carlos Garnacho + */ + +#ifndef META_WAYLAND_TEXT_INPUT_H +#define META_WAYLAND_TEXT_INPUT_H + +#include +#include "wayland/meta-wayland-types.h" +#include "meta/window.h" + +#define META_TYPE_WAYLAND_TEXT_INPUT (meta_wayland_text_input_get_type ()) +G_DECLARE_FINAL_TYPE (MetaWaylandTextInput, + meta_wayland_text_input, + META, WAYLAND_TEXT_INPUT, + GObject); + +MetaWaylandTextInput * meta_wayland_text_input_new (MetaWaylandSeat *seat); +void meta_wayland_text_input_destroy (MetaWaylandTextInput *text_input); + +gboolean meta_wayland_text_input_init (MetaWaylandCompositor *compositor); + +void meta_wayland_text_input_set_focus (MetaWaylandTextInput *text_input, + MetaWaylandSurface *surface); + +gboolean meta_wayland_text_input_handle_event (MetaWaylandTextInput *text_input, + const ClutterEvent *event); + +#endif /* META_WAYLAND_TEXT_INPUT_H */ diff --git a/src/wayland/meta-wayland-versions.h b/src/wayland/meta-wayland-versions.h index 81d37ef0a..63ca9b0d6 100644 --- a/src/wayland/meta-wayland-versions.h +++ b/src/wayland/meta-wayland-versions.h @@ -53,5 +53,6 @@ #define META_ZXDG_OUTPUT_V1_VERSION 1 #define META_ZWP_XWAYLAND_KEYBOARD_GRAB_V1_VERSION 1 #define META_GTK_TEXT_INPUT_VERSION 1 +#define META_ZWP_TEXT_INPUT_V3_VERSION 1 #endif diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c index 2cdf64cd0..30759d7de 100644 --- a/src/wayland/meta-wayland.c +++ b/src/wayland/meta-wayland.c @@ -392,6 +392,7 @@ meta_wayland_init (void) meta_wayland_dma_buf_init (compositor); meta_wayland_keyboard_shortcuts_inhibit_init (compositor); meta_wayland_surface_inhibit_shortcuts_dialog_init (); + meta_wayland_text_input_init (compositor); meta_wayland_gtk_text_input_init (compositor); /* Xwayland specific protocol, needs to be filtered out for all other clients */