Wayland: much lighter but partial implementation of the "GTK Shell" protocol

Only the middle-button click gesture is implemented which avoids interference
with what libdecor does with right-click and double-click.
This commit is contained in:
ManoloFLTK 2023-12-05 22:53:26 +01:00
parent 68594ec7fa
commit 18ccbb4a4f
4 changed files with 20 additions and 248 deletions

View File

@ -31,15 +31,10 @@
# define HAVE_GTK 0
#endif
#if HAVE_GTK
# include "gtk-shell-client-protocol.h"
#endif
#if USE_SYSTEM_LIBDECOR
#include "../src/libdecor-plugin.h"
#if HAVE_GTK
#include <linux/input.h>
#include "gtk-shell-protocol.c"
#endif
enum zxdg_toplevel_decoration_v1_mode {
@ -237,55 +232,6 @@ struct libdecor_frame_gtk {
struct wl_list link;
};
#if USE_SYSTEM_LIBDECOR
struct libdecor_plugin_gtk {
struct libdecor_plugin plugin;
struct wl_callback *globals_callback;
struct wl_callback *globals_callback_shm;
struct libdecor *context;
struct wl_registry *wl_registry;
struct wl_subcompositor *wl_subcompositor;
struct wl_compositor *wl_compositor;
struct wl_shm *wl_shm;
struct wl_callback *shm_callback;
bool has_argb;
struct wl_list visible_frame_list;
struct wl_list seat_list;
struct wl_list output_list;
char *cursor_theme_name;
int cursor_size;
int double_click_time_ms;
};
struct seat {
struct libdecor_plugin_gtk *plugin_gtk;
char *name;
struct wl_seat *wl_seat;
struct wl_pointer *wl_pointer;
struct wl_touch *wl_touch;
struct wl_surface *cursor_surface;
struct wl_cursor *current_cursor;
int cursor_scale;
struct wl_list cursor_outputs;
struct wl_cursor_theme *cursor_theme;
struct wl_cursor *cursors[8]; // 8 replaces ARRAY_LENGTH(cursor_names)
struct wl_cursor *cursor_left_ptr;
struct wl_surface *pointer_focus;
struct wl_surface *touch_focus;
int pointer_x, pointer_y;
uint32_t pointer_button_time_stamp;
uint32_t touch_down_time_stamp;
uint32_t serial;
bool grabbed;
struct wl_list link;
};
static bool own_surface(struct wl_surface *surface)
{
return true;
}
#endif // USE_SYSTEM_LIBDECOR
#endif // USE_SYSTEM_LIBDECOR || !HAVE_GTK
@ -391,187 +337,3 @@ unsigned char *fl_libdecor_titlebar_buffer(struct libdecor_frame *frame,
}
return NULL;
}
/* === Beginning of code to add support of GTK Shell to libdecor-gtk === */
#if HAVE_GTK
static struct gtk_shell1 *gtk_shell = NULL;
static uint32_t doubleclick_time = 250;
// libdecor's button member of wl_pointer_listener objects
static void (*decor_pointer_button)(void*, struct wl_pointer *,
uint32_t ,
uint32_t ,
uint32_t ,
uint32_t);
// FLTK's replacement for button member of wl_pointer_listener objects
static void fltk_pointer_button(void *data,
struct wl_pointer *wl_pointer,
uint32_t serial,
uint32_t time,
uint32_t button,
uint32_t state) {
struct seat *seat = data;
struct libdecor_frame_gtk *frame_gtk;
if (!seat->pointer_focus || !own_surface(seat->pointer_focus))
return;
frame_gtk = wl_surface_get_user_data(seat->pointer_focus);
if (!frame_gtk)
return;
if (button == BTN_MIDDLE && state == WL_POINTER_BUTTON_STATE_PRESSED) {
struct gtk_surface1 *gtk_surface = gtk_shell1_get_gtk_surface(gtk_shell,
frame_gtk->headerbar.wl_surface);
gtk_surface1_titlebar_gesture(gtk_surface, serial,
seat->wl_seat, GTK_SURFACE1_GESTURE_MIDDLE_CLICK);
gtk_surface1_release(gtk_surface);
return;
} else if (button == BTN_RIGHT && state == WL_POINTER_BUTTON_STATE_PRESSED) {
struct gtk_surface1 *gtk_surface = gtk_shell1_get_gtk_surface(gtk_shell,
frame_gtk->headerbar.wl_surface);
gtk_surface1_titlebar_gesture(gtk_surface, serial,
seat->wl_seat, GTK_SURFACE1_GESTURE_RIGHT_CLICK);
gtk_surface1_release(gtk_surface);
return;
} else if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) {
if (time - seat->pointer_button_time_stamp < doubleclick_time) {
seat->pointer_button_time_stamp = 0;
struct gtk_surface1 *gtk_surface = gtk_shell1_get_gtk_surface(gtk_shell,
frame_gtk->headerbar.wl_surface);
gtk_surface1_titlebar_gesture(gtk_surface, serial,
seat->wl_seat, GTK_SURFACE1_GESTURE_DOUBLE_CLICK);
gtk_surface1_release(gtk_surface);
return;
}
}
decor_pointer_button(data, wl_pointer, serial, time, button, state);
}
#if USE_SYSTEM_LIBDECOR
static struct border_component_gtk *
get_component_for_surface(struct libdecor_frame_gtk *frame_gtk,
const struct wl_surface *surface)
{
if (frame_gtk->shadow.wl_surface == surface)
return &frame_gtk->shadow;
if (frame_gtk->headerbar.wl_surface == surface)
return &frame_gtk->headerbar;
return NULL;
}
#endif
// libdecor's touch_down member of wl_touch_listener objects
void (*decor_touch_down)(void *data, struct wl_touch *wl_touch, uint32_t serial,
uint32_t time, struct wl_surface *surface, int32_t id,
wl_fixed_t x, wl_fixed_t y);
// FLTK's replacement for touch_down member of wl_touch_listener objects
static void fltk_touch_down(void *data, struct wl_touch *wl_touch, uint32_t serial,
uint32_t time, struct wl_surface *surface, int32_t id, wl_fixed_t x, wl_fixed_t y)
{
struct seat *seat = data;
struct libdecor_frame_gtk *frame_gtk;
if (!surface || !own_surface(surface)) return;
frame_gtk = wl_surface_get_user_data(surface);
if (!frame_gtk) return;
seat->touch_focus = surface;
frame_gtk->touch_active = get_component_for_surface(frame_gtk, surface);
if (!frame_gtk->touch_active) return;
if (frame_gtk->touch_active->type == HEADER &&
frame_gtk->hdr_focus.type < HEADER_MIN &&
time - seat->touch_down_time_stamp <
(uint32_t)frame_gtk->plugin_gtk->double_click_time_ms
) {
struct gtk_surface1 *gtk_surface = gtk_shell1_get_gtk_surface(
gtk_shell, surface);
gtk_surface1_titlebar_gesture(gtk_surface, serial, seat->wl_seat,
GTK_SURFACE1_GESTURE_DOUBLE_CLICK);
gtk_surface1_release(gtk_surface);
} else {
decor_touch_down(data, wl_touch, serial, time, surface, id, x, y);
}
}
struct wl_object { // copied from wayland-private.h
const struct wl_interface *interface;
const void *implementation;
uint32_t id;
};
#endif // HAVE_GTK
// replace libdecor's pointer_button by FLTK's
void use_FLTK_pointer_button(struct libdecor_frame *frame) {
#if HAVE_GTK
static const char *my_plugin = NULL;
if (!my_plugin) my_plugin = get_libdecor_plugin_description(frame);
if (!my_plugin || strcmp(my_plugin, "GTK3 plugin")) return;
static struct wl_pointer_listener *fltk_listener = NULL;
if (!gtk_shell || fltk_listener) return;
struct libdecor_frame_gtk *lfg = (struct libdecor_frame_gtk *)frame;
if (wl_list_empty(&lfg->plugin_gtk->seat_list)) return;
struct seat *seat;
wl_list_for_each(seat, &lfg->plugin_gtk->seat_list, link) {
break;
}
struct wl_surface *surface = lfg->headerbar.wl_surface;
if (surface && !own_surface(surface)) {
// occurs if libdecor-gtk.so was dynamically loaded via LIBDECOR_PLUGIN_DIR
gtk_shell = NULL;
return;
}
struct wl_object *object = (struct wl_object *)seat->wl_pointer;
if (!object) return;
struct wl_pointer_listener *decor_listener =
(struct wl_pointer_listener*)object->implementation;
fltk_listener =
(struct wl_pointer_listener*)malloc(sizeof(struct wl_pointer_listener));
// initialize FLTK's listener with libdecor's values
*fltk_listener = *decor_listener;
// memorize libdecor's button cb
decor_pointer_button = decor_listener->button;
// replace libdecor's button by FLTK's
fltk_listener->button = fltk_pointer_button;
// replace the pointer listener by a copy whose button member is FLTK's
object->implementation = fltk_listener;
object = (struct wl_object *)seat->wl_touch;
if (object) {
struct wl_touch_listener *decor_touch_listener =
(struct wl_touch_listener*)object->implementation;
struct wl_touch_listener *fltk_touch_listener =
(struct wl_touch_listener*)malloc(sizeof(struct wl_touch_listener));
// initialize FLTK's touch listener with libdecor's values
*fltk_touch_listener = *decor_touch_listener;
// memorize libdecor's down cb
decor_touch_down = decor_touch_listener->down;
// replace libdecor's down by FLTK's
fltk_touch_listener->down = fltk_touch_down;
// replace the touch listener by a copy whose down member is FLTK's
object->implementation = fltk_touch_listener;
}
// get gnome's double_click_time_ms value
doubleclick_time = lfg->plugin_gtk->double_click_time_ms;
#endif // HAVE_GTK
}
void bind_to_gtk_shell(struct wl_registry *wl_registry, uint32_t id) {
#if HAVE_GTK
gtk_shell = (struct gtk_shell1*)wl_registry_bind(wl_registry, id,
&gtk_shell1_interface, 5);
#endif // HAVE_GTK
}
/* === End of code to add support of GTK Shell to libdecor-gtk === */

