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