[mutter/wip/carlosg/wp-text-input: 3/3] wayland: Implement text-input from wayland-protocols



commit 85e5b160ee7dbeec474c3f6877ddbd5310f0fbe8
Author: Carlos Garnacho <carlosg gnome org>
Date:   Mon Aug 13 18:59:02 2018 +0200

    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(-)
---
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 <carlosg gnome org>
+ */
+
+#include "config.h"
+
+#include <wayland-server.h>
+
+#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 <carlosg gnome org>
+ */
+
+#ifndef META_WAYLAND_TEXT_INPUT_H
+#define META_WAYLAND_TEXT_INPUT_H
+
+#include <wayland-server.h>
+#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 */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]