diff --git a/common/meson.build b/common/meson.build index c0ce1f681..c21aa9e45 100644 --- a/common/meson.build +++ b/common/meson.build @@ -8,6 +8,7 @@ lib_sway_common = static_library( 'loop.c', 'list.c', 'pango.c', + 'sfdo.c', 'stringop.c', 'util.c' ), diff --git a/common/sfdo.c b/common/sfdo.c new file mode 100644 index 000000000..e2e685650 --- /dev/null +++ b/common/sfdo.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include + +#include "log.h" +#include "sfdo.h" + +// this extends libsfdo's behavior to also handle icons specified as absolute paths +char *sfdo_icon_lookup_extended(struct sfdo *sfdo, char *icon_name, int target_size, int scale) { + char *icon_path = NULL; + if (icon_name[0] == '/') { + struct stat sb; + if (!stat(icon_name, &sb)) { + icon_path = strdup(icon_name); + } + } else { + int lookup_options = SFDO_ICON_THEME_LOOKUP_OPTIONS_DEFAULT; + struct sfdo_icon_file *icon_file = \ + sfdo_icon_theme_lookup(sfdo->icon_theme, icon_name, SFDO_NT, \ + target_size, scale, lookup_options); + if (icon_file && icon_file != SFDO_ICON_FILE_INVALID) { + icon_path = strdup(sfdo_icon_file_get_path(icon_file, NULL)); + } + sfdo_icon_file_destroy(icon_file); + } + return icon_path; +} + +struct sfdo *sfdo_create(char *icon_theme) { + if (!icon_theme) { + goto error_null; + } + + struct sfdo *sfdo = calloc(1, sizeof(struct sfdo)); + if (!sfdo) { + goto error_calloc; + } + + struct sfdo_basedir_ctx *basedir_ctx = sfdo_basedir_ctx_create(); + if (!basedir_ctx) { + goto error_basedir_ctx; + } + + sfdo->desktop_ctx = sfdo_desktop_ctx_create(basedir_ctx); + if (!sfdo->desktop_ctx) { + goto error_desktop_ctx; + } + + sfdo->icon_ctx = sfdo_icon_ctx_create(basedir_ctx); + if (!sfdo->icon_ctx) { + goto error_icon_ctx; + } + + sfdo->desktop_db = sfdo_desktop_db_load(sfdo->desktop_ctx, NULL); + if (!sfdo->desktop_db) { + goto error_desktop_db; + } + + int load_options = SFDO_ICON_THEME_LOAD_OPTIONS_DEFAULT + | SFDO_ICON_THEME_LOAD_OPTION_ALLOW_MISSING + | SFDO_ICON_THEME_LOAD_OPTION_RELAXED; + + sfdo->icon_theme = sfdo_icon_theme_load(sfdo->icon_ctx, icon_theme, load_options); + if (!sfdo->icon_theme) { + goto error_icon_theme; + } + + sfdo_basedir_ctx_destroy(basedir_ctx); + + sway_log(SWAY_INFO, "Successfully setup sfdo with icon theme %s", icon_theme); + return sfdo; + +error_icon_theme: + sfdo_desktop_db_destroy(sfdo->desktop_db); +error_desktop_db: + sfdo_icon_ctx_destroy(sfdo->icon_ctx); +error_icon_ctx: + sfdo_desktop_ctx_destroy(sfdo->desktop_ctx); +error_desktop_ctx: + sfdo_basedir_ctx_destroy(basedir_ctx); +error_basedir_ctx: + free(sfdo); +error_calloc: +error_null: + // it's safe to call with null + sway_log(SWAY_ERROR, "Failed to setup sfdo with icon theme %s", icon_theme); + return NULL; +} + +void sfdo_destroy(struct sfdo *sfdo) { + if (!sfdo) { + sway_log(SWAY_DEBUG, "Null sfdo passed in"); + return; + } + + sfdo_desktop_ctx_destroy(sfdo->desktop_ctx); + sfdo_icon_ctx_destroy(sfdo->icon_ctx); + sfdo_desktop_db_destroy(sfdo->desktop_db); + sfdo_icon_theme_destroy(sfdo->icon_theme); + free(sfdo); + sway_log(SWAY_DEBUG, "Successfully destroyed sfdo"); +} diff --git a/include/sfdo.h b/include/sfdo.h new file mode 100644 index 000000000..a009f3963 --- /dev/null +++ b/include/sfdo.h @@ -0,0 +1,19 @@ +#ifndef _SWAY_SFDO_H +#define _SWAY_SFDO_H + +#include +#include +#include + +struct sfdo { + struct sfdo_desktop_ctx *desktop_ctx; + struct sfdo_icon_ctx *icon_ctx; + struct sfdo_desktop_db *desktop_db; + struct sfdo_icon_theme *icon_theme; +}; + +char *sfdo_icon_lookup_extended(struct sfdo *sfdo, char *icon_name, int target_size, int scale); +struct sfdo *sfdo_create(char *icon_theme); +void sfdo_destroy(struct sfdo *sfdo); + +#endif diff --git a/include/sway/config.h b/include/sway/config.h index bb770c6f7..aa151d985 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -399,8 +399,9 @@ struct bar_config { char *binding_mode_text; } colors; -#if HAVE_TRAY char *icon_theme; + +#if HAVE_TRAY struct wl_list tray_bindings; // struct tray_binding::link list_t *tray_outputs; // char * int tray_padding; diff --git a/include/sway/server.h b/include/sway/server.h index 72bccd70c..b1053d25f 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -9,6 +9,8 @@ #include "sway/xwayland.h" #endif +#include "sfdo.h" + struct sway_transaction; struct sway_session_lock { @@ -146,6 +148,8 @@ struct sway_server { list_t *dirty_nodes; struct wl_event_source *delayed_modeset; + + struct sfdo *sfdo; }; extern struct sway_server server; diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 197d21901..cbfa3df04 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -16,6 +16,8 @@ struct swaybar_tray; struct swaybar_workspace; struct loop; +struct sfdo; + struct swaybar { char *id; char *mode; @@ -50,6 +52,8 @@ struct swaybar { struct swaybar_tray *tray; #endif + struct sfdo *sfdo; + bool running; }; diff --git a/include/swaybar/config.h b/include/swaybar/config.h index ad58b3c3c..b360dbf0e 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h @@ -8,6 +8,8 @@ #include "util.h" #include +#include "sfdo.h" + struct box_colors { uint32_t border; uint32_t background; @@ -73,13 +75,16 @@ struct swaybar_config { struct box_colors binding_mode; } colors; -#if HAVE_TRAY char *icon_theme; + +#if HAVE_TRAY struct wl_list tray_bindings; // struct tray_binding::link bool tray_hidden; list_t *tray_outputs; // char * int tray_padding; #endif + + struct sfdo *sfdo; }; #if HAVE_TRAY diff --git a/include/swaybar/image.h b/include/swaybar/image.h index 53a210dd7..332e81152 100644 --- a/include/swaybar/image.h +++ b/include/swaybar/image.h @@ -2,6 +2,6 @@ #define _SWAYBAR_IMAGE_H #include -cairo_surface_t *load_image(const char *path); +cairo_surface_t *load_image(const char *path, int target_size, int scale); #endif diff --git a/meson.build b/meson.build index eab1348ae..a8f3f6ad6 100644 --- a/meson.build +++ b/meson.build @@ -60,6 +60,10 @@ endforeach null_dep = dependency('', required: false) +libsfdo_basedir = dependency('libsfdo-basedir', version: '>=0.1.3') +libsfdo_desktop = dependency('libsfdo-desktop', version: '>=0.1.3') +libsfdo_desktop_file = dependency('libsfdo-desktop-file', version: '>=0.1.3') +libsfdo_icon = dependency('libsfdo-icon', version: '>=0.1.3') jsonc = dependency('json-c', version: '>=0.13') pcre2 = dependency('libpcre2-8') wayland_server = dependency('wayland-server', version: '>=1.21.0') diff --git a/meson_options.txt b/meson_options.txt index 506ecc9a6..6f776bab7 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -8,3 +8,4 @@ option('tray', type: 'feature', value: 'auto', description: 'Enable support for option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybar tray') option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') option('sd-bus-provider', type: 'combo', choices: ['auto', 'libsystemd', 'libelogind', 'basu'], value: 'auto', description: 'Provider of the sd-bus library') +option('sfdo', type: 'feature', value: 'auto', description: 'Enable libsfdo integration') diff --git a/sway/config/bar.c b/sway/config/bar.c index f4efb276c..93b217dff 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -70,9 +70,14 @@ void free_bar_config(struct bar_config *bar) { free(bar->colors.binding_mode_border); free(bar->colors.binding_mode_bg); free(bar->colors.binding_mode_text); + + // since libsfdo is a hard depedency now we always have at least + // a default icon theme and need to free the string that contains + // its name + free(bar->icon_theme); + #if HAVE_TRAY list_free_items_and_destroy(bar->tray_outputs); - free(bar->icon_theme); struct tray_binding *tray_bind = NULL, *tmp_tray_bind = NULL; wl_list_for_each_safe(tray_bind, tmp_tray_bind, &bar->tray_bindings, link) { @@ -170,6 +175,11 @@ struct bar_config *default_bar_config(void) { bar->colors.binding_mode_bg = NULL; bar->colors.binding_mode_text = NULL; + // we need some default when we initialize sfdo + if (!(bar->icon_theme = strdup("Hicolor"))) { + goto cleanup; + } + #if HAVE_TRAY bar->tray_padding = 2; wl_list_init(&bar->tray_bindings); diff --git a/sway/meson.build b/sway/meson.build index 8042c89be..9bc83e460 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -218,6 +218,10 @@ sway_sources = files( ) sway_deps = [ + libsfdo_basedir, + libsfdo_desktop, + libsfdo_desktop_file, + libsfdo_icon, cairo, drm, jsonc, diff --git a/sway/server.c b/sway/server.c index 6ca5906df..e4fe3944a 100644 --- a/sway/server.c +++ b/sway/server.c @@ -1,3 +1,13 @@ +#include "sway/server.h" +#include "config.h" +#include "list.h" +#include "log.h" +#include "sway/config.h" +#include "sway/desktop/idle_inhibit_v1.h" +#include "sway/input/cursor.h" +#include "sway/input/input-manager.h" +#include "sway/output.h" +#include "sway/tree/root.h" #include #include #include @@ -14,13 +24,13 @@ #include #include #include -#include #include #include +#include #include -#include #include #include +#include #include #include #include @@ -49,26 +59,18 @@ #include #include #include -#include "config.h" -#include "list.h" -#include "log.h" -#include "sway/config.h" -#include "sway/desktop/idle_inhibit_v1.h" -#include "sway/input/input-manager.h" -#include "sway/output.h" -#include "sway/server.h" -#include "sway/input/cursor.h" -#include "sway/tree/root.h" #if WLR_HAS_XWAYLAND -#include #include "sway/xwayland.h" +#include #endif #if WLR_HAS_DRM_BACKEND #include #endif +#include "sfdo.h" + #define SWAY_XDG_SHELL_VERSION 5 #define SWAY_LAYER_SHELL_VERSION 4 #define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1 @@ -78,498 +80,520 @@ bool allow_unsupported_gpu = false; #if WLR_HAS_DRM_BACKEND static void handle_drm_lease_request(struct wl_listener *listener, void *data) { - /* We only offer non-desktop outputs, but in the future we might want to do - * more logic here. */ + /* We only offer non-desktop outputs, but in the future we might want to do + * more logic here. */ - struct wlr_drm_lease_request_v1 *req = data; - struct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req); - if (!lease) { - sway_log(SWAY_ERROR, "Failed to grant lease request"); - wlr_drm_lease_request_v1_reject(req); - } + struct wlr_drm_lease_request_v1 *req = data; + struct wlr_drm_lease_v1 *lease = wlr_drm_lease_request_v1_grant(req); + if (!lease) { + sway_log(SWAY_ERROR, "Failed to grant lease request"); + wlr_drm_lease_request_v1_reject(req); + } } #endif static bool is_privileged(const struct wl_global *global) { #if WLR_HAS_DRM_BACKEND - if (server.drm_lease_manager != NULL) { - struct wlr_drm_lease_device_v1 *drm_lease_dev; - wl_list_for_each(drm_lease_dev, &server.drm_lease_manager->devices, link) { - if (drm_lease_dev->global == global) { - return true; - } - } - } + if (server.drm_lease_manager != NULL) { + struct wlr_drm_lease_device_v1 *drm_lease_dev; + wl_list_for_each(drm_lease_dev, &server.drm_lease_manager->devices, link) { + if (drm_lease_dev->global == global) { + return true; + } + } + } #endif - return - global == server.output_manager_v1->global || - global == server.output_power_manager_v1->global || - global == server.input_method->global || - global == server.foreign_toplevel_list->global || - global == server.foreign_toplevel_manager->global || - global == server.wlr_data_control_manager_v1->global || - global == server.ext_data_control_manager_v1->global || - global == server.screencopy_manager_v1->global || - global == server.ext_image_copy_capture_manager_v1->global || - global == server.export_dmabuf_manager_v1->global || - global == server.security_context_manager_v1->global || - global == server.gamma_control_manager_v1->global || - global == server.layer_shell->global || - global == server.session_lock.manager->global || - global == server.input->keyboard_shortcuts_inhibit->global || - global == server.input->virtual_keyboard->global || - global == server.input->virtual_pointer->global || - global == server.input->transient_seat_manager->global || - global == server.xdg_output_manager_v1->global; + return global == server.output_manager_v1->global || + global == server.output_power_manager_v1->global || + global == server.input_method->global || + global == server.foreign_toplevel_list->global || + global == server.foreign_toplevel_manager->global || + global == server.wlr_data_control_manager_v1->global || + global == server.ext_data_control_manager_v1->global || + global == server.screencopy_manager_v1->global || + global == server.ext_image_copy_capture_manager_v1->global || + global == server.export_dmabuf_manager_v1->global || + global == server.security_context_manager_v1->global || + global == server.gamma_control_manager_v1->global || + global == server.layer_shell->global || + global == server.session_lock.manager->global || + global == server.input->keyboard_shortcuts_inhibit->global || + global == server.input->virtual_keyboard->global || + global == server.input->virtual_pointer->global || + global == server.input->transient_seat_manager->global || + global == server.xdg_output_manager_v1->global; } static bool filter_global(const struct wl_client *client, - const struct wl_global *global, void *data) { + const struct wl_global *global, void *data) { #if WLR_HAS_XWAYLAND - struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; - if (xwayland && global == xwayland->shell_v1->global) { - return xwayland->server != NULL && client == xwayland->server->client; - } + struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; + if (xwayland && global == xwayland->shell_v1->global) { + return xwayland->server != NULL && client == xwayland->server->client; + } #endif - // Restrict usage of privileged protocols to unsandboxed clients - // TODO: add a way for users to configure an allow-list - const struct wlr_security_context_v1_state *security_context = - wlr_security_context_manager_v1_lookup_client( - server.security_context_manager_v1, (struct wl_client *)client); - if (is_privileged(global)) { - return security_context == NULL; - } + // Restrict usage of privileged protocols to unsandboxed clients + // TODO: add a way for users to configure an allow-list + const struct wlr_security_context_v1_state *security_context = + wlr_security_context_manager_v1_lookup_client( + server.security_context_manager_v1, (struct wl_client *)client); + if (is_privileged(global)) { + return security_context == NULL; + } - return true; + return true; } static void detect_proprietary(struct wlr_backend *backend, void *data) { - int drm_fd = wlr_backend_get_drm_fd(backend); - if (drm_fd < 0) { - return; - } + int drm_fd = wlr_backend_get_drm_fd(backend); + if (drm_fd < 0) { + return; + } - drmVersion *version = drmGetVersion(drm_fd); - if (version == NULL) { - sway_log(SWAY_ERROR, "drmGetVersion() failed"); - return; - } + drmVersion *version = drmGetVersion(drm_fd); + if (version == NULL) { + sway_log(SWAY_ERROR, "drmGetVersion() failed"); + return; + } - bool is_unsupported = false; - if (strcmp(version->name, "nvidia-drm") == 0) { - is_unsupported = true; - sway_log(SWAY_ERROR, "!!! Proprietary Nvidia drivers are in use !!!"); - if (!allow_unsupported_gpu) { - sway_log(SWAY_ERROR, "Use Nouveau instead"); - } - } + bool is_unsupported = false; + if (strcmp(version->name, "nvidia-drm") == 0) { + is_unsupported = true; + sway_log(SWAY_ERROR, "!!! Proprietary Nvidia drivers are in use !!!"); + if (!allow_unsupported_gpu) { + sway_log(SWAY_ERROR, "Use Nouveau instead"); + } + } - if (strcmp(version->name, "evdi") == 0) { - is_unsupported = true; - sway_log(SWAY_ERROR, "!!! Proprietary DisplayLink drivers are in use !!!"); - } + if (strcmp(version->name, "evdi") == 0) { + is_unsupported = true; + sway_log(SWAY_ERROR, "!!! Proprietary DisplayLink drivers are in use !!!"); + } - if (!allow_unsupported_gpu && is_unsupported) { - sway_log(SWAY_ERROR, - "Proprietary drivers are NOT supported. To launch sway anyway, " - "launch with --unsupported-gpu and DO NOT report issues."); - exit(EXIT_FAILURE); - } + if (!allow_unsupported_gpu && is_unsupported) { + sway_log(SWAY_ERROR, + "Proprietary drivers are NOT supported. To launch sway anyway, " + "launch with --unsupported-gpu and DO NOT report issues."); + exit(EXIT_FAILURE); + } - drmFreeVersion(version); + drmFreeVersion(version); } static void do_renderer_recreate(void *data) { - struct sway_server *server = data; - server->recreating_renderer = NULL; + struct sway_server *server = data; + server->recreating_renderer = NULL; - sway_log(SWAY_INFO, "Re-creating renderer after GPU reset"); - struct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend); - if (renderer == NULL) { - sway_log(SWAY_ERROR, "Unable to create renderer"); - return; - } + sway_log(SWAY_INFO, "Re-creating renderer after GPU reset"); + struct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend); + if (renderer == NULL) { + sway_log(SWAY_ERROR, "Unable to create renderer"); + return; + } - struct wlr_allocator *allocator = - wlr_allocator_autocreate(server->backend, renderer); - if (allocator == NULL) { - sway_log(SWAY_ERROR, "Unable to create allocator"); - wlr_renderer_destroy(renderer); - return; - } + struct wlr_allocator *allocator = + wlr_allocator_autocreate(server->backend, renderer); + if (allocator == NULL) { + sway_log(SWAY_ERROR, "Unable to create allocator"); + wlr_renderer_destroy(renderer); + return; + } - struct wlr_renderer *old_renderer = server->renderer; - struct wlr_allocator *old_allocator = server->allocator; - server->renderer = renderer; - server->allocator = allocator; + struct wlr_renderer *old_renderer = server->renderer; + struct wlr_allocator *old_allocator = server->allocator; + server->renderer = renderer; + server->allocator = allocator; - wl_list_remove(&server->renderer_lost.link); - wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); + wl_list_remove(&server->renderer_lost.link); + wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); - wlr_compositor_set_renderer(server->compositor, renderer); + wlr_compositor_set_renderer(server->compositor, renderer); - struct sway_output *output; - wl_list_for_each(output, &root->all_outputs, link) { - wlr_output_init_render(output->wlr_output, - server->allocator, server->renderer); - } + struct sway_output *output; + wl_list_for_each(output, &root->all_outputs, link) { + wlr_output_init_render(output->wlr_output, server->allocator, + server->renderer); + } - wlr_allocator_destroy(old_allocator); - wlr_renderer_destroy(old_renderer); + wlr_allocator_destroy(old_allocator); + wlr_renderer_destroy(old_renderer); } static void handle_renderer_lost(struct wl_listener *listener, void *data) { - struct sway_server *server = wl_container_of(listener, server, renderer_lost); + struct sway_server *server = wl_container_of(listener, server, renderer_lost); - if (server->recreating_renderer != NULL) { - sway_log(SWAY_DEBUG, "Re-creation of renderer already scheduled"); - return; - } + if (server->recreating_renderer != NULL) { + sway_log(SWAY_DEBUG, "Re-creation of renderer already scheduled"); + return; + } - sway_log(SWAY_INFO, "Scheduling re-creation of renderer after GPU reset"); - server->recreating_renderer = wl_event_loop_add_idle(server->wl_event_loop, do_renderer_recreate, server); + sway_log(SWAY_INFO, "Scheduling re-creation of renderer after GPU reset"); + server->recreating_renderer = wl_event_loop_add_idle( + server->wl_event_loop, do_renderer_recreate, server); } -static void handle_new_foreign_toplevel_capture_request(struct wl_listener *listener, void *data) { - struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request = data; - struct sway_view *view = request->toplevel_handle->data; +static void +handle_new_foreign_toplevel_capture_request(struct wl_listener *listener, + void *data) { + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request + *request = data; + struct sway_view *view = request->toplevel_handle->data; - if (view->image_capture_source == NULL) { - view->image_capture_source = wlr_ext_image_capture_source_v1_create_with_scene_node( - &view->image_capture_scene->tree.node, server.wl_event_loop, server.allocator, server.renderer); - if (view->image_capture_source == NULL) { - return; - } - } + if (view->image_capture_source == NULL) { + view->image_capture_source = + wlr_ext_image_capture_source_v1_create_with_scene_node( + &view->image_capture_scene->tree.node, server.wl_event_loop, + server.allocator, server.renderer); + if (view->image_capture_source == NULL) { + return; + } + } - wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept(request, view->image_capture_source); + wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept( + request, view->image_capture_source); } bool server_init(struct sway_server *server) { - sway_log(SWAY_DEBUG, "Initializing Wayland server"); - server->wl_display = wl_display_create(); - server->wl_event_loop = wl_display_get_event_loop(server->wl_display); + sway_log(SWAY_DEBUG, "Initializing Wayland server"); + server->wl_display = wl_display_create(); + server->wl_event_loop = wl_display_get_event_loop(server->wl_display); - wl_display_set_global_filter(server->wl_display, filter_global, NULL); - wl_display_set_default_max_buffer_size(server->wl_display, 1024 * 1024); + wl_display_set_global_filter(server->wl_display, filter_global, NULL); + wl_display_set_default_max_buffer_size(server->wl_display, 1024 * 1024); - root = root_create(server->wl_display); + root = root_create(server->wl_display); - server->backend = wlr_backend_autocreate(server->wl_event_loop, &server->session); - if (!server->backend) { - sway_log(SWAY_ERROR, "Unable to create backend"); - return false; - } + server->backend = + wlr_backend_autocreate(server->wl_event_loop, &server->session); + if (!server->backend) { + sway_log(SWAY_ERROR, "Unable to create backend"); + return false; + } - wlr_multi_for_each_backend(server->backend, detect_proprietary, NULL); + wlr_multi_for_each_backend(server->backend, detect_proprietary, NULL); - server->renderer = wlr_renderer_autocreate(server->backend); - if (!server->renderer) { - sway_log(SWAY_ERROR, "Failed to create renderer"); - return false; - } + server->renderer = wlr_renderer_autocreate(server->backend); + if (!server->renderer) { + sway_log(SWAY_ERROR, "Failed to create renderer"); + return false; + } - server->renderer_lost.notify = handle_renderer_lost; - wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); + server->renderer_lost.notify = handle_renderer_lost; + wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); - wlr_renderer_init_wl_shm(server->renderer, server->wl_display); + wlr_renderer_init_wl_shm(server->renderer, server->wl_display); - if (wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF) != NULL) { - server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( - server->wl_display, 4, server->renderer); - } - if (wlr_renderer_get_drm_fd(server->renderer) >= 0 && - server->renderer->features.timeline && - server->backend->features.timeline) { - wlr_linux_drm_syncobj_manager_v1_create(server->wl_display, 1, - wlr_renderer_get_drm_fd(server->renderer)); - } + if (wlr_renderer_get_texture_formats(server->renderer, + WLR_BUFFER_CAP_DMABUF) != NULL) { + server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( + server->wl_display, 4, server->renderer); + } + if (wlr_renderer_get_drm_fd(server->renderer) >= 0 && + server->renderer->features.timeline && + server->backend->features.timeline) { + wlr_linux_drm_syncobj_manager_v1_create( + server->wl_display, 1, wlr_renderer_get_drm_fd(server->renderer)); + } - server->allocator = wlr_allocator_autocreate(server->backend, - server->renderer); - if (!server->allocator) { - sway_log(SWAY_ERROR, "Failed to create allocator"); - return false; - } + server->allocator = + wlr_allocator_autocreate(server->backend, server->renderer); + if (!server->allocator) { + sway_log(SWAY_ERROR, "Failed to create allocator"); + return false; + } - server->compositor = wlr_compositor_create(server->wl_display, 6, - server->renderer); + server->compositor = + wlr_compositor_create(server->wl_display, 6, server->renderer); - wlr_subcompositor_create(server->wl_display); + wlr_subcompositor_create(server->wl_display); - server->data_device_manager = - wlr_data_device_manager_create(server->wl_display); + server->data_device_manager = + wlr_data_device_manager_create(server->wl_display); - server->gamma_control_manager_v1 = - wlr_gamma_control_manager_v1_create(server->wl_display); - wlr_scene_set_gamma_control_manager_v1(root->root_scene, - server->gamma_control_manager_v1); + server->gamma_control_manager_v1 = + wlr_gamma_control_manager_v1_create(server->wl_display); + wlr_scene_set_gamma_control_manager_v1(root->root_scene, + server->gamma_control_manager_v1); - server->new_output.notify = handle_new_output; - wl_signal_add(&server->backend->events.new_output, &server->new_output); + server->new_output.notify = handle_new_output; + wl_signal_add(&server->backend->events.new_output, &server->new_output); - server->xdg_output_manager_v1 = - wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); + server->xdg_output_manager_v1 = + wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); - server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display); - sway_idle_inhibit_manager_v1_init(); + server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display); + sway_idle_inhibit_manager_v1_init(); - server->layer_shell = wlr_layer_shell_v1_create(server->wl_display, - SWAY_LAYER_SHELL_VERSION); - wl_signal_add(&server->layer_shell->events.new_surface, - &server->layer_shell_surface); - server->layer_shell_surface.notify = handle_layer_shell_surface; + server->layer_shell = + wlr_layer_shell_v1_create(server->wl_display, SWAY_LAYER_SHELL_VERSION); + wl_signal_add(&server->layer_shell->events.new_surface, + &server->layer_shell_surface); + server->layer_shell_surface.notify = handle_layer_shell_surface; - server->xdg_shell = wlr_xdg_shell_create(server->wl_display, - SWAY_XDG_SHELL_VERSION); - wl_signal_add(&server->xdg_shell->events.new_toplevel, - &server->xdg_shell_toplevel); - server->xdg_shell_toplevel.notify = handle_xdg_shell_toplevel; + server->xdg_shell = + wlr_xdg_shell_create(server->wl_display, SWAY_XDG_SHELL_VERSION); + wl_signal_add(&server->xdg_shell->events.new_toplevel, + &server->xdg_shell_toplevel); + server->xdg_shell_toplevel.notify = handle_xdg_shell_toplevel; - server->tablet_v2 = wlr_tablet_v2_create(server->wl_display); + server->tablet_v2 = wlr_tablet_v2_create(server->wl_display); - server->server_decoration_manager = - wlr_server_decoration_manager_create(server->wl_display); - wlr_server_decoration_manager_set_default_mode( - server->server_decoration_manager, - WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); - wl_signal_add(&server->server_decoration_manager->events.new_decoration, - &server->server_decoration); - server->server_decoration.notify = handle_server_decoration; - wl_list_init(&server->decorations); + server->server_decoration_manager = + wlr_server_decoration_manager_create(server->wl_display); + wlr_server_decoration_manager_set_default_mode( + server->server_decoration_manager, + WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); + wl_signal_add(&server->server_decoration_manager->events.new_decoration, + &server->server_decoration); + server->server_decoration.notify = handle_server_decoration; + wl_list_init(&server->decorations); - server->xdg_decoration_manager = - wlr_xdg_decoration_manager_v1_create(server->wl_display); - wl_signal_add( - &server->xdg_decoration_manager->events.new_toplevel_decoration, - &server->xdg_decoration); - server->xdg_decoration.notify = handle_xdg_decoration; - wl_list_init(&server->xdg_decorations); + server->xdg_decoration_manager = + wlr_xdg_decoration_manager_v1_create(server->wl_display); + wl_signal_add(&server->xdg_decoration_manager->events.new_toplevel_decoration, + &server->xdg_decoration); + server->xdg_decoration.notify = handle_xdg_decoration; + wl_list_init(&server->xdg_decorations); - server->relative_pointer_manager = - wlr_relative_pointer_manager_v1_create(server->wl_display); + server->relative_pointer_manager = + wlr_relative_pointer_manager_v1_create(server->wl_display); - server->pointer_constraints = - wlr_pointer_constraints_v1_create(server->wl_display); - server->pointer_constraint.notify = handle_pointer_constraint; - wl_signal_add(&server->pointer_constraints->events.new_constraint, - &server->pointer_constraint); + server->pointer_constraints = + wlr_pointer_constraints_v1_create(server->wl_display); + server->pointer_constraint.notify = handle_pointer_constraint; + wl_signal_add(&server->pointer_constraints->events.new_constraint, + &server->pointer_constraint); - wlr_presentation_create(server->wl_display, server->backend, SWAY_PRESENTATION_VERSION); - wlr_alpha_modifier_v1_create(server->wl_display); + wlr_presentation_create(server->wl_display, server->backend, + SWAY_PRESENTATION_VERSION); + wlr_alpha_modifier_v1_create(server->wl_display); - server->output_manager_v1 = - wlr_output_manager_v1_create(server->wl_display); - server->output_manager_apply.notify = handle_output_manager_apply; - wl_signal_add(&server->output_manager_v1->events.apply, - &server->output_manager_apply); - server->output_manager_test.notify = handle_output_manager_test; - wl_signal_add(&server->output_manager_v1->events.test, - &server->output_manager_test); + server->output_manager_v1 = wlr_output_manager_v1_create(server->wl_display); + server->output_manager_apply.notify = handle_output_manager_apply; + wl_signal_add(&server->output_manager_v1->events.apply, + &server->output_manager_apply); + server->output_manager_test.notify = handle_output_manager_test; + wl_signal_add(&server->output_manager_v1->events.test, + &server->output_manager_test); - server->output_power_manager_v1 = - wlr_output_power_manager_v1_create(server->wl_display); - server->output_power_manager_set_mode.notify = - handle_output_power_manager_set_mode; - wl_signal_add(&server->output_power_manager_v1->events.set_mode, - &server->output_power_manager_set_mode); - server->input_method = wlr_input_method_manager_v2_create(server->wl_display); - server->text_input = wlr_text_input_manager_v3_create(server->wl_display); - server->foreign_toplevel_list = - wlr_ext_foreign_toplevel_list_v1_create(server->wl_display, SWAY_FOREIGN_TOPLEVEL_LIST_VERSION); - server->foreign_toplevel_manager = - wlr_foreign_toplevel_manager_v1_create(server->wl_display); + server->output_power_manager_v1 = + wlr_output_power_manager_v1_create(server->wl_display); + server->output_power_manager_set_mode.notify = + handle_output_power_manager_set_mode; + wl_signal_add(&server->output_power_manager_v1->events.set_mode, + &server->output_power_manager_set_mode); + server->input_method = wlr_input_method_manager_v2_create(server->wl_display); + server->text_input = wlr_text_input_manager_v3_create(server->wl_display); + server->foreign_toplevel_list = wlr_ext_foreign_toplevel_list_v1_create( + server->wl_display, SWAY_FOREIGN_TOPLEVEL_LIST_VERSION); + server->foreign_toplevel_manager = + wlr_foreign_toplevel_manager_v1_create(server->wl_display); - sway_session_lock_init(); + sway_session_lock_init(); #if WLR_HAS_DRM_BACKEND - server->drm_lease_manager= - wlr_drm_lease_v1_manager_create(server->wl_display, server->backend); - if (server->drm_lease_manager) { - server->drm_lease_request.notify = handle_drm_lease_request; - wl_signal_add(&server->drm_lease_manager->events.request, - &server->drm_lease_request); - } else { - sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1"); - sway_log(SWAY_INFO, "VR will not be available"); - } + server->drm_lease_manager = + wlr_drm_lease_v1_manager_create(server->wl_display, server->backend); + if (server->drm_lease_manager) { + server->drm_lease_request.notify = handle_drm_lease_request; + wl_signal_add(&server->drm_lease_manager->events.request, + &server->drm_lease_request); + } else { + sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1"); + sway_log(SWAY_INFO, "VR will not be available"); + } #endif - server->export_dmabuf_manager_v1 = wlr_export_dmabuf_manager_v1_create(server->wl_display); - server->screencopy_manager_v1 = wlr_screencopy_manager_v1_create(server->wl_display); - server->ext_image_copy_capture_manager_v1 = wlr_ext_image_copy_capture_manager_v1_create(server->wl_display, 1); - wlr_ext_output_image_capture_source_manager_v1_create(server->wl_display, 1); - server->wlr_data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display); - server->ext_data_control_manager_v1 = wlr_ext_data_control_manager_v1_create(server->wl_display, 1); - server->security_context_manager_v1 = wlr_security_context_manager_v1_create(server->wl_display); - wlr_viewporter_create(server->wl_display); - wlr_single_pixel_buffer_manager_v1_create(server->wl_display); - server->content_type_manager_v1 = - wlr_content_type_manager_v1_create(server->wl_display, 1); - wlr_fractional_scale_manager_v1_create(server->wl_display, 1); + server->export_dmabuf_manager_v1 = + wlr_export_dmabuf_manager_v1_create(server->wl_display); + server->screencopy_manager_v1 = + wlr_screencopy_manager_v1_create(server->wl_display); + server->ext_image_copy_capture_manager_v1 = + wlr_ext_image_copy_capture_manager_v1_create(server->wl_display, 1); + wlr_ext_output_image_capture_source_manager_v1_create(server->wl_display, 1); + server->wlr_data_control_manager_v1 = + wlr_data_control_manager_v1_create(server->wl_display); + server->ext_data_control_manager_v1 = + wlr_ext_data_control_manager_v1_create(server->wl_display, 1); + server->security_context_manager_v1 = + wlr_security_context_manager_v1_create(server->wl_display); + wlr_viewporter_create(server->wl_display); + wlr_single_pixel_buffer_manager_v1_create(server->wl_display); + server->content_type_manager_v1 = + wlr_content_type_manager_v1_create(server->wl_display, 1); + wlr_fractional_scale_manager_v1_create(server->wl_display, 1); - server->ext_foreign_toplevel_image_capture_source_manager_v1 = - wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(server->wl_display, 1); - server->new_foreign_toplevel_capture_request.notify = handle_new_foreign_toplevel_capture_request; - wl_signal_add(&server->ext_foreign_toplevel_image_capture_source_manager_v1->events.new_request, - &server->new_foreign_toplevel_capture_request); + server->ext_foreign_toplevel_image_capture_source_manager_v1 = + wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create( + server->wl_display, 1); + server->new_foreign_toplevel_capture_request.notify = + handle_new_foreign_toplevel_capture_request; + wl_signal_add(&server->ext_foreign_toplevel_image_capture_source_manager_v1 + ->events.new_request, + &server->new_foreign_toplevel_capture_request); - server->tearing_control_v1 = - wlr_tearing_control_manager_v1_create(server->wl_display, 1); - server->tearing_control_new_object.notify = handle_new_tearing_hint; - wl_signal_add(&server->tearing_control_v1->events.new_object, - &server->tearing_control_new_object); - wl_list_init(&server->tearing_controllers); + server->tearing_control_v1 = + wlr_tearing_control_manager_v1_create(server->wl_display, 1); + server->tearing_control_new_object.notify = handle_new_tearing_hint; + wl_signal_add(&server->tearing_control_v1->events.new_object, + &server->tearing_control_new_object); + wl_list_init(&server->tearing_controllers); - struct wlr_xdg_foreign_registry *foreign_registry = - wlr_xdg_foreign_registry_create(server->wl_display); - wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry); - wlr_xdg_foreign_v2_create(server->wl_display, foreign_registry); + struct wlr_xdg_foreign_registry *foreign_registry = + wlr_xdg_foreign_registry_create(server->wl_display); + wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry); + wlr_xdg_foreign_v2_create(server->wl_display, foreign_registry); - server->xdg_activation_v1 = wlr_xdg_activation_v1_create(server->wl_display); - server->xdg_activation_v1_request_activate.notify = - xdg_activation_v1_handle_request_activate; - wl_signal_add(&server->xdg_activation_v1->events.request_activate, - &server->xdg_activation_v1_request_activate); - server->xdg_activation_v1_new_token.notify = - xdg_activation_v1_handle_new_token; - wl_signal_add(&server->xdg_activation_v1->events.new_token, - &server->xdg_activation_v1_new_token); + server->xdg_activation_v1 = wlr_xdg_activation_v1_create(server->wl_display); + server->xdg_activation_v1_request_activate.notify = + xdg_activation_v1_handle_request_activate; + wl_signal_add(&server->xdg_activation_v1->events.request_activate, + &server->xdg_activation_v1_request_activate); + server->xdg_activation_v1_new_token.notify = + xdg_activation_v1_handle_new_token; + wl_signal_add(&server->xdg_activation_v1->events.new_token, + &server->xdg_activation_v1_new_token); - struct wlr_cursor_shape_manager_v1 *cursor_shape_manager = - wlr_cursor_shape_manager_v1_create(server->wl_display, 1); - server->request_set_cursor_shape.notify = handle_request_set_cursor_shape; - wl_signal_add(&cursor_shape_manager->events.request_set_shape, &server->request_set_cursor_shape); + struct wlr_cursor_shape_manager_v1 *cursor_shape_manager = + wlr_cursor_shape_manager_v1_create(server->wl_display, 1); + server->request_set_cursor_shape.notify = handle_request_set_cursor_shape; + wl_signal_add(&cursor_shape_manager->events.request_set_shape, + &server->request_set_cursor_shape); - wl_list_init(&server->pending_launcher_ctxs); + wl_list_init(&server->pending_launcher_ctxs); - // Avoid using "wayland-0" as display socket - char name_candidate[16]; - for (unsigned int i = 1; i <= 32; ++i) { - snprintf(name_candidate, sizeof(name_candidate), "wayland-%u", i); - if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) { - server->socket = strdup(name_candidate); - break; - } - } + // Avoid using "wayland-0" as display socket + char name_candidate[16]; + for (unsigned int i = 1; i <= 32; ++i) { + snprintf(name_candidate, sizeof(name_candidate), "wayland-%u", i); + if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) { + server->socket = strdup(name_candidate); + break; + } + } - if (!server->socket) { - sway_log(SWAY_ERROR, "Unable to open wayland socket"); - wlr_backend_destroy(server->backend); - return false; - } + if (!server->socket) { + sway_log(SWAY_ERROR, "Unable to open wayland socket"); + wlr_backend_destroy(server->backend); + return false; + } - server->headless_backend = wlr_headless_backend_create(server->wl_event_loop); - if (!server->headless_backend) { - sway_log(SWAY_ERROR, "Failed to create secondary headless backend"); - wlr_backend_destroy(server->backend); - return false; - } else { - wlr_multi_backend_add(server->backend, server->headless_backend); - } + server->headless_backend = wlr_headless_backend_create(server->wl_event_loop); + if (!server->headless_backend) { + sway_log(SWAY_ERROR, "Failed to create secondary headless backend"); + wlr_backend_destroy(server->backend); + return false; + } else { + wlr_multi_backend_add(server->backend, server->headless_backend); + } - struct wlr_output *wlr_output = - wlr_headless_add_output(server->headless_backend, 800, 600); - wlr_output_set_name(wlr_output, "FALLBACK"); - root->fallback_output = output_create(wlr_output); + struct wlr_output *wlr_output = + wlr_headless_add_output(server->headless_backend, 800, 600); + wlr_output_set_name(wlr_output, "FALLBACK"); + root->fallback_output = output_create(wlr_output); - // This may have been set already via -Dtxn-timeout - if (!server->txn_timeout_ms) { - server->txn_timeout_ms = 200; - } + // This may have been set already via -Dtxn-timeout + if (!server->txn_timeout_ms) { + server->txn_timeout_ms = 200; + } - server->dirty_nodes = create_list(); + server->dirty_nodes = create_list(); - server->input = input_manager_create(server); - input_manager_get_default_seat(); // create seat0 + server->input = input_manager_create(server); + input_manager_get_default_seat(); // create seat0 - return true; + return true; } void server_fini(struct sway_server *server) { - // remove listeners - wl_list_remove(&server->renderer_lost.link); - wl_list_remove(&server->new_output.link); - wl_list_remove(&server->layer_shell_surface.link); - wl_list_remove(&server->xdg_shell_toplevel.link); - wl_list_remove(&server->server_decoration.link); - wl_list_remove(&server->xdg_decoration.link); - wl_list_remove(&server->pointer_constraint.link); - wl_list_remove(&server->output_manager_apply.link); - wl_list_remove(&server->output_manager_test.link); - wl_list_remove(&server->output_power_manager_set_mode.link); + // remove listeners + wl_list_remove(&server->renderer_lost.link); + wl_list_remove(&server->new_output.link); + wl_list_remove(&server->layer_shell_surface.link); + wl_list_remove(&server->xdg_shell_toplevel.link); + wl_list_remove(&server->server_decoration.link); + wl_list_remove(&server->xdg_decoration.link); + wl_list_remove(&server->pointer_constraint.link); + wl_list_remove(&server->output_manager_apply.link); + wl_list_remove(&server->output_manager_test.link); + wl_list_remove(&server->output_power_manager_set_mode.link); #if WLR_HAS_DRM_BACKEND - if (server->drm_lease_manager) { - wl_list_remove(&server->drm_lease_request.link); - } + if (server->drm_lease_manager) { + wl_list_remove(&server->drm_lease_request.link); + } #endif - wl_list_remove(&server->tearing_control_new_object.link); - wl_list_remove(&server->xdg_activation_v1_request_activate.link); - wl_list_remove(&server->xdg_activation_v1_new_token.link); - wl_list_remove(&server->request_set_cursor_shape.link); - wl_list_remove(&server->new_foreign_toplevel_capture_request.link); - input_manager_finish(server->input); + wl_list_remove(&server->tearing_control_new_object.link); + wl_list_remove(&server->xdg_activation_v1_request_activate.link); + wl_list_remove(&server->xdg_activation_v1_new_token.link); + wl_list_remove(&server->request_set_cursor_shape.link); + wl_list_remove(&server->new_foreign_toplevel_capture_request.link); + input_manager_finish(server->input); - // TODO: free sway-specific resources + // TODO: free sway-specific resources #if WLR_HAS_XWAYLAND - if (server->xwayland.wlr_xwayland != NULL) { - wl_list_remove(&server->xwayland_surface.link); - wl_list_remove(&server->xwayland_ready.link); - wlr_xwayland_destroy(server->xwayland.wlr_xwayland); - } + if (server->xwayland.wlr_xwayland != NULL) { + wl_list_remove(&server->xwayland_surface.link); + wl_list_remove(&server->xwayland_ready.link); + wlr_xwayland_destroy(server->xwayland.wlr_xwayland); + } #endif - wl_display_destroy_clients(server->wl_display); - wlr_backend_destroy(server->backend); - wl_display_destroy(server->wl_display); - list_free(server->dirty_nodes); - free(server->socket); + wl_display_destroy_clients(server->wl_display); + wlr_backend_destroy(server->backend); + wl_display_destroy(server->wl_display); + list_free(server->dirty_nodes); + sfdo_destroy(server->sfdo); + free(server->socket); } bool server_start(struct sway_server *server) { #if WLR_HAS_XWAYLAND - if (config->xwayland != XWAYLAND_MODE_DISABLED) { - sway_log(SWAY_DEBUG, "Initializing Xwayland (lazy=%d)", - config->xwayland == XWAYLAND_MODE_LAZY); - server->xwayland.wlr_xwayland = - wlr_xwayland_create(server->wl_display, server->compositor, - config->xwayland == XWAYLAND_MODE_LAZY); - if (!server->xwayland.wlr_xwayland) { - sway_log(SWAY_ERROR, "Failed to start Xwayland"); - unsetenv("DISPLAY"); - } else { - wl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface, - &server->xwayland_surface); - server->xwayland_surface.notify = handle_xwayland_surface; - wl_signal_add(&server->xwayland.wlr_xwayland->events.ready, - &server->xwayland_ready); - server->xwayland_ready.notify = handle_xwayland_ready; + if (config->xwayland != XWAYLAND_MODE_DISABLED) { + sway_log(SWAY_DEBUG, "Initializing Xwayland (lazy=%d)", + config->xwayland == XWAYLAND_MODE_LAZY); + server->xwayland.wlr_xwayland = + wlr_xwayland_create(server->wl_display, server->compositor, + config->xwayland == XWAYLAND_MODE_LAZY); + if (!server->xwayland.wlr_xwayland) { + sway_log(SWAY_ERROR, "Failed to start Xwayland"); + unsetenv("DISPLAY"); + } else { + wl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface, + &server->xwayland_surface); + server->xwayland_surface.notify = handle_xwayland_surface; + wl_signal_add(&server->xwayland.wlr_xwayland->events.ready, + &server->xwayland_ready); + server->xwayland_ready.notify = handle_xwayland_ready; - setenv("DISPLAY", server->xwayland.wlr_xwayland->display_name, true); + setenv("DISPLAY", server->xwayland.wlr_xwayland->display_name, true); - /* xcursor configured by the default seat */ - } - } + /* xcursor configured by the default seat */ + } + } #endif - if (config->primary_selection) { - wlr_primary_selection_v1_device_manager_create(server->wl_display); - } + if (config->primary_selection) { + wlr_primary_selection_v1_device_manager_create(server->wl_display); + } - sway_log(SWAY_INFO, "Starting backend on wayland display '%s'", - server->socket); - if (!wlr_backend_start(server->backend)) { - sway_log(SWAY_ERROR, "Failed to start backend"); - wlr_backend_destroy(server->backend); - return false; - } + sway_log(SWAY_INFO, "Starting backend on wayland display '%s'", + server->socket); + if (!wlr_backend_start(server->backend)) { + sway_log(SWAY_ERROR, "Failed to start backend"); + wlr_backend_destroy(server->backend); + return false; + } - return true; + // TODO: allow configurability of global sway icon theme if and when + // it is applicable (titlebar icons? ssd icons?) + server->sfdo = sfdo_create("Hicolor"); + + return true; } void server_run(struct sway_server *server) { - sway_log(SWAY_INFO, "Running compositor on wayland display '%s'", - server->socket); - wl_display_run(server->wl_display); + sway_log(SWAY_INFO, "Running compositor on wayland display '%s'", + server->socket); + wl_display_run(server->wl_display); } diff --git a/swaybar/bar.c b/swaybar/bar.c index 4d20f20f0..8f808fa93 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -538,6 +538,7 @@ void bar_teardown(struct swaybar *bar) { #if HAVE_TRAY destroy_tray(bar->tray); #endif + sfdo_destroy(bar->config->sfdo); free_outputs(&bar->outputs); free_outputs(&bar->unused_outputs); free_seats(&bar->seats); diff --git a/swaybar/config.c b/swaybar/config.c index 55bfcb722..eb8b1a064 100644 --- a/swaybar/config.c +++ b/swaybar/config.c @@ -7,6 +7,8 @@ #include "list.h" #include "log.h" +#include "sfdo.h" + uint32_t parse_position(const char *position) { uint32_t horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; @@ -128,8 +130,11 @@ void free_config(struct swaybar_config *config) { wl_list_remove(&tray_bind->link); free_tray_binding(tray_bind); } +#endif free(config->icon_theme); -#endif + sfdo_destroy(config->sfdo); + sway_log(SWAY_DEBUG, "Destroyed swaybar sfdo"); + free(config); } diff --git a/swaybar/image.c b/swaybar/image.c index ed24b9f99..f3ba2082d 100644 --- a/swaybar/image.c +++ b/swaybar/image.c @@ -104,11 +104,27 @@ static cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf( } #endif // HAVE_GDK_PIXBUF -cairo_surface_t *load_image(const char *path) { +cairo_surface_t *load_image(const char *path, int target_size, int scale) { cairo_surface_t *image; #if HAVE_GDK_PIXBUF GError *err = NULL; - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err); + GdkPixbuf *pixbuf = NULL; + // svg images should be loaded at target size. the size read from an svg + // file is only nominal and can lead to an image too small for the avaialble + // space compared to bitmap icons selected at the nearest available size + int i = strlen(path) - 1; + // this is naive and assumes ascii, utf-8, or another encoding + // that encodes these letters as single bytes + if ((i > 2) && + (path[i] == 'g' || path[i] == 'G') && \ + (path[i - 1] == 'v' || path[i - 1] == 'V') && \ + (path[i - 2] == 's' || path[i - 2] == 'S') && \ + (path[i - 3] == '.' )) { + pixbuf = gdk_pixbuf_new_from_file_at_scale(path, -1, target_size, \ + true, &err); + } else { + pixbuf = gdk_pixbuf_new_from_file(path, &err); + } if (!pixbuf) { sway_log(SWAY_ERROR, "Failed to load background image (%s).", err->message); diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 68d8dd32d..2e38775a5 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -18,6 +18,8 @@ #include "stringop.h" #include "util.h" +#include "sfdo.h" + void ipc_send_workspace_command(struct swaybar *bar, const char *ws) { uint32_t size = strlen("workspace \"\"") + strlen(ws); for (size_t i = 0; i < strlen(ws); ++i) { @@ -328,10 +330,14 @@ static bool ipc_parse_config( } } +#endif + + // whether or not there is a tray, we now always have an icon theme if ((json_object_object_get_ex(bar_config, "icon_theme", &icon_theme))) { config->icon_theme = strdup(json_object_get_string(icon_theme)); + sfdo_destroy(config->sfdo); + config->sfdo = sfdo_create(config->icon_theme); } -#endif json_object_put(bar_config); return true; diff --git a/swaybar/meson.build b/swaybar/meson.build index 34bbdeea9..f7b54ba7c 100644 --- a/swaybar/meson.build +++ b/swaybar/meson.build @@ -7,6 +7,10 @@ tray_files = have_tray ? [ ] : [] swaybar_deps = [ + libsfdo_basedir, + libsfdo_desktop, + libsfdo_desktop_file, + libsfdo_icon, cairo, gdk_pixbuf, jsonc, diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index 12929743b..6ee5bf505 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include #include #include @@ -18,6 +20,8 @@ #include "stringop.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" +#include "sfdo.h" + // TODO menu static bool sni_ready(struct swaybar_sni *sni) { @@ -415,25 +419,34 @@ static enum hotspot_event_handling icon_hotspot_callback( return HOTSPOT_PROCESS; } -static void reload_sni(struct swaybar_sni *sni, char *icon_theme, - int target_size) { +static void reload_sni(struct swaybar_sni *sni, char *icon_theme, int target_size) { char *icon_name = sni->status[0] == 'N' ? sni->attention_icon_name : sni->icon_name; if (icon_name) { - list_t *icon_search_paths = create_list(); - list_cat(icon_search_paths, sni->tray->basedirs); - if (sni->icon_theme_path) { - list_add(icon_search_paths, sni->icon_theme_path); + char *icon_path = NULL; + // TODO: at some point we will need to make this scaling-aware + int scale = 1; + struct sfdo *sfdo = sni->tray->bar->config->sfdo; + if (sfdo) { + icon_path = sfdo_icon_lookup_extended(sfdo, icon_name, target_size, scale); + if (!icon_path) { + sway_log(SWAY_DEBUG, "sfdo: icon %s invalid or not found in theme %s at size %d", \ + icon_name, icon_theme, target_size); + } } - char *icon_path = find_icon(sni->tray->themes, icon_search_paths, - icon_name, target_size, icon_theme, - &sni->min_size, &sni->max_size); - list_free(icon_search_paths); if (icon_path) { cairo_surface_destroy(sni->icon); - sni->icon = load_image(icon_path); + sni->icon = load_image(icon_path, target_size, scale); free(icon_path); return; + } else { + // the :( icon won't be drawn for a missing icon and whichever old icon was + // loaded will persist if this is not done. one might not have noticed this + // for tray items that have only one icon loaded only once, successfully or + // unsuccessfully. items with multiple icons, such as fcitx5 or other input + // method frameworks make the problem apparent + free(sni->icon); + sni->icon = NULL; } }