diff options
| author | maxim nikonov <maxim.nikonov@hqo.co> | 2026-02-05 15:21:34 +0500 |
|---|---|---|
| committer | maxim nikonov <maxim.nikonov@hqo.co> | 2026-02-05 15:21:34 +0500 |
| commit | db214d1145e46d527a46d1fc2519548d2c4d23f1 (patch) | |
| tree | cf0fd9922e4d54f6beb63888f9b28c8e2a787bdf /libdecor/src | |
| parent | 75fc94d6c71fe686f6dde5b41ad91cba2f6bdd6f (diff) | |
wip: fork
Diffstat (limited to 'libdecor/src')
| -rw-r--r-- | libdecor/src/desktop-settings.c | 213 | ||||
| -rw-r--r-- | libdecor/src/desktop-settings.h | 41 | ||||
| -rw-r--r-- | libdecor/src/libdecor-fallback.c | 190 | ||||
| -rw-r--r-- | libdecor/src/libdecor-fallback.h | 35 | ||||
| -rw-r--r-- | libdecor/src/libdecor-plugin.h | 186 | ||||
| -rw-r--r-- | libdecor/src/libdecor.c | 1871 | ||||
| -rw-r--r-- | libdecor/src/libdecor.h | 605 | ||||
| -rw-r--r-- | libdecor/src/os-compatibility.c | 198 | ||||
| -rw-r--r-- | libdecor/src/os-compatibility.h | 34 | ||||
| -rw-r--r-- | libdecor/src/plugins/cairo/libdecor-cairo.c | 2796 | ||||
| -rw-r--r-- | libdecor/src/plugins/common/libdecor-cairo-blur.c | 255 | ||||
| -rw-r--r-- | libdecor/src/plugins/common/libdecor-cairo-blur.h | 10 | ||||
| -rw-r--r-- | libdecor/src/plugins/dummy/libdecor-dummy.c | 141 | ||||
| -rw-r--r-- | libdecor/src/plugins/gtk/libdecor-gtk.c | 3028 | ||||
| -rw-r--r-- | libdecor/src/utils.h | 48 |
15 files changed, 0 insertions, 9651 deletions
diff --git a/libdecor/src/desktop-settings.c b/libdecor/src/desktop-settings.c deleted file mode 100644 index 28067ac3e..000000000 --- a/libdecor/src/desktop-settings.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright © 2019 Christian Rauch - * Copyright © 2024 Colin Kinloch - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "desktop-settings.h" -#include <stdlib.h> -#include <string.h> -#include <stdbool.h> -#include <stdint.h> -#include "config.h" - -static bool -get_cursor_settings_from_env(char **theme, int *size) -{ - char *env_xtheme; - char *env_xsize; - - env_xtheme = getenv("XCURSOR_THEME"); - if (env_xtheme != NULL) - *theme = strdup(env_xtheme); - - env_xsize = getenv("XCURSOR_SIZE"); - if (env_xsize != NULL) - *size = atoi(env_xsize); - - return env_xtheme != NULL && env_xsize != NULL; -} - -#ifdef HAS_DBUS -#include <dbus/dbus.h> - -static DBusMessage * -get_setting_sync(DBusConnection *const connection, - const char *key, - const char *value) -{ - DBusError error; - dbus_bool_t success; - DBusMessage *message; - DBusMessage *reply; - - message = dbus_message_new_method_call( - "org.freedesktop.portal.Desktop", - "/org/freedesktop/portal/desktop", - "org.freedesktop.portal.Settings", - "Read"); - - success = dbus_message_append_args(message, - DBUS_TYPE_STRING, &key, - DBUS_TYPE_STRING, &value, - DBUS_TYPE_INVALID); - - if (!success) - return NULL; - - dbus_error_init(&error); - - reply = dbus_connection_send_with_reply_and_block( - connection, - message, - DBUS_TIMEOUT_USE_DEFAULT, - &error); - - dbus_message_unref(message); - - if (dbus_error_is_set(&error)) { - dbus_error_free(&error); - return NULL; - } - - dbus_error_free(&error); - return reply; -} - -static bool -parse_type(DBusMessage *const reply, - const int type, - void *value) -{ - DBusMessageIter iter[3]; - - dbus_message_iter_init(reply, &iter[0]); - if (dbus_message_iter_get_arg_type(&iter[0]) != DBUS_TYPE_VARIANT) - return false; - - dbus_message_iter_recurse(&iter[0], &iter[1]); - if (dbus_message_iter_get_arg_type(&iter[1]) != DBUS_TYPE_VARIANT) - return false; - - dbus_message_iter_recurse(&iter[1], &iter[2]); - if (dbus_message_iter_get_arg_type(&iter[2]) != type) - return false; - - dbus_message_iter_get_basic(&iter[2], value); - - return true; -} - -bool -libdecor_get_cursor_settings(char **theme, int *size) -{ - static const char name[] = "org.gnome.desktop.interface"; - static const char key_theme[] = "cursor-theme"; - static const char key_size[] = "cursor-size"; - - DBusError error; - DBusConnection *connection; - DBusMessage *reply; - const char *value_theme = NULL; - - dbus_error_init(&error); - - connection = dbus_bus_get(DBUS_BUS_SESSION, &error); - - if (dbus_error_is_set(&error)) - goto fallback; - - reply = get_setting_sync(connection, name, key_theme); - if (!reply) - goto fallback; - - if (!parse_type(reply, DBUS_TYPE_STRING, &value_theme)) { - dbus_message_unref(reply); - goto fallback; - } - - *theme = strdup(value_theme); - - dbus_message_unref(reply); - - reply = get_setting_sync(connection, name, key_size); - if (!reply) - goto fallback; - - if (!parse_type(reply, DBUS_TYPE_INT32, size)) { - dbus_message_unref(reply); - goto fallback; - } - - dbus_message_unref(reply); - - return true; - -fallback: - return get_cursor_settings_from_env(theme, size); -} - -enum libdecor_color_scheme -libdecor_get_color_scheme() -{ - static const char name[] = "org.freedesktop.appearance"; - static const char key_color_scheme[] = "color-scheme"; - uint32_t color = 0; - - DBusError error; - DBusConnection *connection; - DBusMessage *reply; - - dbus_error_init(&error); - - connection = dbus_bus_get(DBUS_BUS_SESSION, &error); - - if (dbus_error_is_set(&error)) - return 0; - - reply = get_setting_sync(connection, name, key_color_scheme); - if (!reply) - return 0; - - if (!parse_type(reply, DBUS_TYPE_UINT32, &color)) { - dbus_message_unref(reply); - return 0; - } - - dbus_message_unref(reply); - - return color; -} -#else -bool -libdecor_get_cursor_settings(char **theme, int *size) -{ - return get_cursor_settings_from_env(theme, size); -} - -uint32_t -libdecor_get_color_scheme() -{ - return LIBDECOR_COLOR_SCHEME_DEFAULT; -} -#endif diff --git a/libdecor/src/desktop-settings.h b/libdecor/src/desktop-settings.h deleted file mode 100644 index 5747d3168..000000000 --- a/libdecor/src/desktop-settings.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright © 2019 Christian Rauch - * Copyright © 2024 Colin Kinloch - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include <stdbool.h> - -enum libdecor_color_scheme { - LIBDECOR_COLOR_SCHEME_DEFAULT, - LIBDECOR_COLOR_SCHEME_PREFER_DARK, - LIBDECOR_COLOR_SCHEME_PREFER_LIGHT, -}; - -bool -libdecor_get_cursor_settings(char **theme, int *size); - -enum libdecor_color_scheme -libdecor_get_color_scheme(); diff --git a/libdecor/src/libdecor-fallback.c b/libdecor/src/libdecor-fallback.c deleted file mode 100644 index ea0d35b03..000000000 --- a/libdecor/src/libdecor-fallback.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright © 2019 Jonas Ådahl - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "config.h" - -#include "libdecor-fallback.h" - -#include <poll.h> -#include <errno.h> - -#include "utils.h" - -struct libdecor_plugin_fallback { - struct libdecor_plugin plugin; - struct libdecor *context; -}; - -static void -libdecor_plugin_fallback_destroy(struct libdecor_plugin *plugin) -{ - libdecor_plugin_release(plugin); - free(plugin); -} - -static int -libdecor_plugin_fallback_get_fd(struct libdecor_plugin *plugin) -{ - struct libdecor_plugin_fallback *plugin_fallback = - (struct libdecor_plugin_fallback *) plugin; - struct wl_display *wl_display = - libdecor_get_wl_display(plugin_fallback->context); - - return wl_display_get_fd(wl_display); -} - -static int -libdecor_plugin_fallback_dispatch(struct libdecor_plugin *plugin, - int timeout) -{ - struct libdecor_plugin_fallback *plugin_fallback = - (struct libdecor_plugin_fallback *) plugin; - struct wl_display *wl_display = - libdecor_get_wl_display(plugin_fallback->context); - struct pollfd fds[1]; - int ret; - int dispatch_count = 0; - - while (wl_display_prepare_read(wl_display) != 0) - dispatch_count += wl_display_dispatch_pending(wl_display); - - if (wl_display_flush(wl_display) < 0 && - errno != EAGAIN) { - wl_display_cancel_read(wl_display); - return -errno; - } - - fds[0] = (struct pollfd) { wl_display_get_fd(wl_display), POLLIN }; - - ret = poll(fds, ARRAY_SIZE (fds), timeout); - if (ret > 0) { - if (fds[0].revents & POLLIN) { - wl_display_read_events(wl_display); - dispatch_count += wl_display_dispatch_pending(wl_display); - return dispatch_count; - } else { - wl_display_cancel_read(wl_display); - return dispatch_count; - } - } else if (ret == 0) { - wl_display_cancel_read(wl_display); - return dispatch_count; - } else { - wl_display_cancel_read(wl_display); - return -errno; - } -} - -static struct libdecor_frame * -libdecor_plugin_fallback_frame_new(struct libdecor_plugin *plugin) -{ - struct libdecor_frame *frame; - - frame = zalloc(sizeof *frame); - - return frame; -} - -static void -libdecor_plugin_fallback_frame_free(struct libdecor_plugin *plugin, - struct libdecor_frame *frame) -{ -} - -static void -libdecor_plugin_fallback_frame_commit(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - struct libdecor_state *state, - struct libdecor_configuration *configuration) -{ -} - -static void -libdecor_plugin_fallback_frame_property_changed(struct libdecor_plugin *plugin, - struct libdecor_frame *frame) -{ -} - -static void -libdecor_plugin_fallback_frame_popup_grab(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - const char *seat_name) -{ -} - -static void -libdecor_plugin_fallback_frame_popup_ungrab(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - const char *seat_name) -{ -} - -static bool -libdecor_plugin_fallback_frame_get_border_size(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - struct libdecor_configuration *configuration, - int *left, - int *right, - int *top, - int *bottom) -{ - if (left) - *left = 0; - if (right) - *right = 0; - if (top) - *top = 0; - if (bottom) - *bottom = 0; - - return true; -} - -static struct libdecor_plugin_interface fallback_plugin_iface = { - .destroy = libdecor_plugin_fallback_destroy, - .get_fd = libdecor_plugin_fallback_get_fd, - .dispatch = libdecor_plugin_fallback_dispatch, - .frame_new = libdecor_plugin_fallback_frame_new, - .frame_free = libdecor_plugin_fallback_frame_free, - .frame_commit = libdecor_plugin_fallback_frame_commit, - .frame_property_changed = libdecor_plugin_fallback_frame_property_changed, - .frame_popup_grab = libdecor_plugin_fallback_frame_popup_grab, - .frame_popup_ungrab = libdecor_plugin_fallback_frame_popup_ungrab, - .frame_get_border_size = libdecor_plugin_fallback_frame_get_border_size, -}; - -struct libdecor_plugin * -libdecor_fallback_plugin_new(struct libdecor *context) -{ - struct libdecor_plugin_fallback *plugin; - - plugin = zalloc(sizeof *plugin); - libdecor_plugin_init(&plugin->plugin, context, &fallback_plugin_iface); - plugin->context = context; - - libdecor_notify_plugin_ready(context); - - return &plugin->plugin; -} diff --git a/libdecor/src/libdecor-fallback.h b/libdecor/src/libdecor-fallback.h deleted file mode 100644 index 40a5c51d2..000000000 --- a/libdecor/src/libdecor-fallback.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright © 2019 Jonas Ådahl - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef LIBDECOR_FALLBACK_H -#define LIBDECOR_FALLBACK_H - -#include "libdecor.h" -#include "libdecor-plugin.h" - -struct libdecor_plugin * -libdecor_fallback_plugin_new(struct libdecor *context); - -#endif /* LIBDECOR_FALLBACK_H */ diff --git a/libdecor/src/libdecor-plugin.h b/libdecor/src/libdecor-plugin.h deleted file mode 100644 index d57430e9c..000000000 --- a/libdecor/src/libdecor-plugin.h +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright © 2017-2018 Red Hat Inc. - * Copyright © 2018 Jonas Ådahl - * Copyright © 2019 Christian Rauch - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef LIBDECOR_PLUGIN_H -#define LIBDECOR_PLUGIN_H - -#include "libdecor.h" - -struct libdecor_frame_private; - -struct libdecor_frame { - struct libdecor_frame_private *priv; - struct wl_list link; -}; - -struct libdecor_plugin_private; - -struct libdecor_plugin { - struct libdecor_plugin_private *priv; -}; - -typedef struct libdecor_plugin * (* libdecor_plugin_constructor)(struct libdecor *context); - -#define LIBDECOR_PLUGIN_PRIORITY_HIGH 1000 -#define LIBDECOR_PLUGIN_PRIORITY_MEDIUM 100 -#define LIBDECOR_PLUGIN_PRIORITY_LOW 0 - -struct libdecor_plugin_priority { - const char *desktop; - int priority; -}; - -enum libdecor_plugin_capabilities { - LIBDECOR_PLUGIN_CAPABILITY_BASE = 1 << 0, -}; - -struct libdecor_plugin_description { - /* API version the plugin is compatible with. */ - int api_version; - - /* Human readable string describing the plugin. */ - char *description; - - /* A plugin has a bitmask of capabilities. The plugin loader can use this - * to load a plugin with the right capabilities. */ - enum libdecor_plugin_capabilities capabilities; - - /* - * The priorities field points to a list of per desktop priorities. - * properties[i].desktop is matched against XDG_CURRENT_DESKTOP when - * determining what plugin to use. The last entry in the list MUST have - * the priorities[i].desktop pointer set to NULL as a default - * priority. - */ - const struct libdecor_plugin_priority *priorities; - - /* Vfunc used for constructing a plugin instance. */ - libdecor_plugin_constructor constructor; - - /* NULL terminated list of incompatible symbols. */ - char *conflicting_symbols[1024]; -}; - -struct libdecor_plugin_interface { - void (* destroy)(struct libdecor_plugin *plugin); - - int (* get_fd)(struct libdecor_plugin *plugin); - int (* dispatch)(struct libdecor_plugin *plugin, - int timeout); - - void (* set_handle_application_cursor)(struct libdecor_plugin *plugin, - bool handle_cursor); - - struct libdecor_frame * (* frame_new)(struct libdecor_plugin *plugin); - void (* frame_free)(struct libdecor_plugin *plugin, - struct libdecor_frame *frame); - void (* frame_commit)(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - struct libdecor_state *state, - struct libdecor_configuration *configuration); - void (*frame_property_changed)(struct libdecor_plugin *plugin, - struct libdecor_frame *frame); - void (* frame_popup_grab)(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - const char *seat_name); - void (* frame_popup_ungrab)(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - const char *seat_name); - - bool (* frame_get_border_size)(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - struct libdecor_configuration *configuration, - int *left, - int *right, - int *top, - int *bottom); - - /* Reserved */ - void (* reserved0)(void); - void (* reserved1)(void); - void (* reserved2)(void); - void (* reserved3)(void); - void (* reserved4)(void); - void (* reserved5)(void); - void (* reserved6)(void); - void (* reserved7)(void); - void (* reserved8)(void); - void (* reserved9)(void); -}; - -struct wl_surface * -libdecor_frame_get_wl_surface(struct libdecor_frame *frame); - -int -libdecor_frame_get_content_width(struct libdecor_frame *frame); - -int -libdecor_frame_get_content_height(struct libdecor_frame *frame); - -enum libdecor_window_state -libdecor_frame_get_window_state(struct libdecor_frame *frame); - -enum libdecor_capabilities -libdecor_frame_get_capabilities(const struct libdecor_frame *frame); - -void -libdecor_frame_dismiss_popup(struct libdecor_frame *frame, - const char *seat_name); - -void -libdecor_frame_toplevel_commit(struct libdecor_frame *frame); - -struct wl_display * -libdecor_get_wl_display(struct libdecor *context); - -void -libdecor_notify_plugin_ready(struct libdecor *context); - -void -libdecor_notify_plugin_error(struct libdecor *context, - enum libdecor_error error, - const char *__restrict fmt, - ...); - -int -libdecor_state_get_content_width(struct libdecor_state *state); - -int -libdecor_state_get_content_height(struct libdecor_state *state); - -enum libdecor_window_state -libdecor_state_get_window_state(struct libdecor_state *state); - -int -libdecor_plugin_init(struct libdecor_plugin *plugin, - struct libdecor *context, - struct libdecor_plugin_interface *iface); - -void -libdecor_plugin_release(struct libdecor_plugin *plugin); - -#endif /* LIBDECOR_PLUGIN_H */ diff --git a/libdecor/src/libdecor.c b/libdecor/src/libdecor.c deleted file mode 100644 index 39886c80f..000000000 --- a/libdecor/src/libdecor.c +++ /dev/null @@ -1,1871 +0,0 @@ -/* - * Copyright © 2017-2018 Red Hat Inc. - * Copyright © 2018 Jonas Ådahl - * Copyright © 2019 Christian Rauch - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "config.h" - -#include <errno.h> -#include <dirent.h> -#include <dlfcn.h> -#include <stdio.h> -#include <string.h> -#include <sys/types.h> - -#include "libdecor.h" -#include "libdecor-fallback.h" -#include "libdecor-plugin.h" -#include "utils.h" - -#include "xdg-shell-client-protocol.h" -#include "xdg-decoration-client-protocol.h" - -struct libdecor { - int ref_count; - - const struct libdecor_interface *iface; - void *user_data; - - 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_state { - enum libdecor_window_state window_state; - - int content_width; - int content_height; -}; - -struct libdecor_limits { - int min_width; - int min_height; - int max_width; - int max_height; -}; - -struct libdecor_configuration { - uint32_t serial; - - bool has_window_state; - enum libdecor_window_state window_state; - - bool has_size; - int window_width; - int window_height; -}; - -struct libdecor_frame_private { - int ref_count; - - struct libdecor *context; - - struct wl_surface *wl_surface; - - const struct libdecor_frame_interface *iface; - void *user_data; - - struct xdg_surface *xdg_surface; - struct xdg_toplevel *xdg_toplevel; - struct zxdg_toplevel_decoration_v1 *toplevel_decoration; - - bool pending_map; - - struct { - char *app_id; - char *title; - struct libdecor_limits content_limits; - struct xdg_toplevel *parent; - } state; - - struct libdecor_configuration *pending_configuration; - - int content_width; - int content_height; - - enum libdecor_window_state window_state; - - bool has_decoration_mode; - enum zxdg_toplevel_decoration_v1_mode decoration_mode; - - enum libdecor_capabilities capabilities; - - enum libdecor_wm_capabilities wm_capabilities; - - /* original limits for interactive resize */ - struct libdecor_limits interactive_limits; - - bool visible; -}; - -struct libdecor_plugin_private { - struct libdecor_plugin_interface *iface; -}; - -/* gather all states at which a window is non-floating */ -static const enum libdecor_window_state states_non_floating = - LIBDECOR_WINDOW_STATE_MAXIMIZED | LIBDECOR_WINDOW_STATE_FULLSCREEN | - LIBDECOR_WINDOW_STATE_TILED_LEFT | LIBDECOR_WINDOW_STATE_TILED_RIGHT | - LIBDECOR_WINDOW_STATE_TILED_TOP | LIBDECOR_WINDOW_STATE_TILED_BOTTOM; - -static bool -streql(const char *str1, const char *str2) -{ - return (str1 && str2) && (strcmp(str1, str2) == 0); -} - - -static void -do_map(struct libdecor_frame *frame); - -static bool -state_is_floating(enum libdecor_window_state window_state) -{ - return !(window_state & states_non_floating); -} - -static void -constrain_content_size(const struct libdecor_frame *frame, - int *width, - int *height) -{ - const struct libdecor_limits lim = frame->priv->state.content_limits; - - if (lim.min_width > 0) - *width = MAX(lim.min_width, *width); - if (lim.max_width > 0) - *width = MIN(*width, lim.max_width); - - if (lim.min_height > 0) - *height = MAX(lim.min_height, *height); - if (lim.max_height > 0) - *height = MIN(*height, lim.max_height); -} - -static bool -frame_has_visible_client_side_decoration(struct libdecor_frame *frame) -{ - /* visibility by client configuration */ - const bool vis_client = frame->priv->visible; - /* visibility by compositor configuration */ - const bool vis_server = (frame->priv->decoration_mode == - ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); - - return vis_client && vis_server; -} - -LIBDECOR_EXPORT int -libdecor_state_get_content_width(struct libdecor_state *state) -{ - return state->content_width; -} - -LIBDECOR_EXPORT int -libdecor_state_get_content_height(struct libdecor_state *state) -{ - return state->content_height; -} - -LIBDECOR_EXPORT enum libdecor_window_state -libdecor_state_get_window_state(struct libdecor_state *state) -{ - return state->window_state; -} - -LIBDECOR_EXPORT struct libdecor_state * -libdecor_state_new(int width, - int height) -{ - struct libdecor_state *state; - - state = zalloc(sizeof *state); - state->content_width = width; - state->content_height = height; - - return state; -} - -LIBDECOR_EXPORT void -libdecor_state_free(struct libdecor_state *state) -{ - free(state); -} - -static struct libdecor_configuration * -libdecor_configuration_new(void) -{ - struct libdecor_configuration *configuration; - - configuration = zalloc(sizeof *configuration); - - return configuration; -} - -static void -libdecor_configuration_free(struct libdecor_configuration *configuration) -{ - free(configuration); -} - -static bool -frame_get_window_size_for(struct libdecor_frame *frame, - struct libdecor_state *state, - int *window_width, - int *window_height) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - struct libdecor *context = frame_priv->context; - struct libdecor_plugin *plugin = context->plugin; - - *window_width = state->content_width; - *window_height = state->content_height; - - if (frame_has_visible_client_side_decoration(frame) && - plugin->priv->iface->frame_get_border_size) { - int left, right, top, bottom; - if (!plugin->priv->iface->frame_get_border_size( - plugin, frame, NULL, &left, &right, &top, &bottom)) - return false; - *window_width += left + right; - *window_height += top + bottom; - } - - return true; -} - -static void -frame_set_window_geometry(struct libdecor_frame *frame, - int32_t content_width, int32_t content_height) -{ - struct libdecor_plugin *plugin = frame->priv->context->plugin; - int x, y, width, height; - int left, right, top, bottom; - - plugin->priv->iface->frame_get_border_size(plugin, frame, NULL, - &left, &right, &top, &bottom); - x = -left; - y = -top; - width = content_width + left + right; - height = content_height + top + bottom; - xdg_surface_set_window_geometry(frame->priv->xdg_surface, x, y, width, height); -} - -LIBDECOR_EXPORT bool -libdecor_configuration_get_content_size(struct libdecor_configuration *configuration, - struct libdecor_frame *frame, - int *width, - int *height) -{ - struct libdecor_plugin *plugin = frame->priv->context->plugin; - - /* get configured toplevel dimensions */ - if (!configuration->has_size) - return false; - - if (configuration->window_width == 0 || configuration->window_height == 0) - return false; - - *width = configuration->window_width; - *height = configuration->window_height; - - /* remove plugin-specific border size */ - if (frame_has_visible_client_side_decoration(frame) && - plugin->priv->iface->frame_get_border_size) { - int left, right, top, bottom; - - /* Update window state for correct border size calculation */ - frame->priv->window_state = configuration->window_state; - if (!plugin->priv->iface->frame_get_border_size( - plugin, frame, configuration, &left, &right, &top, &bottom)) - return false; - - *width -= (left + right); - *height -= (top + bottom); - } - - /* constrain content dimensions manually */ - if (state_is_floating(configuration->window_state)) { - constrain_content_size(frame, width, height); - } - - return true; -} - -LIBDECOR_EXPORT bool -libdecor_configuration_get_window_state(struct libdecor_configuration *configuration, - enum libdecor_window_state *window_state) -{ - if (!configuration->has_window_state) - return false; - - *window_state = configuration->window_state; - return true; -} - -static void -xdg_surface_configure(void *user_data, - struct xdg_surface *xdg_surface, - uint32_t serial) -{ - struct libdecor_frame *frame = user_data; - struct libdecor_frame_private *frame_priv = frame->priv; - struct libdecor_configuration *configuration; - - configuration = frame_priv->pending_configuration; - frame_priv->pending_configuration = NULL; - - if (!configuration) - configuration = libdecor_configuration_new(); - - configuration->serial = serial; - - frame_priv->iface->configure(frame, - configuration, - frame_priv->user_data); - - libdecor_configuration_free(configuration); -} - -static const struct xdg_surface_listener xdg_surface_listener = { - xdg_surface_configure, -}; - -static enum libdecor_window_state -parse_states(struct wl_array *states) -{ - enum libdecor_window_state pending_state = LIBDECOR_WINDOW_STATE_NONE; - uint32_t *p; - - wl_array_for_each(p, states) { - enum xdg_toplevel_state state = *p; - - switch (state) { - case XDG_TOPLEVEL_STATE_FULLSCREEN: - pending_state |= LIBDECOR_WINDOW_STATE_FULLSCREEN; - break; - case XDG_TOPLEVEL_STATE_MAXIMIZED: - pending_state |= LIBDECOR_WINDOW_STATE_MAXIMIZED; - break; - case XDG_TOPLEVEL_STATE_ACTIVATED: - pending_state |= LIBDECOR_WINDOW_STATE_ACTIVE; - break; - case XDG_TOPLEVEL_STATE_TILED_LEFT: - pending_state |= LIBDECOR_WINDOW_STATE_TILED_LEFT; - break; - case XDG_TOPLEVEL_STATE_TILED_RIGHT: - pending_state |= LIBDECOR_WINDOW_STATE_TILED_RIGHT; - break; - case XDG_TOPLEVEL_STATE_TILED_TOP: - pending_state |= LIBDECOR_WINDOW_STATE_TILED_TOP; - break; - case XDG_TOPLEVEL_STATE_TILED_BOTTOM: - pending_state |= LIBDECOR_WINDOW_STATE_TILED_BOTTOM; - break; - case XDG_TOPLEVEL_STATE_RESIZING: - pending_state |= LIBDECOR_WINDOW_STATE_RESIZING; - break; -#ifdef XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION - case XDG_TOPLEVEL_STATE_SUSPENDED: - pending_state |= LIBDECOR_WINDOW_STATE_SUSPENDED; - break; -#endif -#ifdef XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION - case XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT: - pending_state |= LIBDECOR_WINDOW_STATE_CONSTRAINED_LEFT; - break; - case XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT: - pending_state |= LIBDECOR_WINDOW_STATE_CONSTRAINED_RIGHT; - break; - case XDG_TOPLEVEL_STATE_CONSTRAINED_TOP: - pending_state |= LIBDECOR_WINDOW_STATE_CONSTRAINED_TOP; - break; - case XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM: - pending_state |= LIBDECOR_WINDOW_STATE_CONSTRAINED_BOTTOM; - break; -#endif - default: - break; - } - } - - return pending_state; -} - -static void -xdg_toplevel_configure(void *user_data, - struct xdg_toplevel *xdg_toplevel, - int32_t width, - int32_t height, - struct wl_array *states) -{ - struct libdecor_frame *frame = user_data; - struct libdecor_frame_private *frame_priv = frame->priv; - enum libdecor_window_state window_state; - - window_state = parse_states(states); - - frame_priv->pending_configuration = libdecor_configuration_new(); - - frame_priv->pending_configuration->has_size = true; - frame_priv->pending_configuration->window_width = width; - frame_priv->pending_configuration->window_height = height; - - frame_priv->pending_configuration->has_window_state = true; - frame_priv->pending_configuration->window_state = window_state; -} - -static void -xdg_toplevel_close(void *user_data, - struct xdg_toplevel *xdg_toplevel) -{ - struct libdecor_frame *frame = user_data; - struct libdecor_frame_private *frame_priv = frame->priv; - - frame_priv->iface->close(frame, frame_priv->user_data); -} - -#ifdef XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION -static void -xdg_toplevel_configure_bounds(void *user_data, - struct xdg_toplevel *xdg_toplevel, - int32_t width, - int32_t height) -{ - struct libdecor_frame *frame = user_data; - struct libdecor_frame_private *frame_priv = frame->priv; - struct libdecor *context = frame_priv->context; - struct libdecor_plugin *plugin = context->plugin; - int left = 0, top = 0, right = 0, bottom = 0; - - if (frame_has_visible_client_side_decoration(frame) && - plugin->priv->iface->frame_get_border_size) { - plugin->priv->iface->frame_get_border_size(plugin, frame, NULL, - &left, &right, &top, &bottom); - } - - width -= left + right; - height -= top + bottom; - if (frame_priv->iface->bounds) { - frame_priv->iface->bounds(frame, width, height, frame_priv->user_data); - } -} -#endif - -#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION -static void -xdg_toplevel_wm_capabilities(void *user_data, - struct xdg_toplevel *xdg_toplevel, - struct wl_array *capabilities) -{ - struct libdecor_frame *frame = user_data; - struct libdecor_frame_private *frame_priv = frame->priv; - enum xdg_toplevel_wm_capabilities *wm_cap; - - frame_priv->wm_capabilities = 0; - - wl_array_for_each(wm_cap, capabilities) { - switch (*wm_cap) { - case XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU: - frame_priv->wm_capabilities |= LIBDECOR_WM_CAPABILITIES_WINDOW_MENU; - break; - case XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE: - frame_priv->wm_capabilities |= LIBDECOR_WM_CAPABILITIES_MAXIMIZE; - break; - case XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN: - frame_priv->wm_capabilities |= LIBDECOR_WM_CAPABILITIES_FULLSCREEN; - break; - case XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE: - frame_priv->wm_capabilities |= LIBDECOR_WM_CAPABILITIES_MINIMIZE; - break; - default: - break; - } - } -} -#endif - -static const struct xdg_toplevel_listener xdg_toplevel_listener = { - xdg_toplevel_configure, - xdg_toplevel_close, -#ifdef XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION - xdg_toplevel_configure_bounds, -#endif -#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION - xdg_toplevel_wm_capabilities, -#endif -}; - -static void -toplevel_decoration_configure( - void *data, - struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, - uint32_t mode) -{ - struct libdecor_frame_private *frame_priv = (struct libdecor_frame_private *)data; - /* Ignore any _configure calls after the first, they will be - * from our set_mode call. */ - if (!frame_priv->has_decoration_mode) { - frame_priv->has_decoration_mode = true; - frame_priv->decoration_mode = mode; - } -} - -static const struct zxdg_toplevel_decoration_v1_listener - xdg_toplevel_decoration_listener = { - toplevel_decoration_configure, -}; - -void -libdecor_frame_create_xdg_decoration(struct libdecor_frame_private *frame_priv) -{ - if (!frame_priv->context->decoration_manager) - return; - - frame_priv->toplevel_decoration = - zxdg_decoration_manager_v1_get_toplevel_decoration( - frame_priv->context->decoration_manager, - frame_priv->xdg_toplevel); - - zxdg_toplevel_decoration_v1_add_listener( - frame_priv->toplevel_decoration, - &xdg_toplevel_decoration_listener, - frame_priv); -} - -static void -init_shell_surface(struct libdecor_frame *frame) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - struct libdecor *context = frame_priv->context; - - if (frame_priv->xdg_surface) - return; - - frame_priv->xdg_surface = - xdg_wm_base_get_xdg_surface(context->xdg_wm_base, - frame_priv->wl_surface); - xdg_surface_add_listener(frame_priv->xdg_surface, - &xdg_surface_listener, - frame); - - frame_priv->xdg_toplevel = - xdg_surface_get_toplevel(frame_priv->xdg_surface); - xdg_toplevel_add_listener(frame_priv->xdg_toplevel, - &xdg_toplevel_listener, - frame); - - frame_priv->decoration_mode = - ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; - frame_priv->toplevel_decoration = NULL; - libdecor_frame_create_xdg_decoration(frame_priv); - - if (frame_priv->state.parent) { - xdg_toplevel_set_parent(frame_priv->xdg_toplevel, - frame_priv->state.parent); - } - if (frame_priv->state.title) { - xdg_toplevel_set_title(frame_priv->xdg_toplevel, - frame_priv->state.title); - } - if (frame_priv->state.app_id) { - xdg_toplevel_set_app_id(frame_priv->xdg_toplevel, - frame_priv->state.app_id); - } - - if (frame_priv->pending_map) - do_map(frame); -} - -LIBDECOR_EXPORT struct libdecor_frame * -libdecor_decorate(struct libdecor *context, - struct wl_surface *wl_surface, - const struct libdecor_frame_interface *iface, - void *user_data) -{ - struct libdecor_plugin *plugin = context->plugin; - struct libdecor_frame *frame; - struct libdecor_frame_private *frame_priv; - - if (context->has_error) - return NULL; - - frame = plugin->priv->iface->frame_new(plugin); - if (!frame) - return NULL; - - frame_priv = zalloc(sizeof *frame_priv); - frame->priv = frame_priv; - - frame_priv->ref_count = 1; - frame_priv->context = context; - - frame_priv->wl_surface = wl_surface; - frame_priv->iface = iface; - frame_priv->user_data = user_data; - frame_priv->wm_capabilities = LIBDECOR_WM_CAPABILITIES_WINDOW_MENU | - LIBDECOR_WM_CAPABILITIES_MAXIMIZE | - LIBDECOR_WM_CAPABILITIES_FULLSCREEN | - LIBDECOR_WM_CAPABILITIES_MINIMIZE; - - wl_list_insert(&context->frames, &frame->link); - - libdecor_frame_set_capabilities(frame, - LIBDECOR_ACTION_MOVE | - LIBDECOR_ACTION_RESIZE | - LIBDECOR_ACTION_MINIMIZE | - LIBDECOR_ACTION_FULLSCREEN | - LIBDECOR_ACTION_CLOSE); - - frame_priv->visible = true; - - if (context->init_done) - init_shell_surface(frame); - - return frame; -} - -LIBDECOR_EXPORT void -libdecor_frame_ref(struct libdecor_frame *frame) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - frame_priv->ref_count++; -} - -LIBDECOR_EXPORT void -libdecor_frame_unref(struct libdecor_frame *frame) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - frame_priv->ref_count--; - if (frame_priv->ref_count == 0) { - struct libdecor *context = frame_priv->context; - struct libdecor_plugin *plugin = context->plugin; - - if (context->decoration_manager && frame_priv->toplevel_decoration) { - zxdg_toplevel_decoration_v1_destroy(frame_priv->toplevel_decoration); - frame_priv->toplevel_decoration = NULL; - } - - wl_list_remove(&frame->link); - - if (frame_priv->xdg_toplevel) - xdg_toplevel_destroy(frame_priv->xdg_toplevel); - if (frame_priv->xdg_surface) - xdg_surface_destroy(frame_priv->xdg_surface); - - plugin->priv->iface->frame_free(plugin, frame); - - free(frame_priv->state.title); - free(frame_priv->state.app_id); - - free(frame_priv); - - free(frame); - } -} - -LIBDECOR_EXPORT void * -libdecor_frame_get_user_data(struct libdecor_frame *frame) -{ - return frame->priv->user_data; -} - -LIBDECOR_EXPORT void -libdecor_frame_set_user_data(struct libdecor_frame *frame, void *user_data) -{ - frame->priv->user_data = user_data; -} - -LIBDECOR_EXPORT void -libdecor_frame_set_visibility(struct libdecor_frame *frame, - bool visible) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - struct libdecor *context = frame_priv->context; - struct libdecor_plugin *plugin = context->plugin; - - frame_priv->visible = visible; - - /* enable/disable decorations that are managed by the compositor. - * Note that, as of xdg_decoration v1, this is just a hint and there is - * no reliable way of disabling all decorations. In practice this should - * work but per spec this is not guaranteed. - * - * See also: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/17 - */ - if (context->decoration_manager && - frame_priv->toplevel_decoration && - frame_priv->has_decoration_mode && - frame_priv->decoration_mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) { - zxdg_toplevel_decoration_v1_set_mode(frame_priv->toplevel_decoration, - frame->priv->visible - ? ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE - : ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); - } - - if (frame_priv->content_width <= 0 || - frame_priv->content_height <= 0) - return; - - /* enable/disable decorations that are managed by a plugin */ - if (frame_has_visible_client_side_decoration(frame)) { - /* show client-side decorations */ - plugin->priv->iface->frame_commit(plugin, frame, NULL, NULL); - } else { - /* destroy client-side decorations */ - plugin->priv->iface->frame_free(plugin, frame); - } - - frame_set_window_geometry(frame, - frame_priv->content_width, - frame_priv->content_height); - - libdecor_frame_toplevel_commit(frame); -} - -LIBDECOR_EXPORT bool -libdecor_frame_is_visible(struct libdecor_frame *frame) -{ - return frame->priv->visible; -} - -LIBDECOR_EXPORT void -libdecor_frame_set_parent(struct libdecor_frame *frame, - struct libdecor_frame *parent) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - if (!frame_priv->xdg_toplevel) - return; - - frame_priv->state.parent = parent ? parent->priv->xdg_toplevel : NULL; - - xdg_toplevel_set_parent(frame_priv->xdg_toplevel, - frame_priv->state.parent); -} - -LIBDECOR_EXPORT void -libdecor_frame_set_title(struct libdecor_frame *frame, - const char *title) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - struct libdecor_plugin *plugin = frame_priv->context->plugin; - - if (!streql(frame_priv->state.title, title)) { - free(frame_priv->state.title); - frame_priv->state.title = strdup(title); - - if (!frame_priv->xdg_toplevel) - return; - - xdg_toplevel_set_title(frame_priv->xdg_toplevel, title); - - plugin->priv->iface->frame_property_changed(plugin, frame); - } -} - -LIBDECOR_EXPORT const char * -libdecor_frame_get_title(struct libdecor_frame *frame) -{ - return frame->priv->state.title; -} - -LIBDECOR_EXPORT void -libdecor_frame_set_app_id(struct libdecor_frame *frame, - const char *app_id) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - free(frame_priv->state.app_id); - frame_priv->state.app_id = strdup(app_id); - - if (!frame_priv->xdg_toplevel) - return; - - xdg_toplevel_set_app_id(frame_priv->xdg_toplevel, app_id); -} - -static void -notify_on_capability_change(struct libdecor_frame *frame, - const enum libdecor_capabilities old_capabilities) -{ - struct libdecor_plugin *plugin = frame->priv->context->plugin; - struct libdecor_state *state; - - if (frame->priv->capabilities == old_capabilities) - return; - - if (frame->priv->content_width == 0 || - frame->priv->content_height == 0) - return; - - plugin->priv->iface->frame_property_changed(plugin, frame); - - if (!libdecor_frame_has_capability(frame, LIBDECOR_ACTION_RESIZE)) { - frame->priv->interactive_limits = frame->priv->state.content_limits; - /* set fixed window size */ - libdecor_frame_set_min_content_size(frame, - frame->priv->content_width, - frame->priv->content_height); - libdecor_frame_set_max_content_size(frame, - frame->priv->content_width, - frame->priv->content_height); - } else { - /* restore old limits */ - frame->priv->state.content_limits = frame->priv->interactive_limits; - } - - state = libdecor_state_new(frame->priv->content_width, - frame->priv->content_height); - libdecor_frame_commit(frame, state, NULL); - libdecor_state_free(state); - - libdecor_frame_toplevel_commit(frame); -} - -LIBDECOR_EXPORT void -libdecor_frame_set_capabilities(struct libdecor_frame *frame, - enum libdecor_capabilities capabilities) -{ - const enum libdecor_capabilities old_capabilities = - frame->priv->capabilities; - - frame->priv->capabilities |= capabilities; - - notify_on_capability_change(frame, old_capabilities); -} - -LIBDECOR_EXPORT void -libdecor_frame_unset_capabilities(struct libdecor_frame *frame, - enum libdecor_capabilities capabilities) -{ - const enum libdecor_capabilities old_capabilities = - frame->priv->capabilities; - - frame->priv->capabilities &= ~capabilities; - - notify_on_capability_change(frame, old_capabilities); -} - -LIBDECOR_EXPORT bool -libdecor_frame_has_capability(struct libdecor_frame *frame, - enum libdecor_capabilities capability) -{ - return frame->priv->capabilities & capability; -} - -LIBDECOR_EXPORT void -libdecor_frame_popup_grab(struct libdecor_frame *frame, - const char *seat_name) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - struct libdecor *context = frame_priv->context; - struct libdecor_plugin *plugin = context->plugin; - - plugin->priv->iface->frame_popup_grab(plugin, frame, seat_name); -} - -LIBDECOR_EXPORT void -libdecor_frame_popup_ungrab(struct libdecor_frame *frame, - const char *seat_name) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - struct libdecor *context = frame_priv->context; - struct libdecor_plugin *plugin = context->plugin; - - plugin->priv->iface->frame_popup_ungrab(plugin, frame, seat_name); -} - -LIBDECOR_EXPORT void -libdecor_frame_dismiss_popup(struct libdecor_frame *frame, - const char *seat_name) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - frame_priv->iface->dismiss_popup(frame, seat_name, frame_priv->user_data); -} - -LIBDECOR_EXPORT void -libdecor_frame_show_window_menu(struct libdecor_frame *frame, - struct wl_seat *wl_seat, - uint32_t serial, - int x, - int y) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - if (!frame_priv->xdg_toplevel) { - fprintf(stderr, "Can't show window menu before being mapped\n"); - return; - } - - xdg_toplevel_show_window_menu(frame_priv->xdg_toplevel, - wl_seat, serial, - x, y); -} - -LIBDECOR_EXPORT void -libdecor_frame_translate_coordinate(struct libdecor_frame *frame, - int content_x, - int content_y, - int *frame_x, - int *frame_y) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - struct libdecor *context = frame_priv->context; - struct libdecor_plugin *plugin = context->plugin; - - *frame_x = content_x; - *frame_y = content_y; - - if (frame_has_visible_client_side_decoration(frame) && - plugin->priv->iface->frame_get_border_size) { - int left, top; - plugin->priv->iface->frame_get_border_size(plugin, frame, NULL, - &left, NULL, &top, NULL); - *frame_x += left; - *frame_y += top; - } -} - -LIBDECOR_EXPORT void -libdecor_frame_set_min_content_size(struct libdecor_frame *frame, - int content_width, - int content_height) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - frame_priv->state.content_limits.min_width = content_width; - frame_priv->state.content_limits.min_height = content_height; -} - -LIBDECOR_EXPORT void -libdecor_frame_set_max_content_size(struct libdecor_frame *frame, - int content_width, - int content_height) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - frame_priv->state.content_limits.max_width = content_width; - frame_priv->state.content_limits.max_height = content_height; -} - -LIBDECOR_EXPORT void -libdecor_frame_get_min_content_size(const struct libdecor_frame *frame, - int *content_width, - int *content_height) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - *content_width = frame_priv->state.content_limits.min_width; - *content_height = frame_priv->state.content_limits.min_height; -} - -LIBDECOR_EXPORT void -libdecor_frame_get_max_content_size(const struct libdecor_frame *frame, - int *content_width, - int *content_height) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - *content_width = frame_priv->state.content_limits.max_width; - *content_height = frame_priv->state.content_limits.max_height; -} - -LIBDECOR_EXPORT enum libdecor_capabilities -libdecor_frame_get_capabilities(const struct libdecor_frame *frame) -{ - return frame->priv->capabilities; -} - -enum xdg_toplevel_resize_edge -edge_to_xdg_edge(enum libdecor_resize_edge edge) -{ - switch (edge) { - case LIBDECOR_RESIZE_EDGE_NONE: - return XDG_TOPLEVEL_RESIZE_EDGE_NONE; - case LIBDECOR_RESIZE_EDGE_TOP: - return XDG_TOPLEVEL_RESIZE_EDGE_TOP; - case LIBDECOR_RESIZE_EDGE_BOTTOM: - return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; - case LIBDECOR_RESIZE_EDGE_LEFT: - return XDG_TOPLEVEL_RESIZE_EDGE_LEFT; - case LIBDECOR_RESIZE_EDGE_TOP_LEFT: - return XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; - case LIBDECOR_RESIZE_EDGE_BOTTOM_LEFT: - return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; - case LIBDECOR_RESIZE_EDGE_RIGHT: - return XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; - case LIBDECOR_RESIZE_EDGE_TOP_RIGHT: - return XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; - case LIBDECOR_RESIZE_EDGE_BOTTOM_RIGHT: - return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; - } - - abort(); -} - -LIBDECOR_EXPORT void -libdecor_frame_resize(struct libdecor_frame *frame, - struct wl_seat *wl_seat, - uint32_t serial, - enum libdecor_resize_edge edge) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - enum xdg_toplevel_resize_edge xdg_edge; - - xdg_edge = edge_to_xdg_edge(edge); - xdg_toplevel_resize(frame_priv->xdg_toplevel, - wl_seat, serial, xdg_edge); -} - -LIBDECOR_EXPORT void -libdecor_frame_move(struct libdecor_frame *frame, - struct wl_seat *wl_seat, - uint32_t serial) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - xdg_toplevel_move(frame_priv->xdg_toplevel, wl_seat, serial); -} - -LIBDECOR_EXPORT void -libdecor_frame_set_minimized(struct libdecor_frame *frame) -{ - xdg_toplevel_set_minimized(frame->priv->xdg_toplevel); -} - -LIBDECOR_EXPORT void -libdecor_frame_set_maximized(struct libdecor_frame *frame) -{ - xdg_toplevel_set_maximized(frame->priv->xdg_toplevel); -} - -LIBDECOR_EXPORT void -libdecor_frame_unset_maximized(struct libdecor_frame *frame) -{ - xdg_toplevel_unset_maximized(frame->priv->xdg_toplevel); -} - -LIBDECOR_EXPORT void -libdecor_frame_set_fullscreen(struct libdecor_frame *frame, - struct wl_output *output) -{ - xdg_toplevel_set_fullscreen(frame->priv->xdg_toplevel, output); -} - -LIBDECOR_EXPORT void -libdecor_frame_unset_fullscreen(struct libdecor_frame *frame) -{ - xdg_toplevel_unset_fullscreen(frame->priv->xdg_toplevel); -} - -LIBDECOR_EXPORT bool -libdecor_frame_is_floating(struct libdecor_frame *frame) -{ - return state_is_floating(frame->priv->window_state); -} - -LIBDECOR_EXPORT void -libdecor_frame_close(struct libdecor_frame *frame) -{ - xdg_toplevel_close(frame, frame->priv->xdg_toplevel); -} - -bool -valid_limits(struct libdecor_frame_private *frame_priv) -{ - if (frame_priv->state.content_limits.min_width > 0 && - frame_priv->state.content_limits.max_width > 0 && - frame_priv->state.content_limits.min_width > - frame_priv->state.content_limits.max_width) - return false; - - if (frame_priv->state.content_limits.min_height > 0 && - frame_priv->state.content_limits.max_height > 0 && - frame_priv->state.content_limits.min_height > - frame_priv->state.content_limits.max_height) - return false; - - return true; -} - -static void -libdecor_frame_apply_limits(struct libdecor_frame *frame, - enum libdecor_window_state window_state) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - if (!valid_limits(frame_priv)) { - libdecor_notify_plugin_error( - frame_priv->context, - LIBDECOR_ERROR_INVALID_FRAME_CONFIGURATION, - "minimum size (%i,%i) must be smaller than maximum size (%i,%i)", - frame_priv->state.content_limits.min_width, - frame_priv->state.content_limits.min_height, - frame_priv->state.content_limits.max_width, - frame_priv->state.content_limits.max_height); - } - - /* If the frame is configured as non-resizable before the first - * configure event is received, we have to manually set the min/max - * limits with the configured content size afterwards. */ - if (!libdecor_frame_has_capability(frame, LIBDECOR_ACTION_RESIZE)) { - frame_priv->state.content_limits.min_width = - frame_priv->content_width; - frame_priv->state.content_limits.max_width = - frame_priv->content_width; - - frame_priv->state.content_limits.min_height = - frame_priv->content_height; - frame_priv->state.content_limits.max_height = - frame_priv->content_height; - } - - if (frame_priv->state.content_limits.min_width > 0 && - frame_priv->state.content_limits.min_height > 0) { - struct libdecor_state state_min; - int win_min_width, win_min_height; - - state_min.content_width = frame_priv->state.content_limits.min_width; - state_min.content_height = frame_priv->state.content_limits.min_height; - state_min.window_state = window_state; - - frame_get_window_size_for(frame, &state_min, - &win_min_width, &win_min_height); - xdg_toplevel_set_min_size(frame_priv->xdg_toplevel, - win_min_width, win_min_height); - } else { - xdg_toplevel_set_min_size(frame_priv->xdg_toplevel, 0, 0); - } - - if (frame_priv->state.content_limits.max_width > 0 && - frame_priv->state.content_limits.max_height > 0) { - struct libdecor_state state_max; - int win_max_width, win_max_height; - - state_max.content_width = frame_priv->state.content_limits.max_width; - state_max.content_height = frame_priv->state.content_limits.max_height; - state_max.window_state = window_state; - - frame_get_window_size_for(frame, &state_max, - &win_max_width, &win_max_height); - xdg_toplevel_set_max_size(frame_priv->xdg_toplevel, - win_max_width, win_max_height); - } else { - xdg_toplevel_set_max_size(frame_priv->xdg_toplevel, 0, 0); - } -} - -static void -libdecor_frame_apply_state(struct libdecor_frame *frame, - struct libdecor_state *state) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - frame_priv->content_width = state->content_width; - frame_priv->content_height = state->content_height; - - /* do not set limits in non-floating states */ - if (state_is_floating(state->window_state)) { - libdecor_frame_apply_limits(frame, state->window_state); - } -} - -LIBDECOR_EXPORT void -libdecor_frame_toplevel_commit(struct libdecor_frame *frame) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - frame_priv->iface->commit(frame, frame_priv->user_data); -} - -LIBDECOR_EXPORT void -libdecor_frame_commit(struct libdecor_frame *frame, - struct libdecor_state *state, - struct libdecor_configuration *configuration) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - struct libdecor *context = frame_priv->context; - struct libdecor_plugin *plugin = context->plugin; - - if (configuration && configuration->has_window_state) { - frame_priv->window_state = configuration->window_state; - state->window_state = configuration->window_state; - } else { - state->window_state = frame_priv->window_state; - } - - libdecor_frame_apply_state(frame, state); - - /* switch between decoration modes */ - if (frame_has_visible_client_side_decoration(frame)) { - plugin->priv->iface->frame_commit(plugin, frame, state, - configuration); - } else { - plugin->priv->iface->frame_free(plugin, frame); - } - - frame_set_window_geometry(frame, - frame_priv->content_width, - frame_priv->content_height); - - if (configuration) { - xdg_surface_ack_configure(frame_priv->xdg_surface, - configuration->serial); - } -} - -static void -do_map(struct libdecor_frame *frame) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - frame_priv->pending_map = false; - wl_surface_commit(frame_priv->wl_surface); -} - -LIBDECOR_EXPORT void -libdecor_frame_map(struct libdecor_frame *frame) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - if (!frame_priv->xdg_surface) { - frame_priv->pending_map = true; - return; - } - - do_map(frame); -} - -LIBDECOR_EXPORT struct wl_surface * -libdecor_frame_get_wl_surface(struct libdecor_frame *frame) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - return frame_priv->wl_surface; -} - -LIBDECOR_EXPORT struct xdg_surface * -libdecor_frame_get_xdg_surface(struct libdecor_frame *frame) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - return frame_priv->xdg_surface; -} - -LIBDECOR_EXPORT struct xdg_toplevel * -libdecor_frame_get_xdg_toplevel(struct libdecor_frame *frame) -{ - return frame->priv->xdg_toplevel; -} - -LIBDECOR_EXPORT void -libdecor_set_handle_application_cursor(struct libdecor *context, - bool handle_cursor) -{ - struct libdecor_plugin *plugin = context->plugin; - - plugin->priv->iface->set_handle_application_cursor(plugin, - handle_cursor); -} - -LIBDECOR_EXPORT int -libdecor_frame_get_content_width(struct libdecor_frame *frame) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - return frame_priv->content_width; -} - -LIBDECOR_EXPORT int -libdecor_frame_get_content_height(struct libdecor_frame *frame) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - return frame_priv->content_height; -} - -LIBDECOR_EXPORT enum libdecor_window_state -libdecor_frame_get_window_state(struct libdecor_frame *frame) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - return frame_priv->window_state; -} - -LIBDECOR_EXPORT enum libdecor_wm_capabilities -libdecor_frame_get_wm_capabilities(struct libdecor_frame *frame) -{ - struct libdecor_frame_private *frame_priv = frame->priv; - - return frame_priv->wm_capabilities; -} - -LIBDECOR_EXPORT int -libdecor_plugin_init(struct libdecor_plugin *plugin, - struct libdecor *context, - struct libdecor_plugin_interface *iface) -{ - plugin->priv = zalloc(sizeof (struct libdecor_plugin_private)); - if (!plugin->priv) - return -1; - - plugin->priv->iface = iface; - - return 0; -} - -LIBDECOR_EXPORT void -libdecor_plugin_release(struct libdecor_plugin *plugin) -{ - free(plugin->priv); -} - -static void -xdg_wm_base_ping(void *user_data, - struct xdg_wm_base *xdg_wm_base, - uint32_t serial) -{ - xdg_wm_base_pong(xdg_wm_base, serial); -} - -static const struct xdg_wm_base_listener xdg_wm_base_listener = { - xdg_wm_base_ping, -}; - -static void -init_xdg_wm_base(struct libdecor *context, - uint32_t id, - uint32_t version) -{ - uint32_t desired_version = 2; - - /* Find the required version for the available features. */ -#ifdef XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION - desired_version = MAX(desired_version, XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION); -#endif -#ifdef XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION - desired_version = MAX(desired_version, XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION); -#endif -#ifdef XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION - desired_version = MAX(desired_version, XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION); -#endif -#ifdef XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION - desired_version = MAX(desired_version, XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION); -#endif - - context->xdg_wm_base = wl_registry_bind(context->wl_registry, - id, - &xdg_wm_base_interface, - MIN(version, desired_version)); - xdg_wm_base_add_listener(context->xdg_wm_base, - &xdg_wm_base_listener, - context); -} - -static void -registry_handle_global(void *user_data, - struct wl_registry *wl_registry, - uint32_t id, - const char *interface, - uint32_t version) -{ - struct libdecor *context = user_data; - - if (!strcmp(interface, xdg_wm_base_interface.name)) { - init_xdg_wm_base(context, id, version); - } else if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name)) { - const char *force_csd = getenv("LIBDECOR_FORCE_CSD"); - - if (force_csd && atoi(force_csd)) { - return; - } - - context->decoration_manager = wl_registry_bind( - context->wl_registry, id, - &zxdg_decoration_manager_v1_interface, - MIN(version,2)); - } -} - -static void -registry_handle_global_remove(void *user_data, - struct wl_registry *wl_registry, - uint32_t name) -{ -} - -static const struct wl_registry_listener registry_listener = { - registry_handle_global, - registry_handle_global_remove -}; - -static bool -is_compositor_compatible(struct libdecor *context) -{ - if (!context->xdg_wm_base) - return false; - - return true; -} - -static void -notify_error(struct libdecor *context, - enum libdecor_error error, - const char *message) -{ - context->has_error = true; - context->iface->error(context, error, message); - context->plugin->priv->iface->destroy(context->plugin); -} - -static void -finish_init(struct libdecor *context) -{ - struct libdecor_frame *frame; - - wl_list_for_each(frame, &context->frames, link) - init_shell_surface(frame); -} - -static void -init_wl_display_callback(void *user_data, - struct wl_callback *callback, - uint32_t time) -{ - struct libdecor *context = user_data; - - context->init_done = true; - - wl_callback_destroy(callback); - context->init_callback = NULL; - - if (!is_compositor_compatible(context)) { - notify_error(context, - LIBDECOR_ERROR_COMPOSITOR_INCOMPATIBLE, - "Compositor is missing required interfaces"); - } - - if (context->plugin_ready) { - finish_init(context); - } -} - -static const struct wl_callback_listener init_wl_display_callback_listener = { - init_wl_display_callback -}; - -struct plugin_loader { - struct wl_list link; - void *lib; - const struct libdecor_plugin_description *description; - int priority; - char *name; -}; - -static int -calculate_priority(const struct libdecor_plugin_description *plugin_description) -{ - const char *current_desktop; - int i; - - if (!plugin_description->priorities) - return -1; - - current_desktop = getenv("XDG_CURRENT_DESKTOP"); - - i = 0; - while (true) { - struct libdecor_plugin_priority priority = - plugin_description->priorities[i]; - - i++; - - if (priority.desktop) { - char *tokens; - char *saveptr; - char *token; - - if (!current_desktop) - continue; - - tokens = strdup(current_desktop); - token = strtok_r(tokens, ":", &saveptr); - while (token) { - if (strcmp(priority.desktop, token) == 0) { - free(tokens); - return priority.priority; - } - token = strtok_r(NULL, ":", &saveptr); - } - free(tokens); - } else { - return priority.priority; - } - } - - return -1; -} - -static bool -check_symbol_conflicts(const struct libdecor_plugin_description *plugin_description, void *lib) -{ - char * const *symbol; - - symbol = plugin_description->conflicting_symbols; - while (*symbol) { - dlerror(); - void *sym = dlsym(RTLD_DEFAULT, *symbol); - if (!dlerror()) { - void *libsym = dlsym(lib, *symbol); - if (!dlerror() && libsym != sym) { - fprintf(stderr, "Plugin \"%s\" uses conflicting symbol \"%s\".\n", - plugin_description->description, *symbol); - return false; - } - } - - symbol++; - } - - return true; -} - -static struct plugin_loader * -load_plugin_loader(struct libdecor *context, - const char *path, - const char *name) -{ - char *ext; - char *filename; - void *lib; - const struct libdecor_plugin_description *plugin_description; - int priority; - struct plugin_loader *plugin_loader; - - ext = strrchr(name, '.'); - if (ext == NULL || strcmp(ext, ".so") != 0) - return NULL; - - if (asprintf(&filename, "%s/%s", path, name) == -1) - return NULL; - - lib = dlopen(filename, RTLD_NOW | RTLD_LAZY); - free(filename); - if (!lib) { - fprintf(stderr, "Failed to load plugin: '%s'\n", dlerror()); - return NULL; - } - - plugin_description = dlsym(lib, "libdecor_plugin_description"); - if (!plugin_description) { - fprintf(stderr, - "Failed to load plugin '%s': no plugin description symbol\n", - name); - dlclose(lib); - return NULL; - } - - if (plugin_description->api_version != LIBDECOR_PLUGIN_API_VERSION) { - fprintf(stderr, - "Plugin '%s' found, but it's incompatible " - "(expected API version %d, but got %d)\n", - name, - LIBDECOR_PLUGIN_API_VERSION, - plugin_description->api_version); - dlclose(lib); - return NULL; - } - - if (!(plugin_description->capabilities & LIBDECOR_PLUGIN_CAPABILITY_BASE)) { - dlclose(lib); - return NULL; - } - - if (!check_symbol_conflicts(plugin_description, lib)) { - dlclose(lib); - return NULL; - } - - priority = calculate_priority(plugin_description); - if (priority == -1) { - fprintf(stderr, - "Plugin '%s' found, but has an invalid description\n", - name); - dlclose(lib); - return NULL; - } - - plugin_loader = zalloc(sizeof *plugin_loader); - plugin_loader->description = plugin_description; - plugin_loader->lib = lib; - plugin_loader->priority = priority; - plugin_loader->name = strdup(name); - - return plugin_loader; -} - -static bool -plugin_loader_higher_priority(struct plugin_loader *plugin_loader, - struct plugin_loader *best_plugin_loader) -{ - return plugin_loader->priority > best_plugin_loader->priority; -} - -static int -init_plugins(struct libdecor *context) -{ - const char *plugin_dir_env; - char *all_plugin_dirs; - char *plugin_dir; - char *saveptr; - DIR *dir; - struct wl_list plugin_loaders; - struct plugin_loader *plugin_loader, *tmp; - struct plugin_loader *best_plugin_loader; - struct libdecor_plugin *plugin; - - plugin_dir_env = getenv("LIBDECOR_PLUGIN_DIR"); - if (!plugin_dir_env) { - plugin_dir_env = LIBDECOR_PLUGIN_DIR; - } - all_plugin_dirs = strdup(plugin_dir_env); - - wl_list_init(&plugin_loaders); - plugin_dir = strtok_r(all_plugin_dirs, ":", &saveptr); - while (plugin_dir) { - dir = opendir(plugin_dir); - if (!dir) { - fprintf(stderr, "Couldn't open plugin directory: %s\n", - strerror(errno)); - } else { - - while (true) { - struct dirent *de; - - de = readdir(dir); - if (!de) - break; - - plugin_loader = load_plugin_loader(context, - plugin_dir, - de->d_name); - if (!plugin_loader) - continue; - - wl_list_insert(plugin_loaders.prev, &plugin_loader->link); - } - - closedir(dir); - } - plugin_dir = strtok_r(NULL, ":", &saveptr); - } - free(all_plugin_dirs); - -retry_next: - best_plugin_loader = NULL; - wl_list_for_each(plugin_loader, &plugin_loaders, link) { - if (!best_plugin_loader) { - best_plugin_loader = plugin_loader; - continue; - } - - if (plugin_loader_higher_priority(plugin_loader, - best_plugin_loader)) - best_plugin_loader = plugin_loader; - } - - if (!best_plugin_loader) - return -1; - - plugin_loader = best_plugin_loader; - plugin = plugin_loader->description->constructor(context); - if (!plugin) { - fprintf(stderr, - "Failed to load plugin '%s': failed to init\n", - plugin_loader->name); - dlclose(plugin_loader->lib); - wl_list_remove(&plugin_loader->link); - free(plugin_loader->name); - free(plugin_loader); - goto retry_next; - } - - context->plugin = plugin; - - wl_list_remove(&plugin_loader->link); - free(plugin_loader->name); - free(plugin_loader); - - wl_list_for_each_safe(plugin_loader, tmp, &plugin_loaders, link) { - dlclose(plugin_loader->lib); - free(plugin_loader->name); - free(plugin_loader); - } - - return 0; -} - -LIBDECOR_EXPORT void * -libdecor_get_user_data(struct libdecor *context) -{ - return context->user_data; -} - -LIBDECOR_EXPORT void -libdecor_set_user_data(struct libdecor *context, void *user_data) -{ - context->user_data = user_data; -} - -LIBDECOR_EXPORT int -libdecor_get_fd(struct libdecor *context) -{ - struct libdecor_plugin *plugin = context->plugin; - - return plugin->priv->iface->get_fd(plugin); -} - -LIBDECOR_EXPORT int -libdecor_dispatch(struct libdecor *context, - int timeout) -{ - struct libdecor_plugin *plugin = context->plugin; - - return plugin->priv->iface->dispatch(plugin, timeout); -} - -LIBDECOR_EXPORT struct wl_display * -libdecor_get_wl_display(struct libdecor *context) -{ - return context->wl_display; -} - -LIBDECOR_EXPORT void -libdecor_notify_plugin_ready(struct libdecor *context) -{ - context->plugin_ready = true; - - if (context->init_done) - finish_init(context); -} - -LIBDECOR_EXPORT void -libdecor_notify_plugin_error(struct libdecor *context, - enum libdecor_error error, - const char *__restrict fmt, - ...) -{ - char *msg = NULL; - int nbytes = 0; - va_list argp; - - if (context->has_error) - return; - - va_start(argp, fmt); - nbytes = vasprintf(&msg, fmt, argp); - va_end(argp); - - if (nbytes>0) - notify_error(context, error, msg); - - if (msg) - free(msg); -} - -LIBDECOR_EXPORT void -libdecor_unref(struct libdecor *context) -{ - context->ref_count--; - if (context->ref_count == 0) { - if (context->plugin) - context->plugin->priv->iface->destroy(context->plugin); - if (context->init_callback) - wl_callback_destroy(context->init_callback); - wl_registry_destroy(context->wl_registry); - if (context->xdg_wm_base) - xdg_wm_base_destroy(context->xdg_wm_base); - if (context->decoration_manager) - zxdg_decoration_manager_v1_destroy( - context->decoration_manager); - free(context); - } -} - -LIBDECOR_EXPORT struct libdecor * -libdecor_new(struct wl_display *wl_display, - const struct libdecor_interface *iface) -{ - return libdecor_new_with_user_data(wl_display, iface, NULL); -} - -LIBDECOR_EXPORT struct libdecor * -libdecor_new_with_user_data(struct wl_display *wl_display, - const struct libdecor_interface *iface, - void *user_data) -{ - struct libdecor *context; - - context = zalloc(sizeof *context); - - context->ref_count = 1; - context->iface = iface; - context->user_data = user_data; - context->wl_display = wl_display; - context->wl_registry = wl_display_get_registry(wl_display); - wl_registry_add_listener(context->wl_registry, - ®istry_listener, - context); - context->init_callback = wl_display_sync(context->wl_display); - wl_callback_add_listener(context->init_callback, - &init_wl_display_callback_listener, - context); - - wl_list_init(&context->frames); - - if (init_plugins(context) != 0) { - fprintf(stderr, - "No plugins found, falling back on no decorations\n"); - context->plugin = libdecor_fallback_plugin_new(context); - } - - wl_display_flush(wl_display); - - return context; -} diff --git a/libdecor/src/libdecor.h b/libdecor/src/libdecor.h deleted file mode 100644 index 71c9d7660..000000000 --- a/libdecor/src/libdecor.h +++ /dev/null @@ -1,605 +0,0 @@ -/* - * Copyright © 2017-2018 Red Hat Inc. - * Copyright © 2018 Jonas Ådahl - * Copyright © 2019 Christian Rauch - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef LIBDECOR_H -#define LIBDECOR_H - -#include <stdbool.h> -#include <wayland-client.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(__GNUC__) && __GNUC__ >= 4 -#define LIBDECOR_EXPORT __attribute__ ((visibility("default"))) -#else -#define LIBDECOR_EXPORT -#endif - -struct xdg_toplevel; - -/** \class libdecor - * - * \brief A libdecor context instance. - */ -struct libdecor; - -/** \class libdecor_frame - * - * \brief A frame used for decorating a Wayland surface. - */ -struct libdecor_frame; - -/** \class libdecor_configuration - * - * \brief An object representing a toplevel window configuration. - */ -struct libdecor_configuration; - -/** \class libdecor_state - * - * \brief An object corresponding to a configured content state. - */ -struct libdecor_state; - -enum libdecor_error { - LIBDECOR_ERROR_COMPOSITOR_INCOMPATIBLE, - LIBDECOR_ERROR_INVALID_FRAME_CONFIGURATION, -}; - -enum libdecor_window_state { - LIBDECOR_WINDOW_STATE_NONE = 0, - LIBDECOR_WINDOW_STATE_ACTIVE = 1 << 0, - LIBDECOR_WINDOW_STATE_MAXIMIZED = 1 << 1, - LIBDECOR_WINDOW_STATE_FULLSCREEN = 1 << 2, - LIBDECOR_WINDOW_STATE_TILED_LEFT = 1 << 3, - LIBDECOR_WINDOW_STATE_TILED_RIGHT = 1 << 4, - LIBDECOR_WINDOW_STATE_TILED_TOP = 1 << 5, - LIBDECOR_WINDOW_STATE_TILED_BOTTOM = 1 << 6, - LIBDECOR_WINDOW_STATE_SUSPENDED = 1 << 7, - LIBDECOR_WINDOW_STATE_RESIZING = 1 << 8, - LIBDECOR_WINDOW_STATE_CONSTRAINED_LEFT = 1 << 9, - LIBDECOR_WINDOW_STATE_CONSTRAINED_RIGHT = 1 << 10, - LIBDECOR_WINDOW_STATE_CONSTRAINED_TOP = 1 << 11, - LIBDECOR_WINDOW_STATE_CONSTRAINED_BOTTOM = 1 << 12, -}; - -enum libdecor_resize_edge { - LIBDECOR_RESIZE_EDGE_NONE, - LIBDECOR_RESIZE_EDGE_TOP, - LIBDECOR_RESIZE_EDGE_BOTTOM, - LIBDECOR_RESIZE_EDGE_LEFT, - LIBDECOR_RESIZE_EDGE_TOP_LEFT, - LIBDECOR_RESIZE_EDGE_BOTTOM_LEFT, - LIBDECOR_RESIZE_EDGE_RIGHT, - LIBDECOR_RESIZE_EDGE_TOP_RIGHT, - LIBDECOR_RESIZE_EDGE_BOTTOM_RIGHT, -}; - -enum libdecor_capabilities { - LIBDECOR_ACTION_MOVE = 1 << 0, - LIBDECOR_ACTION_RESIZE = 1 << 1, - LIBDECOR_ACTION_MINIMIZE = 1 << 2, - LIBDECOR_ACTION_FULLSCREEN = 1 << 3, - LIBDECOR_ACTION_CLOSE = 1 << 4, -}; - -enum libdecor_wm_capabilities { - LIBDECOR_WM_CAPABILITIES_WINDOW_MENU = 1 << 0, - LIBDECOR_WM_CAPABILITIES_MAXIMIZE = 1 << 1, - LIBDECOR_WM_CAPABILITIES_FULLSCREEN = 1 << 2, - LIBDECOR_WM_CAPABILITIES_MINIMIZE = 1 << 3 -}; - -struct libdecor_interface { - /** - * An error event - */ - void (* error)(struct libdecor *context, - enum libdecor_error error, - const char *message); - - /* Reserved */ - void (* reserved0)(void); - void (* reserved1)(void); - void (* reserved2)(void); - void (* reserved3)(void); - void (* reserved4)(void); - void (* reserved5)(void); - void (* reserved6)(void); - void (* reserved7)(void); - void (* reserved8)(void); - void (* reserved9)(void); -}; - -/** - * Interface for integrating a Wayland surface with libdecor. - */ -struct libdecor_frame_interface { - /** - * A new configuration was received. An application should respond to - * this by creating a suitable libdecor_state, and apply it using - * libdecor_frame_commit. - */ - void (* configure)(struct libdecor_frame *frame, - struct libdecor_configuration *configuration, - void *user_data); - - /** - * The window was requested to be closed by the compositor. - */ - void (* close)(struct libdecor_frame *frame, - void *user_data); - - /** - * The window decoration asked to have the main surface to be - * committed. This is required when the decoration is implemented using - * synchronous subsurfaces. - */ - void (* commit)(struct libdecor_frame *frame, - void *user_data); - - /** - * Any mapped popup that has a grab on the given seat should be - * dismissed. - */ - void (* dismiss_popup)(struct libdecor_frame *frame, - const char *seat_name, - void *user_data); - - /** - * The recommended client region bounds for the window. - * This will be followed by a configure event. - */ - void (* bounds)(struct libdecor_frame *frame, - int width, - int height, - void *user_data); - - /* Reserved */ - void (* reserved0)(void); - void (* reserved1)(void); - void (* reserved2)(void); - void (* reserved3)(void); - void (* reserved4)(void); - void (* reserved5)(void); - void (* reserved6)(void); - void (* reserved7)(void); - void (* reserved8)(void); -}; - -/** - * Remove a reference to the libdecor instance. When the reference count - * reaches zero, it is freed. - */ -void -libdecor_unref(struct libdecor *context); - -/** - * Create a new libdecor context for the given wl_display. - */ -struct libdecor * -libdecor_new(struct wl_display *display, - const struct libdecor_interface *iface); - -/** - * Create a new libdecor context for the given wl_display and attach user data. - */ -struct libdecor * -libdecor_new_with_user_data(struct wl_display *display, - const struct libdecor_interface *iface, - void *user_data); - -/** - * Get the user data associated with this libdecor context. - */ -void * -libdecor_get_user_data(struct libdecor *context); - -/** - * Set the user data associated with this libdecor context. - */ -void -libdecor_set_user_data(struct libdecor *context, void *user_data); - -/** - * Get the file descriptor used by libdecor. This is similar to - * wl_display_get_fd(), thus should be polled, and when data is available, - * libdecor_dispatch() should be called. - */ -int -libdecor_get_fd(struct libdecor *context); - -/** - * Dispatch events. This function should be called when data is available on - * the file descriptor returned by libdecor_get_fd(). If timeout is zero, this - * function will never block. - */ -int -libdecor_dispatch(struct libdecor *context, - int timeout); - -/** - * Decorate the given content wl_surface. - * - * This will create an xdg_surface and an xdg_toplevel, and integrate it - * properly with the windowing system, including creating appropriate - * decorations when needed, as well as handle windowing integration events such - * as resizing, moving, maximizing, etc. - * - * The passed wl_surface should only contain actual application content, - * without any window decoration. - */ -struct libdecor_frame * -libdecor_decorate(struct libdecor *context, - struct wl_surface *surface, - const struct libdecor_frame_interface *iface, - void *user_data); - -/** - * Add a reference to the frame object. - */ -void -libdecor_frame_ref(struct libdecor_frame *frame); - -/** - * Remove a reference to the frame object. When the reference count reaches - * zero, the frame object is destroyed. - */ -void -libdecor_frame_unref(struct libdecor_frame *frame); - -/** - * Get the user data associated with this libdecor frame. - */ -void * -libdecor_frame_get_user_data(struct libdecor_frame *frame); - -/** - * Set the user data associated with this libdecor frame. - */ -void -libdecor_frame_set_user_data(struct libdecor_frame *frame, void *user_data); - -/** - * Set the visibility of the frame. - * - * If an application wants to be borderless, it can set the frame visibility to - * false. - */ -void -libdecor_frame_set_visibility(struct libdecor_frame *frame, - bool visible); - -/** - * Get the visibility of the frame. - */ -bool -libdecor_frame_is_visible(struct libdecor_frame *frame); - - -/** - * Set the parent of the window. - * - * This can be used to stack multiple toplevel windows above or under each - * other. - */ -void -libdecor_frame_set_parent(struct libdecor_frame *frame, - struct libdecor_frame *parent); - -/** - * Set the title of the window. - */ -void -libdecor_frame_set_title(struct libdecor_frame *frame, - const char *title); - -/** - * Get the title of the window. - */ -const char * -libdecor_frame_get_title(struct libdecor_frame *frame); - -/** - * Set the application ID of the window. - */ -void -libdecor_frame_set_app_id(struct libdecor_frame *frame, - const char *app_id); - -/** - * Set new capabilities of the window. - * - * This determines whether e.g. a window decoration should show a maximize - * button, etc. - * - * Setting a capability does not implicitly unset any other. - */ -void -libdecor_frame_set_capabilities(struct libdecor_frame *frame, - enum libdecor_capabilities capabilities); - -/** - * Unset capabilities of the window. - * - * The opposite of libdecor_frame_set_capabilities. - */ -void -libdecor_frame_unset_capabilities(struct libdecor_frame *frame, - enum libdecor_capabilities capabilities); - -/** - * Check whether the window has any of the given capabilities. - */ -bool -libdecor_frame_has_capability(struct libdecor_frame *frame, - enum libdecor_capabilities capability); - -/** - * Show the window menu. - */ -void -libdecor_frame_show_window_menu(struct libdecor_frame *frame, - struct wl_seat *wl_seat, - uint32_t serial, - int x, - int y); - -/** - * Issue a popup grab on the window. Call this when a xdg_popup is mapped, so - * that it can be properly dismissed by the decorations. - */ -void -libdecor_frame_popup_grab(struct libdecor_frame *frame, - const char *seat_name); - -/** - * Release the popup grab. Call this when you unmap a popup. - */ -void -libdecor_frame_popup_ungrab(struct libdecor_frame *frame, - const char *seat_name); - -/** - * Translate content surface local coordinates to toplevel window local - * coordinates. - * - * This can be used to translate surface coordinates to coordinates useful for - * e.g. showing the window menu, or positioning a popup. - */ -void -libdecor_frame_translate_coordinate(struct libdecor_frame *frame, - int surface_x, - int surface_y, - int *frame_x, - int *frame_y); - -/** - * Set the min content size. - * - * This translates roughly to xdg_toplevel_set_min_size(). - */ -void -libdecor_frame_set_min_content_size(struct libdecor_frame *frame, - int content_width, - int content_height); - -/** - * Set the max content size. - * - * This translates roughly to xdg_toplevel_set_max_size(). - */ -void -libdecor_frame_set_max_content_size(struct libdecor_frame *frame, - int content_width, - int content_height); - -/** - * Get the min content size. - */ -void -libdecor_frame_get_min_content_size(const struct libdecor_frame *frame, - int *content_width, - int *content_height); - -/** - * Get the max content size. - */ -void -libdecor_frame_get_max_content_size(const struct libdecor_frame *frame, - int *content_width, - int *content_height); - -/** - * Initiate an interactive resize. - * - * This roughly translates to xdg_toplevel_resize(). - */ -void -libdecor_frame_resize(struct libdecor_frame *frame, - struct wl_seat *wl_seat, - uint32_t serial, - enum libdecor_resize_edge edge); - -/** - * Initiate an interactive move. - * - * This roughly translates to xdg_toplevel_move(). - */ -void -libdecor_frame_move(struct libdecor_frame *frame, - struct wl_seat *wl_seat, - uint32_t serial); - -/** - * Commit a new window state. This can be called on application driven resizes - * when the window is floating, or in response to received configurations, i.e. - * from e.g. interactive resizes or state changes. - */ -void -libdecor_frame_commit(struct libdecor_frame *frame, - struct libdecor_state *state, - struct libdecor_configuration *configuration); - -/** - * Minimize the window. - * - * Roughly translates to xdg_toplevel_set_minimized(). - */ -void -libdecor_frame_set_minimized(struct libdecor_frame *frame); - -/** - * Maximize the window. - * - * Roughly translates to xdg_toplevel_set_maximized(). - */ -void -libdecor_frame_set_maximized(struct libdecor_frame *frame); - -/** - * Unmaximize the window. - * - * Roughly translates to xdg_toplevel_unset_maximized(). - */ -void -libdecor_frame_unset_maximized(struct libdecor_frame *frame); - -/** - * Fullscreen the window. - * - * Roughly translates to xdg_toplevel_set_fullscreen(). - */ -void -libdecor_frame_set_fullscreen(struct libdecor_frame *frame, - struct wl_output *output); - -/** - * Unfullscreen the window. - * - * Roughly translates to xdg_toplevel_unset_unfullscreen(). - */ -void -libdecor_frame_unset_fullscreen(struct libdecor_frame *frame); - -/** - * Return true if the window is floating. - * - * A window is floating when it's not maximized, tiled, fullscreen, or in any - * similar way with a fixed size and state. - * Note that this function uses the "applied" configuration. If this function - * is used in the 'configure' callback, the provided configuration has to be - * applied via 'libdecor_frame_commit' first, before it will reflect the current - * window state from the provided configuration. - */ -bool -libdecor_frame_is_floating(struct libdecor_frame *frame); - -/** - * Close the window. - * - * Roughly translates to xdg_toplevel_close(). - */ -void -libdecor_frame_close(struct libdecor_frame *frame); - -/** - * Map the window. - * - * This will eventually result in the initial configure event. - */ -void -libdecor_frame_map(struct libdecor_frame *frame); - -/** - * Get the associated xdg_surface for content wl_surface. - */ -struct xdg_surface * -libdecor_frame_get_xdg_surface(struct libdecor_frame *frame); - -/** - * Get the associated xdg_toplevel for the content wl_surface. - */ -struct xdg_toplevel * -libdecor_frame_get_xdg_toplevel(struct libdecor_frame *frame); - -/** - * Get the supported window manager capabilities for the window. - */ -enum libdecor_wm_capabilities -libdecor_frame_get_wm_capabilities(struct libdecor_frame *frame); - -/** - * Tell libdecor to set the default pointer cursor when the pointer is over an - * application surface. The default false. - */ -void -libdecor_set_handle_application_cursor(struct libdecor *context, - bool handle_cursor); - -/** - * Create a new content surface state. - */ -struct libdecor_state * -libdecor_state_new(int width, - int height); - -/** - * Free a content surface state. - */ -void -libdecor_state_free(struct libdecor_state *state); - -/** - * Get the expected size of the content for this configuration. - * - * If the configuration doesn't contain a size, false is returned. - */ -bool -libdecor_configuration_get_content_size(struct libdecor_configuration *configuration, - struct libdecor_frame *frame, - int *width, - int *height); - -/** - * Get the window state for this configuration. - * - * If the configuration doesn't contain any associated window state, false is - * returned, and the application should assume the window state remains - * unchanged. - */ -bool -libdecor_configuration_get_window_state(struct libdecor_configuration *configuration, - enum libdecor_window_state *window_state); - -#ifdef __cplusplus -} -#endif - -#endif /* LIBDECOR_H */ diff --git a/libdecor/src/os-compatibility.c b/libdecor/src/os-compatibility.c deleted file mode 100644 index 23766473d..000000000 --- a/libdecor/src/os-compatibility.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright © 2012 Collabora, Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "config.h" - -#include <sys/types.h> -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> -#include <signal.h> -#include <string.h> -#include <stdlib.h> - -#ifdef HAVE_MEMFD_CREATE -#include <sys/mman.h> -#endif - -#include "os-compatibility.h" - -#ifndef HAVE_MKOSTEMP -static int -set_cloexec_or_close(int fd) -{ - long flags; - - if (fd == -1) - return -1; - - flags = fcntl(fd, F_GETFD); - if (flags == -1) - goto err; - - if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) - goto err; - - return fd; - -err: - close(fd); - return -1; -} -#endif - -static int -create_tmpfile_cloexec(char *tmpname) -{ - int fd; - -#ifdef HAVE_MKOSTEMP - fd = mkostemp(tmpname, O_CLOEXEC); - if (fd >= 0) - unlink(tmpname); -#else - fd = mkstemp(tmpname); - if (fd >= 0) { - fd = set_cloexec_or_close(fd); - unlink(tmpname); - } -#endif - - return fd; -} - -static int -os_resize_anonymous_file(int fd, off_t size) -{ -#ifdef HAVE_POSIX_FALLOCATE - sigset_t mask; - sigset_t old_mask; - - /* posix_fallocate() might be interrupted, so we need to check - * for EINTR and retry in that case. - * However, in the presence of an alarm, the interrupt may trigger - * repeatedly and prevent a large posix_fallocate() to ever complete - * successfully, so we need to first block SIGALRM to prevent - * this. - */ - sigemptyset(&mask); - sigaddset(&mask, SIGALRM); - sigprocmask(SIG_BLOCK, &mask, &old_mask); - /* - * Filesystems that do not support fallocate will return EINVAL or - * EOPNOTSUPP. In this case we need to fall back to ftruncate - */ - do { - errno = posix_fallocate(fd, 0, size); - } while (errno == EINTR); - sigprocmask(SIG_SETMASK, &old_mask, NULL); - if (errno == 0) - return 0; - else if (errno != EINVAL && errno != EOPNOTSUPP) - return -1; -#endif - if (ftruncate(fd, size) < 0) - return -1; - - return 0; -} - -/* - * Create a new, unique, anonymous file of the given size, and - * return the file descriptor for it. The file descriptor is set - * CLOEXEC. The file is immediately suitable for mmap()'ing - * the given size at offset zero. - * - * The file should not have a permanent backing store like a disk, - * but may have if XDG_RUNTIME_DIR is not properly implemented in OS. - * - * The file name is deleted from the file system. - * - * The file is suitable for buffer sharing between processes by - * transmitting the file descriptor over Unix sockets using the - * SCM_RIGHTS methods. - * - * If the C library implements posix_fallocate(), it is used to - * guarantee that disk space is available for the file at the - * given size. If disk space is insufficient, errno is set to ENOSPC. - * If posix_fallocate() is not supported, program may receive - * SIGBUS on accessing mmap()'ed file contents instead. - * - * If the C library implements memfd_create(), it is used to create the - * file purely in memory, without any backing file name on the file - * system, and then sealing off the possibility of shrinking it. This - * can then be checked before accessing mmap()'ed file contents, to - * make sure SIGBUS can't happen. It also avoids requiring - * XDG_RUNTIME_DIR. - */ -int -libdecor_os_create_anonymous_file(off_t size) -{ - static const char template[] = "/libdecor-shared-XXXXXX"; - const char *path; - char *name; - int fd; - -#ifdef HAVE_MEMFD_CREATE - fd = memfd_create("libdecor", MFD_CLOEXEC | MFD_ALLOW_SEALING); - if (fd >= 0) { - /* We can add this seal before calling posix_fallocate(), as - * the file is currently zero-sized anyway. - * - * There is also no need to check for the return value, we - * couldn't do anything with it anyway. - */ - fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); - } else -#endif - { - path = getenv("XDG_RUNTIME_DIR"); - if (!path) { - errno = ENOENT; - return -1; - } - - name = malloc(strlen(path) + sizeof(template)); - if (!name) - return -1; - - strcpy(name, path); - strcat(name, template); - - fd = create_tmpfile_cloexec(name); - - free(name); - - if (fd < 0) - return -1; - } - - if (os_resize_anonymous_file(fd, size) < 0) { - close(fd); - return -1; - } - - return fd; -} diff --git a/libdecor/src/os-compatibility.h b/libdecor/src/os-compatibility.h deleted file mode 100644 index 6ce49d01c..000000000 --- a/libdecor/src/os-compatibility.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright © 2012 Collabora, Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef OS_COMPATIBILITY_H -#define OS_COMPATIBILITY_H - -#include <sys/types.h> - -int -libdecor_os_create_anonymous_file(off_t size); - -#endif /* OS_COMPATIBILITY_H */ diff --git a/libdecor/src/plugins/cairo/libdecor-cairo.c b/libdecor/src/plugins/cairo/libdecor-cairo.c deleted file mode 100644 index d8854bc8e..000000000 --- a/libdecor/src/plugins/cairo/libdecor-cairo.c +++ /dev/null @@ -1,2796 +0,0 @@ -/* - * Copyright © 2018 Jonas Ådahl - * Copyright © 2019 Christian Rauch - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "config.h" - -#include <linux/input.h> -#include <fcntl.h> -#include <poll.h> -#include <string.h> -#include <sys/mman.h> -#include <unistd.h> -#include <stdio.h> -#include <errno.h> -#include <limits.h> -#include <wayland-cursor.h> - -#include "libdecor-plugin.h" -#include "utils.h" -#include "desktop-settings.h" -#include "os-compatibility.h" - -#include <cairo/cairo.h> -#include <pango/pangocairo.h> - -#include "common/libdecor-cairo-blur.h" - -static const size_t SHADOW_MARGIN = 24; /* graspable part of the border */ -static const size_t TITLE_HEIGHT = 24; -static const size_t BUTTON_WIDTH = 32; -static const size_t SYM_DIM = 14; - -static const uint32_t COL_TITLE = 0xFF080706; -static const uint32_t COL_TITLE_INACT = 0xFF303030; -static const uint32_t COL_BUTTON_MIN = 0xFFFFBB00; -static const uint32_t COL_BUTTON_MAX = 0xFF238823; -static const uint32_t COL_BUTTON_CLOSE = 0xFFFB6542; -static const uint32_t COL_BUTTON_INACT = 0xFF404040; -static const uint32_t COL_SYM = 0xFFF4F4EF; -static const uint32_t COL_SYM_ACT = 0xFF20322A; -static const uint32_t COL_SYM_INACT = 0xFF909090; - -static const uint32_t DOUBLE_CLICK_TIME_MS = 400; - -static const char *cursor_names[] = { - "top_side", - "bottom_side", - "left_side", - "top_left_corner", - "bottom_left_corner", - "right_side", - "top_right_corner", - "bottom_right_corner" -}; - - -/* color conversion function from 32bit integer to double components */ - -double -red(const uint32_t *const col) { - return ((const uint8_t*)(col))[2] / (double)(255); -} - -double -green(const uint32_t *const col) { - return ((const uint8_t*)(col))[1] / (double)(255); -} - -double -blue(const uint32_t *const col) { - return ((const uint8_t*)(col))[0] / (double)(255); -} - -double -alpha(const uint32_t *const col) { - return ((const uint8_t*)(col))[3] / (double)(255); -} - -void -cairo_set_rgba32(cairo_t *cr, const uint32_t *const c) { - cairo_set_source_rgba(cr, red(c), green(c), blue(c), alpha(c)); -} - -static bool -streql(const char *str1, const char *str2) -{ - return (str1 && str2) && (strcmp(str1, str2) == 0); -} - -enum decoration_type { - DECORATION_TYPE_NONE, - DECORATION_TYPE_ALL, - DECORATION_TYPE_MAXIMIZED, - DECORATION_TYPE_TILED -}; - -enum component { - NONE = 0, - SHADOW, - TITLE, - BUTTON_MIN, - BUTTON_MAX, - BUTTON_CLOSE, -}; - -enum composite_mode { - COMPOSITE_SERVER, - COMPOSITE_CLIENT, -}; - -struct seat { - struct libdecor_plugin_cairo *plugin_cairo; - - char *name; - - struct wl_seat *wl_seat; - struct wl_pointer *wl_pointer; - - struct wl_surface *cursor_surface; - struct wl_cursor *current_cursor; - int cursor_scale; - struct wl_list cursor_outputs; - - struct wl_cursor_theme *cursor_theme; - /* cursors for resize edges and corners */ - struct wl_cursor *cursors[ARRAY_LENGTH(cursor_names)]; - struct wl_cursor *cursor_left_ptr; - - struct wl_surface *pointer_focus; - struct libdecor_frame_cairo *pointer_focus_frame; - - int pointer_x, pointer_y; - - uint32_t pointer_button_time_stamp; - - uint32_t serial; - - bool grabbed; - - struct wl_list link; -}; - -struct output { - struct libdecor_plugin_cairo *plugin_cairo; - - struct wl_output *wl_output; - uint32_t id; - int scale; - - struct wl_list link; -}; - -struct buffer { - 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; -}; - -struct border_component { - 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 *parent_component; - } client; - - struct wl_list child_components; /* border_component::link */ - struct wl_list link; /* border_component::child_components */ -}; - -struct surface_output { - struct output *output; - struct wl_list link; -}; - -struct cursor_output { - struct output *output; - struct wl_list link; -}; - -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 *focus; - struct border_component *active; - struct border_component *grab; - - bool shadow_showing; - struct border_component shadow; - - struct { - bool is_showing; - struct border_component title; - struct border_component min; - struct border_component max; - struct border_component close; - } title_bar; - - /* store pre-processed shadow tile */ - cairo_surface_t *shadow_blur; - - struct wl_list link; -}; - -struct libdecor_plugin_cairo { - 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; - - PangoFontDescription *font; - - bool handle_cursor; -}; - -static const char *libdecor_cairo_proxy_tag = "libdecor-cairo"; - -static void -sync_active_component(struct libdecor_frame_cairo *frame_cairo, - struct seat *seat); - -static void -synthesize_pointer_enter(struct seat *seat); - -static void -synthesize_pointer_leave(struct seat *seat); - -static bool -own_proxy(struct wl_proxy *proxy) -{ - if (!proxy) - return false; - - return (wl_proxy_get_tag(proxy) == &libdecor_cairo_proxy_tag); -} - -static bool -own_surface(struct wl_surface *surface) -{ - return own_proxy((struct wl_proxy *) surface); -} - -static bool -own_output(struct wl_output *output) -{ - return own_proxy((struct wl_proxy *) output); -} - -static bool -moveable(struct libdecor_frame_cairo *frame_cairo) { - return libdecor_frame_has_capability(&frame_cairo->frame, - LIBDECOR_ACTION_MOVE); -} - -static bool -resizable(struct libdecor_frame_cairo *frame_cairo) { - return libdecor_frame_has_capability(&frame_cairo->frame, - LIBDECOR_ACTION_RESIZE); -} - -static bool -minimizable(struct libdecor_frame_cairo *frame_cairo) { - return libdecor_frame_has_capability(&frame_cairo->frame, - LIBDECOR_ACTION_MINIMIZE); -} - -static bool -closeable(struct libdecor_frame_cairo *frame_cairo) { - return libdecor_frame_has_capability(&frame_cairo->frame, - LIBDECOR_ACTION_CLOSE); -} - -static void -buffer_free(struct buffer *buffer); - -static void -draw_border_component(struct libdecor_frame_cairo *frame_cairo, - struct border_component *border_component); - -static void -send_cursor(struct seat *seat); - -static bool -update_local_cursor(struct seat *seat); - -static void -libdecor_plugin_cairo_destroy(struct libdecor_plugin *plugin) -{ - struct libdecor_plugin_cairo *plugin_cairo = - (struct libdecor_plugin_cairo *) plugin; - struct seat *seat, *seat_tmp; - struct output *output, *output_tmp; - struct libdecor_frame_cairo *frame, *frame_tmp; - - if (plugin_cairo->globals_callback) - wl_callback_destroy(plugin_cairo->globals_callback); - if (plugin_cairo->globals_callback_shm) - wl_callback_destroy(plugin_cairo->globals_callback_shm); - if (plugin_cairo->shm_callback) - wl_callback_destroy(plugin_cairo->shm_callback); - wl_registry_destroy(plugin_cairo->wl_registry); - - wl_list_for_each_safe(seat, seat_tmp, &plugin_cairo->seat_list, link) { - struct cursor_output *cursor_output, *tmp; - - if (seat->wl_pointer) - wl_pointer_destroy(seat->wl_pointer); - if (seat->cursor_surface) - wl_surface_destroy(seat->cursor_surface); - wl_seat_destroy(seat->wl_seat); - if (seat->cursor_theme) - wl_cursor_theme_destroy(seat->cursor_theme); - - wl_list_for_each_safe(cursor_output, tmp, &seat->cursor_outputs, link) { - wl_list_remove(&cursor_output->link); - free(cursor_output); - } - free(seat->name); - - free(seat); - } - - wl_list_for_each_safe(output, output_tmp, - &plugin_cairo->output_list, link) { - if (wl_output_get_version (output->wl_output) >= - WL_OUTPUT_RELEASE_SINCE_VERSION) - wl_output_release(output->wl_output); - else - wl_output_destroy(output->wl_output); - free(output); - } - - wl_list_for_each_safe(frame, frame_tmp, - &plugin_cairo->visible_frame_list, link) { - wl_list_remove(&frame->link); - } - - free(plugin_cairo->cursor_theme_name); - - if (plugin_cairo->wl_shm) - wl_shm_destroy(plugin_cairo->wl_shm); - - pango_font_description_free(plugin_cairo->font); - - if (plugin_cairo->wl_compositor) - wl_compositor_destroy(plugin_cairo->wl_compositor); - if (plugin_cairo->wl_subcompositor) - wl_subcompositor_destroy(plugin_cairo->wl_subcompositor); - - libdecor_plugin_release(&plugin_cairo->plugin); - free(plugin_cairo); -} - -static void -init_server_component(struct border_component *border_component, - enum component type) -{ - border_component->composite_mode = COMPOSITE_SERVER; - wl_list_init(&border_component->child_components); - border_component->type = type; -} - -static void -init_client_component(struct border_component *border_component, - struct border_component *parent, - enum component type) -{ - border_component->composite_mode = COMPOSITE_CLIENT; - wl_list_init(&border_component->child_components); - wl_list_insert(parent->child_components.prev, &border_component->link); - border_component->client.parent_component = parent; - border_component->type = type; -} - -static void -init_components(struct libdecor_frame_cairo *frame_cairo) -{ - init_server_component(&frame_cairo->title_bar.title, - TITLE); - init_client_component(&frame_cairo->title_bar.min, - &frame_cairo->title_bar.title, - BUTTON_MIN); - init_client_component(&frame_cairo->title_bar.max, - &frame_cairo->title_bar.title, - BUTTON_MAX); - init_client_component(&frame_cairo->title_bar.close, - &frame_cairo->title_bar.title, - BUTTON_CLOSE); - init_server_component(&frame_cairo->shadow, - SHADOW); -} - -static struct libdecor_frame_cairo * -libdecor_frame_cairo_new(struct libdecor_plugin_cairo *plugin_cairo) -{ - struct libdecor_frame_cairo *frame_cairo = zalloc(sizeof *frame_cairo); - cairo_t *cr; - - static const int size = 128; - static const int boundary = 32; - - frame_cairo->plugin_cairo = plugin_cairo; - frame_cairo->shadow_blur = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, size, size); - wl_list_insert(&plugin_cairo->visible_frame_list, &frame_cairo->link); - - init_components(frame_cairo); - - cr = cairo_create(frame_cairo->shadow_blur); - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - cairo_set_source_rgba(cr, 0, 0, 0, 1); - cairo_rectangle(cr, boundary, boundary, size-2*boundary, size-2*boundary); - cairo_fill(cr); - cairo_destroy(cr); - blur_surface(frame_cairo->shadow_blur, 64); - - return frame_cairo; -} - -static int -libdecor_plugin_cairo_get_fd(struct libdecor_plugin *plugin) -{ - struct libdecor_plugin_cairo *plugin_cairo = - (struct libdecor_plugin_cairo *) plugin; - struct wl_display *wl_display = - libdecor_get_wl_display(plugin_cairo->context); - - return wl_display_get_fd(wl_display); -} - -static int -libdecor_plugin_cairo_dispatch(struct libdecor_plugin *plugin, - int timeout) -{ - struct libdecor_plugin_cairo *plugin_cairo = - (struct libdecor_plugin_cairo *) plugin; - struct wl_display *wl_display = - libdecor_get_wl_display(plugin_cairo->context); - struct pollfd fds[1]; - int ret; - int dispatch_count = 0; - - while (wl_display_prepare_read(wl_display) != 0) - dispatch_count += wl_display_dispatch_pending(wl_display); - - if (wl_display_flush(wl_display) < 0 && - errno != EAGAIN) { - wl_display_cancel_read(wl_display); - return -errno; - } - - fds[0] = (struct pollfd) { wl_display_get_fd(wl_display), POLLIN }; - - ret = poll(fds, ARRAY_SIZE (fds), timeout); - if (ret > 0) { - if (fds[0].revents & POLLIN) { - wl_display_read_events(wl_display); - dispatch_count += wl_display_dispatch_pending(wl_display); - return dispatch_count; - } else { - wl_display_cancel_read(wl_display); - return dispatch_count; - } - } else if (ret == 0) { - wl_display_cancel_read(wl_display); - return dispatch_count; - } else { - wl_display_cancel_read(wl_display); - return -errno; - } -} - -static void -libdecor_plugin_cairo_set_handle_application_cursor(struct libdecor_plugin *plugin, - bool handle_cursor) -{ - struct libdecor_plugin_cairo *plugin_cairo = - (struct libdecor_plugin_cairo *) plugin; - - plugin_cairo->handle_cursor = handle_cursor; -} - -static struct libdecor_frame * -libdecor_plugin_cairo_frame_new(struct libdecor_plugin *plugin) -{ - struct libdecor_plugin_cairo *plugin_cairo = - (struct libdecor_plugin_cairo *) plugin; - struct libdecor_frame_cairo *frame_cairo; - - frame_cairo = libdecor_frame_cairo_new(plugin_cairo); - - return &frame_cairo->frame; -} - -static void -toggle_maximized(struct libdecor_frame *const frame) -{ - if (!resizable((struct libdecor_frame_cairo *)frame)) - return; - - if (!(libdecor_frame_get_window_state(frame) & - LIBDECOR_WINDOW_STATE_MAXIMIZED)) - libdecor_frame_set_maximized(frame); - else - libdecor_frame_unset_maximized(frame); -} - -static void -buffer_release(void *user_data, - struct wl_buffer *wl_buffer) -{ - struct buffer *buffer = user_data; - - if (buffer->is_detached) - buffer_free(buffer); - else - buffer->in_use = false; -} - -static const struct wl_buffer_listener buffer_listener = { - buffer_release -}; - -static struct buffer * -create_shm_buffer(struct libdecor_plugin_cairo *plugin_cairo, - int width, - int height, - bool opaque, - int scale) -{ - struct wl_shm_pool *pool; - int fd, size, buffer_width, buffer_height, stride; - void *data; - struct buffer *buffer; - enum wl_shm_format buf_fmt; - - buffer_width = width * scale; - buffer_height = height * scale; - stride = buffer_width * 4; - size = stride * buffer_height; - - fd = libdecor_os_create_anonymous_file(size); - if (fd < 0) { - fprintf(stderr, "creating a buffer file for %d B failed: %s\n", - size, strerror(errno)); - return NULL; - } - - data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (data == MAP_FAILED) { - fprintf(stderr, "mmap failed: %s\n", strerror(errno)); - close(fd); - return NULL; - } - - buf_fmt = opaque ? WL_SHM_FORMAT_XRGB8888 : WL_SHM_FORMAT_ARGB8888; - - pool = wl_shm_create_pool(plugin_cairo->wl_shm, fd, size); - buffer = zalloc(sizeof *buffer); - buffer->wl_buffer = wl_shm_pool_create_buffer(pool, 0, - buffer_width, buffer_height, - stride, - buf_fmt); - wl_buffer_add_listener(buffer->wl_buffer, &buffer_listener, buffer); - wl_shm_pool_destroy(pool); - close(fd); - - buffer->data = data; - buffer->data_size = size; - buffer->width = width; - buffer->height = height; - buffer->scale = scale; - buffer->buffer_width = buffer_width; - buffer->buffer_height = buffer_height; - - return buffer; -} - -static void -buffer_free(struct buffer *buffer) -{ - if (buffer->wl_buffer) { - wl_buffer_destroy(buffer->wl_buffer); - munmap(buffer->data, buffer->data_size); - buffer->wl_buffer = NULL; - buffer->in_use = false; - } - free(buffer); -} - -static void -free_border_component(struct border_component *border_component) -{ - struct surface_output *surface_output, *surface_output_tmp; - - if (border_component->server.wl_surface) { - wl_subsurface_destroy(border_component->server.wl_subsurface); - border_component->server.wl_subsurface = NULL; - wl_surface_destroy(border_component->server.wl_surface); - border_component->server.wl_surface = NULL; - } - if (border_component->server.buffer) { - buffer_free(border_component->server.buffer); - border_component->server.buffer = NULL; - } - if (border_component->client.image) { - cairo_surface_destroy(border_component->client.image); - border_component->client.image = NULL; - } - if (border_component->server.output_list.next != NULL) { - wl_list_for_each_safe(surface_output, surface_output_tmp, - &border_component->server.output_list, link) { - wl_list_remove(&surface_output->link); - free(surface_output); - } - } -} - -static void -libdecor_plugin_cairo_frame_free(struct libdecor_plugin *plugin, - struct libdecor_frame *frame) -{ - struct libdecor_plugin_cairo *plugin_cairo = - (struct libdecor_plugin_cairo *) plugin; - struct libdecor_frame_cairo *frame_cairo = - (struct libdecor_frame_cairo *) frame; - struct seat *seat; - - wl_list_for_each(seat, &plugin_cairo->seat_list, link) { - if (seat->pointer_focus) { - if (wl_surface_get_user_data(seat->pointer_focus) == frame_cairo) - seat->pointer_focus = NULL; - if (seat->pointer_focus_frame == frame_cairo) - seat->pointer_focus_frame = NULL; - } - } - - free_border_component(&frame_cairo->title_bar.title); - free_border_component(&frame_cairo->title_bar.min); - free_border_component(&frame_cairo->title_bar.max); - free_border_component(&frame_cairo->title_bar.close); - frame_cairo->title_bar.is_showing = false; - free_border_component(&frame_cairo->shadow); - frame_cairo->shadow_showing = false; - if (frame_cairo->shadow_blur != NULL) { - cairo_surface_destroy(frame_cairo->shadow_blur); - frame_cairo->shadow_blur = NULL; - } - - free(frame_cairo->title); - frame_cairo->title = NULL; - - frame_cairo->decoration_type = DECORATION_TYPE_NONE; - - if (frame_cairo->link.next != NULL) - wl_list_remove(&frame_cairo->link); -} - -static bool -is_border_surfaces_showing(struct libdecor_frame_cairo *frame_cairo) -{ - return frame_cairo->shadow_showing; -} - -static bool -is_title_bar_surfaces_showing(struct libdecor_frame_cairo *frame_cairo) -{ - return frame_cairo->title_bar.is_showing; -} - -static struct border_component * -get_server_component(struct border_component *border_component) -{ - switch (border_component->composite_mode) { - case COMPOSITE_SERVER: - return border_component; - case COMPOSITE_CLIENT: - return get_server_component(border_component->client.parent_component); - } - return NULL; -} - -static void -redraw_border_component(struct libdecor_frame_cairo *frame_cairo, - struct border_component *border_component) -{ - struct border_component *server_component; - - server_component = get_server_component(border_component); - draw_border_component(frame_cairo, server_component); -} - -static void -hide_border_component(struct libdecor_frame_cairo *frame_cairo, - struct border_component *border_component) -{ - border_component->is_hidden = true; - - switch (border_component->composite_mode) { - case COMPOSITE_SERVER: - if (!border_component->server.wl_surface) - return; - - wl_surface_attach(border_component->server.wl_surface, - NULL, 0, 0); - wl_surface_commit(border_component->server.wl_surface); - break; - case COMPOSITE_CLIENT: - redraw_border_component(frame_cairo, border_component); - break; - } -} - -static void -hide_border_surfaces(struct libdecor_frame_cairo *frame_cairo) -{ - hide_border_component(frame_cairo, &frame_cairo->shadow); - frame_cairo->shadow_showing = false; -} - -static void -hide_title_bar_surfaces(struct libdecor_frame_cairo *frame_cairo) -{ - hide_border_component(frame_cairo, &frame_cairo->title_bar.title); - hide_border_component(frame_cairo, &frame_cairo->title_bar.min); - hide_border_component(frame_cairo, &frame_cairo->title_bar.max); - hide_border_component(frame_cairo, &frame_cairo->title_bar.close); - frame_cairo->title_bar.is_showing = false; -} - -static struct border_component * -get_component_for_surface(struct libdecor_frame_cairo *frame_cairo, - struct wl_surface *surface) -{ - if (frame_cairo->shadow.server.wl_surface == surface) - return &frame_cairo->shadow; - if (frame_cairo->title_bar.title.server.wl_surface == surface) - return &frame_cairo->title_bar.title; - return NULL; -} - -static void -calculate_component_size(struct libdecor_frame_cairo *frame_cairo, - enum component component, - int *component_x, - int *component_y, - int *component_width, - int *component_height); - -static void -update_component_focus(struct libdecor_frame_cairo *frame_cairo, - struct wl_surface *surface, - struct seat *seat) -{ - static struct border_component *border_component; - static struct border_component *child_component; - static struct border_component *focus_component; - - border_component = get_component_for_surface(frame_cairo, surface); - if (!border_component) { - focus_component = NULL; - goto out; - } - - focus_component = border_component; - wl_list_for_each(child_component, &border_component->child_components, link) { - int component_x = 0, component_y = 0; - int component_width = 0, component_height = 0; - - calculate_component_size(frame_cairo, child_component->type, - &component_x, &component_y, - &component_width, &component_height); - if (seat->pointer_x >= component_x && - seat->pointer_x < component_x + component_width && - seat->pointer_y >= component_y && - seat->pointer_y < component_y + component_height) { - focus_component = child_component; - break; - } - } - -out: - if (frame_cairo->grab) - frame_cairo->active = frame_cairo->grab; - else - frame_cairo->active = focus_component; - frame_cairo->focus = focus_component; - -} - -static void -ensure_component(struct libdecor_frame_cairo *frame_cairo, - struct border_component *cmpnt); - -static bool -redraw_scale(struct libdecor_frame_cairo *frame_cairo, - struct border_component *cmpnt) -{ - struct surface_output *surface_output; - int scale = 1; - - if (cmpnt->is_hidden) - return false; - - ensure_component(frame_cairo, cmpnt); - - wl_list_for_each(surface_output, &cmpnt->server.output_list, link) { - scale = MAX(scale, surface_output->output->scale); - } - if (scale != cmpnt->server.scale) { - cmpnt->server.scale = scale; - if ((cmpnt->type != SHADOW) || is_border_surfaces_showing(frame_cairo)) { - draw_border_component(frame_cairo, cmpnt); - return true; - } - } - return false; -} - -static bool -add_surface_output(struct libdecor_plugin_cairo *plugin_cairo, - struct wl_output *wl_output, - struct wl_list *list) -{ - struct output *output; - struct surface_output *surface_output; - - if (!own_output(wl_output)) - return false; - - output = wl_output_get_user_data(wl_output); - - if (output == NULL) - return false; - - surface_output = zalloc(sizeof *surface_output); - surface_output->output = output; - wl_list_insert(list, &surface_output->link); - return true; -} - -static void -surface_enter(void *data, - struct wl_surface *wl_surface, - struct wl_output *wl_output) -{ - struct libdecor_frame_cairo *frame_cairo = data; - struct border_component *cmpnt; - - if (!(own_surface(wl_surface) && own_output(wl_output))) - return; - - cmpnt = get_component_for_surface(frame_cairo, wl_surface); - if (cmpnt == NULL) - return; - - if (!add_surface_output(frame_cairo->plugin_cairo, wl_output, - &cmpnt->server.output_list)) - return; - - if (redraw_scale(frame_cairo, cmpnt)) - libdecor_frame_toplevel_commit(&frame_cairo->frame); -} - -static bool -remove_surface_output(struct wl_list *list, struct wl_output *wl_output) -{ - struct surface_output *surface_output; - wl_list_for_each(surface_output, list, link) { - if (surface_output->output->wl_output == wl_output) { - wl_list_remove(&surface_output->link); - free(surface_output); - return true; - } - } - return false; -} - -static void -surface_leave(void *data, - struct wl_surface *wl_surface, - struct wl_output *wl_output) -{ - struct libdecor_frame_cairo *frame_cairo = data; - struct border_component *cmpnt; - - if (!(own_surface(wl_surface) && own_output(wl_output))) - return; - - cmpnt = get_component_for_surface(frame_cairo, wl_surface); - if (cmpnt == NULL) - return; - - if (!remove_surface_output(&cmpnt->server.output_list, wl_output)) - return; - - if (redraw_scale(frame_cairo, cmpnt)) - libdecor_frame_toplevel_commit(&frame_cairo->frame); -} - -static struct wl_surface_listener surface_listener = { - surface_enter, - surface_leave, -}; - -static void -create_surface_subsurface_pair(struct libdecor_frame_cairo *frame_cairo, - struct wl_surface **out_wl_surface, - struct wl_subsurface **out_wl_subsurface) -{ - struct libdecor_plugin_cairo *plugin_cairo = frame_cairo->plugin_cairo; - struct libdecor_frame *frame = &frame_cairo->frame; - struct wl_compositor *wl_compositor = plugin_cairo->wl_compositor; - struct wl_subcompositor *wl_subcompositor = plugin_cairo->wl_subcompositor; - struct wl_surface *wl_surface; - struct wl_surface *parent; - struct wl_subsurface *wl_subsurface; - - wl_surface = wl_compositor_create_surface(wl_compositor); - wl_proxy_set_tag((struct wl_proxy *) wl_surface, - &libdecor_cairo_proxy_tag); - - parent = libdecor_frame_get_wl_surface(frame); - wl_subsurface = wl_subcompositor_get_subsurface(wl_subcompositor, - wl_surface, - parent); - - *out_wl_surface = wl_surface; - *out_wl_subsurface = wl_subsurface; -} - -static void -ensure_component(struct libdecor_frame_cairo *frame_cairo, - struct border_component *cmpnt) -{ - switch (cmpnt->composite_mode) { - case COMPOSITE_SERVER: - if (!cmpnt->server.wl_surface) { - wl_list_init(&cmpnt->server.output_list); - cmpnt->server.scale = 1; - create_surface_subsurface_pair(frame_cairo, - &cmpnt->server.wl_surface, - &cmpnt->server.wl_subsurface); - wl_surface_add_listener(cmpnt->server.wl_surface, - &surface_listener, - frame_cairo); - } - break; - case COMPOSITE_CLIENT: - wl_list_init(&cmpnt->server.output_list); - break; - } - - cmpnt->is_hidden = false; -} - -static void -ensure_border_surfaces(struct libdecor_frame_cairo *frame_cairo) -{ - int min_width, min_height, current_max_w, current_max_h; - - frame_cairo->shadow.opaque = false; - ensure_component(frame_cairo, &frame_cairo->shadow); - - libdecor_frame_get_min_content_size(&frame_cairo->frame, - &min_width, &min_height); - min_width = MAX(min_width, (int)MAX(56, 4 * BUTTON_WIDTH)); - min_height = MAX(min_height, (int)MAX(56, TITLE_HEIGHT + 1)); - libdecor_frame_set_min_content_size(&frame_cairo->frame, min_width, min_height); - libdecor_frame_get_max_content_size(&frame_cairo->frame, ¤t_max_w, - ¤t_max_h); - if (current_max_w && current_max_w < min_width) current_max_w = min_width; - if (current_max_h && current_max_h < min_height) current_max_h = min_height; - libdecor_frame_set_max_content_size(&frame_cairo->frame, current_max_w, - current_max_h); -} - - -static void -ensure_title_bar_surfaces(struct libdecor_frame_cairo *frame_cairo) -{ - frame_cairo->title_bar.title.opaque = true; - ensure_component(frame_cairo, &frame_cairo->title_bar.title); - - frame_cairo->title_bar.min.opaque = true; - ensure_component(frame_cairo, &frame_cairo->title_bar.min); - - frame_cairo->title_bar.max.opaque = true; - ensure_component(frame_cairo, &frame_cairo->title_bar.max); - - frame_cairo->title_bar.close.opaque = true; - ensure_component(frame_cairo, &frame_cairo->title_bar.close); -} - -static void -calculate_component_size(struct libdecor_frame_cairo *frame_cairo, - enum component component, - int *component_x, - int *component_y, - int *component_width, - int *component_height) -{ - struct libdecor_frame *frame = &frame_cairo->frame; - int content_width, content_height; - - content_width = libdecor_frame_get_content_width(frame); - content_height = libdecor_frame_get_content_height(frame); - - switch (component) { - case NONE: - *component_width = 0; - *component_height = 0; - return; - case SHADOW: - *component_x = -(int)SHADOW_MARGIN; - *component_y = -(int)(SHADOW_MARGIN+TITLE_HEIGHT); - *component_width = content_width + 2 * SHADOW_MARGIN; - *component_height = content_height - + 2 * SHADOW_MARGIN - + TITLE_HEIGHT; - return; - case TITLE: - *component_x = 0; - *component_y = -(int)TITLE_HEIGHT; - *component_width = content_width; - *component_height = TITLE_HEIGHT; - return; - case BUTTON_MIN: - *component_x = content_width - 3 * BUTTON_WIDTH; - *component_y = 0; - *component_width = BUTTON_WIDTH; - *component_height = TITLE_HEIGHT; - return; - case BUTTON_MAX: - *component_x = content_width - 2 * BUTTON_WIDTH; - *component_y = 0; - *component_width = BUTTON_WIDTH; - *component_height = TITLE_HEIGHT; - return; - case BUTTON_CLOSE: - *component_x = content_width - BUTTON_WIDTH; - *component_y = 0; - *component_width = BUTTON_WIDTH; - *component_height = TITLE_HEIGHT; - return; - } - - abort(); -} - -static int -border_component_get_scale(struct border_component *border_component) -{ - switch (border_component->composite_mode) { - case COMPOSITE_SERVER: - return border_component->server.scale; - case COMPOSITE_CLIENT: - return border_component_get_scale( - border_component->client.parent_component); - } - return 0; -} - -static void -draw_title_text(struct libdecor_frame_cairo *frame_cairo, - cairo_t *cr, - const int *title_width, - bool active) -{ - const uint32_t col_title = active ? COL_TITLE : COL_TITLE_INACT; - const uint32_t col_title_text = active ? COL_SYM : COL_SYM_INACT; - - PangoLayout *layout; - - /* title fade out at buttons */ - const int fade_width = 5 * BUTTON_WIDTH; - int fade_start; - cairo_pattern_t *fade; - - /* text position and dimensions */ - int text_extents_width, text_extents_height; - double text_x, text_y; - double text_width, text_height; - - const char *title; - - title = libdecor_frame_get_title((struct libdecor_frame*) frame_cairo); - if (!title) - return; - - layout = pango_cairo_create_layout(cr); - - pango_layout_set_text(layout, - title, - -1); - pango_layout_set_font_description(layout, frame_cairo->plugin_cairo->font); - pango_layout_get_size(layout, &text_extents_width, &text_extents_height); - - /* set text position and dimensions */ - text_width = text_extents_width / PANGO_SCALE; - text_height = text_extents_height / PANGO_SCALE; - text_x = *title_width / 2.0 - text_width / 2.0; - text_x += MIN(0.0, ((*title_width - fade_width) - (text_x + text_width))); - text_x = MAX(text_x, BUTTON_WIDTH); - text_y = TITLE_HEIGHT / 2.0 - text_height / 2.0; - - /* draw title text */ - cairo_move_to(cr, text_x, text_y); - cairo_set_rgba32(cr, &col_title_text); - pango_cairo_show_layout(cr, layout); - - /* draw fade-out from title text to buttons */ - fade_start = *title_width - fade_width; - fade = cairo_pattern_create_linear(fade_start, 0, - fade_start + 2 * BUTTON_WIDTH, 0); - cairo_pattern_add_color_stop_rgba(fade, 0, - red(&col_title), - green(&col_title), - blue(&col_title), - 0); - cairo_pattern_add_color_stop_rgb(fade, 1, - red(&col_title), - green(&col_title), - blue(&col_title)); - cairo_rectangle(cr, fade_start, 0, fade_width, TITLE_HEIGHT); - cairo_set_source(cr, fade); - cairo_fill(cr); - - cairo_pattern_destroy(fade); - g_object_unref(layout); -} - -static void -draw_component_content(struct libdecor_frame_cairo *frame_cairo, - struct border_component *border_component, - int component_width, - int component_height, - enum component component) -{ - struct buffer *buffer; - cairo_surface_t *surface = NULL; - int width = 0, height = 0; - int scale; - cairo_t *cr; - - /* button symbol origin */ - const double x = BUTTON_WIDTH / 2 - SYM_DIM / 2 + 0.5; - const double y = TITLE_HEIGHT / 2 - SYM_DIM / 2 + 0.5; - - enum libdecor_window_state state; - - bool active; - - uint32_t col_title; - - bool cap_min, cap_max, cap_close; - - /* capabilities of decorations */ - cap_min = minimizable(frame_cairo); - cap_max = resizable(frame_cairo); - cap_close = closeable(frame_cairo); - - scale = border_component_get_scale(border_component); - - state = libdecor_frame_get_window_state((struct libdecor_frame *) frame_cairo); - - active = state & LIBDECOR_WINDOW_STATE_ACTIVE; - - col_title = active ? COL_TITLE : COL_TITLE_INACT; - - /* clear buffer */ - switch (border_component->composite_mode) { - case COMPOSITE_SERVER: - buffer = border_component->server.buffer; - - surface = cairo_image_surface_create_for_data( - buffer->data, CAIRO_FORMAT_ARGB32, - buffer->buffer_width, buffer->buffer_height, - cairo_format_stride_for_width( - CAIRO_FORMAT_ARGB32, - buffer->buffer_width) - ); - cairo_surface_set_device_scale(surface, scale, scale); - width = buffer->width; - height = buffer->height; - break; - case COMPOSITE_CLIENT: - surface = cairo_surface_reference(border_component->client.image); - width = cairo_image_surface_get_width(surface); - height = cairo_image_surface_get_height(surface); - break; - } - - cr = cairo_create(surface); - cairo_save(cr); - cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_paint(cr); - cairo_restore(cr); - - /* background */ - switch (component) { - case NONE: - break; - case SHADOW: - if (frame_cairo->decoration_type != DECORATION_TYPE_TILED) - render_shadow(cr, - frame_cairo->shadow_blur, - -(int)SHADOW_MARGIN/2, - -(int)SHADOW_MARGIN/2, - width + SHADOW_MARGIN, - height + SHADOW_MARGIN, - 64, - 64); - break; - case TITLE: - cairo_set_rgba32(cr, &col_title); - cairo_paint(cr); - break; - case BUTTON_MIN: - if (cap_min && frame_cairo->active == &frame_cairo->title_bar.min) - cairo_set_rgba32(cr, active ? &COL_BUTTON_MIN : &COL_BUTTON_INACT); - else - cairo_set_rgba32(cr, &col_title); - cairo_paint(cr); - break; - case BUTTON_MAX: - if (cap_max && frame_cairo->active == &frame_cairo->title_bar.max) - cairo_set_rgba32(cr, active ? &COL_BUTTON_MAX : &COL_BUTTON_INACT); - else - cairo_set_rgba32(cr, &col_title); - cairo_paint(cr); - break; - case BUTTON_CLOSE: - if (cap_close && frame_cairo->active == &frame_cairo->title_bar.close) - cairo_set_rgba32(cr, active ? &COL_BUTTON_CLOSE : &COL_BUTTON_INACT); - else - cairo_set_rgba32(cr, &col_title); - cairo_paint(cr); - break; - } - - /* button symbols */ - /* https://www.cairographics.org/FAQ/#sharp_lines */ - cairo_set_line_width(cr, 1); - - switch (component) { - case TITLE: - draw_title_text(frame_cairo,cr, &component_width, active); - break; - case BUTTON_MIN: - if (!active) { - /* inactive: use single desaturated color */ - cairo_set_rgba32(cr, &COL_SYM_INACT); - } else { - if (!cap_min || - frame_cairo->active == &frame_cairo->title_bar.min) { - /* active (a.k.a. prelight) */ - cairo_set_rgba32(cr, &COL_SYM_ACT); - } else { - /* normal */ - cairo_set_rgba32(cr, &COL_SYM); - } - } - cairo_move_to(cr, x, y + SYM_DIM - 1); - cairo_rel_line_to(cr, SYM_DIM - 1, 0); - cairo_stroke(cr); - break; - case BUTTON_MAX: - if (!active) { - /* inactive: use single desaturated color */ - cairo_set_rgba32(cr, &COL_SYM_INACT); - } else { - if (!cap_max || - frame_cairo->active == &frame_cairo->title_bar.max) { - /* active (a.k.a. prelight) */ - cairo_set_rgba32(cr, &COL_SYM_ACT); - } else { - /* normal */ - cairo_set_rgba32(cr, &COL_SYM); - } - } - - if (state & LIBDECOR_WINDOW_STATE_MAXIMIZED) { - const size_t small = 12; - cairo_rectangle(cr, - x, - y + SYM_DIM - small, - small - 1, - small - 1); - cairo_move_to(cr, - x + SYM_DIM - small, - y + SYM_DIM - small); - cairo_line_to(cr, x + SYM_DIM - small, y); - cairo_rel_line_to(cr, small - 1, 0); - cairo_rel_line_to(cr, 0, small - 1); - cairo_line_to(cr, x + small - 1, y + small - 1); - } else { - cairo_rectangle(cr, x, y, SYM_DIM - 1, SYM_DIM - 1); - } - cairo_stroke(cr); - break; - case BUTTON_CLOSE: - if (!active) { - /* inactive: use single desaturated color */ - cairo_set_rgba32(cr, &COL_SYM_INACT); - } else { - if (!cap_close || - frame_cairo->active == &frame_cairo->title_bar.close) { - /* active (a.k.a. prelight) */ - cairo_set_rgba32(cr, &COL_SYM_ACT); - } else { - /* normal */ - cairo_set_rgba32(cr, &COL_SYM); - } - } - cairo_move_to(cr, x, y); - cairo_rel_line_to(cr, SYM_DIM - 1, SYM_DIM - 1); - cairo_move_to(cr, x + SYM_DIM - 1, y); - cairo_line_to(cr, x, y + SYM_DIM - 1); - cairo_stroke(cr); - break; - default: - break; - } - - /* mask the toplevel surface */ - if (component == SHADOW) { - int component_x, component_y, component_width, component_height; - calculate_component_size(frame_cairo, component, - &component_x, &component_y, - &component_width, &component_height); - cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); - cairo_rectangle(cr, -component_x, -component_y, - libdecor_frame_get_content_width( - &frame_cairo->frame), - libdecor_frame_get_content_height( - &frame_cairo->frame)); - cairo_fill(cr); - } - - cairo_destroy(cr); - cairo_surface_destroy(surface); -} - -static void -set_component_input_region(struct libdecor_frame_cairo *frame_cairo, - struct border_component *border_component) -{ - if (border_component->type == SHADOW && frame_cairo->shadow_showing) { - struct wl_region *input_region; - int component_x; - int component_y; - int component_width; - int component_height; - - calculate_component_size(frame_cairo, border_component->type, - &component_x, &component_y, - &component_width, &component_height); - - /* - * the input region is the outer surface size minus the inner - * content size - */ - input_region = wl_compositor_create_region( - frame_cairo->plugin_cairo->wl_compositor); - wl_region_add(input_region, 0, 0, - component_width, component_height); - wl_region_subtract(input_region, -component_x, -component_y, - libdecor_frame_get_content_width(&frame_cairo->frame), - libdecor_frame_get_content_height(&frame_cairo->frame)); - wl_surface_set_input_region(border_component->server.wl_surface, - input_region); - wl_region_destroy(input_region); - } -} - -static void -ensure_component_realized_server(struct libdecor_frame_cairo *frame_cairo, - struct border_component *border_component, - int component_width, - int component_height, - int scale) -{ - struct buffer *old_buffer; - struct buffer *buffer = NULL; - - old_buffer = border_component->server.buffer; - if (old_buffer) { - if (!old_buffer->in_use && - old_buffer->buffer_width == component_width * scale && - old_buffer->buffer_height == component_height * scale) { - buffer = old_buffer; - } else { - buffer_free(old_buffer); - border_component->server.buffer = NULL; - } - } - - if (!buffer) - buffer = create_shm_buffer(frame_cairo->plugin_cairo, - component_width, - component_height, - border_component->opaque, - border_component->server.scale); - - border_component->server.buffer = buffer; -} - -static void -ensure_component_realized_client(struct libdecor_frame_cairo *frame_cairo, - struct border_component *border_component, - int component_width, - int component_height, - int scale) -{ - cairo_surface_t *old_image; - - old_image = border_component->client.image; - if (old_image) { - int cairo_buffer_width; - int cairo_buffer_height; - double x_scale; - double y_scale; - - cairo_surface_get_device_scale(old_image, &x_scale, &y_scale); - cairo_buffer_width = - (int) round(cairo_image_surface_get_width(old_image) * - x_scale); - cairo_buffer_height = - (int) round(cairo_image_surface_get_height(old_image) * - y_scale); - - if (cairo_buffer_width != component_width * scale || - cairo_buffer_height != component_height * scale) { - cairo_surface_destroy(old_image); - border_component->client.image = NULL; - } - } - - if (!border_component->client.image) { - cairo_surface_t *new_image; - - new_image = - cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - component_width * scale, - component_height * scale); - cairo_surface_set_device_scale(new_image, scale, scale); - border_component->client.image = new_image; - } - -} - -static void -ensure_component_realized(struct libdecor_frame_cairo *frame_cairo, - struct border_component *border_component, - int component_width, - int component_height, - int scale) -{ - switch (border_component->composite_mode) { - case COMPOSITE_SERVER: - ensure_component_realized_server(frame_cairo, border_component, - component_width, - component_height, - scale); - break; - case COMPOSITE_CLIENT: - ensure_component_realized_client(frame_cairo, border_component, - component_width, - component_height, - scale); - break; - } -} - -static cairo_t * -create_cairo_for_parent(struct border_component *border_component) -{ - struct border_component *parent = - border_component->client.parent_component; - struct buffer *buffer; - struct border_component *server_component; - cairo_surface_t *parent_surface; - cairo_t *cr; - - switch (parent->composite_mode) { - case COMPOSITE_SERVER: - buffer = parent->server.buffer; - parent_surface = cairo_image_surface_create_for_data( - buffer->data, CAIRO_FORMAT_ARGB32, - buffer->buffer_width, buffer->buffer_height, - cairo_format_stride_for_width( - CAIRO_FORMAT_ARGB32, - buffer->buffer_width) - ); - cr = cairo_create(parent_surface); - cairo_surface_destroy(parent_surface); - cairo_scale(cr, buffer->scale, buffer->scale); - return cr; - case COMPOSITE_CLIENT: - cr = cairo_create(parent->client.image); - server_component = get_server_component(border_component); - cairo_scale(cr, - server_component->server.scale, - server_component->server.scale); - return cr; - } - return NULL; -} - -static void -draw_border_component(struct libdecor_frame_cairo *frame_cairo, - struct border_component *border_component) -{ - enum component component = border_component->type; - struct buffer *buffer; - cairo_t *cr; - int component_x; - int component_y; - int component_width; - int component_height; - int scale; - struct border_component *child_component; - - if (border_component->is_hidden) - return; - - calculate_component_size(frame_cairo, component, - &component_x, &component_y, - &component_width, &component_height); - - set_component_input_region(frame_cairo, border_component); - - scale = border_component_get_scale(border_component); - ensure_component_realized(frame_cairo, border_component, - component_width, - component_height, - scale); - - draw_component_content(frame_cairo, - border_component, - component_width, component_height, - component); - - switch(border_component->composite_mode) { - case COMPOSITE_SERVER: - buffer = border_component->server.buffer; - wl_surface_attach(border_component->server.wl_surface, - buffer->wl_buffer, - 0, 0); - wl_surface_set_buffer_scale(border_component->server.wl_surface, - buffer->scale); - buffer->in_use = true; - wl_surface_commit(border_component->server.wl_surface); - wl_surface_damage_buffer(border_component->server.wl_surface, 0, 0, - component_width * scale, - component_height * scale); - wl_subsurface_set_position(border_component->server.wl_subsurface, - component_x, component_y); - break; - case COMPOSITE_CLIENT: - cr = create_cairo_for_parent(border_component); - cairo_set_source_surface(cr, - border_component->client.image, - component_x, component_y); - cairo_paint(cr); - cairo_destroy(cr); - break; - } - - wl_list_for_each(child_component, &border_component->child_components, link) - draw_border_component(frame_cairo, child_component); -} - -static void -draw_border(struct libdecor_frame_cairo *frame_cairo) -{ - draw_border_component(frame_cairo, &frame_cairo->shadow); - frame_cairo->shadow_showing = true; -} - -static void -draw_title_bar(struct libdecor_frame_cairo *frame_cairo) -{ - draw_border_component(frame_cairo, &frame_cairo->title_bar.title); - frame_cairo->title_bar.is_showing = true; -} - -static void -draw_decoration(struct libdecor_frame_cairo *frame_cairo) -{ - switch (frame_cairo->decoration_type) { - case DECORATION_TYPE_NONE: - if (frame_cairo->link.next != NULL) - wl_list_remove(&frame_cairo->link); - if (is_border_surfaces_showing(frame_cairo)) - hide_border_surfaces(frame_cairo); - if (is_title_bar_surfaces_showing(frame_cairo)) - hide_title_bar_surfaces(frame_cairo); - break; - case DECORATION_TYPE_TILED: - case DECORATION_TYPE_ALL: - /* show borders */ - ensure_border_surfaces(frame_cairo); - draw_border(frame_cairo); - /* show title bar */ - ensure_title_bar_surfaces(frame_cairo); - draw_title_bar(frame_cairo); - /* link frame */ - if (frame_cairo->link.next == NULL) - wl_list_insert( - &frame_cairo->plugin_cairo->visible_frame_list, - &frame_cairo->link); - break; - case DECORATION_TYPE_MAXIMIZED: - /* hide borders */ - if (is_border_surfaces_showing(frame_cairo)) - hide_border_surfaces(frame_cairo); - /* show title bar */ - ensure_title_bar_surfaces(frame_cairo); - draw_title_bar(frame_cairo); - /* link frame */ - if (frame_cairo->link.next == NULL) - wl_list_insert( - &frame_cairo->plugin_cairo->visible_frame_list, - &frame_cairo->link); - break; - } -} - -static enum decoration_type -window_state_to_decoration_type(enum libdecor_window_state window_state) -{ - if (window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN) - return DECORATION_TYPE_NONE; - else if (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) - /* title bar, no shadows */ - return DECORATION_TYPE_MAXIMIZED; - else if (window_state & LIBDECOR_WINDOW_STATE_TILED_LEFT || - window_state & LIBDECOR_WINDOW_STATE_TILED_RIGHT || - window_state & LIBDECOR_WINDOW_STATE_TILED_TOP || - window_state & LIBDECOR_WINDOW_STATE_TILED_BOTTOM) - /* title bar, invisible shadows */ - return DECORATION_TYPE_TILED; - else - /* title bar, shadows */ - return DECORATION_TYPE_ALL; -} - -static void -libdecor_plugin_cairo_frame_commit(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - struct libdecor_state *state, - struct libdecor_configuration *configuration) -{ - struct libdecor_frame_cairo *frame_cairo = - (struct libdecor_frame_cairo *) frame; - enum libdecor_window_state old_window_state; - enum libdecor_window_state new_window_state; - int old_content_width, old_content_height; - int new_content_width, new_content_height; - enum decoration_type old_decoration_type; - enum decoration_type new_decoration_type; - - old_window_state = frame_cairo->window_state; - new_window_state = libdecor_frame_get_window_state(frame); - - old_content_width = frame_cairo->content_width; - old_content_height = frame_cairo->content_height; - new_content_width = libdecor_frame_get_content_width(frame); - new_content_height = libdecor_frame_get_content_height(frame); - - old_decoration_type = frame_cairo->decoration_type; - new_decoration_type = window_state_to_decoration_type(new_window_state); - - if (old_decoration_type == new_decoration_type && - old_content_width == new_content_width && - old_content_height == new_content_height && - old_window_state == new_window_state) - return; - - frame_cairo->content_width = new_content_width; - frame_cairo->content_height = new_content_height; - frame_cairo->decoration_type = new_decoration_type; - frame_cairo->window_state = new_window_state; - - draw_decoration(frame_cairo); -} - -static void -libdecor_plugin_cairo_frame_property_changed(struct libdecor_plugin *plugin, - struct libdecor_frame *frame) -{ - struct libdecor_frame_cairo *frame_cairo = - (struct libdecor_frame_cairo *) frame; - bool redraw_needed = false; - const char *new_title; - - new_title = libdecor_frame_get_title(frame); - if (frame_cairo->title_bar.is_showing) { - if (!streql(frame_cairo->title, new_title)) - redraw_needed = true; - } - - if (frame_cairo->title) { - free(frame_cairo->title); - frame_cairo->title = NULL; - } - - if (new_title) { - frame_cairo->title = strdup(new_title); - } - - if (frame_cairo->capabilities != libdecor_frame_get_capabilities(frame)) { - frame_cairo->capabilities = libdecor_frame_get_capabilities(frame); - redraw_needed = true; - } - - if (redraw_needed) { - draw_decoration(frame_cairo); - libdecor_frame_toplevel_commit(frame); - } -} - -static bool -streq(const char *str1, - const char *str2) -{ - if (!str1 && !str2) - return true; - - if (str1 && str2) - return strcmp(str1, str2) == 0; - - return false; -} - -static void -libdecor_plugin_cairo_frame_popup_grab(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - const char *seat_name) -{ - struct libdecor_frame_cairo *frame_cairo = - (struct libdecor_frame_cairo *) frame; - struct libdecor_plugin_cairo *plugin_cairo = frame_cairo->plugin_cairo; - struct seat *seat; - - wl_list_for_each(seat, &plugin_cairo->seat_list, link) { - if (streq(seat->name, seat_name)) { - if (seat->grabbed) { - fprintf(stderr, "libdecor-WARNING: Application " - "tried to grab seat twice\n"); - } - synthesize_pointer_leave(seat); - seat->grabbed = true; - return; - } - } - - fprintf(stderr, - "libdecor-WARNING: Application tried to grab unknown seat\n"); -} - -static void -libdecor_plugin_cairo_frame_popup_ungrab(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - const char *seat_name) -{ - struct libdecor_frame_cairo *frame_cairo = - (struct libdecor_frame_cairo *) frame; - struct libdecor_plugin_cairo *plugin_cairo = frame_cairo->plugin_cairo; - struct seat *seat; - - wl_list_for_each(seat, &plugin_cairo->seat_list, link) { - if (streq(seat->name, seat_name)) { - if (!seat->grabbed) { - fprintf(stderr, "libdecor-WARNING: Application " - "tried to ungrab seat twice\n"); - } - seat->grabbed = false; - synthesize_pointer_enter(seat); - sync_active_component(frame_cairo, seat); - return; - } - } - - fprintf(stderr, - "libdecor-WARNING: Application tried to ungrab unknown seat\n"); -} - -static bool -libdecor_plugin_cairo_frame_get_border_size(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - struct libdecor_configuration *configuration, - int *left, - int *right, - int *top, - int *bottom) -{ - enum libdecor_window_state window_state; - - if (configuration) { - if (!libdecor_configuration_get_window_state( - configuration, &window_state)) - return false; - } else { - window_state = libdecor_frame_get_window_state(frame); - } - - if (left) - *left = 0; - if (right) - *right = 0; - if (bottom) - *bottom = 0; - if (top) { - enum decoration_type type = window_state_to_decoration_type(window_state); - - if (((struct libdecor_frame_cairo *)frame)->title_bar.is_showing && - (type != DECORATION_TYPE_NONE)) - *top = TITLE_HEIGHT; - else - *top = 0; - } - - return true; -} - -static struct libdecor_plugin_interface cairo_plugin_iface = { - .destroy = libdecor_plugin_cairo_destroy, - .get_fd = libdecor_plugin_cairo_get_fd, - .dispatch = libdecor_plugin_cairo_dispatch, - - .set_handle_application_cursor = libdecor_plugin_cairo_set_handle_application_cursor, - - .frame_new = libdecor_plugin_cairo_frame_new, - .frame_free = libdecor_plugin_cairo_frame_free, - .frame_commit = libdecor_plugin_cairo_frame_commit, - .frame_property_changed = libdecor_plugin_cairo_frame_property_changed, - .frame_popup_grab = libdecor_plugin_cairo_frame_popup_grab, - .frame_popup_ungrab = libdecor_plugin_cairo_frame_popup_ungrab, - .frame_get_border_size = libdecor_plugin_cairo_frame_get_border_size, -}; - -static void -init_wl_compositor(struct libdecor_plugin_cairo *plugin_cairo, - uint32_t id, - uint32_t version) -{ - plugin_cairo->wl_compositor = - wl_registry_bind(plugin_cairo->wl_registry, - id, &wl_compositor_interface, - MIN(version, 4)); -} - -static void -init_wl_subcompositor(struct libdecor_plugin_cairo *plugin_cairo, - uint32_t id, - uint32_t version) -{ - plugin_cairo->wl_subcompositor = - wl_registry_bind(plugin_cairo->wl_registry, - id, &wl_subcompositor_interface, 1); -} - -static void -shm_format(void *user_data, - struct wl_shm *wl_shm, - uint32_t format) -{ - struct libdecor_plugin_cairo *plugin_cairo = user_data; - - if (format == WL_SHM_FORMAT_ARGB8888) - plugin_cairo->has_argb = true; -} - -struct wl_shm_listener shm_listener = { - shm_format -}; - -static void -shm_callback(void *user_data, - struct wl_callback *callback, - uint32_t time) -{ - struct libdecor_plugin_cairo *plugin_cairo = user_data; - struct libdecor *context = plugin_cairo->context; - - wl_callback_destroy(callback); - plugin_cairo->globals_callback_shm = NULL; - - if (!plugin_cairo->has_argb) { - libdecor_notify_plugin_error( - context, - LIBDECOR_ERROR_COMPOSITOR_INCOMPATIBLE, - "Compositor is missing required shm format"); - return; - } - - libdecor_notify_plugin_ready(context); -} - -static const struct wl_callback_listener shm_callback_listener = { - shm_callback -}; - -static void -init_wl_shm(struct libdecor_plugin_cairo *plugin_cairo, - uint32_t id, - uint32_t version) -{ - struct libdecor *context = plugin_cairo->context; - struct wl_display *wl_display = libdecor_get_wl_display(context); - - plugin_cairo->wl_shm = - wl_registry_bind(plugin_cairo->wl_registry, - id, &wl_shm_interface, 1); - wl_shm_add_listener(plugin_cairo->wl_shm, &shm_listener, plugin_cairo); - - plugin_cairo->globals_callback_shm = wl_display_sync(wl_display); - wl_callback_add_listener(plugin_cairo->globals_callback_shm, - &shm_callback_listener, - plugin_cairo); -} - -static void -cursor_surface_enter(void *data, - struct wl_surface *wl_surface, - struct wl_output *wl_output) -{ - struct seat *seat = data; - - if(own_output(wl_output)) { - struct cursor_output *cursor_output; - cursor_output = zalloc(sizeof *cursor_output); - cursor_output->output = wl_output_get_user_data(wl_output); - wl_list_insert(&seat->cursor_outputs, &cursor_output->link); - if (update_local_cursor(seat)) - send_cursor(seat); - } -} - -static void -cursor_surface_leave(void *data, - struct wl_surface *wl_surface, - struct wl_output *wl_output) -{ - struct seat *seat = data; - - if(own_output(wl_output)) { - struct cursor_output *cursor_output, *tmp; - wl_list_for_each_safe(cursor_output, tmp, &seat->cursor_outputs, link) { - if (cursor_output->output->wl_output == wl_output) { - wl_list_remove(&cursor_output->link); - free(cursor_output); - } - } - - if (update_local_cursor(seat)) - send_cursor(seat); - } -} - -static struct wl_surface_listener cursor_surface_listener = { - cursor_surface_enter, - cursor_surface_leave, -}; - -static void -ensure_cursor_surface(struct seat *seat) -{ - struct wl_compositor *wl_compositor = seat->plugin_cairo->wl_compositor; - - if (seat->cursor_surface) - return; - - seat->cursor_surface = wl_compositor_create_surface(wl_compositor); - wl_surface_add_listener(seat->cursor_surface, - &cursor_surface_listener, seat); -} - -static bool -ensure_cursor_theme(struct seat *seat) -{ - struct libdecor_plugin_cairo *plugin_cairo = seat->plugin_cairo; - int scale = 1; - struct wl_cursor_theme *theme; - struct cursor_output *cursor_output; - - wl_list_for_each(cursor_output, &seat->cursor_outputs, link) { - scale = MAX(scale, cursor_output->output->scale); - } - - if (seat->cursor_theme && seat->cursor_scale == scale) - return false; - - seat->cursor_scale = scale; - theme = wl_cursor_theme_load(plugin_cairo->cursor_theme_name, - plugin_cairo->cursor_size * scale, - plugin_cairo->wl_shm); - if (theme == NULL) - return false; - - if (seat->cursor_theme) - wl_cursor_theme_destroy(seat->cursor_theme); - - seat->cursor_theme = theme; - - for (unsigned int i = 0; i < ARRAY_LENGTH(cursor_names); i++) { - seat->cursors[i] = wl_cursor_theme_get_cursor( - seat->cursor_theme, - cursor_names[i]); - } - - seat->cursor_left_ptr = wl_cursor_theme_get_cursor(seat->cursor_theme, - "left_ptr"); - seat->current_cursor = seat->cursor_left_ptr; - - return true; -} - -enum libdecor_resize_edge -component_edge(const struct border_component *cmpnt, - const int pointer_x, - const int pointer_y, - const int margin) -{ - const bool top = pointer_y < margin * 2; - const bool bottom = pointer_y > (cmpnt->server.buffer->height - margin * 2); - const bool left = pointer_x < margin * 2; - const bool right = pointer_x > (cmpnt->server.buffer->width - margin * 2); - - if (top) - if (left) - return LIBDECOR_RESIZE_EDGE_TOP_LEFT; - else if (right) - return LIBDECOR_RESIZE_EDGE_TOP_RIGHT; - else - return LIBDECOR_RESIZE_EDGE_TOP; - else if (bottom) - if (left) - return LIBDECOR_RESIZE_EDGE_BOTTOM_LEFT; - else if (right) - return LIBDECOR_RESIZE_EDGE_BOTTOM_RIGHT; - else - return LIBDECOR_RESIZE_EDGE_BOTTOM; - else if (left) - return LIBDECOR_RESIZE_EDGE_LEFT; - else if (right) - return LIBDECOR_RESIZE_EDGE_RIGHT; - else - return LIBDECOR_RESIZE_EDGE_NONE; -} - -static bool -update_local_cursor(struct seat *seat) -{ - if (!seat->pointer_focus) { - seat->current_cursor = seat->cursor_left_ptr; - return false; - } - - if (!own_surface(seat->pointer_focus)) { - if (seat->plugin_cairo->handle_cursor) { - seat->current_cursor = seat->cursor_left_ptr; - return true; - } else { - return false; - } - } - - struct libdecor_frame_cairo *frame_cairo = - wl_surface_get_user_data(seat->pointer_focus); - struct wl_cursor *wl_cursor = NULL; - - if (!frame_cairo || !frame_cairo->active) { - seat->current_cursor = seat->cursor_left_ptr; - return false; - } - - bool theme_updated = ensure_cursor_theme(seat); - - if (frame_cairo->active->type == SHADOW && - is_border_surfaces_showing(frame_cairo) && - resizable(frame_cairo)) { - enum libdecor_resize_edge edge; - edge = component_edge(frame_cairo->active, - seat->pointer_x, - seat->pointer_y, SHADOW_MARGIN); - - if (edge != LIBDECOR_RESIZE_EDGE_NONE) - wl_cursor = seat->cursors[edge - 1]; - } else { - wl_cursor = seat->cursor_left_ptr; - } - - if (seat->current_cursor != wl_cursor) { - seat->current_cursor = wl_cursor; - return true; - } - - return theme_updated; -} - -static void -send_cursor(struct seat *seat) -{ - struct wl_cursor_image *image; - struct wl_buffer *buffer; - - if (seat->pointer_focus == NULL || seat->current_cursor == NULL) - return; - - image = seat->current_cursor->images[0]; - buffer = wl_cursor_image_get_buffer(image); - wl_surface_attach(seat->cursor_surface, buffer, 0, 0); - wl_surface_set_buffer_scale(seat->cursor_surface, seat->cursor_scale); - wl_surface_damage_buffer(seat->cursor_surface, 0, 0, - image->width * seat->cursor_scale, - image->height * seat->cursor_scale); - wl_surface_commit(seat->cursor_surface); - wl_pointer_set_cursor(seat->wl_pointer, seat->serial, - seat->cursor_surface, - image->hotspot_x / seat->cursor_scale, - image->hotspot_y / seat->cursor_scale); -} - -static void -sync_active_component(struct libdecor_frame_cairo *frame_cairo, - struct seat *seat) -{ - struct border_component *old_active; - - if (!seat->pointer_focus) - return; - - old_active = frame_cairo->active; - update_component_focus(frame_cairo, seat->pointer_focus, seat); - if (old_active != frame_cairo->active) { - draw_decoration(frame_cairo); - libdecor_frame_toplevel_commit(&frame_cairo->frame); - } - - if (update_local_cursor(seat)) - send_cursor(seat); -} - -static void -synthesize_pointer_enter(struct seat *seat) -{ - struct wl_surface *surface; - struct libdecor_frame_cairo *frame_cairo; - - surface = seat->pointer_focus; - if (!surface || !own_surface(surface)) - return; - - frame_cairo = wl_surface_get_user_data(surface); - if (!frame_cairo) - return; - - update_component_focus(frame_cairo, seat->pointer_focus, seat); - frame_cairo->grab = NULL; - - /* update decorations */ - if (frame_cairo->active) { - draw_decoration(frame_cairo); - libdecor_frame_toplevel_commit(&frame_cairo->frame); - } - - update_local_cursor(seat); - send_cursor(seat); -} - -static void -synthesize_pointer_leave(struct seat *seat) -{ - struct wl_surface *surface; - struct libdecor_frame_cairo *frame_cairo; - - surface = seat->pointer_focus; - if (!surface || !own_surface(surface)) - return; - - frame_cairo = wl_surface_get_user_data(surface); - if (!frame_cairo) - return; - - if (!frame_cairo->active) - return; - - frame_cairo->active = NULL; - draw_decoration(frame_cairo); - libdecor_frame_toplevel_commit(&frame_cairo->frame); - update_local_cursor(seat); -} - -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) -{ - struct seat *seat = data; - struct libdecor_frame_cairo *frame_cairo = NULL; - - if (!surface) - return; - - if (!own_surface(surface)) { - struct seat *seat = wl_pointer_get_user_data(wl_pointer); - struct libdecor_plugin_cairo *plugin_cairo = seat->plugin_cairo; - - if (!plugin_cairo->handle_cursor) - return; - } else { - frame_cairo = wl_surface_get_user_data(surface); - } - - ensure_cursor_surface(seat); - - seat->pointer_x = wl_fixed_to_int(surface_x); - seat->pointer_y = wl_fixed_to_int(surface_y); - seat->serial = serial; - seat->pointer_focus = surface; - seat->pointer_focus_frame = frame_cairo; - - if (seat->grabbed) - return; - - synthesize_pointer_enter(seat); -} - -static void -pointer_leave(void *data, - struct wl_pointer *wl_pointer, - uint32_t serial, - struct wl_surface *surface) -{ - struct seat *seat = data; - - if (!surface) - return; - - if (!own_surface(surface)) - return; - - synthesize_pointer_leave(seat); - seat->pointer_focus = NULL; - seat->pointer_focus_frame = NULL; -} - -static void -pointer_motion(void *data, - struct wl_pointer *wl_pointer, - uint32_t time, - wl_fixed_t surface_x, - wl_fixed_t surface_y) -{ - struct seat *seat = data; - - seat->pointer_x = wl_fixed_to_int(surface_x); - seat->pointer_y = wl_fixed_to_int(surface_y); - - if (seat->grabbed) - return; - - if (!seat->pointer_focus_frame) - return; - - sync_active_component(seat->pointer_focus_frame, seat); -} - -static void -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_cairo *frame_cairo; - - if (!seat->pointer_focus || !own_surface(seat->pointer_focus)) - return; - - frame_cairo = wl_surface_get_user_data(seat->pointer_focus); - if (!frame_cairo) - return; - - if (seat->grabbed) { - libdecor_frame_dismiss_popup(&frame_cairo->frame, seat->name); - return; - } - - if (!frame_cairo->active) - return; - - if (button == BTN_LEFT) { - if (state == WL_POINTER_BUTTON_STATE_PRESSED) { - enum libdecor_resize_edge edge = - LIBDECOR_RESIZE_EDGE_NONE; - - frame_cairo->grab = NULL; - - switch (frame_cairo->active->type) { - case SHADOW: - edge = component_edge(frame_cairo->active, - seat->pointer_x, - seat->pointer_y, - SHADOW_MARGIN); - break; - case TITLE: - if (time-seat->pointer_button_time_stamp < - DOUBLE_CLICK_TIME_MS) { - toggle_maximized(&frame_cairo->frame); - } - else if (moveable(frame_cairo)) { - seat->pointer_button_time_stamp = time; - libdecor_frame_move(&frame_cairo->frame, - seat->wl_seat, - serial); - } - break; - case BUTTON_MIN: - case BUTTON_MAX: - case BUTTON_CLOSE: - frame_cairo->grab = frame_cairo->active; - break; - default: - break; - } - - if (edge != LIBDECOR_RESIZE_EDGE_NONE && - resizable(frame_cairo)) { - libdecor_frame_resize( - &frame_cairo->frame, - seat->wl_seat, - serial, - edge); - } - } - else if (state == WL_POINTER_BUTTON_STATE_RELEASED && - frame_cairo->grab) { - libdecor_frame_ref(&frame_cairo->frame); - if (frame_cairo->grab == frame_cairo->focus) { - switch (frame_cairo->active->type) { - case BUTTON_MIN: - if (minimizable(frame_cairo)) - libdecor_frame_set_minimized( - &frame_cairo->frame); - break; - case BUTTON_MAX: - toggle_maximized(&frame_cairo->frame); - break; - case BUTTON_CLOSE: - if (closeable(frame_cairo)) - libdecor_frame_close(&frame_cairo->frame); - break; - default: - break; - } - } - frame_cairo->grab = NULL; - sync_active_component(frame_cairo, seat); - libdecor_frame_unref(&frame_cairo->frame); - } - } - else if (button == BTN_RIGHT && - state == WL_POINTER_BUTTON_STATE_PRESSED && - seat->pointer_focus == frame_cairo->title_bar.title.server.wl_surface) { - libdecor_frame_show_window_menu(&frame_cairo->frame, - seat->wl_seat, - serial, - seat->pointer_x, - seat->pointer_y - TITLE_HEIGHT); - } -} - -static void -pointer_axis(void *data, - struct wl_pointer *wl_pointer, - uint32_t time, - uint32_t axis, - wl_fixed_t value) -{ -} - -static struct wl_pointer_listener pointer_listener = { - pointer_enter, - pointer_leave, - pointer_motion, - pointer_button, - pointer_axis -}; - -static void -seat_capabilities(void *data, - struct wl_seat *wl_seat, - uint32_t capabilities) -{ - struct seat *seat = data; - - if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && - !seat->wl_pointer) { - seat->wl_pointer = wl_seat_get_pointer(wl_seat); - wl_pointer_add_listener(seat->wl_pointer, - &pointer_listener, seat); - } else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && - seat->wl_pointer) { - wl_pointer_release(seat->wl_pointer); - seat->wl_pointer = NULL; - } -} - -static void -seat_name(void *data, - struct wl_seat *wl_seat, - const char *name) -{ - struct seat *seat = data; - - seat->name = strdup(name); -} - -static struct wl_seat_listener seat_listener = { - seat_capabilities, - seat_name -}; - -static void -init_wl_seat(struct libdecor_plugin_cairo *plugin_cairo, - uint32_t id, - uint32_t version) -{ - struct seat *seat; - - if (version < 3) { - libdecor_notify_plugin_error( - plugin_cairo->context, - LIBDECOR_ERROR_COMPOSITOR_INCOMPATIBLE, - "%s version 3 required but only version %i is available\n", - wl_seat_interface.name, version); - } - - seat = zalloc(sizeof *seat); - seat->cursor_scale = 1; - seat->plugin_cairo = plugin_cairo; - wl_list_init(&seat->cursor_outputs); - wl_list_insert(&plugin_cairo->seat_list, &seat->link); - seat->wl_seat = - wl_registry_bind(plugin_cairo->wl_registry, - id, &wl_seat_interface, 3); - wl_seat_add_listener(seat->wl_seat, &seat_listener, seat); -} - -static void -output_geometry(void *data, - struct wl_output *wl_output, - int32_t x, - int32_t y, - int32_t physical_width, - int32_t physical_height, - int32_t subpixel, - const char *make, - const char *model, - int32_t transform) -{ -} - -static void -output_mode(void *data, - struct wl_output *wl_output, - uint32_t flags, - int32_t width, - int32_t height, - int32_t refresh) -{ -} - -static void -output_done(void *data, - struct wl_output *wl_output) -{ - struct output *output = data; - struct libdecor_frame_cairo *frame_cairo; - struct seat *seat; - - wl_list_for_each(frame_cairo, - &output->plugin_cairo->visible_frame_list, link) { - bool updated = false; - updated |= redraw_scale(frame_cairo, &frame_cairo->shadow); - updated |= redraw_scale(frame_cairo, &frame_cairo->title_bar.title); - if (updated) - libdecor_frame_toplevel_commit(&frame_cairo->frame); - } - wl_list_for_each(seat, &output->plugin_cairo->seat_list, link) { - if (update_local_cursor(seat)) - send_cursor(seat); - } -} - -static void -output_scale(void *data, - struct wl_output *wl_output, - int32_t factor) -{ - struct output *output = data; - - output->scale = factor; -} - -static struct wl_output_listener output_listener = { - output_geometry, - output_mode, - output_done, - output_scale -}; - -static void -init_wl_output(struct libdecor_plugin_cairo *plugin_cairo, - uint32_t id, - uint32_t version) -{ - struct output *output; - - if (version < 2) { - libdecor_notify_plugin_error( - plugin_cairo->context, - LIBDECOR_ERROR_COMPOSITOR_INCOMPATIBLE, - "%s version 2 required but only version %i is available\n", - wl_output_interface.name, version); - } - - output = zalloc(sizeof *output); - output->plugin_cairo = plugin_cairo; - wl_list_insert(&plugin_cairo->output_list, &output->link); - output->id = id; - output->wl_output = - wl_registry_bind(plugin_cairo->wl_registry, - id, &wl_output_interface, - MIN (version, 3)); - wl_proxy_set_tag((struct wl_proxy *) output->wl_output, - &libdecor_cairo_proxy_tag); - wl_output_add_listener(output->wl_output, &output_listener, output); -} - -static void -registry_handle_global(void *user_data, - struct wl_registry *wl_registry, - uint32_t id, - const char *interface, - uint32_t version) -{ - struct libdecor_plugin_cairo *plugin_cairo = user_data; - - if (strcmp(interface, "wl_compositor") == 0) - init_wl_compositor(plugin_cairo, id, version); - else if (strcmp(interface, "wl_subcompositor") == 0) - init_wl_subcompositor(plugin_cairo, id, version); - else if (strcmp(interface, "wl_shm") == 0) - init_wl_shm(plugin_cairo, id, version); - else if (strcmp(interface, "wl_seat") == 0) - init_wl_seat(plugin_cairo, id, version); - else if (strcmp(interface, "wl_output") == 0) - init_wl_output(plugin_cairo, id, version); -} - -static void -remove_surface_outputs(struct border_component *cmpnt, struct output *output) -{ - struct surface_output *surface_output; - wl_list_for_each(surface_output, &cmpnt->server.output_list, link) { - if (surface_output->output == output) { - wl_list_remove(&surface_output->link); - free(surface_output); - break; - } - } -} - -static void -output_removed(struct libdecor_plugin_cairo *plugin_cairo, - struct output *output) -{ - struct libdecor_frame_cairo *frame_cairo; - struct seat *seat; - - wl_list_for_each(frame_cairo, &plugin_cairo->visible_frame_list, link) { - remove_surface_outputs(&frame_cairo->shadow, output); - remove_surface_outputs(&frame_cairo->title_bar.title, output); - remove_surface_outputs(&frame_cairo->title_bar.min, output); - remove_surface_outputs(&frame_cairo->title_bar.max, output); - remove_surface_outputs(&frame_cairo->title_bar.close, output); - } - wl_list_for_each(seat, &plugin_cairo->seat_list, link) { - struct cursor_output *cursor_output, *tmp; - wl_list_for_each_safe(cursor_output, tmp, &seat->cursor_outputs, link) { - if (cursor_output->output == output) { - wl_list_remove(&cursor_output->link); - free(cursor_output); - } - } - } - - wl_list_remove(&output->link); - wl_output_destroy(output->wl_output); - free(output); -} - -static void -registry_handle_global_remove(void *user_data, - struct wl_registry *wl_registry, - uint32_t name) -{ - struct libdecor_plugin_cairo *plugin_cairo = user_data; - struct output *output; - - wl_list_for_each(output, &plugin_cairo->output_list, link) { - if (output->id == name) { - output_removed(plugin_cairo, output); - break; - } - } -} - -static const struct wl_registry_listener registry_listener = { - registry_handle_global, - registry_handle_global_remove -}; - -static bool -has_required_globals(struct libdecor_plugin_cairo *plugin_cairo) -{ - if (!plugin_cairo->wl_compositor) - return false; - if (!plugin_cairo->wl_subcompositor) - return false; - if (!plugin_cairo->wl_shm) - return false; - - return true; -} - -static void -globals_callback(void *user_data, - struct wl_callback *callback, - uint32_t time) -{ - struct libdecor_plugin_cairo *plugin_cairo = user_data; - - wl_callback_destroy(callback); - plugin_cairo->globals_callback = NULL; -} - -static const struct wl_callback_listener globals_callback_listener = { - globals_callback -}; - -static struct libdecor_plugin * -libdecor_plugin_new(struct libdecor *context) -{ - struct libdecor_plugin_cairo *plugin_cairo; - struct wl_display *wl_display; - - plugin_cairo = zalloc(sizeof *plugin_cairo); - libdecor_plugin_init(&plugin_cairo->plugin, - context, - &cairo_plugin_iface); - plugin_cairo->context = context; - - wl_list_init(&plugin_cairo->visible_frame_list); - wl_list_init(&plugin_cairo->seat_list); - wl_list_init(&plugin_cairo->output_list); - - /* fetch cursor theme and size*/ - if (!libdecor_get_cursor_settings(&plugin_cairo->cursor_theme_name, - &plugin_cairo->cursor_size)) { - plugin_cairo->cursor_theme_name = NULL; - plugin_cairo->cursor_size = 24; - } - - /* define a sens-serif bold font at symbol size */ - plugin_cairo->font = pango_font_description_new(); - pango_font_description_set_family(plugin_cairo->font, "sans"); - pango_font_description_set_weight(plugin_cairo->font, PANGO_WEIGHT_BOLD); - pango_font_description_set_absolute_size(plugin_cairo->font, SYM_DIM * PANGO_SCALE); - - wl_display = libdecor_get_wl_display(context); - plugin_cairo->wl_registry = wl_display_get_registry(wl_display); - wl_registry_add_listener(plugin_cairo->wl_registry, - ®istry_listener, - plugin_cairo); - - plugin_cairo->globals_callback = wl_display_sync(wl_display); - wl_callback_add_listener(plugin_cairo->globals_callback, - &globals_callback_listener, - plugin_cairo); - wl_display_roundtrip(wl_display); - - if (!has_required_globals(plugin_cairo)) { - fprintf(stderr, "libdecor-cairo-WARNING: Could not get required globals\n"); - libdecor_plugin_cairo_destroy(&plugin_cairo->plugin); - return NULL; - } - - return &plugin_cairo->plugin; -} - -static struct libdecor_plugin_priority priorities[] = { - { NULL, LIBDECOR_PLUGIN_PRIORITY_MEDIUM } -}; - -LIBDECOR_EXPORT const struct libdecor_plugin_description -libdecor_plugin_description = { - .api_version = LIBDECOR_PLUGIN_API_VERSION, - .capabilities = LIBDECOR_PLUGIN_CAPABILITY_BASE, - .description = "libdecor plugin using Cairo", - .priorities = priorities, - .constructor = libdecor_plugin_new, -}; diff --git a/libdecor/src/plugins/common/libdecor-cairo-blur.c b/libdecor/src/plugins/common/libdecor-cairo-blur.c deleted file mode 100644 index 2cddc7a23..000000000 --- a/libdecor/src/plugins/common/libdecor-cairo-blur.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright © 2008 Kristian Høgsberg - * Copyright © 2012 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * functions 'blur_surface' and 'render_shadow' from weston project: - * https://gitlab.freedesktop.org/wayland/weston/raw/master/shared/cairo-util.c - */ - -#include "libdecor-cairo-blur.h" -#include <stdint.h> -#include <stdlib.h> -#include <math.h> - -/** - * Compile-time computation of number of items in a hardcoded array. - * - * @param a the array being measured. - * @return the number of items hardcoded into the array. - */ -#ifndef ARRAY_LENGTH -#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) -#endif - -int -blur_surface(cairo_surface_t *surface, int margin) -{ - int32_t width, height, stride, x, y, z, w; - uint8_t *src, *dst; - uint32_t *s, *d, a, p; - int i, j, k, size, half; - uint32_t kernel[71]; - double f; - - size = ARRAY_LENGTH(kernel); - width = cairo_image_surface_get_width(surface); - height = cairo_image_surface_get_height(surface); - stride = cairo_image_surface_get_stride(surface); - src = cairo_image_surface_get_data(surface); - - dst = malloc(height * stride); - if (dst == NULL) - return -1; - - half = size / 2; - a = 0; - for (i = 0; i < size; i++) { - f = (i - half); - kernel[i] = exp(- f * f / ARRAY_LENGTH(kernel)) * 10000; - a += kernel[i]; - } - - for (i = 0; i < height; i++) { - s = (uint32_t *) (src + i * stride); - d = (uint32_t *) (dst + i * stride); - for (j = 0; j < width; j++) { - if (margin < j && j < width - margin) { - d[j] = s[j]; - continue; - } - - x = 0; - y = 0; - z = 0; - w = 0; - for (k = 0; k < size; k++) { - if (j - half + k < 0 || j - half + k >= width) - continue; - p = s[j - half + k]; - - x += (p >> 24) * kernel[k]; - y += ((p >> 16) & 0xff) * kernel[k]; - z += ((p >> 8) & 0xff) * kernel[k]; - w += (p & 0xff) * kernel[k]; - } - d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a; - } - } - - for (i = 0; i < height; i++) { - s = (uint32_t *) (dst + i * stride); - d = (uint32_t *) (src + i * stride); - for (j = 0; j < width; j++) { - if (margin <= i && i < height - margin) { - d[j] = s[j]; - continue; - } - - x = 0; - y = 0; - z = 0; - w = 0; - for (k = 0; k < size; k++) { - if (i - half + k < 0 || i - half + k >= height) - continue; - s = (uint32_t *) (dst + (i - half + k) * stride); - p = s[j]; - - x += (p >> 24) * kernel[k]; - y += ((p >> 16) & 0xff) * kernel[k]; - z += ((p >> 8) & 0xff) * kernel[k]; - w += (p & 0xff) * kernel[k]; - } - d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a; - } - } - - free(dst); - cairo_surface_mark_dirty(surface); - - return 0; -} - -void -render_shadow(cairo_t *cr, cairo_surface_t *surface, - int x, int y, int width, int height, int margin, int top_margin) -{ - cairo_pattern_t *pattern; - cairo_matrix_t matrix; - int i, fx, fy, shadow_height, shadow_width; - - cairo_set_source_rgba(cr, 0, 0, 0, 0.45); - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - pattern = cairo_pattern_create_for_surface (surface); - cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST); - - for (i = 0; i < 4; i++) { - /* when fy is set, then we are working with lower corners, - * when fx is set, then we are working with right corners - * - * 00 ------- 01 - * | | - * | | - * 10 ------- 11 - */ - fx = i & 1; - fy = i >> 1; - - cairo_matrix_init_translate(&matrix, - -x + fx * (128 - width), - -y + fy * (128 - height)); - cairo_pattern_set_matrix(pattern, &matrix); - - shadow_width = margin; - shadow_height = fy ? margin : top_margin; - - /* if the shadows together are greater than the surface, we need - * to fix it - set the shadow size to the half of - * the size of surface. Also handle the case when the size is - * not divisible by 2. In that case we need one part of the - * shadow to be one pixel greater. !fy or !fx, respectively, - * will do the work. - */ - if (height < 2 * shadow_height) - shadow_height = (height + !fy) / 2; - - if (width < 2 * shadow_width) - shadow_width = (width + !fx) / 2; - - cairo_reset_clip(cr); - cairo_rectangle(cr, - x + fx * (width - shadow_width), - y + fy * (height - shadow_height), - shadow_width, shadow_height); - cairo_clip (cr); - cairo_mask(cr, pattern); - } - - - shadow_width = width - 2 * margin; - shadow_height = top_margin; - if (height < 2 * shadow_height) - shadow_height = height / 2; - - if (shadow_width > 0 && shadow_height) { - /* Top stretch */ - cairo_matrix_init_translate(&matrix, 60, 0); - cairo_matrix_scale(&matrix, 8.0 / width, 1); - cairo_matrix_translate(&matrix, -x - width / 2, -y); - cairo_pattern_set_matrix(pattern, &matrix); - cairo_rectangle(cr, x + margin, y, shadow_width, shadow_height); - - cairo_reset_clip(cr); - cairo_rectangle(cr, - x + margin, y, - shadow_width, shadow_height); - cairo_clip (cr); - cairo_mask(cr, pattern); - - /* Bottom stretch */ - cairo_matrix_translate(&matrix, 0, -height + 128); - cairo_pattern_set_matrix(pattern, &matrix); - - cairo_reset_clip(cr); - cairo_rectangle(cr, x + margin, y + height - margin, - shadow_width, margin); - cairo_clip (cr); - cairo_mask(cr, pattern); - } - - shadow_width = margin; - if (width < 2 * shadow_width) - shadow_width = width / 2; - - shadow_height = height - margin - top_margin; - - /* if height is smaller than sum of margins, - * then the shadow is already done by the corners */ - if (shadow_height > 0 && shadow_width) { - /* Left stretch */ - cairo_matrix_init_translate(&matrix, 0, 60); - cairo_matrix_scale(&matrix, 1, 8.0 / height); - cairo_matrix_translate(&matrix, -x, -y - height / 2); - cairo_pattern_set_matrix(pattern, &matrix); - cairo_reset_clip(cr); - cairo_rectangle(cr, x, y + top_margin, - shadow_width, shadow_height); - cairo_clip (cr); - cairo_mask(cr, pattern); - - /* Right stretch */ - cairo_matrix_translate(&matrix, -width + 128, 0); - cairo_pattern_set_matrix(pattern, &matrix); - cairo_rectangle(cr, x + width - shadow_width, y + top_margin, - shadow_width, shadow_height); - cairo_reset_clip(cr); - cairo_clip (cr); - cairo_mask(cr, pattern); - } - - cairo_pattern_destroy(pattern); - cairo_reset_clip(cr); -} diff --git a/libdecor/src/plugins/common/libdecor-cairo-blur.h b/libdecor/src/plugins/common/libdecor-cairo-blur.h deleted file mode 100644 index d48e9dd3d..000000000 --- a/libdecor/src/plugins/common/libdecor-cairo-blur.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include <cairo/cairo.h> - -int -blur_surface(cairo_surface_t *surface, int margin); - -void -render_shadow(cairo_t *cr, cairo_surface_t *surface, - int x, int y, int width, int height, int margin, int top_margin); diff --git a/libdecor/src/plugins/dummy/libdecor-dummy.c b/libdecor/src/plugins/dummy/libdecor-dummy.c deleted file mode 100644 index 621dd027f..000000000 --- a/libdecor/src/plugins/dummy/libdecor-dummy.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright © 2021 Jonas Ådahl - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "config.h" - -#include "libdecor-plugin.h" - -#include <errno.h> -#include <fcntl.h> -#include <poll.h> -#include <stdlib.h> -#include <wayland-cursor.h> - -#include "utils.h" - -struct libdecor_plugin_dummy { - struct libdecor_plugin plugin; - struct libdecor *context; -}; - -static void -libdecor_plugin_dummy_destroy(struct libdecor_plugin *plugin) -{ - struct libdecor_plugin_dummy *plugin_dummy = - (struct libdecor_plugin_dummy *) plugin; - - libdecor_plugin_release(plugin); - free(plugin_dummy); -} - -static struct libdecor_frame * -libdecor_plugin_dummy_frame_new(struct libdecor_plugin *plugin) -{ - struct libdecor_frame *frame; - - frame = zalloc(sizeof *frame); - - return frame; -} - -static void -libdecor_plugin_dummy_set_handle_application_cursor(struct libdecor_plugin *plugin, - bool handle_cursor) -{ -} - -static void -libdecor_plugin_dummy_frame_free(struct libdecor_plugin *plugin, - struct libdecor_frame *frame) -{ -} - -static void -libdecor_plugin_dummy_frame_commit(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - struct libdecor_state *state, - struct libdecor_configuration *configuration) -{ -} - -static void -libdecor_plugin_dummy_frame_property_changed(struct libdecor_plugin *plugin, - struct libdecor_frame *frame) -{ -} - -static void -libdecor_plugin_dummy_frame_popup_grab(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - const char *seat_name) -{ -} - -static void -libdecor_plugin_dummy_frame_popup_ungrab(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - const char *seat_name) -{ -} - -static struct libdecor_plugin_interface dummy_plugin_iface = { - .destroy = libdecor_plugin_dummy_destroy, - - .set_handle_application_cursor = libdecor_plugin_dummy_set_handle_application_cursor, - - .frame_new = libdecor_plugin_dummy_frame_new, - .frame_free = libdecor_plugin_dummy_frame_free, - .frame_commit = libdecor_plugin_dummy_frame_commit, - .frame_property_changed = libdecor_plugin_dummy_frame_property_changed, - .frame_popup_grab = libdecor_plugin_dummy_frame_popup_grab, - .frame_popup_ungrab = libdecor_plugin_dummy_frame_popup_ungrab, -}; - -static struct libdecor_plugin * -libdecor_plugin_new(struct libdecor *context) -{ - struct libdecor_plugin_dummy *plugin_dummy; - - plugin_dummy = zalloc(sizeof *plugin_dummy); - libdecor_plugin_init(&plugin_dummy->plugin, context, &dummy_plugin_iface); - plugin_dummy->context = context; - - libdecor_notify_plugin_ready(context); - - return &plugin_dummy->plugin; -} - -static struct libdecor_plugin_priority priorities[] = { - { NULL, LIBDECOR_PLUGIN_PRIORITY_LOW } -}; - -LIBDECOR_EXPORT const struct libdecor_plugin_description -libdecor_plugin_description = { - .api_version = LIBDECOR_PLUGIN_API_VERSION, - .capabilities = LIBDECOR_PLUGIN_CAPABILITY_BASE, - .description = "dummy libdecor plugin", - .priorities = priorities, - .constructor = libdecor_plugin_new, -}; diff --git a/libdecor/src/plugins/gtk/libdecor-gtk.c b/libdecor/src/plugins/gtk/libdecor-gtk.c deleted file mode 100644 index bc5b0fa61..000000000 --- a/libdecor/src/plugins/gtk/libdecor-gtk.c +++ /dev/null @@ -1,3028 +0,0 @@ -/* - * Copyright © 2018 Jonas Ådahl - * Copyright © 2021 Christian Rauch - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "config.h" - -#include <linux/input.h> -#include <fcntl.h> -#include <string.h> -#include <sys/mman.h> -#include <unistd.h> -#include <stdio.h> -#include <errno.h> -#include <wayland-client-core.h> -#include <wayland-cursor.h> - -#include "libdecor-plugin.h" -#include "utils.h" -#include "desktop-settings.h" -#include "os-compatibility.h" - -#include <cairo/cairo.h> - -#include "common/libdecor-cairo-blur.h" -#include <poll.h> - -#include <gtk/gtk.h> - -static const size_t SHADOW_MARGIN = 24; /* grabbable part of the border */ - -static const char *cursor_names[] = { - "top_side", - "bottom_side", - "left_side", - "top_left_corner", - "bottom_left_corner", - "right_side", - "top_right_corner", - "bottom_right_corner" -}; - -enum header_element { - HEADER_NONE, - HEADER_FULL, /* entire header bar */ - HEADER_TITLE, /* label */ - HEADER_MIN, - HEADER_MAX, - HEADER_CLOSE, -}; - -enum titlebar_gesture_state { - TITLEBAR_GESTURE_STATE_INIT, - TITLEBAR_GESTURE_STATE_BUTTON_PRESSED, - TITLEBAR_GESTURE_STATE_CONSUMED, - TITLEBAR_GESTURE_STATE_DISCARDED, -}; - -struct header_element_data { - const char *name; - enum header_element type; - /* pointer to button or NULL if not found*/ - GtkWidget *widget; - GtkStateFlags state; -}; - -static void -find_widget_by_name(GtkWidget *widget, void *data) -{ - if (GTK_IS_WIDGET(widget)) { - char *style_ctx = gtk_style_context_to_string( - gtk_widget_get_style_context(widget), - GTK_STYLE_CONTEXT_PRINT_SHOW_STYLE); - if (strstr(style_ctx, ((struct header_element_data *)data)->name)) { - ((struct header_element_data *)data)->widget = widget; - free(style_ctx); - return; - } - free(style_ctx); - } - - if (GTK_IS_CONTAINER(widget)) { - /* recursively traverse container */ - gtk_container_forall(GTK_CONTAINER(widget), &find_widget_by_name, data); - } -} - -static struct header_element_data -find_widget_by_type(GtkWidget *widget, enum header_element type) -{ - char* name = NULL; - switch (type) { - case HEADER_FULL: - name = "headerbar.titlebar:"; - break; - case HEADER_TITLE: - name = "label.title:"; - break; - case HEADER_MIN: - name = ".minimize"; - break; - case HEADER_MAX: - name = ".maximize"; - break; - case HEADER_CLOSE: - name = ".close"; - break; - default: - break; - } - - struct header_element_data data = {.name = name, .type = type, .widget = NULL}; - find_widget_by_name(widget, &data); - return data; -} - -static bool -in_region(const cairo_rectangle_int_t *rect, const int *x, const int *y) -{ - return (*x>=rect->x) & (*y>=rect->y) & - (*x<(rect->x+rect->width)) & (*y<(rect->y+rect->height)); -} - -static struct header_element_data -get_header_focus(const GtkHeaderBar *header_bar, const int x, const int y) -{ - /* we have to check child widgets (buttons, title) before the 'HDR_HDR' root widget */ - static const enum header_element elems[] = - {HEADER_TITLE, HEADER_MIN, HEADER_MAX, HEADER_CLOSE}; - - for (size_t i = 0; i < ARRAY_SIZE(elems); i++) { - struct header_element_data elem = - find_widget_by_type(GTK_WIDGET(header_bar), elems[i]); - if (elem.widget) { - GtkAllocation allocation; - gtk_widget_get_allocation(GTK_WIDGET(elem.widget), &allocation); - if (in_region(&allocation, &x, &y)) - return elem; - } - } - - struct header_element_data elem_none = { .widget=NULL}; - return elem_none; -} - -static bool -streq(const char *str1, - const char *str2) -{ - if (!str1 && !str2) - return true; - - if (str1 && str2) - return strcmp(str1, str2) == 0; - - return false; -} - -enum decoration_type { - DECORATION_TYPE_NONE, - DECORATION_TYPE_ALL, - DECORATION_TYPE_TITLE_ONLY -}; - -enum component { - NONE = 0, - SHADOW, - HEADER, -}; - -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; - /* cursors for resize edges and corners */ - struct wl_cursor *cursors[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 touch_down_time_stamp; - - uint32_t serial; - - bool grabbed; - - struct wl_list link; -}; - -struct output { - struct libdecor_plugin_gtk *plugin_gtk; - - struct wl_output *wl_output; - uint32_t id; - int scale; - - struct wl_list link; -}; - -struct buffer { - 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; -}; - -struct border_component { - 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 surface_output { - struct output *output; - struct wl_list link; -}; - -struct cursor_output { - struct output *output; - struct wl_list link; -}; - -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 *active; - struct border_component *touch_active; - - struct border_component *focus; - struct border_component *grab; - - bool shadow_showing; - struct border_component shadow; - - GtkWidget *window; /* offscreen window for rendering */ - GtkWidget *header; /* header bar with widgets */ - struct border_component headerbar; - struct header_element_data hdr_focus; - - /* store pre-processed shadow tile */ - cairo_surface_t *shadow_blur; - - struct wl_list link; - - struct { - enum titlebar_gesture_state 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; -}; - -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; - - uint32_t color_scheme_setting; - - int double_click_time_ms; - int drag_threshold; - - bool handle_cursor; -}; - -static const char *libdecor_gtk_proxy_tag = "libdecor-gtk"; - -static bool -own_proxy(struct wl_proxy *proxy) -{ - if (!proxy) - return false; - - return (wl_proxy_get_tag(proxy) == &libdecor_gtk_proxy_tag); -} - -static bool -own_surface(struct wl_surface *surface) -{ - return own_proxy((struct wl_proxy *) surface); -} - -static bool -own_output(struct wl_output *output) -{ - return own_proxy((struct wl_proxy *) output); -} - -static bool -moveable(struct libdecor_frame_gtk *frame_gtk) { - return libdecor_frame_has_capability(&frame_gtk->frame, - LIBDECOR_ACTION_MOVE); -} - -static bool -resizable(struct libdecor_frame_gtk *frame_gtk) { - return libdecor_frame_has_capability(&frame_gtk->frame, - LIBDECOR_ACTION_RESIZE); -} - -static bool -minimizable(struct libdecor_frame_gtk *frame_gtk) { - return libdecor_frame_has_capability(&frame_gtk->frame, - LIBDECOR_ACTION_MINIMIZE); -} - -static bool -closeable(struct libdecor_frame_gtk *frame_gtk) { - return libdecor_frame_has_capability(&frame_gtk->frame, - LIBDECOR_ACTION_CLOSE); -} - -static void -buffer_free(struct buffer *buffer); - -static void -draw_border_component(struct libdecor_frame_gtk *frame_gtk, - struct border_component *border_component, - enum component component); - -static void -send_cursor(struct seat *seat); - -static bool -update_local_cursor(struct seat *seat); - -static void -libdecor_plugin_gtk_destroy(struct libdecor_plugin *plugin) -{ - struct libdecor_plugin_gtk *plugin_gtk = - (struct libdecor_plugin_gtk *) plugin; - struct seat *seat, *seat_tmp; - struct output *output, *output_tmp; - struct libdecor_frame_gtk *frame, *frame_tmp; - - if (plugin_gtk->globals_callback) - wl_callback_destroy(plugin_gtk->globals_callback); - if (plugin_gtk->globals_callback_shm) - wl_callback_destroy(plugin_gtk->globals_callback_shm); - if (plugin_gtk->shm_callback) - wl_callback_destroy(plugin_gtk->shm_callback); - wl_registry_destroy(plugin_gtk->wl_registry); - - wl_list_for_each_safe(seat, seat_tmp, &plugin_gtk->seat_list, link) { - struct cursor_output *cursor_output, *tmp; - - if (seat->wl_pointer) - wl_pointer_destroy(seat->wl_pointer); - if (seat->wl_touch) - wl_touch_destroy(seat->wl_touch); - if (seat->cursor_surface) - wl_surface_destroy(seat->cursor_surface); - wl_seat_destroy(seat->wl_seat); - if (seat->cursor_theme) - wl_cursor_theme_destroy(seat->cursor_theme); - - wl_list_for_each_safe(cursor_output, tmp, &seat->cursor_outputs, link) { - wl_list_remove(&cursor_output->link); - free(cursor_output); - } - - free(seat->name); - free(seat); - } - - wl_list_for_each_safe(output, output_tmp, - &plugin_gtk->output_list, link) { - if (wl_output_get_version (output->wl_output) >= - WL_OUTPUT_RELEASE_SINCE_VERSION) - wl_output_release(output->wl_output); - else - wl_output_destroy(output->wl_output); - free(output); - } - - wl_list_for_each_safe(frame, frame_tmp, - &plugin_gtk->visible_frame_list, link) { - wl_list_remove(&frame->link); - } - - free(plugin_gtk->cursor_theme_name); - - if (plugin_gtk->wl_shm) - wl_shm_destroy(plugin_gtk->wl_shm); - - if (plugin_gtk->wl_compositor) - wl_compositor_destroy(plugin_gtk->wl_compositor); - if (plugin_gtk->wl_subcompositor) - wl_subcompositor_destroy(plugin_gtk->wl_subcompositor); - - libdecor_plugin_release(&plugin_gtk->plugin); - free(plugin_gtk); -} - -static struct libdecor_frame_gtk * -libdecor_frame_gtk_new(struct libdecor_plugin_gtk *plugin_gtk) -{ - struct libdecor_frame_gtk *frame_gtk = zalloc(sizeof *frame_gtk); - cairo_t *cr; - - static const int size = 128; - static const int boundary = 32; - - frame_gtk->plugin_gtk = plugin_gtk; - frame_gtk->shadow_blur = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, size, size); - wl_list_insert(&plugin_gtk->visible_frame_list, &frame_gtk->link); - - cr = cairo_create(frame_gtk->shadow_blur); - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - cairo_set_source_rgba(cr, 0, 0, 0, 1); - cairo_rectangle(cr, boundary, boundary, - size - 2 * boundary, - size - 2 * boundary); - cairo_fill(cr); - cairo_destroy(cr); - blur_surface(frame_gtk->shadow_blur, 64); - - return frame_gtk; -} - -static int -libdecor_plugin_gtk_get_fd(struct libdecor_plugin *plugin) -{ - struct libdecor_plugin_gtk *plugin_gtk = - (struct libdecor_plugin_gtk *) plugin; - struct wl_display *wl_display = - libdecor_get_wl_display(plugin_gtk->context); - - return wl_display_get_fd(wl_display); -} - -static int -libdecor_plugin_gtk_dispatch(struct libdecor_plugin *plugin, - int timeout) -{ - struct libdecor_plugin_gtk *plugin_gtk = - (struct libdecor_plugin_gtk *) plugin; - struct wl_display *wl_display = - libdecor_get_wl_display(plugin_gtk->context); - struct pollfd fds[1]; - int ret; - int dispatch_count = 0; - - while (g_main_context_iteration(NULL, FALSE)); - - while (wl_display_prepare_read(wl_display) != 0) - dispatch_count += wl_display_dispatch_pending(wl_display); - - if (wl_display_flush(wl_display) < 0 && - errno != EAGAIN) { - wl_display_cancel_read(wl_display); - return -errno; - } - - fds[0] = (struct pollfd) { wl_display_get_fd(wl_display), POLLIN }; - - ret = poll(fds, ARRAY_SIZE (fds), timeout); - if (ret > 0) { - if (fds[0].revents & POLLIN) { - wl_display_read_events(wl_display); - dispatch_count += wl_display_dispatch_pending(wl_display); - return dispatch_count; - } else { - wl_display_cancel_read(wl_display); - return dispatch_count; - } - } else if (ret == 0) { - wl_display_cancel_read(wl_display); - return dispatch_count; - } else { - wl_display_cancel_read(wl_display); - return -errno; - } -} - -static void -libdecor_plugin_gtk_set_handle_application_cursor(struct libdecor_plugin *plugin, - bool handle_cursor) -{ - struct libdecor_plugin_gtk *plugin_gtk = - (struct libdecor_plugin_gtk *) plugin; - - plugin_gtk->handle_cursor = handle_cursor; -} - -static struct libdecor_frame * -libdecor_plugin_gtk_frame_new(struct libdecor_plugin *plugin) -{ - struct libdecor_plugin_gtk *plugin_gtk = - (struct libdecor_plugin_gtk *) plugin; - struct libdecor_frame_gtk *frame_gtk; - - frame_gtk = libdecor_frame_gtk_new(plugin_gtk); - - return &frame_gtk->frame; -} - -static void -toggle_maximized(struct libdecor_frame *const frame) -{ - if (!resizable((struct libdecor_frame_gtk *)frame)) - return; - - if (!(libdecor_frame_get_window_state(frame) & - LIBDECOR_WINDOW_STATE_MAXIMIZED)) - libdecor_frame_set_maximized(frame); - else - libdecor_frame_unset_maximized(frame); -} - -static void -buffer_release(void *user_data, - struct wl_buffer *wl_buffer) -{ - struct buffer *buffer = user_data; - - if (buffer->is_detached) - buffer_free(buffer); - else - buffer->in_use = false; -} - -static const struct wl_buffer_listener buffer_listener = { - buffer_release -}; - -static struct buffer * -create_shm_buffer(struct libdecor_plugin_gtk *plugin_gtk, - int width, - int height, - bool opaque, - int scale) -{ - struct wl_shm_pool *pool; - int fd, size, buffer_width, buffer_height, stride; - void *data; - struct buffer *buffer; - enum wl_shm_format buf_fmt; - - buffer_width = width * scale; - buffer_height = height * scale; - stride = buffer_width * 4; - size = stride * buffer_height; - - fd = libdecor_os_create_anonymous_file(size); - if (fd < 0) { - fprintf(stderr, "creating a buffer file for %d B failed: %s\n", - size, strerror(errno)); - return NULL; - } - - data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (data == MAP_FAILED) { - fprintf(stderr, "mmap failed: %s\n", strerror(errno)); - close(fd); - return NULL; - } - - buf_fmt = opaque ? WL_SHM_FORMAT_XRGB8888 : WL_SHM_FORMAT_ARGB8888; - - pool = wl_shm_create_pool(plugin_gtk->wl_shm, fd, size); - buffer = zalloc(sizeof *buffer); - buffer->wl_buffer = wl_shm_pool_create_buffer(pool, 0, - buffer_width, buffer_height, - stride, - buf_fmt); - wl_buffer_add_listener(buffer->wl_buffer, &buffer_listener, buffer); - wl_shm_pool_destroy(pool); - close(fd); - - buffer->data = data; - buffer->data_size = size; - buffer->width = width; - buffer->height = height; - buffer->scale = scale; - buffer->buffer_width = buffer_width; - buffer->buffer_height = buffer_height; - - return buffer; -} - -static void -buffer_free(struct buffer *buffer) -{ - if (buffer->wl_buffer) { - wl_buffer_destroy(buffer->wl_buffer); - munmap(buffer->data, buffer->data_size); - buffer->wl_buffer = NULL; - buffer->in_use = false; - } - free(buffer); -} - -static void -free_border_component(struct border_component *border_component) -{ - if (border_component->wl_surface) { - wl_subsurface_destroy(border_component->wl_subsurface); - border_component->wl_subsurface = NULL; - wl_surface_destroy(border_component->wl_surface); - border_component->wl_surface = NULL; - } - if (border_component->buffer) { - buffer_free(border_component->buffer); - border_component->buffer = NULL; - } - if (border_component->output_list.next != NULL) { - struct surface_output *surface_output, *surface_output_tmp; - wl_list_for_each_safe(surface_output, surface_output_tmp, - &border_component->output_list, link) { - wl_list_remove(&surface_output->link); - free(surface_output); - } - } -} - -static void -libdecor_plugin_gtk_frame_free(struct libdecor_plugin *plugin, - struct libdecor_frame *frame) -{ - struct libdecor_frame_gtk *frame_gtk = - (struct libdecor_frame_gtk *) frame; - - g_clear_pointer (&frame_gtk->header, gtk_widget_destroy); - g_clear_pointer (&frame_gtk->window, gtk_widget_destroy); - - free_border_component(&frame_gtk->headerbar); - free_border_component(&frame_gtk->shadow); - frame_gtk->shadow_showing = false; - - g_clear_pointer (&frame_gtk->shadow_blur, cairo_surface_destroy); - - g_clear_pointer (&frame_gtk->title, free); - - frame_gtk->decoration_type = DECORATION_TYPE_NONE; - - if (frame_gtk->link.next != NULL) - wl_list_remove(&frame_gtk->link); -} - -static bool -is_border_surfaces_showing(struct libdecor_frame_gtk *frame_gtk) -{ - return frame_gtk->shadow_showing; -} - -static void -hide_border_component(struct border_component *border_component) -{ - if (!border_component->wl_surface) - return; - - wl_surface_attach(border_component->wl_surface, NULL, 0, 0); - wl_surface_commit(border_component->wl_surface); -} - -static void -hide_border_surfaces(struct libdecor_frame_gtk *frame_gtk) -{ - hide_border_component(&frame_gtk->shadow); - frame_gtk->shadow_showing = false; -} - -static struct border_component * -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; -} - -static bool -redraw_scale(struct libdecor_frame_gtk *frame_gtk, - struct border_component *cmpnt) -{ - struct surface_output *surface_output; - int scale = 1; - - if (cmpnt->wl_surface == NULL) - return false; - - wl_list_for_each(surface_output, &cmpnt->output_list, link) { - scale = MAX(scale, surface_output->output->scale); - } - if (scale != cmpnt->scale) { - cmpnt->scale = scale; - if ((frame_gtk->decoration_type != DECORATION_TYPE_NONE) && - ((cmpnt->type != SHADOW) || is_border_surfaces_showing(frame_gtk))) { - draw_border_component(frame_gtk, cmpnt, cmpnt->type); - return true; - } - } - return false; -} - -static bool -add_surface_output(struct libdecor_plugin_gtk *plugin_gtk, - struct wl_output *wl_output, - struct wl_list *list) -{ - struct output *output; - struct surface_output *surface_output; - - if (!own_output(wl_output)) - return false; - - output = wl_output_get_user_data(wl_output); - - if (output == NULL) - return false; - - surface_output = zalloc(sizeof *surface_output); - surface_output->output = output; - wl_list_insert(list, &surface_output->link); - return true; -} - -static void -surface_enter(void *data, - struct wl_surface *wl_surface, - struct wl_output *wl_output) -{ - struct libdecor_frame_gtk *frame_gtk = data; - struct border_component *cmpnt; - - if (!(own_surface(wl_surface) && own_output(wl_output))) - return; - - cmpnt = get_component_for_surface(frame_gtk, wl_surface); - if (cmpnt == NULL) - return; - - if (!add_surface_output(frame_gtk->plugin_gtk, wl_output, - &cmpnt->output_list)) - return; - - if (redraw_scale(frame_gtk, cmpnt)) - libdecor_frame_toplevel_commit(&frame_gtk->frame); -} - -static bool -remove_surface_output(struct wl_list *list, const struct wl_output *wl_output) -{ - struct surface_output *surface_output; - wl_list_for_each(surface_output, list, link) { - if (surface_output->output->wl_output == wl_output) { - wl_list_remove(&surface_output->link); - free(surface_output); - return true; - } - } - return false; -} - -static void -surface_leave(void *data, - struct wl_surface *wl_surface, - struct wl_output *wl_output) -{ - struct libdecor_frame_gtk *frame_gtk = data; - struct border_component *cmpnt; - - if (!(own_surface(wl_surface) && own_output(wl_output))) - return; - - cmpnt = get_component_for_surface(frame_gtk, wl_surface); - if (cmpnt == NULL) - return; - - if (!remove_surface_output(&cmpnt->output_list, wl_output)) - return; - - if (redraw_scale(frame_gtk, cmpnt)) - libdecor_frame_toplevel_commit(&frame_gtk->frame); -} - -static struct wl_surface_listener surface_listener = { - surface_enter, - surface_leave, -}; - -static void -create_surface_subsurface_pair(struct libdecor_frame_gtk *frame_gtk, - struct wl_surface **out_wl_surface, - struct wl_subsurface **out_wl_subsurface) -{ - struct libdecor_plugin_gtk *plugin_gtk = frame_gtk->plugin_gtk; - struct libdecor_frame *frame = &frame_gtk->frame; - struct wl_compositor *wl_compositor = plugin_gtk->wl_compositor; - struct wl_subcompositor *wl_subcompositor = plugin_gtk->wl_subcompositor; - struct wl_surface *wl_surface; - struct wl_surface *parent; - struct wl_subsurface *wl_subsurface; - - wl_surface = wl_compositor_create_surface(wl_compositor); - wl_proxy_set_tag((struct wl_proxy *) wl_surface, - &libdecor_gtk_proxy_tag); - - parent = libdecor_frame_get_wl_surface(frame); - wl_subsurface = wl_subcompositor_get_subsurface(wl_subcompositor, - wl_surface, - parent); - - *out_wl_surface = wl_surface; - *out_wl_subsurface = wl_subsurface; -} - -static void -ensure_component(struct libdecor_frame_gtk *frame_gtk, - struct border_component *cmpnt) -{ - if (!cmpnt->wl_surface) { - wl_list_init(&cmpnt->output_list); - cmpnt->scale = 1; - create_surface_subsurface_pair(frame_gtk, - &cmpnt->wl_surface, - &cmpnt->wl_subsurface); - wl_surface_add_listener(cmpnt->wl_surface, &surface_listener, - frame_gtk); - } -} - -static void -ensure_border_surfaces(struct libdecor_frame_gtk *frame_gtk) -{ - frame_gtk->shadow.type = SHADOW; - frame_gtk->shadow.opaque = false; - ensure_component(frame_gtk, &frame_gtk->shadow); -} - -static void -ensure_title_bar_surfaces(struct libdecor_frame_gtk *frame_gtk) -{ - GtkStyleContext *context_hdr; - - frame_gtk->headerbar.type = HEADER; - frame_gtk->headerbar.opaque = false; - ensure_component(frame_gtk, &frame_gtk->headerbar); - - if (frame_gtk->shadow.wl_surface) { - wl_subsurface_place_above(frame_gtk->headerbar.wl_subsurface, - frame_gtk->shadow.wl_surface); - } - - /* create an offscreen window with a header bar */ - /* TODO: This should only be done once at frame consutrction, but then - * the window and headerbar would not change style (e.g. backdrop) - * after construction. So we just destroy and re-create them. - */ - /* avoid warning when restoring previously turned off decoration */ - if (GTK_IS_WIDGET(frame_gtk->header)) { - gtk_widget_destroy(frame_gtk->header); - frame_gtk->header = NULL; - } - /* avoid warning when restoring previously turned off decoration */ - if (GTK_IS_WIDGET(frame_gtk->window)) { - gtk_widget_destroy(frame_gtk->window); - frame_gtk->window = NULL; - } - frame_gtk->window = gtk_offscreen_window_new(); - frame_gtk->header = gtk_header_bar_new(); - - g_object_get(gtk_widget_get_settings(frame_gtk->window), - "gtk-double-click-time", - &frame_gtk->plugin_gtk->double_click_time_ms, - "gtk-dnd-drag-threshold", - &frame_gtk->plugin_gtk->drag_threshold, - NULL); - /* set as "default" decoration */ - g_object_set(frame_gtk->header, - "title", libdecor_frame_get_title(&frame_gtk->frame), - "has-subtitle", FALSE, - "show-close-button", TRUE, - NULL); - - context_hdr = gtk_widget_get_style_context(frame_gtk->header); - gtk_style_context_add_class(context_hdr, GTK_STYLE_CLASS_TITLEBAR); - gtk_style_context_add_class(context_hdr, "default-decoration"); - - gtk_window_set_titlebar(GTK_WINDOW(frame_gtk->window), frame_gtk->header); - gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(frame_gtk->header), TRUE); - - gtk_window_set_resizable(GTK_WINDOW(frame_gtk->window), resizable(frame_gtk)); -} - -static void -calculate_component_size(struct libdecor_frame_gtk *frame_gtk, - enum component component, - int *component_x, - int *component_y, - int *component_width, - int *component_height) -{ - struct libdecor_frame *frame = &frame_gtk->frame; - int content_width, content_height; - - content_width = libdecor_frame_get_content_width(frame); - content_height = libdecor_frame_get_content_height(frame); - - /* avoid warning when restoring previously turned off decoration */ - const int title_height = - GTK_IS_WIDGET(frame_gtk->header) - ? gtk_widget_get_allocated_height(frame_gtk->header) : 0; - - switch (component) { - case NONE: - *component_width = 0; - *component_height = 0; - return; - case SHADOW: - *component_x = -(int)SHADOW_MARGIN; - *component_y = -(int)(SHADOW_MARGIN+title_height); - *component_width = content_width + 2 * SHADOW_MARGIN; - *component_height = content_height - + 2 * SHADOW_MARGIN - + title_height; - return; - case HEADER: - *component_x = 0; - /* reuse product of function call above */ - *component_y = - title_height; - *component_width = gtk_widget_get_allocated_width(frame_gtk->header); - /* reuse product of function call above */ - *component_height = title_height; - return; - } - - abort(); -} - -static void -array_append(enum header_element **array, size_t *n, enum header_element item) -{ - (*n)++; - *array = realloc(*array, (*n) * sizeof (enum header_element)); - (*array)[(*n)-1] = item; -} - -static void -draw_header_background(struct libdecor_frame_gtk *frame_gtk, - cairo_t *cr) -{ - /* background */ - GtkAllocation allocation; - GtkStyleContext* style; - - gtk_widget_get_allocation(GTK_WIDGET(frame_gtk->header), &allocation); - style = gtk_widget_get_style_context(frame_gtk->header); - gtk_render_background(style, cr, allocation.x, allocation.y, allocation.width, allocation.height); -} - -static void -draw_header_title(struct libdecor_frame_gtk *frame_gtk, - cairo_surface_t *surface) -{ - /* title */ - GtkWidget *label; - GtkAllocation allocation; - cairo_surface_t *label_surface = NULL; - cairo_t *cr; - - label = find_widget_by_type(frame_gtk->header, HEADER_TITLE).widget; - gtk_widget_get_allocation(label, &allocation); - - /* create subsection in which to draw label */ - label_surface = cairo_surface_create_for_rectangle( - surface, - allocation.x, allocation.y, - allocation.width, allocation.height); - cr = cairo_create(label_surface); - gtk_widget_size_allocate(label, &allocation); - gtk_widget_draw(label, cr); - cairo_destroy(cr); - cairo_surface_destroy(label_surface); -} - -static void -draw_header_button(struct libdecor_frame_gtk *frame_gtk, - cairo_t *cr, - cairo_surface_t *surface, - enum header_element button_type, - enum libdecor_window_state window_state) -{ - struct header_element_data elem; - GtkWidget *button; - GtkStyleContext* button_style; - GtkStateFlags style_state; - - GtkAllocation allocation; - - gchar *icon_name; - int scale; - GtkWidget *icon_widget; - GtkAllocation allocation_icon; - GtkIconInfo* icon_info; - - double sx, sy; - - gint icon_width, icon_height; - - GdkPixbuf* icon_pixbuf; - cairo_surface_t* icon_surface; - - gint width = 0, height = 0; - - gint left = 0, top = 0, right = 0, bottom = 0; - GtkBorder border; - - GtkBorder padding; - - elem = find_widget_by_type(frame_gtk->header, button_type); - button = elem.widget; - if (!button) - return; - button_style = gtk_widget_get_style_context(button); - style_state = elem.state; - - /* change style based on window state and focus */ - if (!(window_state & LIBDECOR_WINDOW_STATE_ACTIVE)) { - style_state |= GTK_STATE_FLAG_BACKDROP; - } - if (frame_gtk->hdr_focus.widget == button) { - style_state |= GTK_STATE_FLAG_PRELIGHT; - if (frame_gtk->hdr_focus.state & GTK_STATE_FLAG_ACTIVE) { - style_state |= GTK_STATE_FLAG_ACTIVE; - } - } - - /* background */ - gtk_widget_get_clip(button, &allocation); - - gtk_style_context_save(button_style); - gtk_style_context_set_state(button_style, style_state); - gtk_render_background(button_style, cr, - allocation.x, allocation.y, - allocation.width, allocation.height); - gtk_render_frame(button_style, cr, - allocation.x, allocation.y, - allocation.width, allocation.height); - gtk_style_context_restore(button_style); - - /* symbol */ - switch (button_type) { - case HEADER_MIN: - icon_name = "window-minimize-symbolic"; - break; - case HEADER_MAX: - icon_name = (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) ? - "window-restore-symbolic" : - "window-maximize-symbolic"; - break; - case HEADER_CLOSE: - icon_name = "window-close-symbolic"; - break; - default: - icon_name = NULL; - break; - } - - /* get scale */ - cairo_surface_get_device_scale(surface, &sx, &sy); - scale = (sx+sy) / 2.0; - - /* get original icon dimensions */ - icon_widget = gtk_bin_get_child(GTK_BIN(button)); - gtk_widget_get_allocation(icon_widget, &allocation_icon); - - /* icon info */ - if (!gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &icon_width, &icon_height)) { - icon_width = 16; - icon_height = 16; - } - icon_info = gtk_icon_theme_lookup_icon_for_scale( - gtk_icon_theme_get_default(), icon_name, - icon_width, scale, (GtkIconLookupFlags)0); - - /* icon pixel buffer*/ - gtk_style_context_save(button_style); - gtk_style_context_set_state(button_style, style_state); - icon_pixbuf = gtk_icon_info_load_symbolic_for_context( - icon_info, button_style, NULL, NULL); - icon_surface = gdk_cairo_surface_create_from_pixbuf(icon_pixbuf, scale, NULL); - gtk_style_context_restore(button_style); - - /* dimensions and position */ - gtk_style_context_get(button_style, gtk_style_context_get_state(button_style), - "min-width", &width, "min-height", &height, NULL); - - if (width < icon_width) - width = icon_width; - if (height < icon_height) - height = icon_height; - - gtk_style_context_get_border(button_style, gtk_style_context_get_state(button_style), &border); - left += border.left; - right += border.right; - top += border.top; - bottom += border.bottom; - - gtk_style_context_get_padding(button_style, gtk_style_context_get_state(button_style), &padding); - left += padding.left; - right += padding.right; - top += padding.top; - bottom += padding.bottom; - - width += left + right; - height += top + bottom; - - gtk_render_icon_surface(gtk_widget_get_style_context(icon_widget), - cr, icon_surface, - allocation.x + ((width - icon_width) / 2), - allocation.y + ((height - icon_height) / 2)); - cairo_paint(cr); - cairo_surface_destroy(icon_surface); - g_object_unref(icon_pixbuf); -} - -static void -draw_header_buttons(struct libdecor_frame_gtk *frame_gtk, - cairo_t *cr, - cairo_surface_t *surface) -{ - /* buttons */ - enum libdecor_window_state window_state; - enum header_element *buttons = NULL; - size_t nbuttons = 0; - - window_state = libdecor_frame_get_window_state( - (struct libdecor_frame*)frame_gtk); - - /* set buttons by capability */ - if (minimizable(frame_gtk)) - array_append(&buttons, &nbuttons, HEADER_MIN); - if (resizable(frame_gtk)) - array_append(&buttons, &nbuttons, HEADER_MAX); - if (closeable(frame_gtk)) - array_append(&buttons, &nbuttons, HEADER_CLOSE); - - for (size_t i = 0; i < nbuttons; i++) { - draw_header_button(frame_gtk, cr, surface, buttons[i], window_state); - } /* loop buttons */ - free(buttons); -} - -static void -draw_header(struct libdecor_frame_gtk *frame_gtk, - cairo_t *cr, - cairo_surface_t *surface) -{ - draw_header_background(frame_gtk, cr); - draw_header_title(frame_gtk, surface); - draw_header_buttons(frame_gtk, cr, surface); -} - -static void -draw_component_content(struct libdecor_frame_gtk *frame_gtk, - struct buffer *buffer, - int component_width, - int component_height, - enum component component) -{ - cairo_surface_t *surface; - cairo_t *cr; - - /* clear buffer */ - memset(buffer->data, 0, buffer->data_size); - - surface = cairo_image_surface_create_for_data( - buffer->data, CAIRO_FORMAT_ARGB32, - buffer->buffer_width, buffer->buffer_height, - cairo_format_stride_for_width( - CAIRO_FORMAT_ARGB32, - buffer->buffer_width) - ); - - cr = cairo_create(surface); - - cairo_surface_set_device_scale(surface, buffer->scale, buffer->scale); - - /* background */ - switch (component) { - case NONE: - break; - case SHADOW: - render_shadow(cr, - frame_gtk->shadow_blur, - -(int)SHADOW_MARGIN/2, - -(int)SHADOW_MARGIN/2, - buffer->width + SHADOW_MARGIN, - buffer->height + SHADOW_MARGIN, - 64, - 64); - break; - case HEADER: - draw_header(frame_gtk, cr, surface); - break; - } - - /* mask the toplevel surface */ - if (component == SHADOW) { - int component_x, component_y, component_width, component_height; - calculate_component_size(frame_gtk, component, - &component_x, &component_y, - &component_width, &component_height); - cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); - cairo_rectangle(cr, -component_x, -component_y, - libdecor_frame_get_content_width( - &frame_gtk->frame), - libdecor_frame_get_content_height( - &frame_gtk->frame)); - cairo_fill(cr); - } - - cairo_destroy(cr); - cairo_surface_destroy(surface); -} - -static void -set_component_input_region(struct libdecor_frame_gtk *frame_gtk, - struct border_component *border_component) -{ - if (border_component->type == SHADOW && frame_gtk->shadow_showing) { - struct wl_region *input_region; - int component_x; - int component_y; - int component_width; - int component_height; - - calculate_component_size(frame_gtk, border_component->type, - &component_x, &component_y, - &component_width, &component_height); - - /* - * the input region is the outer surface size minus the inner - * content size - */ - input_region = wl_compositor_create_region( - frame_gtk->plugin_gtk->wl_compositor); - wl_region_add(input_region, 0, 0, - component_width, component_height); - wl_region_subtract(input_region, -component_x, -component_y, - libdecor_frame_get_content_width(&frame_gtk->frame), - libdecor_frame_get_content_height(&frame_gtk->frame)); - wl_surface_set_input_region(border_component->wl_surface, - input_region); - wl_region_destroy(input_region); - } -} - -static void -draw_border_component(struct libdecor_frame_gtk *frame_gtk, - struct border_component *border_component, - enum component component) -{ - struct libdecor_plugin_gtk *plugin_gtk = frame_gtk->plugin_gtk; - struct buffer *old_buffer; - struct buffer *buffer = NULL; - int component_x; - int component_y; - int component_width; - int component_height; - int scale = border_component->scale; - - if (border_component->wl_surface == NULL) - return; - - calculate_component_size(frame_gtk, component, - &component_x, &component_y, - &component_width, &component_height); - - set_component_input_region(frame_gtk, border_component); - - old_buffer = border_component->buffer; - if (old_buffer) { - if (!old_buffer->in_use && - old_buffer->buffer_width == component_width * scale && - old_buffer->buffer_height == component_height * scale) { - buffer = old_buffer; - } else { - buffer_free(old_buffer); - border_component->buffer = NULL; - } - } - - if (!buffer) - buffer = create_shm_buffer(plugin_gtk, - component_width, - component_height, - border_component->opaque, - border_component->scale); - - draw_component_content(frame_gtk, buffer, - component_width, component_height, - component); - - wl_surface_attach(border_component->wl_surface, buffer->wl_buffer, 0, 0); - wl_surface_set_buffer_scale(border_component->wl_surface, buffer->scale); - buffer->in_use = true; - wl_surface_commit(border_component->wl_surface); - wl_surface_damage_buffer(border_component->wl_surface, 0, 0, - component_width * scale, - component_height * scale); - wl_subsurface_set_position(border_component->wl_subsurface, - component_x, component_y); - - border_component->buffer = buffer; -} - -static void -draw_border(struct libdecor_frame_gtk *frame_gtk) -{ - draw_border_component(frame_gtk, &frame_gtk->shadow, SHADOW); - frame_gtk->shadow_showing = true; -} - -static void -draw_title_bar(struct libdecor_frame_gtk *frame_gtk) -{ - GtkAllocation allocation = {0, 0, frame_gtk->content_width, 0}; - enum libdecor_window_state state; - GtkStyleContext *style; - int pref_width; - int current_min_w, current_min_h, current_max_w, current_max_h, W, H; - - state = libdecor_frame_get_window_state((struct libdecor_frame*)frame_gtk); - style = gtk_widget_get_style_context(frame_gtk->window); - - if (!(state & LIBDECOR_WINDOW_STATE_ACTIVE)) { - gtk_widget_set_state_flags(frame_gtk->window, GTK_STATE_FLAG_BACKDROP, true); - } else { - gtk_widget_unset_state_flags(frame_gtk->window, GTK_STATE_FLAG_BACKDROP); - } - - if (libdecor_frame_is_floating(&frame_gtk->frame)) { - gtk_style_context_remove_class(style, "maximized"); - } else { - gtk_style_context_add_class(style, "maximized"); - } - - gtk_widget_show_all(frame_gtk->window); - - /* set default width, using an empty title to estimate its smallest admissible value */ - gtk_header_bar_set_title(GTK_HEADER_BAR(frame_gtk->header), ""); - gtk_widget_get_preferred_width(frame_gtk->header, NULL, &pref_width); - gtk_header_bar_set_title(GTK_HEADER_BAR(frame_gtk->header), - libdecor_frame_get_title(&frame_gtk->frame)); - libdecor_frame_get_min_content_size(&frame_gtk->frame, ¤t_min_w, ¤t_min_h); - if (current_min_w < pref_width) { - current_min_w = pref_width; - libdecor_frame_set_min_content_size(&frame_gtk->frame, current_min_w, current_min_h); - } - libdecor_frame_get_max_content_size(&frame_gtk->frame, ¤t_max_w, ¤t_max_h); - if (current_max_w && current_max_w < current_min_w) { - libdecor_frame_set_max_content_size(&frame_gtk->frame, current_min_w, current_max_h); - } - W = libdecor_frame_get_content_width(&frame_gtk->frame); - H = libdecor_frame_get_content_height(&frame_gtk->frame); - if (W < current_min_w) { - W = current_min_w; - struct libdecor_state *libdecor_state = libdecor_state_new(W, H); - libdecor_frame_commit(&frame_gtk->frame, libdecor_state, NULL); - libdecor_state_free(libdecor_state); - return; - } - /* set default height */ - gtk_widget_get_preferred_height(frame_gtk->header, NULL, &allocation.height); - - gtk_widget_size_allocate(frame_gtk->header, &allocation); - - draw_border_component(frame_gtk, &frame_gtk->headerbar, HEADER); -} - -static void -draw_decoration(struct libdecor_frame_gtk *frame_gtk) -{ - switch (frame_gtk->decoration_type) { - case DECORATION_TYPE_NONE: - if (frame_gtk->link.next != NULL) - wl_list_remove(&frame_gtk->link); - if (is_border_surfaces_showing(frame_gtk)) - hide_border_surfaces(frame_gtk); - hide_border_component(&frame_gtk->headerbar); - break; - case DECORATION_TYPE_ALL: - /* show borders */ - ensure_border_surfaces(frame_gtk); - draw_border(frame_gtk); - /* show title bar */ - ensure_title_bar_surfaces(frame_gtk); - draw_title_bar(frame_gtk); - /* link frame */ - if (frame_gtk->link.next == NULL) - wl_list_insert( - &frame_gtk->plugin_gtk->visible_frame_list, - &frame_gtk->link); - break; - case DECORATION_TYPE_TITLE_ONLY: - /* hide borders */ - if (is_border_surfaces_showing(frame_gtk)) - hide_border_surfaces(frame_gtk); - /* show title bar */ - ensure_title_bar_surfaces(frame_gtk); - draw_title_bar(frame_gtk); - /* link frame */ - if (frame_gtk->link.next == NULL) - wl_list_insert( - &frame_gtk->plugin_gtk->visible_frame_list, - &frame_gtk->link); - break; - } -} - -static enum decoration_type -window_state_to_decoration_type(enum libdecor_window_state window_state) -{ - if (window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN) - return DECORATION_TYPE_NONE; - else if (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED || - window_state & LIBDECOR_WINDOW_STATE_TILED_LEFT || - window_state & LIBDECOR_WINDOW_STATE_TILED_RIGHT || - window_state & LIBDECOR_WINDOW_STATE_TILED_TOP || - window_state & LIBDECOR_WINDOW_STATE_TILED_BOTTOM) - /* title bar, no shadows */ - return DECORATION_TYPE_TITLE_ONLY; - else - /* title bar, shadows */ - return DECORATION_TYPE_ALL; -} - -static void -libdecor_plugin_gtk_frame_commit(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - struct libdecor_state *state, - struct libdecor_configuration *configuration) -{ - struct libdecor_frame_gtk *frame_gtk = - (struct libdecor_frame_gtk *) frame; - enum libdecor_window_state old_window_state; - enum libdecor_window_state new_window_state; - int old_content_width, old_content_height; - int new_content_width, new_content_height; - enum decoration_type old_decoration_type; - enum decoration_type new_decoration_type; - - old_window_state = frame_gtk->window_state; - new_window_state = libdecor_frame_get_window_state(frame); - - old_content_width = frame_gtk->content_width; - old_content_height = frame_gtk->content_height; - new_content_width = libdecor_frame_get_content_width(frame); - new_content_height = libdecor_frame_get_content_height(frame); - - old_decoration_type = frame_gtk->decoration_type; - new_decoration_type = window_state_to_decoration_type(new_window_state); - - if (old_decoration_type == new_decoration_type && - old_content_width == new_content_width && - old_content_height == new_content_height && - old_window_state == new_window_state) - return; - - frame_gtk->content_width = new_content_width; - frame_gtk->content_height = new_content_height; - frame_gtk->window_state = new_window_state; - frame_gtk->decoration_type = new_decoration_type; - - draw_decoration(frame_gtk); - - /* set fixed window size */ - if (!resizable(frame_gtk)) { - libdecor_frame_set_min_content_size(frame, - frame_gtk->content_width, - frame_gtk->content_height); - libdecor_frame_set_max_content_size(frame, - frame_gtk->content_width, - frame_gtk->content_height); - } -} - -static void -libdecor_plugin_gtk_frame_property_changed(struct libdecor_plugin *plugin, - struct libdecor_frame *frame) -{ - struct libdecor_frame_gtk *frame_gtk = - (struct libdecor_frame_gtk *) frame; - bool redraw_needed = false; - const char *new_title; - - /* - * when in SSD mode, the window title is not to be managed by GTK; - * this is detected by frame_gtk->header not being a proper GTK widget - */ - if (!GTK_IS_WIDGET(frame_gtk->header)) return; - - new_title = libdecor_frame_get_title(frame); - if (!streq(frame_gtk->title, new_title)) - redraw_needed = true; - free(frame_gtk->title); - frame_gtk->title = NULL; - if (new_title) - frame_gtk->title = strdup(new_title); - - if (frame_gtk->capabilities != libdecor_frame_get_capabilities(frame)) { - frame_gtk->capabilities = libdecor_frame_get_capabilities(frame); - redraw_needed = true; - } - - if (redraw_needed) { - draw_decoration(frame_gtk); - libdecor_frame_toplevel_commit(frame); - } -} - -static void -update_component_focus(struct libdecor_frame_gtk *frame_gtk, - struct wl_surface *surface, - struct seat *seat) -{ - static struct border_component *border_component; - static struct border_component *child_component; - static struct border_component *focus_component; - - border_component = get_component_for_surface(frame_gtk, surface); - - focus_component = border_component; - wl_list_for_each(child_component, &border_component->child_components, link) { - int component_x = 0, component_y = 0; - int component_width = 0, component_height = 0; - - calculate_component_size(frame_gtk, child_component->type, - &component_x, &component_y, - &component_width, &component_height); - if (seat->pointer_x >= component_x && - seat->pointer_x < component_x + component_width && - seat->pointer_y >= component_y && - seat->pointer_y < component_y + component_height) { - focus_component = child_component; - break; - } - } - - if (frame_gtk->grab) - frame_gtk->active = frame_gtk->grab; - else - frame_gtk->active = focus_component; - frame_gtk->focus = focus_component; - -} - -static void -sync_active_component(struct libdecor_frame_gtk *frame_gtk, - struct seat *seat) -{ - struct border_component *old_active; - - if (!seat->pointer_focus) - return; - - old_active = frame_gtk->active; - update_component_focus(frame_gtk, seat->pointer_focus, seat); - if (old_active != frame_gtk->active) { - draw_decoration(frame_gtk); - libdecor_frame_toplevel_commit(&frame_gtk->frame); - } - - if (update_local_cursor(seat)) - send_cursor(seat); -} - -static void -synthesize_pointer_enter(struct seat *seat) -{ - struct wl_surface *surface; - struct libdecor_frame_gtk *frame_gtk; - - surface = seat->pointer_focus; - if (!surface || !own_surface(surface)) - return; - - frame_gtk = wl_surface_get_user_data(surface); - if (!frame_gtk) - return; - - update_component_focus(frame_gtk, seat->pointer_focus, seat); - frame_gtk->grab = NULL; - - /* update decorations */ - if (frame_gtk->active) { - draw_decoration(frame_gtk); - libdecor_frame_toplevel_commit(&frame_gtk->frame); - } - - update_local_cursor(seat); - send_cursor(seat); -} - -static void -synthesize_pointer_leave(struct seat *seat) -{ - struct wl_surface *surface; - struct libdecor_frame_gtk *frame_gtk; - - surface = seat->pointer_focus; - if (!surface || !own_surface(surface)) - return; - - frame_gtk = wl_surface_get_user_data(surface); - if (!frame_gtk) - return; - - if (!frame_gtk->active) - return; - - frame_gtk->active = NULL; - draw_decoration(frame_gtk); - libdecor_frame_toplevel_commit(&frame_gtk->frame); - update_local_cursor(seat); -} - -static void -libdecor_plugin_gtk_frame_popup_grab(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - const char *seat_name) -{ - struct libdecor_frame_gtk *frame_gtk = - (struct libdecor_frame_gtk *) frame; - struct libdecor_plugin_gtk *plugin_gtk = frame_gtk->plugin_gtk; - struct seat *seat; - - wl_list_for_each(seat, &plugin_gtk->seat_list, link) { - if (streq(seat->name, seat_name)) { - if (seat->grabbed) { - fprintf(stderr, "libdecor-WARNING: Application " - "tried to grab seat twice\n"); - } - synthesize_pointer_leave(seat); - seat->grabbed = true; - return; - } - } - - fprintf(stderr, - "libdecor-WARNING: Application tried to grab unknown seat\n"); -} - -static void -libdecor_plugin_gtk_frame_popup_ungrab(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - const char *seat_name) -{ - struct libdecor_frame_gtk *frame_gtk = - (struct libdecor_frame_gtk *) frame; - struct libdecor_plugin_gtk *plugin_gtk = frame_gtk->plugin_gtk; - struct seat *seat; - - wl_list_for_each(seat, &plugin_gtk->seat_list, link) { - if (streq(seat->name, seat_name)) { - if (!seat->grabbed) { - fprintf(stderr, "libdecor-WARNING: Application " - "tried to ungrab seat twice\n"); - } - seat->grabbed = false; - synthesize_pointer_enter(seat); - sync_active_component(frame_gtk, seat); - return; - } - } - - fprintf(stderr, - "libdecor-WARNING: Application tried to ungrab unknown seat\n"); -} - -static bool -libdecor_plugin_gtk_frame_get_border_size(struct libdecor_plugin *plugin, - struct libdecor_frame *frame, - struct libdecor_configuration *configuration, - int *left, - int *right, - int *top, - int *bottom) -{ - struct libdecor_frame_gtk *frame_gtk = - (struct libdecor_frame_gtk *) frame; - enum libdecor_window_state window_state; - - if (configuration) { - if (!libdecor_configuration_get_window_state( - configuration, &window_state)) - return false; - } else { - window_state = libdecor_frame_get_window_state(frame); - } - - if (left) - *left = 0; - if (right) - *right = 0; - if (bottom) - *bottom = 0; - if (top) { - enum decoration_type type = window_state_to_decoration_type(window_state); - - switch (type) { - case DECORATION_TYPE_NONE: - *top = 0; - break; - case DECORATION_TYPE_ALL: - ensure_border_surfaces(frame_gtk); - //draw_border(frame_gtk); - G_GNUC_FALLTHROUGH; - case DECORATION_TYPE_TITLE_ONLY: - if (!frame_gtk->header) - ensure_title_bar_surfaces(frame_gtk); - gtk_widget_show_all(frame_gtk->window); - gtk_widget_get_preferred_height(frame_gtk->header, NULL, top); - break; - } - } - - return true; -} - -static struct libdecor_plugin_interface gtk_plugin_iface = { - .destroy = libdecor_plugin_gtk_destroy, - .get_fd = libdecor_plugin_gtk_get_fd, - .dispatch = libdecor_plugin_gtk_dispatch, - - .set_handle_application_cursor = libdecor_plugin_gtk_set_handle_application_cursor, - - .frame_new = libdecor_plugin_gtk_frame_new, - .frame_free = libdecor_plugin_gtk_frame_free, - .frame_commit = libdecor_plugin_gtk_frame_commit, - .frame_property_changed = libdecor_plugin_gtk_frame_property_changed, - - .frame_popup_grab = libdecor_plugin_gtk_frame_popup_grab, - .frame_popup_ungrab = libdecor_plugin_gtk_frame_popup_ungrab, - - .frame_get_border_size = libdecor_plugin_gtk_frame_get_border_size, -}; - -static void -init_wl_compositor(struct libdecor_plugin_gtk *plugin_gtk, - uint32_t id, - uint32_t version) -{ - plugin_gtk->wl_compositor = - wl_registry_bind(plugin_gtk->wl_registry, - id, &wl_compositor_interface, - MIN(version, 4)); -} - -static void -init_wl_subcompositor(struct libdecor_plugin_gtk *plugin_gtk, - uint32_t id, - uint32_t version) -{ - plugin_gtk->wl_subcompositor = - wl_registry_bind(plugin_gtk->wl_registry, - id, &wl_subcompositor_interface, 1); -} - -static void -shm_format(void *user_data, - struct wl_shm *wl_shm, - uint32_t format) -{ - struct libdecor_plugin_gtk *plugin_gtk = user_data; - - if (format == WL_SHM_FORMAT_ARGB8888) - plugin_gtk->has_argb = true; -} - -struct wl_shm_listener shm_listener = { - shm_format -}; - -static void -shm_callback(void *user_data, - struct wl_callback *callback, - uint32_t time) -{ - struct libdecor_plugin_gtk *plugin_gtk = user_data; - struct libdecor *context = plugin_gtk->context; - - wl_callback_destroy(callback); - plugin_gtk->globals_callback_shm = NULL; - - if (!plugin_gtk->has_argb) { - libdecor_notify_plugin_error( - context, - LIBDECOR_ERROR_COMPOSITOR_INCOMPATIBLE, - "Compositor is missing required shm format"); - return; - } - - libdecor_notify_plugin_ready(context); -} - -static const struct wl_callback_listener shm_callback_listener = { - shm_callback -}; - -static void -init_wl_shm(struct libdecor_plugin_gtk *plugin_gtk, - uint32_t id, - uint32_t version) -{ - struct libdecor *context = plugin_gtk->context; - struct wl_display *wl_display = libdecor_get_wl_display(context); - - plugin_gtk->wl_shm = - wl_registry_bind(plugin_gtk->wl_registry, - id, &wl_shm_interface, 1); - wl_shm_add_listener(plugin_gtk->wl_shm, &shm_listener, plugin_gtk); - - plugin_gtk->globals_callback_shm = wl_display_sync(wl_display); - wl_callback_add_listener(plugin_gtk->globals_callback_shm, - &shm_callback_listener, - plugin_gtk); -} - -static void -cursor_surface_enter(void *data, - struct wl_surface *wl_surface, - struct wl_output *wl_output) -{ - struct seat *seat = data; - - if (own_output(wl_output)) { - struct cursor_output *cursor_output; - cursor_output = zalloc(sizeof *cursor_output); - cursor_output->output = wl_output_get_user_data(wl_output); - wl_list_insert(&seat->cursor_outputs, &cursor_output->link); - if (update_local_cursor(seat)) - send_cursor(seat); - } -} - -static void -cursor_surface_leave(void *data, - struct wl_surface *wl_surface, - struct wl_output *wl_output) -{ - struct seat *seat = data; - - if (own_output(wl_output)) { - struct cursor_output *cursor_output, *tmp; - wl_list_for_each_safe(cursor_output, tmp, &seat->cursor_outputs, link) { - if (cursor_output->output->wl_output == wl_output) { - wl_list_remove(&cursor_output->link); - free(cursor_output); - } - } - - if (update_local_cursor(seat)) - send_cursor(seat); - } -} - -static struct wl_surface_listener cursor_surface_listener = { - cursor_surface_enter, - cursor_surface_leave, -}; - -static void -ensure_cursor_surface(struct seat *seat) -{ - struct wl_compositor *wl_compositor = seat->plugin_gtk->wl_compositor; - - if (seat->cursor_surface) - return; - - seat->cursor_surface = wl_compositor_create_surface(wl_compositor); - wl_surface_add_listener(seat->cursor_surface, - &cursor_surface_listener, seat); -} - -static bool -ensure_cursor_theme(struct seat *seat) -{ - struct libdecor_plugin_gtk *plugin_gtk = seat->plugin_gtk; - int scale = 1; - struct wl_cursor_theme *theme; - struct cursor_output *cursor_output; - - wl_list_for_each(cursor_output, &seat->cursor_outputs, link) { - scale = MAX(scale, cursor_output->output->scale); - } - - if (seat->cursor_theme && seat->cursor_scale == scale) - return false; - - seat->cursor_scale = scale; - theme = wl_cursor_theme_load(plugin_gtk->cursor_theme_name, - plugin_gtk->cursor_size * scale, - plugin_gtk->wl_shm); - if (theme == NULL) - return false; - - if (seat->cursor_theme) - wl_cursor_theme_destroy(seat->cursor_theme); - - seat->cursor_theme = theme; - - for (unsigned int i = 0; i < ARRAY_LENGTH(cursor_names); i++) { - seat->cursors[i] = wl_cursor_theme_get_cursor( - seat->cursor_theme, - cursor_names[i]); - } - - seat->cursor_left_ptr = wl_cursor_theme_get_cursor(seat->cursor_theme, - "left_ptr"); - seat->current_cursor = seat->cursor_left_ptr; - - return true; -} - -enum libdecor_resize_edge -component_edge(const struct border_component *cmpnt, - const int pointer_x, - const int pointer_y, - const int margin) -{ - const bool top = pointer_y < margin * 2; - const bool bottom = pointer_y > (cmpnt->buffer->height - margin * 2); - const bool left = pointer_x < margin * 2; - const bool right = pointer_x > (cmpnt->buffer->width - margin * 2); - - if (top) { - if (left) - return LIBDECOR_RESIZE_EDGE_TOP_LEFT; - else if (right) - return LIBDECOR_RESIZE_EDGE_TOP_RIGHT; - else - return LIBDECOR_RESIZE_EDGE_TOP; - } else if (bottom) { - if (left) - return LIBDECOR_RESIZE_EDGE_BOTTOM_LEFT; - else if (right) - return LIBDECOR_RESIZE_EDGE_BOTTOM_RIGHT; - else - return LIBDECOR_RESIZE_EDGE_BOTTOM; - } else if (left) { - return LIBDECOR_RESIZE_EDGE_LEFT; - } else if (right) { - return LIBDECOR_RESIZE_EDGE_RIGHT; - } else { - return LIBDECOR_RESIZE_EDGE_NONE; - } -} - -static bool -update_local_cursor(struct seat *seat) -{ - if (!seat->pointer_focus) { - seat->current_cursor = seat->cursor_left_ptr; - return false; - } - - if (!own_surface(seat->pointer_focus)) - return false; - - struct libdecor_frame_gtk *frame_gtk = - wl_surface_get_user_data(seat->pointer_focus); - struct wl_cursor *wl_cursor = NULL; - - if (!frame_gtk || !frame_gtk->active) { - seat->current_cursor = seat->cursor_left_ptr; - return false; - } - - bool theme_updated = ensure_cursor_theme(seat); - - if (frame_gtk->active->type == SHADOW && - is_border_surfaces_showing(frame_gtk) && - resizable(frame_gtk)) { - enum libdecor_resize_edge edge; - edge = component_edge(frame_gtk->active, - seat->pointer_x, - seat->pointer_y, SHADOW_MARGIN); - - if (edge != LIBDECOR_RESIZE_EDGE_NONE) - wl_cursor = seat->cursors[edge - 1]; - } else { - wl_cursor = seat->cursor_left_ptr; - } - - if (seat->current_cursor != wl_cursor) { - seat->current_cursor = wl_cursor; - return true; - } - - return theme_updated; -} - -static void -send_cursor(struct seat *seat) -{ - struct wl_cursor_image *image; - struct wl_buffer *buffer; - - if (seat->pointer_focus == NULL || seat->current_cursor == NULL) - return; - - image = seat->current_cursor->images[0]; - buffer = wl_cursor_image_get_buffer(image); - wl_surface_attach(seat->cursor_surface, buffer, 0, 0); - wl_surface_set_buffer_scale(seat->cursor_surface, seat->cursor_scale); - wl_surface_damage_buffer(seat->cursor_surface, 0, 0, - image->width * seat->cursor_scale, - image->height * seat->cursor_scale); - wl_surface_commit(seat->cursor_surface); - wl_pointer_set_cursor(seat->wl_pointer, seat->serial, - seat->cursor_surface, - image->hotspot_x / seat->cursor_scale, - image->hotspot_y / seat->cursor_scale); -} - -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) -{ - if (!surface) - return; - - struct seat *seat = data; - struct libdecor_frame_gtk *frame_gtk = NULL; - - if (!own_surface(surface)) { - struct seat *seat = wl_pointer_get_user_data(wl_pointer); - struct libdecor_plugin_gtk *plugin_gtk = seat->plugin_gtk; - - if (!plugin_gtk->handle_cursor) - return; - } else { - frame_gtk = wl_surface_get_user_data(surface); - } - - ensure_cursor_surface(seat); - - seat->pointer_x = wl_fixed_to_int(surface_x); - seat->pointer_y = wl_fixed_to_int(surface_y); - seat->serial = serial; - seat->pointer_focus = surface; - - if (!frame_gtk) - return; - - frame_gtk->active = get_component_for_surface(frame_gtk, surface); - - /* update decorations */ - if (frame_gtk->active) { - draw_decoration(frame_gtk); - libdecor_frame_toplevel_commit(&frame_gtk->frame); - } - - update_local_cursor(seat); - send_cursor(seat); -} - -static void -pointer_leave(void *data, - struct wl_pointer *wl_pointer, - uint32_t serial, - struct wl_surface *surface) -{ - struct seat *seat = data; - struct libdecor_frame_gtk *frame_gtk; - - seat->pointer_focus = NULL; - - if (!surface) - return; - - if (!own_surface(surface)) - return; - - frame_gtk = wl_surface_get_user_data(surface); - - if (frame_gtk) { - frame_gtk->titlebar_gesture.state = - TITLEBAR_GESTURE_STATE_INIT; - frame_gtk->titlebar_gesture.first_pressed_button = 0; - - frame_gtk->active = NULL; - frame_gtk->hdr_focus.widget = NULL; - frame_gtk->hdr_focus.type = HEADER_NONE; - draw_decoration(frame_gtk); - libdecor_frame_toplevel_commit(&frame_gtk->frame); - update_local_cursor(seat); - } -} - -static void -pointer_motion(void *data, - struct wl_pointer *wl_pointer, - uint32_t time, - wl_fixed_t surface_x, - wl_fixed_t surface_y) -{ - struct seat *seat = data; - struct libdecor_frame_gtk *frame_gtk; - struct header_element_data new_focus; - - if (!seat->pointer_focus || !own_surface(seat->pointer_focus)) - return; - - seat->pointer_x = wl_fixed_to_int(surface_x); - seat->pointer_y = wl_fixed_to_int(surface_y); - if (update_local_cursor(seat)) - send_cursor(seat); - - frame_gtk = wl_surface_get_user_data(seat->pointer_focus); - /* avoid warnings after decoration has been turned off */ - if (!GTK_IS_WIDGET(frame_gtk->header) || frame_gtk->active->type != HEADER) { - frame_gtk->hdr_focus.type = HEADER_NONE; - } - - new_focus = get_header_focus(GTK_HEADER_BAR(frame_gtk->header), - seat->pointer_x, seat->pointer_y); - - /* only update if widget change so that we keep the state */ - if (frame_gtk->hdr_focus.widget != new_focus.widget) { - frame_gtk->hdr_focus = new_focus; - } - frame_gtk->hdr_focus.state |= GTK_STATE_FLAG_PRELIGHT; - /* redraw with updated button visuals */ - draw_title_bar(frame_gtk); - libdecor_frame_toplevel_commit(&frame_gtk->frame); - - switch (frame_gtk->titlebar_gesture.state) { - case TITLEBAR_GESTURE_STATE_BUTTON_PRESSED: - if (frame_gtk->titlebar_gesture.first_pressed_button == BTN_LEFT) { - if (ABS ((double) seat->pointer_x - - (double) frame_gtk->titlebar_gesture.pressed_x) > - frame_gtk->plugin_gtk->drag_threshold || - ABS ((double) seat->pointer_y - - (double) frame_gtk->titlebar_gesture.pressed_y) > - frame_gtk->plugin_gtk->drag_threshold) { - libdecor_frame_move(&frame_gtk->frame, - seat->wl_seat, - frame_gtk->titlebar_gesture.pressed_serial); - } - } - case TITLEBAR_GESTURE_STATE_INIT: - case TITLEBAR_GESTURE_STATE_CONSUMED: - case TITLEBAR_GESTURE_STATE_DISCARDED: - break; - } -} - -static void -handle_button_on_shadow(struct libdecor_frame_gtk *frame_gtk, - struct seat *seat, - uint32_t serial, - uint32_t time, - uint32_t button, - uint32_t state) -{ - enum libdecor_resize_edge edge = LIBDECOR_RESIZE_EDGE_NONE; - - edge = component_edge(frame_gtk->active, - seat->pointer_x, - seat->pointer_y, - SHADOW_MARGIN); - - if (edge != LIBDECOR_RESIZE_EDGE_NONE && resizable(frame_gtk)) { - libdecor_frame_resize(&frame_gtk->frame, - seat->wl_seat, - serial, - edge); - } -} - -enum titlebar_gesture { - TITLEBAR_GESTURE_DOUBLE_CLICK, - TITLEBAR_GESTURE_MIDDLE_CLICK, - TITLEBAR_GESTURE_RIGHT_CLICK, -}; - -static void -handle_titlebar_gesture(struct libdecor_frame_gtk *frame_gtk, - struct seat *seat, - uint32_t serial, - enum titlebar_gesture gesture) -{ - switch (gesture) { - case TITLEBAR_GESTURE_DOUBLE_CLICK: - toggle_maximized(&frame_gtk->frame); - break; - case TITLEBAR_GESTURE_MIDDLE_CLICK: - break; - case TITLEBAR_GESTURE_RIGHT_CLICK: - { - const int title_height = gtk_widget_get_allocated_height(frame_gtk->header); - libdecor_frame_show_window_menu(&frame_gtk->frame, - seat->wl_seat, - serial, - seat->pointer_x, - seat->pointer_y - -title_height); - } - break; - } -} - -static void -handle_button_on_header(struct libdecor_frame_gtk *frame_gtk, - struct seat *seat, - uint32_t serial, - uint32_t time, - uint32_t button, - uint32_t state) -{ - switch (frame_gtk->titlebar_gesture.state) { - case TITLEBAR_GESTURE_STATE_INIT: - if (state != WL_POINTER_BUTTON_STATE_PRESSED) - return; - - if (button == BTN_RIGHT) { - handle_titlebar_gesture(frame_gtk, - seat, - serial, - TITLEBAR_GESTURE_RIGHT_CLICK); - frame_gtk->titlebar_gesture.state = - TITLEBAR_GESTURE_STATE_CONSUMED; - } else { - if (button == BTN_LEFT && - frame_gtk->titlebar_gesture.first_pressed_button == BTN_LEFT && - time - frame_gtk->titlebar_gesture.first_pressed_time < - (uint32_t) frame_gtk->plugin_gtk->double_click_time_ms) { - handle_titlebar_gesture(frame_gtk, - seat, - serial, - TITLEBAR_GESTURE_DOUBLE_CLICK); - frame_gtk->titlebar_gesture.state = - TITLEBAR_GESTURE_STATE_CONSUMED; - } else { - frame_gtk->titlebar_gesture.first_pressed_button = button; - frame_gtk->titlebar_gesture.first_pressed_time = time; - frame_gtk->titlebar_gesture.pressed_x = seat->pointer_x; - frame_gtk->titlebar_gesture.pressed_y = seat->pointer_y; - frame_gtk->titlebar_gesture.pressed_serial = serial; - frame_gtk->titlebar_gesture.state = - TITLEBAR_GESTURE_STATE_BUTTON_PRESSED; - } - } - - frame_gtk->titlebar_gesture.button_pressed_count = 1; - - switch (frame_gtk->hdr_focus.type) { - case HEADER_MIN: - case HEADER_MAX: - case HEADER_CLOSE: - frame_gtk->hdr_focus.state |= GTK_STATE_FLAG_ACTIVE; - draw_title_bar(frame_gtk); - libdecor_frame_toplevel_commit(&frame_gtk->frame); - break; - default: - break; - } - - break; - case TITLEBAR_GESTURE_STATE_BUTTON_PRESSED: - if (state == WL_POINTER_BUTTON_STATE_PRESSED) { - frame_gtk->titlebar_gesture.state = - TITLEBAR_GESTURE_STATE_DISCARDED; - frame_gtk->titlebar_gesture.button_pressed_count++; - } else { - frame_gtk->titlebar_gesture.button_pressed_count--; - - if (frame_gtk->titlebar_gesture.button_pressed_count == 0) { - frame_gtk->titlebar_gesture.state = - TITLEBAR_GESTURE_STATE_INIT; - if (frame_gtk->titlebar_gesture.first_pressed_button == button && - button == BTN_LEFT) { - libdecor_frame_ref(&frame_gtk->frame); - switch (frame_gtk->hdr_focus.type) { - case HEADER_MIN: - if (minimizable(frame_gtk)) - libdecor_frame_set_minimized( - &frame_gtk->frame); - break; - case HEADER_MAX: - toggle_maximized(&frame_gtk->frame); - break; - case HEADER_CLOSE: - if (closeable(frame_gtk)) { - libdecor_frame_close( - &frame_gtk->frame); - seat->pointer_focus = NULL; - } - break; - default: - break; - } - - frame_gtk->hdr_focus.state &= ~GTK_STATE_FLAG_ACTIVE; - if (GTK_IS_WIDGET(frame_gtk->header)) { - draw_title_bar(frame_gtk); - libdecor_frame_toplevel_commit(&frame_gtk->frame); - } - libdecor_frame_unref(&frame_gtk->frame); - } - } else { - frame_gtk->hdr_focus.state &= ~GTK_STATE_FLAG_ACTIVE; - if (GTK_IS_WIDGET(frame_gtk->header)) { - draw_title_bar(frame_gtk); - libdecor_frame_toplevel_commit(&frame_gtk->frame); - } - } - - } - break; - case TITLEBAR_GESTURE_STATE_CONSUMED: - case TITLEBAR_GESTURE_STATE_DISCARDED: - if (state == WL_POINTER_BUTTON_STATE_PRESSED) { - frame_gtk->titlebar_gesture.button_pressed_count++; - } else { - frame_gtk->titlebar_gesture.button_pressed_count--; - if (frame_gtk->titlebar_gesture.button_pressed_count == 0) { - frame_gtk->titlebar_gesture.state = - TITLEBAR_GESTURE_STATE_INIT; - frame_gtk->titlebar_gesture.first_pressed_button = 0; - } - } - break; - } -} - -static void -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; - - switch (frame_gtk->active->type) { - case SHADOW: - handle_button_on_shadow (frame_gtk, seat, serial, time, button, state); - break; - case HEADER: - handle_button_on_header (frame_gtk, seat, serial, time, button, state); - break; - default: - break; - } -} - -static void -pointer_axis(void *data, - struct wl_pointer *wl_pointer, - uint32_t time, - uint32_t axis, - wl_fixed_t value) -{ -} - -static struct wl_pointer_listener pointer_listener = { - pointer_enter, - pointer_leave, - pointer_motion, - pointer_button, - pointer_axis -}; - -static void -update_touch_focus(struct seat *seat, - struct libdecor_frame_gtk *frame_gtk, - wl_fixed_t x, - wl_fixed_t y) -{ - /* avoid warnings after decoration has been turned off */ - if (GTK_IS_WIDGET(frame_gtk->header) && frame_gtk->touch_active->type == HEADER) { - struct header_element_data new_focus = get_header_focus( - GTK_HEADER_BAR(frame_gtk->header), - wl_fixed_to_int(x), wl_fixed_to_int(y)); - /* only update if widget change so that we keep the state */ - if (frame_gtk->hdr_focus.widget != new_focus.widget) { - frame_gtk->hdr_focus = new_focus; - } - frame_gtk->hdr_focus.state |= GTK_STATE_FLAG_PRELIGHT; - /* redraw with updated button visuals */ - draw_title_bar(frame_gtk); - libdecor_frame_toplevel_commit(&frame_gtk->frame); - } else { - frame_gtk->hdr_focus.type = HEADER_NONE; - } -} - -static void -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; - - update_touch_focus(seat, frame_gtk, x, y); - - /* update decorations */ - draw_decoration(frame_gtk); - libdecor_frame_toplevel_commit(&frame_gtk->frame); - - enum libdecor_resize_edge edge = - LIBDECOR_RESIZE_EDGE_NONE; - switch (frame_gtk->touch_active->type) { - case SHADOW: - edge = component_edge(frame_gtk->touch_active, - wl_fixed_to_int(x), - wl_fixed_to_int(y), - SHADOW_MARGIN); - break; - case HEADER: - switch (frame_gtk->hdr_focus.type) { - case HEADER_MIN: - case HEADER_MAX: - case HEADER_CLOSE: - frame_gtk->hdr_focus.state |= GTK_STATE_FLAG_ACTIVE; - draw_title_bar(frame_gtk); - libdecor_frame_toplevel_commit(&frame_gtk->frame); - break; - default: - if (time - seat->touch_down_time_stamp < - (uint32_t)frame_gtk->plugin_gtk->double_click_time_ms) { - toggle_maximized(&frame_gtk->frame); - } - else if (moveable(frame_gtk)) { - seat->touch_down_time_stamp = time; - libdecor_frame_move(&frame_gtk->frame, - seat->wl_seat, - serial); - } - break; - } - break; - default: - break; - } - if (edge != LIBDECOR_RESIZE_EDGE_NONE && - resizable(frame_gtk)) { - libdecor_frame_resize( - &frame_gtk->frame, - seat->wl_seat, - serial, - edge); - } -} - -static void -touch_up(void *data, - struct wl_touch *wl_touch, - uint32_t serial, - uint32_t time, - int32_t id) -{ - struct seat *seat = data; - struct libdecor_frame_gtk *frame_gtk; - - if (!seat->touch_focus || !own_surface(seat->touch_focus)) - return; - - frame_gtk = wl_surface_get_user_data(seat->touch_focus); - if (!frame_gtk) - return; - - if (!frame_gtk->touch_active) - return; - - switch (frame_gtk->touch_active->type) { - case HEADER: - libdecor_frame_ref(&frame_gtk->frame); - switch (frame_gtk->hdr_focus.type) { - case HEADER_MIN: - if (minimizable(frame_gtk)) { - libdecor_frame_set_minimized( - &frame_gtk->frame); - } - break; - case HEADER_MAX: - toggle_maximized(&frame_gtk->frame); - break; - case HEADER_CLOSE: - if (closeable(frame_gtk)) { - libdecor_frame_close( - &frame_gtk->frame); - seat->touch_focus = NULL; - } - break; - default: - break; - } - /* unset active/clicked state once released */ - frame_gtk->hdr_focus.state &= ~GTK_STATE_FLAG_ACTIVE; - if (GTK_IS_WIDGET(frame_gtk->header)) { - draw_title_bar(frame_gtk); - libdecor_frame_toplevel_commit(&frame_gtk->frame); - } - libdecor_frame_unref(&frame_gtk->frame); - break; - default: - break; - } - - seat->touch_focus = NULL; - frame_gtk->touch_active = NULL; - frame_gtk->hdr_focus.widget = NULL; - frame_gtk->hdr_focus.type = HEADER_NONE; - draw_decoration(frame_gtk); - libdecor_frame_toplevel_commit(&frame_gtk->frame); -} - -static void -touch_motion(void *data, - struct wl_touch *wl_touch, - uint32_t time, - int32_t id, - wl_fixed_t x, - wl_fixed_t y) -{ - struct seat *seat = data; - struct libdecor_frame_gtk *frame_gtk; - - if (!seat->touch_focus || !own_surface(seat->touch_focus)) - return; - - frame_gtk = wl_surface_get_user_data(seat->touch_focus); - if (!frame_gtk) - return; - - update_touch_focus(seat, frame_gtk, x, y); -} - -static void -touch_frame(void *data, - struct wl_touch *wl_touch) -{ -} - -static void -touch_cancel(void *data, - struct wl_touch *wl_touch) -{ -} - -static struct wl_touch_listener touch_listener = { - touch_down, - touch_up, - touch_motion, - touch_frame, - touch_cancel -}; - -static void -seat_capabilities(void *data, - struct wl_seat *wl_seat, - uint32_t capabilities) -{ - struct seat *seat = data; - - if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && - !seat->wl_pointer) { - seat->wl_pointer = wl_seat_get_pointer(wl_seat); - wl_pointer_add_listener(seat->wl_pointer, - &pointer_listener, seat); - } else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && - seat->wl_pointer) { - wl_pointer_release(seat->wl_pointer); - seat->wl_pointer = NULL; - } - - if ((capabilities & WL_SEAT_CAPABILITY_TOUCH) && - !seat->wl_touch) { - seat->wl_touch = wl_seat_get_touch(wl_seat); - wl_touch_add_listener(seat->wl_touch, - &touch_listener, seat); - } else if (!(capabilities & WL_SEAT_CAPABILITY_TOUCH) && - seat->wl_touch) { - wl_touch_release(seat->wl_touch); - seat->wl_touch = NULL; - } -} - -static void -seat_name(void *data, - struct wl_seat *wl_seat, - const char *name) -{ - /* avoid warning messages when opening/closing popup window */ - struct seat *seat = (struct seat*)data; - seat->name = strdup(name); -} - -static struct wl_seat_listener seat_listener = { - seat_capabilities, - seat_name -}; - -static void -init_wl_seat(struct libdecor_plugin_gtk *plugin_gtk, - uint32_t id, - uint32_t version) -{ - struct seat *seat; - - if (version < 3) { - libdecor_notify_plugin_error( - plugin_gtk->context, - LIBDECOR_ERROR_COMPOSITOR_INCOMPATIBLE, - "%s version 3 required but only version %i is available\n", - wl_seat_interface.name, - version); - } - - seat = zalloc(sizeof *seat); - seat->cursor_scale = 1; - seat->plugin_gtk = plugin_gtk; - wl_list_init(&seat->cursor_outputs); - wl_list_insert(&plugin_gtk->seat_list, &seat->link); - seat->wl_seat = - wl_registry_bind(plugin_gtk->wl_registry, - id, &wl_seat_interface, 3); - wl_seat_add_listener(seat->wl_seat, &seat_listener, seat); -} - -static void -output_geometry(void *data, - struct wl_output *wl_output, - int32_t x, - int32_t y, - int32_t physical_width, - int32_t physical_height, - int32_t subpixel, - const char *make, - const char *model, - int32_t transform) -{ -} - -static void -output_mode(void *data, - struct wl_output *wl_output, - uint32_t flags, - int32_t width, - int32_t height, - int32_t refresh) -{ -} - -static void -output_done(void *data, - struct wl_output *wl_output) -{ - struct output *output = data; - struct libdecor_frame_gtk *frame_gtk; - struct seat *seat; - - wl_list_for_each(frame_gtk, - &output->plugin_gtk->visible_frame_list, link) { - bool updated = false; - updated |= redraw_scale(frame_gtk, &frame_gtk->shadow); - if (updated) - libdecor_frame_toplevel_commit(&frame_gtk->frame); - } - wl_list_for_each(seat, &output->plugin_gtk->seat_list, link) { - if (update_local_cursor(seat)) - send_cursor(seat); - } -} - -static void -output_scale(void *data, - struct wl_output *wl_output, - int32_t factor) -{ - struct output *output = data; - - output->scale = factor; -} - -static struct wl_output_listener output_listener = { - output_geometry, - output_mode, - output_done, - output_scale -}; - -static void -init_wl_output(struct libdecor_plugin_gtk *plugin_gtk, - uint32_t id, - uint32_t version) -{ - struct output *output; - - if (version < 2) { - libdecor_notify_plugin_error( - plugin_gtk->context, - LIBDECOR_ERROR_COMPOSITOR_INCOMPATIBLE, - "%s version 2 required but only version %i is available\n", - wl_output_interface.name, - version); - } - - output = zalloc(sizeof *output); - output->plugin_gtk = plugin_gtk; - wl_list_insert(&plugin_gtk->output_list, &output->link); - output->id = id; - output->wl_output = - wl_registry_bind(plugin_gtk->wl_registry, - id, &wl_output_interface, - MIN (version, 3)); - wl_proxy_set_tag((struct wl_proxy *) output->wl_output, - &libdecor_gtk_proxy_tag); - wl_output_add_listener(output->wl_output, &output_listener, output); -} - -static void -registry_handle_global(void *user_data, - struct wl_registry *wl_registry, - uint32_t id, - const char *interface, - uint32_t version) -{ - struct libdecor_plugin_gtk *plugin_gtk = user_data; - - if (strcmp(interface, "wl_compositor") == 0) - init_wl_compositor(plugin_gtk, id, version); - else if (strcmp(interface, "wl_subcompositor") == 0) - init_wl_subcompositor(plugin_gtk, id, version); - else if (strcmp(interface, "wl_shm") == 0) - init_wl_shm(plugin_gtk, id, version); - else if (strcmp(interface, "wl_seat") == 0) - init_wl_seat(plugin_gtk, id, version); - else if (strcmp(interface, "wl_output") == 0) - init_wl_output(plugin_gtk, id, version); -} - -static void -remove_surface_outputs(struct border_component *cmpnt, const struct output *output) -{ - struct surface_output *surface_output; - wl_list_for_each(surface_output, &cmpnt->output_list, link) { - if (surface_output->output == output) { - wl_list_remove(&surface_output->link); - free(surface_output); - break; - } - } -} - -static void -output_removed(struct libdecor_plugin_gtk *plugin_gtk, - struct output *output) -{ - struct libdecor_frame_gtk *frame_gtk; - struct seat *seat; - - wl_list_for_each(frame_gtk, &plugin_gtk->visible_frame_list, link) { - remove_surface_outputs(&frame_gtk->shadow, output); - } - wl_list_for_each(seat, &plugin_gtk->seat_list, link) { - struct cursor_output *cursor_output; - wl_list_for_each(cursor_output, &seat->cursor_outputs, link) { - if (cursor_output->output == output) { - wl_list_remove(&cursor_output->link); - free(cursor_output); - } - } - } - - wl_list_remove(&output->link); - wl_output_destroy(output->wl_output); - free(output); -} - -static void -registry_handle_global_remove(void *user_data, - struct wl_registry *wl_registry, - uint32_t name) -{ - struct libdecor_plugin_gtk *plugin_gtk = user_data; - struct output *output; - - wl_list_for_each(output, &plugin_gtk->output_list, link) { - if (output->id == name) { - output_removed(plugin_gtk, output); - break; - } - } -} - -static const struct wl_registry_listener registry_listener = { - registry_handle_global, - registry_handle_global_remove -}; - -static bool -has_required_globals(struct libdecor_plugin_gtk *plugin_gtk) -{ - if (!plugin_gtk->wl_compositor) - return false; - if (!plugin_gtk->wl_subcompositor) - return false; - if (!plugin_gtk->wl_shm) - return false; - - return true; -} - -static void -globals_callback(void *user_data, - struct wl_callback *callback, - uint32_t time) -{ - struct libdecor_plugin_gtk *plugin_gtk = user_data; - - wl_callback_destroy(callback); - plugin_gtk->globals_callback = NULL; -} - -static const struct wl_callback_listener globals_callback_listener = { - globals_callback -}; - -static struct libdecor_plugin * -libdecor_plugin_new(struct libdecor *context) -{ - struct libdecor_plugin_gtk *plugin_gtk; - struct wl_display *wl_display; - -#ifdef HAVE_GETTID - /* Only support running on the main thread. */ - if (getpid () != gettid ()) - return NULL; -#endif - - plugin_gtk = zalloc(sizeof *plugin_gtk); - libdecor_plugin_init(&plugin_gtk->plugin, - context, - >k_plugin_iface); - plugin_gtk->context = context; - - wl_list_init(&plugin_gtk->visible_frame_list); - wl_list_init(&plugin_gtk->seat_list); - wl_list_init(&plugin_gtk->output_list); - - /* fetch cursor theme and size*/ - if (!libdecor_get_cursor_settings(&plugin_gtk->cursor_theme_name, - &plugin_gtk->cursor_size)) { - plugin_gtk->cursor_theme_name = NULL; - plugin_gtk->cursor_size = 24; - } - - plugin_gtk->color_scheme_setting = libdecor_get_color_scheme(); - - wl_display = libdecor_get_wl_display(context); - plugin_gtk->wl_registry = wl_display_get_registry(wl_display); - wl_registry_add_listener(plugin_gtk->wl_registry, - ®istry_listener, - plugin_gtk); - - plugin_gtk->globals_callback = wl_display_sync(wl_display); - wl_callback_add_listener(plugin_gtk->globals_callback, - &globals_callback_listener, - plugin_gtk); - wl_display_roundtrip(wl_display); - - if (!has_required_globals(plugin_gtk)) { - fprintf(stderr, "libdecor-gtk-WARNING: Could not get required globals\n"); - libdecor_plugin_gtk_destroy(&plugin_gtk->plugin); - return NULL; - } - - /* setup GTK context */ - gdk_set_allowed_backends("wayland"); - gtk_disable_setlocale(); - - if (!gtk_init_check(NULL, NULL)) { - fprintf(stderr, "libdecor-gtk-WARNING: Failed to initialize GTK\n"); - libdecor_plugin_gtk_destroy(&plugin_gtk->plugin); - return NULL; - } - - g_object_set(gtk_settings_get_default(), - "gtk-application-prefer-dark-theme", - plugin_gtk->color_scheme_setting == LIBDECOR_COLOR_SCHEME_PREFER_DARK, - NULL); - - return &plugin_gtk->plugin; -} - -static struct libdecor_plugin_priority priorities[] = { - { NULL, LIBDECOR_PLUGIN_PRIORITY_HIGH } -}; - -LIBDECOR_EXPORT const struct libdecor_plugin_description -libdecor_plugin_description = { - .api_version = LIBDECOR_PLUGIN_API_VERSION, - .capabilities = LIBDECOR_PLUGIN_CAPABILITY_BASE, - .description = "GTK3 plugin", - .priorities = priorities, - .constructor = libdecor_plugin_new, - .conflicting_symbols = { - "png_free", - "gdk_get_use_xshm", - NULL, - }, -}; diff --git a/libdecor/src/utils.h b/libdecor/src/utils.h deleted file mode 100644 index 7892793b6..000000000 --- a/libdecor/src/utils.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2017 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef UTILS_H -#define UTILS_H - -#include <stdlib.h> - -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) - -#ifndef ARRAY_LENGTH -#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) -#endif - -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) -#endif - -static inline void * -zalloc(size_t size) -{ - return calloc(1, size); -} - -#endif /* UTILS_H */ |
