fltk/libdecor/build/fl_libdecor-plugins.c

358 lines
11 KiB
C

//
// Interface with the libdecor library for the Fast Light Tool Kit (FLTK).
//
// Copyright 2022-2024 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
// file is missing or damaged, see the license at:
//
// https://www.fltk.org/COPYING.php
//
// Please see the following page on how to report bugs and issues:
//
// https://www.fltk.org/bugs.php
//
/* Support of interactions between FLTK and libdecor plugins, either dynamically
loaded by dlopen() or built-in FLTK.
Under USE_SYSTEM_LIBDECOR, the plugin can only be dynamically loaded.
Under ! USE_SYSTEM_LIBDECOR, it can be dynamically loaded from a directory
given in environment variable LIBDECOR_PLUGIN_DIR, or the built-in one is used.
*/
#include <dlfcn.h>
#include <string.h>
#include "../src/libdecor.h"
#include <pango/pangocairo.h>
#include <dlfcn.h>
#ifndef HAVE_GTK
# define HAVE_GTK 0
#endif
enum plugin_kind { UNKNOWN, SSD, CAIRO, GTK3 };
#if USE_SYSTEM_LIBDECOR
# include "../src/libdecor-plugin.h"
enum component {NONE}; /* details are not necessary*/
enum decoration_type {DECORATION_TYPE_NONE}; /* details are not necessary*/
struct buffer { // identical in libdecor-cairo.c and libdecor-gtk.c
struct wl_buffer *wl_buffer;
bool in_use;
bool is_detached;
void *data;
size_t data_size;
int width;
int height;
int scale;
int buffer_width;
int buffer_height;
};
#else // !USE_SYSTEM_LIBDECOR
const struct libdecor_plugin_description *fl_libdecor_plugin_description = NULL;
# if HAVE_GTK
# include "../src/plugins/gtk/libdecor-gtk.c"
# else
# include "../src/plugins/cairo/libdecor-cairo.c"
# endif // HAVE_GTK
#endif // USE_SYSTEM_LIBDECOR
#if USE_SYSTEM_LIBDECOR || HAVE_GTK
/* these definitions derive from libdecor/src/plugins/cairo/libdecor-cairo.c */
enum composite_mode {COMPOSITE_SERVER}; /* details are not necessary*/
struct border_component_cairo {
enum component type;
bool is_hidden;
bool opaque;
enum composite_mode composite_mode;
struct {
struct wl_surface *wl_surface;
struct wl_subsurface *wl_subsurface;
struct buffer *buffer;
struct wl_list output_list;
int scale;
} server;
struct {
cairo_surface_t *image;
struct border_component_cairo *parent_component;
} client;
struct wl_list child_components; /* border_component::link */
struct wl_list link; /* border_component::child_components */
};
struct libdecor_frame_cairo {
struct libdecor_frame frame;
struct libdecor_plugin_cairo *plugin_cairo;
int content_width;
int content_height;
enum decoration_type decoration_type;
enum libdecor_window_state window_state;
char *title;
enum libdecor_capabilities capabilities;
struct border_component_cairo *focus;
struct border_component_cairo *active;
struct border_component_cairo *grab;
bool shadow_showing;
struct border_component_cairo shadow;
struct {
bool is_showing;
struct border_component_cairo title;
struct border_component_cairo min;
struct border_component_cairo max;
struct border_component_cairo close;
} title_bar;
/* store pre-processed shadow tile */
cairo_surface_t *shadow_blur;
struct wl_list link;
};
#endif // USE_SYSTEM_LIBDECOR || HAVE_GTK
#if USE_SYSTEM_LIBDECOR || !HAVE_GTK
/* Definitions derived from libdecor-gtk.c */
typedef struct _GtkWidget GtkWidget;
enum header_element { HEADER_NONE }; /* details are not needed */
typedef enum { GTK_STATE_FLAG_NORMAL = 0 } GtkStateFlags;
struct border_component_gtk {
enum component type;
struct wl_surface *wl_surface;
struct wl_subsurface *wl_subsurface;
struct buffer *buffer;
bool opaque;
struct wl_list output_list;
int scale;
struct wl_list child_components; /* border_component::link */
struct wl_list link; /* border_component::child_components */
};
struct header_element_data {
const char* name;
enum header_element type;
GtkWidget *widget;
GtkStateFlags state;
};
struct libdecor_frame_gtk {
struct libdecor_frame frame;
struct libdecor_plugin_gtk *plugin_gtk;
int content_width;
int content_height;
enum libdecor_window_state window_state;
enum decoration_type decoration_type;
char *title;
enum libdecor_capabilities capabilities;
struct border_component_gtk *active;
struct border_component_gtk *touch_active;
struct border_component_gtk *focus;
struct border_component_gtk *grab;
bool shadow_showing;
struct border_component_gtk shadow;
GtkWidget *window; /* offscreen window for rendering */
GtkWidget *header; /* header bar with widgets */
struct border_component_gtk headerbar;
struct header_element_data hdr_focus;
cairo_surface_t *shadow_blur;
struct wl_list link;
struct {
enum titlebar_gesture_state {TITLEBAR_GESTURE_STATE_INIT} state;
int button_pressed_count;
uint32_t first_pressed_button;
uint32_t first_pressed_time;
double pressed_x;
double pressed_y;
uint32_t pressed_serial;
} titlebar_gesture;
};
#endif // USE_SYSTEM_LIBDECOR || !HAVE_GTK
static unsigned char *gtk_titlebar_buffer(struct libdecor_frame *frame,
int *width, int *height, int *stride)
{
struct libdecor_frame_gtk *lfg = (struct libdecor_frame_gtk *)frame;
struct buffer *buffer = lfg->headerbar.buffer;
*width = buffer->buffer_width;
*height = buffer->buffer_height;
*stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, buffer->buffer_width);
return (unsigned char*)buffer->data;
}
static unsigned char *cairo_titlebar_buffer(struct libdecor_frame *frame,
int *width, int *height, int *stride)
{
struct libdecor_frame_cairo *lfc = (struct libdecor_frame_cairo *)frame;
struct buffer *buffer = lfc->title_bar.title.server.buffer;
*width = buffer->buffer_width;
*height = buffer->buffer_height;
*stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, buffer->buffer_width);
return (unsigned char*)buffer->data;
}
/*
Although each plugin declares an exported global variable
LIBDECOR_EXPORT const struct libdecor_plugin_description libdecor_plugin_description;
these plugins are dlopen()'ed in libdecor.c without the RTLD_GLOBAL flag.
Consequently their symbols are not discovered by dlsym(RTLD_DEFAULT, "symbol-name").
Under USE_SYSTEM_LIBDECOR, we repeat the dlopen() for the same plugin
then dlsym() will report the address of libdecor_plugin_description.
Under !USE_SYSTEM_LIBDECOR, we compile fl_libdecor.c which modifies the dlopen()
to call dlsym(ld, "libdecor_plugin_description") just after the dlopen and memorizes
this address.
A plugin is loaded also if SSD.
KWin has its own size limit, similar to that of GDK plugin
*/
static const char *get_libdecor_plugin_description() {
static const struct libdecor_plugin_description *plugin_description = NULL;
if (!plugin_description) {
#if USE_SYSTEM_LIBDECOR
char fname[PATH_MAX];
const char *dir = getenv("LIBDECOR_PLUGIN_DIR");
if (!dir) dir = LIBDECOR_PLUGIN_DIR;
snprintf(fname, PATH_MAX, "%s/libdecor-gtk.so", dir);
void *dl = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
if (!dl) {
snprintf(fname, PATH_MAX, "%s/libdecor-cairo.so", dir);
dl = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
}
if (dl) plugin_description = (const struct libdecor_plugin_description*)dlsym(dl, "libdecor_plugin_description");
#else
plugin_description = fl_libdecor_plugin_description;
extern const struct libdecor_plugin_description libdecor_plugin_description;
if (!plugin_description) plugin_description = &libdecor_plugin_description;
#endif
//if (plugin_description) puts(plugin_description->description);
}
return plugin_description ? plugin_description->description : NULL;
}
static enum plugin_kind get_plugin_kind(struct libdecor_frame *frame) {
static enum plugin_kind kind = UNKNOWN;
if (kind == UNKNOWN) {
if (frame) {
int X, Y = 0;
libdecor_frame_translate_coordinate(frame, 0, 0, &X, &Y);
if (Y == 0) {
return SSD;
}
}
const char *name = get_libdecor_plugin_description();
if (name && !strcmp(name, "GTK3 plugin")) kind = GTK3;
else if (name && !strcmp(name, "libdecor plugin using Cairo")) kind = CAIRO;
}
return kind;
}
/*
FLTK-added utility function to give access to the pixel array representing
the titlebar of a window decorated by the cairo plugin of libdecor.
frame: a libdecor-defined pointer given by fl_xid(win)->frame (with Fl_Window *win);
*width, *height: returned assigned to the width and height in pixels of the titlebar;
*stride: returned assigned to the number of bytes per line of the pixel array;
return value: start of the pixel array, which is in BGRA order, or NULL.
*/
unsigned char *fl_libdecor_titlebar_buffer(struct libdecor_frame *frame,
int *width, int *height, int *stride)
{
enum plugin_kind kind = get_plugin_kind(frame);
if (kind == GTK3) {
return gtk_titlebar_buffer(frame, width, height, stride);
}
else if (kind == CAIRO) {
return cairo_titlebar_buffer(frame, width, height, stride);
}
return NULL;
}
// When the libdecor version after 0.2.2 will be released, support of older versions
// will be removed from FLTK. LIBDECOR_MR131 stuff also will be removed.
struct libdecor_022 { // for libdecor versions ≤ 0.2.2
int ref_count;
const struct libdecor_interface *iface;
struct libdecor_plugin *plugin;
bool plugin_ready;
struct wl_display *wl_display;
struct wl_registry *wl_registry;
struct xdg_wm_base *xdg_wm_base;
struct zxdg_decoration_manager_v1 *decoration_manager;
struct wl_callback *init_callback;
bool init_done;
bool has_error;
struct wl_list frames;
};
struct libdecor { // copied from libdecor.c, for libdecor versions > 0.2.2
int ref_count;
const struct libdecor_interface *iface;
void *user_data; // added after libdecor version 0.2.2
struct libdecor_plugin *plugin;
bool plugin_ready;
struct wl_display *wl_display;
struct wl_registry *wl_registry;
struct xdg_wm_base *xdg_wm_base;
struct zxdg_decoration_manager_v1 *decoration_manager;
struct wl_callback *init_callback;
bool init_done;
bool has_error;
struct wl_list frames;
};
/* Returns whether surface is a GTK-titlebar created by libdecor-gtk */
bool fl_is_surface_gtk_titlebar(struct wl_surface *surface, struct libdecor *context) {
if (!context || get_plugin_kind(NULL) != GTK3) return false;
static void *new_symbol = NULL;
static bool first = true;
if (first) {
first = false;
// new_symbol is NULL for libdecor versions ≤ 0.2.2
new_symbol = dlsym(RTLD_DEFAULT, "libdecor_frame_get_user_data");
}
struct wl_list *frames_addr = (new_symbol ? &context->frames :
&(((struct libdecor_022*)context)->frames) );
// loop over all decorations created by libdecor-gtk
struct libdecor_frame *frame;
wl_list_for_each(frame, frames_addr, link) {
struct libdecor_frame_gtk *frame_gtk = (struct libdecor_frame_gtk*)frame;
if (frame_gtk->headerbar.wl_surface == surface) return true;
}
return false;
}