2024-11-12 12:17:00 +01:00
|
|
|
From 2a94e19b00434fe4d7ab858a6cdcff6364f6e408 Mon Sep 17 00:00:00 2001
|
|
|
|
From: Alynx Zhou <alynx.zhou@gmail.com>
|
|
|
|
Date: Wed, 15 May 2024 00:07:41 +0800
|
|
|
|
Subject: [PATCH 2/2] wayland/text-input-v1: Implement basic text-input-v1
|
|
|
|
support
|
|
|
|
|
|
|
|
This commit makes input methods work in text-input-v1 only clients
|
|
|
|
(mostly Chromium/Electron based apps with Ozone Wayland), which is
|
|
|
|
needed by users who needs IME to input their languages, like Chinese,
|
|
|
|
Japanese or Korean.
|
|
|
|
|
|
|
|
Closes <https://gitlab.gnome.org/GNOME/mutter/-/issues/3200>.
|
|
|
|
---
|
|
|
|
clutter/clutter/clutter-enums.h | 3 +
|
|
|
|
src/core/events.c | 11 +-
|
|
|
|
src/meson.build | 3 +
|
|
|
|
src/wayland/meta-wayland-seat.c | 12 +-
|
|
|
|
src/wayland/meta-wayland-seat.h | 2 +
|
|
|
|
src/wayland/meta-wayland-text-input-v1.c | 859 +++++++++++++++++++++++
|
|
|
|
src/wayland/meta-wayland-text-input-v1.h | 38 +
|
|
|
|
src/wayland/meta-wayland-versions.h | 1 +
|
|
|
|
src/wayland/meta-wayland.c | 7 +
|
|
|
|
src/wayland/meta-wayland.h | 2 +
|
|
|
|
10 files changed, 933 insertions(+), 5 deletions(-)
|
|
|
|
create mode 100644 src/wayland/meta-wayland-text-input-v1.c
|
|
|
|
create mode 100644 src/wayland/meta-wayland-text-input-v1.h
|
|
|
|
|
|
|
|
Index: mutter-47.beta/clutter/clutter/clutter-enums.h
|
|
|
|
===================================================================
|
|
|
|
--- mutter-47.beta.orig/clutter/clutter/clutter-enums.h
|
|
|
|
+++ mutter-47.beta/clutter/clutter/clutter-enums.h
|
|
|
|
@@ -1215,6 +1215,9 @@ typedef enum
|
2024-07-22 17:41:29 +02:00
|
|
|
CLUTTER_INPUT_CONTENT_HINT_SENSITIVE_DATA = 1 << 7,
|
|
|
|
CLUTTER_INPUT_CONTENT_HINT_LATIN = 1 << 8,
|
|
|
|
CLUTTER_INPUT_CONTENT_HINT_MULTILINE = 1 << 9,
|
|
|
|
+ CLUTTER_INPUT_CONTENT_HINT_DEFAULT = 1 << 10,
|
|
|
|
+ CLUTTER_INPUT_CONTENT_HINT_PASSWORD = 1 << 11,
|
|
|
|
+ CLUTTER_INPUT_CONTENT_HINT_AUTO_CORRECTION = 1 << 12,
|
|
|
|
} ClutterInputContentHintFlags;
|
|
|
|
|
|
|
|
typedef enum
|
2024-11-12 12:17:00 +01:00
|
|
|
Index: mutter-47.beta/src/core/events.c
|
|
|
|
===================================================================
|
|
|
|
--- mutter-47.beta.orig/src/core/events.c
|
|
|
|
+++ mutter-47.beta/src/core/events.c
|
|
|
|
@@ -239,6 +239,7 @@ meta_display_handle_event (MetaDisplay
|
2024-07-22 17:41:29 +02:00
|
|
|
#ifdef HAVE_WAYLAND
|
|
|
|
MetaWaylandCompositor *wayland_compositor;
|
|
|
|
MetaWaylandTextInput *wayland_text_input = NULL;
|
|
|
|
+ MetaWaylandTextInputV1 *wayland_text_input_v1 = NULL;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_WAYLAND
|
2024-11-12 12:17:00 +01:00
|
|
|
@@ -247,6 +248,8 @@ meta_display_handle_event (MetaDisplay
|
2024-07-22 17:41:29 +02:00
|
|
|
{
|
|
|
|
wayland_text_input =
|
|
|
|
meta_wayland_compositor_get_text_input (wayland_compositor);
|
|
|
|
+ wayland_text_input_v1 =
|
|
|
|
+ meta_wayland_compositor_get_text_input_v1 (wayland_compositor);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2024-11-12 12:17:00 +01:00
|
|
|
@@ -288,9 +291,11 @@ meta_display_handle_event (MetaDisplay
|
2024-07-22 17:41:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_WAYLAND
|
|
|
|
- if (wayland_text_input &&
|
2024-11-12 12:17:00 +01:00
|
|
|
- !meta_compositor_get_current_window_drag (compositor) &&
|
2024-07-22 17:41:29 +02:00
|
|
|
- meta_wayland_text_input_update (wayland_text_input, event))
|
2024-11-12 12:17:00 +01:00
|
|
|
+ if (!meta_compositor_get_current_window_drag (compositor) &&
|
|
|
|
+ ((wayland_text_input &&
|
|
|
|
+ meta_wayland_text_input_update (wayland_text_input, event)) ||
|
|
|
|
+ (wayland_text_input_v1 &&
|
|
|
|
+ meta_wayland_text_input_v1_update (wayland_text_input_v1, event))))
|
|
|
|
return CLUTTER_EVENT_STOP;
|
|
|
|
|
|
|
|
if (wayland_compositor)
|
|
|
|
Index: mutter-47.beta/src/meson.build
|
|
|
|
===================================================================
|
|
|
|
--- mutter-47.beta.orig/src/meson.build
|
|
|
|
+++ mutter-47.beta/src/meson.build
|
|
|
|
@@ -694,6 +694,8 @@ if have_wayland
|
2024-07-22 17:41:29 +02:00
|
|
|
'wayland/meta-wayland-tablet-tool.h',
|
|
|
|
'wayland/meta-wayland-text-input.c',
|
|
|
|
'wayland/meta-wayland-text-input.h',
|
|
|
|
+ 'wayland/meta-wayland-text-input-v1.c',
|
|
|
|
+ 'wayland/meta-wayland-text-input-v1.h',
|
|
|
|
'wayland/meta-wayland-touch.c',
|
|
|
|
'wayland/meta-wayland-touch.h',
|
|
|
|
'wayland/meta-wayland-transaction.c',
|
2024-11-12 12:17:00 +01:00
|
|
|
@@ -1102,6 +1104,7 @@ if have_wayland
|
2024-07-22 17:41:29 +02:00
|
|
|
['single-pixel-buffer', 'staging', 'v1', ],
|
|
|
|
['tablet', 'unstable', 'v2', ],
|
|
|
|
['text-input', 'unstable', 'v3', ],
|
|
|
|
+ ['text-input', 'unstable', 'v1', ],
|
|
|
|
['viewporter', 'stable', ],
|
|
|
|
['xdg-activation', 'staging', 'v1', ],
|
2024-11-12 12:17:00 +01:00
|
|
|
['xdg-dialog', 'staging', 'v1', ],
|
|
|
|
Index: mutter-47.beta/src/wayland/meta-wayland-seat.c
|
|
|
|
===================================================================
|
|
|
|
--- mutter-47.beta.orig/src/wayland/meta-wayland-seat.c
|
|
|
|
+++ mutter-47.beta/src/wayland/meta-wayland-seat.c
|
|
|
|
@@ -229,6 +229,7 @@ default_focus (MetaWaylandEventHandler *
|
|
|
|
surface);
|
|
|
|
meta_wayland_tablet_seat_set_pad_focus (seat->tablet_seat, surface);
|
|
|
|
meta_wayland_text_input_set_focus (seat->text_input, surface);
|
|
|
|
+ /* text-input-v1 will set focused surface on activate. */
|
2024-07-22 17:41:29 +02:00
|
|
|
}
|
|
|
|
|
2024-11-12 12:17:00 +01:00
|
|
|
if (caps & CLUTTER_INPUT_CAPABILITY_TABLET_TOOL)
|
|
|
|
@@ -290,6 +291,8 @@ meta_wayland_seat_new (MetaWaylandCompos
|
2024-07-22 17:41:29 +02:00
|
|
|
NULL);
|
|
|
|
|
|
|
|
seat->text_input = meta_wayland_text_input_new (seat);
|
|
|
|
+ /* Chromium/Electron-based apps only support text-input-v1. */
|
|
|
|
+ seat->text_input_v1 = meta_wayland_text_input_v1_new (seat);
|
|
|
|
|
|
|
|
meta_wayland_data_device_init (&seat->data_device, seat);
|
|
|
|
meta_wayland_data_device_primary_init (&seat->primary_data_device, seat);
|
2024-11-12 12:17:00 +01:00
|
|
|
@@ -338,6 +341,7 @@ meta_wayland_seat_free (MetaWaylandSeat
|
2024-07-22 17:41:29 +02:00
|
|
|
g_object_unref (seat->touch);
|
|
|
|
|
|
|
|
meta_wayland_text_input_destroy (seat->text_input);
|
|
|
|
+ meta_wayland_text_input_v1_destroy (seat->text_input_v1);
|
|
|
|
|
|
|
|
g_free (seat);
|
|
|
|
}
|
2024-11-12 12:17:00 +01:00
|
|
|
@@ -478,7 +482,10 @@ meta_wayland_seat_handle_event_internal
|
2024-07-22 17:41:29 +02:00
|
|
|
if (event_type == CLUTTER_BUTTON_PRESS ||
|
|
|
|
event_type == CLUTTER_TOUCH_BEGIN)
|
|
|
|
{
|
|
|
|
- meta_wayland_text_input_handle_event (seat->text_input, event);
|
|
|
|
+ gboolean handled = FALSE;
|
|
|
|
+ handled = meta_wayland_text_input_handle_event (seat->text_input, event);
|
|
|
|
+ if (!handled)
|
|
|
|
+ handled = meta_wayland_text_input_v1_handle_event (seat->text_input_v1, event);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (event_type)
|
2024-11-12 12:17:00 +01:00
|
|
|
@@ -510,7 +517,8 @@ meta_wayland_seat_handle_event_internal
|
2024-07-22 17:41:29 +02:00
|
|
|
case CLUTTER_IM_COMMIT:
|
|
|
|
case CLUTTER_IM_DELETE:
|
|
|
|
case CLUTTER_IM_PREEDIT:
|
|
|
|
- if (meta_wayland_text_input_handle_event (seat->text_input, event))
|
|
|
|
+ if (meta_wayland_text_input_handle_event (seat->text_input, event) ||
|
|
|
|
+ meta_wayland_text_input_v1_handle_event (seat->text_input_v1, event))
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
break;
|
2024-11-12 12:17:00 +01:00
|
|
|
Index: mutter-47.beta/src/wayland/meta-wayland-seat.h
|
|
|
|
===================================================================
|
|
|
|
--- mutter-47.beta.orig/src/wayland/meta-wayland-seat.h
|
|
|
|
+++ mutter-47.beta/src/wayland/meta-wayland-seat.h
|
|
|
|
@@ -30,6 +30,7 @@
|
2024-07-22 17:41:29 +02:00
|
|
|
#include "wayland/meta-wayland-pointer.h"
|
|
|
|
#include "wayland/meta-wayland-tablet-tool.h"
|
|
|
|
#include "wayland/meta-wayland-text-input.h"
|
|
|
|
+#include "wayland/meta-wayland-text-input-v1.h"
|
|
|
|
#include "wayland/meta-wayland-touch.h"
|
|
|
|
#include "wayland/meta-wayland-types.h"
|
|
|
|
|
2024-11-12 12:17:00 +01:00
|
|
|
@@ -49,6 +50,7 @@ struct _MetaWaylandSeat
|
2024-07-22 17:41:29 +02:00
|
|
|
MetaWaylandDataDevicePrimary primary_data_device;
|
|
|
|
|
|
|
|
MetaWaylandTextInput *text_input;
|
|
|
|
+ MetaWaylandTextInputV1 *text_input_v1;
|
|
|
|
|
2024-11-12 12:17:00 +01:00
|
|
|
MetaWaylandInput *input_handler;
|
|
|
|
MetaWaylandEventHandler *default_handler;
|
|
|
|
Index: mutter-47.beta/src/wayland/meta-wayland-text-input-v1.c
|
|
|
|
===================================================================
|
|
|
|
--- /dev/null
|
|
|
|
+++ mutter-47.beta/src/wayland/meta-wayland-text-input-v1.c
|
2024-07-22 17:41:29 +02:00
|
|
|
@@ -0,0 +1,859 @@
|
|
|
|
+/*
|
|
|
|
+ * Copyright (C) 2024 SUSE LLC
|
|
|
|
+ *
|
|
|
|
+ * 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, see <http://www.gnu.org/licenses/>.
|
|
|
|
+ *
|
|
|
|
+ * Author: Alynx Zhou <alynx.zhou@gmail.com>
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#include "config.h"
|
|
|
|
+#include "wayland/meta-wayland-text-input-v1.h"
|
|
|
|
+
|
|
|
|
+#include <wayland-server.h>
|
|
|
|
+
|
|
|
|
+#include "compositor/meta-surface-actor-wayland.h"
|
|
|
|
+#include "wayland/meta-wayland-private.h"
|
|
|
|
+#include "wayland/meta-wayland-seat.h"
|
|
|
|
+#include "wayland/meta-wayland-versions.h"
|
|
|
|
+
|
|
|
|
+#include "text-input-unstable-v1-server-protocol.h"
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Main difference between text-input-v1 and text-input-v3:
|
|
|
|
+ * text-input-v1 is not required to be double-buffered, we are expected to send
|
|
|
|
+ * response immediately after we receive requests, while text-input-v3 requires
|
|
|
|
+ * us to hold pending state and apply on commit, and all responses are applied
|
|
|
|
+ * after we send done.
|
|
|
|
+ *
|
|
|
|
+ * This implementation is incomplete, but it do make IME work.
|
|
|
|
+ *
|
|
|
|
+ * Things won't be implemented (Reminders for myself):
|
|
|
|
+ * - set_preferred_language (We don't have equivalence in ClutterInputMethod.)
|
|
|
|
+ * - invoke_action (No description about what button and index are.)
|
|
|
|
+ * - input_panel_state (We don't set this from ClutterInputFocus to text_input,
|
|
|
|
+ * we only set this from text_input to ClutterInputFocus.)
|
|
|
|
+ * - cursor_position (We don't have equivalence in ClutterInputMethod.)
|
|
|
|
+ * - language (We don't have equivalence in ClutterInputMethod.)
|
|
|
|
+ * - text_direction (We don't have equivalence in ClutterInputMethod.)
|
|
|
|
+ * - keysym (This matches keysym request in input-method-v1, but we only have
|
|
|
|
+ * forward_key in ClutterInputMethod, which is more like key request in
|
|
|
|
+ * input-method-v1 and will finally become a keyboard key event, we don't have
|
|
|
|
+ * equivalence for this in ClutterInputMethod.)
|
|
|
|
+ * - modifiers_map (This is used by keysym and we don't support keysym.)
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+struct _MetaWaylandTextInputV1
|
|
|
|
+{
|
|
|
|
+ MetaWaylandSeat *seat;
|
|
|
|
+ ClutterInputFocus *input_focus;
|
|
|
|
+
|
|
|
|
+ struct wl_list resource_list;
|
|
|
|
+ struct wl_list focus_resource_list;
|
|
|
|
+ MetaWaylandSurface *surface;
|
|
|
|
+ struct wl_listener surface_listener;
|
|
|
|
+
|
|
|
|
+ GHashTable *resource_serials;
|
|
|
|
+
|
|
|
|
+ struct
|
|
|
|
+ {
|
|
|
|
+ char *text;
|
|
|
|
+ uint32_t cursor;
|
|
|
|
+ uint32_t anchor;
|
|
|
|
+ } surrounding;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+#define META_TYPE_WAYLAND_TEXT_INPUT_V1_FOCUS (meta_wayland_text_input_v1_focus_get_type ())
|
|
|
|
+G_DECLARE_FINAL_TYPE (MetaWaylandTextInputV1Focus, meta_wayland_text_input_v1_focus,
|
|
|
|
+ META, WAYLAND_TEXT_INPUT_V1_FOCUS, ClutterInputFocus)
|
|
|
|
+
|
|
|
|
+struct _MetaWaylandTextInputV1Focus
|
|
|
|
+{
|
|
|
|
+ ClutterInputFocus parent_instance;
|
|
|
|
+ MetaWaylandTextInputV1 *text_input;
|
|
|
|
+};
|
|
|
|
+G_DEFINE_TYPE (MetaWaylandTextInputV1Focus, meta_wayland_text_input_v1_focus,
|
|
|
|
+ CLUTTER_TYPE_INPUT_FOCUS)
|
|
|
|
+
|
|
|
|
+static MetaBackend *
|
|
|
|
+backend_from_text_input_v1 (MetaWaylandTextInputV1 *text_input)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandSeat *seat = text_input->seat;
|
|
|
|
+ MetaWaylandCompositor *compositor = meta_wayland_seat_get_compositor (seat);
|
|
|
|
+ MetaContext *context = meta_wayland_compositor_get_context (compositor);
|
|
|
|
+
|
|
|
|
+ return meta_context_get_backend (context);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static uint32_t
|
|
|
|
+get_serial (MetaWaylandTextInputV1 *text_input,
|
|
|
|
+ struct wl_resource *resource)
|
|
|
|
+{
|
|
|
|
+ return GPOINTER_TO_UINT (g_hash_table_lookup (text_input->resource_serials,
|
|
|
|
+ resource));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+set_serial (MetaWaylandTextInputV1 *text_input,
|
|
|
|
+ struct wl_resource *resource,
|
|
|
|
+ uint32_t serial)
|
|
|
|
+{
|
|
|
|
+ g_hash_table_insert (text_input->resource_serials, resource,
|
|
|
|
+ GUINT_TO_POINTER (serial));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+text_input_v1_send_preedit_string (struct wl_resource *resource,
|
|
|
|
+ uint32_t serial,
|
|
|
|
+ const char *text,
|
|
|
|
+ unsigned int cursor)
|
|
|
|
+{
|
|
|
|
+ gsize pos = 0;
|
|
|
|
+
|
|
|
|
+ /* Chromium does not accept NULL as preedit/commit string... */
|
|
|
|
+ text = text ? text : "";
|
|
|
|
+ pos = g_utf8_offset_to_pointer (text, cursor) - text;
|
|
|
|
+
|
|
|
|
+ /* We really don't need so much styles... */
|
|
|
|
+ zwp_text_input_v1_send_preedit_styling (resource, 0, strlen (text),
|
|
|
|
+ ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_UNDERLINE);
|
|
|
|
+ zwp_text_input_v1_send_preedit_cursor (resource, pos);
|
|
|
|
+ zwp_text_input_v1_send_preedit_string (resource, serial, text, text);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+meta_wayland_text_input_v1_focus_set_preedit_text (ClutterInputFocus *focus,
|
|
|
|
+ const gchar *text,
|
|
|
|
+ unsigned int cursor,
|
|
|
|
+ unsigned int anchor)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1 *text_input;
|
|
|
|
+ struct wl_resource *resource;
|
|
|
|
+
|
|
|
|
+ text_input = META_WAYLAND_TEXT_INPUT_V1_FOCUS (focus)->text_input;
|
|
|
|
+
|
|
|
|
+ wl_resource_for_each (resource, &text_input->focus_resource_list)
|
|
|
|
+ {
|
|
|
|
+ text_input_v1_send_preedit_string (resource,
|
|
|
|
+ get_serial (text_input, resource),
|
|
|
|
+ text,
|
|
|
|
+ cursor);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+meta_wayland_text_input_v1_focus_request_surrounding (ClutterInputFocus *focus)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1 *text_input;
|
|
|
|
+ long cursor, anchor;
|
|
|
|
+
|
|
|
|
+ /* Clutter uses char offsets but text-input-v1 uses byte offsets. */
|
|
|
|
+ text_input = META_WAYLAND_TEXT_INPUT_V1_FOCUS (focus)->text_input;
|
|
|
|
+ cursor = g_utf8_strlen (text_input->surrounding.text,
|
|
|
|
+ text_input->surrounding.cursor);
|
|
|
|
+ anchor = g_utf8_strlen (text_input->surrounding.text,
|
|
|
|
+ text_input->surrounding.anchor);
|
|
|
|
+ clutter_input_focus_set_surrounding (focus,
|
|
|
|
+ text_input->surrounding.text,
|
|
|
|
+ cursor,
|
|
|
|
+ anchor);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+text_input_v1_send_commit_string (struct wl_resource *resource,
|
|
|
|
+ uint32_t serial,
|
|
|
|
+ const char *text)
|
|
|
|
+{
|
|
|
|
+ /* Chromium does not accept NULL as preedit/commit string... */
|
|
|
|
+ text = text ? text : "";
|
|
|
|
+
|
|
|
|
+ zwp_text_input_v1_send_commit_string (resource, serial, text);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+meta_wayland_text_input_v1_focus_delete_surrounding (ClutterInputFocus *focus,
|
|
|
|
+ int offset,
|
|
|
|
+ guint len)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1 *text_input;
|
|
|
|
+ struct wl_resource *resource;
|
|
|
|
+ const char *start, *end;
|
|
|
|
+ const char *before, *after;
|
|
|
|
+ const char *cursor;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * offset and len are counted by UTF-8 chars, but text-input-v1's lengths are
|
|
|
|
+ * counted by bytes, so we convert UTF-8 char offsets to pointers here, this
|
|
|
|
+ * needs the surrounding text
|
|
|
|
+ */
|
|
|
|
+ text_input = META_WAYLAND_TEXT_INPUT_V1_FOCUS (focus)->text_input;
|
|
|
|
+ offset = MIN (offset, 0);
|
|
|
|
+
|
|
|
|
+ start = text_input->surrounding.text;
|
|
|
|
+ end = start + strlen (text_input->surrounding.text);
|
|
|
|
+ cursor = start + text_input->surrounding.cursor;
|
|
|
|
+
|
|
|
|
+ before = g_utf8_offset_to_pointer (cursor, offset);
|
|
|
|
+ g_assert (before >= start);
|
|
|
|
+
|
|
|
|
+ after = g_utf8_offset_to_pointer (cursor, offset + len);
|
|
|
|
+ g_assert (after <= end);
|
|
|
|
+
|
|
|
|
+ wl_resource_for_each (resource, &text_input->focus_resource_list)
|
|
|
|
+ {
|
|
|
|
+ zwp_text_input_v1_send_delete_surrounding_text (resource,
|
|
|
|
+ before - cursor,
|
|
|
|
+ after - before);
|
|
|
|
+ /*
|
|
|
|
+ * text-input-v1 says delete_surrounding belongs to next commit, so an
|
|
|
|
+ * empty commit is required.
|
|
|
|
+ */
|
|
|
|
+ text_input_v1_send_commit_string (resource,
|
|
|
|
+ get_serial (text_input, resource),
|
|
|
|
+ NULL);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+meta_wayland_text_input_v1_focus_commit_text (ClutterInputFocus *focus,
|
|
|
|
+ const gchar *text)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1 *text_input;
|
|
|
|
+ struct wl_resource *resource;
|
|
|
|
+
|
|
|
|
+ text_input = META_WAYLAND_TEXT_INPUT_V1_FOCUS (focus)->text_input;
|
|
|
|
+
|
|
|
|
+ wl_resource_for_each (resource, &text_input->focus_resource_list)
|
|
|
|
+ {
|
|
|
|
+ /*
|
|
|
|
+ * You have to clear preedit string after committing string, otherwise
|
|
|
|
+ * some apps (I reproduced with Code OSS) will send you empty surrounding
|
|
|
|
+ * text and breaks delete_surrounding_text.
|
|
|
|
+ */
|
|
|
|
+ text_input_v1_send_commit_string (resource,
|
|
|
|
+ get_serial (text_input, resource),
|
|
|
|
+ text);
|
|
|
|
+ /* Clear preedit string because we already committed. */
|
|
|
|
+ text_input_v1_send_preedit_string (resource,
|
|
|
|
+ get_serial (text_input, resource),
|
|
|
|
+ NULL,
|
|
|
|
+ 0);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+meta_wayland_text_input_v1_focus_class_init (MetaWaylandTextInputV1FocusClass *klass)
|
|
|
|
+{
|
|
|
|
+ ClutterInputFocusClass *focus_class = CLUTTER_INPUT_FOCUS_CLASS (klass);
|
|
|
|
+
|
|
|
|
+ focus_class->request_surrounding = meta_wayland_text_input_v1_focus_request_surrounding;
|
|
|
|
+ focus_class->delete_surrounding = meta_wayland_text_input_v1_focus_delete_surrounding;
|
|
|
|
+ focus_class->commit_text = meta_wayland_text_input_v1_focus_commit_text;
|
|
|
|
+ focus_class->set_preedit_text = meta_wayland_text_input_v1_focus_set_preedit_text;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+meta_wayland_text_input_v1_focus_init (MetaWaylandTextInputV1Focus *focus)
|
|
|
|
+{
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static ClutterInputFocus *
|
|
|
|
+meta_wayland_text_input_focus_new (MetaWaylandTextInputV1 *text_input)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1Focus *focus;
|
|
|
|
+
|
|
|
|
+ focus = g_object_new (META_TYPE_WAYLAND_TEXT_INPUT_V1_FOCUS, NULL);
|
|
|
|
+ focus->text_input = text_input;
|
|
|
|
+
|
|
|
|
+ return CLUTTER_INPUT_FOCUS (focus);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+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));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+meta_wayland_text_input_v1_set_focus (MetaWaylandTextInputV1 *text_input,
|
|
|
|
+ MetaWaylandSurface *surface)
|
|
|
|
+{
|
|
|
|
+ if (text_input->surface == surface)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ 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_focus_reset (focus);
|
|
|
|
+ clutter_input_method_focus_out (input_method);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ wl_resource_for_each (resource, &text_input->focus_resource_list)
|
|
|
|
+ {
|
|
|
|
+ zwp_text_input_v1_send_leave (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 && surface->resource)
|
|
|
|
+ {
|
|
|
|
+ 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_v1_send_enter (resource, surface->resource);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+text_input_v1_handle_focus_surface_destroy (struct wl_listener *listener,
|
|
|
|
+ void *data)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1 *text_input = wl_container_of (listener, text_input, surface_listener);
|
|
|
|
+
|
|
|
|
+ meta_wayland_text_input_v1_set_focus (text_input, NULL);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+text_input_v1_destructor (struct wl_resource *resource)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1 *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 gboolean
|
|
|
|
+client_matches_focus (MetaWaylandTextInputV1 *text_input,
|
|
|
|
+ struct wl_client *client)
|
|
|
|
+{
|
|
|
|
+ if (!text_input->surface)
|
|
|
|
+ return FALSE;
|
|
|
|
+
|
|
|
|
+ return client == wl_resource_get_client (text_input->surface->resource);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+text_input_v1_activate (struct wl_client *client,
|
|
|
|
+ struct wl_resource *resource,
|
|
|
|
+ struct wl_resource *seat_resource,
|
|
|
|
+ struct wl_resource *surface_resource)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1 *text_input = wl_resource_get_user_data (resource);
|
|
|
|
+ MetaWaylandSurface *surface;
|
|
|
|
+ ClutterInputFocus *focus = text_input->input_focus;
|
|
|
|
+ ClutterInputMethod *input_method;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Don't use client_matches_focus() here because we have no focused surface if
|
|
|
|
+ * not activated in text-input-v1.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ surface = wl_resource_get_user_data (surface_resource);
|
|
|
|
+ meta_wayland_text_input_v1_set_focus (text_input, surface);
|
|
|
|
+
|
|
|
|
+ input_method = clutter_backend_get_input_method (clutter_get_default_backend ());
|
|
|
|
+
|
|
|
|
+ if (input_method)
|
|
|
|
+ {
|
|
|
|
+ if (!clutter_input_focus_is_focused (focus))
|
|
|
|
+ clutter_input_method_focus_in (input_method, focus);
|
|
|
|
+
|
|
|
|
+ clutter_input_focus_set_can_show_preedit (focus, TRUE);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+text_input_v1_deactivate (struct wl_client *client,
|
|
|
|
+ struct wl_resource *resource,
|
|
|
|
+ struct wl_resource *seat_resource)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1 *text_input = wl_resource_get_user_data (resource);
|
|
|
|
+ ClutterInputFocus *focus = text_input->input_focus;
|
|
|
|
+ ClutterInputMethod *input_method;
|
|
|
|
+
|
|
|
|
+ if (!client_matches_focus (text_input, client))
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ meta_wayland_text_input_v1_set_focus (text_input, NULL);
|
|
|
|
+
|
|
|
|
+ input_method = clutter_backend_get_input_method (clutter_get_default_backend ());
|
|
|
|
+ if (input_method && clutter_input_focus_is_focused (focus))
|
|
|
|
+ {
|
|
|
|
+ clutter_input_focus_reset (focus);
|
|
|
|
+ clutter_input_method_focus_out (input_method);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+text_input_v1_show_input_panel (struct wl_client *client,
|
|
|
|
+ struct wl_resource *resource)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1 *text_input = wl_resource_get_user_data (resource);
|
|
|
|
+ ClutterInputFocus *focus = text_input->input_focus;
|
|
|
|
+
|
|
|
|
+ if (!client_matches_focus (text_input, client))
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ clutter_input_focus_set_input_panel_state (focus,
|
|
|
|
+ CLUTTER_INPUT_PANEL_STATE_ON);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+text_input_v1_hide_input_panel (struct wl_client *client,
|
|
|
|
+ struct wl_resource *resource)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1 *text_input = wl_resource_get_user_data (resource);
|
|
|
|
+ ClutterInputFocus *focus = text_input->input_focus;
|
|
|
|
+
|
|
|
|
+ if (!client_matches_focus (text_input, client))
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ clutter_input_focus_set_input_panel_state (focus,
|
|
|
|
+ CLUTTER_INPUT_PANEL_STATE_OFF);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+text_input_v1_set_surrounding_text (struct wl_client *client,
|
|
|
|
+ struct wl_resource *resource,
|
|
|
|
+ const char *text,
|
|
|
|
+ uint32_t cursor,
|
|
|
|
+ uint32_t anchor)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1 *text_input = wl_resource_get_user_data (resource);
|
|
|
|
+ ClutterInputFocus *focus = text_input->input_focus;
|
|
|
|
+ long char_cursor, char_anchor;
|
|
|
|
+
|
|
|
|
+ if (!client_matches_focus (text_input, client))
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ /* Save the surrounding text for `delete_surrounding_text`. */
|
|
|
|
+ g_free (text_input->surrounding.text);
|
|
|
|
+ text_input->surrounding.text = g_strdup (text);
|
|
|
|
+ text_input->surrounding.cursor = cursor;
|
|
|
|
+ text_input->surrounding.anchor = anchor;
|
|
|
|
+
|
|
|
|
+ /* Pass the surrounding text to Clutter to handle it with input method. */
|
|
|
|
+ /* Clutter uses char offsets but text-input-v1 uses byte offsets. */
|
|
|
|
+ char_cursor = g_utf8_strlen (text_input->surrounding.text,
|
|
|
|
+ text_input->surrounding.cursor);
|
|
|
|
+ char_anchor = g_utf8_strlen (text_input->surrounding.text,
|
|
|
|
+ text_input->surrounding.anchor);
|
|
|
|
+ clutter_input_focus_set_surrounding (focus,
|
|
|
|
+ text_input->surrounding.text,
|
|
|
|
+ char_cursor,
|
|
|
|
+ char_anchor);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+text_input_v1_reset (struct wl_client *client,
|
|
|
|
+ struct wl_resource *resource)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1 *text_input = wl_resource_get_user_data (resource);
|
|
|
|
+ ClutterInputFocus *focus = text_input->input_focus;
|
|
|
|
+
|
|
|
|
+ if (!client_matches_focus (text_input, client))
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * This means text was changed outside of normal input method flow, but we are
|
|
|
|
+ * still focusing the same text entry, so we only reset states, but don't
|
|
|
|
+ * reset focus, cursor position and panel visibility.
|
|
|
|
+ */
|
|
|
|
+ g_clear_pointer (&text_input->surrounding.text, g_free);
|
|
|
|
+ clutter_input_focus_set_surrounding (focus, NULL, 0, 0);
|
|
|
|
+ clutter_input_focus_set_content_hints (focus, 0);
|
|
|
|
+ clutter_input_focus_set_content_purpose (focus,
|
|
|
|
+ CLUTTER_INPUT_CONTENT_PURPOSE_NORMAL);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static ClutterInputContentHintFlags
|
|
|
|
+translate_hints (uint32_t hints)
|
|
|
|
+{
|
|
|
|
+ ClutterInputContentHintFlags clutter_hints = 0;
|
|
|
|
+
|
|
|
|
+ if (hints & ZWP_TEXT_INPUT_V1_CONTENT_HINT_DEFAULT)
|
|
|
|
+ clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_DEFAULT;
|
|
|
|
+ if (hints & ZWP_TEXT_INPUT_V1_CONTENT_HINT_PASSWORD)
|
|
|
|
+ clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_PASSWORD;
|
|
|
|
+ if (hints & ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_COMPLETION)
|
|
|
|
+ clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_COMPLETION;
|
|
|
|
+ if (hints & ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_CORRECTION)
|
|
|
|
+ clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_AUTO_CORRECTION;
|
|
|
|
+ if (hints & ZWP_TEXT_INPUT_V1_CONTENT_HINT_AUTO_CAPITALIZATION)
|
|
|
|
+ clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_AUTO_CAPITALIZATION;
|
|
|
|
+ if (hints & ZWP_TEXT_INPUT_V1_CONTENT_HINT_LOWERCASE)
|
|
|
|
+ clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_LOWERCASE;
|
|
|
|
+ if (hints & ZWP_TEXT_INPUT_V1_CONTENT_HINT_UPPERCASE)
|
|
|
|
+ clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_UPPERCASE;
|
|
|
|
+ if (hints & ZWP_TEXT_INPUT_V1_CONTENT_HINT_TITLECASE)
|
|
|
|
+ clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_TITLECASE;
|
|
|
|
+ if (hints & ZWP_TEXT_INPUT_V1_CONTENT_HINT_HIDDEN_TEXT)
|
|
|
|
+ clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_HIDDEN_TEXT;
|
|
|
|
+ if (hints & ZWP_TEXT_INPUT_V1_CONTENT_HINT_SENSITIVE_DATA)
|
|
|
|
+ clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_SENSITIVE_DATA;
|
|
|
|
+ if (hints & ZWP_TEXT_INPUT_V1_CONTENT_HINT_LATIN)
|
|
|
|
+ clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_LATIN;
|
|
|
|
+ if (hints & ZWP_TEXT_INPUT_V1_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_V1_CONTENT_PURPOSE_NORMAL:
|
|
|
|
+ return CLUTTER_INPUT_CONTENT_PURPOSE_NORMAL;
|
|
|
|
+ case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_ALPHA:
|
|
|
|
+ return CLUTTER_INPUT_CONTENT_PURPOSE_ALPHA;
|
|
|
|
+ case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_DIGITS:
|
|
|
|
+ return CLUTTER_INPUT_CONTENT_PURPOSE_DIGITS;
|
|
|
|
+ case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NUMBER:
|
|
|
|
+ return CLUTTER_INPUT_CONTENT_PURPOSE_NUMBER;
|
|
|
|
+ case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_PHONE:
|
|
|
|
+ return CLUTTER_INPUT_CONTENT_PURPOSE_PHONE;
|
|
|
|
+ case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_URL:
|
|
|
|
+ return CLUTTER_INPUT_CONTENT_PURPOSE_URL;
|
|
|
|
+ case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_EMAIL:
|
|
|
|
+ return CLUTTER_INPUT_CONTENT_PURPOSE_EMAIL;
|
|
|
|
+ case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_NAME:
|
|
|
|
+ return CLUTTER_INPUT_CONTENT_PURPOSE_NAME;
|
|
|
|
+ case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_PASSWORD:
|
|
|
|
+ return CLUTTER_INPUT_CONTENT_PURPOSE_PASSWORD;
|
|
|
|
+ case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_DATE:
|
|
|
|
+ return CLUTTER_INPUT_CONTENT_PURPOSE_DATE;
|
|
|
|
+ case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_TIME:
|
|
|
|
+ return CLUTTER_INPUT_CONTENT_PURPOSE_TIME;
|
|
|
|
+ case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_DATETIME:
|
|
|
|
+ return CLUTTER_INPUT_CONTENT_PURPOSE_DATETIME;
|
|
|
|
+ case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_TERMINAL:
|
|
|
|
+ return CLUTTER_INPUT_CONTENT_PURPOSE_TERMINAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ g_warn_if_reached ();
|
|
|
|
+ return CLUTTER_INPUT_CONTENT_PURPOSE_NORMAL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+text_input_v1_set_content_type (struct wl_client *client,
|
|
|
|
+ struct wl_resource *resource,
|
|
|
|
+ uint32_t hint,
|
|
|
|
+ uint32_t purpose)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1 *text_input = wl_resource_get_user_data (resource);
|
|
|
|
+ ClutterInputFocus *focus = text_input->input_focus;
|
|
|
|
+
|
|
|
|
+ if (!client_matches_focus (text_input, client))
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ clutter_input_focus_set_content_hints (focus, translate_hints (hint));
|
|
|
|
+ clutter_input_focus_set_content_purpose (focus, translate_purpose (purpose));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+text_input_v1_set_cursor_rectangle (struct wl_client *client,
|
|
|
|
+ struct wl_resource *resource,
|
|
|
|
+ int32_t x,
|
|
|
|
+ int32_t y,
|
|
|
|
+ int32_t width,
|
|
|
|
+ int32_t height)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1 *text_input = wl_resource_get_user_data (resource);
|
|
|
|
+ ClutterInputFocus *focus = text_input->input_focus;
|
|
|
|
+ MtkRectangle rect = (MtkRectangle) { x, y, width, height };
|
|
|
|
+ graphene_rect_t cursor_rect;
|
|
|
|
+ float x1, y1, x2, y2;
|
|
|
|
+
|
|
|
|
+ if (!client_matches_focus (text_input, client))
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ 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);
|
|
|
|
+
|
|
|
|
+ graphene_rect_init (&cursor_rect, x1, y1, x2 - x1, y2 - y1);
|
|
|
|
+ clutter_input_focus_set_cursor_location (focus, &cursor_rect);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+text_input_v1_set_preferred_lanaguage (struct wl_client *client,
|
|
|
|
+ struct wl_resource *resource,
|
|
|
|
+ const char *language)
|
|
|
|
+{
|
|
|
|
+ /* ClutterInputMethod does not support this so this is useless. */
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * text-input-v1 is not required to be double-buffered!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
|
|
+ * commit_state just means "I am giving you a new serial and you should use
|
|
|
|
+ * this". It can work without commit_state, chromium does not send this.
|
|
|
|
+ */
|
|
|
|
+static void
|
|
|
|
+text_input_v1_commit_state (struct wl_client *client,
|
|
|
|
+ struct wl_resource *resource,
|
|
|
|
+ uint32_t serial)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1 *text_input = wl_resource_get_user_data (resource);
|
|
|
|
+
|
|
|
|
+ if (!client_matches_focus (text_input, client))
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ set_serial (text_input, resource, serial);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+text_input_v1_invoke_action (struct wl_client *client,
|
|
|
|
+ struct wl_resource *resource,
|
|
|
|
+ uint32_t button,
|
|
|
|
+ uint32_t index)
|
|
|
|
+{
|
|
|
|
+ /* There is no doc about what button and index are, I am not an invoker. */
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct zwp_text_input_v1_interface meta_text_input_v1_interface = {
|
|
|
|
+ text_input_v1_activate,
|
|
|
|
+ text_input_v1_deactivate,
|
|
|
|
+ text_input_v1_show_input_panel,
|
|
|
|
+ text_input_v1_hide_input_panel,
|
|
|
|
+ text_input_v1_reset,
|
|
|
|
+ text_input_v1_set_surrounding_text,
|
|
|
|
+ text_input_v1_set_content_type,
|
|
|
|
+ text_input_v1_set_cursor_rectangle,
|
|
|
|
+ text_input_v1_set_preferred_lanaguage,
|
|
|
|
+ text_input_v1_commit_state,
|
|
|
|
+ text_input_v1_invoke_action
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+meta_wayland_text_input_v1_destroy (MetaWaylandTextInputV1 *text_input)
|
|
|
|
+{
|
|
|
|
+ meta_wayland_text_input_v1_set_focus (text_input, NULL);
|
|
|
|
+ g_object_unref (text_input->input_focus);
|
|
|
|
+ g_hash_table_destroy (text_input->resource_serials);
|
|
|
|
+ g_clear_pointer (&text_input->surrounding.text, g_free);
|
|
|
|
+ g_free (text_input);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+meta_wayland_text_input_v1_create_new_resource (MetaWaylandTextInputV1 *text_input,
|
|
|
|
+ struct wl_client *client,
|
|
|
|
+ uint32_t id)
|
|
|
|
+{
|
|
|
|
+ struct wl_resource *text_input_resource;
|
|
|
|
+
|
|
|
|
+ text_input_resource = wl_resource_create (client,
|
|
|
|
+ &zwp_text_input_v1_interface,
|
|
|
|
+ META_ZWP_TEXT_INPUT_V1_VERSION,
|
|
|
|
+ id);
|
|
|
|
+
|
|
|
|
+ wl_resource_set_implementation (text_input_resource,
|
|
|
|
+ &meta_text_input_v1_interface,
|
|
|
|
+ text_input, text_input_v1_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_v1_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_v1_get_text_input (struct wl_client *client,
|
|
|
|
+ struct wl_resource *resource,
|
|
|
|
+ uint32_t id)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1 *text_input = wl_resource_get_user_data (resource);
|
|
|
|
+
|
|
|
|
+ meta_wayland_text_input_v1_create_new_resource (text_input, client, id);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct zwp_text_input_manager_v1_interface meta_text_input_manager_v1_interface = {
|
|
|
|
+ text_input_manager_v1_get_text_input
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+bind_text_input_v1 (struct wl_client *client,
|
|
|
|
+ void *data,
|
|
|
|
+ uint32_t version,
|
|
|
|
+ uint32_t id)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1 *text_input = data;
|
|
|
|
+ struct wl_resource *resource;
|
|
|
|
+
|
|
|
|
+ resource = wl_resource_create (client,
|
|
|
|
+ &zwp_text_input_manager_v1_interface,
|
|
|
|
+ META_ZWP_TEXT_INPUT_V1_VERSION,
|
|
|
|
+ id);
|
|
|
|
+ wl_resource_set_implementation (resource,
|
|
|
|
+ &meta_text_input_manager_v1_interface,
|
|
|
|
+ text_input, NULL);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+gboolean
|
|
|
|
+meta_wayland_text_input_v1_init (MetaWaylandCompositor *compositor)
|
|
|
|
+{
|
|
|
|
+ return (wl_global_create (compositor->wayland_display,
|
|
|
|
+ &zwp_text_input_manager_v1_interface,
|
|
|
|
+ META_ZWP_TEXT_INPUT_V1_VERSION,
|
|
|
|
+ compositor->seat->text_input_v1,
|
|
|
|
+ bind_text_input_v1) != NULL);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+MetaWaylandTextInputV1 *
|
|
|
|
+meta_wayland_text_input_v1_new (MetaWaylandSeat *seat)
|
|
|
|
+{
|
|
|
|
+ MetaWaylandTextInputV1 *text_input;
|
|
|
|
+
|
|
|
|
+ text_input = g_new0 (MetaWaylandTextInputV1, 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_v1_handle_focus_surface_destroy;
|
|
|
|
+
|
|
|
|
+ text_input->resource_serials = g_hash_table_new (NULL, NULL);
|
|
|
|
+
|
|
|
|
+ return text_input;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* This function eats key events and will send them to input method. */
|
|
|
|
+gboolean
|
|
|
|
+meta_wayland_text_input_v1_update (MetaWaylandTextInputV1 *text_input,
|
|
|
|
+ const ClutterEvent *event)
|
|
|
|
+{
|
|
|
|
+ ClutterInputFocus *focus = text_input->input_focus;
|
|
|
|
+ ClutterEventType event_type;
|
|
|
|
+
|
|
|
|
+ if (!text_input->surface || !clutter_input_focus_is_focused (focus))
|
|
|
|
+ return FALSE;
|
|
|
|
+
|
|
|
|
+ event_type = clutter_event_type (event);
|
|
|
|
+
|
|
|
|
+ if (event_type == CLUTTER_KEY_PRESS ||
|
|
|
|
+ event_type == CLUTTER_KEY_RELEASE)
|
|
|
|
+ {
|
|
|
|
+ gboolean filtered = FALSE;
|
|
|
|
+
|
|
|
|
+ filtered = clutter_input_focus_filter_event (focus, event);
|
|
|
|
+
|
|
|
|
+ return filtered;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return FALSE;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+gboolean
|
|
|
|
+meta_wayland_text_input_v1_handle_event (MetaWaylandTextInputV1 *text_input,
|
|
|
|
+ const ClutterEvent *event)
|
|
|
|
+{
|
|
|
|
+ ClutterInputFocus *focus = text_input->input_focus;
|
|
|
|
+ ClutterEventType event_type;
|
|
|
|
+ gboolean retval;
|
|
|
|
+
|
|
|
|
+ if (!text_input->surface || !clutter_input_focus_is_focused (focus))
|
|
|
|
+ return FALSE;
|
|
|
|
+
|
|
|
|
+ event_type = clutter_event_type (event);
|
|
|
|
+
|
|
|
|
+ retval = clutter_input_focus_process_event (focus, event);
|
|
|
|
+
|
|
|
|
+ if (event_type == CLUTTER_BUTTON_PRESS || event_type == CLUTTER_TOUCH_BEGIN)
|
|
|
|
+ {
|
|
|
|
+ MetaWaylandSurface *surface = NULL;
|
|
|
|
+ MetaBackend *backend;
|
|
|
|
+ ClutterStage *stage;
|
|
|
|
+ ClutterActor *actor;
|
|
|
|
+
|
|
|
|
+ backend = backend_from_text_input_v1 (text_input);
|
|
|
|
+ stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
|
|
|
|
+
|
|
|
|
+ actor = clutter_stage_get_device_actor (stage,
|
|
|
|
+ clutter_event_get_device (event),
|
|
|
|
+ clutter_event_get_event_sequence (event));
|
|
|
|
+
|
|
|
|
+ if (META_IS_SURFACE_ACTOR_WAYLAND (actor))
|
|
|
|
+ {
|
|
|
|
+ MetaSurfaceActorWayland *actor_wayland =
|
|
|
|
+ META_SURFACE_ACTOR_WAYLAND (actor);
|
|
|
|
+
|
|
|
|
+ surface = meta_surface_actor_wayland_get_surface (actor_wayland);
|
|
|
|
+
|
|
|
|
+ if (surface == text_input->surface)
|
|
|
|
+ clutter_input_focus_reset (focus);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return retval;
|
|
|
|
+}
|
2024-11-12 12:17:00 +01:00
|
|
|
Index: mutter-47.beta/src/wayland/meta-wayland-text-input-v1.h
|
|
|
|
===================================================================
|
|
|
|
--- /dev/null
|
|
|
|
+++ mutter-47.beta/src/wayland/meta-wayland-text-input-v1.h
|
2024-07-22 17:41:29 +02:00
|
|
|
@@ -0,0 +1,38 @@
|
|
|
|
+/*
|
|
|
|
+ * Copyright (C) 2024 SUSE LLC
|
|
|
|
+ *
|
|
|
|
+ * 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, see <http://www.gnu.org/licenses/>.
|
|
|
|
+ *
|
|
|
|
+ * Author: Alynx Zhou <alynx.zhou@gmail.com>
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#pragma once
|
|
|
|
+
|
|
|
|
+#include <wayland-server.h>
|
|
|
|
+
|
|
|
|
+#include "meta/window.h"
|
|
|
|
+#include "wayland/meta-wayland-types.h"
|
|
|
|
+
|
|
|
|
+typedef struct _MetaWaylandTextInputV1 MetaWaylandTextInputV1;
|
|
|
|
+
|
|
|
|
+MetaWaylandTextInputV1 * meta_wayland_text_input_v1_new (MetaWaylandSeat *seat);
|
|
|
|
+void meta_wayland_text_input_v1_destroy (MetaWaylandTextInputV1 *text_input);
|
|
|
|
+
|
|
|
|
+gboolean meta_wayland_text_input_v1_init (MetaWaylandCompositor *compositor);
|
|
|
|
+
|
|
|
|
+gboolean meta_wayland_text_input_v1_update (MetaWaylandTextInputV1 *text_input,
|
|
|
|
+ const ClutterEvent *event);
|
|
|
|
+
|
|
|
|
+gboolean meta_wayland_text_input_v1_handle_event (MetaWaylandTextInputV1 *text_input,
|
|
|
|
+ const ClutterEvent *event);
|
2024-11-12 12:17:00 +01:00
|
|
|
Index: mutter-47.beta/src/wayland/meta-wayland-versions.h
|
|
|
|
===================================================================
|
|
|
|
--- mutter-47.beta.orig/src/wayland/meta-wayland-versions.h
|
|
|
|
+++ mutter-47.beta/src/wayland/meta-wayland-versions.h
|
2024-07-22 17:41:29 +02:00
|
|
|
@@ -49,6 +49,7 @@
|
|
|
|
#define META_ZXDG_OUTPUT_V1_VERSION 3
|
|
|
|
#define META_ZWP_XWAYLAND_KEYBOARD_GRAB_V1_VERSION 1
|
|
|
|
#define META_ZWP_TEXT_INPUT_V3_VERSION 1
|
|
|
|
+#define META_ZWP_TEXT_INPUT_V1_VERSION 1
|
|
|
|
#define META_WP_VIEWPORTER_VERSION 1
|
|
|
|
#define META_ZWP_PRIMARY_SELECTION_V1_VERSION 1
|
|
|
|
#define META_WP_PRESENTATION_VERSION 1
|
2024-11-12 12:17:00 +01:00
|
|
|
Index: mutter-47.beta/src/wayland/meta-wayland.c
|
|
|
|
===================================================================
|
|
|
|
--- mutter-47.beta.orig/src/wayland/meta-wayland.c
|
|
|
|
+++ mutter-47.beta/src/wayland/meta-wayland.c
|
|
|
|
@@ -869,6 +869,7 @@ meta_wayland_compositor_new (MetaContext
|
|
|
|
meta_wayland_keyboard_shortcuts_inhibit_init (compositor);
|
|
|
|
meta_wayland_surface_inhibit_shortcuts_dialog_init ();
|
|
|
|
meta_wayland_text_input_init (compositor);
|
|
|
|
+ meta_wayland_text_input_v1_init (compositor);
|
|
|
|
meta_wayland_init_presentation_time (compositor);
|
|
|
|
meta_wayland_activation_init (compositor);
|
|
|
|
meta_wayland_transaction_init (compositor);
|
|
|
|
@@ -1133,6 +1134,12 @@ meta_wayland_compositor_get_text_input (
|
|
|
|
return compositor->seat->text_input;
|
|
|
|
}
|
|
|
|
|
|
|
|
+MetaWaylandTextInputV1 *
|
|
|
|
+meta_wayland_compositor_get_text_input_v1 (MetaWaylandCompositor *compositor)
|
|
|
|
+{
|
|
|
|
+ return compositor->seat->text_input_v1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
static void
|
|
|
|
meta_wayland_compositor_update_focus (MetaWaylandCompositor *compositor,
|
|
|
|
MetaWindow *window)
|
|
|
|
Index: mutter-47.beta/src/wayland/meta-wayland.h
|
|
|
|
===================================================================
|
|
|
|
--- mutter-47.beta.orig/src/wayland/meta-wayland.h
|
|
|
|
+++ mutter-47.beta/src/wayland/meta-wayland.h
|
|
|
|
@@ -26,6 +26,7 @@
|
|
|
|
#include "meta/types.h"
|
|
|
|
#include "meta/meta-wayland-compositor.h"
|
|
|
|
#include "wayland/meta-wayland-text-input.h"
|
|
|
|
+#include "wayland/meta-wayland-text-input-v1.h"
|
|
|
|
#include "wayland/meta-wayland-types.h"
|
|
|
|
|
|
|
|
META_EXPORT_TEST
|
|
|
|
@@ -88,6 +89,7 @@ void meta_wayland_com
|
|
|
|
MetaWindow *window);
|
|
|
|
|
|
|
|
MetaWaylandTextInput * meta_wayland_compositor_get_text_input (MetaWaylandCompositor *compositor);
|
|
|
|
+MetaWaylandTextInputV1 * meta_wayland_compositor_get_text_input_v1 (MetaWaylandCompositor *compositor);
|
|
|
|
|
|
|
|
#ifdef HAVE_XWAYLAND
|
|
|
|
void meta_wayland_compositor_notify_surface_id (MetaWaylandCompositor *compositor,
|