From 382e8af418a7e1b8cf93d3398509b93c6874cb0d Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Wed, 28 Mar 2018 21:21:36 -0400 Subject: [PATCH 01/34] Allow sway IPC clients to fall back to i3 socket --- common/ipc-client.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/common/ipc-client.c b/common/ipc-client.c index 582c5e86..117e9910 100644 --- a/common/ipc-client.c +++ b/common/ipc-client.c @@ -1,4 +1,4 @@ -#define _POSIX_C_SOURCE 2 +#define _POSIX_C_SOURCE 200809L #include #include #include @@ -14,13 +14,31 @@ static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; static const size_t ipc_header_size = sizeof(ipc_magic)+8; char *get_socketpath(void) { - FILE *fp = popen("sway --get-socketpath", "r"); - if (!fp) { - return NULL; + const char *swaysock = getenv("SWAYSOCK"); + if (swaysock) { + return strdup(swaysock); } - char *line = read_line(fp); - pclose(fp); - return line; + FILE *fp = popen("sway --get-socketpath 2>/dev/null", "r"); + if (fp) { + char *line = read_line(fp); + pclose(fp); + if (line && *line) { + return line; + } + } + const char *i3sock = getenv("I3SOCK"); + if (i3sock) { + return strdup(i3sock); + } + fp = popen("i3 --get-socketpath 2>/dev/null", "r"); + if (fp) { + char *line = read_line(fp); + pclose(fp); + if (line && *line) { + return line; + } + } + return NULL; } int ipc_open_socket(const char *socket_path) { From cab1352801b62d1b8a12ca1c995cb24445ce4bc9 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Wed, 28 Mar 2018 23:04:20 -0400 Subject: [PATCH 02/34] Start port of swaybar to layer shell This starts up the event loop and wayland display and shims out the basic top level rendering concepts. Also includes some changes to incorporate pango into the 1.x codebase properly. --- common/meson.build | 3 + common/pango.c | 67 +++++ include/pango.h | 16 ++ include/sway/config.h | 7 +- include/swaybar/bar.h | 89 ++---- include/swaybar/config.h | 43 +-- include/swaybar/event_loop.h | 4 +- include/swaybar/ipc.h | 22 +- include/swaybar/render.h | 22 +- meson.build | 3 + swaybar/bar.c | 444 +++++++---------------------- swaybar/config.c | 37 +-- swaybar/event_loop.c | 10 +- swaybar/ipc.c | 410 --------------------------- swaybar/main.c | 33 ++- swaybar/meson.build | 25 ++ swaybar/render.c | 388 +++---------------------- swaybar/status_line.c | 530 ----------------------------------- swaybar/tray/dbus.c | 197 ------------- swaybar/tray/icon.c | 400 -------------------------- swaybar/tray/sni.c | 481 ------------------------------- swaybar/tray/sni_watcher.c | 497 -------------------------------- swaybar/tray/tray.c | 398 -------------------------- 23 files changed, 342 insertions(+), 3784 deletions(-) create mode 100644 common/pango.c create mode 100644 include/pango.h delete mode 100644 swaybar/ipc.c create mode 100644 swaybar/meson.build delete mode 100644 swaybar/status_line.c delete mode 100644 swaybar/tray/dbus.c delete mode 100644 swaybar/tray/icon.c delete mode 100644 swaybar/tray/sni.c delete mode 100644 swaybar/tray/sni_watcher.c delete mode 100644 swaybar/tray/tray.c diff --git a/common/meson.build b/common/meson.build index 01736ca6..4ad47077 100644 --- a/common/meson.build +++ b/common/meson.build @@ -1,5 +1,7 @@ deps = [ cairo, + pango, + pangocairo, wlroots ] @@ -14,6 +16,7 @@ lib_sway_common = static_library( 'ipc-client.c', 'log.c', 'list.c', + 'pango.c', 'readline.c', 'stringop.c', 'util.c' diff --git a/common/pango.c b/common/pango.c new file mode 100644 index 00000000..212d96cf --- /dev/null +++ b/common/pango.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, + const char *text, int32_t scale, bool markup) { + PangoLayout *layout = pango_cairo_create_layout(cairo); + PangoAttrList *attrs; + if (markup) { + char *buf; + pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, NULL); + pango_layout_set_markup(layout, buf, -1); + free(buf); + } else { + attrs = pango_attr_list_new(); + pango_layout_set_text(layout, text, -1); + } + pango_attr_list_insert(attrs, pango_attr_scale_new(scale)); + PangoFontDescription *desc = pango_font_description_from_string(font); + pango_layout_set_font_description(layout, desc); + pango_layout_set_single_paragraph_mode(layout, 1); + pango_layout_set_attributes(layout, attrs); + pango_attr_list_unref(attrs); + pango_font_description_free(desc); + return layout; +} + +void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, + int32_t scale, bool markup, const char *fmt, ...) { + char *buf = malloc(2048); + + va_list args; + va_start(args, fmt); + if (vsnprintf(buf, 2048, fmt, args) >= 2048) { + strcpy(buf, "[buffer overflow]"); + } + va_end(args); + + PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); + pango_cairo_update_layout(cairo, layout); + pango_layout_get_pixel_size(layout, width, height); + g_object_unref(layout); + free(buf); +} + +void pango_printf(cairo_t *cairo, const char *font, + int32_t scale, bool markup, const char *fmt, ...) { + char *buf = malloc(2048); + + va_list args; + va_start(args, fmt); + if (vsnprintf(buf, 2048, fmt, args) >= 2048) { + strcpy(buf, "[buffer overflow]"); + } + va_end(args); + + PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); + pango_cairo_update_layout(cairo, layout); + pango_cairo_show_layout(cairo, layout); + g_object_unref(layout); + free(buf); +} diff --git a/include/pango.h b/include/pango.h new file mode 100644 index 00000000..f6325f28 --- /dev/null +++ b/include/pango.h @@ -0,0 +1,16 @@ +#ifndef _SWAY_PANGO_H +#define _SWAY_PANGO_H +#include +#include +#include +#include +#include + +PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, + const char *text, int32_t scale, bool markup); +void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, + int32_t scale, bool markup, const char *fmt, ...); +void pango_printf(cairo_t *cairo, const char *font, + int32_t scale, bool markup, const char *fmt, ...); + +#endif diff --git a/include/sway/config.h b/include/sway/config.h index 48a8b0ab..8c9e04de 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -1,17 +1,16 @@ #ifndef _SWAY_CONFIG_H #define _SWAY_CONFIG_H - #define PID_WORKSPACE_TIMEOUT 60 - #include #include #include +#include #include #include -#include #include "list.h" #include "layout.h" #include "container.h" +#include "wlr-layer-shell-unstable-v1-protocol.h" /** * Describes a variable created via the `set` command. @@ -152,7 +151,7 @@ struct bar_config { char *id; uint32_t modifier; list_t *outputs; - //enum desktop_shell_panel_position position; // TODO + char *position; list_t *bindings; char *status_command; bool pango_markup; diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 50d36e76..3ae8c0b3 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -1,72 +1,45 @@ #ifndef _SWAYBAR_BAR_H #define _SWAYBAR_BAR_H - -#include "client/registry.h" -#include "client/window.h" +#include +#include "pool-buffer.h" #include "list.h" -struct bar { - struct config *config; - struct status_line *status; - list_t *outputs; - struct output *focused_output; +struct swaybar_config; +struct swaybar_output; +struct swaybar_workspace; + +struct swaybar { + struct wl_display *display; + struct wl_compositor *compositor; + struct zwlr_layer_shell_v1 *layer_shell; + struct wl_shm *shm; - int ipc_event_socketfd; - int ipc_socketfd; - int status_read_fd; - int status_write_fd; - pid_t status_command_pid; + struct swaybar_config *config; + struct swaybar_output *focused_output; + + struct wl_list outputs; }; -struct output { - struct window *window; - struct registry *registry; - list_t *workspaces; -#ifdef ENABLE_TRAY - list_t *items; -#endif +struct swaybar_output { + struct wl_list link; + struct swaybar *bar; + struct wl_output *output; + struct wl_surface *surface; + struct zwlr_layer_surface_v1 *layer_surface; + char *name; int idx; bool focused; -}; -struct workspace { - int num; - char *name; - bool focused; - bool visible; - bool urgent; + uint32_t width, height; + struct pool_buffer buffers[2]; + struct pool_buffer *current_buffer; }; -/** Global bar state */ -extern struct bar swaybar; +void bar_setup(struct swaybar *bar, + const char *socket_path, + const char *bar_id); +void bar_run(struct swaybar *bar); +void bar_teardown(struct swaybar *bar); -/** True if sway needs to render */ -extern bool dirty; - -/** - * Setup bar. - */ -void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id); - -/** - * Create new output struct from name. - */ -struct output *new_output(const char *name); - -/** - * Bar mainloop. - */ -void bar_run(struct bar *bar); - -/** - * free workspace list. - */ -void free_workspaces(list_t *workspaces); - -/** - * Teardown bar. - */ -void bar_teardown(struct bar *bar); - -#endif /* _SWAYBAR_BAR_H */ +#endif diff --git a/include/swaybar/config.h b/include/swaybar/config.h index 651f0ee3..1bfe4843 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h @@ -1,9 +1,7 @@ #ifndef _SWAYBAR_CONFIG_H #define _SWAYBAR_CONFIG_H - -#include #include - +#include #include "list.h" #include "util.h" @@ -19,10 +17,10 @@ struct box_colors { /** * Swaybar config. */ -struct config { +struct swaybar_config { char *status_command; bool pango_markup; - uint32_t position; + uint32_t position; // zwlr_layer_surface_v1_anchor char *font; char *sep_symbol; char *mode; @@ -32,18 +30,6 @@ struct config { bool workspace_buttons; bool all_outputs; list_t *outputs; - -#ifdef ENABLE_TRAY - // Tray - char *tray_output; - char *icon_theme; - - uint32_t tray_padding; - uint32_t activate_button; - uint32_t context_button; - uint32_t secondary_button; -#endif - int height; struct { @@ -63,24 +49,7 @@ struct config { } colors; }; -/** - * Parse position top|bottom|left|right. - */ -uint32_t parse_position(const char *position); - -/** - * Parse font. - */ -char *parse_font(const char *font); - -/** - * Initialize default sway config. - */ -struct config *init_config(); - -/** - * Free config struct. - */ -void free_config(struct config *config); +struct swaybar_config *init_config(); +void free_config(struct swaybar_config *config); -#endif /* _SWAYBAR_CONFIG_H */ +#endif diff --git a/include/swaybar/event_loop.h b/include/swaybar/event_loop.h index a0cde07f..99f6ed36 100644 --- a/include/swaybar/event_loop.h +++ b/include/swaybar/event_loop.h @@ -1,6 +1,5 @@ #ifndef _SWAYBAR_EVENT_LOOP_H #define _SWAYBAR_EVENT_LOOP_H - #include #include @@ -23,4 +22,5 @@ bool remove_timer(timer_t timer); void event_loop_poll(); void init_event_loop(); -#endif /*_SWAYBAR_EVENT_LOOP_H */ + +#endif diff --git a/include/swaybar/ipc.h b/include/swaybar/ipc.h index c11931d0..57a1b925 100644 --- a/include/swaybar/ipc.h +++ b/include/swaybar/ipc.h @@ -1,23 +1,9 @@ #ifndef _SWAYBAR_IPC_H #define _SWAYBAR_IPC_H +#include "swaybar/bar.h" -#include "bar.h" - -/** - * Initialize ipc connection to sway and get sway state, outputs, bar_config. - */ -void ipc_bar_init(struct bar *bar, const char *bar_id); - -/** - * Handle ipc event from sway. - */ -bool handle_ipc_event(struct bar *bar); - - -/** - * Send workspace command to sway - */ +void ipc_bar_init(struct swaybar *bar, const char *bar_id); +bool handle_ipc_event(struct swaybar *bar); void ipc_send_workspace_command(const char *workspace_name); -#endif /* _SWAYBAR_IPC_H */ - +#endif diff --git a/include/swaybar/render.h b/include/swaybar/render.h index 114f43f4..071e2298 100644 --- a/include/swaybar/render.h +++ b/include/swaybar/render.h @@ -1,22 +1,10 @@ #ifndef _SWAYBAR_RENDER_H #define _SWAYBAR_RENDER_H -#include "config.h" -#include "bar.h" +struct swaybar; +struct swaybar_output; +struct swaybar_config; -/** - * Render swaybar. - */ -void render(struct output *output, struct config *config, struct status_line *line); +void render_frame(struct swaybar *bar, struct swaybar_output *output); -/** - * Set window height and modify internal spacing accordingly. - */ -void set_window_height(struct window *window, int height); - -/** - * Compute the size of a workspace name - */ -void workspace_button_size(struct window *window, const char *workspace_name, int *width, int *height); - -#endif /* _SWAYBAR_RENDER_H */ +#endif diff --git a/meson.build b/meson.build index b681f43a..49824b30 100644 --- a/meson.build +++ b/meson.build @@ -35,6 +35,7 @@ pixman = dependency('pixman-1') libcap = dependency('libcap') libinput = dependency('libinput') math = cc.find_library('m') +rt = cc.find_library('rt') git = find_program('git', required: false) a2x = find_program('a2x', required: false) @@ -99,8 +100,10 @@ subdir('protocols') subdir('common') subdir('sway') subdir('swaymsg') + subdir('client') subdir('swaybg') +subdir('swaybar') config = configuration_data() config.set('sysconfdir', join_paths(prefix, sysconfdir)) diff --git a/swaybar/bar.c b/swaybar/bar.c index f12923a8..e1d594b4 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -1,390 +1,146 @@ #define _XOPEN_SOURCE 500 +#include +#include +#include +#include +#include #include -#include #include -#include -#include #include -#include -#include -#ifdef __FreeBSD__ -#include -#else -#include -#endif -#ifdef ENABLE_TRAY -#include -#include "swaybar/tray/sni_watcher.h" -#include "swaybar/tray/tray.h" -#include "swaybar/tray/sni.h" -#endif -#include "swaybar/ipc.h" +#include +#include +#include #include "swaybar/render.h" #include "swaybar/config.h" -#include "swaybar/status_line.h" #include "swaybar/event_loop.h" #include "swaybar/bar.h" -#include "ipc-client.h" #include "list.h" -#include "log.h" +#include "pango.h" +#include "pool-buffer.h" +#include "wlr-layer-shell-unstable-v1-client-protocol.h" -static void bar_init(struct bar *bar) { +static void bar_init(struct swaybar *bar) { bar->config = init_config(); - bar->status = init_status_line(); - bar->outputs = create_list(); + wl_list_init(&bar->outputs); } -static void spawn_status_cmd_proc(struct bar *bar) { - if (bar->config->status_command) { - int pipe_read_fd[2]; - int pipe_write_fd[2]; - - if (pipe(pipe_read_fd) != 0) { - sway_log(L_ERROR, "Unable to create pipes for status_command fork"); - return; - } - if (pipe(pipe_write_fd) != 0) { - sway_log(L_ERROR, "Unable to create pipe for status_command fork (write)"); - close(pipe_read_fd[0]); - close(pipe_read_fd[1]); - return; - } - - bar->status_command_pid = fork(); - if (bar->status_command_pid == 0) { - close(pipe_read_fd[0]); - dup2(pipe_read_fd[1], STDOUT_FILENO); - close(pipe_read_fd[1]); - - dup2(pipe_write_fd[0], STDIN_FILENO); - close(pipe_write_fd[0]); - close(pipe_write_fd[1]); - - char *const cmd[] = { - "sh", - "-c", - bar->config->status_command, - NULL, - }; - execvp(cmd[0], cmd); - return; - } - - close(pipe_read_fd[1]); - bar->status_read_fd = pipe_read_fd[0]; - fcntl(bar->status_read_fd, F_SETFL, O_NONBLOCK); - - close(pipe_write_fd[0]); - bar->status_write_fd = pipe_write_fd[1]; - fcntl(bar->status_write_fd, F_SETFL, O_NONBLOCK); - } -} - -struct output *new_output(const char *name) { - struct output *output = malloc(sizeof(struct output)); +struct swaybar_output *new_output(const char *name) { + struct swaybar_output *output = malloc(sizeof(struct swaybar_output)); output->name = strdup(name); - output->window = NULL; - output->registry = NULL; - output->workspaces = create_list(); -#ifdef ENABLE_TRAY - output->items = create_list(); -#endif return output; } -static void mouse_button_notify(struct window *window, int x, int y, - uint32_t button, uint32_t state_w) { - sway_log(L_DEBUG, "Mouse button %d clicked at %d %d %d", button, x, y, state_w); - if (!state_w) { - return; - } - - struct output *clicked_output = NULL; - for (int i = 0; i < swaybar.outputs->length; i++) { - struct output *output = swaybar.outputs->items[i]; - if (window == output->window) { - clicked_output = output; - break; - } - } - - if (!sway_assert(clicked_output != NULL, "Got pointer event for non-existing output")) { - return; - } - - double button_x = 0.5; - for (int i = 0; i < clicked_output->workspaces->length; i++) { - struct workspace *workspace = clicked_output->workspaces->items[i]; - int button_width, button_height; - - workspace_button_size(window, workspace->name, &button_width, &button_height); - - button_x += button_width; - if (x <= button_x) { - ipc_send_workspace_command(workspace->name); - break; - } - } - - switch (button) { - case BTN_LEFT: - status_line_mouse_event(&swaybar, x, y, 1); - break; - case BTN_MIDDLE: - status_line_mouse_event(&swaybar, x, y, 2); - break; - case BTN_RIGHT: - status_line_mouse_event(&swaybar, x, y, 3); - break; - } - -#ifdef ENABLE_TRAY - tray_mouse_event(clicked_output, x, y, button, state_w); -#endif - +static void layer_surface_configure(void *data, + struct zwlr_layer_surface_v1 *surface, + uint32_t serial, uint32_t width, uint32_t height) { + struct swaybar_output *output = data; + output->width = width; + output->height = height; + zwlr_layer_surface_v1_ack_configure(surface, serial); + render_frame(output->bar, output); } -static void mouse_scroll_notify(struct window *window, enum scroll_direction direction) { - sway_log(L_DEBUG, "Mouse wheel scrolled %s", direction == SCROLL_UP ? "up" : "down"); - - // If there are status blocks and click_events are enabled - // check if the position is within the status area and if so - // tell the status line to output the event and skip workspace - // switching below. - int num_blocks = swaybar.status->block_line->length; - if (swaybar.status->click_events && num_blocks > 0) { - struct status_block *first_block = swaybar.status->block_line->items[0]; - int x = window->pointer_input.last_x; - int y = window->pointer_input.last_y; - if (x > first_block->x) { - if (direction == SCROLL_UP) { - status_line_mouse_event(&swaybar, x, y, 4); - } else { - status_line_mouse_event(&swaybar, x, y, 5); - } - return; - } - } +static void layer_surface_closed(void *_output, + struct zwlr_layer_surface_v1 *surface) { + // TODO: Deal with hotplugging + struct swaybar_output *output = output; + zwlr_layer_surface_v1_destroy(output->layer_surface); + wl_surface_destroy(output->surface); +} - if (!swaybar.config->wrap_scroll) { - // Find output this window lives on - int i; - struct output *output = NULL; - for (i = 0; i < swaybar.outputs->length; ++i) { - output = swaybar.outputs->items[i]; - if (output->window == window) { - break; - } - } - if (!sway_assert(i != swaybar.outputs->length, "Unknown window in scroll event")) { - return; - } - int focused = -1; - for (i = 0; i < output->workspaces->length; ++i) { - struct workspace *ws = output->workspaces->items[i]; - if (ws->focused) { - focused = i; - break; - } - } - if (!sway_assert(focused != -1, "Scroll wheel event received on inactive output")) { - return; - } - if ((focused == 0 && direction == SCROLL_UP) || - (focused == output->workspaces->length - 1 && direction == SCROLL_DOWN)) { - // Do not wrap - return; - } +struct zwlr_layer_surface_v1_listener layer_surface_listener = { + .configure = layer_surface_configure, + .closed = layer_surface_closed, +}; + +static void handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) { + struct swaybar *bar = data; + if (strcmp(interface, wl_compositor_interface.name) == 0) { + bar->compositor = wl_registry_bind(registry, name, + &wl_compositor_interface, 1); + } else if (strcmp(interface, wl_shm_interface.name) == 0) { + bar->shm = wl_registry_bind(registry, name, + &wl_shm_interface, 1); + } else if (strcmp(interface, wl_output_interface.name) == 0) { + static int idx = 0; + struct swaybar_output *output = + calloc(1, sizeof(struct swaybar_output)); + output->bar = bar; + output->output = wl_registry_bind(registry, name, + &wl_output_interface, 1); + output->idx = idx++; + wl_list_insert(&bar->outputs, &output->link); + } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { + bar->layer_shell = wl_registry_bind( + registry, name, &zwlr_layer_shell_v1_interface, 1); } +} - const char *workspace_name = direction == SCROLL_UP ? "prev_on_output" : "next_on_output"; - ipc_send_workspace_command(workspace_name); +static void handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) { + // who cares } -void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id) { - /* initialize bar with default values */ - bar_init(bar); +static const struct wl_registry_listener registry_listener = { + .global = handle_global, + .global_remove = handle_global_remove, +}; - /* Initialize event loop lists */ +void bar_setup(struct swaybar *bar, + const char *socket_path, const char *bar_id) { + bar_init(bar); init_event_loop(); - /* connect to sway ipc */ - bar->ipc_socketfd = ipc_open_socket(socket_path); - bar->ipc_event_socketfd = ipc_open_socket(socket_path); - - ipc_bar_init(bar, bar_id); - - int i; - for (i = 0; i < bar->outputs->length; ++i) { - struct output *bar_output = bar->outputs->items[i]; - - bar_output->registry = registry_poll(); - - if (!bar_output->registry->desktop_shell) { - sway_abort("swaybar requires the compositor to support the desktop-shell extension."); - } - - struct output_state *output = bar_output->registry->outputs->items[bar_output->idx]; - - bar_output->window = window_setup(bar_output->registry, - output->width / output->scale, 30, output->scale, false); - if (!bar_output->window) { - sway_abort("Failed to create window."); - } - desktop_shell_set_panel(bar_output->registry->desktop_shell, - output->output, bar_output->window->surface); - desktop_shell_set_panel_position(bar_output->registry->desktop_shell, + assert(bar->display = wl_display_connect(NULL)); + + struct wl_registry *registry = wl_display_get_registry(bar->display); + wl_registry_add_listener(registry, ®istry_listener, bar); + wl_display_roundtrip(bar->display); + assert(bar->compositor && bar->layer_shell && bar->shm); + + // TODO: we might not necessarily be meant to do all of the outputs + struct swaybar_output *output; + wl_list_for_each(output, &bar->outputs, link) { + assert(output->surface = wl_compositor_create_surface(bar->compositor)); + output->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + bar->layer_shell, output->surface, output->output, + ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel"); + assert(output->layer_surface); + zwlr_layer_surface_v1_add_listener(output->layer_surface, + &layer_surface_listener, output); + zwlr_layer_surface_v1_set_anchor(output->layer_surface, bar->config->position); - - window_make_shell(bar_output->window); - - /* set font */ - bar_output->window->font = bar->config->font; - - /* set mouse event callbacks */ - bar_output->window->pointer_input.notify_button = mouse_button_notify; - bar_output->window->pointer_input.notify_scroll = mouse_scroll_notify; - - /* set window height */ - set_window_height(bar_output->window, bar->config->height); + render_frame(bar, output); } - /* spawn status command */ - spawn_status_cmd_proc(bar); - -#ifdef ENABLE_TRAY - init_tray(bar); -#endif } -bool dirty = true; - -static void respond_ipc(int fd, short mask, void *_bar) { - struct bar *bar = (struct bar *)_bar; - sway_log(L_DEBUG, "Got IPC event."); - dirty = handle_ipc_event(bar); -} - -static void respond_command(int fd, short mask, void *_bar) { - struct bar *bar = (struct bar *)_bar; - dirty = handle_status_line(bar); -} - -static void respond_output(int fd, short mask, void *_output) { - struct output *output = (struct output *)_output; - if (wl_display_dispatch(output->registry->display) == -1) { - sway_log(L_ERROR, "failed to dispatch wl: %d", errno); +static void display_in(int fd, short mask, void *_bar) { + struct swaybar *bar = (struct swaybar *)_bar; + if (wl_display_dispatch(bar->display) == -1) { + wlr_log(L_ERROR, "failed to dispatch wl: %d", errno); } } -void bar_run(struct bar *bar) { - add_event(bar->ipc_event_socketfd, POLLIN, respond_ipc, bar); - add_event(bar->status_read_fd, POLLIN, respond_command, bar); - - int i; - for (i = 0; i < bar->outputs->length; ++i) { - struct output *output = bar->outputs->items[i]; - add_event(wl_display_get_fd(output->registry->display), - POLLIN, respond_output, output); - } - +void bar_run(struct swaybar *bar) { + add_event(wl_display_get_fd(bar->display), POLLIN, display_in, bar); while (1) { - if (dirty) { - int i; - for (i = 0; i < bar->outputs->length; ++i) { - struct output *output = bar->outputs->items[i]; - if (window_prerender(output->window) && output->window->cairo) { - render(output, bar->config, bar->status); - window_render(output->window); - wl_display_flush(output->registry->display); - } - } - } - - dirty = false; - event_loop_poll(); -#ifdef ENABLE_TRAY - dispatch_dbus(); -#endif } } -void free_workspaces(list_t *workspaces) { - int i; - for (i = 0; i < workspaces->length; ++i) { - struct workspace *ws = workspaces->items[i]; - free(ws->name); - free(ws); +static void free_outputs(struct wl_list *list) { + struct swaybar_output *output, *tmp; + wl_list_for_each_safe(output, tmp, list, link) { + wl_list_remove(&output->link); + free(output->name); + free(output); } - list_free(workspaces); } -static void free_output(struct output *output) { - window_teardown(output->window); - if (output->registry) { - registry_teardown(output->registry); - } - - free(output->name); - - if (output->workspaces) { - free_workspaces(output->workspaces); - } - - free(output); -} - -static void free_outputs(list_t *outputs) { - int i; - for (i = 0; i < outputs->length; ++i) { - free_output(outputs->items[i]); - } - list_free(outputs); -} - -static void terminate_status_command(pid_t pid) { - if (pid) { - // terminate status_command process - int ret = kill(pid, SIGTERM); - if (ret != 0) { - sway_log(L_ERROR, "Unable to terminate status_command [pid: %d]", pid); - } else { - int status; - waitpid(pid, &status, 0); - } - } -} - -void bar_teardown(struct bar *bar) { +void bar_teardown(struct swaybar *bar) { + free_outputs(&bar->outputs); if (bar->config) { free_config(bar->config); } - - if (bar->outputs) { - free_outputs(bar->outputs); - } - - if (bar->status) { - free_status_line(bar->status); - } - - /* close sockets/pipes */ - if (bar->status_read_fd) { - close(bar->status_read_fd); - } - - if (bar->status_write_fd) { - close(bar->status_write_fd); - } - - if (bar->ipc_socketfd) { - close(bar->ipc_socketfd); - } - - if (bar->ipc_event_socketfd) { - close(bar->ipc_event_socketfd); - } - - /* terminate status command process */ - terminate_status_command(bar->status_command_pid); } diff --git a/swaybar/config.c b/swaybar/config.c index 8fe552f2..0c2b57e0 100644 --- a/swaybar/config.c +++ b/swaybar/config.c @@ -1,21 +1,24 @@ #define _XOPEN_SOURCE 500 #include #include -#include "wayland-desktop-shell-client-protocol.h" -#include "log.h" #include "swaybar/config.h" +#include "wlr-layer-shell-unstable-v1-client-protocol.h" uint32_t parse_position(const char *position) { + uint32_t horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + uint32_t vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; if (strcmp("top", position) == 0) { - return DESKTOP_SHELL_PANEL_POSITION_TOP; + return ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | horiz; } else if (strcmp("bottom", position) == 0) { - return DESKTOP_SHELL_PANEL_POSITION_BOTTOM; + return ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | horiz; } else if (strcmp("left", position) == 0) { - return DESKTOP_SHELL_PANEL_POSITION_LEFT; + return ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | vert; } else if (strcmp("right", position) == 0) { - return DESKTOP_SHELL_PANEL_POSITION_RIGHT; + return ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | vert; } else { - return DESKTOP_SHELL_PANEL_POSITION_BOTTOM; + return ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | horiz; } } @@ -30,11 +33,11 @@ char *parse_font(const char *font) { return new_font; } -struct config *init_config() { - struct config *config = calloc(1, sizeof(struct config)); +struct swaybar_config *init_config() { + struct swaybar_config *config = calloc(1, sizeof(struct swaybar_config)); config->status_command = NULL; config->pango_markup = false; - config->position = DESKTOP_SHELL_PANEL_POSITION_BOTTOM; + config->position = parse_position("bottom"); config->font = strdup("monospace 10"); config->mode = NULL; config->sep_symbol = NULL; @@ -48,18 +51,6 @@ struct config *init_config() { /* height */ config->height = 0; -#ifdef ENABLE_TRAY - config->tray_output = NULL; - config->icon_theme = NULL; - config->tray_padding = 2; - /** - * These constants are used by wayland and are defined in - * linux/input-event-codes.h - */ - config->activate_button = 0x110; /* BTN_LEFT */ - config->context_button = 0x111; /* BTN_RIGHT */ -#endif - /* colors */ config->colors.background = 0x000000FF; config->colors.statusline = 0xFFFFFFFF; @@ -88,7 +79,7 @@ struct config *init_config() { return config; } -void free_config(struct config *config) { +void free_config(struct swaybar_config *config) { free(config->status_command); free(config->font); free(config->mode); diff --git a/swaybar/event_loop.c b/swaybar/event_loop.c index 0d1be1da..748372ed 100644 --- a/swaybar/event_loop.c +++ b/swaybar/event_loop.c @@ -4,19 +4,18 @@ #include #include #include -#include "swaybar/bar.h" +#include #include "swaybar/event_loop.h" #include "list.h" -#include "log.h" struct event_item { - void(*cb)(int fd, short mask, void *data); + void (*cb)(int fd, short mask, void *data); void *data; }; struct timer_item { timer_t timer; - void(*cb)(timer_t timer, void *data); + void (*cb)(timer_t timer, void *data); void *data; }; @@ -138,7 +137,8 @@ void event_loop_poll() { void init_event_loop() { event_loop.fds.length = 0; event_loop.fds.capacity = 10; - event_loop.fds.items = malloc(event_loop.fds.capacity * sizeof(struct pollfd)); + event_loop.fds.items = malloc( + event_loop.fds.capacity * sizeof(struct pollfd)); event_loop.items = create_list(); event_loop.timers = create_list(); } diff --git a/swaybar/ipc.c b/swaybar/ipc.c deleted file mode 100644 index 93d1219c..00000000 --- a/swaybar/ipc.c +++ /dev/null @@ -1,410 +0,0 @@ -#define _XOPEN_SOURCE 500 -#include -#include -#include -#include "swaybar/config.h" -#include "swaybar/ipc.h" -#include "ipc-client.h" -#include "list.h" -#include "log.h" - -void ipc_send_workspace_command(const char *workspace_name) { - uint32_t size = strlen("workspace \"\"") + strlen(workspace_name) + 1; - - char command[size]; - sprintf(command, "workspace \"%s\"", workspace_name); - - ipc_single_command(swaybar.ipc_socketfd, IPC_COMMAND, command, &size); -} - -static void ipc_parse_config(struct config *config, const char *payload) { - json_object *bar_config = json_tokener_parse(payload); - json_object *markup, *mode, *hidden_bar, *position, *status_command; - json_object *font, *bar_height, *wrap_scroll, *workspace_buttons, *strip_workspace_numbers; - json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol, *outputs; -#ifdef ENABLE_TRAY - json_object *tray_output, *icon_theme, *tray_padding, *activate_button, *context_button; - json_object *secondary_button; - json_object_object_get_ex(bar_config, "tray_output", &tray_output); - json_object_object_get_ex(bar_config, "icon_theme", &icon_theme); - json_object_object_get_ex(bar_config, "tray_padding", &tray_padding); - json_object_object_get_ex(bar_config, "activate_button", &activate_button); - json_object_object_get_ex(bar_config, "context_button", &context_button); - json_object_object_get_ex(bar_config, "secondary_button", &secondary_button); -#endif - json_object_object_get_ex(bar_config, "mode", &mode); - json_object_object_get_ex(bar_config, "hidden_bar", &hidden_bar); - json_object_object_get_ex(bar_config, "position", &position); - json_object_object_get_ex(bar_config, "status_command", &status_command); - json_object_object_get_ex(bar_config, "font", &font); - json_object_object_get_ex(bar_config, "bar_height", &bar_height); - json_object_object_get_ex(bar_config, "wrap_scroll", &wrap_scroll); - json_object_object_get_ex(bar_config, "workspace_buttons", &workspace_buttons); - json_object_object_get_ex(bar_config, "strip_workspace_numbers", &strip_workspace_numbers); - json_object_object_get_ex(bar_config, "binding_mode_indicator", &binding_mode_indicator); - json_object_object_get_ex(bar_config, "verbose", &verbose); - json_object_object_get_ex(bar_config, "separator_symbol", &sep_symbol); - json_object_object_get_ex(bar_config, "colors", &colors); - json_object_object_get_ex(bar_config, "outputs", &outputs); - json_object_object_get_ex(bar_config, "pango_markup", &markup); - - if (status_command) { - free(config->status_command); - config->status_command = strdup(json_object_get_string(status_command)); - } - - if (position) { - config->position = parse_position(json_object_get_string(position)); - } - - if (font) { - free(config->font); - config->font = parse_font(json_object_get_string(font)); - } - - if (sep_symbol) { - free(config->sep_symbol); - config->sep_symbol = strdup(json_object_get_string(sep_symbol)); - } - - if (strip_workspace_numbers) { - config->strip_workspace_numbers = json_object_get_boolean(strip_workspace_numbers); - } - - if (binding_mode_indicator) { - config->binding_mode_indicator = json_object_get_boolean(binding_mode_indicator); - } - - if (wrap_scroll) { - config->wrap_scroll = json_object_get_boolean(wrap_scroll); - } - - if (workspace_buttons) { - config->workspace_buttons = json_object_get_boolean(workspace_buttons); - } - - if (bar_height) { - config->height = json_object_get_int(bar_height); - } - - if (markup) { - config->pango_markup = json_object_get_boolean(markup); - } - -#ifdef ENABLE_TRAY - if (tray_output) { - free(config->tray_output); - config->tray_output = strdup(json_object_get_string(tray_output)); - } - - if (icon_theme) { - free(config->icon_theme); - config->icon_theme = strdup(json_object_get_string(icon_theme)); - } - - if (tray_padding) { - config->tray_padding = json_object_get_int(tray_padding); - } - - if (activate_button) { - config->activate_button = json_object_get_int(activate_button); - } - - if (context_button) { - config->context_button = json_object_get_int(context_button); - } - - if (secondary_button) { - config->secondary_button = json_object_get_int(secondary_button); - } -#endif - - // free previous outputs list - int i; - for (i = 0; i < config->outputs->length; ++i) { - free(config->outputs->items[i]); - } - list_free(config->outputs); - config->outputs = create_list(); - - if (outputs) { - int length = json_object_array_length(outputs); - json_object *output; - const char *output_str; - for (i = 0; i < length; ++i) { - output = json_object_array_get_idx(outputs, i); - output_str = json_object_get_string(output); - if (strcmp("*", output_str) == 0) { - config->all_outputs = true; - break; - } - list_add(config->outputs, strdup(output_str)); - } - } else { - config->all_outputs = true; - } - - if (colors) { - json_object *background, *statusline, *separator; - json_object *focused_background, *focused_statusline, *focused_separator; - json_object *focused_workspace_border, *focused_workspace_bg, *focused_workspace_text; - json_object *inactive_workspace_border, *inactive_workspace_bg, *inactive_workspace_text; - json_object *active_workspace_border, *active_workspace_bg, *active_workspace_text; - json_object *urgent_workspace_border, *urgent_workspace_bg, *urgent_workspace_text; - json_object *binding_mode_border, *binding_mode_bg, *binding_mode_text; - json_object_object_get_ex(colors, "background", &background); - json_object_object_get_ex(colors, "statusline", &statusline); - json_object_object_get_ex(colors, "separator", &separator); - json_object_object_get_ex(colors, "focused_background", &focused_background); - json_object_object_get_ex(colors, "focused_statusline", &focused_statusline); - json_object_object_get_ex(colors, "focused_separator", &focused_separator); - json_object_object_get_ex(colors, "focused_workspace_border", &focused_workspace_border); - json_object_object_get_ex(colors, "focused_workspace_bg", &focused_workspace_bg); - json_object_object_get_ex(colors, "focused_workspace_text", &focused_workspace_text); - json_object_object_get_ex(colors, "active_workspace_border", &active_workspace_border); - json_object_object_get_ex(colors, "active_workspace_bg", &active_workspace_bg); - json_object_object_get_ex(colors, "active_workspace_text", &active_workspace_text); - json_object_object_get_ex(colors, "inactive_workspace_border", &inactive_workspace_border); - json_object_object_get_ex(colors, "inactive_workspace_bg", &inactive_workspace_bg); - json_object_object_get_ex(colors, "inactive_workspace_text", &inactive_workspace_text); - json_object_object_get_ex(colors, "urgent_workspace_border", &urgent_workspace_border); - json_object_object_get_ex(colors, "urgent_workspace_bg", &urgent_workspace_bg); - json_object_object_get_ex(colors, "urgent_workspace_text", &urgent_workspace_text); - json_object_object_get_ex(colors, "binding_mode_border", &binding_mode_border); - json_object_object_get_ex(colors, "binding_mode_bg", &binding_mode_bg); - json_object_object_get_ex(colors, "binding_mode_text", &binding_mode_text); - if (background) { - config->colors.background = parse_color(json_object_get_string(background)); - } - - if (statusline) { - config->colors.statusline = parse_color(json_object_get_string(statusline)); - } - - if (separator) { - config->colors.separator = parse_color(json_object_get_string(separator)); - } - - if (focused_background) { - config->colors.focused_background = parse_color(json_object_get_string(focused_background)); - } - - if (focused_statusline) { - config->colors.focused_statusline = parse_color(json_object_get_string(focused_statusline)); - } - - if (focused_separator) { - config->colors.focused_separator = parse_color(json_object_get_string(focused_separator)); - } - - if (focused_workspace_border) { - config->colors.focused_workspace.border = parse_color(json_object_get_string(focused_workspace_border)); - } - - if (focused_workspace_bg) { - config->colors.focused_workspace.background = parse_color(json_object_get_string(focused_workspace_bg)); - } - - if (focused_workspace_text) { - config->colors.focused_workspace.text = parse_color(json_object_get_string(focused_workspace_text)); - } - - if (active_workspace_border) { - config->colors.active_workspace.border = parse_color(json_object_get_string(active_workspace_border)); - } - - if (active_workspace_bg) { - config->colors.active_workspace.background = parse_color(json_object_get_string(active_workspace_bg)); - } - - if (active_workspace_text) { - config->colors.active_workspace.text = parse_color(json_object_get_string(active_workspace_text)); - } - - if (inactive_workspace_border) { - config->colors.inactive_workspace.border = parse_color(json_object_get_string(inactive_workspace_border)); - } - - if (inactive_workspace_bg) { - config->colors.inactive_workspace.background = parse_color(json_object_get_string(inactive_workspace_bg)); - } - - if (inactive_workspace_text) { - config->colors.inactive_workspace.text = parse_color(json_object_get_string(inactive_workspace_text)); - } - - if (binding_mode_border) { - config->colors.binding_mode.border = parse_color(json_object_get_string(binding_mode_border)); - } - - if (binding_mode_bg) { - config->colors.binding_mode.background = parse_color(json_object_get_string(binding_mode_bg)); - } - - if (binding_mode_text) { - config->colors.binding_mode.text = parse_color(json_object_get_string(binding_mode_text)); - } - } - - json_object_put(bar_config); -} - -static void ipc_update_workspaces(struct bar *bar) { - int i; - for (i = 0; i < bar->outputs->length; ++i) { - struct output *output = bar->outputs->items[i]; - if (output->workspaces) { - free_workspaces(output->workspaces); - } - output->workspaces = create_list(); - } - - uint32_t len = 0; - char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_WORKSPACES, NULL, &len); - json_object *results = json_tokener_parse(res); - if (!results) { - free(res); - return; - } - - int length = json_object_array_length(results); - json_object *ws_json; - json_object *num, *name, *visible, *focused, *out, *urgent; - for (i = 0; i < length; ++i) { - ws_json = json_object_array_get_idx(results, i); - - json_object_object_get_ex(ws_json, "num", &num); - json_object_object_get_ex(ws_json, "name", &name); - json_object_object_get_ex(ws_json, "visible", &visible); - json_object_object_get_ex(ws_json, "focused", &focused); - json_object_object_get_ex(ws_json, "output", &out); - json_object_object_get_ex(ws_json, "urgent", &urgent); - - int j; - for (j = 0; j < bar->outputs->length; ++j) { - struct output *output = bar->outputs->items[j]; - if (strcmp(json_object_get_string(out), output->name) == 0) { - struct workspace *ws = malloc(sizeof(struct workspace)); - ws->num = json_object_get_int(num); - ws->name = strdup(json_object_get_string(name)); - ws->visible = json_object_get_boolean(visible); - ws->focused = json_object_get_boolean(focused); - if (ws->focused) { - if (bar->focused_output) { - bar->focused_output->focused = false; - } - bar->focused_output = output; - output->focused = true; - } - ws->urgent = json_object_get_boolean(urgent); - list_add(output->workspaces, ws); - } - } - } - - json_object_put(results); - free(res); -} - -void ipc_bar_init(struct bar *bar, const char *bar_id) { - // Get bar config - uint32_t len = strlen(bar_id); - char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_BAR_CONFIG, bar_id, &len); - - ipc_parse_config(bar->config, res); - free(res); - - // Get outputs - len = 0; - res = ipc_single_command(bar->ipc_socketfd, IPC_GET_OUTPUTS, NULL, &len); - json_object *outputs = json_tokener_parse(res); - int i; - int length = json_object_array_length(outputs); - json_object *output, *output_name, *output_active; - const char *name; - bool active; - for (i = 0; i < length; ++i) { - output = json_object_array_get_idx(outputs, i); - json_object_object_get_ex(output, "name", &output_name); - json_object_object_get_ex(output, "active", &output_active); - name = json_object_get_string(output_name); - active = json_object_get_boolean(output_active); - if (!active) { - continue; - } - - bool use_output = false; - if (bar->config->all_outputs) { - use_output = true; - } else { - int j = 0; - for (j = 0; j < bar->config->outputs->length; ++j) { - const char *conf_name = bar->config->outputs->items[j]; - if (strcasecmp(name, conf_name) == 0) { - use_output = true; - break; - } - } - } - - if (!use_output) { - continue; - } - - // add bar to the output - struct output *bar_output = new_output(name); - bar_output->idx = i; - list_add(bar->outputs, bar_output); - } - free(res); - json_object_put(outputs); - - const char *subscribe_json = "[ \"workspace\", \"mode\" ]"; - len = strlen(subscribe_json); - res = ipc_single_command(bar->ipc_event_socketfd, IPC_SUBSCRIBE, subscribe_json, &len); - free(res); - - ipc_update_workspaces(bar); -} - -bool handle_ipc_event(struct bar *bar) { - struct ipc_response *resp = ipc_recv_response(bar->ipc_event_socketfd); - if (!resp) { - return false; - } - switch (resp->type) { - case IPC_EVENT_WORKSPACE: - ipc_update_workspaces(bar); - break; - case IPC_EVENT_MODE: { - json_object *result = json_tokener_parse(resp->payload); - if (!result) { - free_ipc_response(resp); - sway_log(L_ERROR, "failed to parse payload as json"); - return false; - } - json_object *json_change; - if (json_object_object_get_ex(result, "change", &json_change)) { - const char *change = json_object_get_string(json_change); - - free(bar->config->mode); - if (strcmp(change, "default") == 0) { - bar->config->mode = NULL; - } else { - bar->config->mode = strdup(change); - } - } else { - sway_log(L_ERROR, "failed to parse response"); - } - - json_object_put(result); - break; - } - default: - free_ipc_response(resp); - return false; - } - - free_ipc_response(resp); - return true; -} diff --git a/swaybar/main.c b/swaybar/main.c index 0abd0755..c897e1c9 100644 --- a/swaybar/main.c +++ b/swaybar/main.c @@ -4,21 +4,20 @@ #include #include #include +#include #include "swaybar/bar.h" #include "ipc-client.h" -#include "log.h" -/* global bar state */ -struct bar swaybar; +static struct swaybar swaybar; -void sway_terminate(int exit_code) { +void sig_handler(int signal) { bar_teardown(&swaybar); - exit(exit_code); + exit(0); } -void sig_handler(int signal) { +void sway_terminate(int code) { bar_teardown(&swaybar); - exit(0); + exit(code); } int main(int argc, char **argv) { @@ -75,20 +74,23 @@ int main(int argc, char **argv) { } } - if (!bar_id) { - sway_abort("No bar_id passed. Provide --bar_id or let sway start swaybar"); - } - if (debug) { - init_log(L_DEBUG); + wlr_log_init(L_DEBUG, NULL); } else { - init_log(L_ERROR); + wlr_log_init(L_ERROR, NULL); + } + + if (!bar_id) { + wlr_log(L_ERROR, "No bar_id passed. " + "Provide --bar_id or let sway start swaybar"); + return 1; } if (!socket_path) { socket_path = get_socketpath(); if (!socket_path) { - sway_abort("Unable to retrieve socket path"); + wlr_log(L_ERROR, "Unable to retrieve socket path"); + return 1; } } @@ -100,9 +102,6 @@ int main(int argc, char **argv) { free(bar_id); bar_run(&swaybar); - - // gracefully shutdown swaybar and status_command bar_teardown(&swaybar); - return 0; } diff --git a/swaybar/meson.build b/swaybar/meson.build new file mode 100644 index 00000000..fd87e51d --- /dev/null +++ b/swaybar/meson.build @@ -0,0 +1,25 @@ +executable( + 'swaybar', + [ + 'bar.c', + 'config.c', + 'event_loop.c', + 'main.c', + 'render.c', + ], + include_directories: [sway_inc], + dependencies: [ + cairo, + client_protos, + gdk_pixbuf, + jsonc, + math, + pango, + pangocairo, + rt, + wayland_client, + wlroots, + ], + link_with: [lib_sway_common, lib_sway_client], + install: true +) diff --git a/swaybar/render.c b/swaybar/render.c index 6fc09078..2eaa0195 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -1,367 +1,63 @@ #include #include #include - -#include "client/cairo.h" -#include "client/pango.h" -#include "client/window.h" +#include +#include "cairo.h" +#include "pango.h" +#include "pool-buffer.h" +#include "swaybar/bar.h" #include "swaybar/config.h" -#include "swaybar/status_line.h" #include "swaybar/render.h" -#ifdef ENABLE_TRAY -#include "swaybar/tray/tray.h" -#include "swaybar/tray/sni.h" -#endif -#include "log.h" - - -/* internal spacing */ -static int margin = 3; -static int ws_horizontal_padding = 5; -static double ws_vertical_padding = 1.5; -static int ws_spacing = 1; - -/** - * Renders a sharp line of any width and height. - * - * The line is drawn from (x,y) to (x+width,y+height) where width/height is 0 - * if the line has a width/height of one pixel, respectively. - */ -static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) { - cairo_set_source_u32(cairo, color); - - if (width > 1 && height > 1) { - cairo_rectangle(cairo, x, y, width, height); - cairo_fill(cairo); - } else { - if (width == 1) { - x += 0.5; - height += y; - width = x; - } - - if (height == 1) { - y += 0.5; - width += x; - height = y; - } - - cairo_move_to(cairo, x, y); - cairo_set_line_width(cairo, 1.0); - cairo_line_to(cairo, width, height); - cairo_stroke(cairo); - } -} - -static void render_block(struct window *window, struct config *config, struct status_block *block, double *x, bool edge, bool is_focused) { - int width, height, sep_width; - get_text_size(window->cairo, window->font, &width, &height, - window->scale, block->markup, "%s", block->full_text); - - int textwidth = width; - double block_width = width; - - if (width < block->min_width) { - width = block->min_width; - } - - *x -= width; - - if (block->border != 0 && block->border_left > 0) { - *x -= (block->border_left + margin); - block_width += block->border_left + margin; - } - - if (block->border != 0 && block->border_right > 0) { - *x -= (block->border_right + margin); - block_width += block->border_right + margin; - } - - // Add separator - if (!edge) { - if (config->sep_symbol) { - get_text_size(window->cairo, window->font, &sep_width, &height, - window->scale, false, "%s", config->sep_symbol); - if (sep_width > block->separator_block_width) { - block->separator_block_width = sep_width + margin * 2; - } - } - - *x -= block->separator_block_width; - } else { - *x -= margin; - } - - double pos = *x; - - block->x = (int)pos; - block->width = (int)block_width; - - // render background - if (block->background != 0x0) { - cairo_set_source_u32(window->cairo, block->background); - cairo_rectangle(window->cairo, pos - 0.5, 1, block_width, (window->height * window->scale) - 2); - cairo_fill(window->cairo); - } - - // render top border - if (block->border != 0 && block->border_top > 0) { - render_sharp_line(window->cairo, block->border, - pos - 0.5, - 1, - block_width, - block->border_top); - } - - // render bottom border - if (block->border != 0 && block->border_bottom > 0) { - render_sharp_line(window->cairo, block->border, - pos - 0.5, - (window->height * window->scale) - 1 - block->border_bottom, - block_width, - block->border_bottom); - } - - // render left border - if (block->border != 0 && block->border_left > 0) { - render_sharp_line(window->cairo, block->border, - pos - 0.5, - 1, - block->border_left, - (window->height * window->scale) - 2); - - pos += block->border_left + margin; - } - - // render text - double offset = 0; - - if (strncmp(block->align, "left", 5) == 0) { - offset = pos; - } else if (strncmp(block->align, "right", 5) == 0) { - offset = pos + width - textwidth; - } else if (strncmp(block->align, "center", 6) == 0) { - offset = pos + (width - textwidth) / 2; - } - - cairo_move_to(window->cairo, offset, margin); - cairo_set_source_u32(window->cairo, block->color); - pango_printf(window->cairo, window->font, window->scale, - block->markup, "%s", block->full_text); - - pos += width; +#include "wlr-layer-shell-unstable-v1-client-protocol.h" - // render right border - if (block->border != 0 && block->border_right > 0) { - pos += margin; - - render_sharp_line(window->cairo, block->border, - pos - 0.5, - 1, - block->border_right, - (window->height * window->scale) - 2); - - pos += block->border_right; - } - - // render separator - if (!edge && block->separator) { - if (is_focused) { - cairo_set_source_u32(window->cairo, config->colors.focused_separator); - } else { - cairo_set_source_u32(window->cairo, config->colors.separator); - } - if (config->sep_symbol) { - offset = pos + (block->separator_block_width - sep_width) / 2; - cairo_move_to(window->cairo, offset, margin); - pango_printf(window->cairo, window->font, window->scale, - false, "%s", config->sep_symbol); - } else { - cairo_set_line_width(window->cairo, 1); - cairo_move_to(window->cairo, pos + block->separator_block_width/2, - margin); - cairo_line_to(window->cairo, pos + block->separator_block_width/2, - (window->height * window->scale) - margin); - cairo_stroke(window->cairo); - } - } +static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar *bar, + struct swaybar_output *output) { + struct swaybar_config *config = bar->config; -} - -static const char *strip_workspace_name(bool strip_num, const char *ws_name) { - bool strip = false; - int i; - - if (strip_num) { - int len = strlen(ws_name); - for (i = 0; i < len; ++i) { - if (!('0' <= ws_name[i] && ws_name[i] <= '9')) { - if (':' == ws_name[i] && i < len-1 && i > 0) { - strip = true; - ++i; - } - break; - } - } - } - - if (strip) { - return ws_name + i; - } - - return ws_name; -} - -void workspace_button_size(struct window *window, const char *workspace_name, int *width, int *height) { - const char *stripped_name = strip_workspace_name(swaybar.config->strip_workspace_numbers, workspace_name); - - get_text_size(window->cairo, window->font, width, height, - window->scale, true, "%s", stripped_name); - *width += 2 * ws_horizontal_padding; - *height += 2 * ws_vertical_padding; -} - -static void render_workspace_button(struct window *window, struct config *config, struct workspace *ws, double *x) { - const char *stripped_name = strip_workspace_name(config->strip_workspace_numbers, ws->name); - - struct box_colors box_colors; - if (ws->urgent) { - box_colors = config->colors.urgent_workspace; - } else if (ws->focused) { - box_colors = config->colors.focused_workspace; - } else if (ws->visible) { - box_colors = config->colors.active_workspace; - } else { - box_colors = config->colors.inactive_workspace; - } - - int width, height; - workspace_button_size(window, stripped_name, &width, &height); - - // background - cairo_set_source_u32(window->cairo, box_colors.background); - cairo_rectangle(window->cairo, *x, 1.5, width - 1, height); - cairo_fill(window->cairo); - - // border - cairo_set_source_u32(window->cairo, box_colors.border); - cairo_rectangle(window->cairo, *x, 1.5, width - 1, height); - cairo_stroke(window->cairo); - - // text - cairo_set_source_u32(window->cairo, box_colors.text); - cairo_move_to(window->cairo, (int)*x + ws_horizontal_padding, margin); - pango_printf(window->cairo, window->font, window->scale, - true, "%s", stripped_name); - - *x += width + ws_spacing; -} - -static void render_binding_mode_indicator(struct window *window, struct config *config, double pos) { - int width, height; - get_text_size(window->cairo, window->font, &width, &height, - window->scale, false, "%s", config->mode); - - // background - cairo_set_source_u32(window->cairo, config->colors.binding_mode.background); - cairo_rectangle(window->cairo, pos, 1.5, width + ws_horizontal_padding * 2 - 1, - height + ws_vertical_padding * 2); - cairo_fill(window->cairo); - - // border - cairo_set_source_u32(window->cairo, config->colors.binding_mode.border); - cairo_rectangle(window->cairo, pos, 1.5, width + ws_horizontal_padding * 2 - 1, - height + ws_vertical_padding * 2); - cairo_stroke(window->cairo); - - // text - cairo_set_source_u32(window->cairo, config->colors.binding_mode.text); - cairo_move_to(window->cairo, (int)pos + ws_horizontal_padding, margin); - pango_printf(window->cairo, window->font, window->scale, - false, "%s", config->mode); -} - -void render(struct output *output, struct config *config, struct status_line *line) { - int i; - - struct window *window = output->window; - cairo_t *cairo = window->cairo; - bool is_focused = output->focused; - - // Clear cairo_save(cairo); cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); cairo_paint(cairo); cairo_restore(cairo); cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); - - // Background - if (is_focused) { + if (output->focused) { cairo_set_source_u32(cairo, config->colors.focused_background); } else { cairo_set_source_u32(cairo, config->colors.background); } cairo_paint(cairo); -#ifdef ENABLE_TRAY - uint32_t tray_width = tray_render(output, config); -#else - const uint32_t tray_width = window->width * window->scale; -#endif - - // Command output - if (is_focused) { - cairo_set_source_u32(cairo, config->colors.focused_statusline); + // TODO: use actual height + return 20; +} + +void render_frame(struct swaybar *bar, + struct swaybar_output *output) { + cairo_surface_t *recorder = cairo_recording_surface_create( + CAIRO_CONTENT_COLOR_ALPHA, NULL); + cairo_t *cairo = cairo_create(recorder); + uint32_t height = render_to_cairo(cairo, bar, output); + if (height != output->height) { + // Reconfigure surface + zwlr_layer_surface_v1_set_size( + output->layer_surface, 0, height); + // TODO: this could infinite loop if the compositor assigns us a + // different height than what we asked for + wl_surface_commit(output->surface); + wl_display_roundtrip(bar->display); } else { - cairo_set_source_u32(cairo, config->colors.statusline); - } - - int width, height; - - if (line->protocol == TEXT) { - get_text_size(window->cairo, window->font, &width, &height, - window->scale, config->pango_markup, "%s", line->text_line); - cairo_move_to(cairo, tray_width - margin - width, margin); - pango_printf(window->cairo, window->font, window->scale, - config->pango_markup, "%s", line->text_line); - } else if (line->protocol == I3BAR && line->block_line) { - double pos = tray_width - 0.5; - bool edge = true; - for (i = line->block_line->length - 1; i >= 0; --i) { - struct status_block *block = line->block_line->items[i]; - if (block->full_text && block->full_text[0]) { - render_block(window, config, block, &pos, edge, is_focused); - edge = false; - } - } - } - - cairo_set_line_width(cairo, 1.0); - double x = 0.5; - - // Workspaces - if (config->workspace_buttons) { - for (i = 0; i < output->workspaces->length; ++i) { - struct workspace *ws = output->workspaces->items[i]; - render_workspace_button(window, config, ws, &x); - } - } - - // binding mode indicator - if (config->mode && config->binding_mode_indicator) { - render_binding_mode_indicator(window, config, x); - } -} - -void set_window_height(struct window *window, int height) { - int text_width, text_height; - get_text_size(window->cairo, window->font, - &text_width, &text_height, window->scale, false, - "Test string for measuring purposes"); - if (height > 0) { - margin = (height - text_height) / 2; - ws_vertical_padding = margin - 1.5; - } - window->height = (text_height + margin * 2) / window->scale; + // Replay recording into shm and send it off + output->current_buffer = get_next_buffer(bar->shm, + output->buffers, output->width, output->height); + cairo_t *shm = output->current_buffer->cairo; + cairo_set_source_surface(shm, recorder, 0.0, 0.0); + cairo_paint(shm); + wl_surface_attach(output->surface, + output->current_buffer->buffer, 0, 0); + wl_surface_damage(output->surface, 0, 0, output->width, output->height); + wl_surface_commit(output->surface); + wl_display_roundtrip(bar->display); + } + cairo_surface_destroy(recorder); + cairo_destroy(cairo); } diff --git a/swaybar/status_line.c b/swaybar/status_line.c deleted file mode 100644 index 87e90caf..00000000 --- a/swaybar/status_line.c +++ /dev/null @@ -1,530 +0,0 @@ -#define _XOPEN_SOURCE 700 -#include -#include -#include -#include - -#include "swaybar/config.h" -#include "swaybar/status_line.h" -#include "log.h" -#include "util.h" - -#define I3JSON_MAXDEPTH 4 -#define I3JSON_UNKNOWN 0 -#define I3JSON_ARRAY 1 -#define I3JSON_STRING 2 - -struct { - int bufsize; - char *buffer; - char *line_start; - char *parserpos; - bool escape; - int depth; - int bar[I3JSON_MAXDEPTH+1]; -} i3json_state = { 0, NULL, NULL, NULL, false, 0, { I3JSON_UNKNOWN } }; - -static char line[1024]; -static char line_rest[1024]; - -static char event_buff[1024]; - -static void free_status_block(void *item) { - if (!item) { - return; - } - struct status_block *sb = (struct status_block*)item; - if (sb->full_text) { - free(sb->full_text); - } - if (sb->short_text) { - free(sb->short_text); - } - if (sb->align) { - free(sb->align); - } - if (sb->name) { - free(sb->name); - } - if (sb->instance) { - free(sb->instance); - } - free(sb); -} - -static void parse_json(struct bar *bar, const char *text) { - json_object *results = json_tokener_parse(text); - if (!results) { - sway_log(L_DEBUG, "Failed to parse json"); - return; - } - - if (json_object_array_length(results) < 1) { - return; - } - - if (bar->status->block_line) { - list_foreach(bar->status->block_line, free_status_block); - list_free(bar->status->block_line); - } - - bar->status->block_line = create_list(); - - int i; - for (i = 0; i < json_object_array_length(results); ++i) { - json_object *full_text, *short_text, *color, *min_width, *align, *urgent; - json_object *name, *instance, *separator, *separator_block_width; - json_object *background, *border, *border_top, *border_bottom; - json_object *border_left, *border_right, *markup; - - json_object *json = json_object_array_get_idx(results, i); - if (!json) { - continue; - } - - json_object_object_get_ex(json, "full_text", &full_text); - json_object_object_get_ex(json, "short_text", &short_text); - json_object_object_get_ex(json, "color", &color); - json_object_object_get_ex(json, "min_width", &min_width); - json_object_object_get_ex(json, "align", &align); - json_object_object_get_ex(json, "urgent", &urgent); - json_object_object_get_ex(json, "name", &name); - json_object_object_get_ex(json, "instance", &instance); - json_object_object_get_ex(json, "markup", &markup); - json_object_object_get_ex(json, "separator", &separator); - json_object_object_get_ex(json, "separator_block_width", &separator_block_width); - json_object_object_get_ex(json, "background", &background); - json_object_object_get_ex(json, "border", &border); - json_object_object_get_ex(json, "border_top", &border_top); - json_object_object_get_ex(json, "border_bottom", &border_bottom); - json_object_object_get_ex(json, "border_left", &border_left); - json_object_object_get_ex(json, "border_right", &border_right); - - struct status_block *new = calloc(1, sizeof(struct status_block)); - - if (full_text) { - new->full_text = strdup(json_object_get_string(full_text)); - } - - if (short_text) { - new->short_text = strdup(json_object_get_string(short_text)); - } - - if (color) { - new->color = parse_color(json_object_get_string(color)); - } else { - new->color = bar->config->colors.statusline; - } - - if (min_width) { - json_type type = json_object_get_type(min_width); - if (type == json_type_int) { - new->min_width = json_object_get_int(min_width); - } else if (type == json_type_string) { - /* the width will be calculated when rendering */ - new->min_width = 0; - } - } - - if (align) { - new->align = strdup(json_object_get_string(align)); - } else { - new->align = strdup("left"); - } - - if (urgent) { - new->urgent = json_object_get_int(urgent); - } - - if (name) { - new->name = strdup(json_object_get_string(name)); - } - - if (instance) { - new->instance = strdup(json_object_get_string(instance)); - } - - if (markup) { - new->markup = false; - const char *markup_str = json_object_get_string(markup); - if (strcmp(markup_str, "pango") == 0) { - new->markup = true; - } - } - - if (separator) { - new->separator = json_object_get_int(separator); - } else { - new->separator = true; // i3bar spec - } - - if (separator_block_width) { - new->separator_block_width = json_object_get_int(separator_block_width); - } else { - new->separator_block_width = 9; // i3bar spec - } - - // Airblader features - if (background) { - new->background = parse_color(json_object_get_string(background)); - } else { - new->background = 0x0; // transparent - } - - if (border) { - new->border = parse_color(json_object_get_string(border)); - } else { - new->border = 0x0; // transparent - } - - if (border_top) { - new->border_top = json_object_get_int(border_top); - } else { - new->border_top = 1; - } - - if (border_bottom) { - new->border_bottom = json_object_get_int(border_bottom); - } else { - new->border_bottom = 1; - } - - if (border_left) { - new->border_left = json_object_get_int(border_left); - } else { - new->border_left = 1; - } - - if (border_right) { - new->border_right = json_object_get_int(border_right); - } else { - new->border_right = 1; - } - - list_add(bar->status->block_line, new); - } - - json_object_put(results); -} - -// continue parsing from last parserpos -static int i3json_parse(struct bar *bar) { - char *c = i3json_state.parserpos; - int handled = 0; - while (*c) { - if (i3json_state.bar[i3json_state.depth] == I3JSON_STRING) { - if (!i3json_state.escape && *c == '"') { - --i3json_state.depth; - } - i3json_state.escape = !i3json_state.escape && *c == '\\'; - } else { - switch (*c) { - case '[': - ++i3json_state.depth; - if (i3json_state.depth > I3JSON_MAXDEPTH) { - sway_abort("JSON too deep"); - } - i3json_state.bar[i3json_state.depth] = I3JSON_ARRAY; - if (i3json_state.depth == 2) { - i3json_state.line_start = c; - } - break; - case ']': - if (i3json_state.bar[i3json_state.depth] != I3JSON_ARRAY) { - sway_abort("JSON malformed"); - } - --i3json_state.depth; - if (i3json_state.depth == 1) { - // c[1] is valid since c[0] != '\0' - char p = c[1]; - c[1] = '\0'; - parse_json(bar, i3json_state.line_start); - c[1] = p; - ++handled; - i3json_state.line_start = c+1; - } - break; - case '"': - ++i3json_state.depth; - if (i3json_state.depth > I3JSON_MAXDEPTH) { - sway_abort("JSON too deep"); - } - i3json_state.bar[i3json_state.depth] = I3JSON_STRING; - break; - } - } - ++c; - } - i3json_state.parserpos = c; - return handled; -} - -// Read line from file descriptor, only show the line tail if it is too long. -// In non-blocking mode treat "no more data" as a linebreak. -// If data after a line break has been read, return it in rest. -// If rest is non-empty, then use that as the start of the next line. -static int read_line_tail(int fd, char *buf, int nbyte, char *rest) { - if (fd < 0 || !buf || !nbyte) { - return -1; - } - int l; - char *buffer = malloc(nbyte*2+1); - char *readpos = buffer; - char *lf; - // prepend old data to new line if necessary - if (rest) { - l = strlen(rest); - if (l > nbyte) { - strcpy(buffer, rest + l - nbyte); - readpos += nbyte; - } else if (l) { - strcpy(buffer, rest); - readpos += l; - } - } - // read until a linefeed is found or no more data is available - while ((l = read(fd, readpos, nbyte)) > 0) { - readpos[l] = '\0'; - lf = strchr(readpos, '\n'); - if (lf) { - // linefeed found, replace with \0 - *lf = '\0'; - // give data from the end of the line, try to fill the buffer - if (lf-buffer > nbyte) { - strcpy(buf, lf - nbyte + 1); - } else { - strcpy(buf, buffer); - } - // we may have read data from the next line, save it to rest - if (rest) { - rest[0] = '\0'; - strcpy(rest, lf + 1); - } - free(buffer); - return strlen(buf); - } else { - // no linefeed found, slide data back. - int overflow = readpos - buffer + l - nbyte; - if (overflow > 0) { - memmove(buffer, buffer + overflow , nbyte + 1); - } - } - } - if (l < 0) { - free(buffer); - return l; - } - readpos[l]='\0'; - if (rest) { - rest[0] = '\0'; - } - if (nbyte < readpos - buffer + l - 1) { - memcpy(buf, readpos - nbyte + l + 1, nbyte); - } else { - strncpy(buf, buffer, nbyte); - } - buf[nbyte-1] = '\0'; - free(buffer); - return strlen(buf); -} - -// make sure that enough buffer space is available starting from parserpos -static void i3json_ensure_free(int min_free) { - int _step = 10240; - int r = min_free % _step; - if (r) { - min_free += _step - r; - } - if (!i3json_state.buffer) { - i3json_state.buffer = malloc(min_free); - i3json_state.bufsize = min_free; - i3json_state.parserpos = i3json_state.buffer; - } else { - int len = 0; - int pos = 0; - if (i3json_state.line_start) { - len = strlen(i3json_state.line_start); - pos = i3json_state.parserpos - i3json_state.line_start; - if (i3json_state.line_start != i3json_state.buffer) { - memmove(i3json_state.buffer, i3json_state.line_start, len+1); - } - } else { - len = strlen(i3json_state.buffer); - } - if (i3json_state.bufsize < len+min_free) { - i3json_state.bufsize += min_free; - if (i3json_state.bufsize > 1024000) { - sway_abort("Status line json too long or malformed."); - } - i3json_state.buffer = realloc(i3json_state.buffer, i3json_state.bufsize); - if (!i3json_state.buffer) { - sway_abort("Could not allocate json buffer"); - } - } - if (i3json_state.line_start) { - i3json_state.line_start = i3json_state.buffer; - i3json_state.parserpos = i3json_state.buffer + pos; - } else { - i3json_state.parserpos = i3json_state.buffer; - } - } - if (!i3json_state.buffer) { - sway_abort("Could not allocate buffer."); - } -} - -// append data and parse it. -static int i3json_handle_data(struct bar *bar, char *data) { - int len = strlen(data); - i3json_ensure_free(len); - strcpy(i3json_state.parserpos, data); - return i3json_parse(bar); -} - -// read data from fd and parse it. -static int i3json_handle_fd(struct bar *bar) { - i3json_ensure_free(10240); - // get fresh data at the end of the buffer - int readlen = read(bar->status_read_fd, i3json_state.parserpos, 10239); - if (readlen < 0) { - return readlen; - } - i3json_state.parserpos[readlen] = '\0'; - return i3json_parse(bar); -} - -bool status_line_mouse_event(struct bar *bar, int x, int y, uint32_t button) { - sway_log(L_DEBUG, "status_line_mouse_event."); - if (!bar->status->click_events) { - sway_log(L_DEBUG, "click_events are not enabled."); - return false; - } - - if (bar->status->protocol == I3BAR) { - sway_log(L_DEBUG, "Sending click event."); - - // find clicked block - struct status_block *clicked_block = NULL; - struct status_block *current_block = NULL; - int num_blocks = bar->status->block_line->length; - - if (num_blocks == 0) { - return false; - } else { - current_block = bar->status->block_line->items[0]; - if (x < current_block->x) { - return false; - } - } - - for (int i = 0; i < num_blocks; i++) { - current_block = bar->status->block_line->items[i]; - if (x < (current_block->x + current_block->width)) { - clicked_block = current_block; - break; - } - } - - if (!clicked_block || !clicked_block->name) { - return false; - } - - // event example {"name":"capture","instance":"label","button":1,"x":3431,"y":18} - - struct json_object *event_json = json_object_new_object(); - json_object_object_add(event_json, "name", json_object_new_string(clicked_block->name)); - if (clicked_block->instance) { - json_object_object_add(event_json, "instance", json_object_new_string(clicked_block->instance)); - } - json_object_object_add(event_json, "button", json_object_new_int(button)); - json_object_object_add(event_json, "x", json_object_new_int(x)); - json_object_object_add(event_json, "y", json_object_new_int(y)); - - int len = snprintf(event_buff, sizeof(event_buff), "%s\n", json_object_to_json_string(event_json)); - - json_object_put(event_json); - - if (len <= (int)sizeof(event_buff)) { // if not truncated - write(bar->status_write_fd, event_buff, len); - return true; - } - } - - return false; -} - -bool handle_status_line(struct bar *bar) { - bool dirty = false; - - switch (bar->status->protocol) { - case I3BAR: - sway_log(L_DEBUG, "Got i3bar protocol."); - if (i3json_handle_fd(bar) > 0) { - dirty = true; - } - break; - case TEXT: - sway_log(L_DEBUG, "Got text protocol."); - read_line_tail(bar->status_read_fd, line, sizeof(line), line_rest); - dirty = true; - bar->status->text_line = line; - break; - case UNDEF: - sway_log(L_DEBUG, "Detecting protocol..."); - if (read_line_tail(bar->status_read_fd, line, sizeof(line), line_rest) < 0) { - break; - } - dirty = true; - bar->status->text_line = line; - bar->status->protocol = TEXT; - if (line[0] == '{') { - // detect i3bar json protocol - json_object *proto = json_tokener_parse(line); - if (proto) { - - json_object *version; - if (json_object_object_get_ex(proto, "version", &version) - && json_object_get_int(version) == 1 - ) { - sway_log(L_DEBUG, "Switched to i3bar protocol."); - bar->status->protocol = I3BAR; - } - - json_object *click_events; - if (json_object_object_get_ex(proto, "click_events", &click_events) - && json_object_get_boolean(click_events)) { - - sway_log(L_DEBUG, "Enabling click events."); - bar->status->click_events = true; - - const char *events_array = "[\n"; - write(bar->status_write_fd, events_array, strlen(events_array)); - } - - i3json_handle_data(bar, line_rest); - - json_object_put(proto); - } - } - break; - } - - return dirty; -} - -struct status_line *init_status_line() { - struct status_line *line = malloc(sizeof(struct status_line)); - line->block_line = create_list(); - line->text_line = NULL; - line->protocol = UNDEF; - line->click_events = false; - - return line; -} - -void free_status_line(struct status_line *line) { - if (line->block_line) { - list_foreach(line->block_line, free_status_block); - list_free(line->block_line); - } -} diff --git a/swaybar/tray/dbus.c b/swaybar/tray/dbus.c deleted file mode 100644 index 8e719fd9..00000000 --- a/swaybar/tray/dbus.c +++ /dev/null @@ -1,197 +0,0 @@ -#define _XOPEN_SOURCE 700 -#include -#include -#include -#include -#include -#include -#include -#include -#include "swaybar/tray/dbus.h" -#include "swaybar/event_loop.h" -#include "log.h" - -DBusConnection *conn = NULL; - -static void dispatch_watch(int fd, short mask, void *data) { - sway_log(L_DEBUG, "Dispatching watch"); - DBusWatch *watch = data; - - if (!dbus_watch_get_enabled(watch)) { - return; - } - - uint32_t flags = 0; - - if (mask & POLLIN) { - flags |= DBUS_WATCH_READABLE; - } if (mask & POLLOUT) { - flags |= DBUS_WATCH_WRITABLE; - } if (mask & POLLHUP) { - flags |= DBUS_WATCH_HANGUP; - } if (mask & POLLERR) { - flags |= DBUS_WATCH_ERROR; - } - - dbus_watch_handle(watch, flags); -} - -static dbus_bool_t add_watch(DBusWatch *watch, void *_data) { - if (!dbus_watch_get_enabled(watch)) { - // Watch should not be polled - return TRUE; - } - - short mask = 0; - uint32_t flags = dbus_watch_get_flags(watch); - - if (flags & DBUS_WATCH_READABLE) { - mask |= POLLIN; - } if (flags & DBUS_WATCH_WRITABLE) { - mask |= POLLOUT; - } - - int fd = dbus_watch_get_unix_fd(watch); - - sway_log(L_DEBUG, "Adding DBus watch fd: %d", fd); - add_event(fd, mask, dispatch_watch, watch); - - return TRUE; -} - -static void remove_watch(DBusWatch *watch, void *_data) { - int fd = dbus_watch_get_unix_fd(watch); - - remove_event(fd); -} - -static void dispatch_timeout(timer_t timer, void *data) { - sway_log(L_DEBUG, "Dispatching DBus timeout"); - DBusTimeout *timeout = data; - - if (dbus_timeout_get_enabled(timeout)) { - dbus_timeout_handle(timeout); - } -} - -static dbus_bool_t add_timeout(DBusTimeout *timeout, void *_data) { - if (!dbus_timeout_get_enabled(timeout)) { - return TRUE; - } - - timer_t *timer = malloc(sizeof(timer_t)); - if (!timer) { - sway_log(L_ERROR, "Cannot allocate memory"); - return FALSE; - } - struct sigevent ev = { - .sigev_notify = SIGEV_NONE, - }; - - if (timer_create(CLOCK_MONOTONIC, &ev, timer)) { - sway_log(L_ERROR, "Could not create DBus timer"); - return FALSE; - } - - int interval = dbus_timeout_get_interval(timeout); - int interval_sec = interval / 1000; - int interval_msec = (interval_sec * 1000) - interval; - - struct timespec period = { - (time_t) interval_sec, - ((long) interval_msec) * 1000 * 1000, - }; - struct itimerspec time = { - period, - period, - }; - - timer_settime(*timer, 0, &time, NULL); - - dbus_timeout_set_data(timeout, timer, NULL); - - sway_log(L_DEBUG, "Adding DBus timeout. Interval: %ds %dms", interval_sec, interval_msec); - add_timer(*timer, dispatch_timeout, timeout); - - return TRUE; -} -static void remove_timeout(DBusTimeout *timeout, void *_data) { - timer_t *timer = (timer_t *) dbus_timeout_get_data(timeout); - sway_log(L_DEBUG, "Removing DBus timeout."); - - if (timer) { - remove_timer(*timer); - timer_delete(*timer); - free(timer); - } -} - -static bool should_dispatch = true; - -static void dispatch_status(DBusConnection *connection, DBusDispatchStatus new_status, - void *_data) { - if (new_status == DBUS_DISPATCH_DATA_REMAINS) { - should_dispatch = true; - } -} - -/* Public functions below */ - -void dispatch_dbus() { - if (!should_dispatch || !conn) { - return; - } - - DBusDispatchStatus status; - - do { - status = dbus_connection_dispatch(conn); - } while (status == DBUS_DISPATCH_DATA_REMAINS); - - if (status != DBUS_DISPATCH_COMPLETE) { - sway_log(L_ERROR, "Cannot dispatch dbus events: %d", status); - } - - should_dispatch = false; -} - -int dbus_init() { - DBusError error; - dbus_error_init(&error); - - conn = dbus_bus_get(DBUS_BUS_SESSION, &error); - if (conn == NULL) { - sway_log(L_INFO, "Compiled with dbus support, but unable to connect to dbus"); - sway_log(L_INFO, "swaybar will be unable to display tray icons."); - return -1; - } - - dbus_connection_set_exit_on_disconnect(conn, FALSE); - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "Cannot get bus connection: %s\n", error.message); - conn = NULL; - return -1; - } - - sway_log(L_INFO, "Unique name: %s\n", dbus_bus_get_unique_name(conn)); - - // Will be called if dispatch status changes - dbus_connection_set_dispatch_status_function(conn, dispatch_status, NULL, NULL); - - if (!dbus_connection_set_watch_functions(conn, add_watch, remove_watch, - NULL, NULL, NULL)) { - dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL); - sway_log(L_ERROR, "Failed to activate DBUS watch functions"); - return -1; - } - - if (!dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, - NULL, NULL, NULL)) { - dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL); - dbus_connection_set_timeout_functions(conn, NULL, NULL, NULL, NULL, NULL); - sway_log(L_ERROR, "Failed to activate DBUS timeout functions"); - return -1; - } - - return 0; -} diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c deleted file mode 100644 index c146bf32..00000000 --- a/swaybar/tray/icon.c +++ /dev/null @@ -1,400 +0,0 @@ -#define _XOPEN_SOURCE 700 -#define _POSIX_C_SOURCE 200809L -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "swaybar/tray/icon.h" -#include "swaybar/bar.h" -#include "swaybar/config.h" -#include "stringop.h" -#include "log.h" - -/** - * REVIEW: - * This file repeats lots of "costly" operations that are the same for every - * icon. It's possible to create a dictionary or some other structure to cache - * these, though it may complicate things somewhat. - * - * Also parsing (index.theme) is currently pretty messy, so that could be made - * much better as well. Over all, things work, but are not optimal. - */ - -/* Finds all themes that the given theme inherits */ -static list_t *find_inherits(const char *theme_dir) { - const char inherits[] = "Inherits"; - const char index_name[] = "index.theme"; - list_t *themes = create_list(); - FILE *index = NULL; - char *path = malloc(strlen(theme_dir) + sizeof(index_name)); - if (!path) { - goto fail; - } - if (!themes) { - goto fail; - } - - strcpy(path, theme_dir); - strcat(path, index_name); - - index = fopen(path, "r"); - if (!index) { - goto fail; - } - - char *buf = NULL; - size_t n = 0; - while (!feof(index) && getline(&buf, &n, index) != -1) { - if (n <= sizeof(inherits) + 1) { - continue; - } - if (strncmp(inherits, buf, sizeof(inherits) - 1) == 0) { - char *themestr = buf + sizeof(inherits); - themes = split_string(themestr, ","); - break; - } - } - free(buf); - -fail: - free(path); - if (index) { - fclose(index); - } - return themes; -} - -static bool isdir(const char *path) { - struct stat statbuf; - if (stat(path, &statbuf) != -1) { - if (S_ISDIR(statbuf.st_mode)) { - return true; - } - } - return false; - -} - -/** - * Returns the directory of a given theme if it exists. - * The returned pointer must be freed. - */ -static char *find_theme_dir(const char *theme) { - char *basedir; - char *icon_dir; - - if (!theme) { - return NULL; - } - - if (!(icon_dir = malloc(1024))) { - sway_log(L_ERROR, "Out of memory!"); - goto fail; - } - - if ((basedir = getenv("HOME"))) { - if (snprintf(icon_dir, 1024, "%s/.icons/%s", basedir, theme) >= 1024) { - sway_log(L_ERROR, "Path too long to render"); - // XXX perhaps just goto trying in /usr/share? This - // shouldn't happen anyway, but might with a long global - goto fail; - } - - if (isdir(icon_dir)) { - return icon_dir; - } - } - - if ((basedir = getenv("XDG_DATA_DIRS"))) { - if (snprintf(icon_dir, 1024, "%s/icons/%s", basedir, theme) >= 1024) { - sway_log(L_ERROR, "Path too long to render"); - // ditto - goto fail; - } - - if (isdir(icon_dir)) { - return icon_dir; - } - } - - // Spec says use "/usr/share/pixmaps/", but I see everything in - // "/usr/share/icons/" look it both, I suppose. - if (snprintf(icon_dir, 1024, "/usr/share/pixmaps/%s", theme) >= 1024) { - sway_log(L_ERROR, "Path too long to render"); - goto fail; - } - if (isdir(icon_dir)) { - return icon_dir; - } - - if (snprintf(icon_dir, 1024, "/usr/share/icons/%s", theme) >= 1024) { - sway_log(L_ERROR, "Path too long to render"); - goto fail; - } - if (isdir(icon_dir)) { - return icon_dir; - } - -fail: - free(icon_dir); - sway_log(L_ERROR, "Could not find dir for theme: %s", theme); - return NULL; -} - -/** - * Returns all theme dirs needed to be looked in for an icon. - * Does not check for duplicates - */ -static list_t *find_all_theme_dirs(const char *theme) { - list_t *dirs = create_list(); - if (!dirs) { - return NULL; - } - char *dir = find_theme_dir(theme); - if (dir) { - list_add(dirs, dir); - list_t *inherits = find_inherits(dir); - list_cat(dirs, inherits); - list_free(inherits); - } - dir = find_theme_dir("hicolor"); - if (dir) { - list_add(dirs, dir); - } - - return dirs; -} - -struct subdir { - int size; - char name[]; -}; - -static int subdir_str_cmp(const void *_subdir, const void *_str) { - const struct subdir *subdir = _subdir; - const char *str = _str; - return strcmp(subdir->name, str); -} -/** - * Helper to find_subdirs. Acts similar to `split_string(subdirs, ",")` but - * generates a list of struct subdirs - */ -static list_t *split_subdirs(char *subdir_str) { - list_t *subdir_list = create_list(); - char *copy = strdup(subdir_str); - if (!subdir_list || !copy) { - list_free(subdir_list); - free(copy); - return NULL; - } - - char *token; - token = strtok(copy, ","); - while(token) { - int len = strlen(token) + 1; - struct subdir *subdir = - malloc(sizeof(struct subdir) + sizeof(char [len])); - if (!subdir) { - // Return what we have - return subdir_list; - } - subdir->size = 0; - strcpy(subdir->name, token); - - list_add(subdir_list, subdir); - - token = strtok(NULL, ","); - } - free(copy); - - return subdir_list; -} -/** - * Returns a list of all subdirectories of a theme. - * Take note: the subdir names are all relative to `theme_dir` and must be - * combined with it to form a valid directory. - * - * Each member of the list is of type (struct subdir *) this struct contains - * the name of the subdir, along with size information. These must be freed - * bye the caller. - * - * This currently ignores min and max sizes of icons. - */ -static list_t* find_theme_subdirs(const char *theme_dir) { - const char index_name[] = "/index.theme"; - list_t *dirs = NULL; - char *path = malloc(strlen(theme_dir) + sizeof(index_name)); - FILE *index = NULL; - if (!path) { - sway_log(L_ERROR, "Failed to allocate memory"); - goto fail; - } - - strcpy(path, theme_dir); - strcat(path, index_name); - - index = fopen(path, "r"); - if (!index) { - sway_log(L_ERROR, "Could not open file: %s", path); - goto fail; - } - - char *buf = NULL; - size_t n = 0; - const char directories[] = "Directories"; - while (!feof(index) && getline(&buf, &n, index) != -1) { - if (n <= sizeof(directories) + 1) { - continue; - } - if (strncmp(directories, buf, sizeof(directories) - 1) == 0) { - char *dirstr = buf + sizeof(directories); - dirs = split_subdirs(dirstr); - break; - } - } - // Now, find the size of each dir - struct subdir *current_subdir = NULL; - const char size[] = "Size"; - while (!feof(index) && getline(&buf, &n, index) != -1) { - if (buf[0] == '[') { - int len = strlen(buf); - if (buf[len-1] == '\n') { - len--; - } - // replace ']' - buf[len-1] = '\0'; - - int index; - if ((index = list_seq_find(dirs, subdir_str_cmp, buf+1)) != -1) { - current_subdir = (dirs->items[index]); - } - } - - if (strncmp(size, buf, sizeof(size) - 1) == 0) { - if (current_subdir) { - current_subdir->size = atoi(buf + sizeof(size)); - } - } - } - free(buf); -fail: - free(path); - if (index) { - fclose(index); - } - return dirs; -} - -/* Returns the file of an icon given its name and size */ -static char *find_icon_file(const char *name, int size) { - int namelen = strlen(name); - list_t *dirs = find_all_theme_dirs(swaybar.config->icon_theme); - if (!dirs) { - return NULL; - } - int min_size_diff = INT_MAX; - char *current_file = NULL; - - for (int i = 0; i < dirs->length; ++i) { - char *dir = dirs->items[i]; - list_t *subdirs = find_theme_subdirs(dir); - - if (!subdirs) { - continue; - } - - for (int i = 0; i < subdirs->length; ++i) { - struct subdir *subdir = subdirs->items[i]; - - // Only use an unsized if we don't already have a - // canidate this should probably change to allow svgs - if (!subdir->size && current_file) { - continue; - } - - int size_diff = abs(size - subdir->size); - - if (size_diff >= min_size_diff) { - continue; - } - - char *path = malloc(strlen(subdir->name) + strlen(dir) + 2); - - strcpy(path, dir); - path[strlen(dir)] = '/'; - strcpy(path + strlen(dir) + 1, subdir->name); - - DIR *icons = opendir(path); - if (!icons) { - free(path); - continue; - } - - struct dirent *direntry; - while ((direntry = readdir(icons)) != NULL) { - int len = strlen(direntry->d_name); - if (len <= namelen + 2) { //must have some ext - continue; - } - if (strncmp(direntry->d_name, name, namelen) == 0) { - char *ext = direntry->d_name + namelen + 1; -#ifdef WITH_GDK_PIXBUF - if (strcmp(ext, "png") == 0 || - strcmp(ext, "xpm") == 0 || - strcmp(ext, "svg") == 0) { -#else - if (strcmp(ext, "png") == 0) { -#endif - free(current_file); - char *icon_path = malloc(strlen(path) + len + 2); - - strcpy(icon_path, path); - icon_path[strlen(path)] = '/'; - strcpy(icon_path + strlen(path) + 1, direntry->d_name); - current_file = icon_path; - min_size_diff = size_diff; - } - } - } - free(path); - closedir(icons); - } - free_flat_list(subdirs); - } - free_flat_list(dirs); - - return current_file; -} - -cairo_surface_t *find_icon(const char *name, int size) { - char *image_path = find_icon_file(name, size); - if (image_path == NULL) { - return NULL; - } - - cairo_surface_t *image = NULL; -#ifdef WITH_GDK_PIXBUF - GError *err = NULL; - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(image_path, &err); - if (!pixbuf) { - sway_log(L_ERROR, "Failed to load icon image: %s", err->message); - } - image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf); - g_object_unref(pixbuf); -#else - // TODO make svg work? cairo supports it. maybe remove gdk alltogether - image = cairo_image_surface_create_from_png(image_path); -#endif //WITH_GDK_PIXBUF - if (!image) { - sway_log(L_ERROR, "Could not read icon image"); - return NULL; - } - - free(image_path); - return image; -} diff --git a/swaybar/tray/sni.c b/swaybar/tray/sni.c deleted file mode 100644 index c9d00657..00000000 --- a/swaybar/tray/sni.c +++ /dev/null @@ -1,481 +0,0 @@ -#define _XOPEN_SOURCE 500 -#include -#include -#include -#include -#include -#include -#include -#include -#include "swaybar/tray/dbus.h" -#include "swaybar/tray/sni.h" -#include "swaybar/tray/icon.h" -#include "swaybar/bar.h" -#include "client/cairo.h" -#include "log.h" - -// Not sure what this is but cairo needs it. -static const cairo_user_data_key_t cairo_user_data_key; - -struct sni_icon_ref *sni_icon_ref_create(struct StatusNotifierItem *item, - int height) { - struct sni_icon_ref *sni_ref = malloc(sizeof(struct sni_icon_ref)); - if (!sni_ref) { - return NULL; - } - sni_ref->icon = cairo_image_surface_scale(item->image, height, height); - sni_ref->ref = item; - - return sni_ref; -} - -void sni_icon_ref_free(struct sni_icon_ref *sni_ref) { - if (!sni_ref) { - return; - } - cairo_surface_destroy(sni_ref->icon); - free(sni_ref); -} - -/* Gets the pixmap of an icon */ -static void reply_icon(DBusPendingCall *pending, void *_data) { - struct StatusNotifierItem *item = _data; - - DBusMessage *reply = dbus_pending_call_steal_reply(pending); - - if (!reply) { - sway_log(L_ERROR, "Did not get reply"); - goto bail; - } - - int message_type = dbus_message_get_type(reply); - - if (message_type == DBUS_MESSAGE_TYPE_ERROR) { - char *msg; - - dbus_message_get_args(reply, NULL, - DBUS_TYPE_STRING, &msg, - DBUS_TYPE_INVALID); - - sway_log(L_ERROR, "Message is error: %s", msg); - goto bail; - } - - DBusMessageIter iter; - DBusMessageIter variant; /* v[a(iiay)] */ - DBusMessageIter array; /* a(iiay) */ - DBusMessageIter d_struct; /* (iiay) */ - DBusMessageIter icon; /* ay */ - - dbus_message_iter_init(reply, &iter); - - // Each if here checks the types above before recursing - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { - sway_log(L_ERROR, "Relpy type incorrect"); - sway_log(L_ERROR, "Should be \"v\", is \"%s\"", - dbus_message_iter_get_signature(&iter)); - goto bail; - } - dbus_message_iter_recurse(&iter, &variant); - - if (strcmp("a(iiay)", dbus_message_iter_get_signature(&variant)) != 0) { - sway_log(L_ERROR, "Relpy type incorrect"); - sway_log(L_ERROR, "Should be \"a(iiay)\", is \"%s\"", - dbus_message_iter_get_signature(&variant)); - goto bail; - } - - if (dbus_message_iter_get_element_count(&variant) == 0) { - // Can't recurse if there are no items - sway_log(L_INFO, "Item has no icon"); - goto bail; - } - dbus_message_iter_recurse(&variant, &array); - - dbus_message_iter_recurse(&array, &d_struct); - - int width; - dbus_message_iter_get_basic(&d_struct, &width); - dbus_message_iter_next(&d_struct); - - int height; - dbus_message_iter_get_basic(&d_struct, &height); - dbus_message_iter_next(&d_struct); - - int len = dbus_message_iter_get_element_count(&d_struct); - - if (!len) { - sway_log(L_ERROR, "No icon data"); - goto bail; - } - - // Also implies len % 4 == 0, useful below - if (len != width * height * 4) { - sway_log(L_ERROR, "Incorrect array size passed"); - goto bail; - } - - dbus_message_iter_recurse(&d_struct, &icon); - - int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); - // FIXME support a variable stride - // (works on my machine though for all tested widths) - if (!sway_assert(stride == width * 4, "Stride must be equal to byte length")) { - goto bail; - } - - // Data is by reference, no need to free - uint8_t *message_data; - dbus_message_iter_get_fixed_array(&icon, &message_data, &len); - - uint8_t *image_data = malloc(stride * height); - if (!image_data) { - sway_log(L_ERROR, "Could not allocate memory for icon"); - goto bail; - } - - // Transform from network byte order to host byte order - // Assumptions are safe because the equality above - uint32_t *network = (uint32_t *) message_data; - uint32_t *host = (uint32_t *)image_data; - for (int i = 0; i < width * height; ++i) { - host[i] = ntohl(network[i]); - } - - cairo_surface_t *image = cairo_image_surface_create_for_data( - image_data, CAIRO_FORMAT_ARGB32, - width, height, stride); - - if (image) { - if (item->image) { - cairo_surface_destroy(item->image); - } - item->image = image; - // Free the image data on surface destruction - cairo_surface_set_user_data(image, - &cairo_user_data_key, - image_data, - free); - item->dirty = true; - dirty = true; - - dbus_message_unref(reply); - dbus_pending_call_unref(pending); - return; - } else { - sway_log(L_ERROR, "Could not create image surface"); - free(image_data); - } - -bail: - if (reply) { - dbus_message_unref(reply); - } - dbus_pending_call_unref(pending); - sway_log(L_ERROR, "Could not get icon from item"); - return; -} -static void send_icon_msg(struct StatusNotifierItem *item) { - DBusPendingCall *pending; - DBusMessage *message = dbus_message_new_method_call( - item->name, - "/StatusNotifierItem", - "org.freedesktop.DBus.Properties", - "Get"); - const char *iface; - if (item->kde_special_snowflake) { - iface = "org.kde.StatusNotifierItem"; - } else { - iface = "org.freedesktop.StatusNotifierItem"; - } - const char *prop = "IconPixmap"; - - dbus_message_append_args(message, - DBUS_TYPE_STRING, &iface, - DBUS_TYPE_STRING, &prop, - DBUS_TYPE_INVALID); - - bool status = - dbus_connection_send_with_reply(conn, message, &pending, -1); - - dbus_message_unref(message); - - if (!(pending || status)) { - sway_log(L_ERROR, "Could not get item icon"); - return; - } - - dbus_pending_call_set_notify(pending, reply_icon, item, NULL); -} - -/* Get an icon by its name */ -static void reply_icon_name(DBusPendingCall *pending, void *_data) { - struct StatusNotifierItem *item = _data; - - DBusMessage *reply = dbus_pending_call_steal_reply(pending); - - if (!reply) { - sway_log(L_INFO, "Got no icon name reply from item"); - goto bail; - } - - int message_type = dbus_message_get_type(reply); - - if (message_type == DBUS_MESSAGE_TYPE_ERROR) { - char *msg; - - dbus_message_get_args(reply, NULL, - DBUS_TYPE_STRING, &msg, - DBUS_TYPE_INVALID); - - sway_log(L_INFO, "Could not get icon name: %s", msg); - goto bail; - } - - DBusMessageIter iter; /* v[s] */ - DBusMessageIter variant; /* s */ - - dbus_message_iter_init(reply, &iter); - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { - sway_log(L_ERROR, "Relpy type incorrect"); - sway_log(L_ERROR, "Should be \"v\", is \"%s\"", - dbus_message_iter_get_signature(&iter)); - goto bail; - } - dbus_message_iter_recurse(&iter, &variant); - - - if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_STRING) { - sway_log(L_ERROR, "Relpy type incorrect"); - sway_log(L_ERROR, "Should be \"s\", is \"%s\"", - dbus_message_iter_get_signature(&iter)); - goto bail; - } - - char *icon_name; - dbus_message_iter_get_basic(&variant, &icon_name); - - cairo_surface_t *image = find_icon(icon_name, 256); - - if (image) { - sway_log(L_DEBUG, "Icon for %s found with size %d", icon_name, - cairo_image_surface_get_width(image)); - if (item->image) { - cairo_surface_destroy(item->image); - } - item->image = image; - item->dirty = true; - dirty = true; - - dbus_message_unref(reply); - dbus_pending_call_unref(pending); - return; - } - -bail: - if (reply) { - dbus_message_unref(reply); - } - dbus_pending_call_unref(pending); - // Now try the pixmap - send_icon_msg(item); - return; -} -static void send_icon_name_msg(struct StatusNotifierItem *item) { - DBusPendingCall *pending; - DBusMessage *message = dbus_message_new_method_call( - item->name, - "/StatusNotifierItem", - "org.freedesktop.DBus.Properties", - "Get"); - const char *iface; - if (item->kde_special_snowflake) { - iface = "org.kde.StatusNotifierItem"; - } else { - iface = "org.freedesktop.StatusNotifierItem"; - } - const char *prop = "IconName"; - - dbus_message_append_args(message, - DBUS_TYPE_STRING, &iface, - DBUS_TYPE_STRING, &prop, - DBUS_TYPE_INVALID); - - bool status = - dbus_connection_send_with_reply(conn, message, &pending, -1); - - dbus_message_unref(message); - - if (!(pending || status)) { - sway_log(L_ERROR, "Could not get item icon name"); - return; - } - - dbus_pending_call_set_notify(pending, reply_icon_name, item, NULL); -} - -void get_icon(struct StatusNotifierItem *item) { - send_icon_name_msg(item); -} - -void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y) { - const char *iface = - (item->kde_special_snowflake ? "org.kde.StatusNotifierItem" - : "org.freedesktop.StatusNotifierItem"); - DBusMessage *message = dbus_message_new_method_call( - item->name, - "/StatusNotifierItem", - iface, - "Activate"); - - dbus_message_append_args(message, - DBUS_TYPE_INT32, &x, - DBUS_TYPE_INT32, &y, - DBUS_TYPE_INVALID); - - dbus_connection_send(conn, message, NULL); - - dbus_message_unref(message); -} - -void sni_context_menu(struct StatusNotifierItem *item, uint32_t x, uint32_t y) { - const char *iface = - (item->kde_special_snowflake ? "org.kde.StatusNotifierItem" - : "org.freedesktop.StatusNotifierItem"); - DBusMessage *message = dbus_message_new_method_call( - item->name, - "/StatusNotifierItem", - iface, - "ContextMenu"); - - dbus_message_append_args(message, - DBUS_TYPE_INT32, &x, - DBUS_TYPE_INT32, &y, - DBUS_TYPE_INVALID); - - dbus_connection_send(conn, message, NULL); - - dbus_message_unref(message); -} -void sni_secondary(struct StatusNotifierItem *item, uint32_t x, uint32_t y) { - const char *iface = - (item->kde_special_snowflake ? "org.kde.StatusNotifierItem" - : "org.freedesktop.StatusNotifierItem"); - DBusMessage *message = dbus_message_new_method_call( - item->name, - "/StatusNotifierItem", - iface, - "SecondaryActivate"); - - dbus_message_append_args(message, - DBUS_TYPE_INT32, &x, - DBUS_TYPE_INT32, &y, - DBUS_TYPE_INVALID); - - dbus_connection_send(conn, message, NULL); - - dbus_message_unref(message); -} - -static void get_unique_name(struct StatusNotifierItem *item) { - // I think that we're fine being sync here becaues the message is - // directly to the message bus. Could be async though. - DBusMessage *message = dbus_message_new_method_call( - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "GetNameOwner"); - - dbus_message_append_args(message, - DBUS_TYPE_STRING, &item->name, - DBUS_TYPE_INVALID); - - DBusMessage *reply = dbus_connection_send_with_reply_and_block( - conn, message, -1, NULL); - - dbus_message_unref(message); - - if (!reply) { - sway_log(L_ERROR, "Could not get unique name for item: %s", - item->name); - return; - } - - char *unique_name; - if (!dbus_message_get_args(reply, NULL, - DBUS_TYPE_STRING, &unique_name, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error parsing method args"); - } else { - if (item->unique_name) { - free(item->unique_name); - } - item->unique_name = strdup(unique_name); - } - - dbus_message_unref(reply); -} - -struct StatusNotifierItem *sni_create(const char *name) { - // Make sure `name` is well formed - if (!dbus_validate_bus_name(name, NULL)) { - sway_log(L_INFO, "Name (%s) is not a bus name. We cannot create an item.", name); - return NULL; - } - - struct StatusNotifierItem *item = malloc(sizeof(struct StatusNotifierItem)); - item->name = strdup(name); - item->unique_name = NULL; - item->image = NULL; - item->dirty = false; - - // If it doesn't use this name then assume that it uses the KDE spec - // This is because xembed-sni-proxy uses neither "org.freedesktop" nor - // "org.kde" and just gives us the items "unique name" - // - // We could use this to our advantage and fill out the "unique name" - // field with the given name if it is neither freedesktop or kde, but - // that's makes us rely on KDE hackyness which is bad practice - const char freedesktop_name[] = "org.freedesktop"; - if (strncmp(name, freedesktop_name, sizeof(freedesktop_name) - 1) != 0) { - item->kde_special_snowflake = true; - } else { - item->kde_special_snowflake = false; - } - - get_icon(item); - - get_unique_name(item); - - return item; -} -/* Return 0 if `item` has a name of `str` */ -int sni_str_cmp(const void *_item, const void *_str) { - const struct StatusNotifierItem *item = _item; - const char *str = _str; - - return strcmp(item->name, str); -} -/* Returns 0 if `item` has a unique name of `str` */ -int sni_uniq_cmp(const void *_item, const void *_str) { - const struct StatusNotifierItem *item = _item; - const char *str = _str; - - if (!item->unique_name) { - return false; - } - return strcmp(item->unique_name, str); -} -void sni_free(struct StatusNotifierItem *item) { - if (!item) { - return; - } - free(item->name); - if (item->unique_name) { - free(item->unique_name); - } - if (item->image) { - cairo_surface_destroy(item->image); - } - free(item); -} diff --git a/swaybar/tray/sni_watcher.c b/swaybar/tray/sni_watcher.c deleted file mode 100644 index 86453e70..00000000 --- a/swaybar/tray/sni_watcher.c +++ /dev/null @@ -1,497 +0,0 @@ -#define _XOPEN_SOURCE 500 -#include -#include -#include -#include -#include -#include -#include "swaybar/tray/dbus.h" -#include "list.h" -#include "log.h" - -static list_t *items = NULL; -static list_t *hosts = NULL; - -/** - * Describes the function of the StatusNotifierWatcher - * See https://freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierWatcher/ - * - * We also implement KDE's special snowflake protocol, it's like this but with - * all occurrences 'freedesktop' replaced with 'kde'. There is no KDE introspect. - */ -static const char *interface_xml = - "" - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - ""; - -static void host_registered_signal(DBusConnection *connection) { - // Send one signal for each protocol - DBusMessage *signal = dbus_message_new_signal( - "/StatusNotifierWatcher", - "org.freedesktop.StatusNotifierWatcher", - "StatusNotifierHostRegistered"); - - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); - - - signal = dbus_message_new_signal( - "/StatusNotifierWatcher", - "org.kde.StatusNotifierWatcher", - "StatusNotifierHostRegistered"); - - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); -} -static void item_registered_signal(DBusConnection *connection, const char *name) { - DBusMessage *signal = dbus_message_new_signal( - "/StatusNotifierWatcher", - "org.freedesktop.StatusNotifierWatcher", - "StatusNotifierItemRegistered"); - dbus_message_append_args(signal, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); - - signal = dbus_message_new_signal( - "/StatusNotifierWatcher", - "org.kde.StatusNotifierWatcher", - "StatusNotifierItemRegistered"); - dbus_message_append_args(signal, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); -} -static void item_unregistered_signal(DBusConnection *connection, const char *name) { - DBusMessage *signal = dbus_message_new_signal( - "/StatusNotifierWatcher", - "org.freedesktop.StatusNotifierWatcher", - "StatusNotifierItemUnregistered"); - dbus_message_append_args(signal, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); - - signal = dbus_message_new_signal( - "/StatusNotifierWatcher", - "org.kde.StatusNotifierWatcher", - "StatusNotifierItemUnregistered"); - dbus_message_append_args(signal, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); -} - -static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) { - DBusMessage *reply; - - reply = dbus_message_new_method_return(request); - dbus_message_append_args(reply, - DBUS_TYPE_STRING, &interface_xml, - DBUS_TYPE_INVALID); - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); -} - -static void register_item(DBusConnection *connection, DBusMessage *message) { - DBusError error; - char *name; - - dbus_error_init(&error); - if (!dbus_message_get_args(message, &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error parsing method args: %s\n", error.message); - } - - sway_log(L_INFO, "RegisterStatusNotifierItem called with \"%s\"\n", name); - - // Don't add duplicate or not real item - if (!dbus_validate_bus_name(name, NULL)) { - sway_log(L_INFO, "This item is not valid, we cannot keep track of it."); - return; - } - - if (list_seq_find(items, (int (*)(const void *, const void *))strcmp, name) != -1) { - return; - } - if (!dbus_bus_name_has_owner(connection, name, &error)) { - return; - } - - list_add(items, strdup(name)); - item_registered_signal(connection, name); - - // It's silly, but xembedsniproxy wants a reply for this function - DBusMessage *reply = dbus_message_new_method_return(message); - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); -} - -static void register_host(DBusConnection *connection, DBusMessage *message) { - DBusError error; - char *name; - - dbus_error_init(&error); - if (!dbus_message_get_args(message, &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error parsing method args: %s\n", error.message); - } - - sway_log(L_INFO, "RegisterStatusNotifierHost called with \"%s\"\n", name); - - // Don't add duplicate or not real host - if (!dbus_validate_bus_name(name, NULL)) { - sway_log(L_INFO, "This item is not valid, we cannot keep track of it."); - return; - } - - - if (list_seq_find(hosts, (int (*)(const void *, const void *))strcmp, name) != -1) { - return; - } - if (!dbus_bus_name_has_owner(connection, name, &error)) { - return; - } - - list_add(hosts, strdup(name)); - host_registered_signal(connection); -} - -static void get_property(DBusConnection *connection, DBusMessage *message) { - DBusError error; - char *interface; - char *property; - - dbus_error_init(&error); - if (!dbus_message_get_args(message, &error, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_STRING, &property, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error parsing prop args: %s\n", error.message); - return; - } - - if (strcmp(property, "RegisteredStatusNotifierItems") == 0) { - sway_log(L_INFO, "Replying with items\n"); - DBusMessage *reply; - reply = dbus_message_new_method_return(message); - DBusMessageIter iter; - DBusMessageIter sub; - DBusMessageIter subsub; - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, - "as", &sub); - dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY, - "s", &subsub); - - for (int i = 0; i < items->length; ++i) { - dbus_message_iter_append_basic(&subsub, - DBUS_TYPE_STRING, &items->items[i]); - } - - dbus_message_iter_close_container(&sub, &subsub); - dbus_message_iter_close_container(&iter, &sub); - - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } else if (strcmp(property, "IsStatusNotifierHostRegistered") == 0) { - DBusMessage *reply; - DBusMessageIter iter; - DBusMessageIter sub; - int registered = (hosts == NULL || hosts->length == 0) ? 0 : 1; - - reply = dbus_message_new_method_return(message); - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, - "b", &sub); - dbus_message_iter_append_basic(&sub, - DBUS_TYPE_BOOLEAN, ®istered); - - dbus_message_iter_close_container(&iter, &sub); - - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } else if (strcmp(property, "ProtocolVersion") == 0) { - DBusMessage *reply; - DBusMessageIter iter; - DBusMessageIter sub; - const int version = 0; - - reply = dbus_message_new_method_return(message); - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, - "i", &sub); - dbus_message_iter_append_basic(&sub, - DBUS_TYPE_INT32, &version); - - dbus_message_iter_close_container(&iter, &sub); - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } -} - -static void set_property(DBusConnection *connection, DBusMessage *message) { - // All properties are read only and we don't allow new properties - return; -} - -static void get_all(DBusConnection *connection, DBusMessage *message) { - DBusMessage *reply; - reply = dbus_message_new_method_return(message); - DBusMessageIter iter; /* a{v} */ - DBusMessageIter arr; - DBusMessageIter dict; - DBusMessageIter sub; - DBusMessageIter subsub; - int registered = (hosts == NULL || hosts->length == 0) ? 0 : 1; - const int version = 0; - const char *prop; - - // Could clean this up with a function for each prop - dbus_message_iter_init_append(reply, &iter); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - "{sv}", &arr); - - prop = "RegisteredStatusNotifierItems"; - dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY, - NULL, &dict); - dbus_message_iter_append_basic(&dict, - DBUS_TYPE_STRING, &prop); - dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, - "as", &sub); - dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY, - "s", &subsub); - for (int i = 0; i < items->length; ++i) { - dbus_message_iter_append_basic(&subsub, - DBUS_TYPE_STRING, &items->items[i]); - } - dbus_message_iter_close_container(&sub, &subsub); - dbus_message_iter_close_container(&dict, &sub); - dbus_message_iter_close_container(&arr, &dict); - - prop = "IsStatusNotifierHostRegistered"; - dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY, - NULL, &dict); - dbus_message_iter_append_basic(&dict, - DBUS_TYPE_STRING, &prop); - dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, - "b", &sub); - dbus_message_iter_append_basic(&sub, - DBUS_TYPE_BOOLEAN, ®istered); - dbus_message_iter_close_container(&dict, &sub); - dbus_message_iter_close_container(&arr, &dict); - - prop = "ProtocolVersion"; - dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY, - NULL, &dict); - dbus_message_iter_append_basic(&dict, - DBUS_TYPE_STRING, &prop); - dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, - "i", &sub); - dbus_message_iter_append_basic(&sub, - DBUS_TYPE_INT32, &version); - dbus_message_iter_close_container(&dict, &sub); - dbus_message_iter_close_container(&arr, &dict); - - dbus_message_iter_close_container(&iter, &arr); - - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); -} - -static DBusHandlerResult message_handler(DBusConnection *connection, - DBusMessage *message, void *data) { - const char *interface_name = dbus_message_get_interface(message); - const char *member_name = dbus_message_get_member(message); - - // In order of the xml above - if (strcmp(interface_name, "org.freedesktop.DBus.Introspectable") == 0 && - strcmp(member_name, "Introspect") == 0) { - // We don't have an introspect for KDE - respond_to_introspect(connection, message); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (strcmp(interface_name, "org.freedesktop.DBus.Properties") == 0) { - if (strcmp(member_name, "Get") == 0) { - get_property(connection, message); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (strcmp(member_name, "Set") == 0) { - set_property(connection, message); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (strcmp(member_name, "GetAll") == 0) { - get_all(connection, message); - return DBUS_HANDLER_RESULT_HANDLED; - } else { - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - } else if (strcmp(interface_name, "org.freedesktop.StatusNotifierWatcher") == 0 || - strcmp(interface_name, "org.kde.StatusNotifierWatcher") == 0) { - if (strcmp(member_name, "RegisterStatusNotifierItem") == 0) { - register_item(connection, message); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (strcmp(member_name, "RegisterStatusNotifierHost") == 0) { - register_host(connection, message); - return DBUS_HANDLER_RESULT_HANDLED; - } else { - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - } - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static DBusHandlerResult signal_handler(DBusConnection *connection, - DBusMessage *message, void *_data) { - if (dbus_message_is_signal(message, "org.freedesktop.DBus", "NameOwnerChanged")) { - // Only eat the message if it is name that we are watching - const char *name; - const char *old_owner; - const char *new_owner; - int index; - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &old_owner, - DBUS_TYPE_STRING, &new_owner, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error getting LostName args"); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - if (strcmp(new_owner, "") != 0) { - // Name is not lost - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - if ((index = list_seq_find(items, (int (*)(const void *, const void *))strcmp, name)) != -1) { - sway_log(L_INFO, "Status Notifier Item lost %s", name); - free(items->items[index]); - list_del(items, index); - item_unregistered_signal(connection, name); - - return DBUS_HANDLER_RESULT_HANDLED; - } - if ((index = list_seq_find(hosts, (int (*)(const void *, const void *))strcmp, name)) != -1) { - sway_log(L_INFO, "Status Notifier Host lost %s", name); - free(hosts->items[index]); - list_del(hosts, index); - - return DBUS_HANDLER_RESULT_HANDLED; - } - } - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static const DBusObjectPathVTable vtable = { - .message_function = message_handler, - .unregister_function = NULL, -}; - -int init_sni_watcher() { - DBusError error; - dbus_error_init(&error); - if (!conn) { - sway_log(L_ERROR, "Connection is null, cannot initiate StatusNotifierWatcher"); - return -1; - } - - items = create_list(); - hosts = create_list(); - - int status = dbus_bus_request_name(conn, "org.freedesktop.StatusNotifierWatcher", - DBUS_NAME_FLAG_REPLACE_EXISTING, - &error); - if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - sway_log(L_DEBUG, "Got watcher name"); - } else if (status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) { - sway_log(L_INFO, "Could not get watcher name, it may start later"); - } - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "dbus err getting watcher name: %s\n", error.message); - return -1; - } - - status = dbus_bus_request_name(conn, "org.kde.StatusNotifierWatcher", - DBUS_NAME_FLAG_REPLACE_EXISTING, - &error); - if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - sway_log(L_DEBUG, "Got kde watcher name"); - } else if (status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) { - sway_log(L_INFO, "Could not get kde watcher name, it may start later"); - } - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "dbus err getting kde watcher name: %s\n", error.message); - return -1; - } - - dbus_connection_try_register_object_path(conn, - "/StatusNotifierWatcher", - &vtable, NULL, &error); - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "dbus_err: %s\n", error.message); - return -1; - } - - dbus_bus_add_match(conn, - "type='signal',\ - sender='org.freedesktop.DBus',\ - interface='org.freedesktop.DBus',\ - member='NameOwnerChanged'", - &error); - - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "DBus error getting match args: %s", error.message); - } - - dbus_connection_add_filter(conn, signal_handler, NULL, NULL); - return 0; -} diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c deleted file mode 100644 index 91c3af06..00000000 --- a/swaybar/tray/tray.c +++ /dev/null @@ -1,398 +0,0 @@ -#define _XOPEN_SOURCE 700 -#include -#include -#include -#include -#include -#include "swaybar/bar.h" -#include "swaybar/tray/tray.h" -#include "swaybar/tray/dbus.h" -#include "swaybar/tray/sni.h" -#include "swaybar/tray/sni_watcher.h" -#include "swaybar/bar.h" -#include "swaybar/config.h" -#include "list.h" -#include "log.h" - -struct tray *tray; - -static void register_host(char *name) { - DBusMessage *message; - - message = dbus_message_new_method_call( - "org.freedesktop.StatusNotifierWatcher", - "/StatusNotifierWatcher", - "org.freedesktop.StatusNotifierWatcher", - "RegisterStatusNotifierHost"); - if (!message) { - sway_log(L_ERROR, "Cannot allocate dbus method call"); - return; - } - - dbus_message_append_args(message, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); - - dbus_connection_send(conn, message, NULL); - - dbus_message_unref(message); -} - -static void get_items_reply(DBusPendingCall *pending, void *_data) { - DBusMessage *reply = dbus_pending_call_steal_reply(pending); - - if (!reply) { - sway_log(L_ERROR, "Got no items reply from sni watcher"); - goto bail; - } - - int message_type = dbus_message_get_type(reply); - - if (message_type == DBUS_MESSAGE_TYPE_ERROR) { - char *msg; - - dbus_message_get_args(reply, NULL, - DBUS_TYPE_STRING, &msg, - DBUS_TYPE_INVALID); - - sway_log(L_ERROR, "Message is error: %s", msg); - goto bail; - } - - DBusMessageIter iter; - DBusMessageIter variant; - DBusMessageIter array; - - dbus_message_iter_init(reply, &iter); - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { - sway_log(L_ERROR, "Replyed with wrong type, not v(as)"); - goto bail; - } - dbus_message_iter_recurse(&iter, &variant); - if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(&variant) != DBUS_TYPE_STRING) { - sway_log(L_ERROR, "Replyed with wrong type, not v(as)"); - goto bail; - } - - // Clear list - list_foreach(tray->items, (void (*)(void *))sni_free); - list_free(tray->items); - tray->items = create_list(); - - // O(n) function, could be faster dynamically reading values - int len = dbus_message_iter_get_element_count(&variant); - - dbus_message_iter_recurse(&variant, &array); - for (int i = 0; i < len; i++) { - const char *name; - dbus_message_iter_get_basic(&array, &name); - - struct StatusNotifierItem *item = sni_create(name); - - if (item) { - sway_log(L_DEBUG, "Item registered with host: %s", name); - list_add(tray->items, item); - dirty = true; - } - } - -bail: - dbus_message_unref(reply); - dbus_pending_call_unref(pending); - return; -} -static void get_items() { - DBusPendingCall *pending; - DBusMessage *message = dbus_message_new_method_call( - "org.freedesktop.StatusNotifierWatcher", - "/StatusNotifierWatcher", - "org.freedesktop.DBus.Properties", - "Get"); - - const char *iface = "org.freedesktop.StatusNotifierWatcher"; - const char *prop = "RegisteredStatusNotifierItems"; - dbus_message_append_args(message, - DBUS_TYPE_STRING, &iface, - DBUS_TYPE_STRING, &prop, - DBUS_TYPE_INVALID); - - bool status = - dbus_connection_send_with_reply(conn, message, &pending, -1); - dbus_message_unref(message); - - if (!(pending || status)) { - sway_log(L_ERROR, "Could not get items"); - return; - } - - dbus_pending_call_set_notify(pending, get_items_reply, NULL, NULL); -} - -static DBusHandlerResult signal_handler(DBusConnection *connection, - DBusMessage *message, void *_data) { - if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierWatcher", - "StatusNotifierItemRegistered")) { - const char *name; - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error getting StatusNotifierItemRegistered args"); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - - if (list_seq_find(tray->items, sni_str_cmp, name) == -1) { - struct StatusNotifierItem *item = sni_create(name); - - if (item) { - list_add(tray->items, item); - dirty = true; - } - } - - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierWatcher", - "StatusNotifierItemUnregistered")) { - const char *name; - if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - sway_log(L_ERROR, "Error getting StatusNotifierItemUnregistered args"); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - - int index; - if ((index = list_seq_find(tray->items, sni_str_cmp, name)) != -1) { - sni_free(tray->items->items[index]); - list_del(tray->items, index); - dirty = true; - } else { - // If it's not in our list, then our list is incorrect. - // Fetch all items again - sway_log(L_INFO, "Host item list incorrect, refreshing"); - get_items(); - } - - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierItem", - "NewIcon") || dbus_message_is_signal(message, - "org.kde.StatusNotifierItem", "NewIcon")) { - const char *name; - int index; - struct StatusNotifierItem *item; - - name = dbus_message_get_sender(message); - if ((index = list_seq_find(tray->items, sni_uniq_cmp, name)) != -1) { - item = tray->items->items[index]; - sway_log(L_INFO, "NewIcon signal from item %s", item->name); - get_icon(item); - } - - return DBUS_HANDLER_RESULT_HANDLED; - } - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static int init_host() { - tray = (struct tray *)malloc(sizeof(tray)); - - tray->items = create_list(); - - DBusError error; - dbus_error_init(&error); - char *name = NULL; - if (!conn) { - sway_log(L_ERROR, "Connection is null, cannot init SNI host"); - goto err; - } - name = calloc(sizeof(char), 256); - - if (!name) { - sway_log(L_ERROR, "Cannot allocate name"); - goto err; - } - - pid_t pid = getpid(); - if (snprintf(name, 256, "org.freedesktop.StatusNotifierHost-%d", pid) - >= 256) { - sway_log(L_ERROR, "Cannot get host name because string is too short." - "This should not happen"); - goto err; - } - - // We want to be the sole owner of this name - if (dbus_bus_request_name(conn, name, DBUS_NAME_FLAG_DO_NOT_QUEUE, - &error) != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - sway_log(L_ERROR, "Cannot get host name and start the tray"); - goto err; - } - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "Dbus err getting host name: %s\n", error.message); - goto err; - } - sway_log(L_DEBUG, "Got host name"); - - register_host(name); - - get_items(); - - // Perhaps use addmatch helper functions like wlc does? - dbus_bus_add_match(conn, - "type='signal',\ - sender='org.freedesktop.StatusNotifierWatcher',\ - member='StatusNotifierItemRegistered'", - &error); - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "dbus_err: %s", error.message); - goto err; - } - dbus_bus_add_match(conn, - "type='signal',\ - sender='org.freedesktop.StatusNotifierWatcher',\ - member='StatusNotifierItemUnregistered'", - &error); - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "dbus_err: %s", error.message); - return -1; - } - - // SNI matches - dbus_bus_add_match(conn, - "type='signal',\ - interface='org.freedesktop.StatusNotifierItem',\ - member='NewIcon'", - &error); - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "dbus_err %s", error.message); - goto err; - } - dbus_bus_add_match(conn, - "type='signal',\ - interface='org.kde.StatusNotifierItem',\ - member='NewIcon'", - &error); - if (dbus_error_is_set(&error)) { - sway_log(L_ERROR, "dbus_err %s", error.message); - goto err; - } - - dbus_connection_add_filter(conn, signal_handler, NULL, NULL); - - free(name); - return 0; - -err: - // TODO better handle errors - free(name); - return -1; -} - -void tray_mouse_event(struct output *output, int x, int y, - uint32_t button, uint32_t state) { - - struct window *window = output->window; - uint32_t tray_padding = swaybar.config->tray_padding; - int tray_width = window->width * window->scale; - - for (int i = 0; i < output->items->length; ++i) { - struct sni_icon_ref *item = - output->items->items[i]; - int icon_width = cairo_image_surface_get_width(item->icon); - - tray_width -= tray_padding; - if (x <= tray_width && x >= tray_width - icon_width) { - if (button == swaybar.config->activate_button) { - sni_activate(item->ref, x, y); - } else if (button == swaybar.config->context_button) { - sni_context_menu(item->ref, x, y); - } else if (button == swaybar.config->secondary_button) { - sni_secondary(item->ref, x, y); - } - break; - } - tray_width -= icon_width; - } -} - -uint32_t tray_render(struct output *output, struct config *config) { - struct window *window = output->window; - cairo_t *cairo = window->cairo; - - // Tray icons - uint32_t tray_padding = config->tray_padding; - uint32_t tray_width = window->width * window->scale; - const int item_size = (window->height * window->scale) - (2 * tray_padding); - - if (item_size < 0) { - // Can't render items if the padding is too large - return tray_width; - } - - if (config->tray_output && strcmp(config->tray_output, output->name) != 0) { - return tray_width; - } - - for (int i = 0; i < tray->items->length; ++i) { - struct StatusNotifierItem *item = - tray->items->items[i]; - if (!item->image) { - continue; - } - - struct sni_icon_ref *render_item = NULL; - int j; - for (j = i; j < output->items->length; ++j) { - struct sni_icon_ref *ref = - output->items->items[j]; - if (ref->ref == item) { - render_item = ref; - break; - } else { - sni_icon_ref_free(ref); - list_del(output->items, j); - } - } - - if (!render_item) { - render_item = sni_icon_ref_create(item, item_size); - list_add(output->items, render_item); - } else if (item->dirty) { - // item needs re-render - sni_icon_ref_free(render_item); - output->items->items[j] = render_item = - sni_icon_ref_create(item, item_size); - } - - tray_width -= tray_padding; - tray_width -= item_size; - - cairo_operator_t op = cairo_get_operator(cairo); - cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); - cairo_set_source_surface(cairo, render_item->icon, tray_width, tray_padding); - cairo_rectangle(cairo, tray_width, tray_padding, item_size, item_size); - cairo_fill(cairo); - cairo_set_operator(cairo, op); - - item->dirty = false; - } - - - if (tray_width != window->width * window->scale) { - tray_width -= tray_padding; - } - - return tray_width; -} - -void init_tray(struct bar *bar) { - if (!bar->config->tray_output || strcmp(bar->config->tray_output, "none") != 0) { - /* Connect to the D-Bus */ - dbus_init(); - - /* Start the SNI watcher */ - init_sni_watcher(); - - /* Start the SNI host */ - init_host(); - } -} From 5c9ad035db1bebba3f1954dd1f4328c6421776d4 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Wed, 28 Mar 2018 23:56:02 -0400 Subject: [PATCH 03/34] Wire up basic IPC support --- include/swaybar/bar.h | 3 + include/swaybar/config.h | 17 ++-- include/swaybar/ipc.h | 5 +- swaybar/bar.c | 11 +++ swaybar/config.c | 14 +-- swaybar/ipc.c | 199 +++++++++++++++++++++++++++++++++++++++ swaybar/meson.build | 1 + 7 files changed, 225 insertions(+), 25 deletions(-) create mode 100644 swaybar/ipc.c diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 3ae8c0b3..df685f47 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -17,6 +17,9 @@ struct swaybar { struct swaybar_config *config; struct swaybar_output *focused_output; + int ipc_event_socketfd; + int ipc_socketfd; + struct wl_list outputs; }; diff --git a/include/swaybar/config.h b/include/swaybar/config.h index 1bfe4843..4b3b5b34 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h @@ -2,21 +2,20 @@ #define _SWAYBAR_CONFIG_H #include #include -#include "list.h" +#include #include "util.h" -/** - * Colors for a box with background, border and text colors. - */ struct box_colors { uint32_t border; uint32_t background; uint32_t text; }; -/** - * Swaybar config. - */ +struct config_output { + struct wl_list link; + char *name; +}; + struct swaybar_config { char *status_command; bool pango_markup; @@ -28,8 +27,7 @@ struct swaybar_config { bool binding_mode_indicator; bool wrap_scroll; bool workspace_buttons; - bool all_outputs; - list_t *outputs; + struct wl_list outputs; int height; struct { @@ -51,5 +49,6 @@ struct swaybar_config { struct swaybar_config *init_config(); void free_config(struct swaybar_config *config); +uint32_t parse_position(const char *position); #endif diff --git a/include/swaybar/ipc.h b/include/swaybar/ipc.h index 57a1b925..7f71a506 100644 --- a/include/swaybar/ipc.h +++ b/include/swaybar/ipc.h @@ -2,8 +2,7 @@ #define _SWAYBAR_IPC_H #include "swaybar/bar.h" -void ipc_bar_init(struct swaybar *bar, const char *bar_id); -bool handle_ipc_event(struct swaybar *bar); -void ipc_send_workspace_command(const char *workspace_name); +void ipc_get_config(struct swaybar *bar, const char *bar_id); +void handle_ipc_event(struct swaybar *bar); #endif diff --git a/swaybar/bar.c b/swaybar/bar.c index e1d594b4..433e2948 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -14,6 +14,8 @@ #include "swaybar/config.h" #include "swaybar/event_loop.h" #include "swaybar/bar.h" +#include "swaybar/ipc.h" +#include "ipc-client.h" #include "list.h" #include "pango.h" #include "pool-buffer.h" @@ -92,6 +94,10 @@ void bar_setup(struct swaybar *bar, bar_init(bar); init_event_loop(); + bar->ipc_socketfd = ipc_open_socket(socket_path); + bar->ipc_event_socketfd = ipc_open_socket(socket_path); + ipc_get_config(bar, bar_id); + assert(bar->display = wl_display_connect(NULL)); struct wl_registry *registry = wl_display_get_registry(bar->display); @@ -122,6 +128,11 @@ static void display_in(int fd, short mask, void *_bar) { } } +static void ipc_in(int fd, short mask, void *_bar) { + struct swaybar *bar = (struct swaybar *)_bar; + handle_ipc_event(bar); +} + void bar_run(struct swaybar *bar) { add_event(wl_display_get_fd(bar->display), POLLIN, display_in, bar); while (1) { diff --git a/swaybar/config.c b/swaybar/config.c index 0c2b57e0..83cf2309 100644 --- a/swaybar/config.c +++ b/swaybar/config.c @@ -22,17 +22,6 @@ uint32_t parse_position(const char *position) { } } -char *parse_font(const char *font) { - char *new_font = NULL; - if (strncmp("pango:", font, 6) == 0) { - font += 6; - } - - new_font = strdup(font); - - return new_font; -} - struct swaybar_config *init_config() { struct swaybar_config *config = calloc(1, sizeof(struct swaybar_config)); config->status_command = NULL; @@ -45,8 +34,7 @@ struct swaybar_config *init_config() { config->binding_mode_indicator = true; config->wrap_scroll = false; config->workspace_buttons = true; - config->all_outputs = false; - config->outputs = create_list(); + wl_list_init(&config->outputs); /* height */ config->height = 0; diff --git a/swaybar/ipc.c b/swaybar/ipc.c new file mode 100644 index 00000000..cef784d0 --- /dev/null +++ b/swaybar/ipc.c @@ -0,0 +1,199 @@ +#define _XOPEN_SOURCE 500 +#include +#include +#include +#include "swaybar/config.h" +#include "swaybar/ipc.h" +#include "ipc-client.h" + +char *parse_font(const char *font) { + char *new_font = NULL; + if (strncmp("pango:", font, 6) == 0) { + font += 6; + } + new_font = strdup(font); + return new_font; +} + +static void ipc_parse_colors( + struct swaybar_config *config, json_object *colors) { + json_object *background, *statusline, *separator; + json_object *focused_background, *focused_statusline, *focused_separator; + json_object *focused_workspace_border, *focused_workspace_bg, *focused_workspace_text; + json_object *inactive_workspace_border, *inactive_workspace_bg, *inactive_workspace_text; + json_object *active_workspace_border, *active_workspace_bg, *active_workspace_text; + json_object *urgent_workspace_border, *urgent_workspace_bg, *urgent_workspace_text; + json_object *binding_mode_border, *binding_mode_bg, *binding_mode_text; + json_object_object_get_ex(colors, "background", &background); + json_object_object_get_ex(colors, "statusline", &statusline); + json_object_object_get_ex(colors, "separator", &separator); + json_object_object_get_ex(colors, "focused_background", &focused_background); + json_object_object_get_ex(colors, "focused_statusline", &focused_statusline); + json_object_object_get_ex(colors, "focused_separator", &focused_separator); + json_object_object_get_ex(colors, "focused_workspace_border", &focused_workspace_border); + json_object_object_get_ex(colors, "focused_workspace_bg", &focused_workspace_bg); + json_object_object_get_ex(colors, "focused_workspace_text", &focused_workspace_text); + json_object_object_get_ex(colors, "active_workspace_border", &active_workspace_border); + json_object_object_get_ex(colors, "active_workspace_bg", &active_workspace_bg); + json_object_object_get_ex(colors, "active_workspace_text", &active_workspace_text); + json_object_object_get_ex(colors, "inactive_workspace_border", &inactive_workspace_border); + json_object_object_get_ex(colors, "inactive_workspace_bg", &inactive_workspace_bg); + json_object_object_get_ex(colors, "inactive_workspace_text", &inactive_workspace_text); + json_object_object_get_ex(colors, "urgent_workspace_border", &urgent_workspace_border); + json_object_object_get_ex(colors, "urgent_workspace_bg", &urgent_workspace_bg); + json_object_object_get_ex(colors, "urgent_workspace_text", &urgent_workspace_text); + json_object_object_get_ex(colors, "binding_mode_border", &binding_mode_border); + json_object_object_get_ex(colors, "binding_mode_bg", &binding_mode_bg); + json_object_object_get_ex(colors, "binding_mode_text", &binding_mode_text); + if (background) { + config->colors.background = parse_color(json_object_get_string(background)); + } + if (statusline) { + config->colors.statusline = parse_color(json_object_get_string(statusline)); + } + if (separator) { + config->colors.separator = parse_color(json_object_get_string(separator)); + } + if (focused_background) { + config->colors.focused_background = parse_color(json_object_get_string(focused_background)); + } + if (focused_statusline) { + config->colors.focused_statusline = parse_color(json_object_get_string(focused_statusline)); + } + if (focused_separator) { + config->colors.focused_separator = parse_color(json_object_get_string(focused_separator)); + } + if (focused_workspace_border) { + config->colors.focused_workspace.border = parse_color(json_object_get_string(focused_workspace_border)); + } + if (focused_workspace_bg) { + config->colors.focused_workspace.background = parse_color(json_object_get_string(focused_workspace_bg)); + } + if (focused_workspace_text) { + config->colors.focused_workspace.text = parse_color(json_object_get_string(focused_workspace_text)); + } + if (active_workspace_border) { + config->colors.active_workspace.border = parse_color(json_object_get_string(active_workspace_border)); + } + if (active_workspace_bg) { + config->colors.active_workspace.background = parse_color(json_object_get_string(active_workspace_bg)); + } + if (active_workspace_text) { + config->colors.active_workspace.text = parse_color(json_object_get_string(active_workspace_text)); + } + if (inactive_workspace_border) { + config->colors.inactive_workspace.border = parse_color(json_object_get_string(inactive_workspace_border)); + } + if (inactive_workspace_bg) { + config->colors.inactive_workspace.background = parse_color(json_object_get_string(inactive_workspace_bg)); + } + if (inactive_workspace_text) { + config->colors.inactive_workspace.text = parse_color(json_object_get_string(inactive_workspace_text)); + } + if (binding_mode_border) { + config->colors.binding_mode.border = parse_color(json_object_get_string(binding_mode_border)); + } + if (binding_mode_bg) { + config->colors.binding_mode.background = parse_color(json_object_get_string(binding_mode_bg)); + } + if (binding_mode_text) { + config->colors.binding_mode.text = parse_color(json_object_get_string(binding_mode_text)); + } +} + +static void ipc_parse_config( + struct swaybar_config *config, const char *payload) { + json_object *bar_config = json_tokener_parse(payload); + json_object *markup, *mode, *hidden_bar, *position, *status_command; + json_object *font, *bar_height, *wrap_scroll, *workspace_buttons, *strip_workspace_numbers; + json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol, *outputs; + json_object_object_get_ex(bar_config, "mode", &mode); + json_object_object_get_ex(bar_config, "hidden_bar", &hidden_bar); + json_object_object_get_ex(bar_config, "position", &position); + json_object_object_get_ex(bar_config, "status_command", &status_command); + json_object_object_get_ex(bar_config, "font", &font); + json_object_object_get_ex(bar_config, "bar_height", &bar_height); + json_object_object_get_ex(bar_config, "wrap_scroll", &wrap_scroll); + json_object_object_get_ex(bar_config, "workspace_buttons", &workspace_buttons); + json_object_object_get_ex(bar_config, "strip_workspace_numbers", &strip_workspace_numbers); + json_object_object_get_ex(bar_config, "binding_mode_indicator", &binding_mode_indicator); + json_object_object_get_ex(bar_config, "verbose", &verbose); + json_object_object_get_ex(bar_config, "separator_symbol", &sep_symbol); + json_object_object_get_ex(bar_config, "colors", &colors); + json_object_object_get_ex(bar_config, "outputs", &outputs); + json_object_object_get_ex(bar_config, "pango_markup", &markup); + if (status_command) { + free(config->status_command); + config->status_command = strdup(json_object_get_string(status_command)); + } + if (position) { + config->position = parse_position(json_object_get_string(position)); + } + if (font) { + free(config->font); + config->font = parse_font(json_object_get_string(font)); + } + if (sep_symbol) { + free(config->sep_symbol); + config->sep_symbol = strdup(json_object_get_string(sep_symbol)); + } + if (strip_workspace_numbers) { + config->strip_workspace_numbers = json_object_get_boolean(strip_workspace_numbers); + } + if (binding_mode_indicator) { + config->binding_mode_indicator = json_object_get_boolean(binding_mode_indicator); + } + if (wrap_scroll) { + config->wrap_scroll = json_object_get_boolean(wrap_scroll); + } + if (workspace_buttons) { + config->workspace_buttons = json_object_get_boolean(workspace_buttons); + } + if (bar_height) { + config->height = json_object_get_int(bar_height); + } + if (markup) { + config->pango_markup = json_object_get_boolean(markup); + } + + struct config_output *output, *tmp; + wl_list_for_each_safe(output, tmp, &config->outputs, link) { + wl_list_remove(&output->link); + free(output->name); + free(output); + } + if (outputs) { + int length = json_object_array_length(outputs); + for (int i = 0; i < length; ++i) { + json_object *output = json_object_array_get_idx(outputs, i); + const char *name = json_object_get_string(output); + if (strcmp("*", name) == 0) { + // TODO: do we need to clear out the list here or something + break; + } + struct config_output *coutput = calloc( + 1, sizeof(struct config_output)); + coutput->name = strdup(name); + wl_list_insert(&config->outputs, &coutput->link); + } + } + + if (colors) { + ipc_parse_colors(config, colors); + } + + json_object_put(bar_config); +} + +void ipc_get_config(struct swaybar *bar, const char *bar_id) { + uint32_t len = strlen(bar_id); + char *res = ipc_single_command(bar->ipc_socketfd, + IPC_GET_BAR_CONFIG, bar_id, &len); + ipc_parse_config(bar->config, res); + free(res); +} + +void handle_ipc_event(struct swaybar *bar) { + struct ipc_response *resp = ipc_recv_response(bar->ipc_event_socketfd); + free_ipc_response(resp); +} diff --git a/swaybar/meson.build b/swaybar/meson.build index fd87e51d..6dc7c564 100644 --- a/swaybar/meson.build +++ b/swaybar/meson.build @@ -4,6 +4,7 @@ executable( 'bar.c', 'config.c', 'event_loop.c', + 'ipc.c', 'main.c', 'render.c', ], From e5e8094dc3119584ae611c3197b38243b6c016c9 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 00:07:35 -0400 Subject: [PATCH 04/34] Only utilize the configured outputs --- include/swaybar/bar.h | 2 +- include/swaybar/config.h | 1 + swaybar/bar.c | 32 ++++++++++++++++++++------------ swaybar/ipc.c | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 13 deletions(-) diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index df685f47..6e1ab66d 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -31,7 +31,7 @@ struct swaybar_output { struct zwlr_layer_surface_v1 *layer_surface; char *name; - int idx; + size_t index; bool focused; uint32_t width, height; diff --git a/include/swaybar/config.h b/include/swaybar/config.h index 4b3b5b34..6bcefe64 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h @@ -14,6 +14,7 @@ struct box_colors { struct config_output { struct wl_list link; char *name; + size_t index; }; struct swaybar_config { diff --git a/swaybar/bar.c b/swaybar/bar.c index 433e2948..a6e3b780 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -65,13 +65,13 @@ static void handle_global(void *data, struct wl_registry *registry, bar->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); } else if (strcmp(interface, wl_output_interface.name) == 0) { - static int idx = 0; + static size_t index = 0; struct swaybar_output *output = calloc(1, sizeof(struct swaybar_output)); output->bar = bar; output->output = wl_registry_bind(registry, name, &wl_output_interface, 1); - output->idx = idx++; + output->index = index++; wl_list_insert(&bar->outputs, &output->link); } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { bar->layer_shell = wl_registry_bind( @@ -108,16 +108,24 @@ void bar_setup(struct swaybar *bar, // TODO: we might not necessarily be meant to do all of the outputs struct swaybar_output *output; wl_list_for_each(output, &bar->outputs, link) { - assert(output->surface = wl_compositor_create_surface(bar->compositor)); - output->layer_surface = zwlr_layer_shell_v1_get_layer_surface( - bar->layer_shell, output->surface, output->output, - ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel"); - assert(output->layer_surface); - zwlr_layer_surface_v1_add_listener(output->layer_surface, - &layer_surface_listener, output); - zwlr_layer_surface_v1_set_anchor(output->layer_surface, - bar->config->position); - render_frame(bar, output); + struct config_output *coutput; + wl_list_for_each(coutput, &bar->config->outputs, link) { + if (coutput->index != output->index) { + continue; + } + assert(output->surface = wl_compositor_create_surface( + bar->compositor)); + output->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + bar->layer_shell, output->surface, output->output, + ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel"); + assert(output->layer_surface); + zwlr_layer_surface_v1_add_listener(output->layer_surface, + &layer_surface_listener, output); + zwlr_layer_surface_v1_set_anchor(output->layer_surface, + bar->config->position); + render_frame(bar, output); + break; + } } } diff --git a/swaybar/ipc.c b/swaybar/ipc.c index cef784d0..d3ee170c 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -1,4 +1,5 @@ #define _XOPEN_SOURCE 500 +#include #include #include #include @@ -174,6 +175,7 @@ static void ipc_parse_config( struct config_output *coutput = calloc( 1, sizeof(struct config_output)); coutput->name = strdup(name); + coutput->index = SIZE_MAX; wl_list_insert(&config->outputs, &coutput->link); } } @@ -185,12 +187,47 @@ static void ipc_parse_config( json_object_put(bar_config); } +static void ipc_get_outputs(struct swaybar *bar) { + uint32_t len = 0; + char *res = ipc_single_command(bar->ipc_socketfd, + IPC_GET_OUTPUTS, NULL, &len); + json_object *outputs = json_tokener_parse(res); + for (size_t i = 0; i < json_object_array_length(outputs); ++i) { + json_object *output = json_object_array_get_idx(outputs, i); + json_object *output_name, *output_active; + json_object_object_get_ex(output, "name", &output_name); + json_object_object_get_ex(output, "active", &output_active); + const char *name = json_object_get_string(output_name); + bool active = json_object_get_boolean(output_active); + if (!active) { + continue; + } + if (wl_list_empty(&bar->config->outputs)) { + struct config_output *coutput = calloc( + 1, sizeof(struct config_output)); + coutput->name = strdup(name); + coutput->index = i; + wl_list_insert(&bar->config->outputs, &coutput->link); + } else { + struct config_output *coutput; + wl_list_for_each(coutput, &bar->config->outputs, link) { + if (strcmp(name, coutput->name) == 0) { + coutput->index = i; + break; + } + } + } + } + free(res); +} + void ipc_get_config(struct swaybar *bar, const char *bar_id) { uint32_t len = strlen(bar_id); char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_BAR_CONFIG, bar_id, &len); ipc_parse_config(bar->config, res); free(res); + ipc_get_outputs(bar); } void handle_ipc_event(struct swaybar *bar) { From 3399ad9840f0d3d42b377f4404115d887f65e18a Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 00:21:05 -0400 Subject: [PATCH 05/34] Round up workspaces on each output --- include/swaybar/bar.h | 16 +++++++++-- include/swaybar/ipc.h | 3 ++- swaybar/bar.c | 9 +++++-- swaybar/ipc.c | 63 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 85 insertions(+), 6 deletions(-) diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 6e1ab66d..c89aa61c 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -30,6 +30,8 @@ struct swaybar_output { struct wl_surface *surface; struct zwlr_layer_surface_v1 *layer_surface; + struct wl_list workspaces; + char *name; size_t index; bool focused; @@ -39,9 +41,19 @@ struct swaybar_output { struct pool_buffer *current_buffer; }; +struct swaybar_workspace { + struct wl_list link; + int num; + char *name; + bool focused; + bool visible; + bool urgent; +}; + +// TODO: Rename stuff to match wlroots conventions (init/create/etc) void bar_setup(struct swaybar *bar, - const char *socket_path, - const char *bar_id); + const char *socket_path, + const char *bar_id); void bar_run(struct swaybar *bar); void bar_teardown(struct swaybar *bar); diff --git a/include/swaybar/ipc.h b/include/swaybar/ipc.h index 7f71a506..f3881bd0 100644 --- a/include/swaybar/ipc.h +++ b/include/swaybar/ipc.h @@ -2,7 +2,8 @@ #define _SWAYBAR_IPC_H #include "swaybar/bar.h" -void ipc_get_config(struct swaybar *bar, const char *bar_id); +void ipc_initialize(struct swaybar *bar, const char *bar_id); void handle_ipc_event(struct swaybar *bar); +void ipc_get_workspaces(struct swaybar *bar); #endif diff --git a/swaybar/bar.c b/swaybar/bar.c index a6e3b780..68dea408 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -72,6 +72,7 @@ static void handle_global(void *data, struct wl_registry *registry, output->output = wl_registry_bind(registry, name, &wl_output_interface, 1); output->index = index++; + wl_list_init(&output->workspaces); wl_list_insert(&bar->outputs, &output->link); } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { bar->layer_shell = wl_registry_bind( @@ -96,7 +97,7 @@ void bar_setup(struct swaybar *bar, bar->ipc_socketfd = ipc_open_socket(socket_path); bar->ipc_event_socketfd = ipc_open_socket(socket_path); - ipc_get_config(bar, bar_id); + ipc_initialize(bar, bar_id); assert(bar->display = wl_display_connect(NULL)); @@ -113,6 +114,7 @@ void bar_setup(struct swaybar *bar, if (coutput->index != output->index) { continue; } + output->name = strdup(coutput->name); assert(output->surface = wl_compositor_create_surface( bar->compositor)); output->layer_surface = zwlr_layer_shell_v1_get_layer_surface( @@ -123,10 +125,13 @@ void bar_setup(struct swaybar *bar, &layer_surface_listener, output); zwlr_layer_surface_v1_set_anchor(output->layer_surface, bar->config->position); - render_frame(bar, output); break; } } + ipc_get_workspaces(bar); + wl_list_for_each(output, &bar->outputs, link) { + render_frame(bar, output); + } } static void display_in(int fd, short mask, void *_bar) { diff --git a/swaybar/ipc.c b/swaybar/ipc.c index d3ee170c..6b832070 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -187,6 +187,65 @@ static void ipc_parse_config( json_object_put(bar_config); } +static void free_workspaces(struct wl_list *list) { + struct swaybar_workspace *ws, *tmp; + wl_list_for_each_safe(ws, tmp, list, link) { + wl_list_remove(&ws->link); + free(ws->name); + free(ws); + } +} + +void ipc_get_workspaces(struct swaybar *bar) { + struct swaybar_output *output; + wl_list_for_each(output, &bar->outputs, link) { + free_workspaces(&output->workspaces); + } + uint32_t len = 0; + char *res = ipc_single_command(bar->ipc_socketfd, + IPC_GET_WORKSPACES, NULL, &len); + json_object *results = json_tokener_parse(res); + if (!results) { + free(res); + return; + } + size_t length = json_object_array_length(results); + json_object *ws_json; + json_object *num, *name, *visible, *focused, *out, *urgent; + for (size_t i = 0; i < length; ++i) { + ws_json = json_object_array_get_idx(results, i); + + json_object_object_get_ex(ws_json, "num", &num); + json_object_object_get_ex(ws_json, "name", &name); + json_object_object_get_ex(ws_json, "visible", &visible); + json_object_object_get_ex(ws_json, "focused", &focused); + json_object_object_get_ex(ws_json, "output", &out); + json_object_object_get_ex(ws_json, "urgent", &urgent); + + wl_list_for_each(output, &bar->outputs, link) { + const char *ws_output = json_object_get_string(out); + if (strcmp(ws_output, output->name) == 0) { + struct swaybar_workspace *ws = + calloc(1, sizeof(struct swaybar_workspace)); + ws->num = json_object_get_int(num); + ws->name = strdup(json_object_get_string(name)); + ws->visible = json_object_get_boolean(visible); + ws->focused = json_object_get_boolean(focused); + if (ws->focused) { + if (bar->focused_output) { + bar->focused_output->focused = false; + } + bar->focused_output = output; + output->focused = true; + } + ws->urgent = json_object_get_boolean(urgent); + wl_list_insert(&output->workspaces, &ws->link); + } + } + } + free(res); +} + static void ipc_get_outputs(struct swaybar *bar) { uint32_t len = 0; char *res = ipc_single_command(bar->ipc_socketfd, @@ -218,16 +277,18 @@ static void ipc_get_outputs(struct swaybar *bar) { } } } + json_object_put(outputs); free(res); } -void ipc_get_config(struct swaybar *bar, const char *bar_id) { +void ipc_initialize(struct swaybar *bar, const char *bar_id) { uint32_t len = strlen(bar_id); char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_BAR_CONFIG, bar_id, &len); ipc_parse_config(bar->config, res); free(res); ipc_get_outputs(bar); + // TODO: subscribe to stuff } void handle_ipc_event(struct swaybar *bar) { From 3a458cd7b55bdfad1b04a01064f4fe8fa86ed0de Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 00:49:59 -0400 Subject: [PATCH 06/34] Implement workspace button rendering --- swaybar/render.c | 92 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 4 deletions(-) diff --git a/swaybar/render.c b/swaybar/render.c index 2eaa0195..226e4ce3 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -10,8 +10,79 @@ #include "swaybar/render.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" -static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar *bar, - struct swaybar_output *output) { +static const char *strip_workspace_number(const char *ws_name) { + size_t len = strlen(ws_name); + for (size_t i = 0; i < len; ++i) { + if (ws_name[i] < '0' || ws_name[i] > '9') { + if (':' == ws_name[i] && i < len - 1 && i > 0) { + return ws_name + i + 1; + } + return ws_name; + } + } + return ws_name; +} + +static uint32_t render_workspace_button(cairo_t *cairo, + struct swaybar_config *config, struct swaybar_workspace *ws, + double *x, uint32_t height) { + static const int ws_horizontal_padding = 5; + static const double ws_vertical_padding = 1.5; + static const int ws_spacing = 1; + + const char *name = ws->name; + if (config->strip_workspace_numbers) { + name = strip_workspace_number(ws->name); + } + + struct box_colors box_colors; + if (ws->urgent) { + box_colors = config->colors.urgent_workspace; + } else if (ws->focused) { + box_colors = config->colors.focused_workspace; + } else if (ws->visible) { + box_colors = config->colors.active_workspace; + } else { + box_colors = config->colors.inactive_workspace; + } + + int text_width, text_height; + get_text_size(cairo, config->font, &text_width, &text_height, + 1, true, "%s", name); + uint32_t ideal_height = ws_vertical_padding * 2 + text_height; + if (height < ideal_height) { + height = ideal_height; + } + uint32_t width = ws_horizontal_padding * 2 + text_width; + + cairo_set_source_u32(cairo, box_colors.background); + cairo_rectangle(cairo, *x, 0, width - 1, height); + cairo_fill(cairo); + + cairo_set_source_u32(cairo, box_colors.border); + cairo_rectangle(cairo, *x, 0, width - 1, height); + cairo_stroke(cairo); + + double text_y = height / 2.0 - text_height / 2.0; + cairo_set_source_u32(cairo, box_colors.text); + cairo_move_to(cairo, (int)*x + ws_horizontal_padding, (int)floor(text_y)); + pango_printf(cairo, config->font, 1, true, "%s", name); + + *x += width + ws_spacing; + return ideal_height; +} + +static void update_heights(uint32_t height, uint32_t *min, uint32_t *max) { + if (*min < height) { + *min = height; + } + if (height > *max) { + *max = height; + } +} + +static uint32_t render_to_cairo(cairo_t *cairo, + struct swaybar *bar, struct swaybar_output *output) { struct swaybar_config *config = bar->config; cairo_save(cairo); @@ -27,8 +98,20 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar *bar, } cairo_paint(cairo); - // TODO: use actual height - return 20; + uint32_t min_height = output->height, max_height = output->height; + + double x = 0; + if (config->workspace_buttons) { + struct swaybar_workspace *ws; + wl_list_for_each(ws, &output->workspaces, link) { + uint32_t h = render_workspace_button( + cairo, config, ws, &x, output->height); + update_heights(h, &min_height, &max_height); + } + } + + // TODO: Shrink via min_height if sane + return max_height; } void render_frame(struct swaybar *bar, @@ -41,6 +124,7 @@ void render_frame(struct swaybar *bar, // Reconfigure surface zwlr_layer_surface_v1_set_size( output->layer_surface, 0, height); + zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, height); // TODO: this could infinite loop if the compositor assigns us a // different height than what we asked for wl_surface_commit(output->surface); From 86ba0fc15d7615b09f0279616d538af5c23bc551 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 10:38:17 -0400 Subject: [PATCH 07/34] Re-render bar on IPC updates --- include/swaybar/ipc.h | 3 ++- swaybar/bar.c | 8 +++++++- swaybar/ipc.c | 46 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/include/swaybar/ipc.h b/include/swaybar/ipc.h index f3881bd0..278baef0 100644 --- a/include/swaybar/ipc.h +++ b/include/swaybar/ipc.h @@ -1,9 +1,10 @@ #ifndef _SWAYBAR_IPC_H #define _SWAYBAR_IPC_H +#include #include "swaybar/bar.h" void ipc_initialize(struct swaybar *bar, const char *bar_id); -void handle_ipc_event(struct swaybar *bar); +bool handle_ipc_event(struct swaybar *bar); void ipc_get_workspaces(struct swaybar *bar); #endif diff --git a/swaybar/bar.c b/swaybar/bar.c index 68dea408..90fd5ad4 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -143,11 +143,17 @@ static void display_in(int fd, short mask, void *_bar) { static void ipc_in(int fd, short mask, void *_bar) { struct swaybar *bar = (struct swaybar *)_bar; - handle_ipc_event(bar); + if (handle_ipc_event(bar)) { + struct swaybar_output *output; + wl_list_for_each(output, &bar->outputs, link) { + render_frame(bar, output); + } + } } void bar_run(struct swaybar *bar) { add_event(wl_display_get_fd(bar->display), POLLIN, display_in, bar); + add_event(bar->ipc_event_socketfd, POLLIN, ipc_in, bar); while (1) { event_loop_poll(); } diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 6b832070..75f17953 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "swaybar/config.h" #include "swaybar/ipc.h" #include "ipc-client.h" @@ -288,10 +289,51 @@ void ipc_initialize(struct swaybar *bar, const char *bar_id) { ipc_parse_config(bar->config, res); free(res); ipc_get_outputs(bar); - // TODO: subscribe to stuff + + const char *subscribe = "[ \"workspace\", \"mode\" ]"; + len = strlen(subscribe); + free(ipc_single_command(bar->ipc_event_socketfd, + IPC_SUBSCRIBE, subscribe, &len)); } -void handle_ipc_event(struct swaybar *bar) { +bool handle_ipc_event(struct swaybar *bar) { struct ipc_response *resp = ipc_recv_response(bar->ipc_event_socketfd); + if (!resp) { + return false; + } + switch (resp->type) { + case IPC_EVENT_WORKSPACE: + ipc_get_workspaces(bar); + break; + case IPC_EVENT_MODE: { + json_object *result = json_tokener_parse(resp->payload); + if (!result) { + free_ipc_response(resp); + wlr_log(L_ERROR, "failed to parse payload as json"); + return false; + } + json_object *json_change; + if (json_object_object_get_ex(result, "change", &json_change)) { + const char *change = json_object_get_string(json_change); + free(bar->config->mode); + if (strcmp(change, "default") == 0) { + bar->config->mode = NULL; + } else { + bar->config->mode = strdup(change); + } + } else { + wlr_log(L_ERROR, "failed to parse response"); + json_object_put(result); + free_ipc_response(resp); + return false; + } + json_object_put(result); + break; + } + default: + free_ipc_response(resp); + return false; + } free_ipc_response(resp); + return true; } From 37b61eff2df3c8b47b1304650d1fb204a62658db Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 10:59:33 -0400 Subject: [PATCH 08/34] Add binding mode indicator --- swaybar/ipc.c | 1 + swaybar/render.c | 77 +++++++++++++++++++++++++++++++++--------------- 2 files changed, 55 insertions(+), 23 deletions(-) diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 75f17953..3c2d6fbc 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -306,6 +306,7 @@ bool handle_ipc_event(struct swaybar *bar) { ipc_get_workspaces(bar); break; case IPC_EVENT_MODE: { + // TODO: interpret "pango_markup" field json_object *result = json_tokener_parse(resp->payload); if (!result) { free_ipc_response(resp); diff --git a/swaybar/render.c b/swaybar/render.c index 226e4ce3..beb4de40 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -10,6 +11,38 @@ #include "swaybar/render.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" +static const int ws_horizontal_padding = 5; +static const double ws_vertical_padding = 1.5; +static const int ws_spacing = 1; + +static uint32_t render_binding_mode_indicator(cairo_t *cairo, + struct swaybar_config *config, const char *mode, double x, + uint32_t height) { + int text_width, text_height; + get_text_size(cairo, config->font, &text_width, &text_height, + 1, true, "âš¡ %s", mode); + uint32_t ideal_height = text_height + ws_vertical_padding * 2; + if (height < ideal_height) { + height = ideal_height; + } + + cairo_set_source_u32(cairo, config->colors.binding_mode.background); + cairo_rectangle(cairo, x, 0, text_width + ws_horizontal_padding * 2 - 1, + height + ws_vertical_padding * 2); + cairo_fill(cairo); + + cairo_set_source_u32(cairo, config->colors.binding_mode.border); + cairo_rectangle(cairo, x, 0, text_width + ws_horizontal_padding * 2 - 1, + height + ws_vertical_padding * 2); + cairo_stroke(cairo); + + double text_y = height / 2.0 - text_height / 2.0; + cairo_set_source_u32(cairo, config->colors.binding_mode.text); + cairo_move_to(cairo, (int)x + ws_horizontal_padding, (int)floor(text_y)); + pango_printf(cairo, config->font, 1, true, "âš¡ %s", mode); + return ideal_height; +} + static const char *strip_workspace_number(const char *ws_name) { size_t len = strlen(ws_name); for (size_t i = 0; i < len; ++i) { @@ -26,10 +59,6 @@ static const char *strip_workspace_number(const char *ws_name) { static uint32_t render_workspace_button(cairo_t *cairo, struct swaybar_config *config, struct swaybar_workspace *ws, double *x, uint32_t height) { - static const int ws_horizontal_padding = 5; - static const double ws_vertical_padding = 1.5; - static const int ws_spacing = 1; - const char *name = ws->name; if (config->strip_workspace_numbers) { name = strip_workspace_number(ws->name); @@ -72,24 +101,10 @@ static uint32_t render_workspace_button(cairo_t *cairo, return ideal_height; } -static void update_heights(uint32_t height, uint32_t *min, uint32_t *max) { - if (*min < height) { - *min = height; - } - if (height > *max) { - *max = height; - } -} - static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar *bar, struct swaybar_output *output) { struct swaybar_config *config = bar->config; - cairo_save(cairo); - cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); - cairo_paint(cairo); - cairo_restore(cairo); - cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); if (output->focused) { cairo_set_source_u32(cairo, config->colors.focused_background); @@ -98,20 +113,30 @@ static uint32_t render_to_cairo(cairo_t *cairo, } cairo_paint(cairo); - uint32_t min_height = output->height, max_height = output->height; - + uint32_t max_height = 0; + /* + * Each render_* function takes the actual height of the bar, and returns + * the ideal height. If the actual height is too short, the render function + * can do whatever it wants - the buffer won't be committed. If the actual + * height is too tall, the render function should adapt its drawing to + * utilize the available space. + */ double x = 0; if (config->workspace_buttons) { struct swaybar_workspace *ws; wl_list_for_each(ws, &output->workspaces, link) { uint32_t h = render_workspace_button( cairo, config, ws, &x, output->height); - update_heights(h, &min_height, &max_height); + max_height = h > max_height ? h : max_height; } } + if (config->binding_mode_indicator && config->mode) { + uint32_t h = render_binding_mode_indicator( + cairo, config, config->mode, x, output->height); + max_height = h > max_height ? h : max_height; + } - // TODO: Shrink via min_height if sane - return max_height; + return max_height > output->height ? max_height : output->height; } void render_frame(struct swaybar *bar, @@ -134,6 +159,12 @@ void render_frame(struct swaybar *bar, output->current_buffer = get_next_buffer(bar->shm, output->buffers, output->width, output->height); cairo_t *shm = output->current_buffer->cairo; + + cairo_save(shm); + cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR); + cairo_paint(shm); + cairo_restore(shm); + cairo_set_source_surface(shm, recorder, 0.0, 0.0); cairo_paint(shm); wl_surface_attach(output->surface, From 1e8faeec0263a7da311a13c56a0de34e47e66fa6 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 11:58:54 -0400 Subject: [PATCH 09/34] Pixel-perfect rendering --- include/swaybar/config.h | 1 + swaybar/config.c | 1 + swaybar/ipc.c | 62 +++++++++++++++++++++++++++------------- swaybar/render.c | 48 ++++++++++++++++++++----------- 4 files changed, 75 insertions(+), 37 deletions(-) diff --git a/include/swaybar/config.h b/include/swaybar/config.h index 6bcefe64..7634cb16 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h @@ -24,6 +24,7 @@ struct swaybar_config { char *font; char *sep_symbol; char *mode; + bool mode_pango_markup; bool strip_workspace_numbers; bool binding_mode_indicator; bool wrap_scroll; diff --git a/swaybar/config.c b/swaybar/config.c index 83cf2309..802d0779 100644 --- a/swaybar/config.c +++ b/swaybar/config.c @@ -41,6 +41,7 @@ struct swaybar_config *init_config() { /* colors */ config->colors.background = 0x000000FF; + config->colors.focused_background = 0x000000FF; config->colors.statusline = 0xFFFFFFFF; config->colors.separator = 0x666666FF; diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 3c2d6fbc..a260b798 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -48,58 +48,76 @@ static void ipc_parse_colors( json_object_object_get_ex(colors, "binding_mode_bg", &binding_mode_bg); json_object_object_get_ex(colors, "binding_mode_text", &binding_mode_text); if (background) { - config->colors.background = parse_color(json_object_get_string(background)); + config->colors.background = parse_color( + json_object_get_string(background)); } if (statusline) { - config->colors.statusline = parse_color(json_object_get_string(statusline)); + config->colors.statusline = parse_color( + json_object_get_string(statusline)); } if (separator) { - config->colors.separator = parse_color(json_object_get_string(separator)); + config->colors.separator = parse_color( + json_object_get_string(separator)); } if (focused_background) { - config->colors.focused_background = parse_color(json_object_get_string(focused_background)); + config->colors.focused_background = parse_color( + json_object_get_string(focused_background)); } if (focused_statusline) { - config->colors.focused_statusline = parse_color(json_object_get_string(focused_statusline)); + config->colors.focused_statusline = parse_color( + json_object_get_string(focused_statusline)); } if (focused_separator) { - config->colors.focused_separator = parse_color(json_object_get_string(focused_separator)); + config->colors.focused_separator = parse_color( + json_object_get_string(focused_separator)); } if (focused_workspace_border) { - config->colors.focused_workspace.border = parse_color(json_object_get_string(focused_workspace_border)); + config->colors.focused_workspace.border = parse_color( + json_object_get_string(focused_workspace_border)); } if (focused_workspace_bg) { - config->colors.focused_workspace.background = parse_color(json_object_get_string(focused_workspace_bg)); + config->colors.focused_workspace.background = parse_color( + json_object_get_string(focused_workspace_bg)); } if (focused_workspace_text) { - config->colors.focused_workspace.text = parse_color(json_object_get_string(focused_workspace_text)); + config->colors.focused_workspace.text = parse_color( + json_object_get_string(focused_workspace_text)); } if (active_workspace_border) { - config->colors.active_workspace.border = parse_color(json_object_get_string(active_workspace_border)); + config->colors.active_workspace.border = parse_color( + json_object_get_string(active_workspace_border)); } if (active_workspace_bg) { - config->colors.active_workspace.background = parse_color(json_object_get_string(active_workspace_bg)); + config->colors.active_workspace.background = parse_color( + json_object_get_string(active_workspace_bg)); } if (active_workspace_text) { - config->colors.active_workspace.text = parse_color(json_object_get_string(active_workspace_text)); + config->colors.active_workspace.text = parse_color( + json_object_get_string(active_workspace_text)); } if (inactive_workspace_border) { - config->colors.inactive_workspace.border = parse_color(json_object_get_string(inactive_workspace_border)); + config->colors.inactive_workspace.border = parse_color( + json_object_get_string(inactive_workspace_border)); } if (inactive_workspace_bg) { - config->colors.inactive_workspace.background = parse_color(json_object_get_string(inactive_workspace_bg)); + config->colors.inactive_workspace.background = parse_color( + json_object_get_string(inactive_workspace_bg)); } if (inactive_workspace_text) { - config->colors.inactive_workspace.text = parse_color(json_object_get_string(inactive_workspace_text)); + config->colors.inactive_workspace.text = parse_color( + json_object_get_string(inactive_workspace_text)); } if (binding_mode_border) { - config->colors.binding_mode.border = parse_color(json_object_get_string(binding_mode_border)); + config->colors.binding_mode.border = parse_color( + json_object_get_string(binding_mode_border)); } if (binding_mode_bg) { - config->colors.binding_mode.background = parse_color(json_object_get_string(binding_mode_bg)); + config->colors.binding_mode.background = parse_color( + json_object_get_string(binding_mode_bg)); } if (binding_mode_text) { - config->colors.binding_mode.text = parse_color(json_object_get_string(binding_mode_text)); + config->colors.binding_mode.text = parse_color( + json_object_get_string(binding_mode_text)); } } @@ -306,14 +324,13 @@ bool handle_ipc_event(struct swaybar *bar) { ipc_get_workspaces(bar); break; case IPC_EVENT_MODE: { - // TODO: interpret "pango_markup" field json_object *result = json_tokener_parse(resp->payload); if (!result) { free_ipc_response(resp); wlr_log(L_ERROR, "failed to parse payload as json"); return false; } - json_object *json_change; + json_object *json_change, *json_pango_markup; if (json_object_object_get_ex(result, "change", &json_change)) { const char *change = json_object_get_string(json_change); free(bar->config->mode); @@ -328,6 +345,11 @@ bool handle_ipc_event(struct swaybar *bar) { free_ipc_response(resp); return false; } + if (json_object_object_get_ex(result, + "pango_markup", &json_pango_markup)) { + bar->config->mode_pango_markup = json_object_get_boolean( + json_pango_markup); + } json_object_put(result); break; } diff --git a/swaybar/render.c b/swaybar/render.c index beb4de40..ba22e9d4 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -13,33 +13,39 @@ static const int ws_horizontal_padding = 5; static const double ws_vertical_padding = 1.5; -static const int ws_spacing = 1; +static const double border_width = 1; static uint32_t render_binding_mode_indicator(cairo_t *cairo, struct swaybar_config *config, const char *mode, double x, uint32_t height) { int text_width, text_height; get_text_size(cairo, config->font, &text_width, &text_height, - 1, true, "âš¡ %s", mode); - uint32_t ideal_height = text_height + ws_vertical_padding * 2; + 1, true, "%s", mode); + uint32_t ideal_height = text_height + ws_vertical_padding * 2 + + border_width * 2; if (height < ideal_height) { height = ideal_height; } + uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; cairo_set_source_u32(cairo, config->colors.binding_mode.background); - cairo_rectangle(cairo, x, 0, text_width + ws_horizontal_padding * 2 - 1, - height + ws_vertical_padding * 2); + cairo_rectangle(cairo, x, 0, width, height); cairo_fill(cairo); cairo_set_source_u32(cairo, config->colors.binding_mode.border); - cairo_rectangle(cairo, x, 0, text_width + ws_horizontal_padding * 2 - 1, - height + ws_vertical_padding * 2); - cairo_stroke(cairo); + cairo_rectangle(cairo, x, 0, width, border_width); + cairo_fill(cairo); + cairo_rectangle(cairo, x, 0, border_width, height); + cairo_fill(cairo); + cairo_rectangle(cairo, x + width - border_width, 0, border_width, height); + cairo_fill(cairo); + cairo_rectangle(cairo, x, height - border_width, width, border_width); + cairo_fill(cairo); double text_y = height / 2.0 - text_height / 2.0; cairo_set_source_u32(cairo, config->colors.binding_mode.text); - cairo_move_to(cairo, (int)x + ws_horizontal_padding, (int)floor(text_y)); - pango_printf(cairo, config->font, 1, true, "âš¡ %s", mode); + cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); + pango_printf(cairo, config->font, 1, true, "%s", mode); return ideal_height; } @@ -78,26 +84,33 @@ static uint32_t render_workspace_button(cairo_t *cairo, int text_width, text_height; get_text_size(cairo, config->font, &text_width, &text_height, 1, true, "%s", name); - uint32_t ideal_height = ws_vertical_padding * 2 + text_height; + uint32_t ideal_height = ws_vertical_padding * 2 + text_height + + border_width * 2; if (height < ideal_height) { height = ideal_height; } - uint32_t width = ws_horizontal_padding * 2 + text_width; + uint32_t width = ws_horizontal_padding * 2 + text_width + border_width * 2; cairo_set_source_u32(cairo, box_colors.background); - cairo_rectangle(cairo, *x, 0, width - 1, height); + cairo_rectangle(cairo, *x, 0, width, height); cairo_fill(cairo); cairo_set_source_u32(cairo, box_colors.border); - cairo_rectangle(cairo, *x, 0, width - 1, height); - cairo_stroke(cairo); + cairo_rectangle(cairo, *x, 0, width, border_width); + cairo_fill(cairo); + cairo_rectangle(cairo, *x, 0, border_width, height); + cairo_fill(cairo); + cairo_rectangle(cairo, *x + width - border_width, 0, border_width, height); + cairo_fill(cairo); + cairo_rectangle(cairo, *x, height - border_width, width, border_width); + cairo_fill(cairo); double text_y = height / 2.0 - text_height / 2.0; cairo_set_source_u32(cairo, box_colors.text); - cairo_move_to(cairo, (int)*x + ws_horizontal_padding, (int)floor(text_y)); + cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); pango_printf(cairo, config->font, 1, true, "%s", name); - *x += width + ws_spacing; + *x += width; return ideal_height; } @@ -167,6 +180,7 @@ void render_frame(struct swaybar *bar, cairo_set_source_surface(shm, recorder, 0.0, 0.0); cairo_paint(shm); + wl_surface_attach(output->surface, output->current_buffer->buffer, 0, 0); wl_surface_damage(output->surface, 0, 0, output->width, output->height); From 531c175d3e60bf9dbfd538af1fd5eebb906c6f91 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 12:03:08 -0400 Subject: [PATCH 10/34] Respect user bar height preference This is an i3-gaps feature we support --- swaybar/render.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/swaybar/render.c b/swaybar/render.c index ba22e9d4..317b0f65 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -158,6 +158,9 @@ void render_frame(struct swaybar *bar, CAIRO_CONTENT_COLOR_ALPHA, NULL); cairo_t *cairo = cairo_create(recorder); uint32_t height = render_to_cairo(cairo, bar, output); + if (bar->config->height >= 0 && height < (uint32_t)bar->config->height) { + height = bar->config->height; + } if (height != output->height) { // Reconfigure surface zwlr_layer_surface_v1_set_size( From a76829f3756d3df22fe46e6688374497de29c2e1 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 12:19:20 -0400 Subject: [PATCH 11/34] Some layer shell fixes Based on the corresponding rootston changes --- include/sway/layers.h | 2 -- include/sway/output.h | 1 + sway/desktop/layer_shell.c | 22 ---------------------- sway/desktop/output.c | 8 ++++++++ 4 files changed, 9 insertions(+), 24 deletions(-) diff --git a/include/sway/layers.h b/include/sway/layers.h index 22054be1..ee47c5ad 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h @@ -14,8 +14,6 @@ struct sway_layer_surface { struct wl_listener unmap; struct wl_listener surface_commit; struct wl_listener output_destroy; - struct wl_listener output_mode; - struct wl_listener output_transform; bool configured; struct wlr_box geo; diff --git a/include/sway/output.h b/include/sway/output.h index f899230f..6fb79987 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -21,6 +21,7 @@ struct sway_output { struct wl_listener frame; struct wl_listener destroy; struct wl_listener mode; + struct wl_listener transform; pid_t bg_pid; }; diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index bd62f84a..31679fb2 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -193,22 +193,10 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) { struct sway_layer_surface *sway_layer = wl_container_of(listener, sway_layer, output_destroy); wl_list_remove(&sway_layer->output_destroy.link); - wl_list_remove(&sway_layer->output_mode.link); - wl_list_remove(&sway_layer->output_transform.link); sway_layer->layer_surface->output = NULL; wlr_layer_surface_close(sway_layer->layer_surface); } -static void handle_output_mode(struct wl_listener *listener, void *data) { - struct wlr_output *output = data; - arrange_layers((struct sway_output *)output->data); -} - -static void handle_output_transform(struct wl_listener *listener, void *data) { - struct wlr_output *output = data; - arrange_layers((struct sway_output *)output->data); -} - static void handle_surface_commit(struct wl_listener *listener, void *data) { struct sway_layer_surface *layer = wl_container_of(listener, layer, surface_commit); @@ -243,8 +231,6 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&sway_layer->surface_commit.link); if (sway_layer->layer_surface->output != NULL) { wl_list_remove(&sway_layer->output_destroy.link); - wl_list_remove(&sway_layer->output_mode.link); - wl_list_remove(&sway_layer->output_transform.link); } struct sway_output *output = sway_layer->layer_surface->output->data; arrange_layers(output); @@ -289,14 +275,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { wl_signal_add(&layer_surface->output->events.destroy, &sway_layer->output_destroy); - sway_layer->output_mode.notify = handle_output_mode; - wl_signal_add(&layer_surface->output->events.mode, - &sway_layer->output_mode); - - sway_layer->output_transform.notify = handle_output_transform; - wl_signal_add(&layer_surface->output->events.transform, - &sway_layer->output_transform); - sway_layer->destroy.notify = handle_destroy; wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy); sway_layer->map.notify = handle_map; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index b8253ace..30b23a18 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -268,6 +268,12 @@ static void handle_output_mode(struct wl_listener *listener, void *data) { arrange_windows(output->swayc, -1, -1); } +static void handle_output_transform(struct wl_listener *listener, void *data) { + struct sway_output *output = wl_container_of(listener, output, transform); + arrange_layers(output); + arrange_windows(output->swayc, -1, -1); +} + void handle_new_output(struct wl_listener *listener, void *data) { struct sway_server *server = wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; @@ -306,6 +312,8 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->destroy.notify = handle_output_destroy; wl_signal_add(&wlr_output->events.mode, &output->mode); output->mode.notify = handle_output_mode; + wl_signal_add(&wlr_output->events.transform, &output->transform); + output->transform.notify = handle_output_transform; arrange_layers(output); arrange_windows(&root_container, -1, -1); From 718502c815d0af81b4ab71fb6b86976b5403adf9 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 13:49:46 -0400 Subject: [PATCH 12/34] Iterate over workspaces backwards --- swaybar/render.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swaybar/render.c b/swaybar/render.c index 317b0f65..f797873c 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -137,7 +137,7 @@ static uint32_t render_to_cairo(cairo_t *cairo, double x = 0; if (config->workspace_buttons) { struct swaybar_workspace *ws; - wl_list_for_each(ws, &output->workspaces, link) { + wl_list_for_each_reverse(ws, &output->workspaces, link) { uint32_t h = render_workspace_button( cairo, config, ws, &x, output->height); max_height = h > max_height ? h : max_height; From 0d0ab7c5ce148bce841fa0682d04bc7b6c21b902 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 15:16:12 -0400 Subject: [PATCH 13/34] Implement status line Does not yet support i3bar json protocol --- include/swaybar/bar.h | 1 + include/swaybar/status_line.h | 65 ++++++++--------------------- swaybar/bar.c | 26 ++++++++++-- swaybar/config.c | 1 + swaybar/meson.build | 1 + swaybar/render.c | 56 +++++++++++++++++++++++++ swaybar/status_line.c | 78 +++++++++++++++++++++++++++++++++++ 7 files changed, 176 insertions(+), 52 deletions(-) create mode 100644 swaybar/status_line.c diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index c89aa61c..1bf2ea2d 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -16,6 +16,7 @@ struct swaybar { struct swaybar_config *config; struct swaybar_output *focused_output; + struct status_line *status; int ipc_event_socketfd; int ipc_socketfd; diff --git a/include/swaybar/status_line.h b/include/swaybar/status_line.h index 0664ddee..6c595df0 100644 --- a/include/swaybar/status_line.h +++ b/include/swaybar/status_line.h @@ -1,61 +1,30 @@ #ifndef _SWAYBAR_STATUS_LINE_H #define _SWAYBAR_STATUS_LINE_H - #include +#include #include - -#include "list.h" #include "bar.h" -typedef enum {UNDEF, TEXT, I3BAR} command_protocol; +enum status_protocol { + PROTOCOL_UNDEF, + PROTOCOL_TEXT, + PROTOCOL_I3BAR, +}; struct status_line { - list_t *block_line; - const char *text_line; - command_protocol protocol; - bool click_events; -}; + pid_t pid; + int read_fd, write_fd; + FILE *read, *write; -struct status_block { - char *full_text, *short_text, *align; - bool urgent; - uint32_t color; - int min_width; - char *name, *instance; - bool separator; - int separator_block_width; - bool markup; - // Airblader features - uint32_t background; - uint32_t border; - int border_top; - int border_bottom; - int border_left; - int border_right; + enum status_protocol protocol; + const char *text; - // Set during rendering - int x; - int width; + char *buffer; + size_t buffer_size; }; -/** - * Initialize status line struct. - */ -struct status_line *init_status_line(); - -/** - * handle status line activity. - */ -bool handle_status_line(struct bar *bar); - -/** - * Handle mouse clicks. - */ -bool status_line_mouse_event(struct bar *bar, int x, int y, uint32_t button); - -/** - * Free status line struct. - */ -void free_status_line(struct status_line *line); +struct status_line *status_line_init(char *cmd); +void status_line_free(struct status_line *status); +bool handle_status_readable(struct status_line *status); -#endif /* _SWAYBAR_STATUS_LINE_H */ +#endif diff --git a/swaybar/bar.c b/swaybar/bar.c index 90fd5ad4..72c4be8f 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -13,6 +13,7 @@ #include "swaybar/render.h" #include "swaybar/config.h" #include "swaybar/event_loop.h" +#include "swaybar/status_line.h" #include "swaybar/bar.h" #include "swaybar/ipc.h" #include "ipc-client.h" @@ -98,6 +99,9 @@ void bar_setup(struct swaybar *bar, bar->ipc_socketfd = ipc_open_socket(socket_path); bar->ipc_event_socketfd = ipc_open_socket(socket_path); ipc_initialize(bar, bar_id); + if (bar->config->status_command) { + bar->status = status_line_init(bar->config->status_command); + } assert(bar->display = wl_display_connect(NULL)); @@ -134,6 +138,13 @@ void bar_setup(struct swaybar *bar, } } +static void render_all_frames(struct swaybar *bar) { + struct swaybar_output *output; + wl_list_for_each(output, &bar->outputs, link) { + render_frame(bar, output); + } +} + static void display_in(int fd, short mask, void *_bar) { struct swaybar *bar = (struct swaybar *)_bar; if (wl_display_dispatch(bar->display) == -1) { @@ -144,16 +155,23 @@ static void display_in(int fd, short mask, void *_bar) { static void ipc_in(int fd, short mask, void *_bar) { struct swaybar *bar = (struct swaybar *)_bar; if (handle_ipc_event(bar)) { - struct swaybar_output *output; - wl_list_for_each(output, &bar->outputs, link) { - render_frame(bar, output); - } + render_all_frames(bar); + } +} + +static void status_in(int fd, short mask, void *_bar) { + struct swaybar *bar = (struct swaybar *)_bar; + if (handle_status_readable(bar->status)) { + render_all_frames(bar); } } void bar_run(struct swaybar *bar) { add_event(wl_display_get_fd(bar->display), POLLIN, display_in, bar); add_event(bar->ipc_event_socketfd, POLLIN, ipc_in, bar); + if (bar->status) { + add_event(bar->status->read_fd, POLLIN, status_in, bar); + } while (1) { event_loop_poll(); } diff --git a/swaybar/config.c b/swaybar/config.c index 802d0779..9169ad27 100644 --- a/swaybar/config.c +++ b/swaybar/config.c @@ -43,6 +43,7 @@ struct swaybar_config *init_config() { config->colors.background = 0x000000FF; config->colors.focused_background = 0x000000FF; config->colors.statusline = 0xFFFFFFFF; + config->colors.focused_statusline = 0xFFFFFFFF; config->colors.separator = 0x666666FF; config->colors.focused_workspace.border = 0x4C7899FF; diff --git a/swaybar/meson.build b/swaybar/meson.build index 6dc7c564..d15e8b5c 100644 --- a/swaybar/meson.build +++ b/swaybar/meson.build @@ -7,6 +7,7 @@ executable( 'ipc.c', 'main.c', 'render.c', + 'status_line.c', ], include_directories: [sway_inc], dependencies: [ diff --git a/swaybar/render.c b/swaybar/render.c index f797873c..ec1239a1 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -9,12 +9,59 @@ #include "swaybar/bar.h" #include "swaybar/config.h" #include "swaybar/render.h" +#include "swaybar/status_line.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" static const int ws_horizontal_padding = 5; static const double ws_vertical_padding = 1.5; static const double border_width = 1; +static uint32_t render_status_line_text(cairo_t *cairo, + struct swaybar_config *config, struct status_line *status, + bool focused, uint32_t width, uint32_t height) { + if (!status->text) { + return 0; + } + //wlr_log(L_DEBUG, "focused %d", focused); + cairo_set_source_u32(cairo, focused ? + config->colors.focused_statusline : config->colors.statusline); + static const int margin = 3; + int text_width, text_height; + get_text_size(cairo, config->font, &text_width, &text_height, + 1, config->pango_markup, "%s", status->text); + uint32_t ideal_height = text_height + ws_vertical_padding * 2; + if (height < ideal_height) { + height = ideal_height; + } + double text_y = height / 2.0 - text_height / 2.0; + cairo_move_to(cairo, width - text_width - margin, (int)floor(text_y)); + pango_printf(cairo, config->font, 1, config->pango_markup, + "%s", status->text); + return ideal_height; +} + +static uint32_t render_status_line_i3bar(cairo_t *cairo, + struct swaybar_config *config, struct status_line *status, + bool focused, uint32_t width, uint32_t height) { + // TODO + return 0; +} + +static uint32_t render_status_line(cairo_t *cairo, + struct swaybar_config *config, struct status_line *status, + bool focused, uint32_t width, uint32_t height) { + switch (status->protocol) { + case PROTOCOL_TEXT: + return render_status_line_text(cairo, + config, status, focused, width, height); + case PROTOCOL_I3BAR: + return render_status_line_i3bar(cairo, + config, status, focused, width, height); + default: + return 0; + } +} + static uint32_t render_binding_mode_indicator(cairo_t *cairo, struct swaybar_config *config, const char *mode, double x, uint32_t height) { @@ -148,6 +195,11 @@ static uint32_t render_to_cairo(cairo_t *cairo, cairo, config, config->mode, x, output->height); max_height = h > max_height ? h : max_height; } + if (bar->status) { + uint32_t h = render_status_line(cairo, config, bar->status, + output->focused, output->width, output->height); + max_height = h > max_height ? h : max_height; + } return max_height > output->height ? max_height : output->height; } @@ -157,6 +209,10 @@ void render_frame(struct swaybar *bar, cairo_surface_t *recorder = cairo_recording_surface_create( CAIRO_CONTENT_COLOR_ALPHA, NULL); cairo_t *cairo = cairo_create(recorder); + cairo_save(cairo); + cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); + cairo_paint(cairo); + cairo_restore(cairo); uint32_t height = render_to_cairo(cairo, bar, output); if (bar->config->height >= 0 && height < (uint32_t)bar->config->height) { height = bar->config->height; diff --git a/swaybar/status_line.c b/swaybar/status_line.c new file mode 100644 index 00000000..ff668c9c --- /dev/null +++ b/swaybar/status_line.c @@ -0,0 +1,78 @@ +#define _POSIX_C_SOURCE +#include +#include +#include +#include +#include +#include +#include "swaybar/config.h" +#include "swaybar/status_line.h" +#include "readline.h" + +bool handle_status_readable(struct status_line *status) { + char *line = read_line_buffer(status->read, + status->buffer, status->buffer_size); + switch (status->protocol) { + case PROTOCOL_I3BAR: + // TODO + break; + case PROTOCOL_TEXT: + status->text = line; + return true; + case PROTOCOL_UNDEF: + if (!line) { + return false; + } + if (line[0] == '{') { + // TODO: JSON + } else { + status->text = line; + status->protocol = PROTOCOL_TEXT; + } + return false; + } + return false; +} + +struct status_line *status_line_init(char *cmd) { + struct status_line *status = calloc(1, sizeof(struct status_line)); + status->buffer_size = 4096; + status->buffer = malloc(status->buffer_size); + + int pipe_read_fd[2]; + int pipe_write_fd[2]; + if (pipe(pipe_read_fd) != 0 || pipe(pipe_write_fd) != 0) { + wlr_log(L_ERROR, "Unable to create pipes for status_command fork"); + exit(1); + } + + status->pid = fork(); + if (status->pid == 0) { + dup2(pipe_read_fd[1], STDOUT_FILENO); + close(pipe_read_fd[0]); + close(pipe_read_fd[1]); + + dup2(pipe_write_fd[0], STDIN_FILENO); + close(pipe_write_fd[0]); + close(pipe_write_fd[1]); + + char *const _cmd[] = { "sh", "-c", cmd, NULL, }; + execvp(_cmd[0], _cmd); + exit(1); + } + + close(pipe_read_fd[1]); + status->read_fd = pipe_read_fd[0]; + fcntl(status->read_fd, F_SETFL, O_NONBLOCK); + close(pipe_write_fd[0]); + status->write_fd = pipe_write_fd[1]; + fcntl(status->write_fd, F_SETFL, O_NONBLOCK); + + status->read = fdopen(status->read_fd, "r"); + status->write = fdopen(status->write_fd, "w"); + return status; +} + +void status_line_free(struct status_line *line) { + free(line); +} From 0464a9910da2b99abea0ab202e179d66260a893b Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 15:19:42 -0400 Subject: [PATCH 14/34] Clean up status line on exit --- swaybar/bar.c | 5 +++++ swaybar/status_line.c | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/swaybar/bar.c b/swaybar/bar.c index 72c4be8f..44f4ee31 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -191,4 +191,9 @@ void bar_teardown(struct swaybar *bar) { if (bar->config) { free_config(bar->config); } + close(bar->ipc_event_socketfd); + close(bar->ipc_socketfd); + if (bar->status) { + status_line_free(bar->status); + } } diff --git a/swaybar/status_line.c b/swaybar/status_line.c index ff668c9c..5b131aee 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c @@ -73,6 +73,8 @@ struct status_line *status_line_init(char *cmd) { return status; } -void status_line_free(struct status_line *line) { - free(line); +void status_line_free(struct status_line *status) { + close(status->read_fd); + close(status->write_fd); + free(status); } From da6e48520bad9718a7c4ddf0591474d54736c1c2 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 15:36:52 -0400 Subject: [PATCH 15/34] Tear down bar when display exits --- swaybar/bar.c | 3 ++- swaybar/render.c | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/swaybar/bar.c b/swaybar/bar.c index 44f4ee31..afbce7cc 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -148,7 +148,8 @@ static void render_all_frames(struct swaybar *bar) { static void display_in(int fd, short mask, void *_bar) { struct swaybar *bar = (struct swaybar *)_bar; if (wl_display_dispatch(bar->display) == -1) { - wlr_log(L_ERROR, "failed to dispatch wl: %d", errno); + bar_teardown(bar); + exit(0); } } diff --git a/swaybar/render.c b/swaybar/render.c index ec1239a1..a5834f4b 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -22,7 +22,6 @@ static uint32_t render_status_line_text(cairo_t *cairo, if (!status->text) { return 0; } - //wlr_log(L_DEBUG, "focused %d", focused); cairo_set_source_u32(cairo, focused ? config->colors.focused_statusline : config->colors.statusline); static const int margin = 3; From b72825441b61f56478ef29372a41a6fa72e4c79d Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 16:02:59 -0400 Subject: [PATCH 16/34] Fixed laggy focused output boolean --- swaybar/ipc.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/swaybar/ipc.c b/swaybar/ipc.c index a260b798..a82904bd 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -216,9 +216,11 @@ static void free_workspaces(struct wl_list *list) { } void ipc_get_workspaces(struct swaybar *bar) { + bar->focused_output = NULL; struct swaybar_output *output; wl_list_for_each(output, &bar->outputs, link) { free_workspaces(&output->workspaces); + output->focused = false; } uint32_t len = 0; char *res = ipc_single_command(bar->ipc_socketfd, @@ -251,10 +253,6 @@ void ipc_get_workspaces(struct swaybar *bar) { ws->visible = json_object_get_boolean(visible); ws->focused = json_object_get_boolean(focused); if (ws->focused) { - if (bar->focused_output) { - bar->focused_output->focused = false; - } - bar->focused_output = output; output->focused = true; } ws->urgent = json_object_get_boolean(urgent); @@ -262,6 +260,7 @@ void ipc_get_workspaces(struct swaybar *bar) { } } } + json_object_put(results); free(res); } From 6836074fed83255438960fdc9597532d8bcae4bd Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 16:51:36 -0400 Subject: [PATCH 17/34] Implement enough IPC for swaybar to work --- include/sway/config.h | 11 --- include/sway/ipc-json.h | 1 + include/sway/ipc-server.h | 1 + sway/config.c | 6 -- sway/desktop/layer_shell.c | 10 +-- sway/input/seat.c | 26 +++++-- sway/ipc-json.c | 134 +++++++++++++++++++++++++++++++++++++ sway/ipc-server.c | 93 ++++++++++++++++++++++++- 8 files changed, 251 insertions(+), 31 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 8c9e04de..f9ab6778 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -158,17 +158,6 @@ struct bar_config { char *swaybar_command; char *font; int height; // -1 not defined - -#ifdef ENABLE_TRAY - // Tray - char *tray_output; - char *icon_theme; - uint32_t tray_padding; - uint32_t activate_button; - uint32_t context_button; - uint32_t secondary_button; -#endif - bool workspace_buttons; bool wrap_scroll; char *separator_symbol; diff --git a/include/sway/ipc-json.h b/include/sway/ipc-json.h index eef5a018..19c5b5bc 100644 --- a/include/sway/ipc-json.h +++ b/include/sway/ipc-json.h @@ -9,5 +9,6 @@ json_object *ipc_json_get_version(); json_object *ipc_json_describe_container(swayc_t *c); json_object *ipc_json_describe_container_recursive(swayc_t *c); json_object *ipc_json_describe_input(struct sway_input_device *device); +json_object *ipc_json_describe_bar_config(struct bar_config *bar); #endif diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h index bcf1c433..6b7404e5 100644 --- a/include/sway/ipc-server.h +++ b/include/sway/ipc-server.h @@ -10,6 +10,7 @@ void ipc_init(struct sway_server *server); void ipc_terminate(void); struct sockaddr_un *ipc_user_sockaddr(void); +void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change); void ipc_event_window(swayc_t *window, const char *change); #endif diff --git a/sway/config.c b/sway/config.c index 213e7680..0422fdd9 100644 --- a/sway/config.c +++ b/sway/config.c @@ -403,12 +403,6 @@ bool load_main_config(const char *file, bool is_active) { free_config(old_config); } config->reading = false; - - if (success) { - // TODO: bar - //update_active_bar_modifiers(); - } - return success; } diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 31679fb2..187c8664 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -156,7 +156,6 @@ void arrange_layers(struct sway_output *output) { struct wlr_box usable_area = { 0 }; wlr_output_effective_resolution(output->wlr_output, &usable_area.width, &usable_area.height); - struct wlr_box usable_area_before = output->usable_area; // Arrange exclusive surfaces from top->bottom arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], @@ -169,11 +168,7 @@ void arrange_layers(struct sway_output *output) { &usable_area, true); memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); - if (memcmp(&usable_area_before, - &usable_area, sizeof(struct wlr_box)) != 0) { - wlr_log(L_DEBUG, "arrange"); - arrange_windows(output->swayc, -1, -1); - } + arrange_windows(output->swayc, -1, -1); // Arrange non-exlusive surfaces from top->bottom usable_area.x = usable_area.y = 0; @@ -221,6 +216,7 @@ static void unmap(struct wlr_layer_surface *layer_surface) { static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_layer_surface *sway_layer = wl_container_of( listener, sway_layer, destroy); + wlr_log(L_DEBUG, "layer surface removed"); if (sway_layer->layer_surface->mapped) { unmap(sway_layer->layer_surface); } @@ -233,8 +229,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&sway_layer->output_destroy.link); } struct sway_output *output = sway_layer->layer_surface->output->data; - arrange_layers(output); free(sway_layer); + arrange_layers(output); } static void handle_map(struct wl_listener *listener, void *data) { diff --git a/sway/input/seat.c b/sway/input/seat.c index 648e7914..81bef7bd 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -6,6 +6,7 @@ #include "sway/input/cursor.h" #include "sway/input/input-manager.h" #include "sway/input/keyboard.h" +#include "sway/ipc-server.h" #include "sway/output.h" #include "sway/view.h" #include "log.h" @@ -309,18 +310,31 @@ void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container) { if (container->type == C_VIEW) { struct sway_view *view = container->sway_view; view_set_activated(view, true); - struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + struct wlr_keyboard *keyboard = + wlr_seat_get_keyboard(seat->wlr_seat); if (keyboard) { - wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface, - keyboard->keycodes, keyboard->num_keycodes, - &keyboard->modifiers); + wlr_seat_keyboard_notify_enter(seat->wlr_seat, + view->surface, keyboard->keycodes, + keyboard->num_keycodes, &keyboard->modifiers); } else { - wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface, - NULL, 0, NULL); + wlr_seat_keyboard_notify_enter( + seat->wlr_seat, view->surface, NULL, 0, NULL); } } } + if (last_focus) { + swayc_t *last_ws = last_focus; + if (last_ws && last_ws->type != C_WORKSPACE) { + last_ws = swayc_parent_by_type( + last_focus, C_WORKSPACE); + } + if (last_ws) { + wlr_log(L_DEBUG, "sending workspace event"); + ipc_event_workspace(last_ws, container, "focus"); + } + } + if (last_focus && last_focus->type == C_VIEW && !sway_input_manager_has_focus(seat->input, last_focus)) { struct sway_view *view = last_focus->sway_view; diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 977f1ecb..24e41581 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -9,6 +9,7 @@ #include "sway/input/seat.h" #include #include +#include "wlr-layer-shell-unstable-v1-protocol.h" json_object *ipc_json_get_version() { int major = 0, minor = 0, patch = 0; @@ -198,3 +199,136 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { return object; } + +json_object *ipc_json_describe_bar_config(struct bar_config *bar) { + if (!sway_assert(bar, "Bar must not be NULL")) { + return NULL; + } + + json_object *json = json_object_new_object(); + json_object_object_add(json, "id", json_object_new_string(bar->id)); + json_object_object_add(json, "mode", json_object_new_string(bar->mode)); + json_object_object_add(json, "hidden_state", + json_object_new_string(bar->hidden_state)); + json_object_object_add(json, "position", + json_object_new_string(bar->position)); + json_object_object_add(json, "status_command", + json_object_new_string(bar->status_command)); + json_object_object_add(json, "font", + json_object_new_string((bar->font) ? bar->font : config->font)); + if (bar->separator_symbol) { + json_object_object_add(json, "separator_symbol", + json_object_new_string(bar->separator_symbol)); + } + json_object_object_add(json, "bar_height", + json_object_new_int(bar->height)); + json_object_object_add(json, "wrap_scroll", + json_object_new_boolean(bar->wrap_scroll)); + json_object_object_add(json, "workspace_buttons", + json_object_new_boolean(bar->workspace_buttons)); + json_object_object_add(json, "strip_workspace_numbers", + json_object_new_boolean(bar->strip_workspace_numbers)); + json_object_object_add(json, "binding_mode_indicator", + json_object_new_boolean(bar->binding_mode_indicator)); + json_object_object_add(json, "verbose", + json_object_new_boolean(bar->verbose)); + json_object_object_add(json, "pango_markup", + json_object_new_boolean(bar->pango_markup)); + + json_object *colors = json_object_new_object(); + json_object_object_add(colors, "background", + json_object_new_string(bar->colors.background)); + json_object_object_add(colors, "statusline", + json_object_new_string(bar->colors.statusline)); + json_object_object_add(colors, "separator", + json_object_new_string(bar->colors.separator)); + + if (bar->colors.focused_background) { + json_object_object_add(colors, "focused_background", + json_object_new_string(bar->colors.focused_background)); + } else { + json_object_object_add(colors, "focused_background", + json_object_new_string(bar->colors.background)); + } + + if (bar->colors.focused_statusline) { + json_object_object_add(colors, "focused_statusline", + json_object_new_string(bar->colors.focused_statusline)); + } else { + json_object_object_add(colors, "focused_statusline", + json_object_new_string(bar->colors.statusline)); + } + + if (bar->colors.focused_separator) { + json_object_object_add(colors, "focused_separator", + json_object_new_string(bar->colors.focused_separator)); + } else { + json_object_object_add(colors, "focused_separator", + json_object_new_string(bar->colors.separator)); + } + + json_object_object_add(colors, "focused_workspace_border", + json_object_new_string(bar->colors.focused_workspace_border)); + json_object_object_add(colors, "focused_workspace_bg", + json_object_new_string(bar->colors.focused_workspace_bg)); + json_object_object_add(colors, "focused_workspace_text", + json_object_new_string(bar->colors.focused_workspace_text)); + + json_object_object_add(colors, "inactive_workspace_border", + json_object_new_string(bar->colors.inactive_workspace_border)); + json_object_object_add(colors, "inactive_workspace_bg", + json_object_new_string(bar->colors.inactive_workspace_bg)); + json_object_object_add(colors, "inactive_workspace_text", + json_object_new_string(bar->colors.inactive_workspace_text)); + + json_object_object_add(colors, "active_workspace_border", + json_object_new_string(bar->colors.active_workspace_border)); + json_object_object_add(colors, "active_workspace_bg", + json_object_new_string(bar->colors.active_workspace_bg)); + json_object_object_add(colors, "active_workspace_text", + json_object_new_string(bar->colors.active_workspace_text)); + + json_object_object_add(colors, "urgent_workspace_border", + json_object_new_string(bar->colors.urgent_workspace_border)); + json_object_object_add(colors, "urgent_workspace_bg", + json_object_new_string(bar->colors.urgent_workspace_bg)); + json_object_object_add(colors, "urgent_workspace_text", + json_object_new_string(bar->colors.urgent_workspace_text)); + + if (bar->colors.binding_mode_border) { + json_object_object_add(colors, "binding_mode_border", + json_object_new_string(bar->colors.binding_mode_border)); + } else { + json_object_object_add(colors, "binding_mode_border", + json_object_new_string(bar->colors.urgent_workspace_border)); + } + + if (bar->colors.binding_mode_bg) { + json_object_object_add(colors, "binding_mode_bg", + json_object_new_string(bar->colors.binding_mode_bg)); + } else { + json_object_object_add(colors, "binding_mode_bg", + json_object_new_string(bar->colors.urgent_workspace_bg)); + } + + if (bar->colors.binding_mode_text) { + json_object_object_add(colors, "binding_mode_text", + json_object_new_string(bar->colors.binding_mode_text)); + } else { + json_object_object_add(colors, "binding_mode_text", + json_object_new_string(bar->colors.urgent_workspace_text)); + } + + json_object_object_add(json, "colors", colors); + + // Add outputs if defined + if (bar->outputs && bar->outputs->length > 0) { + json_object *outputs = json_object_new_array(); + for (int i = 0; i < bar->outputs->length; ++i) { + const char *name = bar->outputs->items[i]; + json_object_array_add(outputs, json_object_new_string(name)); + } + json_object_object_add(json, "outputs", outputs); + } + return json; +} diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 156de380..408ed432 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -21,6 +21,7 @@ #include "sway/ipc-server.h" #include "sway/server.h" #include "sway/input/input-manager.h" +#include "sway/input/seat.h" #include "list.h" #include "log.h" @@ -279,6 +280,31 @@ static void ipc_send_event(const char *json_string, enum ipc_command_type event) } } +void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change) { + wlr_log(L_DEBUG, "Sending workspace::%s event", change); + json_object *obj = json_object_new_object(); + json_object_object_add(obj, "change", json_object_new_string(change)); + if (strcmp("focus", change) == 0) { + if (old) { + json_object_object_add(obj, "old", + ipc_json_describe_container_recursive(old)); + } else { + json_object_object_add(obj, "old", NULL); + } + } + + if (new) { + json_object_object_add(obj, "current", + ipc_json_describe_container_recursive(new)); + } else { + json_object_object_add(obj, "current", NULL); + } + + const char *json_string = json_object_to_json_string(obj); + ipc_send_event(json_string, IPC_EVENT_WORKSPACE); + json_object_put(obj); +} + void ipc_event_window(swayc_t *window, const char *change) { wlr_log(L_DEBUG, "Sending window::%s event", change); json_object *obj = json_object_new_object(); @@ -357,6 +383,25 @@ void ipc_client_disconnect(struct ipc_client *client) { free(client); } +static void ipc_get_workspaces_callback(swayc_t *workspace, void *data) { + if (workspace->type == C_WORKSPACE) { + json_object *workspace_json = ipc_json_describe_container(workspace); + // override the default focused indicator because + // it's set differently for the get_workspaces reply + struct sway_seat *seat = + sway_input_manager_get_default_seat(input_manager); + swayc_t *focused_ws = sway_seat_get_focus(seat); + if (focused_ws->type != C_WORKSPACE) { + focused_ws = swayc_parent_by_type(focused_ws, C_WORKSPACE); + } + bool focused = workspace == focused_ws; + json_object_object_del(workspace_json, "focused"); + json_object_object_add(workspace_json, "focused", + json_object_new_boolean(focused)); + json_object_array_add((json_object *)data, workspace_json); + } +} + void ipc_client_handle_command(struct ipc_client *client) { if (!sway_assert(client != NULL, "client != NULL")) { return; @@ -412,6 +457,16 @@ void ipc_client_handle_command(struct ipc_client *client) { goto exit_cleanup; } + case IPC_GET_WORKSPACES: + { + json_object *workspaces = json_object_new_array(); + container_map(&root_container, ipc_get_workspaces_callback, workspaces); + const char *json_string = json_object_to_json_string(workspaces); + ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); + json_object_put(workspaces); // free + goto exit_cleanup; + } + case IPC_SUBSCRIBE: { // TODO: Check if they're permitted to use these events @@ -446,7 +501,6 @@ void ipc_client_handle_command(struct ipc_client *client) { } json_object_put(request); - ipc_send_reply(client, "{\"success\": true}", 17); goto exit_cleanup; } @@ -483,6 +537,43 @@ void ipc_client_handle_command(struct ipc_client *client) { goto exit_cleanup; } + case IPC_GET_BAR_CONFIG: + { + if (!buf[0]) { + // Send list of configured bar IDs + json_object *bars = json_object_new_array(); + int i; + for (i = 0; i < config->bars->length; ++i) { + struct bar_config *bar = config->bars->items[i]; + json_object_array_add(bars, json_object_new_string(bar->id)); + } + const char *json_string = json_object_to_json_string(bars); + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + json_object_put(bars); // free + } else { + // Send particular bar's details + struct bar_config *bar = NULL; + int i; + for (i = 0; i < config->bars->length; ++i) { + bar = config->bars->items[i]; + if (strcmp(buf, bar->id) == 0) { + break; + } + bar = NULL; + } + if (!bar) { + const char *error = "{ \"success\": false, \"error\": \"No bar with that ID\" }"; + ipc_send_reply(client, error, (uint32_t)strlen(error)); + goto exit_cleanup; + } + json_object *json = ipc_json_describe_bar_config(bar); + const char *json_string = json_object_to_json_string(json); + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + json_object_put(json); // free + } + goto exit_cleanup; + } + default: wlr_log(L_INFO, "Unknown IPC command type %i", client->current_command); goto exit_cleanup; From bf7a4cd0ebd465a0597e9eec0142fad222b396de Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 17:20:03 -0400 Subject: [PATCH 18/34] Add bar configuration commands --- include/sway/commands.h | 3 + include/sway/ipc-server.h | 1 + sway/commands.c | 135 +++++++++++++------- sway/commands/bar.c | 57 +++++++++ sway/commands/bar/activate_button.c | 9 ++ sway/commands/bar/binding_mode_indicator.c | 27 ++++ sway/commands/bar/bindsym.c | 11 ++ sway/commands/bar/colors.c | 129 +++++++++++++++++++ sway/commands/bar/context_button.c | 9 ++ sway/commands/bar/font.c | 26 ++++ sway/commands/bar/height.c | 21 +++ sway/commands/bar/hidden_state.c | 78 +++++++++++ sway/commands/bar/icon_theme.c | 9 ++ sway/commands/bar/id.c | 33 +++++ sway/commands/bar/mode.c | 78 +++++++++++ sway/commands/bar/modifier.c | 35 +++++ sway/commands/bar/output.c | 50 ++++++++ sway/commands/bar/pango_markup.c | 27 ++++ sway/commands/bar/position.c | 29 +++++ sway/commands/bar/secondary_button.c | 9 ++ sway/commands/bar/separator_symbol.c | 21 +++ sway/commands/bar/status_command.c | 21 +++ sway/commands/bar/strip_workspace_numbers.c | 27 ++++ sway/commands/bar/swaybar_command.c | 21 +++ sway/commands/bar/tray_output.c | 9 ++ sway/commands/bar/tray_padding.c | 10 ++ sway/commands/bar/workspace_buttons.c | 27 ++++ sway/commands/bar/wrap_scroll.c | 27 ++++ sway/config.c | 127 ++++++++++++++++++ sway/ipc-server.c | 10 +- sway/meson.build | 25 ++++ 31 files changed, 1051 insertions(+), 50 deletions(-) create mode 100644 sway/commands/bar.c create mode 100644 sway/commands/bar/activate_button.c create mode 100644 sway/commands/bar/binding_mode_indicator.c create mode 100644 sway/commands/bar/bindsym.c create mode 100644 sway/commands/bar/colors.c create mode 100644 sway/commands/bar/context_button.c create mode 100644 sway/commands/bar/font.c create mode 100644 sway/commands/bar/height.c create mode 100644 sway/commands/bar/hidden_state.c create mode 100644 sway/commands/bar/icon_theme.c create mode 100644 sway/commands/bar/id.c create mode 100644 sway/commands/bar/mode.c create mode 100644 sway/commands/bar/modifier.c create mode 100644 sway/commands/bar/output.c create mode 100644 sway/commands/bar/pango_markup.c create mode 100644 sway/commands/bar/position.c create mode 100644 sway/commands/bar/secondary_button.c create mode 100644 sway/commands/bar/separator_symbol.c create mode 100644 sway/commands/bar/status_command.c create mode 100644 sway/commands/bar/strip_workspace_numbers.c create mode 100644 sway/commands/bar/swaybar_command.c create mode 100644 sway/commands/bar/tray_output.c create mode 100644 sway/commands/bar/tray_padding.c create mode 100644 sway/commands/bar/workspace_buttons.c create mode 100644 sway/commands/bar/wrap_scroll.c diff --git a/include/sway/commands.h b/include/sway/commands.h index 9ff18823..dda286a2 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -74,6 +74,9 @@ void free_cmd_results(struct cmd_results *results); */ const char *cmd_results_to_json(struct cmd_results *results); +struct cmd_results *add_color(const char *name, + char *buffer, const char *color); + typedef struct cmd_results *sway_cmd(int argc, char **argv); sway_cmd cmd_assign; diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h index 6b7404e5..1f6fffff 100644 --- a/include/sway/ipc-server.h +++ b/include/sway/ipc-server.h @@ -12,5 +12,6 @@ struct sockaddr_un *ipc_user_sockaddr(void); void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change); void ipc_event_window(swayc_t *window, const char *change); +void ipc_event_barconfig_update(struct bar_config *bar); #endif diff --git a/sway/commands.c b/sway/commands.c index b52eb200..8d8b643b 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -91,45 +91,9 @@ void apply_seat_config(struct seat_config *seat) { sway_input_manager_apply_seat_config(input_manager, seat); } -/** - * Check and add color to buffer. - * - * return error object, or NULL if color is valid. - */ -struct cmd_results *add_color(const char *name, char *buffer, const char *color) { - int len = strlen(color); - if (len != 7 && len != 9) { - return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color); - } - - if (color[0] != '#') { - return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color); - } - - int i; - for (i = 1; i < len; ++i) { - if (!isxdigit(color[i])) { - return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color); - } - } - - // copy color to buffer - strncpy(buffer, color, len); - // add default alpha channel if color was defined without it - if (len == 7) { - buffer[7] = 'f'; - buffer[8] = 'f'; - } - buffer[9] = '\0'; - - return NULL; -} - -/** - * handlers that can run in either config or command context - * Keep alphabetized - */ +/* Keep alphabetized */ static struct cmd_handler handlers[] = { + { "bar", cmd_bar }, { "bindcode", cmd_bindcode }, { "bindsym", cmd_bindsym }, { "exec", cmd_exec }, @@ -141,18 +105,53 @@ static struct cmd_handler handlers[] = { { "workspace", cmd_workspace }, }; -/** - * Commands that can *only* run in the config loading context - * Keep alphabetized - */ +static struct cmd_handler bar_handlers[] = { + { "activate_button", bar_cmd_activate_button }, + { "binding_mode_indicator", bar_cmd_binding_mode_indicator }, + { "bindsym", bar_cmd_bindsym }, + { "colors", bar_cmd_colors }, + { "context_button", bar_cmd_context_button }, + { "font", bar_cmd_font }, + { "height", bar_cmd_height }, + { "hidden_state", bar_cmd_hidden_state }, + { "icon_theme", bar_cmd_icon_theme }, + { "id", bar_cmd_id }, + { "mode", bar_cmd_mode }, + { "modifier", bar_cmd_modifier }, + { "output", bar_cmd_output }, + { "pango_markup", bar_cmd_pango_markup }, + { "position", bar_cmd_position }, + { "secondary_button", bar_cmd_secondary_button }, + { "separator_symbol", bar_cmd_separator_symbol }, + { "status_command", bar_cmd_status_command }, + { "strip_workspace_numbers", bar_cmd_strip_workspace_numbers }, + { "swaybar_command", bar_cmd_swaybar_command }, + { "tray_output", bar_cmd_tray_output }, + { "tray_padding", bar_cmd_tray_padding }, + { "workspace_buttons", bar_cmd_workspace_buttons }, + { "wrap_scroll", bar_cmd_wrap_scroll }, +}; + +static struct cmd_handler bar_colors_handlers[] = { + { "active_workspace", bar_colors_cmd_active_workspace }, + { "background", bar_colors_cmd_background }, + { "binding_mode", bar_colors_cmd_binding_mode }, + { "focused_background", bar_colors_cmd_focused_background }, + { "focused_separator", bar_colors_cmd_focused_separator }, + { "focused_statusline", bar_colors_cmd_focused_statusline }, + { "focused_workspace", bar_colors_cmd_focused_workspace }, + { "inactive_workspace", bar_colors_cmd_inactive_workspace }, + { "separator", bar_colors_cmd_separator }, + { "statusline", bar_colors_cmd_statusline }, + { "urgent_workspace", bar_colors_cmd_urgent_workspace }, +}; + +/* Config-time only commands. Keep alphabetized */ static struct cmd_handler config_handlers[] = { { "set", cmd_set }, }; -/** - * Commands that can *not* run in the config loading context - * Keep alphabetized - */ +/* Runtime-only commands. Keep alphabetized */ static struct cmd_handler command_handlers[] = { { "exit", cmd_exit }, { "focus", cmd_focus }, @@ -200,13 +199,19 @@ static struct cmd_handler *find_handler(char *line, enum cmd_status block) { bool config_loading = config->reading || !config->active; - if (block == CMD_BLOCK_INPUT) { - // input commands can run in either context + if (block == CMD_BLOCK_BAR) { + return bsearch(&d, bar_handlers, + sizeof(bar_handlers) / sizeof(struct cmd_handler), + sizeof(struct cmd_handler), handler_compare); + } else if (block == CMD_BLOCK_BAR_COLORS) { + return bsearch(&d, bar_colors_handlers, + sizeof(bar_colors_handlers) / sizeof(struct cmd_handler), + sizeof(struct cmd_handler), handler_compare); + } else if (block == CMD_BLOCK_INPUT) { return bsearch(&d, input_handlers, sizeof(input_handlers) / sizeof(struct cmd_handler), sizeof(struct cmd_handler), handler_compare); } else if (block == CMD_BLOCK_SEAT) { - // seat commands can run in either context return bsearch(&d, seat_handlers, sizeof(seat_handlers) / sizeof(struct cmd_handler), sizeof(struct cmd_handler), handler_compare); @@ -558,3 +563,35 @@ const char *cmd_results_to_json(struct cmd_results *results) { free(root); return json; } + +/** + * Check and add color to buffer. + * + * return error object, or NULL if color is valid. + */ +struct cmd_results *add_color(const char *name, + char *buffer, const char *color) { + int len = strlen(color); + if (len != 7 && len != 9) { + return cmd_results_new(CMD_INVALID, name, + "Invalid color definition %s", color); + } + if (color[0] != '#') { + return cmd_results_new(CMD_INVALID, name, + "Invalid color definition %s", color); + } + for (int i = 1; i < len; ++i) { + if (!isxdigit(color[i])) { + return cmd_results_new(CMD_INVALID, name, + "Invalid color definition %s", color); + } + } + strncpy(buffer, color, len); + // add default alpha channel if color was defined without it + if (len == 7) { + buffer[7] = 'f'; + buffer[8] = 'f'; + } + buffer[9] = '\0'; + return NULL; +} diff --git a/sway/commands/bar.c b/sway/commands/bar.c new file mode 100644 index 00000000..548106b3 --- /dev/null +++ b/sway/commands/bar.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include "sway/commands.h" +#include "sway/config.h" +#include "util.h" + +struct cmd_results *cmd_bar(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "bar", EXPECTED_AT_LEAST, 1))) { + return error; + } + + if (config->reading && strcmp("{", argv[0]) != 0) { + return cmd_results_new(CMD_INVALID, "bar", + "Expected '{' at start of bar config definition."); + } + + if (!config->reading) { + if (argc > 1) { + if (strcasecmp("mode", argv[0]) == 0) { + return bar_cmd_mode(argc-1, argv + 1); + } + + if (strcasecmp("hidden_state", argv[0]) == 0) { + return bar_cmd_hidden_state(argc-1, argv + 1); + } + } + return cmd_results_new(CMD_FAILURE, "bar", "Can only be used in config file."); + } + + // Create new bar with default values + struct bar_config *bar = default_bar_config(); + if (!bar) { + return cmd_results_new(CMD_FAILURE, "bar", "Unable to allocate bar state"); + } + + // set bar id + int i; + for (i = 0; i < config->bars->length; ++i) { + if (bar == config->bars->items[i]) { + const int len = 5 + numlen(i); // "bar-" + i + \0 + bar->id = malloc(len * sizeof(char)); + if (bar->id) { + snprintf(bar->id, len, "bar-%d", i); + } else { + return cmd_results_new(CMD_FAILURE, "bar", "Unable to allocate bar ID"); + } + break; + } + } + + // Set current bar + config->current_bar = bar; + wlr_log(L_DEBUG, "Configuring bar %s", bar->id); + return cmd_results_new(CMD_BLOCK_BAR, NULL, NULL); +} diff --git a/sway/commands/bar/activate_button.c b/sway/commands/bar/activate_button.c new file mode 100644 index 00000000..0665d2a6 --- /dev/null +++ b/sway/commands/bar/activate_button.c @@ -0,0 +1,9 @@ +#include +#include "sway/commands.h" +#include "log.h" + +struct cmd_results *bar_cmd_activate_button(int argc, char **argv) { + const char *cmd_name = "activate_button"; + // TODO TRAY + return cmd_results_new(CMD_INVALID, cmd_name, "TODO TRAY"); +} diff --git a/sway/commands/bar/binding_mode_indicator.c b/sway/commands/bar/binding_mode_indicator.c new file mode 100644 index 00000000..e11e1033 --- /dev/null +++ b/sway/commands/bar/binding_mode_indicator.c @@ -0,0 +1,27 @@ +#include +#include +#include "sway/commands.h" +#include "log.h" + +struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "binding_mode_indicator", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, "binding_mode_indicator", "No bar defined."); + } + + if (strcasecmp("yes", argv[0]) == 0) { + config->current_bar->binding_mode_indicator = true; + wlr_log(L_DEBUG, "Enabling binding mode indicator on bar: %s", config->current_bar->id); + } else if (strcasecmp("no", argv[0]) == 0) { + config->current_bar->binding_mode_indicator = false; + wlr_log(L_DEBUG, "Disabling binding mode indicator on bar: %s", config->current_bar->id); + } else { + error = cmd_results_new(CMD_INVALID, "binding_mode_indicator", "Invalid value %s", argv[0]); + return error; + } + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/bar/bindsym.c b/sway/commands/bar/bindsym.c new file mode 100644 index 00000000..ac09a03f --- /dev/null +++ b/sway/commands/bar/bindsym.c @@ -0,0 +1,11 @@ +#include +#include +#include "sway/commands.h" +#include "sway/config.h" +#include "list.h" +#include "log.h" +#include "stringop.h" + +struct cmd_results *bar_cmd_bindsym(int argc, char **argv) { + return cmd_results_new(CMD_FAILURE, "bindsym", "TODO"); // TODO +} diff --git a/sway/commands/bar/colors.c b/sway/commands/bar/colors.c new file mode 100644 index 00000000..8b3b0aac --- /dev/null +++ b/sway/commands/bar/colors.c @@ -0,0 +1,129 @@ +#include +#include "sway/commands.h" + +static struct cmd_results *parse_single_color(char **color, const char *cmd_name, int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (!*color) { + *color = malloc(10); + if (!*color) { + return cmd_results_new(CMD_FAILURE, cmd_name, "Unable to allocate color"); + } + } + + error = add_color(cmd_name, *color, argv[0]); + if (error) { + return error; + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + +static struct cmd_results *parse_three_colors(char ***colors, const char *cmd_name, int argc, char **argv) { + struct cmd_results *error = NULL; + if (argc != 3) { + return cmd_results_new(CMD_INVALID, cmd_name, "Requires exactly three color values"); + } + + int i; + for (i = 0; i < 3; i++) { + if (!*colors[i]) { + *(colors[i]) = malloc(10); + if (!*(colors[i])) { + return cmd_results_new(CMD_FAILURE, cmd_name, "Unable to allocate color"); + } + } + error = add_color(cmd_name, *(colors[i]), argv[i]); + if (error) { + return error; + } + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + +struct cmd_results *bar_cmd_colors(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "colors", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (strcmp("{", argv[0]) != 0) { + return cmd_results_new(CMD_INVALID, "colors", + "Expected '{' at the start of colors config definition."); + } + + return cmd_results_new(CMD_BLOCK_BAR_COLORS, NULL, NULL); +} + +struct cmd_results *bar_colors_cmd_active_workspace(int argc, char **argv) { + char **colors[3] = { + &(config->current_bar->colors.active_workspace_border), + &(config->current_bar->colors.active_workspace_bg), + &(config->current_bar->colors.active_workspace_text) + }; + return parse_three_colors(colors, "active_workspace", argc, argv); +} + +struct cmd_results *bar_colors_cmd_background(int argc, char **argv) { + return parse_single_color(&(config->current_bar->colors.background), "background", argc, argv); +} + +struct cmd_results *bar_colors_cmd_focused_background(int argc, char **argv) { + return parse_single_color(&(config->current_bar->colors.focused_background), "focused_background", argc, argv); +} + +struct cmd_results *bar_colors_cmd_binding_mode(int argc, char **argv) { + char **colors[3] = { + &(config->current_bar->colors.binding_mode_border), + &(config->current_bar->colors.binding_mode_bg), + &(config->current_bar->colors.binding_mode_text) + }; + return parse_three_colors(colors, "binding_mode", argc, argv); +} + +struct cmd_results *bar_colors_cmd_focused_workspace(int argc, char **argv) { + char **colors[3] = { + &(config->current_bar->colors.focused_workspace_border), + &(config->current_bar->colors.focused_workspace_bg), + &(config->current_bar->colors.focused_workspace_text) + }; + return parse_three_colors(colors, "focused_workspace", argc, argv); +} + +struct cmd_results *bar_colors_cmd_inactive_workspace(int argc, char **argv) { + char **colors[3] = { + &(config->current_bar->colors.inactive_workspace_border), + &(config->current_bar->colors.inactive_workspace_bg), + &(config->current_bar->colors.inactive_workspace_text) + }; + return parse_three_colors(colors, "inactive_workspace", argc, argv); +} + +struct cmd_results *bar_colors_cmd_separator(int argc, char **argv) { + return parse_single_color(&(config->current_bar->colors.separator), "separator", argc, argv); +} + +struct cmd_results *bar_colors_cmd_focused_separator(int argc, char **argv) { + return parse_single_color(&(config->current_bar->colors.focused_separator), "focused_separator", argc, argv); +} + +struct cmd_results *bar_colors_cmd_statusline(int argc, char **argv) { + return parse_single_color(&(config->current_bar->colors.statusline), "statusline", argc, argv); +} + +struct cmd_results *bar_colors_cmd_focused_statusline(int argc, char **argv) { + return parse_single_color(&(config->current_bar->colors.focused_separator), "focused_separator", argc, argv); +} + +struct cmd_results *bar_colors_cmd_urgent_workspace(int argc, char **argv) { + char **colors[3] = { + &(config->current_bar->colors.urgent_workspace_border), + &(config->current_bar->colors.urgent_workspace_bg), + &(config->current_bar->colors.urgent_workspace_text) + }; + return parse_three_colors(colors, "urgent_workspace", argc, argv); +} diff --git a/sway/commands/bar/context_button.c b/sway/commands/bar/context_button.c new file mode 100644 index 00000000..e6d17f10 --- /dev/null +++ b/sway/commands/bar/context_button.c @@ -0,0 +1,9 @@ +#include +#include "sway/commands.h" +#include "log.h" + +struct cmd_results *bar_cmd_context_button(int argc, char **argv) { + const char *cmd_name = "context_button"; + // TODO TRAY + return cmd_results_new(CMD_INVALID, cmd_name, "TODO TRAY"); +} diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c new file mode 100644 index 00000000..6d7c533a --- /dev/null +++ b/sway/commands/bar/font.c @@ -0,0 +1,26 @@ +#include +#include "sway/commands.h" +#include "log.h" +#include "stringop.h" + +struct cmd_results *bar_cmd_font(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "font", EXPECTED_AT_LEAST, 1))) { + return error; + } + + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, "font", "No bar defined."); + } + + char *font = join_args(argv, argc); + free(config->current_bar->font); + if (strlen(font) > 6 && strncmp("pango:", font, 6) == 0) { + config->current_bar->font = font; + } else { + config->current_bar->font = font; + } + + wlr_log(L_DEBUG, "Settings font '%s' for bar: %s", config->current_bar->font, config->current_bar->id); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/bar/height.c b/sway/commands/bar/height.c new file mode 100644 index 00000000..ae0c7834 --- /dev/null +++ b/sway/commands/bar/height.c @@ -0,0 +1,21 @@ +#include +#include +#include "sway/commands.h" +#include "log.h" + +struct cmd_results *bar_cmd_height(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "height", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + int height = atoi(argv[0]); + if (height < 0) { + return cmd_results_new(CMD_INVALID, "height", + "Invalid height value: %s", argv[0]); + } + + config->current_bar->height = height; + wlr_log(L_DEBUG, "Setting bar height to %d on bar: %s", height, config->current_bar->id); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c new file mode 100644 index 00000000..245d0858 --- /dev/null +++ b/sway/commands/bar/hidden_state.c @@ -0,0 +1,78 @@ +#define _XOPEN_SOURCE 500 +#include +#include +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/ipc-server.h" +#include "log.h" + +static struct cmd_results *bar_set_hidden_state(struct bar_config *bar, + const char *hidden_state) { + char *old_state = bar->hidden_state; + if (strcasecmp("toggle", hidden_state) == 0 && !config->reading) { + if (strcasecmp("hide", bar->hidden_state) == 0) { + bar->hidden_state = strdup("show"); + } else if (strcasecmp("show", bar->hidden_state) == 0) { + bar->hidden_state = strdup("hide"); + } + } else if (strcasecmp("hide", hidden_state) == 0) { + bar->hidden_state = strdup("hide"); + } else if (strcasecmp("show", hidden_state) == 0) { + bar->hidden_state = strdup("show"); + } else { + return cmd_results_new(CMD_INVALID, "hidden_state", + "Invalid value %s", hidden_state); + } + + if (strcmp(old_state, bar->hidden_state) != 0) { + if (!config->reading) { + ipc_event_barconfig_update(bar); + } + wlr_log(L_DEBUG, "Setting hidden_state: '%s' for bar: %s", + bar->hidden_state, bar->id); + } + + // free old mode + free(old_state); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + +struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "hidden_state", EXPECTED_AT_LEAST, 1))) { + return error; + } + if ((error = checkarg(argc, "hidden_state", EXPECTED_LESS_THAN, 3))) { + return error; + } + + if (config->reading && argc > 1) { + return cmd_results_new(CMD_INVALID, "hidden_state", + "Unexpected value %s in config mode", argv[1]); + } + + const char *state = argv[0]; + + if (config->reading) { + return bar_set_hidden_state(config->current_bar, state); + } + + const char *id = NULL; + if (argc == 2) { + id = argv[1]; + } + + struct bar_config *bar; + for (int i = 0; i < config->bars->length; ++i) { + bar = config->bars->items[i]; + if (id && strcmp(id, bar->id) == 0) { + return bar_set_hidden_state(bar, state); + } + + error = bar_set_hidden_state(bar, state); + if (error) { + return error; + } + } + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c new file mode 100644 index 00000000..c6090b81 --- /dev/null +++ b/sway/commands/bar/icon_theme.c @@ -0,0 +1,9 @@ +#define _XOPEN_SOURCE 500 +#include +#include "sway/commands.h" + +struct cmd_results *bar_cmd_icon_theme(int argc, char **argv) { + const char *cmd_name = "tray_output"; + // TODO TRAY + return cmd_results_new(CMD_INVALID, cmd_name, "TODO TRAY"); +} diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c new file mode 100644 index 00000000..d9e7f8df --- /dev/null +++ b/sway/commands/bar/id.c @@ -0,0 +1,33 @@ +#define _XOPEN_SOURCE 500 +#include +#include "sway/commands.h" +#include "log.h" + +struct cmd_results *bar_cmd_id(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "id", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + const char *name = argv[0]; + const char *oldname = config->current_bar->id; + + // check if id is used by a previously defined bar + int i; + for (i = 0; i < config->bars->length; ++i) { + struct bar_config *find = config->bars->items[i]; + if (strcmp(name, find->id) == 0 && config->current_bar != find) { + return cmd_results_new(CMD_FAILURE, "id", + "Id '%s' already defined for another bar. Id unchanged (%s).", + name, oldname); + } + } + + wlr_log(L_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name); + + // free old bar id + free(config->current_bar->id); + + config->current_bar->id = strdup(name); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c new file mode 100644 index 00000000..7d346956 --- /dev/null +++ b/sway/commands/bar/mode.c @@ -0,0 +1,78 @@ +#define _XOPEN_SOURCE 500 +#include +#include +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/ipc-server.h" +#include "log.h" + +static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode) { + char *old_mode = bar->mode; + if (strcasecmp("toggle", mode) == 0 && !config->reading) { + if (strcasecmp("dock", bar->mode) == 0) { + bar->mode = strdup("hide"); + } else if (strcasecmp("hide", bar->mode) == 0) { + bar->mode = strdup("dock"); + } + } else if (strcasecmp("dock", mode) == 0) { + bar->mode = strdup("dock"); + } else if (strcasecmp("hide", mode) == 0) { + bar->mode = strdup("hide"); + } else if (strcasecmp("invisible", mode) == 0) { + bar->mode = strdup("invisible"); + } else { + return cmd_results_new(CMD_INVALID, "mode", "Invalid value %s", mode); + } + + if (strcmp(old_mode, bar->mode) != 0) { + if (!config->reading) { + ipc_event_barconfig_update(bar); + } + wlr_log(L_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id); + } + + // free old mode + free(old_mode); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + +struct cmd_results *bar_cmd_mode(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "mode", EXPECTED_AT_LEAST, 1))) { + return error; + } + if ((error = checkarg(argc, "mode", EXPECTED_LESS_THAN, 3))) { + return error; + } + + if (config->reading && argc > 1) { + return cmd_results_new(CMD_INVALID, "mode", "Unexpected value %s in config mode", argv[1]); + } + + const char *mode = argv[0]; + + if (config->reading) { + return bar_set_mode(config->current_bar, mode); + } + + const char *id = NULL; + if (argc == 2) { + id = argv[1]; + } + + int i; + struct bar_config *bar; + for (i = 0; i < config->bars->length; ++i) { + bar = config->bars->items[i]; + if (id && strcmp(id, bar->id) == 0) { + return bar_set_mode(bar, mode); + } + + error = bar_set_mode(bar, mode); + if (error) { + return error; + } + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/bar/modifier.c b/sway/commands/bar/modifier.c new file mode 100644 index 00000000..9a1f8b01 --- /dev/null +++ b/sway/commands/bar/modifier.c @@ -0,0 +1,35 @@ +#include +#include "sway/commands.h" +#include "log.h" +#include "stringop.h" +#include "util.h" + +struct cmd_results *bar_cmd_modifier(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "modifier", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, "modifier", "No bar defined."); + } + + uint32_t mod = 0; + + list_t *split = split_string(argv[0], "+"); + for (int i = 0; i < split->length; ++i) { + uint32_t tmp_mod; + if ((tmp_mod = get_modifier_mask_by_name(split->items[i])) > 0) { + mod |= tmp_mod; + continue; + } else { + free_flat_list(split); + return cmd_results_new(CMD_INVALID, "modifier", "Unknown modifier '%s'", split->items[i]); + } + } + free_flat_list(split); + + config->current_bar->modifier = mod; + wlr_log(L_DEBUG, "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c new file mode 100644 index 00000000..034d9ca4 --- /dev/null +++ b/sway/commands/bar/output.c @@ -0,0 +1,50 @@ +#define _XOPEN_SOURCE 500 +#include +#include "sway/commands.h" +#include "list.h" +#include "log.h" + +struct cmd_results *bar_cmd_output(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "output", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, "output", "No bar defined."); + } + + const char *output = argv[0]; + list_t *outputs = config->current_bar->outputs; + if (!outputs) { + outputs = create_list(); + config->current_bar->outputs = outputs; + } + + int i; + int add_output = 1; + if (strcmp("*", output) == 0) { + // remove all previous defined outputs and replace with '*' + for (i = 0; i < outputs->length; ++i) { + free(outputs->items[i]); + list_del(outputs, i); + } + } else { + // only add output if not already defined with either the same + // name or as '*' + for (i = 0; i < outputs->length; ++i) { + const char *find = outputs->items[i]; + if (strcmp("*", find) == 0 || strcmp(output, find) == 0) { + add_output = 0; + break; + } + } + } + + if (add_output) { + list_add(outputs, strdup(output)); + wlr_log(L_DEBUG, "Adding bar: '%s' to output '%s'", config->current_bar->id, output); + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/bar/pango_markup.c b/sway/commands/bar/pango_markup.c new file mode 100644 index 00000000..34b85e98 --- /dev/null +++ b/sway/commands/bar/pango_markup.c @@ -0,0 +1,27 @@ +#include +#include +#include "sway/commands.h" +#include "log.h" + +struct cmd_results *bar_cmd_pango_markup(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "pango_markup", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, "pango_markup", "No bar defined."); + } + + if (strcasecmp("enabled", argv[0]) == 0) { + config->current_bar->pango_markup = true; + wlr_log(L_DEBUG, "Enabling pango markup for bar: %s", config->current_bar->id); + } else if (strcasecmp("disabled", argv[0]) == 0) { + config->current_bar->pango_markup = false; + wlr_log(L_DEBUG, "Disabling pango markup for bar: %s", config->current_bar->id); + } else { + error = cmd_results_new(CMD_INVALID, "pango_markup", "Invalid value %s", argv[0]); + return error; + } + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c new file mode 100644 index 00000000..efa8c0bc --- /dev/null +++ b/sway/commands/bar/position.c @@ -0,0 +1,29 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include "sway/commands.h" +#include "log.h" + +struct cmd_results *bar_cmd_position(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "position", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, "position", "No bar defined."); + } + + char *valid[] = { "top", "bottom", "left", "right" }; + for (size_t i = 0; i < sizeof(valid) / sizeof(valid[0]); ++i) { + if (strcasecmp(valid[i], argv[0]) == 0) { + wlr_log(L_DEBUG, "Setting bar position '%s' for bar: %s", + argv[0], config->current_bar->id); + config->current_bar->position = strdup(argv[0]); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); + } + } + + error = cmd_results_new(CMD_INVALID, "position", "Invalid value %s", argv[0]); + return error; +} diff --git a/sway/commands/bar/secondary_button.c b/sway/commands/bar/secondary_button.c new file mode 100644 index 00000000..46d53e3f --- /dev/null +++ b/sway/commands/bar/secondary_button.c @@ -0,0 +1,9 @@ +#include +#include "sway/commands.h" +#include "log.h" + +struct cmd_results *bar_cmd_secondary_button(int argc, char **argv) { + const char *cmd_name = "secondary_button"; + // TODO TRAY + return cmd_results_new(CMD_INVALID, cmd_name, "TODO TRAY"); +} diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c new file mode 100644 index 00000000..7dc0956b --- /dev/null +++ b/sway/commands/bar/separator_symbol.c @@ -0,0 +1,21 @@ +#define _XOPEN_SOURCE 500 +#include +#include "sway/commands.h" +#include "log.h" + +struct cmd_results *bar_cmd_separator_symbol(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "separator_symbol", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, "separator_symbol", "No bar defined."); + } + + free(config->current_bar->separator_symbol); + config->current_bar->separator_symbol = strdup(argv[0]); + wlr_log(L_DEBUG, "Settings separator_symbol '%s' for bar: %s", config->current_bar->separator_symbol, config->current_bar->id); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/bar/status_command.c b/sway/commands/bar/status_command.c new file mode 100644 index 00000000..05377fcc --- /dev/null +++ b/sway/commands/bar/status_command.c @@ -0,0 +1,21 @@ +#include +#include "sway/commands.h" +#include "log.h" +#include "stringop.h" + +struct cmd_results *bar_cmd_status_command(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "status_command", EXPECTED_AT_LEAST, 1))) { + return error; + } + + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, "status_command", "No bar defined."); + } + + free(config->current_bar->status_command); + config->current_bar->status_command = join_args(argv, argc); + wlr_log(L_DEBUG, "Feeding bar with status command: %s", config->current_bar->status_command); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/bar/strip_workspace_numbers.c b/sway/commands/bar/strip_workspace_numbers.c new file mode 100644 index 00000000..0558db8f --- /dev/null +++ b/sway/commands/bar/strip_workspace_numbers.c @@ -0,0 +1,27 @@ +#include +#include +#include "sway/commands.h" +#include "log.h" + +struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "strip_workspace_numbers", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, "strip_workspace_numbers", "No bar defined."); + } + + if (strcasecmp("yes", argv[0]) == 0) { + config->current_bar->strip_workspace_numbers = true; + wlr_log(L_DEBUG, "Stripping workspace numbers on bar: %s", config->current_bar->id); + } else if (strcasecmp("no", argv[0]) == 0) { + config->current_bar->strip_workspace_numbers = false; + wlr_log(L_DEBUG, "Enabling workspace numbers on bar: %s", config->current_bar->id); + } else { + error = cmd_results_new(CMD_INVALID, "strip_workspace_numbers", "Invalid value %s", argv[0]); + return error; + } + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/bar/swaybar_command.c b/sway/commands/bar/swaybar_command.c new file mode 100644 index 00000000..63d4f29a --- /dev/null +++ b/sway/commands/bar/swaybar_command.c @@ -0,0 +1,21 @@ +#include +#include "sway/commands.h" +#include "log.h" +#include "stringop.h" + +struct cmd_results *bar_cmd_swaybar_command(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "swaybar_command", EXPECTED_AT_LEAST, 1))) { + return error; + } + + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, "swaybar_command", "No bar defined."); + } + + free(config->current_bar->swaybar_command); + config->current_bar->swaybar_command = join_args(argv, argc); + wlr_log(L_DEBUG, "Using custom swaybar command: %s", config->current_bar->swaybar_command); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c new file mode 100644 index 00000000..2d822146 --- /dev/null +++ b/sway/commands/bar/tray_output.c @@ -0,0 +1,9 @@ +#define _XOPEN_SOURCE 500 +#include +#include "sway/commands.h" + +struct cmd_results *bar_cmd_tray_output(int argc, char **argv) { + const char *cmd_name = "tray_output"; + // TODO TRAY + return cmd_results_new(CMD_INVALID, cmd_name, "TODO TRAY"); +} diff --git a/sway/commands/bar/tray_padding.c b/sway/commands/bar/tray_padding.c new file mode 100644 index 00000000..95b8ad3b --- /dev/null +++ b/sway/commands/bar/tray_padding.c @@ -0,0 +1,10 @@ +#include +#include +#include "sway/commands.h" +#include "log.h" + +struct cmd_results *bar_cmd_tray_padding(int argc, char **argv) { + const char *cmd_name = "tray_padding"; + // TODO TRAY + return cmd_results_new(CMD_INVALID, cmd_name, "TODO TRAY"); +} diff --git a/sway/commands/bar/workspace_buttons.c b/sway/commands/bar/workspace_buttons.c new file mode 100644 index 00000000..1a617eb8 --- /dev/null +++ b/sway/commands/bar/workspace_buttons.c @@ -0,0 +1,27 @@ +#include +#include +#include "sway/commands.h" +#include "log.h" + +struct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "workspace_buttons", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, "workspace_buttons", "No bar defined."); + } + + if (strcasecmp("yes", argv[0]) == 0) { + config->current_bar->workspace_buttons = true; + wlr_log(L_DEBUG, "Enabling workspace buttons on bar: %s", config->current_bar->id); + } else if (strcasecmp("no", argv[0]) == 0) { + config->current_bar->workspace_buttons = false; + wlr_log(L_DEBUG, "Disabling workspace buttons on bar: %s", config->current_bar->id); + } else { + error = cmd_results_new(CMD_INVALID, "workspace_buttons", "Invalid value %s", argv[0]); + return error; + } + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/bar/wrap_scroll.c b/sway/commands/bar/wrap_scroll.c new file mode 100644 index 00000000..c89dff5f --- /dev/null +++ b/sway/commands/bar/wrap_scroll.c @@ -0,0 +1,27 @@ +#include +#include +#include "sway/commands.h" +#include "log.h" + +struct cmd_results *bar_cmd_wrap_scroll(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "wrap_scroll", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (!config->current_bar) { + return cmd_results_new(CMD_FAILURE, "wrap_scroll", "No bar defined."); + } + + if (strcasecmp("yes", argv[0]) == 0) { + config->current_bar->wrap_scroll = true; + wlr_log(L_DEBUG, "Enabling wrap scroll on bar: %s", config->current_bar->id); + } else if (strcasecmp("no", argv[0]) == 0) { + config->current_bar->wrap_scroll = false; + wlr_log(L_DEBUG, "Disabling wrap scroll on bar: %s", config->current_bar->id); + } else { + error = cmd_results_new(CMD_INVALID, "wrap_scroll", "Invalid value %s", argv[0]); + return error; + } + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/config.c b/sway/config.c index 0422fdd9..a0e408de 100644 --- a/sway/config.c +++ b/sway/config.c @@ -110,6 +110,48 @@ void free_config(struct sway_config *config) { free(config); } +static void free_bar(struct bar_config *bar) { + if (!bar) { + return; + } + free(bar->mode); + free(bar->position); + free(bar->hidden_state); + free(bar->status_command); + free(bar->font); + free(bar->separator_symbol); + // TODO: Free mouse bindings + list_free(bar->bindings); + if (bar->outputs) { + free_flat_list(bar->outputs); + } + if (bar->pid != 0) { + // TODO terminate_swaybar(bar->pid); + } + free(bar->colors.background); + free(bar->colors.statusline); + free(bar->colors.separator); + free(bar->colors.focused_background); + free(bar->colors.focused_statusline); + free(bar->colors.focused_separator); + free(bar->colors.focused_workspace_border); + free(bar->colors.focused_workspace_bg); + free(bar->colors.focused_workspace_text); + free(bar->colors.active_workspace_border); + free(bar->colors.active_workspace_bg); + free(bar->colors.active_workspace_text); + free(bar->colors.inactive_workspace_border); + free(bar->colors.inactive_workspace_bg); + free(bar->colors.inactive_workspace_text); + free(bar->colors.urgent_workspace_border); + free(bar->colors.urgent_workspace_bg); + free(bar->colors.urgent_workspace_text); + free(bar->colors.binding_mode_border); + free(bar->colors.binding_mode_bg); + free(bar->colors.binding_mode_text); + free(bar); +} + static void destroy_removed_seats(struct sway_config *old_config, struct sway_config *new_config) { struct seat_config *seat_config; @@ -239,6 +281,91 @@ cleanup: sway_abort("Unable to allocate config structures"); } +struct bar_config *default_bar_config(void) { + struct bar_config *bar = NULL; + bar = malloc(sizeof(struct bar_config)); + if (!bar) { + return NULL; + } + if (!(bar->mode = strdup("dock"))) goto cleanup; + if (!(bar->hidden_state = strdup("hide"))) goto cleanup; + bar->outputs = NULL; + bar->position = strdup("bottom"); + if (!(bar->bindings = create_list())) goto cleanup; + if (!(bar->status_command = strdup("while :; do date +'%Y-%m-%d %l:%M:%S %p'; sleep 1; done"))) goto cleanup; + bar->pango_markup = false; + bar->swaybar_command = NULL; + bar->font = NULL; + bar->height = -1; + bar->workspace_buttons = true; + bar->wrap_scroll = false; + bar->separator_symbol = NULL; + bar->strip_workspace_numbers = false; + bar->binding_mode_indicator = true; + bar->verbose = false; + bar->pid = 0; + // set default colors + if (!(bar->colors.background = strndup("#000000ff", 9))) { + goto cleanup; + } + if (!(bar->colors.statusline = strndup("#ffffffff", 9))) { + goto cleanup; + } + if (!(bar->colors.separator = strndup("#666666ff", 9))) { + goto cleanup; + } + if (!(bar->colors.focused_workspace_border = strndup("#4c7899ff", 9))) { + goto cleanup; + } + if (!(bar->colors.focused_workspace_bg = strndup("#285577ff", 9))) { + goto cleanup; + } + if (!(bar->colors.focused_workspace_text = strndup("#ffffffff", 9))) { + goto cleanup; + } + if (!(bar->colors.active_workspace_border = strndup("#333333ff", 9))) { + goto cleanup; + } + if (!(bar->colors.active_workspace_bg = strndup("#5f676aff", 9))) { + goto cleanup; + } + if (!(bar->colors.active_workspace_text = strndup("#ffffffff", 9))) { + goto cleanup; + } + if (!(bar->colors.inactive_workspace_border = strndup("#333333ff", 9))) { + goto cleanup; + } + if (!(bar->colors.inactive_workspace_bg = strndup("#222222ff", 9))) { + goto cleanup; + } + if (!(bar->colors.inactive_workspace_text = strndup("#888888ff", 9))) { + goto cleanup; + } + if (!(bar->colors.urgent_workspace_border = strndup("#2f343aff", 9))) { + goto cleanup; + } + if (!(bar->colors.urgent_workspace_bg = strndup("#900000ff", 9))) { + goto cleanup; + } + if (!(bar->colors.urgent_workspace_text = strndup("#ffffffff", 9))) { + goto cleanup; + } + // if the following colors stay undefined, they fall back to background, + // statusline, separator and urgent_workspace_*. + bar->colors.focused_background = NULL; + bar->colors.focused_statusline = NULL; + bar->colors.focused_separator = NULL; + bar->colors.binding_mode_border = NULL; + bar->colors.binding_mode_bg = NULL; + bar->colors.binding_mode_text = NULL; + + list_add(config->bars, bar); + return bar; +cleanup: + free_bar(bar); + return NULL; +} + static bool file_exists(const char *path) { return path && access(path, R_OK) != -1; } diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 408ed432..59fc05f9 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -313,10 +313,18 @@ void ipc_event_window(swayc_t *window, const char *change) { const char *json_string = json_object_to_json_string(obj); ipc_send_event(json_string, IPC_EVENT_WINDOW); - json_object_put(obj); // free } +void ipc_event_barconfig_update(struct bar_config *bar) { + wlr_log(L_DEBUG, "Sending barconfig_update event"); + json_object *json = ipc_json_describe_bar_config(bar); + + const char *json_string = json_object_to_json_string(json); + ipc_send_event(json_string, IPC_EVENT_BARCONFIG_UPDATE); + json_object_put(json); // free +} + int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { struct ipc_client *client = data; diff --git a/sway/meson.build b/sway/meson.build index 8bddb11b..8fcb0dbf 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -6,6 +6,7 @@ sway_sources = files( 'input/seat.c', 'input/cursor.c', 'input/keyboard.c', + 'commands/bar.c', 'commands/bind.c', 'commands/exit.c', 'commands/exec.c', @@ -19,6 +20,30 @@ sway_sources = files( 'commands/seat/attach.c', 'commands/seat/fallback.c', 'commands/set.c', + 'commands/bar/activate_button.c', + 'commands/bar/binding_mode_indicator.c', + 'commands/bar/bindsym.c', + 'commands/bar/colors.c', + 'commands/bar/context_button.c', + 'commands/bar/font.c', + 'commands/bar/height.c', + 'commands/bar/hidden_state.c', + 'commands/bar/icon_theme.c', + 'commands/bar/id.c', + 'commands/bar/mode.c', + 'commands/bar/modifier.c', + 'commands/bar/output.c', + 'commands/bar/pango_markup.c', + 'commands/bar/position.c', + 'commands/bar/secondary_button.c', + 'commands/bar/separator_symbol.c', + 'commands/bar/status_command.c', + 'commands/bar/strip_workspace_numbers.c', + 'commands/bar/swaybar_command.c', + 'commands/bar/tray_output.c', + 'commands/bar/tray_padding.c', + 'commands/bar/workspace_buttons.c', + 'commands/bar/wrap_scroll.c', 'commands/input/accel_profile.c', 'commands/input/click_method.c', 'commands/input/drag_lock.c', From 569b2bfd5daae5b3be49772bdca4a3f224e20629 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 17:41:02 -0400 Subject: [PATCH 19/34] Move bar config into its own file --- include/sway/config.h | 26 ++------ sway/config.c | 127 ------------------------------------- sway/config/bar.c | 143 ++++++++++++++++++++++++++++++++++++++++++ sway/config/output.c | 2 +- sway/meson.build | 1 + 5 files changed, 151 insertions(+), 148 deletions(-) create mode 100644 sway/config/bar.c diff --git a/include/sway/config.h b/include/sway/config.h index f9ab6778..dbcfc91e 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -12,6 +12,8 @@ #include "container.h" #include "wlr-layer-shell-unstable-v1-protocol.h" +// TODO: Refactor this shit + /** * Describes a variable created via the `set` command. */ @@ -407,11 +409,6 @@ void merge_output_config(struct output_config *dst, struct output_config *src); void apply_output_config(struct output_config *oc, swayc_t *output); void free_output_config(struct output_config *oc); -/** - * Updates the list of active bar modifiers - */ -void update_active_bar_modifiers(void); - int workspace_output_cmp_workspace(const void *a, const void *b); int sway_binding_cmp(const void *a, const void *b); @@ -420,27 +417,16 @@ int sway_binding_cmp_keys(const void *a, const void *b); void free_sway_binding(struct sway_binding *sb); struct sway_binding *sway_binding_dup(struct sway_binding *sb); -int sway_mouse_binding_cmp(const void *a, const void *b); -int sway_mouse_binding_cmp_qsort(const void *a, const void *b); -int sway_mouse_binding_cmp_buttons(const void *a, const void *b); -void free_sway_mouse_binding(struct sway_mouse_binding *smb); - +/* Bar stuff */ void load_swaybars(); void terminate_swaybg(pid_t pid); - -/** - * Allocate and initialize default bar configuration. - */ struct bar_config *default_bar_config(void); +void free_bar_config(struct bar_config *bar); -/** - * Global config singleton. - */ +/* Global config singleton. */ extern struct sway_config *config; -/** - * Config file currently being read. - */ +/* Config file currently being read */ extern const char *current_config_path; #endif diff --git a/sway/config.c b/sway/config.c index a0e408de..0422fdd9 100644 --- a/sway/config.c +++ b/sway/config.c @@ -110,48 +110,6 @@ void free_config(struct sway_config *config) { free(config); } -static void free_bar(struct bar_config *bar) { - if (!bar) { - return; - } - free(bar->mode); - free(bar->position); - free(bar->hidden_state); - free(bar->status_command); - free(bar->font); - free(bar->separator_symbol); - // TODO: Free mouse bindings - list_free(bar->bindings); - if (bar->outputs) { - free_flat_list(bar->outputs); - } - if (bar->pid != 0) { - // TODO terminate_swaybar(bar->pid); - } - free(bar->colors.background); - free(bar->colors.statusline); - free(bar->colors.separator); - free(bar->colors.focused_background); - free(bar->colors.focused_statusline); - free(bar->colors.focused_separator); - free(bar->colors.focused_workspace_border); - free(bar->colors.focused_workspace_bg); - free(bar->colors.focused_workspace_text); - free(bar->colors.active_workspace_border); - free(bar->colors.active_workspace_bg); - free(bar->colors.active_workspace_text); - free(bar->colors.inactive_workspace_border); - free(bar->colors.inactive_workspace_bg); - free(bar->colors.inactive_workspace_text); - free(bar->colors.urgent_workspace_border); - free(bar->colors.urgent_workspace_bg); - free(bar->colors.urgent_workspace_text); - free(bar->colors.binding_mode_border); - free(bar->colors.binding_mode_bg); - free(bar->colors.binding_mode_text); - free(bar); -} - static void destroy_removed_seats(struct sway_config *old_config, struct sway_config *new_config) { struct seat_config *seat_config; @@ -281,91 +239,6 @@ cleanup: sway_abort("Unable to allocate config structures"); } -struct bar_config *default_bar_config(void) { - struct bar_config *bar = NULL; - bar = malloc(sizeof(struct bar_config)); - if (!bar) { - return NULL; - } - if (!(bar->mode = strdup("dock"))) goto cleanup; - if (!(bar->hidden_state = strdup("hide"))) goto cleanup; - bar->outputs = NULL; - bar->position = strdup("bottom"); - if (!(bar->bindings = create_list())) goto cleanup; - if (!(bar->status_command = strdup("while :; do date +'%Y-%m-%d %l:%M:%S %p'; sleep 1; done"))) goto cleanup; - bar->pango_markup = false; - bar->swaybar_command = NULL; - bar->font = NULL; - bar->height = -1; - bar->workspace_buttons = true; - bar->wrap_scroll = false; - bar->separator_symbol = NULL; - bar->strip_workspace_numbers = false; - bar->binding_mode_indicator = true; - bar->verbose = false; - bar->pid = 0; - // set default colors - if (!(bar->colors.background = strndup("#000000ff", 9))) { - goto cleanup; - } - if (!(bar->colors.statusline = strndup("#ffffffff", 9))) { - goto cleanup; - } - if (!(bar->colors.separator = strndup("#666666ff", 9))) { - goto cleanup; - } - if (!(bar->colors.focused_workspace_border = strndup("#4c7899ff", 9))) { - goto cleanup; - } - if (!(bar->colors.focused_workspace_bg = strndup("#285577ff", 9))) { - goto cleanup; - } - if (!(bar->colors.focused_workspace_text = strndup("#ffffffff", 9))) { - goto cleanup; - } - if (!(bar->colors.active_workspace_border = strndup("#333333ff", 9))) { - goto cleanup; - } - if (!(bar->colors.active_workspace_bg = strndup("#5f676aff", 9))) { - goto cleanup; - } - if (!(bar->colors.active_workspace_text = strndup("#ffffffff", 9))) { - goto cleanup; - } - if (!(bar->colors.inactive_workspace_border = strndup("#333333ff", 9))) { - goto cleanup; - } - if (!(bar->colors.inactive_workspace_bg = strndup("#222222ff", 9))) { - goto cleanup; - } - if (!(bar->colors.inactive_workspace_text = strndup("#888888ff", 9))) { - goto cleanup; - } - if (!(bar->colors.urgent_workspace_border = strndup("#2f343aff", 9))) { - goto cleanup; - } - if (!(bar->colors.urgent_workspace_bg = strndup("#900000ff", 9))) { - goto cleanup; - } - if (!(bar->colors.urgent_workspace_text = strndup("#ffffffff", 9))) { - goto cleanup; - } - // if the following colors stay undefined, they fall back to background, - // statusline, separator and urgent_workspace_*. - bar->colors.focused_background = NULL; - bar->colors.focused_statusline = NULL; - bar->colors.focused_separator = NULL; - bar->colors.binding_mode_border = NULL; - bar->colors.binding_mode_bg = NULL; - bar->colors.binding_mode_text = NULL; - - list_add(config->bars, bar); - return bar; -cleanup: - free_bar(bar); - return NULL; -} - static bool file_exists(const char *path) { return path && access(path, R_OK) != -1; } diff --git a/sway/config/bar.c b/sway/config/bar.c new file mode 100644 index 00000000..ecc357d0 --- /dev/null +++ b/sway/config/bar.c @@ -0,0 +1,143 @@ +#define _POSIX_C_SOURCE 200809L +#define _XOPEN_SOURCE 700 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sway/config.h" +#include "stringop.h" +#include "list.h" +#include "log.h" + +void free_bar_config(struct bar_config *bar) { + if (!bar) { + return; + } + free(bar->mode); + free(bar->position); + free(bar->hidden_state); + free(bar->status_command); + free(bar->font); + free(bar->separator_symbol); + // TODO: Free mouse bindings + list_free(bar->bindings); + if (bar->outputs) { + free_flat_list(bar->outputs); + } + if (bar->pid != 0) { + // TODO terminate_swaybar(bar->pid); + } + free(bar->colors.background); + free(bar->colors.statusline); + free(bar->colors.separator); + free(bar->colors.focused_background); + free(bar->colors.focused_statusline); + free(bar->colors.focused_separator); + free(bar->colors.focused_workspace_border); + free(bar->colors.focused_workspace_bg); + free(bar->colors.focused_workspace_text); + free(bar->colors.active_workspace_border); + free(bar->colors.active_workspace_bg); + free(bar->colors.active_workspace_text); + free(bar->colors.inactive_workspace_border); + free(bar->colors.inactive_workspace_bg); + free(bar->colors.inactive_workspace_text); + free(bar->colors.urgent_workspace_border); + free(bar->colors.urgent_workspace_bg); + free(bar->colors.urgent_workspace_text); + free(bar->colors.binding_mode_border); + free(bar->colors.binding_mode_bg); + free(bar->colors.binding_mode_text); + free(bar); +} + +struct bar_config *default_bar_config(void) { + struct bar_config *bar = NULL; + bar = malloc(sizeof(struct bar_config)); + if (!bar) { + return NULL; + } + if (!(bar->mode = strdup("dock"))) goto cleanup; + if (!(bar->hidden_state = strdup("hide"))) goto cleanup; + bar->outputs = NULL; + bar->position = strdup("bottom"); + if (!(bar->bindings = create_list())) goto cleanup; + if (!(bar->status_command = strdup("while :; do date +'%Y-%m-%d %l:%M:%S %p'; sleep 1; done"))) goto cleanup; + bar->pango_markup = false; + bar->swaybar_command = NULL; + bar->font = NULL; + bar->height = -1; + bar->workspace_buttons = true; + bar->wrap_scroll = false; + bar->separator_symbol = NULL; + bar->strip_workspace_numbers = false; + bar->binding_mode_indicator = true; + bar->verbose = false; + bar->pid = 0; + // set default colors + if (!(bar->colors.background = strndup("#000000ff", 9))) { + goto cleanup; + } + if (!(bar->colors.statusline = strndup("#ffffffff", 9))) { + goto cleanup; + } + if (!(bar->colors.separator = strndup("#666666ff", 9))) { + goto cleanup; + } + if (!(bar->colors.focused_workspace_border = strndup("#4c7899ff", 9))) { + goto cleanup; + } + if (!(bar->colors.focused_workspace_bg = strndup("#285577ff", 9))) { + goto cleanup; + } + if (!(bar->colors.focused_workspace_text = strndup("#ffffffff", 9))) { + goto cleanup; + } + if (!(bar->colors.active_workspace_border = strndup("#333333ff", 9))) { + goto cleanup; + } + if (!(bar->colors.active_workspace_bg = strndup("#5f676aff", 9))) { + goto cleanup; + } + if (!(bar->colors.active_workspace_text = strndup("#ffffffff", 9))) { + goto cleanup; + } + if (!(bar->colors.inactive_workspace_border = strndup("#333333ff", 9))) { + goto cleanup; + } + if (!(bar->colors.inactive_workspace_bg = strndup("#222222ff", 9))) { + goto cleanup; + } + if (!(bar->colors.inactive_workspace_text = strndup("#888888ff", 9))) { + goto cleanup; + } + if (!(bar->colors.urgent_workspace_border = strndup("#2f343aff", 9))) { + goto cleanup; + } + if (!(bar->colors.urgent_workspace_bg = strndup("#900000ff", 9))) { + goto cleanup; + } + if (!(bar->colors.urgent_workspace_text = strndup("#ffffffff", 9))) { + goto cleanup; + } + // if the following colors stay undefined, they fall back to background, + // statusline, separator and urgent_workspace_*. + bar->colors.focused_background = NULL; + bar->colors.focused_statusline = NULL; + bar->colors.focused_separator = NULL; + bar->colors.binding_mode_border = NULL; + bar->colors.binding_mode_bg = NULL; + bar->colors.binding_mode_text = NULL; + + list_add(config->bars, bar); + return bar; +cleanup: + free_bar_config(bar); + return NULL; +} diff --git a/sway/config/output.c b/sway/config/output.c index 9e211861..24b4a18e 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -186,7 +186,7 @@ void apply_output_config(struct output_config *oc, swayc_t *output) { output_id[bufsize-1] = 0; char *const cmd[] = { - "./swaybg/swaybg", + "swaybg", output_id, oc->background, oc->background_option, diff --git a/sway/meson.build b/sway/meson.build index 8fcb0dbf..ac65d05e 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -64,6 +64,7 @@ sway_sources = files( 'commands/reload.c', 'commands/workspace.c', 'config.c', + 'config/bar.c', 'config/output.c', 'config/seat.c', 'config/input.c', From 5c9cdbcdd2a07e2ced7b60d629a3e20bd7c8bf68 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 17:49:44 -0400 Subject: [PATCH 20/34] Add swaybg_command --- include/sway/commands.h | 1 + include/sway/config.h | 1 + sway/commands.c | 1 + sway/commands/swaybg_command.c | 20 ++++++++++++++++++++ sway/config/output.c | 25 +++++++++++++------------ sway/meson.build | 1 + sway/sway.5.txt | 3 +++ 7 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 sway/commands/swaybg_command.c diff --git a/include/sway/commands.h b/include/sway/commands.h index dda286a2..1291d5fb 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -141,6 +141,7 @@ sway_cmd cmd_splith; sway_cmd cmd_splitt; sway_cmd cmd_splitv; sway_cmd cmd_sticky; +sway_cmd cmd_swaybg_command; sway_cmd cmd_unmark; sway_cmd cmd_workspace; sway_cmd cmd_ws_auto_back_and_forth; diff --git a/include/sway/config.h b/include/sway/config.h index dbcfc91e..4a7fee0f 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -282,6 +282,7 @@ struct sway_config { list_t *active_bar_modifiers; struct sway_mode *current_mode; struct bar_config *current_bar; + char *swaybg_command; uint32_t floating_mod; uint32_t dragging_key; uint32_t resizing_key; diff --git a/sway/commands.c b/sway/commands.c index 8d8b643b..38e2f764 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -149,6 +149,7 @@ static struct cmd_handler bar_colors_handlers[] = { /* Config-time only commands. Keep alphabetized */ static struct cmd_handler config_handlers[] = { { "set", cmd_set }, + { "swaybg_command", cmd_swaybg_command }, }; /* Runtime-only commands. Keep alphabetized */ diff --git a/sway/commands/swaybg_command.c b/sway/commands/swaybg_command.c new file mode 100644 index 00000000..770d4821 --- /dev/null +++ b/sway/commands/swaybg_command.c @@ -0,0 +1,20 @@ +#include +#include "sway/commands.h" +#include "log.h" +#include "stringop.h" + +struct cmd_results *cmd_swaybg_command(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "swaybg_command", EXPECTED_AT_LEAST, 1))) { + return error; + } + + if (config->swaybg_command) { + free(config->swaybg_command); + } + config->swaybg_command = join_args(argv, argc); + wlr_log(L_DEBUG, "Using custom swaybg command: %s", + config->swaybg_command); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/config/output.c b/sway/config/output.c index 24b4a18e..c3ec61b7 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -180,19 +180,20 @@ void apply_output_config(struct output_config *oc, swayc_t *output) { wlr_log(L_DEBUG, "Setting background for output %d to %s", output_i, oc->background); - size_t bufsize = 12; - char output_id[bufsize]; - snprintf(output_id, bufsize, "%d", output_i); - output_id[bufsize-1] = 0; - - char *const cmd[] = { - "swaybg", - output_id, - oc->background, - oc->background_option, - NULL, - }; + size_t len = snprintf(NULL, 0, "%s %d %s %s", + config->swaybg_command ? config->swaybg_command : "swaybg", + output_i, oc->background, oc->background_option); + char *command = malloc(len + 1); + if (!command) { + wlr_log(L_DEBUG, "Unable to allocate swaybg command"); + return; + } + snprintf(command, len + 1, "%s %d %s %s", + config->swaybg_command ? config->swaybg_command : "swaybg", + output_i, oc->background, oc->background_option); + wlr_log(L_DEBUG, "-> %s", command); + char *const cmd[] = { "sh", "-c", command, NULL }; output->sway_output->bg_pid = fork(); if (output->sway_output->bg_pid == 0) { execvp(cmd[0], cmd); diff --git a/sway/meson.build b/sway/meson.build index ac65d05e..54c03061 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -20,6 +20,7 @@ sway_sources = files( 'commands/seat/attach.c', 'commands/seat/fallback.c', 'commands/set.c', + 'commands/swaybg_command.c', 'commands/bar/activate_button.c', 'commands/bar/binding_mode_indicator.c', 'commands/bar/bindsym.c', diff --git a/sway/sway.5.txt b/sway/sway.5.txt index 6c9bce7a..900e499a 100644 --- a/sway/sway.5.txt +++ b/sway/sway.5.txt @@ -43,6 +43,9 @@ The following commands may only be used in the configuration file. Sets variable $name to _value_. You can use the new variable in the arguments of future commands. +**swaybg_command** :: + Executes custom bg command, default is _swaybg_. + The following commands cannot be used directly in the configuration file. They are expected to be used with **bindsym** or at runtime through **swaymsg**(1). From 2719ddfe5e171881f3997f9f853bfca97fe01529 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 18:07:03 -0400 Subject: [PATCH 21/34] Spawn swaybars when outputs are added --- include/sway/config.h | 1 + sway/config/bar.c | 95 +++++++++++++++++++++++++++++++++++++++++++ sway/server.c | 6 ++- sway/tree/container.c | 2 +- 4 files changed, 102 insertions(+), 2 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 4a7fee0f..b7820128 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -420,6 +420,7 @@ struct sway_binding *sway_binding_dup(struct sway_binding *sb); /* Bar stuff */ void load_swaybars(); +void invoke_swaybar(struct bar_config *bar); void terminate_swaybg(pid_t pid); struct bar_config *default_bar_config(void); void free_bar_config(struct bar_config *bar); diff --git a/sway/config/bar.c b/sway/config/bar.c index ecc357d0..5e02c01c 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -141,3 +141,98 @@ cleanup: free_bar_config(bar); return NULL; } + +void invoke_swaybar(struct bar_config *bar) { + // Pipe to communicate errors + int filedes[2]; + if (pipe(filedes) == -1) { + wlr_log(L_ERROR, "Pipe setup failed! Cannot fork into bar"); + return; + } + + bar->pid = fork(); + if (bar->pid == 0) { + close(filedes[0]); + + // run custom swaybar + size_t len = snprintf(NULL, 0, "%s -b %s", + bar->swaybar_command ? bar->swaybar_command : "swaybar", + bar->id); + char *command = malloc(len + 1); + if (!command) { + const char msg[] = "Unable to allocate swaybar command string"; + size_t len = sizeof(msg); + if (write(filedes[1], &len, sizeof(int))) {}; + if (write(filedes[1], msg, len)) {}; + close(filedes[1]); + exit(1); + } + snprintf(command, len + 1, "%s -b %s", + bar->swaybar_command ? bar->swaybar_command : "swaybar", + bar->id); + char *const cmd[] = { "sh", "-c", command, NULL, }; + close(filedes[1]); + execvp(cmd[0], cmd); + exit(1); + } + close(filedes[0]); + ssize_t len; + if (read(filedes[1], &len, sizeof(int)) == sizeof(int)) { + char *buf = malloc(len); + if(!buf) { + wlr_log(L_ERROR, "Cannot allocate error string"); + return; + } + if (read(filedes[1], buf, len)) { + wlr_log(L_ERROR, "%s", buf); + } + free(buf); + } + close(filedes[1]); +} + +static void terminate_swaybar(pid_t pid) { + int ret = kill(pid, SIGTERM); + if (ret != 0) { + wlr_log_errno(L_ERROR, "Unable to terminate swaybar %d", pid); + } else { + int status; + waitpid(pid, &status, 0); + } +} + +static bool active_output(const char *name) { + swayc_t *cont = NULL; + for (int i = 0; i < root_container.children->length; ++i) { + cont = root_container.children->items[i]; + if (cont->type == C_OUTPUT && strcasecmp(name, cont->name) == 0) { + return true; + } + } + return false; +} + +void load_swaybars() { + for (int i = 0; i < config->bars->length; ++i) { + struct bar_config *bar = config->bars->items[i]; + bool apply = false; + if (bar->outputs) { + for (int j = 0; j < bar->outputs->length; ++j) { + char *o = bar->outputs->items[j]; + if (!strcmp(o, "*") || active_output(o)) { + apply = true; + break; + } + } + } else { + apply = true; + } + if (apply) { + if (bar->pid != 0) { + terminate_swaybar(bar->pid); + } + wlr_log(L_DEBUG, "Invoking swaybar for bar id '%s'", bar->id); + invoke_swaybar(bar); + } + } +} diff --git a/sway/server.c b/sway/server.c index 92f72f13..75202df2 100644 --- a/sway/server.c +++ b/sway/server.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include // TODO WLR: make Xwayland optional #include @@ -45,10 +47,12 @@ bool server_init(struct sway_server *server) { server->compositor = wlr_compositor_create( server->wl_display, server->renderer); - server->data_device_manager = wlr_data_device_manager_create(server->wl_display); + wlr_screenshooter_create(server->wl_display); + wlr_gamma_control_manager_create(server->wl_display); + server->new_output.notify = handle_new_output; wl_signal_add(&server->backend->events.new_output, &server->new_output); diff --git a/sway/tree/container.c b/sway/tree/container.c index bbafe9ec..64e2bdee 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -165,8 +165,8 @@ swayc_t *new_output(struct sway_output *sway_output) { } apply_output_config(oc, output); - add_child(&root_container, output); + load_swaybars(); // Create workspace char *ws_name = workspace_next_name(output->name); From 2e84f21ab786a5e31de836fda4e8bbd6de08d0ec Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 18:08:23 -0400 Subject: [PATCH 22/34] Terminate swaybar when freeing bar config --- sway/config/bar.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/sway/config/bar.c b/sway/config/bar.c index 5e02c01c..10abdef7 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -15,6 +15,16 @@ #include "list.h" #include "log.h" +static void terminate_swaybar(pid_t pid) { + int ret = kill(pid, SIGTERM); + if (ret != 0) { + wlr_log_errno(L_ERROR, "Unable to terminate swaybar %d", pid); + } else { + int status; + waitpid(pid, &status, 0); + } +} + void free_bar_config(struct bar_config *bar) { if (!bar) { return; @@ -31,7 +41,7 @@ void free_bar_config(struct bar_config *bar) { free_flat_list(bar->outputs); } if (bar->pid != 0) { - // TODO terminate_swaybar(bar->pid); + terminate_swaybar(bar->pid); } free(bar->colors.background); free(bar->colors.statusline); @@ -191,16 +201,6 @@ void invoke_swaybar(struct bar_config *bar) { close(filedes[1]); } -static void terminate_swaybar(pid_t pid) { - int ret = kill(pid, SIGTERM); - if (ret != 0) { - wlr_log_errno(L_ERROR, "Unable to terminate swaybar %d", pid); - } else { - int status; - waitpid(pid, &status, 0); - } -} - static bool active_output(const char *name) { swayc_t *cont = NULL; for (int i = 0; i < root_container.children->length; ++i) { From 8b5b72c576b59c875fc10f8ae88d9643c9797c39 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 18:09:42 -0400 Subject: [PATCH 23/34] Restart swaybar on config reload --- sway/commands/reload.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sway/commands/reload.c b/sway/commands/reload.c index d54d40db..a06a9aa0 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c @@ -11,8 +11,7 @@ struct cmd_results *cmd_reload(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config."); } - /* load_swaybars(); -- for when it's implemented */ - + load_swaybars(); arrange_windows(&root_container, -1, -1); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } From 5f5076baffa34a9e0d3e33c8fd5ce7c8e8bdeb2d Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 18:24:29 -0400 Subject: [PATCH 24/34] Call arrange_windows on layer destroy --- sway/desktop/layer_shell.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 187c8664..d8ce0db1 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -81,6 +81,7 @@ static void arrange_layer(struct sway_output *output, struct wl_list *list, &full_area.width, &full_area.height); wl_list_for_each(sway_layer, list, link) { struct wlr_layer_surface *layer = sway_layer->layer_surface; + wlr_log(L_DEBUG, "arranging layer %p %s", sway_layer, layer->namespace); struct wlr_layer_surface_state *state = &layer->current; if (exclusive != (state->exclusive_zone > 0)) { continue; @@ -168,7 +169,10 @@ void arrange_layers(struct sway_output *output) { &usable_area, true); memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); - arrange_windows(output->swayc, -1, -1); + if (memcmp(&usable_area, &output->usable_area, + sizeof(struct wlr_box)) != 0) { + arrange_windows(output->swayc, -1, -1); + } // Arrange non-exlusive surfaces from top->bottom usable_area.x = usable_area.y = 0; @@ -216,7 +220,8 @@ static void unmap(struct wlr_layer_surface *layer_surface) { static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_layer_surface *sway_layer = wl_container_of( listener, sway_layer, destroy); - wlr_log(L_DEBUG, "layer surface removed"); + wlr_log(L_DEBUG, "Layer surface destroyed (%s)", + sway_layer->layer_surface->namespace); if (sway_layer->layer_surface->mapped) { unmap(sway_layer->layer_surface); } @@ -231,6 +236,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = sway_layer->layer_surface->output->data; free(sway_layer); arrange_layers(output); + arrange_windows(output->swayc, -1, -1); } static void handle_map(struct wl_listener *listener, void *data) { From f3fbf193127507e5ec5d23587c3e521ac2eb5891 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 21:09:34 -0400 Subject: [PATCH 25/34] Do some small cleanup - Fix workspace events (security config isn't in use so it wasn't being sent) - Kill status bar process when swaybar exits - Don't rearrange windows on every layer surface commit --- sway/desktop/layer_shell.c | 2 +- sway/ipc-server.c | 23 ----------------------- swaybar/status_line.c | 1 + 3 files changed, 2 insertions(+), 24 deletions(-) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index d8ce0db1..a5cd79ba 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -204,8 +204,8 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { if (wlr_output != NULL) { struct sway_output *output = wlr_output->data; struct wlr_box old_geo = layer->geo; - arrange_layers(output); if (memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0) { + arrange_layers(output); // TODO DAMAGE apply whole surface from previous and new geos } else { // TODO DAMAGE from surface damage diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 59fc05f9..8250b3a0 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -242,33 +242,10 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { } static void ipc_send_event(const char *json_string, enum ipc_command_type event) { - static struct { - enum ipc_command_type event; - enum ipc_feature feature; - } security_mappings[] = { - { IPC_EVENT_WORKSPACE, IPC_FEATURE_EVENT_WORKSPACE }, - { IPC_EVENT_OUTPUT, IPC_FEATURE_EVENT_OUTPUT }, - { IPC_EVENT_MODE, IPC_FEATURE_EVENT_MODE }, - { IPC_EVENT_WINDOW, IPC_FEATURE_EVENT_WINDOW }, - { IPC_EVENT_BINDING, IPC_FEATURE_EVENT_BINDING }, - { IPC_EVENT_INPUT, IPC_FEATURE_EVENT_INPUT } - }; - - uint32_t security_mask = 0; - for (size_t i = 0; i < sizeof(security_mappings) / sizeof(security_mappings[0]); ++i) { - if (security_mappings[i].event == event) { - security_mask = security_mappings[i].feature; - break; - } - } - int i; struct ipc_client *client; for (i = 0; i < ipc_client_list->length; i++) { client = ipc_client_list->items[i]; - if (!(client->security_policy & security_mask)) { - continue; - } if ((client->subscribed_events & event_mask(event)) == 0) { continue; } diff --git a/swaybar/status_line.c b/swaybar/status_line.c index 5b131aee..3454f207 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c @@ -76,5 +76,6 @@ struct status_line *status_line_init(char *cmd) { void status_line_free(struct status_line *status) { close(status->read_fd); close(status->write_fd); + kill(status->pid, SIGTERM); free(status); } From c91adbd188414c12f5c30d94790e3495532653a1 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 21:28:31 -0400 Subject: [PATCH 26/34] Fix failure to rearrange output in some cases --- sway/desktop/layer_shell.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index a5cd79ba..bf76b075 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -81,7 +81,6 @@ static void arrange_layer(struct sway_output *output, struct wl_list *list, &full_area.width, &full_area.height); wl_list_for_each(sway_layer, list, link) { struct wlr_layer_surface *layer = sway_layer->layer_surface; - wlr_log(L_DEBUG, "arranging layer %p %s", sway_layer, layer->namespace); struct wlr_layer_surface_state *state = &layer->current; if (exclusive != (state->exclusive_zone > 0)) { continue; @@ -167,10 +166,11 @@ void arrange_layers(struct sway_output *output) { &usable_area, true); arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &usable_area, true); - memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); if (memcmp(&usable_area, &output->usable_area, sizeof(struct wlr_box)) != 0) { + wlr_log(L_DEBUG, "Usable area changed, rearranging output"); + memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); arrange_windows(output->swayc, -1, -1); } @@ -204,8 +204,8 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { if (wlr_output != NULL) { struct sway_output *output = wlr_output->data; struct wlr_box old_geo = layer->geo; + arrange_layers(output); if (memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0) { - arrange_layers(output); // TODO DAMAGE apply whole surface from previous and new geos } else { // TODO DAMAGE from surface damage @@ -236,7 +236,6 @@ static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = sway_layer->layer_surface->output->data; free(sway_layer); arrange_layers(output); - arrange_windows(output->swayc, -1, -1); } static void handle_map(struct wl_listener *listener, void *data) { From 849c3515abff7033dbd4723fd7328cb07af74222 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 21:54:08 -0400 Subject: [PATCH 27/34] Use statically allocated text buffer --- common/pango.c | 6 ++---- sway/commands/bar.c | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/common/pango.c b/common/pango.c index 212d96cf..2ae7883c 100644 --- a/common/pango.c +++ b/common/pango.c @@ -32,7 +32,7 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, int32_t scale, bool markup, const char *fmt, ...) { - char *buf = malloc(2048); + static char buf[2048]; va_list args; va_start(args, fmt); @@ -45,12 +45,11 @@ void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, pango_cairo_update_layout(cairo, layout); pango_layout_get_pixel_size(layout, width, height); g_object_unref(layout); - free(buf); } void pango_printf(cairo_t *cairo, const char *font, int32_t scale, bool markup, const char *fmt, ...) { - char *buf = malloc(2048); + static char buf[2048]; va_list args; va_start(args, fmt); @@ -63,5 +62,4 @@ void pango_printf(cairo_t *cairo, const char *font, pango_cairo_update_layout(cairo, layout); pango_cairo_show_layout(cairo, layout); g_object_unref(layout); - free(buf); } diff --git a/sway/commands/bar.c b/sway/commands/bar.c index 548106b3..ff111163 100644 --- a/sway/commands/bar.c +++ b/sway/commands/bar.c @@ -36,15 +36,15 @@ struct cmd_results *cmd_bar(int argc, char **argv) { } // set bar id - int i; - for (i = 0; i < config->bars->length; ++i) { + for (int i = 0; i < config->bars->length; ++i) { if (bar == config->bars->items[i]) { const int len = 5 + numlen(i); // "bar-" + i + \0 bar->id = malloc(len * sizeof(char)); if (bar->id) { snprintf(bar->id, len, "bar-%d", i); } else { - return cmd_results_new(CMD_FAILURE, "bar", "Unable to allocate bar ID"); + return cmd_results_new(CMD_FAILURE, + "bar", "Unable to allocate bar ID"); } break; } From 741424c4e7811c12d8cca28466f89bd61eaf3a75 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 22:05:16 -0400 Subject: [PATCH 28/34] Clean up imported bar commands --- sway/commands/bar/activate_button.c | 3 +- sway/commands/bar/binding_mode_indicator.c | 20 ++++---- sway/commands/bar/colors.c | 51 ++++++++++----------- sway/commands/bar/context_button.c | 3 +- sway/commands/bar/font.c | 13 ++---- sway/commands/bar/height.c | 5 +- sway/commands/bar/hidden_state.c | 5 -- sway/commands/bar/icon_theme.c | 3 +- sway/commands/bar/id.c | 5 +- sway/commands/bar/mode.c | 10 ++-- sway/commands/bar/modifier.c | 8 ++-- sway/commands/bar/output.c | 15 +++--- sway/commands/bar/pango_markup.c | 11 +++-- sway/commands/bar/position.c | 7 +-- sway/commands/bar/secondary_button.c | 3 +- sway/commands/bar/separator_symbol.c | 9 ++-- sway/commands/bar/status_command.c | 9 ++-- sway/commands/bar/strip_workspace_numbers.c | 18 ++++---- sway/commands/bar/swaybar_command.c | 9 ++-- sway/commands/bar/tray_output.c | 3 +- sway/commands/bar/tray_padding.c | 3 +- sway/commands/bar/workspace_buttons.c | 15 +++--- sway/commands/bar/wrap_scroll.c | 12 ++--- 23 files changed, 104 insertions(+), 136 deletions(-) diff --git a/sway/commands/bar/activate_button.c b/sway/commands/bar/activate_button.c index 0665d2a6..7310e7ec 100644 --- a/sway/commands/bar/activate_button.c +++ b/sway/commands/bar/activate_button.c @@ -3,7 +3,6 @@ #include "log.h" struct cmd_results *bar_cmd_activate_button(int argc, char **argv) { - const char *cmd_name = "activate_button"; // TODO TRAY - return cmd_results_new(CMD_INVALID, cmd_name, "TODO TRAY"); + return cmd_results_new(CMD_INVALID, "activate_button", "TODO TRAY"); } diff --git a/sway/commands/bar/binding_mode_indicator.c b/sway/commands/bar/binding_mode_indicator.c index e11e1033..3ba5f33f 100644 --- a/sway/commands/bar/binding_mode_indicator.c +++ b/sway/commands/bar/binding_mode_indicator.c @@ -5,23 +5,23 @@ struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "binding_mode_indicator", EXPECTED_EQUAL_TO, 1))) { + if ((error = checkarg(argc, + "binding_mode_indicator", EXPECTED_EQUAL_TO, 1))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "binding_mode_indicator", "No bar defined."); + return cmd_results_new(CMD_FAILURE, + "binding_mode_indicator", "No bar defined."); } - if (strcasecmp("yes", argv[0]) == 0) { config->current_bar->binding_mode_indicator = true; - wlr_log(L_DEBUG, "Enabling binding mode indicator on bar: %s", config->current_bar->id); + wlr_log(L_DEBUG, "Enabling binding mode indicator on bar: %s", + config->current_bar->id); } else if (strcasecmp("no", argv[0]) == 0) { config->current_bar->binding_mode_indicator = false; - wlr_log(L_DEBUG, "Disabling binding mode indicator on bar: %s", config->current_bar->id); - } else { - error = cmd_results_new(CMD_INVALID, "binding_mode_indicator", "Invalid value %s", argv[0]); - return error; + wlr_log(L_DEBUG, "Disabling binding mode indicator on bar: %s", + config->current_bar->id); } - return cmd_results_new(CMD_SUCCESS, NULL, NULL); + return cmd_results_new(CMD_INVALID, "binding_mode_indicator", + "Invalid value %s", argv[0]); } diff --git a/sway/commands/bar/colors.c b/sway/commands/bar/colors.c index 8b3b0aac..17ba9b7c 100644 --- a/sway/commands/bar/colors.c +++ b/sway/commands/bar/colors.c @@ -1,47 +1,38 @@ #include #include "sway/commands.h" -static struct cmd_results *parse_single_color(char **color, const char *cmd_name, int argc, char **argv) { +static struct cmd_results *parse_single_color(char **color, + const char *cmd_name, int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) { return error; } - - if (!*color) { - *color = malloc(10); - if (!*color) { - return cmd_results_new(CMD_FAILURE, cmd_name, "Unable to allocate color"); - } + if (!*color && !(*color = malloc(10))) { + return NULL; } - error = add_color(cmd_name, *color, argv[0]); if (error) { return error; } - return cmd_results_new(CMD_SUCCESS, NULL, NULL); } -static struct cmd_results *parse_three_colors(char ***colors, const char *cmd_name, int argc, char **argv) { +static struct cmd_results *parse_three_colors(char ***colors, + const char *cmd_name, int argc, char **argv) { struct cmd_results *error = NULL; if (argc != 3) { - return cmd_results_new(CMD_INVALID, cmd_name, "Requires exactly three color values"); + return cmd_results_new(CMD_INVALID, + cmd_name, "Requires exactly three color values"); } - - int i; - for (i = 0; i < 3; i++) { - if (!*colors[i]) { - *(colors[i]) = malloc(10); - if (!*(colors[i])) { - return cmd_results_new(CMD_FAILURE, cmd_name, "Unable to allocate color"); - } + for (size_t i = 0; i < 3; i++) { + if (!*colors[i] && !(*(colors[i]) = malloc(10))) { + return NULL; } error = add_color(cmd_name, *(colors[i]), argv[i]); if (error) { return error; } } - return cmd_results_new(CMD_SUCCESS, NULL, NULL); } @@ -50,12 +41,10 @@ struct cmd_results *bar_cmd_colors(int argc, char **argv) { if ((error = checkarg(argc, "colors", EXPECTED_EQUAL_TO, 1))) { return error; } - if (strcmp("{", argv[0]) != 0) { return cmd_results_new(CMD_INVALID, "colors", "Expected '{' at the start of colors config definition."); } - return cmd_results_new(CMD_BLOCK_BAR_COLORS, NULL, NULL); } @@ -69,11 +58,13 @@ struct cmd_results *bar_colors_cmd_active_workspace(int argc, char **argv) { } struct cmd_results *bar_colors_cmd_background(int argc, char **argv) { - return parse_single_color(&(config->current_bar->colors.background), "background", argc, argv); + return parse_single_color(&(config->current_bar->colors.background), + "background", argc, argv); } struct cmd_results *bar_colors_cmd_focused_background(int argc, char **argv) { - return parse_single_color(&(config->current_bar->colors.focused_background), "focused_background", argc, argv); + return parse_single_color(&(config->current_bar->colors.focused_background), + "focused_background", argc, argv); } struct cmd_results *bar_colors_cmd_binding_mode(int argc, char **argv) { @@ -104,19 +95,23 @@ struct cmd_results *bar_colors_cmd_inactive_workspace(int argc, char **argv) { } struct cmd_results *bar_colors_cmd_separator(int argc, char **argv) { - return parse_single_color(&(config->current_bar->colors.separator), "separator", argc, argv); + return parse_single_color(&(config->current_bar->colors.separator), + "separator", argc, argv); } struct cmd_results *bar_colors_cmd_focused_separator(int argc, char **argv) { - return parse_single_color(&(config->current_bar->colors.focused_separator), "focused_separator", argc, argv); + return parse_single_color(&(config->current_bar->colors.focused_separator), + "focused_separator", argc, argv); } struct cmd_results *bar_colors_cmd_statusline(int argc, char **argv) { - return parse_single_color(&(config->current_bar->colors.statusline), "statusline", argc, argv); + return parse_single_color(&(config->current_bar->colors.statusline), + "statusline", argc, argv); } struct cmd_results *bar_colors_cmd_focused_statusline(int argc, char **argv) { - return parse_single_color(&(config->current_bar->colors.focused_separator), "focused_separator", argc, argv); + return parse_single_color(&(config->current_bar->colors.focused_separator), + "focused_separator", argc, argv); } struct cmd_results *bar_colors_cmd_urgent_workspace(int argc, char **argv) { diff --git a/sway/commands/bar/context_button.c b/sway/commands/bar/context_button.c index e6d17f10..3b76885a 100644 --- a/sway/commands/bar/context_button.c +++ b/sway/commands/bar/context_button.c @@ -3,7 +3,6 @@ #include "log.h" struct cmd_results *bar_cmd_context_button(int argc, char **argv) { - const char *cmd_name = "context_button"; // TODO TRAY - return cmd_results_new(CMD_INVALID, cmd_name, "TODO TRAY"); + return cmd_results_new(CMD_INVALID, "context_button", "TODO TRAY"); } diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c index 6d7c533a..80b7a593 100644 --- a/sway/commands/bar/font.c +++ b/sway/commands/bar/font.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "log.h" @@ -8,19 +9,13 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) { if ((error = checkarg(argc, "font", EXPECTED_AT_LEAST, 1))) { return error; } - if (!config->current_bar) { return cmd_results_new(CMD_FAILURE, "font", "No bar defined."); } - char *font = join_args(argv, argc); free(config->current_bar->font); - if (strlen(font) > 6 && strncmp("pango:", font, 6) == 0) { - config->current_bar->font = font; - } else { - config->current_bar->font = font; - } - - wlr_log(L_DEBUG, "Settings font '%s' for bar: %s", config->current_bar->font, config->current_bar->id); + config->current_bar->font = strdup(font); + wlr_log(L_DEBUG, "Settings font '%s' for bar: %s", + config->current_bar->font, config->current_bar->id); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/height.c b/sway/commands/bar/height.c index ae0c7834..3160caed 100644 --- a/sway/commands/bar/height.c +++ b/sway/commands/bar/height.c @@ -8,14 +8,13 @@ struct cmd_results *bar_cmd_height(int argc, char **argv) { if ((error = checkarg(argc, "height", EXPECTED_EQUAL_TO, 1))) { return error; } - int height = atoi(argv[0]); if (height < 0) { return cmd_results_new(CMD_INVALID, "height", "Invalid height value: %s", argv[0]); } - config->current_bar->height = height; - wlr_log(L_DEBUG, "Setting bar height to %d on bar: %s", height, config->current_bar->id); + wlr_log(L_DEBUG, "Setting bar height to %d on bar: %s", + height, config->current_bar->id); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c index 245d0858..6641f184 100644 --- a/sway/commands/bar/hidden_state.c +++ b/sway/commands/bar/hidden_state.c @@ -23,7 +23,6 @@ static struct cmd_results *bar_set_hidden_state(struct bar_config *bar, return cmd_results_new(CMD_INVALID, "hidden_state", "Invalid value %s", hidden_state); } - if (strcmp(old_state, bar->hidden_state) != 0) { if (!config->reading) { ipc_event_barconfig_update(bar); @@ -31,7 +30,6 @@ static struct cmd_results *bar_set_hidden_state(struct bar_config *bar, wlr_log(L_DEBUG, "Setting hidden_state: '%s' for bar: %s", bar->hidden_state, bar->id); } - // free old mode free(old_state); return cmd_results_new(CMD_SUCCESS, NULL, NULL); @@ -45,14 +43,12 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) { if ((error = checkarg(argc, "hidden_state", EXPECTED_LESS_THAN, 3))) { return error; } - if (config->reading && argc > 1) { return cmd_results_new(CMD_INVALID, "hidden_state", "Unexpected value %s in config mode", argv[1]); } const char *state = argv[0]; - if (config->reading) { return bar_set_hidden_state(config->current_bar, state); } @@ -61,7 +57,6 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) { if (argc == 2) { id = argv[1]; } - struct bar_config *bar; for (int i = 0; i < config->bars->length; ++i) { bar = config->bars->items[i]; diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c index c6090b81..44cd3076 100644 --- a/sway/commands/bar/icon_theme.c +++ b/sway/commands/bar/icon_theme.c @@ -3,7 +3,6 @@ #include "sway/commands.h" struct cmd_results *bar_cmd_icon_theme(int argc, char **argv) { - const char *cmd_name = "tray_output"; // TODO TRAY - return cmd_results_new(CMD_INVALID, cmd_name, "TODO TRAY"); + return cmd_results_new(CMD_INVALID, "icon_theme", "TODO TRAY"); } diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c index d9e7f8df..c1e56f03 100644 --- a/sway/commands/bar/id.c +++ b/sway/commands/bar/id.c @@ -11,10 +11,8 @@ struct cmd_results *bar_cmd_id(int argc, char **argv) { const char *name = argv[0]; const char *oldname = config->current_bar->id; - // check if id is used by a previously defined bar - int i; - for (i = 0; i < config->bars->length; ++i) { + for (int i = 0; i < config->bars->length; ++i) { struct bar_config *find = config->bars->items[i]; if (strcmp(name, find->id) == 0 && config->current_bar != find) { return cmd_results_new(CMD_FAILURE, "id", @@ -27,7 +25,6 @@ struct cmd_results *bar_cmd_id(int argc, char **argv) { // free old bar id free(config->current_bar->id); - config->current_bar->id = strdup(name); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c index 7d346956..34bb0a4f 100644 --- a/sway/commands/bar/mode.c +++ b/sway/commands/bar/mode.c @@ -44,13 +44,12 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) { if ((error = checkarg(argc, "mode", EXPECTED_LESS_THAN, 3))) { return error; } - if (config->reading && argc > 1) { - return cmd_results_new(CMD_INVALID, "mode", "Unexpected value %s in config mode", argv[1]); + return cmd_results_new(CMD_INVALID, + "mode", "Unexpected value %s in config mode", argv[1]); } const char *mode = argv[0]; - if (config->reading) { return bar_set_mode(config->current_bar, mode); } @@ -60,19 +59,16 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) { id = argv[1]; } - int i; struct bar_config *bar; - for (i = 0; i < config->bars->length; ++i) { + for (int i = 0; i < config->bars->length; ++i) { bar = config->bars->items[i]; if (id && strcmp(id, bar->id) == 0) { return bar_set_mode(bar, mode); } - error = bar_set_mode(bar, mode); if (error) { return error; } } - return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/modifier.c b/sway/commands/bar/modifier.c index 9a1f8b01..7ba4b125 100644 --- a/sway/commands/bar/modifier.c +++ b/sway/commands/bar/modifier.c @@ -15,7 +15,6 @@ struct cmd_results *bar_cmd_modifier(int argc, char **argv) { } uint32_t mod = 0; - list_t *split = split_string(argv[0], "+"); for (int i = 0; i < split->length; ++i) { uint32_t tmp_mod; @@ -24,12 +23,13 @@ struct cmd_results *bar_cmd_modifier(int argc, char **argv) { continue; } else { free_flat_list(split); - return cmd_results_new(CMD_INVALID, "modifier", "Unknown modifier '%s'", split->items[i]); + return cmd_results_new(CMD_INVALID, "modifier", + "Unknown modifier '%s'", split->items[i]); } } free_flat_list(split); - config->current_bar->modifier = mod; - wlr_log(L_DEBUG, "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]); + wlr_log(L_DEBUG, + "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c index 034d9ca4..f7ca0aa4 100644 --- a/sway/commands/bar/output.c +++ b/sway/commands/bar/output.c @@ -1,4 +1,5 @@ #define _XOPEN_SOURCE 500 +#include #include #include "sway/commands.h" #include "list.h" @@ -9,7 +10,6 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) { if ((error = checkarg(argc, "output", EXPECTED_EQUAL_TO, 1))) { return error; } - if (!config->current_bar) { return cmd_results_new(CMD_FAILURE, "output", "No bar defined."); } @@ -21,21 +21,20 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) { config->current_bar->outputs = outputs; } - int i; - int add_output = 1; + bool add_output = true; if (strcmp("*", output) == 0) { // remove all previous defined outputs and replace with '*' - for (i = 0; i < outputs->length; ++i) { + for (int i = 0; i < outputs->length; ++i) { free(outputs->items[i]); list_del(outputs, i); } } else { // only add output if not already defined with either the same // name or as '*' - for (i = 0; i < outputs->length; ++i) { + for (int i = 0; i < outputs->length; ++i) { const char *find = outputs->items[i]; if (strcmp("*", find) == 0 || strcmp(output, find) == 0) { - add_output = 0; + add_output = false; break; } } @@ -43,8 +42,8 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) { if (add_output) { list_add(outputs, strdup(output)); - wlr_log(L_DEBUG, "Adding bar: '%s' to output '%s'", config->current_bar->id, output); + wlr_log(L_DEBUG, "Adding bar: '%s' to output '%s'", + config->current_bar->id, output); } - return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/pango_markup.c b/sway/commands/bar/pango_markup.c index 34b85e98..480af724 100644 --- a/sway/commands/bar/pango_markup.c +++ b/sway/commands/bar/pango_markup.c @@ -8,19 +8,20 @@ struct cmd_results *bar_cmd_pango_markup(int argc, char **argv) { if ((error = checkarg(argc, "pango_markup", EXPECTED_EQUAL_TO, 1))) { return error; } - if (!config->current_bar) { return cmd_results_new(CMD_FAILURE, "pango_markup", "No bar defined."); } - if (strcasecmp("enabled", argv[0]) == 0) { config->current_bar->pango_markup = true; - wlr_log(L_DEBUG, "Enabling pango markup for bar: %s", config->current_bar->id); + wlr_log(L_DEBUG, "Enabling pango markup for bar: %s", + config->current_bar->id); } else if (strcasecmp("disabled", argv[0]) == 0) { config->current_bar->pango_markup = false; - wlr_log(L_DEBUG, "Disabling pango markup for bar: %s", config->current_bar->id); + wlr_log(L_DEBUG, "Disabling pango markup for bar: %s", + config->current_bar->id); } else { - error = cmd_results_new(CMD_INVALID, "pango_markup", "Invalid value %s", argv[0]); + error = cmd_results_new(CMD_INVALID, "pango_markup", + "Invalid value %s", argv[0]); return error; } return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c index efa8c0bc..9c580483 100644 --- a/sway/commands/bar/position.c +++ b/sway/commands/bar/position.c @@ -9,11 +9,9 @@ struct cmd_results *bar_cmd_position(int argc, char **argv) { if ((error = checkarg(argc, "position", EXPECTED_EQUAL_TO, 1))) { return error; } - if (!config->current_bar) { return cmd_results_new(CMD_FAILURE, "position", "No bar defined."); } - char *valid[] = { "top", "bottom", "left", "right" }; for (size_t i = 0; i < sizeof(valid) / sizeof(valid[0]); ++i) { if (strcasecmp(valid[i], argv[0]) == 0) { @@ -23,7 +21,6 @@ struct cmd_results *bar_cmd_position(int argc, char **argv) { return cmd_results_new(CMD_SUCCESS, NULL, NULL); } } - - error = cmd_results_new(CMD_INVALID, "position", "Invalid value %s", argv[0]); - return error; + return cmd_results_new(CMD_INVALID, + "position", "Invalid value %s", argv[0]); } diff --git a/sway/commands/bar/secondary_button.c b/sway/commands/bar/secondary_button.c index 46d53e3f..449124cb 100644 --- a/sway/commands/bar/secondary_button.c +++ b/sway/commands/bar/secondary_button.c @@ -3,7 +3,6 @@ #include "log.h" struct cmd_results *bar_cmd_secondary_button(int argc, char **argv) { - const char *cmd_name = "secondary_button"; // TODO TRAY - return cmd_results_new(CMD_INVALID, cmd_name, "TODO TRAY"); + return cmd_results_new(CMD_INVALID, "secondary_button", "TODO TRAY"); } diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c index 7dc0956b..1e08df6d 100644 --- a/sway/commands/bar/separator_symbol.c +++ b/sway/commands/bar/separator_symbol.c @@ -8,14 +8,13 @@ struct cmd_results *bar_cmd_separator_symbol(int argc, char **argv) { if ((error = checkarg(argc, "separator_symbol", EXPECTED_EQUAL_TO, 1))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "separator_symbol", "No bar defined."); + return cmd_results_new(CMD_FAILURE, + "separator_symbol", "No bar defined."); } - free(config->current_bar->separator_symbol); config->current_bar->separator_symbol = strdup(argv[0]); - wlr_log(L_DEBUG, "Settings separator_symbol '%s' for bar: %s", config->current_bar->separator_symbol, config->current_bar->id); - + wlr_log(L_DEBUG, "Settings separator_symbol '%s' for bar: %s", + config->current_bar->separator_symbol, config->current_bar->id); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/status_command.c b/sway/commands/bar/status_command.c index 05377fcc..5e199cde 100644 --- a/sway/commands/bar/status_command.c +++ b/sway/commands/bar/status_command.c @@ -8,14 +8,13 @@ struct cmd_results *bar_cmd_status_command(int argc, char **argv) { if ((error = checkarg(argc, "status_command", EXPECTED_AT_LEAST, 1))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "status_command", "No bar defined."); + return cmd_results_new(CMD_FAILURE, + "status_command", "No bar defined."); } - free(config->current_bar->status_command); config->current_bar->status_command = join_args(argv, argc); - wlr_log(L_DEBUG, "Feeding bar with status command: %s", config->current_bar->status_command); - + wlr_log(L_DEBUG, "Feeding bar with status command: %s", + config->current_bar->status_command); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/strip_workspace_numbers.c b/sway/commands/bar/strip_workspace_numbers.c index 0558db8f..4f24a356 100644 --- a/sway/commands/bar/strip_workspace_numbers.c +++ b/sway/commands/bar/strip_workspace_numbers.c @@ -5,23 +5,25 @@ struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "strip_workspace_numbers", EXPECTED_EQUAL_TO, 1))) { + if ((error = checkarg(argc, + "strip_workspace_numbers", EXPECTED_EQUAL_TO, 1))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "strip_workspace_numbers", "No bar defined."); + return cmd_results_new(CMD_FAILURE, + "strip_workspace_numbers", "No bar defined."); } - if (strcasecmp("yes", argv[0]) == 0) { config->current_bar->strip_workspace_numbers = true; - wlr_log(L_DEBUG, "Stripping workspace numbers on bar: %s", config->current_bar->id); + wlr_log(L_DEBUG, "Stripping workspace numbers on bar: %s", + config->current_bar->id); } else if (strcasecmp("no", argv[0]) == 0) { config->current_bar->strip_workspace_numbers = false; - wlr_log(L_DEBUG, "Enabling workspace numbers on bar: %s", config->current_bar->id); + wlr_log(L_DEBUG, "Enabling workspace numbers on bar: %s", + config->current_bar->id); } else { - error = cmd_results_new(CMD_INVALID, "strip_workspace_numbers", "Invalid value %s", argv[0]); - return error; + return cmd_results_new(CMD_INVALID, + "strip_workspace_numbers", "Invalid value %s", argv[0]); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/swaybar_command.c b/sway/commands/bar/swaybar_command.c index 63d4f29a..520cdd11 100644 --- a/sway/commands/bar/swaybar_command.c +++ b/sway/commands/bar/swaybar_command.c @@ -8,14 +8,13 @@ struct cmd_results *bar_cmd_swaybar_command(int argc, char **argv) { if ((error = checkarg(argc, "swaybar_command", EXPECTED_AT_LEAST, 1))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "swaybar_command", "No bar defined."); + return cmd_results_new(CMD_FAILURE, + "swaybar_command", "No bar defined."); } - free(config->current_bar->swaybar_command); config->current_bar->swaybar_command = join_args(argv, argc); - wlr_log(L_DEBUG, "Using custom swaybar command: %s", config->current_bar->swaybar_command); - + wlr_log(L_DEBUG, "Using custom swaybar command: %s", + config->current_bar->swaybar_command); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c index 2d822146..6ab16731 100644 --- a/sway/commands/bar/tray_output.c +++ b/sway/commands/bar/tray_output.c @@ -3,7 +3,6 @@ #include "sway/commands.h" struct cmd_results *bar_cmd_tray_output(int argc, char **argv) { - const char *cmd_name = "tray_output"; // TODO TRAY - return cmd_results_new(CMD_INVALID, cmd_name, "TODO TRAY"); + return cmd_results_new(CMD_INVALID, "tray_output", "TODO TRAY"); } diff --git a/sway/commands/bar/tray_padding.c b/sway/commands/bar/tray_padding.c index 95b8ad3b..91c56f19 100644 --- a/sway/commands/bar/tray_padding.c +++ b/sway/commands/bar/tray_padding.c @@ -4,7 +4,6 @@ #include "log.h" struct cmd_results *bar_cmd_tray_padding(int argc, char **argv) { - const char *cmd_name = "tray_padding"; // TODO TRAY - return cmd_results_new(CMD_INVALID, cmd_name, "TODO TRAY"); + return cmd_results_new(CMD_INVALID, "tray_padding", "TODO TRAY"); } diff --git a/sway/commands/bar/workspace_buttons.c b/sway/commands/bar/workspace_buttons.c index 1a617eb8..6edc3a0d 100644 --- a/sway/commands/bar/workspace_buttons.c +++ b/sway/commands/bar/workspace_buttons.c @@ -8,20 +8,21 @@ struct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) { if ((error = checkarg(argc, "workspace_buttons", EXPECTED_EQUAL_TO, 1))) { return error; } - if (!config->current_bar) { - return cmd_results_new(CMD_FAILURE, "workspace_buttons", "No bar defined."); + return cmd_results_new(CMD_FAILURE, + "workspace_buttons", "No bar defined."); } - if (strcasecmp("yes", argv[0]) == 0) { config->current_bar->workspace_buttons = true; - wlr_log(L_DEBUG, "Enabling workspace buttons on bar: %s", config->current_bar->id); + wlr_log(L_DEBUG, "Enabling workspace buttons on bar: %s", + config->current_bar->id); } else if (strcasecmp("no", argv[0]) == 0) { config->current_bar->workspace_buttons = false; - wlr_log(L_DEBUG, "Disabling workspace buttons on bar: %s", config->current_bar->id); + wlr_log(L_DEBUG, "Disabling workspace buttons on bar: %s", + config->current_bar->id); } else { - error = cmd_results_new(CMD_INVALID, "workspace_buttons", "Invalid value %s", argv[0]); - return error; + return cmd_results_new(CMD_INVALID, "workspace_buttons", + "Invalid value %s", argv[0]); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bar/wrap_scroll.c b/sway/commands/bar/wrap_scroll.c index c89dff5f..7386f82c 100644 --- a/sway/commands/bar/wrap_scroll.c +++ b/sway/commands/bar/wrap_scroll.c @@ -8,20 +8,20 @@ struct cmd_results *bar_cmd_wrap_scroll(int argc, char **argv) { if ((error = checkarg(argc, "wrap_scroll", EXPECTED_EQUAL_TO, 1))) { return error; } - if (!config->current_bar) { return cmd_results_new(CMD_FAILURE, "wrap_scroll", "No bar defined."); } - if (strcasecmp("yes", argv[0]) == 0) { config->current_bar->wrap_scroll = true; - wlr_log(L_DEBUG, "Enabling wrap scroll on bar: %s", config->current_bar->id); + wlr_log(L_DEBUG, "Enabling wrap scroll on bar: %s", + config->current_bar->id); } else if (strcasecmp("no", argv[0]) == 0) { config->current_bar->wrap_scroll = false; - wlr_log(L_DEBUG, "Disabling wrap scroll on bar: %s", config->current_bar->id); + wlr_log(L_DEBUG, "Disabling wrap scroll on bar: %s", + config->current_bar->id); } else { - error = cmd_results_new(CMD_INVALID, "wrap_scroll", "Invalid value %s", argv[0]); - return error; + return cmd_results_new(CMD_INVALID, + "wrap_scroll", "Invalid value %s", argv[0]); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } From 8efee109ad2ab4861f25e54e9f6d1ceb06203791 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 22:10:33 -0400 Subject: [PATCH 29/34] Implement modes --- include/sway/ipc-server.h | 1 + sway/commands.c | 1 + sway/commands/mode.c | 59 +++++++++++++++++++++++++++++++++++++++ sway/ipc-server.c | 14 ++++++++-- sway/meson.build | 1 + 5 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 sway/commands/mode.c diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h index 1f6fffff..b4db75c3 100644 --- a/include/sway/ipc-server.h +++ b/include/sway/ipc-server.h @@ -13,5 +13,6 @@ struct sockaddr_un *ipc_user_sockaddr(void); void ipc_event_workspace(swayc_t *old, swayc_t *new, const char *change); void ipc_event_window(swayc_t *window, const char *change); void ipc_event_barconfig_update(struct bar_config *bar); +void ipc_event_mode(const char *mode); #endif diff --git a/sway/commands.c b/sway/commands.c index 38e2f764..bcc777ed 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -100,6 +100,7 @@ static struct cmd_handler handlers[] = { { "exec_always", cmd_exec_always }, { "include", cmd_include }, { "input", cmd_input }, + { "mode", cmd_mode }, { "output", cmd_output }, { "seat", cmd_seat }, { "workspace", cmd_workspace }, diff --git a/sway/commands/mode.c b/sway/commands/mode.c new file mode 100644 index 00000000..c30a8bac --- /dev/null +++ b/sway/commands/mode.c @@ -0,0 +1,59 @@ +#define _XOPEN_SOURCE 500 +#include +#include +#include +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/ipc-server.h" +#include "list.h" +#include "log.h" + +struct cmd_results *cmd_mode(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "mode", EXPECTED_AT_LEAST, 1))) { + return error; + } + + const char *mode_name = argv[0]; + bool new_mode = (argc == 2 && strcmp(argv[1], "{") == 0); + if (new_mode && !config->reading) { + return cmd_results_new(CMD_FAILURE, + "mode", "Can only be used in config file."); + } + struct sway_mode *mode = NULL; + // Find mode + for (int i = 0; i < config->modes->length; ++i) { + struct sway_mode *test = config->modes->items[i]; + if (strcasecmp(test->name, mode_name) == 0) { + mode = test; + break; + } + } + // Create mode if it doesn't exist + if (!mode && new_mode) { + mode = calloc(1, sizeof(struct sway_mode)); + if (!mode) { + return cmd_results_new(CMD_FAILURE, + "mode", "Unable to allocate mode"); + } + mode->name = strdup(mode_name); + mode->keysym_bindings = create_list(); + mode->keycode_bindings = create_list(); + list_add(config->modes, mode); + } + if (!mode) { + error = cmd_results_new(CMD_INVALID, + "mode", "Unknown mode `%s'", mode_name); + return error; + } + if ((config->reading && new_mode) || (!config->reading && !new_mode)) { + wlr_log(L_DEBUG, "Switching to mode `%s'",mode->name); + } + // Set current mode + config->current_mode = mode; + if (!new_mode) { + // trigger IPC mode event + ipc_event_mode(config->current_mode->name); + } + return cmd_results_new(new_mode ? CMD_BLOCK_MODE : CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 8250b3a0..c3b589a6 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -290,7 +290,7 @@ void ipc_event_window(swayc_t *window, const char *change) { const char *json_string = json_object_to_json_string(obj); ipc_send_event(json_string, IPC_EVENT_WINDOW); - json_object_put(obj); // free + json_object_put(obj); } void ipc_event_barconfig_update(struct bar_config *bar) { @@ -299,7 +299,17 @@ void ipc_event_barconfig_update(struct bar_config *bar) { const char *json_string = json_object_to_json_string(json); ipc_send_event(json_string, IPC_EVENT_BARCONFIG_UPDATE); - json_object_put(json); // free + json_object_put(json); +} + +void ipc_event_mode(const char *mode) { + wlr_log(L_DEBUG, "Sending mode::%s event", mode); + json_object *obj = json_object_new_object(); + json_object_object_add(obj, "change", json_object_new_string(mode)); + + const char *json_string = json_object_to_json_string(obj); + ipc_send_event(json_string, IPC_EVENT_MODE); + json_object_put(obj); } int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { diff --git a/sway/meson.build b/sway/meson.build index 54c03061..1e7ee7ae 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -16,6 +16,7 @@ sway_sources = files( 'commands/include.c', 'commands/input.c', 'commands/layout.c', + 'commands/mode.c', 'commands/seat.c', 'commands/seat/attach.c', 'commands/seat/fallback.c', From ddc09940b161ac2e2d21d70d8ef4d89d33e2eb20 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 22:16:43 -0400 Subject: [PATCH 30/34] Exit ipc_get_workspace_callback early --- sway/ipc-server.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/sway/ipc-server.c b/sway/ipc-server.c index c3b589a6..872c5704 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -379,22 +379,23 @@ void ipc_client_disconnect(struct ipc_client *client) { } static void ipc_get_workspaces_callback(swayc_t *workspace, void *data) { - if (workspace->type == C_WORKSPACE) { - json_object *workspace_json = ipc_json_describe_container(workspace); - // override the default focused indicator because - // it's set differently for the get_workspaces reply - struct sway_seat *seat = - sway_input_manager_get_default_seat(input_manager); - swayc_t *focused_ws = sway_seat_get_focus(seat); - if (focused_ws->type != C_WORKSPACE) { - focused_ws = swayc_parent_by_type(focused_ws, C_WORKSPACE); - } - bool focused = workspace == focused_ws; - json_object_object_del(workspace_json, "focused"); - json_object_object_add(workspace_json, "focused", - json_object_new_boolean(focused)); - json_object_array_add((json_object *)data, workspace_json); + if (workspace->type != C_WORKSPACE) { + return; } + json_object *workspace_json = ipc_json_describe_container(workspace); + // override the default focused indicator because + // it's set differently for the get_workspaces reply + struct sway_seat *seat = + sway_input_manager_get_default_seat(input_manager); + swayc_t *focused_ws = sway_seat_get_focus(seat); + if (focused_ws->type != C_WORKSPACE) { + focused_ws = swayc_parent_by_type(focused_ws, C_WORKSPACE); + } + bool focused = workspace == focused_ws; + json_object_object_del(workspace_json, "focused"); + json_object_object_add(workspace_json, "focused", + json_object_new_boolean(focused)); + json_object_array_add((json_object *)data, workspace_json); } void ipc_client_handle_command(struct ipc_client *client) { From dbda7bfbed7d4ac67f7c2932dd0b82fb5531f89a Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 22:23:41 -0400 Subject: [PATCH 31/34] Move declaration into loop --- sway/ipc-server.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 872c5704..9d23607b 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -538,8 +538,7 @@ void ipc_client_handle_command(struct ipc_client *client) { if (!buf[0]) { // Send list of configured bar IDs json_object *bars = json_object_new_array(); - int i; - for (i = 0; i < config->bars->length; ++i) { + for (int i = 0; i < config->bars->length; ++i) { struct bar_config *bar = config->bars->items[i]; json_object_array_add(bars, json_object_new_string(bar->id)); } @@ -549,8 +548,7 @@ void ipc_client_handle_command(struct ipc_client *client) { } else { // Send particular bar's details struct bar_config *bar = NULL; - int i; - for (i = 0; i < config->bars->length; ++i) { + for (int i = 0; i < config->bars->length; ++i) { bar = config->bars->items[i]; if (strcmp(buf, bar->id) == 0) { break; From 6fe66d0e6c9a20a9dbe2d464671ff19ea5b0387c Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 22:25:25 -0400 Subject: [PATCH 32/34] Fix layer_surface_closed --- swaybar/bar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swaybar/bar.c b/swaybar/bar.c index afbce7cc..82404d33 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -46,7 +46,7 @@ static void layer_surface_configure(void *data, static void layer_surface_closed(void *_output, struct zwlr_layer_surface_v1 *surface) { // TODO: Deal with hotplugging - struct swaybar_output *output = output; + struct swaybar_output *output = _output; zwlr_layer_surface_v1_destroy(output->layer_surface); wl_surface_destroy(output->surface); } From 095ac319214d6e51df223950292bfca5ddaf591a Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 22:32:17 -0400 Subject: [PATCH 33/34] Use render_all_frames from bar_setup --- swaybar/bar.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/swaybar/bar.c b/swaybar/bar.c index 82404d33..0fc41517 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -91,6 +91,13 @@ static const struct wl_registry_listener registry_listener = { .global_remove = handle_global_remove, }; +static void render_all_frames(struct swaybar *bar) { + struct swaybar_output *output; + wl_list_for_each(output, &bar->outputs, link) { + render_frame(bar, output); + } +} + void bar_setup(struct swaybar *bar, const char *socket_path, const char *bar_id) { bar_init(bar); @@ -133,16 +140,7 @@ void bar_setup(struct swaybar *bar, } } ipc_get_workspaces(bar); - wl_list_for_each(output, &bar->outputs, link) { - render_frame(bar, output); - } -} - -static void render_all_frames(struct swaybar *bar) { - struct swaybar_output *output; - wl_list_for_each(output, &bar->outputs, link) { - render_frame(bar, output); - } + render_all_frames(bar); } static void display_in(int fd, short mask, void *_bar) { From d8104db8f1820bd3d4db8bf4f1ee51ae334ee6e7 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 29 Mar 2018 23:35:49 -0400 Subject: [PATCH 34/34] Early return from render functions if necessary --- swaybar/render.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/swaybar/render.c b/swaybar/render.c index a5834f4b..3d9ef66b 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -30,7 +30,7 @@ static uint32_t render_status_line_text(cairo_t *cairo, 1, config->pango_markup, "%s", status->text); uint32_t ideal_height = text_height + ws_vertical_padding * 2; if (height < ideal_height) { - height = ideal_height; + return ideal_height; } double text_y = height / 2.0 - text_height / 2.0; cairo_move_to(cairo, width - text_width - margin, (int)floor(text_y)); @@ -70,7 +70,7 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo, uint32_t ideal_height = text_height + ws_vertical_padding * 2 + border_width * 2; if (height < ideal_height) { - height = ideal_height; + return ideal_height; } uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; @@ -133,7 +133,7 @@ static uint32_t render_workspace_button(cairo_t *cairo, uint32_t ideal_height = ws_vertical_padding * 2 + text_height + border_width * 2; if (height < ideal_height) { - height = ideal_height; + return ideal_height; } uint32_t width = ws_horizontal_padding * 2 + text_width + border_width * 2;