summarylogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.SRCINFO13
-rw-r--r--PKGBUILD32
-rw-r--r--mr3751.patch1167
3 files changed, 1197 insertions, 15 deletions
diff --git a/.SRCINFO b/.SRCINFO
index f11ca1390dc7..05da32819637 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,7 +1,7 @@
pkgbase = mutter-performance
pkgdesc = A window manager for GNOME | Attempts to improve performances with non-upstreamed merge-requests and frequent stable branch resync
- pkgver = 46.1+r7+g35836f0f1
- pkgrel = 3
+ pkgver = 46.1+r8+gc23274cd2
+ pkgrel = 1
epoch = 1
url = https://gitlab.gnome.org/GNOME/mutter
arch = x86_64
@@ -73,24 +73,27 @@ pkgbase = mutter-performance
depends = systemd-libs
depends = wayland
depends = xorg-xwayland
- source = git+https://gitlab.gnome.org/GNOME/mutter.git#commit=35836f0f1a526a846d909b31f185dc838beb0664
+ source = git+https://gitlab.gnome.org/GNOME/mutter.git#commit=c23274cd2344fddab12a60e221a966322a3eb9cc
source = mr1441.patch
source = mr3373.patch
source = mr3720.patch
source = mr3729.patch
source = mr3742.patch
- sha256sums = e5f178cc9692498c6cc3d6466102c8906f77dd112c12efb4762a903a28a574f5
+ source = mr3751.patch
+ sha256sums = 8853e7359867d4c739fcc5d47f649c04a823109a01ef863b13f3a49d2c51020c
sha256sums = 7d5c90e3f18b2c0645e6224759e571acef723d53495cff2ceaac3c44168226b3
sha256sums = 3e1f07b696ad37b1c639a524c092cd9259444bc6156542901ccaec936bea240f
sha256sums = c6070384f7272813a8ac6f5e27e0216e4c7da168adbe697f46578e0b4494c89a
sha256sums = c072d1983ddd482db993514e86e15fb44024887c5c4d7c1d6f4cf4b8d01743ce
sha256sums = 204dee1642fcd4818d1bb8f161e7c68a6b11754cf0354b6ea92ead36a1b50e94
- b2sums = 28f0cba4142dee6526ddc1147e26e32d875674a927bfda271b3abbc65ddda4021e6765bf74390e37718056b5b5ab44c5b5566ca20f23dc4e846887ed79961d26
+ sha256sums = 7eec91f857cf1d8183c32199f33cc86a1a8a95017282e602738a90fe219c8b33
+ b2sums = f9a995a1e289e8a2e8afa54610e411b33f9a6b136a529d0eaebfecb63f9639f9621171543fac5384dd3d85b4b8094e68fe7af3886add4736044afcbf7d1363ba
b2sums = 135b9a24273328c8010d08c3688b527741b0d678814ed52fb983be649b7d40ce6f2d6151d1087a8fed311f35cb3033d8b9d1c7b04cdd2bbef7dff43ee80ea530
b2sums = 71f10db4ebe04a787940c7048131eac67cffd3ec8e415cfc961b8041b881f272650581e9df273e2a8da23a50ec9151c790dc2d5ecc0309ab2847a22f8c922c9c
b2sums = 0e77ab7c0477554eaa5060ad8ca3c509fc092ca63e2f40e9db21bdb54420fce568982a39ee4f1ec5bd60fd298633359205b1112cf03cfedc9924c4e801748790
b2sums = a1b283fc3b45160d740702f8bee3c89d6dcef7ecedece5e01b4f1a414ea85a1251481af7b94e2c77e5a895cd93d3b708df97e80749a69edb3b0108bef8c2bf9f
b2sums = aa658b3717048507b627abbe9dfaf574e78f944bfa7b34a95f82ea551e291a4b6413568fe70fba20c18e58e30a01040add94f66afda52228651d96d6730b56de
+ b2sums = a79155ce4f11d8ccc674b08baccb18ac3bf254a0e41d9830a0ca964027633317bbd157f33b1f12f21bb378bfeedd4ebd111d993cdfd39b98ff3ce0f34850f6b1
pkgname = mutter-performance
groups = gnome
diff --git a/PKGBUILD b/PKGBUILD
index f70943976dea..764c522e687a 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -11,8 +11,8 @@
### PACKAGE OPTIONS
## MERGE REQUESTS SELECTION
-# Merge Requests List: ('579' '1441' '3373' '3720' '3729' '3742')
-_merge_requests_to_use=('1441' '3373' '3720' '3729' '3742')
+# Merge Requests List: ('579' '1441' '3373' '3720' '3729' '3742' '3751')
+_merge_requests_to_use=('1441' '3373' '3720' '3729' '3742' '3751')
## Disable building the DOCS package (Enabled if not set)
# Remember to unset this variable when producing .SRCINFO
@@ -32,8 +32,8 @@ else
pkgname=(mutter-performance mutter-performance-docs)
fi
epoch=1
-pkgver=46.1+r7+g35836f0f1
-pkgrel=3
+pkgver=46.1+r8+gc23274cd2
+pkgrel=1
pkgdesc="A window manager for GNOME | Attempts to improve performances with non-upstreamed merge-requests and frequent stable branch resync"
url="https://gitlab.gnome.org/GNOME/mutter"
arch=(x86_64 aarch64)
@@ -112,25 +112,28 @@ makedepends=(
if [ -n "$_enable_check" ]; then
checkdepends=(gnome-session xorg-server-xvfb pipewire-session-manager python-dbusmock zenity)
fi
-_commit=35836f0f1a526a846d909b31f185dc838beb0664 # tags/46.1^7
+_commit=c23274cd2344fddab12a60e221a966322a3eb9cc # tags/46.1^8
source=("git+$url.git#commit=$_commit"
'mr1441.patch'
'mr3373.patch'
'mr3720.patch'
'mr3729.patch'
- 'mr3742.patch')
-sha256sums=('e5f178cc9692498c6cc3d6466102c8906f77dd112c12efb4762a903a28a574f5'
+ 'mr3742.patch'
+ 'mr3751.patch')
+sha256sums=('8853e7359867d4c739fcc5d47f649c04a823109a01ef863b13f3a49d2c51020c'
'7d5c90e3f18b2c0645e6224759e571acef723d53495cff2ceaac3c44168226b3'
'3e1f07b696ad37b1c639a524c092cd9259444bc6156542901ccaec936bea240f'
'c6070384f7272813a8ac6f5e27e0216e4c7da168adbe697f46578e0b4494c89a'
'c072d1983ddd482db993514e86e15fb44024887c5c4d7c1d6f4cf4b8d01743ce'
- '204dee1642fcd4818d1bb8f161e7c68a6b11754cf0354b6ea92ead36a1b50e94')
-b2sums=('28f0cba4142dee6526ddc1147e26e32d875674a927bfda271b3abbc65ddda4021e6765bf74390e37718056b5b5ab44c5b5566ca20f23dc4e846887ed79961d26'
+ '204dee1642fcd4818d1bb8f161e7c68a6b11754cf0354b6ea92ead36a1b50e94'
+ '7eec91f857cf1d8183c32199f33cc86a1a8a95017282e602738a90fe219c8b33')
+b2sums=('f9a995a1e289e8a2e8afa54610e411b33f9a6b136a529d0eaebfecb63f9639f9621171543fac5384dd3d85b4b8094e68fe7af3886add4736044afcbf7d1363ba'
'135b9a24273328c8010d08c3688b527741b0d678814ed52fb983be649b7d40ce6f2d6151d1087a8fed311f35cb3033d8b9d1c7b04cdd2bbef7dff43ee80ea530'
'71f10db4ebe04a787940c7048131eac67cffd3ec8e415cfc961b8041b881f272650581e9df273e2a8da23a50ec9151c790dc2d5ecc0309ab2847a22f8c922c9c'
'0e77ab7c0477554eaa5060ad8ca3c509fc092ca63e2f40e9db21bdb54420fce568982a39ee4f1ec5bd60fd298633359205b1112cf03cfedc9924c4e801748790'
'a1b283fc3b45160d740702f8bee3c89d6dcef7ecedece5e01b4f1a414ea85a1251481af7b94e2c77e5a895cd93d3b708df97e80749a69edb3b0108bef8c2bf9f'
- 'aa658b3717048507b627abbe9dfaf574e78f944bfa7b34a95f82ea551e291a4b6413568fe70fba20c18e58e30a01040add94f66afda52228651d96d6730b56de')
+ 'aa658b3717048507b627abbe9dfaf574e78f944bfa7b34a95f82ea551e291a4b6413568fe70fba20c18e58e30a01040add94f66afda52228651d96d6730b56de'
+ 'a79155ce4f11d8ccc674b08baccb18ac3bf254a0e41d9830a0ca964027633317bbd157f33b1f12f21bb378bfeedd4ebd111d993cdfd39b98ff3ce0f34850f6b1')
pkgver() {
cd $_pkgname
@@ -246,6 +249,15 @@ prepare() {
# monitors.
pick_mr '3742' 'mr3742.patch' 'patch'
+ # Title: wayland/text-input-v1: Implement basic text-input-v1 support
+ # Author: Alynx Zhou <alynx.zhou@gmail.com>
+ # URL: https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3751
+ # Type: 1
+ # Status: Not gonna be merged considering the maintainers' feedbacks.
+ # Comment: This commit makes input methods work in text-input-v1 only clients.
+ # (mostly Chromium/Electron based apps with Ozone Wayland.)
+ pick_mr '3751' 'mr3751.patch' 'patch'
+
# Title: Draft: Dynamic triple/double buffering (v4)
# Author: Daniel van Vugt <daniel.van.vugt@canonical.com>
# URL: https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1441
diff --git a/mr3751.patch b/mr3751.patch
new file mode 100644
index 000000000000..5b4706a90ffa
--- /dev/null
+++ b/mr3751.patch
@@ -0,0 +1,1167 @@
+From e12da9d8a8f747a7eeebfc9e399cd6f664d6abf3 Mon Sep 17 00:00:00 2001
+From: Alynx Zhou <alynx.zhou@gmail.com>
+Date: Wed, 15 May 2024 00:05:55 +0800
+Subject: [PATCH 1/2] wayland/text-input: Remove unused repeated code
+
+Maybe they are generated by copy during refactor.
+---
+ src/wayland/meta-wayland-text-input.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/src/wayland/meta-wayland-text-input.c b/src/wayland/meta-wayland-text-input.c
+index 04ee51dbaa5..6ae226131c4 100644
+--- a/src/wayland/meta-wayland-text-input.c
++++ b/src/wayland/meta-wayland-text-input.c
+@@ -302,9 +302,6 @@ meta_wayland_text_input_focus_set_preedit_text (ClutterInputFocus *focus,
+
+ text_input = META_WAYLAND_TEXT_INPUT_FOCUS (focus)->text_input;
+
+- if (text)
+- pos = g_utf8_offset_to_pointer (text, cursor) - text;
+-
+ g_clear_pointer (&text_input->preedit.string, g_free);
+ text_input->preedit.string = g_strdup (text);
+
+--
+GitLab
+
+
+From 6b9bbebbdc3a8b35f898a269227f36a36590359e 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
+
+diff --git a/clutter/clutter/clutter-enums.h b/clutter/clutter/clutter-enums.h
+index 45956bf57b2..8198e73516f 100644
+--- a/clutter/clutter/clutter-enums.h
++++ b/clutter/clutter/clutter-enums.h
+@@ -1183,6 +1183,9 @@ typedef enum
+ 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
+diff --git a/src/core/events.c b/src/core/events.c
+index 4261bfb98a8..35b94e18768 100644
+--- a/src/core/events.c
++++ b/src/core/events.c
+@@ -238,6 +238,7 @@ meta_display_handle_event (MetaDisplay *display,
+ #ifdef HAVE_WAYLAND
+ MetaWaylandCompositor *wayland_compositor;
+ MetaWaylandTextInput *wayland_text_input = NULL;
++ MetaWaylandTextInputV1 *wayland_text_input_v1 = NULL;
+ #endif
+
+ #ifdef HAVE_WAYLAND
+@@ -246,6 +247,8 @@ meta_display_handle_event (MetaDisplay *display,
+ {
+ 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
+
+@@ -287,10 +290,12 @@ meta_display_handle_event (MetaDisplay *display,
+ }
+
+ #ifdef HAVE_WAYLAND
+- if (wayland_text_input &&
+- !has_grab &&
++ if (!has_grab &&
+ !meta_compositor_get_current_window_drag (compositor) &&
+- meta_wayland_text_input_update (wayland_text_input, event))
++ ((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)
+diff --git a/src/meson.build b/src/meson.build
+index 3060b28802b..12a249c540e 100644
+--- a/src/meson.build
++++ b/src/meson.build
+@@ -687,6 +687,8 @@ if have_wayland
+ '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',
+@@ -1075,6 +1077,7 @@ if have_wayland
+ ['single-pixel-buffer', 'staging', 'v1', ],
+ ['tablet', 'unstable', 'v2', ],
+ ['text-input', 'unstable', 'v3', ],
++ ['text-input', 'unstable', 'v1', ],
+ ['viewporter', 'stable', ],
+ ['xdg-activation', 'staging', 'v1', ],
+ ['xdg-foreign', 'unstable', 'v1', ],
+diff --git a/src/wayland/meta-wayland-seat.c b/src/wayland/meta-wayland-seat.c
+index f8d58a61252..fca0b346db7 100644
+--- a/src/wayland/meta-wayland-seat.c
++++ b/src/wayland/meta-wayland-seat.c
+@@ -228,6 +228,7 @@ default_focus (MetaWaylandEventHandler *handler,
+ meta_wayland_data_device_primary_sync_focus (&seat->primary_data_device);
+ 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. */
+ }
+
+ if (caps & CLUTTER_INPUT_CAPABILITY_TABLET_TOOL)
+@@ -289,6 +290,8 @@ meta_wayland_seat_new (MetaWaylandCompositor *compositor,
+ 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);
+@@ -337,6 +340,7 @@ meta_wayland_seat_free (MetaWaylandSeat *seat)
+ 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);
+ }
+@@ -477,7 +481,10 @@ meta_wayland_seat_handle_event_internal (MetaWaylandSeat *seat,
+ 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)
+@@ -509,7 +516,8 @@ meta_wayland_seat_handle_event_internal (MetaWaylandSeat *seat,
+ 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;
+diff --git a/src/wayland/meta-wayland-seat.h b/src/wayland/meta-wayland-seat.h
+index 169a92e4e17..10633b02947 100644
+--- a/src/wayland/meta-wayland-seat.h
++++ b/src/wayland/meta-wayland-seat.h
+@@ -30,6 +30,7 @@
+ #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"
+
+@@ -49,6 +50,7 @@ struct _MetaWaylandSeat
+ MetaWaylandDataDevicePrimary primary_data_device;
+
+ MetaWaylandTextInput *text_input;
++ MetaWaylandTextInputV1 *text_input_v1;
+
+ MetaWaylandInput *input_handler;
+ MetaWaylandEventHandler *default_handler;
+diff --git a/src/wayland/meta-wayland-text-input-v1.c b/src/wayland/meta-wayland-text-input-v1.c
+new file mode 100644
+index 00000000000..1826a4ff43e
+--- /dev/null
++++ b/src/wayland/meta-wayland-text-input-v1.c
+@@ -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;
++}
+diff --git a/src/wayland/meta-wayland-text-input-v1.h b/src/wayland/meta-wayland-text-input-v1.h
+new file mode 100644
+index 00000000000..79b1c0a5413
+--- /dev/null
++++ b/src/wayland/meta-wayland-text-input-v1.h
+@@ -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);
+diff --git a/src/wayland/meta-wayland-versions.h b/src/wayland/meta-wayland-versions.h
+index 900f30d7888..a77b81461b7 100644
+--- a/src/wayland/meta-wayland-versions.h
++++ b/src/wayland/meta-wayland-versions.h
+@@ -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
+diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
+index 501b69a91c0..0114823c145 100644
+--- a/src/wayland/meta-wayland.c
++++ b/src/wayland/meta-wayland.c
+@@ -865,6 +865,7 @@ meta_wayland_compositor_new (MetaContext *context)
+ 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);
+@@ -1124,6 +1125,12 @@ meta_wayland_compositor_get_text_input (MetaWaylandCompositor *compositor)
+ 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)
+diff --git a/src/wayland/meta-wayland.h b/src/wayland/meta-wayland.h
+index 0a0476eba0b..c23e82cdcae 100644
+--- a/src/wayland/meta-wayland.h
++++ b/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_compositor_schedule_surface_association (Me
+ 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,
+--
+GitLab
+