/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2017 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see .
*/
#include
#include
#include
#include
#include
#include "pointer-constraints-unstable-v1-client-protocol.h"
#include "relative-pointer-unstable-v1-client-protocol.h"
#include "wayland-extensions.h"
static void *
gtk_wl_registry_bind(GtkWidget *widget,
uint32_t name,
const struct wl_interface *interface,
uint32_t version)
{
GdkDisplay *gdk_display = gtk_widget_get_display(widget);
struct wl_display *display;
struct wl_registry *registry;
if (!GDK_IS_WAYLAND_DISPLAY(gdk_display)) {
return NULL;
}
display = gdk_wayland_display_get_wl_display(gdk_display);
registry = wl_display_get_registry(display);
return wl_registry_bind(registry, name, interface, version);
}
static void
gtk_wl_registry_add_listener(GtkWidget *widget, const struct wl_registry_listener *listener)
{
GdkDisplay *gdk_display = gtk_widget_get_display(widget);
struct wl_display *display;
struct wl_registry *registry;
if (!GDK_IS_WAYLAND_DISPLAY(gdk_display)) {
return;
}
display = gdk_wayland_display_get_wl_display(gdk_display);
registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, listener, widget);
wl_display_roundtrip(display);
}
static void
registry_handle_global(void *data,
struct wl_registry *registry,
uint32_t name,
const char *interface,
uint32_t version)
{
GtkWidget *widget = GTK_WIDGET(data);
if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) {
struct zwp_relative_pointer_manager_v1 *relative_pointer_manager;
relative_pointer_manager = gtk_wl_registry_bind(widget, name,
&zwp_relative_pointer_manager_v1_interface,
1);
g_object_set_data_full(G_OBJECT(widget),
"zwp_relative_pointer_manager_v1",
relative_pointer_manager,
(GDestroyNotify)zwp_relative_pointer_manager_v1_destroy);
} else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) {
struct zwp_pointer_constraints_v1 *pointer_constraints;
pointer_constraints = gtk_wl_registry_bind(widget, name,
&zwp_pointer_constraints_v1_interface,
1);
g_object_set_data_full(G_OBJECT(widget),
"zwp_pointer_constraints_v1",
pointer_constraints,
(GDestroyNotify)zwp_pointer_constraints_v1_destroy);
}
}
static void
registry_handle_global_remove(void *data,
struct wl_registry *registry,
uint32_t name)
{
}
static const struct wl_registry_listener registry_listener = {
registry_handle_global,
registry_handle_global_remove
};
void
spice_wayland_extensions_init(GtkWidget *widget)
{
g_return_if_fail(GTK_IS_WIDGET(widget));
gtk_wl_registry_add_listener(widget, ®istry_listener);
}
static GdkDevice *
spice_gdk_window_get_pointing_device(GdkWindow *window)
{
GdkDisplay *gdk_display = gdk_window_get_display(window);
return gdk_seat_get_pointer(gdk_display_get_default_seat(gdk_display));
}
static struct zwp_relative_pointer_v1_listener relative_pointer_listener;
// NOTE this API works only on a single widget per application
int
spice_wayland_extensions_enable_relative_pointer(GtkWidget *widget,
void (*cb)(void *,
struct zwp_relative_pointer_v1 *,
uint32_t, uint32_t,
wl_fixed_t, wl_fixed_t, wl_fixed_t, wl_fixed_t))
{
struct zwp_relative_pointer_v1 *relative_pointer;
g_return_val_if_fail(GTK_IS_WIDGET(widget), -1);
relative_pointer = g_object_get_data(G_OBJECT(widget), "zwp_relative_pointer_v1");
if (relative_pointer == NULL) {
struct zwp_relative_pointer_manager_v1 *relative_pointer_manager;
GdkWindow *window = gtk_widget_get_window(widget);
struct wl_pointer *pointer;
relative_pointer_manager = g_object_get_data(G_OBJECT(widget), "zwp_relative_pointer_manager_v1");
if (relative_pointer_manager == NULL)
return -1;
pointer = gdk_wayland_device_get_wl_pointer(spice_gdk_window_get_pointing_device(window));
relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(relative_pointer_manager,
pointer);
relative_pointer_listener.relative_motion = cb;
zwp_relative_pointer_v1_add_listener(relative_pointer,
&relative_pointer_listener,
widget);
g_object_set_data_full(G_OBJECT(widget),
"zwp_relative_pointer_v1",
relative_pointer,
(GDestroyNotify)zwp_relative_pointer_v1_destroy);
}
return 0;
}
int spice_wayland_extensions_disable_relative_pointer(GtkWidget *widget)
{
g_return_val_if_fail(GTK_IS_WIDGET(widget), -1);
/* This will call zwp_relative_pointer_v1_destroy() and stop relative
* movement */
g_object_set_data(G_OBJECT(widget), "zwp_relative_pointer_v1", NULL);
return 0;
}
static struct zwp_locked_pointer_v1_listener locked_pointer_listener;
// NOTE this API works only on a single widget per application
int
spice_wayland_extensions_lock_pointer(GtkWidget *widget,
void (*lock_cb)(void *, struct zwp_locked_pointer_v1 *),
void (*unlock_cb)(void *, struct zwp_locked_pointer_v1 *))
{
struct zwp_pointer_constraints_v1 *pointer_constraints;
struct zwp_locked_pointer_v1 *locked_pointer;
GdkWindow *window;
struct wl_pointer *pointer;
g_return_val_if_fail(GTK_IS_WIDGET(widget), -1);
pointer_constraints = g_object_get_data(G_OBJECT(widget), "zwp_pointer_constraints_v1");
locked_pointer = g_object_get_data(G_OBJECT(widget), "zwp_locked_pointer_v1");
if (locked_pointer != NULL) {
/* A previous lock already in place */
return 0;
}
window = gtk_widget_get_window(widget);
pointer = gdk_wayland_device_get_wl_pointer(spice_gdk_window_get_pointing_device(window));
locked_pointer = zwp_pointer_constraints_v1_lock_pointer(pointer_constraints,
gdk_wayland_window_get_wl_surface(window),
pointer,
NULL,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
if (lock_cb || unlock_cb) {
locked_pointer_listener.locked = lock_cb;
locked_pointer_listener.unlocked = unlock_cb;
zwp_locked_pointer_v1_add_listener(locked_pointer,
&locked_pointer_listener,
widget);
}
g_object_set_data_full(G_OBJECT(widget),
"zwp_locked_pointer_v1",
locked_pointer,
(GDestroyNotify)zwp_locked_pointer_v1_destroy);
return 0;
}
int
spice_wayland_extensions_unlock_pointer(GtkWidget *widget)
{
g_return_val_if_fail(GTK_IS_WIDGET(widget), -1);
g_object_set_data(G_OBJECT(widget), "zwp_locked_pointer_v1", NULL);
return 0;
}