From 66d7539eab2bea859ea3584ca3e62a67b74ef576 Mon Sep 17 00:00:00 2001 From: Guanran Wang Date: Sun, 25 Feb 2024 02:39:46 +0800 Subject: [PATCH] sway: use latest git version https://github.com/swaywm/sway/pull/7226 is merged https://github.com/swaywm/sway/pull/6249 looks dead :( also simplified ./overlays --- nixos/profiles/common/core/default.nix | 7 +- overlays/default.nix | 13 +- .../{nautilus/typeahead.nix => nautilus.nix} | 0 .../offline-mode.nix => prismlauncher.nix} | 0 .../sway/0001-Tray-Implement-dbusmenu.patch | 1754 +++++++++++++++++ .../sway/{tray-dbus-menu.nix => _1.8.nix} | 7 + overlays/sway/_1.9.nix | 32 + overlays/sway/default.nix | 49 + overlays/sway/input-method-popup.nix | 10 - 9 files changed, 1848 insertions(+), 24 deletions(-) rename overlays/{nautilus/typeahead.nix => nautilus.nix} (100%) rename overlays/{prismlauncher/offline-mode.nix => prismlauncher.nix} (100%) create mode 100644 overlays/sway/0001-Tray-Implement-dbusmenu.patch rename overlays/sway/{tray-dbus-menu.nix => _1.8.nix} (66%) create mode 100644 overlays/sway/_1.9.nix create mode 100644 overlays/sway/default.nix delete mode 100644 overlays/sway/input-method-popup.nix diff --git a/nixos/profiles/common/core/default.nix b/nixos/profiles/common/core/default.nix index 2efd75e..05a7221 100644 --- a/nixos/profiles/common/core/default.nix +++ b/nixos/profiles/common/core/default.nix @@ -21,10 +21,9 @@ ]; nixpkgs.overlays = [ - inputs.self.overlays.nautilus.typeahead - inputs.self.overlays.prismlauncher.offline-mode - inputs.self.overlays.sway.input-method-popup - inputs.self.overlays.sway.tray-dbus-menu + inputs.self.overlays.nautilus + inputs.self.overlays.prismlauncher + inputs.self.overlays.sway ]; ### home-manager diff --git a/overlays/default.nix b/overlays/default.nix index 01f7bc1..7d783f8 100644 --- a/overlays/default.nix +++ b/overlays/default.nix @@ -4,14 +4,7 @@ let patches = (old.patches or []) ++ patches; }); in { - nautilus = { - typeahead = import ./nautilus/typeahead.nix {inherit addPatches;}; - }; - prismlauncher = { - offline-mode = import ./prismlauncher/offline-mode.nix {inherit addPatches;}; - }; - sway = { - input-method-popup = import ./sway/input-method-popup.nix {inherit addPatches;}; - tray-dbus-menu = import ./sway/tray-dbus-menu.nix {inherit addPatches;}; - }; + nautilus = import ./nautilus.nix {inherit addPatches;}; + prismlauncher = import ./prismlauncher.nix {inherit addPatches;}; + sway = import ./sway; } diff --git a/overlays/nautilus/typeahead.nix b/overlays/nautilus.nix similarity index 100% rename from overlays/nautilus/typeahead.nix rename to overlays/nautilus.nix diff --git a/overlays/prismlauncher/offline-mode.nix b/overlays/prismlauncher.nix similarity index 100% rename from overlays/prismlauncher/offline-mode.nix rename to overlays/prismlauncher.nix diff --git a/overlays/sway/0001-Tray-Implement-dbusmenu.patch b/overlays/sway/0001-Tray-Implement-dbusmenu.patch new file mode 100644 index 0000000..ca730d9 --- /dev/null +++ b/overlays/sway/0001-Tray-Implement-dbusmenu.patch @@ -0,0 +1,1754 @@ +From c0389b28a6ea0c1917212fb60d19f1d401bdc943 Mon Sep 17 00:00:00 2001 +From: Felix Weilbach +Date: Sun, 30 May 2021 20:45:01 +0200 +Subject: [PATCH] Tray: Implement dbusmenu + +Co-authored-by: Ian Fan +Co-authored-by: Nathan Schulte + +Signed-off-by: Felix Weilbach +--- + include/swaybar/bar.h | 1 + + include/swaybar/input.h | 5 +- + include/swaybar/tray/dbusmenu.h | 27 + + include/swaybar/tray/item.h | 2 + + include/swaybar/tray/tray.h | 3 + + swaybar/bar.c | 4 + + swaybar/input.c | 49 +- + swaybar/meson.build | 3 +- + swaybar/render.c | 2 + + swaybar/tray/dbusmenu.c | 1367 +++++++++++++++++++++++++++++++ + swaybar/tray/item.c | 16 +- + 11 files changed, 1468 insertions(+), 11 deletions(-) + create mode 100644 include/swaybar/tray/dbusmenu.h + create mode 100644 swaybar/tray/dbusmenu.c + +diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h +index 197d2190..d0b3dc81 100644 +--- a/include/swaybar/bar.h ++++ b/include/swaybar/bar.h +@@ -33,6 +33,7 @@ struct swaybar { + struct zxdg_output_manager_v1 *xdg_output_manager; + struct wp_cursor_shape_manager_v1 *cursor_shape_manager; + struct wl_shm *shm; ++ struct xdg_wm_base *wm_base; + + struct swaybar_config *config; + struct status_line *status; +diff --git a/include/swaybar/input.h b/include/swaybar/input.h +index 8ea88a69..81ccaa98 100644 +--- a/include/swaybar/input.h ++++ b/include/swaybar/input.h +@@ -15,6 +15,7 @@ + + struct swaybar; + struct swaybar_output; ++struct swaybar_seat; + + struct swaybar_pointer { + struct wl_pointer *pointer; +@@ -48,8 +49,8 @@ struct swaybar_hotspot { + struct wl_list link; // swaybar_output::hotspots + int x, y, width, height; + enum hotspot_event_handling (*callback)(struct swaybar_output *output, +- struct swaybar_hotspot *hotspot, double x, double y, uint32_t button, +- bool released, void *data); ++ struct swaybar_hotspot *hotspot, struct swaybar_seat *seat, uint32_t serial, ++ double x, double y, uint32_t button, bool released, void *data); + void (*destroy)(void *data); + void *data; + }; +diff --git a/include/swaybar/tray/dbusmenu.h b/include/swaybar/tray/dbusmenu.h +new file mode 100644 +index 00000000..dc90f6e5 +--- /dev/null ++++ b/include/swaybar/tray/dbusmenu.h +@@ -0,0 +1,27 @@ ++#ifndef _SWAYBAR_TRAY_DBUSMENU_H ++#define _SWAYBAR_TRAY_DBUSMENU_H ++ ++#include "swaybar/bar.h" ++#include "swaybar/tray/item.h" ++ ++void swaybar_dbusmenu_open(struct swaybar_sni *sni, ++ struct swaybar_output *output, struct swaybar_seat *seat, uint32_t serial, ++ int x, int y); ++ ++bool dbusmenu_pointer_button(void *data, struct wl_pointer *wl_pointer, ++ uint32_t serial, uint32_t time_, uint32_t button, uint32_t state); ++ ++bool dbusmenu_pointer_motion(struct swaybar_seat *seat, struct wl_pointer *wl_pointer, ++ uint32_t time_, wl_fixed_t surface_x, wl_fixed_t surface_y); ++ ++bool dbusmenu_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); ++ ++bool dbusmenu_pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, ++ struct wl_surface *surface); ++ ++bool dbusmenu_pointer_frame(struct swaybar_seat *data, struct wl_pointer *wl_pointer); ++ ++bool dbusmenu_pointer_axis(struct swaybar_seat *data, struct wl_pointer *wl_pointer); ++ ++#endif +diff --git a/include/swaybar/tray/item.h b/include/swaybar/tray/item.h +index 73937a0c..9a4a00ff 100644 +--- a/include/swaybar/tray/item.h ++++ b/include/swaybar/tray/item.h +@@ -18,6 +18,7 @@ struct swaybar_pixmap { + struct swaybar_sni_slot { + struct wl_list link; // swaybar_sni::slots + struct swaybar_sni *sni; ++ int menu_id; + const char *prop; + const char *type; + void *dest; +@@ -48,6 +49,7 @@ struct swaybar_sni { + char *icon_theme_path; // non-standard KDE property + + struct wl_list slots; // swaybar_sni_slot::link ++ char **menu_icon_theme_paths; + }; + + struct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray); +diff --git a/include/swaybar/tray/tray.h b/include/swaybar/tray/tray.h +index d2e80a6d..853f17cd 100644 +--- a/include/swaybar/tray/tray.h ++++ b/include/swaybar/tray/tray.h +@@ -32,6 +32,9 @@ struct swaybar_tray { + + list_t *basedirs; // char * + list_t *themes; // struct swaybar_theme * ++ ++ struct swaybar_dbusmenu *menu; ++ struct swaybar_dbusmenu_menu *menu_pointer_focus; + }; + + struct swaybar_tray *create_tray(struct swaybar *bar); +diff --git a/swaybar/bar.c b/swaybar/bar.c +index 5b1213a8..b6ed284c 100644 +--- a/swaybar/bar.c ++++ b/swaybar/bar.c +@@ -28,6 +28,7 @@ + #include "pool-buffer.h" + #include "wlr-layer-shell-unstable-v1-client-protocol.h" + #include "xdg-output-unstable-v1-client-protocol.h" ++#include "xdg-shell-client-protocol.h" + + void free_workspaces(struct wl_list *list) { + struct swaybar_workspace *ws, *tmp; +@@ -364,6 +365,8 @@ static void handle_global(void *data, struct wl_registry *registry, + } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { + bar->cursor_shape_manager = wl_registry_bind(registry, name, + &wp_cursor_shape_manager_v1_interface, 1); ++ } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { ++ bar->wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); + } + } + +@@ -538,6 +541,7 @@ void bar_teardown(struct swaybar *bar) { + #if HAVE_TRAY + destroy_tray(bar->tray); + #endif ++ xdg_wm_base_destroy(bar->wm_base); + free_outputs(&bar->outputs); + free_outputs(&bar->unused_outputs); + free_seats(&bar->seats); +diff --git a/swaybar/input.c b/swaybar/input.c +index ada4bc86..dfac5480 100644 +--- a/swaybar/input.c ++++ b/swaybar/input.c +@@ -10,6 +10,10 @@ + #include "swaybar/input.h" + #include "swaybar/ipc.h" + ++#if HAVE_TRAY ++#include "swaybar/tray/dbusmenu.h" ++#endif ++ + void free_hotspots(struct wl_list *list) { + struct swaybar_hotspot *hotspot, *tmp; + wl_list_for_each_safe(hotspot, tmp, list, link) { +@@ -131,10 +135,23 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, + pointer->serial = serial; + update_cursor(seat); + } ++ ++#if HAVE_TRAY ++ if (dbusmenu_pointer_enter(data, wl_pointer, serial, surface, surface_x, ++ surface_y)) { ++ return; ++ } ++#endif + } + + static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface) { ++#if HAVE_TRAY ++ if (dbusmenu_pointer_leave(data, wl_pointer, serial, surface)) { ++ return; ++ } ++#endif ++ + struct swaybar_seat *seat = data; + seat->pointer.current = NULL; + } +@@ -144,6 +161,11 @@ static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, + struct swaybar_seat *seat = data; + seat->pointer.x = wl_fixed_to_double(surface_x); + seat->pointer.y = wl_fixed_to_double(surface_y); ++#if HAVE_TRAY ++ if (dbusmenu_pointer_motion(data, wl_pointer, time, surface_x, surface_y)) { ++ return; ++ } ++#endif + } + + static bool check_bindings(struct swaybar *bar, uint32_t button, +@@ -160,6 +182,7 @@ static bool check_bindings(struct swaybar *bar, uint32_t button, + } + + static bool process_hotspots(struct swaybar_output *output, ++ struct swaybar_seat *seat, uint32_t serial, + double x, double y, uint32_t button, uint32_t state) { + bool released = state == WL_POINTER_BUTTON_STATE_RELEASED; + struct swaybar_hotspot *hotspot; +@@ -167,7 +190,7 @@ static bool process_hotspots(struct swaybar_output *output, + if (x >= hotspot->x && y >= hotspot->y + && x < hotspot->x + hotspot->width + && y < hotspot->y + hotspot->height) { +- if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y, ++ if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, seat, serial, x, y, + button, released, hotspot->data)) { + return true; + } +@@ -180,13 +203,19 @@ static bool process_hotspots(struct swaybar_output *output, + static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { + struct swaybar_seat *seat = data; ++#if HAVE_TRAY ++ if (dbusmenu_pointer_button(seat, wl_pointer, serial, time, button, ++ state)) { ++ return; ++ } ++#endif + struct swaybar_pointer *pointer = &seat->pointer; + struct swaybar_output *output = pointer->current; + if (!sway_assert(output, "button with no active output")) { + return; + } + +- if (process_hotspots(output, pointer->x, pointer->y, button, state)) { ++ if (process_hotspots(output, seat, serial, pointer->x, pointer->y, button, state)) { + return; + } + +@@ -240,7 +269,7 @@ static void process_discrete_scroll(struct swaybar_seat *seat, + struct swaybar_output *output, struct swaybar_pointer *pointer, + uint32_t axis, wl_fixed_t value) { + uint32_t button = wl_axis_to_button(axis, value); +- if (process_hotspots(output, pointer->x, pointer->y, button, WL_POINTER_BUTTON_STATE_PRESSED)) { ++ if (process_hotspots(output, seat, 0, pointer->x, pointer->y, button, WL_POINTER_BUTTON_STATE_PRESSED)) { + // (Currently hotspots don't do anything on release events, so no need to emit one) + return; + } +@@ -297,6 +326,12 @@ static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, + return; + } + ++#if HAVE_TRAY ++ if (dbusmenu_pointer_axis(data, wl_pointer)) { ++ return; ++ } ++#endif ++ + // If there's a while since the last scroll event, + // set 'value' to zero as if to reset the "virtual scroll wheel" + if (seat->axis[axis].discrete_steps == 0 && +@@ -313,6 +348,12 @@ static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { + struct swaybar_pointer *pointer = &seat->pointer; + struct swaybar_output *output = pointer->current; + ++#if HAVE_TRAY ++ if (dbusmenu_pointer_frame(data, wl_pointer)) { ++ return; ++ } ++#endif ++ + if (output == NULL) { + return; + } +@@ -420,7 +461,7 @@ static void wl_touch_up(void *data, struct wl_touch *wl_touch, + } + if (time - slot->time < 500) { + // Tap, treat it like a pointer click +- process_hotspots(slot->output, slot->x, slot->y, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); ++ process_hotspots(slot->output, seat, serial, slot->x, slot->y, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); + // (Currently hotspots don't do anything on release events, so no need to emit one) + } + slot->output = NULL; +diff --git a/swaybar/meson.build b/swaybar/meson.build +index 34bbdeea..baeb74c4 100644 +--- a/swaybar/meson.build ++++ b/swaybar/meson.build +@@ -3,7 +3,8 @@ tray_files = have_tray ? [ + 'tray/icon.c', + 'tray/item.c', + 'tray/tray.c', +- 'tray/watcher.c' ++ 'tray/watcher.c', ++ 'tray/dbusmenu.c' + ] : [] + + swaybar_deps = [ +diff --git a/swaybar/render.c b/swaybar/render.c +index 879a4e42..eea6b35d 100644 +--- a/swaybar/render.c ++++ b/swaybar/render.c +@@ -159,6 +159,7 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color, + + static enum hotspot_event_handling block_hotspot_callback( + struct swaybar_output *output, struct swaybar_hotspot *hotspot, ++ struct swaybar_seat *seat, uint32_t serial, + double x, double y, uint32_t button, bool released, void *data) { + struct i3bar_block *block = data; + struct status_line *status = output->bar->status; +@@ -598,6 +599,7 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx, + + static enum hotspot_event_handling workspace_hotspot_callback( + struct swaybar_output *output, struct swaybar_hotspot *hotspot, ++ struct swaybar_seat *seat, uint32_t serial, + double x, double y, uint32_t button, bool released, void *data) { + if (button != BTN_LEFT) { + return HOTSPOT_PROCESS; +diff --git a/swaybar/tray/dbusmenu.c b/swaybar/tray/dbusmenu.c +new file mode 100644 +index 00000000..77b5d3f5 +--- /dev/null ++++ b/swaybar/tray/dbusmenu.c +@@ -0,0 +1,1367 @@ ++#define _POSIX_C_SOURCE 200809L ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cairo.h" ++#include "cairo_util.h" ++#include "list.h" ++#include "log.h" ++#include "pango.h" ++#include "swaybar/bar.h" ++#include "swaybar/config.h" ++#include "swaybar/image.h" ++#include "swaybar/input.h" ++#include "swaybar/tray/icon.h" ++#include "swaybar/tray/item.h" ++#include "swaybar/tray/tray.h" ++ ++static const char *menu_interface = "com.canonical.dbusmenu"; ++ ++static void swaybar_dbusmenu_get_layout_root(struct swaybar_dbusmenu *menu); ++static void swaybar_dbusmenu_get_layout(struct swaybar_dbusmenu *menu, int id); ++static void swaybar_dbusmenu_draw(struct swaybar_dbusmenu *dbusmenu, int id); ++ ++struct swaybar_dbusmenu_hotspot { ++ int x, y, width, height; ++}; ++ ++struct swaybar_dbusmenu_surface { ++ struct xdg_popup *xdg_popup; ++ struct xdg_surface *xdg_surface; ++ struct wl_surface *surface; ++ struct pool_buffer *current_buffer; ++ struct pool_buffer buffers[2]; ++ int width, height; ++ bool configured; ++}; ++ ++enum menu_toggle_type { ++ MENU_NONE, ++ MENU_CHECKMARK, ++ MENU_RADIO ++}; ++ ++struct swaybar_dbusmenu_menu_item { ++ struct swaybar_dbusmenu_hotspot hotspot; ++ // Set if the item has a submenu ++ struct swaybar_dbusmenu_menu *submenu; ++ // The menu in which the item is displayed ++ struct swaybar_dbusmenu_menu *menu; ++ struct swaybar_dbusmenu_menu_item *parent_item; ++ int id; ++ int toggle_state; ++ char *label; ++ char *icon_name; ++ cairo_surface_t *icon_data; ++ enum menu_toggle_type toggle_type; ++ bool enabled; ++ bool visible; ++ bool is_separator; ++}; ++ ++struct swaybar_dbusmenu_menu { ++ struct swaybar_dbusmenu *dbusmenu; ++ struct swaybar_dbusmenu_surface *surface; ++ struct swaybar_dbusmenu_menu_item *last_hovered_item; ++ list_t *items; // struct swaybar_dbusmenu_menu_item ++ int item_id; ++ int x, y; ++}; ++ ++struct swaybar_dbusmenu { ++ struct swaybar_sni *sni; ++ struct swaybar_output *output; ++ struct swaybar_seat *seat; ++ struct swaybar_dbusmenu_menu *menu; ++ struct swaybar *bar; ++ int serial; ++ int x, y; ++}; ++ ++struct get_layout_callback_data { ++ struct swaybar_dbusmenu *menu; ++ int id; ++}; ++ ++struct png_stream { ++ const void *data; ++ size_t left; ++}; ++ ++static void commit_menu_surface(struct swaybar_dbusmenu_menu *menu) ++{ ++ struct swaybar_dbusmenu_surface * dbusmenu_surface = menu->surface; ++ if (!dbusmenu_surface->configured || dbusmenu_surface->current_buffer == NULL) { ++ return; ++ } ++ ++ struct wl_surface *surface = dbusmenu_surface->surface; ++ wl_surface_set_buffer_scale(surface, menu->dbusmenu->output->scale); ++ wl_surface_attach(surface, dbusmenu_surface->current_buffer->buffer, 0, 0); ++ wl_surface_damage(surface, 0, 0, dbusmenu_surface->width, dbusmenu_surface->height); ++ wl_surface_commit(surface); ++} ++ ++static int handle_items_properties_updated(sd_bus_message *msg, void *data, ++ sd_bus_error *error) { ++ struct swaybar_sni *sni = data; ++ sway_log(SWAY_DEBUG, "%s%s item properties updated", sni->service, sni->menu); ++ ++ // TODO: Optimize. Update only needed properties ++ if (sni->tray->menu) { ++ swaybar_dbusmenu_get_layout_root(sni->tray->menu); ++ } ++ return 0; ++} ++ ++static int handle_layout_updated(sd_bus_message *msg, void *data, ++ sd_bus_error *error) { ++ struct swaybar_sni *sni = data; ++ sway_log(SWAY_DEBUG, "%s%s layout updated", sni->service, sni->menu); ++ ++ int id; ++ sd_bus_message_read(msg, "ui", NULL, &id); ++ if (sni->tray->menu) { ++ swaybar_dbusmenu_get_layout(sni->tray->menu, id); ++ } ++ return 0; ++} ++ ++static int handle_item_activation_requested(sd_bus_message *msg, void *data, ++ sd_bus_error *error) { ++ return 0; // TODO: Implement handling of hotkeys for opening the menu ++} ++ ++static struct swaybar_dbusmenu_surface *swaybar_dbusmenu_surface_create() { ++ struct swaybar_dbusmenu_surface *dbusmenu = calloc(1, ++ sizeof(struct swaybar_dbusmenu_surface)); ++ if (!dbusmenu) { ++ sway_log(SWAY_DEBUG, "Could not allocate dbusmenu"); ++ } ++ return dbusmenu; ++} ++ ++static void xdg_surface_handle_configure(void *data, ++ struct xdg_surface *xdg_surface, uint32_t serial) { ++ xdg_surface_ack_configure(xdg_surface, serial); ++ ++ struct swaybar_dbusmenu_menu *menu = data; ++ menu->surface->configured = true; ++ commit_menu_surface(menu); ++} ++ ++static const struct xdg_surface_listener xdg_surface_listener = { ++ .configure = xdg_surface_handle_configure, ++}; ++ ++static void xdg_popup_configure(void *data, struct xdg_popup *xdg_popup, ++ int32_t x, int32_t y, int32_t width, int32_t height) { ++ // intentionally left blank ++} ++ ++static void destroy_dbusmenu_surface( ++ struct swaybar_dbusmenu_surface *dbusmenu_surface) { ++ if (!dbusmenu_surface) { ++ return; ++ } ++ ++ if (dbusmenu_surface->xdg_popup) { ++ xdg_popup_destroy(dbusmenu_surface->xdg_popup); ++ dbusmenu_surface->xdg_popup = NULL; ++ } ++ if (dbusmenu_surface->surface) { ++ xdg_surface_destroy(dbusmenu_surface->xdg_surface); ++ wl_surface_destroy(dbusmenu_surface->surface); ++ dbusmenu_surface->surface = NULL; ++ } ++ destroy_buffer(&dbusmenu_surface->buffers[0]); ++ destroy_buffer(&dbusmenu_surface->buffers[1]); ++ ++ free(dbusmenu_surface); ++} ++ ++static void close_menu(struct swaybar_dbusmenu_menu *menu) { ++ if (!menu) { ++ return; ++ } ++ ++ if (menu->surface) { ++ destroy_dbusmenu_surface(menu->surface); ++ menu->surface = NULL; ++ ++ int id = menu->item_id; ++ struct swaybar_sni *sni = menu->dbusmenu->sni; ++ sd_bus_call_method_async(sni->tray->bus, NULL, sni->service, sni->menu, ++ menu_interface, "Event", NULL, NULL, "isvu", id, "closed", "y", ++ 0, time(NULL)); ++ sway_log(SWAY_DEBUG, "%s%s closed id %d", sni->service, sni->menu, id); ++ } ++} ++ ++static void close_menus(struct swaybar_dbusmenu_menu *menu) { ++ if (!menu) { ++ return; ++ } ++ ++ if (menu->items) { ++ for (int i = 0; i < menu->items->length; ++i) { ++ struct swaybar_dbusmenu_menu_item *item = menu->items->items[i]; ++ if (item->submenu && item->submenu->item_id != 0) { ++ close_menus(item->submenu); ++ } ++ } ++ } ++ ++ close_menu(menu); ++} ++ ++static void swaybar_dbusmenu_menu_destroy(struct swaybar_dbusmenu_menu *menu) { ++ if (!menu) { ++ return; ++ } ++ ++ if (menu->items) { ++ for (int i = 0; i < menu->items->length; ++i) { ++ struct swaybar_dbusmenu_menu_item *item = menu->items->items[i]; ++ struct swaybar_dbusmenu_menu *child_menu = item->submenu; ++ if (child_menu && child_menu->item_id != 0) { ++ swaybar_dbusmenu_menu_destroy(item->submenu); ++ } ++ free(item->label); ++ free(item->icon_name); ++ free(item->icon_data); ++ free(item); ++ } ++ } ++ list_free(menu->items); ++ free(menu); ++} ++ ++void swaybar_dbusmenu_destroy(struct swaybar_dbusmenu *menu) { ++ if (!menu) { ++ return; ++ } ++ ++ menu->sni->tray->menu = NULL; ++ menu->sni->tray->menu_pointer_focus = NULL; ++ ++ close_menus(menu->menu); ++ swaybar_dbusmenu_menu_destroy(menu->menu); ++ free(menu); ++} ++ ++static void xdg_popup_done(void *data, struct xdg_popup *xdg_popup) { ++ struct swaybar_dbusmenu_menu *menu = data; ++ swaybar_dbusmenu_destroy(menu->dbusmenu); ++} ++ ++static const struct xdg_popup_listener xdg_popup_listener = { ++ .configure = xdg_popup_configure, .popup_done = xdg_popup_done}; ++ ++static struct swaybar_dbusmenu_menu_item * ++find_item_under_menu(struct swaybar_dbusmenu_menu *menu, int item_id) { ++ if (!menu->items) { ++ return NULL; ++ } ++ ++ for (int i = 0; i < menu->items->length; ++i) { ++ struct swaybar_dbusmenu_menu_item *item = menu->items->items[i]; ++ if (item->id == item_id) { ++ return item; ++ } ++ if (item->submenu && item->submenu->item_id != 0) { ++ struct swaybar_dbusmenu_menu_item *found_item = ++ find_item_under_menu(item->submenu, item_id); ++ if (found_item) { ++ return found_item; ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct swaybar_dbusmenu_menu_item * ++find_item(struct swaybar_dbusmenu *dbusmenu, int item_id) { ++ if (!dbusmenu->menu) { ++ return NULL; ++ } ++ ++ return find_item_under_menu(dbusmenu->menu, item_id); ++} ++ ++static struct swaybar_dbusmenu_menu * ++find_parent_menu_under_menu(struct swaybar_dbusmenu_menu *menu, ++ struct swaybar_dbusmenu_menu *child_menu) { ++ if (!menu->items) { ++ return NULL; ++ } ++ ++ for (int i = 0; i < menu->items->length; ++i) { ++ struct swaybar_dbusmenu_menu_item *item = menu->items->items[i]; ++ struct swaybar_dbusmenu_menu *maybe_child_menu = item->submenu; ++ if (maybe_child_menu && maybe_child_menu->item_id != 0) { ++ if (maybe_child_menu == child_menu) { ++ return menu; ++ } ++ maybe_child_menu = find_parent_menu_under_menu(maybe_child_menu, child_menu); ++ if (maybe_child_menu) { ++ return maybe_child_menu; ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct swaybar_dbusmenu_menu * ++find_parent_menu(struct swaybar_dbusmenu_menu *menu) { ++ if (menu && menu->item_id == 0) { ++ return NULL; ++ } ++ struct swaybar_dbusmenu_menu *root_menu = menu->dbusmenu->menu; ++ return find_parent_menu_under_menu(root_menu, menu); ++} ++ ++static bool is_in_hotspot(struct swaybar_dbusmenu_hotspot *hotspot, int x, int y) { ++ if (!hotspot) { ++ return false; ++ } ++ ++ if (hotspot->x <= x && x < hotspot->x + hotspot->width && hotspot->y <= y && ++ y < hotspot->y + hotspot->height) { ++ return true; ++ } ++ ++ return false; ++} ++ ++static void draw_menu_items(cairo_t *cairo, struct swaybar_dbusmenu_menu *menu, ++ int *surface_x, int *surface_y, int *surface_width, int *surface_height, ++ bool open) { ++ struct swaybar_sni *sni = menu->dbusmenu->sni; ++ struct swaybar_tray *tray = sni->tray; ++ struct swaybar_output *output = menu->dbusmenu->output; ++ struct swaybar_config *config = menu->dbusmenu->output->bar->config; ++ ++ int padding = config->tray_padding * output->scale; ++ ++ list_t *items = menu->items; ++ int height = 0; ++ ++ *surface_y = 0; ++ *surface_x = 0; ++ *surface_width = 0; ++ bool is_icon_drawn = false; ++ int icon_size = 0; ++ ++ for (int i = 0; i < items->length; ++i) { ++ struct swaybar_dbusmenu_menu_item *item = items->items[i]; ++ ++ if (!item->visible) { ++ continue; ++ } ++ ++ int new_height = height; ++ if (item->is_separator) { ++ // drawn later, after the width is known ++ new_height = height + output->scale; ++ } else if (item->label) { ++ cairo_move_to(cairo, padding, height + padding); ++ ++ // draw label ++ if (item->enabled) { ++ cairo_set_source_u32(cairo, config->colors.focused_statusline); ++ } else { ++ uint32_t c = config->colors.focused_statusline; ++ uint32_t disabled_color = c - ((c & 0xFF) >> 1); ++ cairo_set_source_u32(cairo, disabled_color); ++ } ++ render_text(cairo, config->font_description, output->scale, false, "%s", ++ item->label); ++ ++ // draw icon or menu indicator if needed ++ int text_height; ++ int text_width; ++ get_text_size(cairo, config->font_description, &text_width, &text_height, ++ NULL, output->scale, false, "%s", item->label); ++ text_width += padding; ++ int size = text_height; ++ int x = -2 * padding - size; ++ int y = height + padding; ++ icon_size = 2 * padding + size; ++ cairo_set_source_u32(cairo, config->colors.focused_statusline); ++ if (item->icon_name) { ++ list_t *icon_search_paths = create_list(); ++ list_cat(icon_search_paths, tray->basedirs); ++ if (sni->menu_icon_theme_paths) { ++ for (char **path = sni->menu_icon_theme_paths; *path; ++path) { ++ list_add(icon_search_paths, *path); ++ } ++ } ++ if (sni->icon_theme_path) { ++ list_add(icon_search_paths, sni->icon_theme_path); ++ } ++ int min_size, max_size; ++ char *icon_path = ++ find_icon(tray->themes, icon_search_paths, item->icon_name, size, ++ config->icon_theme, &min_size, &max_size); ++ list_free(icon_search_paths); ++ ++ if (icon_path) { ++ cairo_surface_t *icon = load_image(icon_path); ++ free(icon_path); ++ cairo_surface_t *icon_scaled = ++ cairo_image_surface_scale(icon, size, size); ++ cairo_surface_destroy(icon); ++ ++ cairo_set_source_surface(cairo, icon_scaled, x, y); ++ cairo_rectangle(cairo, x, y, size, size); ++ cairo_fill(cairo); ++ cairo_surface_destroy(icon_scaled); ++ is_icon_drawn = true; ++ } ++ } else if (item->icon_data) { ++ cairo_surface_t *icon = cairo_image_surface_scale(item->icon_data, size, size); ++ cairo_set_source_surface(cairo, icon, x, y); ++ cairo_rectangle(cairo, x, y, size, size); ++ cairo_fill(cairo); ++ cairo_surface_destroy(icon); ++ is_icon_drawn = true; ++ } else if (item->toggle_type == MENU_CHECKMARK) { ++ cairo_rectangle(cairo, x, y, size, size); ++ cairo_fill(cairo); ++ cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); ++ if (item->toggle_state == 1) { // tick ++ cairo_move_to(cairo, x + size * 3.0 / 4, y + size * 5.0 / 16.0); ++ cairo_line_to(cairo, x + size * 3.0 / 8, y + size * 11.0 / 16.0); ++ cairo_line_to(cairo, x + size / 4.0, y + size * 9.0 / 16.0); ++ cairo_stroke(cairo); ++ } else if (item->toggle_state != 0) { // horizontal line ++ cairo_rectangle(cairo, x + size / 4.0, y + size / 2.0 - 1, ++ size / 2.0, 2); ++ cairo_fill(cairo); ++ } ++ cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); ++ is_icon_drawn = true; ++ } else if (item->toggle_type == MENU_RADIO) { ++ cairo_arc(cairo, x + size / 2.0, y + size / 2.0, size / 2.0, 0, 7); ++ cairo_fill(cairo); ++ if (item->toggle_state == 1) { ++ cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); ++ cairo_arc(cairo, x + size / 2.0, y + size / 2.0, size / 4.0, 0, 7); ++ cairo_fill(cairo); ++ cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); ++ } ++ is_icon_drawn = true; ++ } else if (item->submenu) { // arrowhead ++ cairo_move_to(cairo, x + size / 4.0, y + size / 2.0); ++ cairo_line_to(cairo, x + size * 3.0 / 4, y + size / 4.0); ++ cairo_line_to(cairo, x + size * 3.0 / 4, y + size * 3.0 / 4); ++ cairo_fill(cairo); ++ is_icon_drawn = true; ++ } ++ ++ *surface_width = *surface_width < text_width ? text_width : *surface_width; ++ new_height = height + text_height + 2 * padding; ++ } else { ++ continue; ++ } ++ ++ struct swaybar_dbusmenu_hotspot *hotspot = &item->hotspot; ++ hotspot->y = height; ++ ++ hotspot->y = height; ++ hotspot->height = new_height - height; ++ // x and width is not known at the moment ++ ++ height = new_height; ++ } ++ ++ if (height == 0) { ++ return; ++ } ++ ++ if (is_icon_drawn) { ++ *surface_x = -icon_size - padding; ++ *surface_width += icon_size + padding; ++ } ++ ++ *surface_width += padding; ++ *surface_height = height; ++ ++ // Make sure height and width are divideable by scale ++ // otherwise the menu will not showup ++ if (*surface_width % output->scale != 0) { ++ *surface_width -= *surface_width % output->scale; ++ } ++ if (*surface_height % output->scale != 0) { ++ *surface_height -= *surface_height % output->scale; ++ } ++ ++ cairo_set_line_width(cairo, output->scale); ++ cairo_set_source_u32(cairo, config->colors.focused_separator); ++ for (int i = 0; i < items->length; ++i) { ++ struct swaybar_dbusmenu_menu_item *item = items->items[i]; ++ struct swaybar_dbusmenu_hotspot *hotspot = &item->hotspot; ++ hotspot->x = 0; ++ hotspot->width = *surface_width; ++ if (item->is_separator) { ++ int y = hotspot->y + hotspot->height / 2.0; ++ cairo_move_to(cairo, *surface_x, y); ++ cairo_line_to(cairo, *surface_x + *surface_width, y); ++ cairo_stroke(cairo); ++ } else if (!open && item->enabled && ++ is_in_hotspot(hotspot, ++ tray->menu->seat->pointer.x * output->scale, ++ tray->menu->seat->pointer.y * output->scale)) { ++ cairo_save(cairo); ++ cairo_set_operator(cairo, CAIRO_OPERATOR_DEST_OVER); ++ cairo_rectangle(cairo, *surface_x, hotspot->y, *surface_width, ++ hotspot->height); ++ cairo_set_source_u32(cairo, ++ sni->tray->bar->config->colors.focused_separator); ++ cairo_fill(cairo); ++ cairo_restore(cairo); ++ } ++ } ++} ++ ++struct swaybar_dbusmenu_menu *find_menu_id(struct swaybar_dbusmenu_menu *menu, ++ int id) { ++ if (!menu) { ++ return NULL; ++ } ++ if (menu->item_id == id) { ++ return menu; ++ } ++ ++ if (menu->items) { ++ for (int i = 0; i < menu->items->length; ++i) { ++ struct swaybar_dbusmenu_menu_item *item = menu->items->items[i]; ++ struct swaybar_dbusmenu_menu *child_menu = item->submenu; ++ if (child_menu) { ++ if (child_menu->item_id == id) { ++ return child_menu; ++ } ++ if (child_menu->item_id == 0) { ++ continue; ++ } ++ struct swaybar_dbusmenu_menu *child_child_menu = find_menu_id(child_menu, id); ++ if (child_child_menu) { ++ return child_child_menu; ++ } ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++static void swaybar_dbusmenu_draw_menu(struct swaybar_dbusmenu_menu *menu, ++ int id, bool open) { ++ // For now just search for menu with id ++ struct swaybar_tray *tray = menu->dbusmenu->sni->tray; ++ menu = find_menu_id(menu->dbusmenu->menu, id); ++ if (!menu) { ++ return; ++ } ++ ++ if (!menu->surface) { ++ menu->surface = swaybar_dbusmenu_surface_create(); ++ if (!menu->surface) { ++ sway_log(SWAY_ERROR, "Could not create surface for menu %d", menu->item_id); ++ return; ++ } ++ } ++ ++ cairo_surface_t *recorder = ++ cairo_recording_surface_create(CAIRO_CONTENT_COLOR_ALPHA, NULL); ++ if (!recorder) { ++ return; ++ } ++ cairo_t *cairo = cairo_create(recorder); ++ if (!cairo) { ++ cairo_surface_destroy(recorder); ++ return; ++ } ++ int surface_x, surface_y, surface_width, surface_height = 0; ++ draw_menu_items(cairo, menu, &surface_x, &surface_y, &surface_width, ++ &surface_height, open); ++ ++ struct swaybar *bar = menu->dbusmenu->sni->tray->bar; ++ struct swaybar_dbusmenu_surface *dbusmenu_surface = menu->surface; ++ dbusmenu_surface->current_buffer = get_next_buffer( ++ bar->shm, dbusmenu_surface->buffers, surface_width, surface_height); ++ ++ if (!dbusmenu_surface->current_buffer) { ++ cairo_surface_destroy(recorder); ++ cairo_destroy(cairo); ++ return; ++ } ++ ++ cairo_t *shm = dbusmenu_surface->current_buffer->cairo; ++ cairo_set_operator(shm, CAIRO_OPERATOR_SOURCE); ++ cairo_set_source_u32( ++ shm, menu->dbusmenu->sni->tray->bar->config->colors.focused_background); ++ cairo_paint(shm); ++ ++ cairo_set_operator(shm, CAIRO_OPERATOR_OVER); ++ cairo_set_source_surface(shm, recorder, -surface_x, -surface_y); ++ cairo_paint(shm); ++ ++ cairo_surface_destroy(recorder); ++ cairo_destroy(cairo); ++ ++ if (dbusmenu_surface->width != surface_width || ++ dbusmenu_surface->height != surface_height) { ++ if (dbusmenu_surface->surface) { ++ xdg_surface_destroy(dbusmenu_surface->xdg_surface); ++ dbusmenu_surface->xdg_surface = NULL; ++ wl_surface_destroy(dbusmenu_surface->surface); ++ dbusmenu_surface->surface = NULL; ++ sway_log(SWAY_DEBUG, "Destroy xdg popup"); ++ xdg_popup_destroy(dbusmenu_surface->xdg_popup); ++ dbusmenu_surface->xdg_popup = NULL; ++ } ++ ++ // configure & position popup surface ++ struct wl_surface *surface = wl_compositor_create_surface(bar->compositor); ++ struct xdg_surface *xdg_surface = ++ xdg_wm_base_get_xdg_surface(menu->dbusmenu->bar->wm_base, surface); ++ struct xdg_positioner *positioner = ++ xdg_wm_base_create_positioner(menu->dbusmenu->bar->wm_base); ++ ++ // find the menu item (if any) which requested to open this submenu ++ // to find out on which x and y coordinate the submenu should be drawn ++ struct swaybar_dbusmenu_menu_item *item = ++ find_item(menu->dbusmenu, menu->item_id); ++ struct swaybar_output *output = menu->dbusmenu->output; ++ int x = menu->item_id == 0 ? menu->dbusmenu->x ++ : item->hotspot.x / output->scale; ++ int y = menu->item_id == 0 ? menu->dbusmenu->y ++ : item->hotspot.y / output->scale; ++ ++ xdg_positioner_set_offset(positioner, 0, 0); ++ // Need to divide through scale because surface width/height is scaled ++ xdg_positioner_set_size(positioner, surface_width / output->scale, ++ surface_height / output->scale); ++ ++ int padding = (tray->bar->config->tray_padding * output->scale) / 2; ++ if (bar->config->position & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) { // top bar ++ xdg_positioner_set_anchor(positioner, XDG_POSITIONER_ANCHOR_BOTTOM_LEFT); ++ xdg_positioner_set_gravity(positioner, XDG_POSITIONER_GRAVITY_BOTTOM_LEFT); ++ xdg_positioner_set_anchor_rect(positioner, x, y - padding, 1, 1); ++ } else { ++ xdg_positioner_set_anchor(positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT); ++ xdg_positioner_set_gravity(positioner, XDG_POSITIONER_GRAVITY_TOP_LEFT); ++ xdg_positioner_set_anchor_rect( ++ positioner, x, item->hotspot.height / output->scale, 1, 1); ++ } ++ ++ struct xdg_popup *xdg_popup; ++ struct swaybar_dbusmenu_menu *parent_menu = find_parent_menu(menu); ++ if (!parent_menu) { ++ // Top level menu ++ xdg_popup = xdg_surface_get_popup(xdg_surface, NULL, positioner); ++ zwlr_layer_surface_v1_get_popup(output->layer_surface, xdg_popup); ++ } else { ++ // Nested menu ++ xdg_popup = xdg_surface_get_popup( ++ xdg_surface, parent_menu->surface->xdg_surface, positioner); ++ } ++ xdg_positioner_destroy(positioner); ++ ++ xdg_popup_grab(xdg_popup, menu->dbusmenu->seat->wl_seat, ++ menu->dbusmenu->serial); ++ xdg_popup_add_listener(xdg_popup, &xdg_popup_listener, menu); ++ xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, menu); ++ wl_surface_commit(surface); ++ ++ dbusmenu_surface->xdg_popup = xdg_popup; ++ dbusmenu_surface->xdg_surface = xdg_surface; ++ dbusmenu_surface->surface = surface; ++ dbusmenu_surface->width = surface_width; ++ dbusmenu_surface->height = surface_height; ++ dbusmenu_surface->configured = false; ++ } ++ ++ commit_menu_surface(menu); ++} ++ ++static void swaybar_dbusmenu_draw(struct swaybar_dbusmenu *dbusmenu, int id) { ++ if (!dbusmenu || !dbusmenu->menu) { ++ sway_log(SWAY_ERROR, "Can not draw dbusmenu, menu structure not initialized yet!"); ++ return; ++ } ++ swaybar_dbusmenu_draw_menu(dbusmenu->menu, id, true); ++} ++ ++static cairo_status_t read_png_stream(void *closure, unsigned char *data, ++ unsigned int length) { ++ struct png_stream *png_stream = closure; ++ if (length > png_stream->left) { ++ return CAIRO_STATUS_READ_ERROR; ++ } ++ memcpy(data, png_stream->data, length); ++ png_stream->data += length; ++ png_stream->left -= length; ++ return CAIRO_STATUS_SUCCESS; ++} ++ ++static cairo_surface_t *read_png(const void *data, size_t data_size) { ++ struct png_stream png_stream = {0}; ++ png_stream.data = data; ++ png_stream.left = data_size; ++ cairo_surface_t *surface = ++ cairo_image_surface_create_from_png_stream(read_png_stream, &png_stream); ++ ++ if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS) { ++ return surface; ++ } ++ ++ cairo_surface_destroy(surface); ++ return NULL; ++} ++ ++static int about_to_show_callback(sd_bus_message *msg, void *data, ++ sd_bus_error *error) { ++ struct swaybar_sni_slot *slot = data; ++ struct swaybar_sni *sni = slot->sni; ++ int menu_id = slot->menu_id; ++ wl_list_remove(&slot->link); ++ free(slot); ++ ++ int need_update; ++ sd_bus_message_read_basic(msg, 'b', &need_update); ++ if (need_update) { ++ swaybar_dbusmenu_get_layout(sni->tray->menu, menu_id); ++ } ++ ++ swaybar_dbusmenu_draw(sni->tray->menu, menu_id); ++ ++ sd_bus_call_method_async(sni->tray->bus, NULL, sni->service, sni->menu, ++ menu_interface, "Event", NULL, NULL, "isvu", menu_id, "opened", "y", 0, ++ time(NULL)); ++ ++ sway_log(SWAY_DEBUG, "%s%s opened id %d", sni->service, sni->menu, menu_id); ++ ++ return 0; ++} ++ ++static void open_menu_id(struct swaybar_dbusmenu *dbusmenu, int menu_id) { ++ struct swaybar_dbusmenu_menu *menu = find_menu_id(dbusmenu->menu, menu_id); ++ if (!menu || menu->surface) { ++ // menu could not be found or is already shown ++ return; ++ } ++ ++ struct swaybar_sni *sni = dbusmenu->sni; ++ struct swaybar_sni_slot *slot = calloc(1, sizeof(struct swaybar_sni_slot)); ++ slot->sni = sni; ++ slot->menu_id = menu_id; ++ ++ int ret = sd_bus_call_method_async(sni->tray->bus, &slot->slot, sni->service, ++ sni->menu, menu_interface, "AboutToShow", about_to_show_callback, slot, "i", ++ menu_id); ++ ++ if (ret >= 0) { ++ wl_list_insert(&sni->slots, &slot->link); ++ } else { ++ sway_log(SWAY_ERROR, "%s%s failed to send AboutToShow signal: %s", ++ sni->service, sni->menu, strerror(-ret)); ++ free(slot); ++ } ++} ++ ++static int update_item_properties(struct swaybar_dbusmenu_menu_item *item, ++ sd_bus_message *msg) { ++ sd_bus_message_enter_container(msg, 'a', "{sv}"); ++ while (!sd_bus_message_at_end(msg, 0)) { ++ sd_bus_message_enter_container(msg, 'e', "sv"); ++ char *key, *log_value; ++ sd_bus_message_read_basic(msg, 's', &key); ++ if (strcmp(key, "type") == 0) { ++ char *type; ++ sd_bus_message_read(msg, "v", "s", &type); ++ item->is_separator = strcmp(type, "separator") == 0; ++ log_value = type; ++ } else if (strcmp(key, "label") == 0) { ++ char *label; ++ sd_bus_message_read(msg, "v", "s", &label); ++ item->label = realloc(item->label, strlen(label) + 1); ++ if (!item->label) { ++ return -ENOMEM; ++ } ++ int i = 0; ++ for (char *c = label; *c; ++c) { ++ if (*c == '_' && !*++c) { ++ break; ++ } ++ item->label[i++] = *c; ++ } ++ item->label[i] = '\0'; ++ log_value = label; ++ } else if (strcmp(key, "enabled") == 0) { ++ int enabled; ++ sd_bus_message_read(msg, "v", "b", &enabled); ++ item->enabled = enabled; ++ log_value = item->enabled ? "true" : "false"; ++ } else if (strcmp(key, "visible") == 0) { ++ int visible; ++ sd_bus_message_read(msg, "v", "b", &visible); ++ item->visible = visible; ++ log_value = item->visible ? "true" : "false"; ++ } else if (strcmp(key, "icon-name") == 0) { ++ sd_bus_message_read(msg, "v", "s", &item->icon_name); ++ item->icon_name = strdup(item->icon_name); ++ log_value = item->icon_name; ++ } else if (strcmp(key, "icon-data") == 0) { ++ const void *data; ++ size_t data_size; ++ sd_bus_message_enter_container(msg, 'v', "ay"); ++ sd_bus_message_read_array(msg, 'y', &data, &data_size); ++ sd_bus_message_exit_container(msg); ++ item->icon_data = read_png(data, data_size); ++ log_value = item->icon_data ? "" : ""; ++ } else if (strcmp(key, "toggle-type") == 0) { ++ char *toggle_type; ++ sd_bus_message_read(msg, "v", "s", &toggle_type); ++ if (strcmp(toggle_type, "checkmark") == 0) { ++ item->toggle_type = MENU_CHECKMARK; ++ } else if (strcmp(toggle_type, "radio") == 0) { ++ item->toggle_type = MENU_RADIO; ++ } ++ log_value = toggle_type; ++ } else if (strcmp(key, "toggle-state") == 0) { ++ sd_bus_message_read(msg, "v", "i", &item->toggle_state); ++ log_value = item->toggle_state == 0 ? ++ "off" : item->toggle_state == 1 ? "on" : "indeterminate"; ++ } else if (strcmp(key, "children-display") == 0) { ++ char *children_display; ++ sd_bus_message_read(msg, "v", "s", &children_display); ++ if (strcmp(children_display, "submenu") == 0) { ++ struct swaybar_dbusmenu_menu *submenu; ++ if (item->id != 0) { ++ submenu = calloc(1, sizeof(struct swaybar_dbusmenu_menu)); ++ if (!submenu) { ++ sway_log(SWAY_ERROR, "Could not allocate submenu"); ++ return -ENOMEM; ++ } ++ } else { ++ submenu = item->menu; ++ } ++ submenu->item_id = item->id; ++ submenu->dbusmenu = item->menu->dbusmenu; ++ item->submenu = submenu; ++ } ++ log_value = children_display; ++ } else { ++ // Ignored: shortcut, disposition ++ sd_bus_message_skip(msg, "v"); ++ log_value = ""; ++ } ++ sd_bus_message_exit_container(msg); ++ sway_log(SWAY_DEBUG, "%s%s %s = '%s'", item->menu->dbusmenu->sni->service, ++ item->menu->dbusmenu->sni->menu, key, log_value); ++ } ++ return sd_bus_message_exit_container(msg); ++} ++ ++static int get_layout_callback(sd_bus_message *msg, void *data, ++ sd_bus_error *error) { ++ struct swaybar_sni_slot *slot = data; ++ struct swaybar_sni *sni = slot->sni; ++ int menu_id = slot->menu_id; ++ wl_list_remove(&slot->link); ++ free(slot); ++ ++ struct swaybar_dbusmenu *dbusmenu = sni->tray->menu; ++ if (dbusmenu == NULL) { ++ return 0; ++ } ++ ++ if (sd_bus_message_is_method_error(msg, NULL)) { ++ sway_log(SWAY_ERROR, "%s%s failed to get layout: %s", ++ dbusmenu->sni->service, dbusmenu->sni->menu, ++ sd_bus_message_get_error(msg)->message); ++ return sd_bus_message_get_errno(msg); ++ } ++ ++ // Parse the layout. The layout comes as a recursive structure as ++ // dbus message in the following form (ia{sv}av) ++ ++ // Skip the menu revision ++ sd_bus_message_skip(msg, "u"); ++ ++ sni->tray->menu_pointer_focus = NULL; ++ ++ bool already_open = false; ++ struct swaybar_dbusmenu_menu *menu_to_update = ++ find_menu_id(dbusmenu->menu, menu_id); ++ if (menu_to_update && menu_to_update->surface) { ++ already_open = true; ++ } ++ ++ if (dbusmenu->menu) { ++ close_menus(dbusmenu->menu); ++ swaybar_dbusmenu_menu_destroy(dbusmenu->menu); ++ dbusmenu->menu = NULL; ++ } ++ ++ struct swaybar_dbusmenu_menu_item *parent_item = NULL; ++ struct swaybar_dbusmenu_menu *menu = calloc(1, ++ sizeof(struct swaybar_dbusmenu_menu)); ++ if (!menu) { ++ sway_log(SWAY_ERROR, "Could not allocate menu"); ++ return -ENOMEM; ++ } ++ dbusmenu->menu = menu; ++ menu->dbusmenu = dbusmenu; ++ int ret = 0; ++ while (!sd_bus_message_at_end(msg, 1)) { ++ sd_bus_message_enter_container(msg, 'r', "ia{sv}av"); ++ ++ struct swaybar_dbusmenu_menu_item *item ++ = calloc(1, sizeof(struct swaybar_dbusmenu_menu_item)); ++ if (!item) { ++ ret = -ENOMEM; ++ break; ++ } ++ ++ // default properties ++ item->parent_item = parent_item; ++ item->menu = menu; ++ item->enabled = true; ++ item->visible = true; ++ item->toggle_state = -1; ++ ++ // Read the id ++ sd_bus_message_read_basic(msg, 'i', &item->id); ++ ++ // Process a{sv}. a{sv} contains key-value pairs ++ ret = update_item_properties(item, msg); ++ if (!menu->items) { ++ menu->items = create_list(); ++ } ++ list_add(menu->items, item); ++ if (ret < 0) { ++ break; ++ } ++ if (item->id != 0 && item->submenu) { ++ menu = item->submenu; ++ } ++ ++ sd_bus_message_enter_container(msg, 'a', "v"); ++ ++ parent_item = item; ++ while (parent_item && sd_bus_message_at_end(msg, 0)) { ++ if (parent_item->submenu) { ++ menu = find_parent_menu(menu); ++ } ++ parent_item = parent_item->parent_item; ++ ++ sd_bus_message_exit_container(msg); ++ sd_bus_message_exit_container(msg); ++ sd_bus_message_exit_container(msg); ++ } ++ ++ if (parent_item) { ++ sd_bus_message_enter_container(msg, 'v', "(ia{sv}av)"); ++ } ++ } ++ ++ if (already_open) { ++ swaybar_dbusmenu_draw(sni->tray->menu, menu_id); ++ } else { ++ open_menu_id(dbusmenu, 0); ++ } ++ ++ return 0; ++} ++ ++static void swaybar_dbusmenu_subscribe_signal(struct swaybar_dbusmenu *menu, ++ const char *signal_name, sd_bus_message_handler_t callback) { ++ int ret = sd_bus_match_signal_async( menu->sni->tray->bus, NULL, ++ menu->sni->service, menu->sni->menu, menu_interface, signal_name, callback, ++ NULL, menu->sni); ++ ++ if (ret < 0) { ++ sway_log(SWAY_ERROR, "%s%s failed to subscribe to signal %s: %s", ++ menu->sni->service, menu->sni->menu, signal_name, strerror(-ret)); ++ } ++} ++ ++static void swaybar_dbusmenu_setup_signals(struct swaybar_dbusmenu *menu) { ++ swaybar_dbusmenu_subscribe_signal(menu, "ItemsPropertiesUpdated", ++ handle_items_properties_updated); ++ swaybar_dbusmenu_subscribe_signal(menu, "LayoutUpdated", ++ handle_layout_updated); ++ swaybar_dbusmenu_subscribe_signal(menu, "ItemActivationRequested", ++ handle_item_activation_requested); ++} ++ ++static void swaybar_dbusmenu_get_layout(struct swaybar_dbusmenu *menu, int id) { ++ if (menu == NULL) { ++ return; ++ } ++ ++ struct swaybar_sni_slot *slot = calloc(1, sizeof(struct swaybar_sni_slot)); ++ if (slot == NULL) { ++ sway_log(SWAY_ERROR, "Could not allocate swaybar_sni_slot"); ++ return; ++ } ++ slot->sni = menu->sni; ++ slot->menu_id = id; ++ ++ int ret = ++ sd_bus_call_method_async(menu->sni->tray->bus, NULL, menu->sni->service, ++ menu->sni->menu, menu_interface, "GetLayout", ++ get_layout_callback, slot, "iias", id, -1, NULL); ++ ++ if (ret >= 0) { ++ wl_list_insert(&menu->sni->slots, &slot->link); ++ } else { ++ sway_log(SWAY_ERROR, "%s%s failed to call method GetLayout: %s", ++ menu->sni->service, menu->sni->menu, strerror(-ret)); ++ free(slot); ++ } ++} ++ ++static void swaybar_dbusmenu_get_layout_root(struct swaybar_dbusmenu *menu) { ++ swaybar_dbusmenu_get_layout(menu, 0); ++} ++ ++static int get_icon_theme_path_callback(sd_bus_message *msg, void *data, ++ sd_bus_error *error) { ++ struct swaybar_sni_slot *slot = data; ++ struct swaybar_sni *sni = slot->sni; ++ wl_list_remove(&slot->link); ++ free(slot); ++ ++ int ret; ++ if (!sd_bus_message_is_method_error(msg, NULL)) { ++ ret = sd_bus_message_enter_container(msg, 'v', NULL); ++ if (ret >= 0) { ++ ret = sd_bus_message_read_strv(msg, &sni->menu_icon_theme_paths); ++ } ++ } else { ++ ret = -sd_bus_message_get_errno(msg); ++ } ++ ++ if (ret < 0) { ++ sway_log(SWAY_ERROR, "%s%s failed to read IconThemePath: %s", sni->service, ++ sni->menu, strerror(-ret)); ++ } ++ return ret; ++} ++ ++static void swaybar_dbusmenu_setup(struct swaybar_dbusmenu *menu) { ++ struct swaybar_sni_slot *slot = calloc(1, sizeof(struct swaybar_sni_slot)); ++ slot->sni = menu->sni; ++ int ret = sd_bus_call_method_async( menu->sni->tray->bus, &slot->slot, ++ menu->sni->service, menu->sni->path, "org.freedesktop.DBus.Properties", ++ "Get", get_icon_theme_path_callback, slot, "ss", menu->sni->interface, ++ "IconThemePath"); ++ if (ret >= 0) { ++ wl_list_insert(&menu->sni->slots, &slot->link); ++ } else { ++ sway_log(SWAY_ERROR, "%s%s failed to get IconThemePath: %s", ++ menu->sni->service, menu->sni->menu, strerror(-ret)); ++ free(slot); ++ } ++ ++ swaybar_dbusmenu_setup_signals(menu); ++ swaybar_dbusmenu_get_layout_root(menu); ++} ++ ++void swaybar_dbusmenu_open(struct swaybar_sni *sni, ++ struct swaybar_output *output, struct swaybar_seat *seat, uint32_t serial, ++ int x, int y) { ++ struct swaybar_dbusmenu *dbusmenu = sni->tray->menu; ++ if (!dbusmenu) { ++ dbusmenu = calloc(1, sizeof(struct swaybar_dbusmenu)); ++ if (!dbusmenu) { ++ sway_log(SWAY_DEBUG, "Could not allocate dbusmenu"); ++ return; ++ } ++ sni->tray->menu = dbusmenu; ++ } ++ ++ dbusmenu->sni = sni; ++ dbusmenu->output = output; ++ dbusmenu->seat = seat; ++ dbusmenu->serial = serial; ++ dbusmenu->x = seat->pointer.x; ++ dbusmenu->y = seat->pointer.y; ++ dbusmenu->bar = output->bar; ++ ++ swaybar_dbusmenu_setup(dbusmenu); ++} ++ ++static void close_child_menus_except(struct swaybar_dbusmenu_menu *menu, ++ int id) { ++ if (!menu || !menu->items) { ++ return; ++ } ++ // close all child menus of menu, except the child menu with the given id ++ for (int i = 0; i < menu->items->length; ++i) { ++ struct swaybar_dbusmenu_menu_item *item = menu->items->items[i]; ++ if (item->id == id) { ++ continue; ++ } ++ struct swaybar_dbusmenu_menu *menu = item->submenu; ++ if (menu && menu->item_id != 0) { ++ close_menus(menu); ++ } ++ } ++} ++ ++static void ++pointer_motion_process_item(struct swaybar_dbusmenu_menu *focused_menu, ++ struct swaybar_dbusmenu_menu_item *item, struct swaybar_seat *seat) { ++ int scale = focused_menu->dbusmenu->output->scale; ++ double x = seat->pointer.x * scale; ++ double y = seat->pointer.y * scale; ++ if (is_in_hotspot(&item->hotspot, x, y) && item->enabled && ++ !item->is_separator) { ++ struct swaybar_tray *tray = focused_menu->dbusmenu->sni->tray; ++ struct swaybar_sni *sni = tray->menu->sni; ++ if (focused_menu->last_hovered_item != item) { ++ sd_bus_call_method_async(tray->bus, NULL, sni->service, sni->menu, ++ menu_interface, "Event", NULL, NULL, "isvu", item->id, "hovered", ++ "y", 0, time(NULL)); ++ ++ sway_log(SWAY_DEBUG, "%s%s hovered id %d", sni->service, sni->menu, ++ item->id); ++ ++ // open child menu if current item has a child menu and close other ++ // potential open child menus. Only one child menu can be open at a time ++ close_child_menus_except(focused_menu, item->id); ++ open_menu_id(focused_menu->dbusmenu, item->id); ++ focused_menu->last_hovered_item = item; ++ ++ // a different item needs to be highlighted ++ swaybar_dbusmenu_draw_menu(focused_menu, focused_menu->item_id, false); ++ } ++ ++ } ++} ++ ++bool dbusmenu_pointer_motion(struct swaybar_seat *seat, ++ struct wl_pointer *wl_pointer, uint32_t time_, wl_fixed_t surface_x, ++ wl_fixed_t surface_y) { ++ struct swaybar_tray *tray = seat->bar->tray; ++ struct swaybar_dbusmenu_menu *focused_menu = tray->menu_pointer_focus; ++ if (!(tray && tray->menu && focused_menu)) { ++ return false; ++ } ++ ++ for (int i = 0; i < focused_menu->items->length; ++i) { ++ struct swaybar_dbusmenu_menu_item *item = focused_menu->items->items[i]; ++ pointer_motion_process_item(focused_menu, item, seat); ++ } ++ ++ return true; ++} ++ ++static struct swaybar_dbusmenu_menu * ++dbusmenu_menu_find_menu_surface(struct swaybar_dbusmenu_menu *menu, ++ struct wl_surface *surface) { ++ if (menu->surface && menu->surface->surface == surface) { ++ return menu; ++ } ++ if (!menu->items) { ++ return NULL; ++ } ++ for (int i = 0; i < menu->items->length; ++i) { ++ struct swaybar_dbusmenu_menu_item *item = menu->items->items[i]; ++ struct swaybar_dbusmenu_menu *child_menu = item->submenu; ++ if (child_menu && child_menu->surface ++ && child_menu->surface->surface == surface) { ++ return child_menu; ++ } ++ if (child_menu) { ++ if (child_menu->item_id == 0) { ++ continue; ++ } ++ struct swaybar_dbusmenu_menu *child_child_menu = ++ dbusmenu_menu_find_menu_surface(child_menu, surface); ++ if (child_child_menu != NULL) { ++ return child_child_menu; ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++static void close_menus_by_id(struct swaybar_dbusmenu_menu *menu, int item_id) { ++ if (menu->items == NULL) { ++ return; ++ } ++ for (int i = 0; i < menu->items->length; ++i) { ++ struct swaybar_dbusmenu_menu_item *item = menu->items->items[i]; ++ struct swaybar_dbusmenu_menu *child_menu = item->submenu; ++ if (child_menu && child_menu->item_id == item_id && child_menu->item_id != 0) { ++ close_menus(child_menu); ++ } ++ } ++} ++ ++static void close_unfocused_child_menus(struct swaybar_dbusmenu_menu *menu, ++ struct swaybar_seat *seat) { ++ for (int i = 0; i < menu->items->length; ++i) { ++ struct swaybar_dbusmenu_menu_item *item = menu->items->items[i]; ++ ++ int scale = menu->dbusmenu->output->scale; ++ int x = seat->pointer.x * scale; ++ int y = seat->pointer.y * scale; ++ if (item->submenu && item->submenu->item_id != 0 ++ && !is_in_hotspot(&item->hotspot, x, y)) { ++ close_menus_by_id(menu, item->id); ++ } ++ } ++} ++ ++bool dbusmenu_pointer_frame(struct swaybar_seat *data, ++ struct wl_pointer *wl_pointer) { ++ struct swaybar_tray *tray = data->bar->tray; ++ if (!(tray && tray->menu && tray->menu_pointer_focus)) { ++ return false; ++ } ++ return true; ++} ++ ++bool dbusmenu_pointer_axis(struct swaybar_seat *data, ++ struct wl_pointer *wl_pointer) { ++ struct swaybar_tray *tray = data->bar->tray; ++ if (!(tray && tray->menu && tray->menu_pointer_focus)) { ++ return false; ++ } ++ return true; ++} ++ ++bool dbusmenu_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 swaybar_seat *seat = data; ++ struct swaybar_tray *tray = seat->bar->tray; ++ if (!(tray && tray->menu)) { ++ return false; ++ } ++ ++ struct swaybar_dbusmenu_menu *new_focused_menu = ++ dbusmenu_menu_find_menu_surface(tray->menu->menu, surface); ++ ++ // Check if there are any child menus ++ bool has_child_menus = false; ++ if (new_focused_menu && new_focused_menu->items) { ++ for (int i = 0; i < new_focused_menu->items->length; ++i) { ++ struct swaybar_dbusmenu_menu_item *item = new_focused_menu->items->items[i]; ++ if (item->submenu && item->submenu->item_id != 0) { ++ has_child_menus = true; ++ } ++ } ++ } ++ ++ if (has_child_menus) { ++ close_unfocused_child_menus(new_focused_menu, seat); ++ } ++ ++ tray->menu_pointer_focus = new_focused_menu; ++ ++ return true; ++} ++ ++bool dbusmenu_pointer_leave(void *data, struct wl_pointer *wl_pointer, ++ uint32_t serial, struct wl_surface *surface) { ++ struct swaybar_seat *seat = data; ++ struct swaybar_tray *tray = seat->bar->tray; ++ if (!(tray && tray->menu)) { ++ return false; ++ } ++ ++ tray->menu_pointer_focus = NULL; ++ ++ return true; ++} ++ ++static bool dbusmenu_pointer_button_left_process_item(struct swaybar_dbusmenu *dbusmenu, ++ struct swaybar_dbusmenu_menu_item *item, struct swaybar_seat *seat) { ++ struct swaybar_sni *sni = dbusmenu->sni; ++ struct swaybar_tray *tray = sni->tray; ++ int scale = dbusmenu->output->scale; ++ ++ if (is_in_hotspot(&item->hotspot, seat->pointer.x * scale, ++ seat->pointer.y * scale)) { ++ if (!item->enabled || item->is_separator) { ++ return false; ++ } ++ ++ sway_log(SWAY_DEBUG, "%s%s menu clicked id %d", sni->service, sni->menu, ++ item->id); ++ ++ sd_bus_call_method_async(tray->bus, NULL, sni->service, sni->menu, ++ menu_interface, "Event", NULL, NULL, "isvu", item->id, "clicked", "y", 0, ++ time(NULL)); ++ ++ if (item->submenu) { ++ open_menu_id(dbusmenu, item->id); ++ } else { ++ // The user clicked an menu item other than a submenu. That means ++ // the user made it's choise. Close the tray menu. ++ swaybar_dbusmenu_destroy(tray->menu); ++ } ++ return true; ++ } ++ ++ return false; ++} ++ ++static bool dbusmenu_pointer_button_left(struct swaybar_dbusmenu *dbusmenu, ++ struct swaybar_seat *seat) { ++ struct swaybar_dbusmenu_menu *focused_menu ++ = dbusmenu->sni->tray->menu_pointer_focus; ++ ++ if (!focused_menu) { ++ return true; ++ } ++ ++ for (int i = 0; i < focused_menu->items->length; ++i) { ++ struct swaybar_dbusmenu_menu_item *item = focused_menu->items->items[i]; ++ if (dbusmenu_pointer_button_left_process_item(dbusmenu, item, seat)) { ++ return true; ++ } ++ } ++ ++ return true; ++} ++ ++bool dbusmenu_pointer_button(void *data, struct wl_pointer *wl_pointer, ++ uint32_t serial, uint32_t time_, uint32_t button, uint32_t state) { ++ struct swaybar_seat *seat = data; ++ struct swaybar_tray *tray = seat->bar->tray; ++ if (!(tray && tray->menu)) { ++ return false; ++ } ++ ++ if (state != WL_POINTER_BUTTON_STATE_PRESSED) { ++ // intentionally left blank ++ return true; ++ } else if (!tray->menu_pointer_focus) { ++ swaybar_dbusmenu_destroy(tray->menu); ++ return true; ++ } else if (button == BTN_LEFT) { ++ return dbusmenu_pointer_button_left(tray->menu, seat); ++ } ++ ++ return false; ++} +diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c +index ca6c03ad..29bc8436 100644 +--- a/swaybar/tray/item.c ++++ b/swaybar/tray/item.c +@@ -8,6 +8,7 @@ + #include "swaybar/config.h" + #include "swaybar/image.h" + #include "swaybar/input.h" ++#include "swaybar/tray/dbusmenu.h" + #include "swaybar/tray/host.h" + #include "swaybar/tray/icon.h" + #include "swaybar/tray/item.h" +@@ -332,8 +333,9 @@ void destroy_sni(struct swaybar_sni *sni) { + free(sni); + } + +-static void handle_click(struct swaybar_sni *sni, int x, int y, +- uint32_t button, int delta) { ++static void handle_click(struct swaybar_sni *sni, struct swaybar_output *output, ++ struct swaybar_seat *seat, uint32_t serial, int x, int y, uint32_t button, ++ int delta) { + const char *method = NULL; + struct tray_binding *binding = NULL; + wl_list_for_each(binding, &sni->tray->bar->config->tray_bindings, link) { +@@ -364,7 +366,11 @@ static void handle_click(struct swaybar_sni *sni, int x, int y, + method = "ContextMenu"; + } + +- if (strncmp(method, "Scroll", strlen("Scroll")) == 0) { ++ if (strcmp(method, "ContextMenu") == 0) { ++ if (sni->menu && !sni->tray->menu) { ++ swaybar_dbusmenu_open(sni, output, seat, serial, x, y); ++ } ++ } else if (strncmp(method, "Scroll", strlen("Scroll")) == 0) { + char dir = method[strlen("Scroll")]; + char *orientation = (dir == 'U' || dir == 'D') ? "vertical" : "horizontal"; + int sign = (dir == 'U' || dir == 'L') ? -1 : 1; +@@ -384,6 +390,7 @@ static int cmp_sni_id(const void *item, const void *cmp_to) { + + static enum hotspot_event_handling icon_hotspot_callback( + struct swaybar_output *output, struct swaybar_hotspot *hotspot, ++ struct swaybar_seat *seat, uint32_t serial, + double x, double y, uint32_t button, bool released, void *data) { + sway_log(SWAY_DEBUG, "Clicked on %s", (char *)data); + +@@ -405,7 +412,8 @@ static enum hotspot_event_handling icon_hotspot_callback( + (int) output->output_height - config->gaps.bottom - y); + + sway_log(SWAY_DEBUG, "Guessing click position at (%d, %d)", global_x, global_y); +- handle_click(sni, global_x, global_y, button, 1); // TODO get delta from event ++ // TODO get delta from event ++ handle_click(sni, output, seat, serial, global_x, global_y, button, 1); + return HOTSPOT_IGNORE; + } else { + sway_log(SWAY_DEBUG, "but it doesn't exist"); +-- +2.43.0 + diff --git a/overlays/sway/tray-dbus-menu.nix b/overlays/sway/_1.8.nix similarity index 66% rename from overlays/sway/tray-dbus-menu.nix rename to overlays/sway/_1.8.nix index c814598..ef48dbe 100644 --- a/overlays/sway/tray-dbus-menu.nix +++ b/overlays/sway/_1.8.nix @@ -1,5 +1,12 @@ {addPatches, ...}: _final: prev: { sway-unwrapped = addPatches prev.sway-unwrapped [ + # text_input: Implement input-method popups + # https://github.com/swaywm/sway/pull/7226 + (prev.fetchpatch { + url = "https://github.com/swaywm/sway/commit/d1c6e44886d1047b3aa6ff6aaac383eadd72f36a.patch"; + hash = "sha256-LsCoK60FKp3d8qopGtrbCFXofxHT+kOv1e1PiLSyvsA="; + }) + # Tray D-Bus Menu # https://github.com/swaywm/sway/pull/6249 (prev.runCommand "2f304ef0532a45d00b2ec2c7fc63adef0aec7607.patch" {} '' diff --git a/overlays/sway/_1.9.nix b/overlays/sway/_1.9.nix new file mode 100644 index 0000000..6363c24 --- /dev/null +++ b/overlays/sway/_1.9.nix @@ -0,0 +1,32 @@ +_final: prev: { + sway-unwrapped = prev.sway-unwrapped.overrideAttrs rec { + version = "1.9"; + + src = prev.fetchFromGitHub { + owner = "swaywm"; + repo = "sway"; + rev = version; + hash = "sha256-/6+iDkQfdLcL/pTJaqNc6QdP4SRVOYLjfOItEu/bZtg"; + }; + + buildInputs = with prev; [ + cairo + gdk-pixbuf + json_c + libGL + libdrm + libevdev + libinput + librsvg + libxkbcommon + pango + pcre2 + wayland + wayland-protocols + wlroots_0_17 + xorg.xcbutilwm + ]; + + patches = []; + }; +} diff --git a/overlays/sway/default.nix b/overlays/sway/default.nix new file mode 100644 index 0000000..c35e946 --- /dev/null +++ b/overlays/sway/default.nix @@ -0,0 +1,49 @@ +_final: prev: { + sway-unwrapped = prev.sway-unwrapped.overrideAttrs (old: { + version = "1.10-unstable-2024-02-23"; + src = prev.fetchFromGitHub { + owner = "swaywm"; + repo = "sway"; + rev = "fc640d5f6c82883c35e90a64f0098486e6091293"; + hash = "sha256-n0U1RoSv3fuOkey2gXPX+O4mRA2PCASpkRrIE5069BI"; + }; + + buildInputs = with prev; [ + (wlroots.overrideAttrs { + version = "1.18.0-unstable-2024-02-23"; + src = prev.fetchFromGitLab { + domain = "gitlab.freedesktop.org"; + owner = "wlroots"; + repo = "wlroots"; + rev = "54e1fefd2e29cb00dff7c02801913d793ceab7d6"; + hash = "sha256-VX2AAzcYl255yF43+uetcOS+uYzAVfk7WccWONpjmYU="; + }; + }) + + cairo + gdk-pixbuf + json_c + libGL + libdrm + libevdev + libinput + librsvg + libxkbcommon + pango + pcre2 + wayland + wayland-protocols + xorg.xcbutilwm + ]; + + patches = let + removePatches = ["LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM.patch"]; + in + builtins.filter (patch: !builtins.elem (patch.name or null) removePatches) (old.patches or []) + ++ [ + # rebased version of https://github.com/swaywm/sway/pull/6249 + # using sed at this point is just stupid (see ./_1.8.nix) + ./0001-Tray-Implement-dbusmenu.patch + ]; + }); +} diff --git a/overlays/sway/input-method-popup.nix b/overlays/sway/input-method-popup.nix deleted file mode 100644 index b0799b6..0000000 --- a/overlays/sway/input-method-popup.nix +++ /dev/null @@ -1,10 +0,0 @@ -{addPatches, ...}: _final: prev: { - sway-unwrapped = addPatches prev.sway-unwrapped [ - # text_input: Implement input-method popups - # https://github.com/swaywm/sway/pull/7226 - (prev.fetchpatch { - url = "https://github.com/swaywm/sway/commit/d1c6e44886d1047b3aa6ff6aaac383eadd72f36a.patch"; - hash = "sha256-LsCoK60FKp3d8qopGtrbCFXofxHT+kOv1e1PiLSyvsA="; - }) - ]; -}