View File

@ -709,17 +709,15 @@ if (UNIX AND OPTION_USE_WAYLAND)
list (APPEND STATIC_FILES "xdg-decoration-protocol.c")
list (APPEND SHARED_FILES "xdg-decoration-protocol.c")
endif (NOT OPTION_USE_SYSTEM_LIBDECOR)
if (GTK_FOUND AND (OPTION_USE_SYSTEM_LIBDECOR OR OPTION_ALLOW_GTK_PLUGIN))
add_custom_command(
add_custom_command(
OUTPUT gtk-shell-protocol.c gtk-shell-client-protocol.h
COMMAND wayland-scanner private-code ${CMAKE_CURRENT_SOURCE_DIR}/../libdecor/build/gtk-shell.xml gtk-shell-protocol.c
COMMAND wayland-scanner client-header ${CMAKE_CURRENT_SOURCE_DIR}/../libdecor/build/gtk-shell.xml gtk-shell-client-protocol.h
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../libdecor/build/gtk-shell.xml
VERBATIM
)
list (APPEND STATIC_FILES "gtk-shell-protocol.c")
list (APPEND SHARED_FILES "gtk-shell-protocol.c")
endif (GTK_FOUND AND (OPTION_USE_SYSTEM_LIBDECOR OR OPTION_ALLOW_GTK_PLUGIN))
)
list (APPEND STATIC_FILES "gtk-shell-protocol.c")
list (APPEND SHARED_FILES "gtk-shell-protocol.c")
add_custom_command(
OUTPUT text-input-protocol.c text-input-client-protocol.h
COMMAND wayland-scanner private-code ${PROTOCOLS}/unstable/text-input/text-input-unstable-v3.xml text-input-protocol.c

View File

@ -39,6 +39,7 @@
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-compose.h>
#include "text-input-client-protocol.h"
#include "gtk-shell-client-protocol.h"
#include <assert.h>
#include <sys/mman.h>
#include <poll.h>
@ -46,7 +47,6 @@
#include <string.h> // for strerror()
extern "C" {
bool libdecor_get_cursor_settings(char **theme, int *size);
void bind_to_gtk_shell(struct wl_registry *, uint32_t);
}
@ -87,6 +87,8 @@ struct pointer_output {
static Fl_Int_Vector key_vector; // used by Fl_Wayland_Screen_Driver::event_key()
static struct gtk_shell1 *gtk_shell = NULL;
static struct wl_surface *gtk_shell_surface = NULL;
Fl_Wayland_Screen_Driver::compositor_name Fl_Wayland_Screen_Driver::compositor =
Fl_Wayland_Screen_Driver::unspecified;
@ -199,6 +201,7 @@ static Fl_Window *event_coords_from_surface(struct wl_surface *surface,
static void pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) {
Fl_Window *win = event_coords_from_surface(surface, surface_x, surface_y);
if (!win && gtk_shell) gtk_shell_surface = surface;
if (!win) return;
// use custom cursor if present
struct wl_cursor *cursor =
@ -220,6 +223,7 @@ static void pointer_leave(void *data, struct wl_pointer *wl_pointer,
struct Fl_Wayland_Screen_Driver::seat *seat = (struct Fl_Wayland_Screen_Driver::seat*)data;
if (seat->pointer_focus == surface) seat->pointer_focus = NULL;
Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(surface);
gtk_shell_surface = NULL;
if (win) {
Fl::belowmouse(0);
set_event_xy(win);
@ -257,6 +261,15 @@ static void pointer_button(void *data,
{
struct Fl_Wayland_Screen_Driver::seat *seat =
(struct Fl_Wayland_Screen_Driver::seat*)data;
if (gtk_shell_surface && state == WL_POINTER_BUTTON_STATE_PRESSED &&
button == BTN_MIDDLE) {
struct gtk_surface1 *gtk_surface = gtk_shell1_get_gtk_surface(gtk_shell,
gtk_shell_surface);
gtk_surface1_titlebar_gesture(gtk_surface, serial, seat->wl_seat,
GTK_SURFACE1_GESTURE_MIDDLE_CLICK);
gtk_surface1_release(gtk_surface);
return;
}
seat->serial = serial;
int event = 0;
Fl_Window *win = Fl_Wayland_Window_Driver::surface_to_window(seat->pointer_focus);
@ -1148,7 +1161,8 @@ static void registry_handle_global(void *user_data, struct wl_registry *wl_regis
Fl_Wayland_Screen_Driver::compositor = Fl_Wayland_Screen_Driver::MUTTER;
//fprintf(stderr, "Running the Mutter compositor\n");
if ( version >= 5) {
bind_to_gtk_shell(wl_registry, id);
gtk_shell = (struct gtk_shell1*)wl_registry_bind(wl_registry, id,
&gtk_shell1_interface, 5);
}
} else if (strcmp(interface, "weston_desktop_shell") == 0) {
Fl_Wayland_Screen_Driver::compositor = Fl_Wayland_Screen_Driver::WESTON;

View File

@ -44,7 +44,6 @@ struct cursor_image { // as in wayland-cursor.c of the Wayland project source co
extern "C" {
# include "../../../libdecor/src/libdecor-plugin.h"
uchar *fl_libdecor_titlebar_buffer(struct libdecor_frame *frame, int *w, int *h, int *stride);
void use_FLTK_pointer_button(struct libdecor_frame *);
}
#define fl_max(a,b) ((a) > (b) ? (a) : (b))
@ -854,7 +853,6 @@ static void handle_configure(struct libdecor_frame *frame,
#ifdef LIBDECOR_MR131
if (is_1st_run) use_FLTK_toplevel_configure_cb(frame);
#endif
use_FLTK_pointer_button(frame);
struct wl_output *wl_output = NULL;
if (window->fl_win->fullscreen_active()) {
if (!(window->state & LIBDECOR_WINDOW_STATE_FULLSCREEN)) {