aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2018-03-30 00:11:00 -0400
committerLibravatar GitHub <noreply@github.com>2018-03-30 00:11:00 -0400
commit9d7f47746cdcb0eed3cf41875d06a8ef238eef1c (patch)
tree997658454de40db3f8b76b68d658efaf2b686188
parentMerge pull request #1654 from acrisci/refactor-2-electric-boogaloo (diff)
parentMerge remote-tracking branch 'origin/wlroots' into swaybar-layers (diff)
downloadsway-9d7f47746cdcb0eed3cf41875d06a8ef238eef1c.tar.gz
sway-9d7f47746cdcb0eed3cf41875d06a8ef238eef1c.tar.zst
sway-9d7f47746cdcb0eed3cf41875d06a8ef238eef1c.zip
Merge pull request #1648 from swaywm/swaybar-layers
Port swaybar to layer shell
-rw-r--r--common/ipc-client.c32
-rw-r--r--common/meson.build3
-rw-r--r--common/pango.c65
-rw-r--r--include/pango.h16
-rw-r--r--include/sway/commands.h4
-rw-r--r--include/sway/config.h46
-rw-r--r--include/sway/ipc-json.h1
-rw-r--r--include/sway/ipc-server.h4
-rw-r--r--include/sway/layers.h2
-rw-r--r--include/sway/output.h1
-rw-r--r--include/swaybar/bar.h89
-rw-r--r--include/swaybar/config.h60
-rw-r--r--include/swaybar/event_loop.h4
-rw-r--r--include/swaybar/ipc.h25
-rw-r--r--include/swaybar/render.h22
-rw-r--r--include/swaybar/status_line.h65
-rw-r--r--meson.build3
-rw-r--r--sway/commands.c137
-rw-r--r--sway/commands/bar.c57
-rw-r--r--sway/commands/bar/activate_button.c8
-rw-r--r--sway/commands/bar/binding_mode_indicator.c27
-rw-r--r--sway/commands/bar/bindsym.c11
-rw-r--r--sway/commands/bar/colors.c124
-rw-r--r--sway/commands/bar/context_button.c8
-rw-r--r--sway/commands/bar/font.c21
-rw-r--r--sway/commands/bar/height.c20
-rw-r--r--sway/commands/bar/hidden_state.c73
-rw-r--r--sway/commands/bar/icon_theme.c8
-rw-r--r--sway/commands/bar/id.c30
-rw-r--r--sway/commands/bar/mode.c74
-rw-r--r--sway/commands/bar/modifier.c35
-rw-r--r--sway/commands/bar/output.c49
-rw-r--r--sway/commands/bar/pango_markup.c28
-rw-r--r--sway/commands/bar/position.c26
-rw-r--r--sway/commands/bar/secondary_button.c8
-rw-r--r--sway/commands/bar/separator_symbol.c20
-rw-r--r--sway/commands/bar/status_command.c20
-rw-r--r--sway/commands/bar/strip_workspace_numbers.c29
-rw-r--r--sway/commands/bar/swaybar_command.c20
-rw-r--r--sway/commands/bar/tray_output.c8
-rw-r--r--sway/commands/bar/tray_padding.c9
-rw-r--r--sway/commands/bar/workspace_buttons.c28
-rw-r--r--sway/commands/bar/wrap_scroll.c27
-rw-r--r--sway/commands/mode.c59
-rw-r--r--sway/commands/reload.c3
-rw-r--r--sway/commands/swaybg_command.c20
-rw-r--r--sway/config.c6
-rw-r--r--sway/config/bar.c238
-rw-r--r--sway/config/output.c25
-rw-r--r--sway/desktop/layer_shell.c35
-rw-r--r--sway/desktop/output.c8
-rw-r--r--sway/input/seat.c25
-rw-r--r--sway/ipc-json.c134
-rw-r--r--sway/ipc-server.c138
-rw-r--r--sway/meson.build28
-rw-r--r--sway/server.c6
-rw-r--r--sway/sway.5.txt3
-rw-r--r--sway/tree/container.c2
-rw-r--r--swaybar/bar.c464
-rw-r--r--swaybar/config.c53
-rw-r--r--swaybar/event_loop.c10
-rw-r--r--swaybar/ipc.c445
-rw-r--r--swaybar/main.c33
-rw-r--r--swaybar/meson.build27
-rw-r--r--swaybar/render.c517
-rw-r--r--swaybar/status_line.c565
-rw-r--r--swaybar/tray/dbus.c197
-rw-r--r--swaybar/tray/icon.c400
-rw-r--r--swaybar/tray/sni.c481
-rw-r--r--swaybar/tray/sni_watcher.c497
-rw-r--r--swaybar/tray/tray.c398
71 files changed, 2379 insertions, 3785 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 @@
1#define _POSIX_C_SOURCE 2 1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 2#include <stdio.h>
3#include <stdint.h> 3#include <stdint.h>
4#include <stdlib.h> 4#include <stdlib.h>
@@ -14,13 +14,31 @@ static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};
14static const size_t ipc_header_size = sizeof(ipc_magic)+8; 14static const size_t ipc_header_size = sizeof(ipc_magic)+8;
15 15
16char *get_socketpath(void) { 16char *get_socketpath(void) {
17 FILE *fp = popen("sway --get-socketpath", "r"); 17 const char *swaysock = getenv("SWAYSOCK");
18 if (!fp) { 18 if (swaysock) {
19 return NULL; 19 return strdup(swaysock);
20 } 20 }
21 char *line = read_line(fp); 21 FILE *fp = popen("sway --get-socketpath 2>/dev/null", "r");
22 pclose(fp); 22 if (fp) {
23 return line; 23 char *line = read_line(fp);
24 pclose(fp);
25 if (line && *line) {
26 return line;
27 }
28 }
29 const char *i3sock = getenv("I3SOCK");
30 if (i3sock) {
31 return strdup(i3sock);
32 }
33 fp = popen("i3 --get-socketpath 2>/dev/null", "r");
34 if (fp) {
35 char *line = read_line(fp);
36 pclose(fp);
37 if (line && *line) {
38 return line;
39 }
40 }
41 return NULL;
24} 42}
25 43
26int ipc_open_socket(const char *socket_path) { 44int ipc_open_socket(const char *socket_path) {
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 @@
1deps = [ 1deps = [
2 cairo, 2 cairo,
3 pango,
4 pangocairo,
3 wlroots 5 wlroots
4] 6]
5 7
@@ -14,6 +16,7 @@ lib_sway_common = static_library(
14 'ipc-client.c', 16 'ipc-client.c',
15 'log.c', 17 'log.c',
16 'list.c', 18 'list.c',
19 'pango.c',
17 'readline.c', 20 'readline.c',
18 'stringop.c', 21 'stringop.c',
19 'util.c' 22 'util.c'
diff --git a/common/pango.c b/common/pango.c
new file mode 100644
index 00000000..2ae7883c
--- /dev/null
+++ b/common/pango.c
@@ -0,0 +1,65 @@
1#include <cairo/cairo.h>
2#include <pango/pangocairo.h>
3#include <stdarg.h>
4#include <stdbool.h>
5#include <stdint.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9
10PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
11 const char *text, int32_t scale, bool markup) {
12 PangoLayout *layout = pango_cairo_create_layout(cairo);
13 PangoAttrList *attrs;
14 if (markup) {
15 char *buf;
16 pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, NULL);
17 pango_layout_set_markup(layout, buf, -1);
18 free(buf);
19 } else {
20 attrs = pango_attr_list_new();
21 pango_layout_set_text(layout, text, -1);
22 }
23 pango_attr_list_insert(attrs, pango_attr_scale_new(scale));
24 PangoFontDescription *desc = pango_font_description_from_string(font);
25 pango_layout_set_font_description(layout, desc);
26 pango_layout_set_single_paragraph_mode(layout, 1);
27 pango_layout_set_attributes(layout, attrs);
28 pango_attr_list_unref(attrs);
29 pango_font_description_free(desc);
30 return layout;
31}
32
33void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
34 int32_t scale, bool markup, const char *fmt, ...) {
35 static char buf[2048];
36
37 va_list args;
38 va_start(args, fmt);
39 if (vsnprintf(buf, 2048, fmt, args) >= 2048) {
40 strcpy(buf, "[buffer overflow]");
41 }
42 va_end(args);
43
44 PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
45 pango_cairo_update_layout(cairo, layout);
46 pango_layout_get_pixel_size(layout, width, height);
47 g_object_unref(layout);
48}
49
50void pango_printf(cairo_t *cairo, const char *font,
51 int32_t scale, bool markup, const char *fmt, ...) {
52 static char buf[2048];
53
54 va_list args;
55 va_start(args, fmt);
56 if (vsnprintf(buf, 2048, fmt, args) >= 2048) {
57 strcpy(buf, "[buffer overflow]");
58 }
59 va_end(args);
60
61 PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
62 pango_cairo_update_layout(cairo, layout);
63 pango_cairo_show_layout(cairo, layout);
64 g_object_unref(layout);
65}
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 @@
1#ifndef _SWAY_PANGO_H
2#define _SWAY_PANGO_H
3#include <stdarg.h>
4#include <stdbool.h>
5#include <stdint.h>
6#include <cairo/cairo.h>
7#include <pango/pangocairo.h>
8
9PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
10 const char *text, int32_t scale, bool markup);
11void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
12 int32_t scale, bool markup, const char *fmt, ...);
13void pango_printf(cairo_t *cairo, const char *font,
14 int32_t scale, bool markup, const char *fmt, ...);
15
16#endif
diff --git a/include/sway/commands.h b/include/sway/commands.h
index 9ff18823..1291d5fb 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -74,6 +74,9 @@ void free_cmd_results(struct cmd_results *results);
74 */ 74 */
75const char *cmd_results_to_json(struct cmd_results *results); 75const char *cmd_results_to_json(struct cmd_results *results);
76 76
77struct cmd_results *add_color(const char *name,
78 char *buffer, const char *color);
79
77typedef struct cmd_results *sway_cmd(int argc, char **argv); 80typedef struct cmd_results *sway_cmd(int argc, char **argv);
78 81
79sway_cmd cmd_assign; 82sway_cmd cmd_assign;
@@ -138,6 +141,7 @@ sway_cmd cmd_splith;
138sway_cmd cmd_splitt; 141sway_cmd cmd_splitt;
139sway_cmd cmd_splitv; 142sway_cmd cmd_splitv;
140sway_cmd cmd_sticky; 143sway_cmd cmd_sticky;
144sway_cmd cmd_swaybg_command;
141sway_cmd cmd_unmark; 145sway_cmd cmd_unmark;
142sway_cmd cmd_workspace; 146sway_cmd cmd_workspace;
143sway_cmd cmd_ws_auto_back_and_forth; 147sway_cmd cmd_ws_auto_back_and_forth;
diff --git a/include/sway/config.h b/include/sway/config.h
index 7fdd0be0..ac1105b4 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -1,17 +1,18 @@
1#ifndef _SWAY_CONFIG_H 1#ifndef _SWAY_CONFIG_H
2#define _SWAY_CONFIG_H 2#define _SWAY_CONFIG_H
3
4#define PID_WORKSPACE_TIMEOUT 60 3#define PID_WORKSPACE_TIMEOUT 60
5
6#include <libinput.h> 4#include <libinput.h>
7#include <stdint.h> 5#include <stdint.h>
8#include <string.h> 6#include <string.h>
7#include <time.h>
9#include <wlr/types/wlr_box.h> 8#include <wlr/types/wlr_box.h>
10#include <xkbcommon/xkbcommon.h> 9#include <xkbcommon/xkbcommon.h>
11#include <time.h>
12#include "list.h" 10#include "list.h"
13#include "tree/layout.h" 11#include "tree/layout.h"
14#include "tree/container.h" 12#include "tree/container.h"
13#include "wlr-layer-shell-unstable-v1-protocol.h"
14
15// TODO: Refactor this shit
15 16
16/** 17/**
17 * Describes a variable created via the `set` command. 18 * Describes a variable created via the `set` command.
@@ -152,24 +153,13 @@ struct bar_config {
152 char *id; 153 char *id;
153 uint32_t modifier; 154 uint32_t modifier;
154 list_t *outputs; 155 list_t *outputs;
155 //enum desktop_shell_panel_position position; // TODO 156 char *position;
156 list_t *bindings; 157 list_t *bindings;
157 char *status_command; 158 char *status_command;
158 bool pango_markup; 159 bool pango_markup;
159 char *swaybar_command; 160 char *swaybar_command;
160 char *font; 161 char *font;
161 int height; // -1 not defined 162 int height; // -1 not defined
162
163#ifdef ENABLE_TRAY
164 // Tray
165 char *tray_output;
166 char *icon_theme;
167 uint32_t tray_padding;
168 uint32_t activate_button;
169 uint32_t context_button;
170 uint32_t secondary_button;
171#endif
172
173 bool workspace_buttons; 163 bool workspace_buttons;
174 bool wrap_scroll; 164 bool wrap_scroll;
175 char *separator_symbol; 165 char *separator_symbol;
@@ -292,6 +282,7 @@ struct sway_config {
292 list_t *active_bar_modifiers; 282 list_t *active_bar_modifiers;
293 struct sway_mode *current_mode; 283 struct sway_mode *current_mode;
294 struct bar_config *current_bar; 284 struct bar_config *current_bar;
285 char *swaybg_command;
295 uint32_t floating_mod; 286 uint32_t floating_mod;
296 uint32_t dragging_key; 287 uint32_t dragging_key;
297 uint32_t resizing_key; 288 uint32_t resizing_key;
@@ -420,11 +411,6 @@ void apply_output_config(struct output_config *oc,
420 struct sway_container *output); 411 struct sway_container *output);
421void free_output_config(struct output_config *oc); 412void free_output_config(struct output_config *oc);
422 413
423/**
424 * Updates the list of active bar modifiers
425 */
426void update_active_bar_modifiers(void);
427
428int workspace_output_cmp_workspace(const void *a, const void *b); 414int workspace_output_cmp_workspace(const void *a, const void *b);
429 415
430int sway_binding_cmp(const void *a, const void *b); 416int sway_binding_cmp(const void *a, const void *b);
@@ -433,27 +419,17 @@ int sway_binding_cmp_keys(const void *a, const void *b);
433void free_sway_binding(struct sway_binding *sb); 419void free_sway_binding(struct sway_binding *sb);
434struct sway_binding *sway_binding_dup(struct sway_binding *sb); 420struct sway_binding *sway_binding_dup(struct sway_binding *sb);
435 421
436int sway_mouse_binding_cmp(const void *a, const void *b); 422/* Bar stuff */
437int sway_mouse_binding_cmp_qsort(const void *a, const void *b);
438int sway_mouse_binding_cmp_buttons(const void *a, const void *b);
439void free_sway_mouse_binding(struct sway_mouse_binding *smb);
440
441void load_swaybars(); 423void load_swaybars();
424void invoke_swaybar(struct bar_config *bar);
442void terminate_swaybg(pid_t pid); 425void terminate_swaybg(pid_t pid);
443
444/**
445 * Allocate and initialize default bar configuration.
446 */
447struct bar_config *default_bar_config(void); 426struct bar_config *default_bar_config(void);
427void free_bar_config(struct bar_config *bar);
448 428
449/** 429/* Global config singleton. */
450 * Global config singleton.
451 */
452extern struct sway_config *config; 430extern struct sway_config *config;
453 431
454/** 432/* Config file currently being read */
455 * Config file currently being read.
456 */
457extern const char *current_config_path; 433extern const char *current_config_path;
458 434
459#endif 435#endif
diff --git a/include/sway/ipc-json.h b/include/sway/ipc-json.h
index 3d2fdc4f..7d87d377 100644
--- a/include/sway/ipc-json.h
+++ b/include/sway/ipc-json.h
@@ -9,5 +9,6 @@ json_object *ipc_json_get_version();
9json_object *ipc_json_describe_container(struct sway_container *c); 9json_object *ipc_json_describe_container(struct sway_container *c);
10json_object *ipc_json_describe_container_recursive(struct sway_container *c); 10json_object *ipc_json_describe_container_recursive(struct sway_container *c);
11json_object *ipc_json_describe_input(struct sway_input_device *device); 11json_object *ipc_json_describe_input(struct sway_input_device *device);
12json_object *ipc_json_describe_bar_config(struct bar_config *bar);
12 13
13#endif 14#endif
diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h
index d73006dc..c3389fe8 100644
--- a/include/sway/ipc-server.h
+++ b/include/sway/ipc-server.h
@@ -12,6 +12,10 @@ void ipc_terminate(void);
12 12
13struct sockaddr_un *ipc_user_sockaddr(void); 13struct sockaddr_un *ipc_user_sockaddr(void);
14 14
15void ipc_event_workspace(struct sway_container *old,
16 struct sway_container *new, const char *change);
15void ipc_event_window(struct sway_container *window, const char *change); 17void ipc_event_window(struct sway_container *window, const char *change);
18void ipc_event_barconfig_update(struct bar_config *bar);
19void ipc_event_mode(const char *mode);
16 20
17#endif 21#endif
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 {
14 struct wl_listener unmap; 14 struct wl_listener unmap;
15 struct wl_listener surface_commit; 15 struct wl_listener surface_commit;
16 struct wl_listener output_destroy; 16 struct wl_listener output_destroy;
17 struct wl_listener output_mode;
18 struct wl_listener output_transform;
19 17
20 bool configured; 18 bool configured;
21 struct wlr_box geo; 19 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 {
21 struct wl_listener frame; 21 struct wl_listener frame;
22 struct wl_listener destroy; 22 struct wl_listener destroy;
23 struct wl_listener mode; 23 struct wl_listener mode;
24 struct wl_listener transform;
24 25
25 pid_t bg_pid; 26 pid_t bg_pid;
26}; 27};
diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h
index 50d36e76..1bf2ea2d 100644
--- a/include/swaybar/bar.h
+++ b/include/swaybar/bar.h
@@ -1,36 +1,49 @@
1#ifndef _SWAYBAR_BAR_H 1#ifndef _SWAYBAR_BAR_H
2#define _SWAYBAR_BAR_H 2#define _SWAYBAR_BAR_H
3 3#include <wayland-client.h>
4#include "client/registry.h" 4#include "pool-buffer.h"
5#include "client/window.h"
6#include "list.h" 5#include "list.h"
7 6
8struct bar { 7struct swaybar_config;
9 struct config *config; 8struct swaybar_output;
9struct swaybar_workspace;
10
11struct swaybar {
12 struct wl_display *display;
13 struct wl_compositor *compositor;
14 struct zwlr_layer_shell_v1 *layer_shell;
15 struct wl_shm *shm;
16
17 struct swaybar_config *config;
18 struct swaybar_output *focused_output;
10 struct status_line *status; 19 struct status_line *status;
11 list_t *outputs;
12 struct output *focused_output;
13 20
14 int ipc_event_socketfd; 21 int ipc_event_socketfd;
15 int ipc_socketfd; 22 int ipc_socketfd;
16 int status_read_fd; 23
17 int status_write_fd; 24 struct wl_list outputs;
18 pid_t status_command_pid;
19}; 25};
20 26
21struct output { 27struct swaybar_output {
22 struct window *window; 28 struct wl_list link;
23 struct registry *registry; 29 struct swaybar *bar;
24 list_t *workspaces; 30 struct wl_output *output;
25#ifdef ENABLE_TRAY 31 struct wl_surface *surface;
26 list_t *items; 32 struct zwlr_layer_surface_v1 *layer_surface;
27#endif 33
34 struct wl_list workspaces;
35
28 char *name; 36 char *name;
29 int idx; 37 size_t index;
30 bool focused; 38 bool focused;
39
40 uint32_t width, height;
41 struct pool_buffer buffers[2];
42 struct pool_buffer *current_buffer;
31}; 43};
32 44
33struct workspace { 45struct swaybar_workspace {
46 struct wl_list link;
34 int num; 47 int num;
35 char *name; 48 char *name;
36 bool focused; 49 bool focused;
@@ -38,35 +51,11 @@ struct workspace {
38 bool urgent; 51 bool urgent;
39}; 52};
40 53
41/** Global bar state */ 54// TODO: Rename stuff to match wlroots conventions (init/create/etc)
42extern struct bar swaybar; 55void bar_setup(struct swaybar *bar,
56 const char *socket_path,
57 const char *bar_id);
58void bar_run(struct swaybar *bar);
59void bar_teardown(struct swaybar *bar);
43 60
44/** True if sway needs to render */ 61#endif
45extern bool dirty;
46
47/**
48 * Setup bar.
49 */
50void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id);
51
52/**
53 * Create new output struct from name.
54 */
55struct output *new_output(const char *name);
56
57/**
58 * Bar mainloop.
59 */
60void bar_run(struct bar *bar);
61
62/**
63 * free workspace list.
64 */
65void free_workspaces(list_t *workspaces);
66
67/**
68 * Teardown bar.
69 */
70void bar_teardown(struct bar *bar);
71
72#endif /* _SWAYBAR_BAR_H */
diff --git a/include/swaybar/config.h b/include/swaybar/config.h
index 651f0ee3..7634cb16 100644
--- a/include/swaybar/config.h
+++ b/include/swaybar/config.h
@@ -1,49 +1,35 @@
1#ifndef _SWAYBAR_CONFIG_H 1#ifndef _SWAYBAR_CONFIG_H
2#define _SWAYBAR_CONFIG_H 2#define _SWAYBAR_CONFIG_H
3
4#include <stdint.h>
5#include <stdbool.h> 3#include <stdbool.h>
6 4#include <stdint.h>
7#include "list.h" 5#include <wayland-client.h>
8#include "util.h" 6#include "util.h"
9 7
10/**
11 * Colors for a box with background, border and text colors.
12 */
13struct box_colors { 8struct box_colors {
14 uint32_t border; 9 uint32_t border;
15 uint32_t background; 10 uint32_t background;
16 uint32_t text; 11 uint32_t text;
17}; 12};
18 13
19/** 14struct config_output {
20 * Swaybar config. 15 struct wl_list link;
21 */ 16 char *name;
22struct config { 17 size_t index;
18};
19
20struct swaybar_config {
23 char *status_command; 21 char *status_command;
24 bool pango_markup; 22 bool pango_markup;
25 uint32_t position; 23 uint32_t position; // zwlr_layer_surface_v1_anchor
26 char *font; 24 char *font;
27 char *sep_symbol; 25 char *sep_symbol;
28 char *mode; 26 char *mode;
27 bool mode_pango_markup;
29 bool strip_workspace_numbers; 28 bool strip_workspace_numbers;
30 bool binding_mode_indicator; 29 bool binding_mode_indicator;
31 bool wrap_scroll; 30 bool wrap_scroll;
32 bool workspace_buttons; 31 bool workspace_buttons;
33 bool all_outputs; 32 struct wl_list outputs;
34 list_t *outputs;
35
36#ifdef ENABLE_TRAY
37 // Tray
38 char *tray_output;
39 char *icon_theme;
40
41 uint32_t tray_padding;
42 uint32_t activate_button;
43 uint32_t context_button;
44 uint32_t secondary_button;
45#endif
46
47 int height; 33 int height;
48 34
49 struct { 35 struct {
@@ -63,24 +49,8 @@ struct config {
63 } colors; 49 } colors;
64}; 50};
65 51
66/** 52struct swaybar_config *init_config();
67 * Parse position top|bottom|left|right. 53void free_config(struct swaybar_config *config);
68 */
69uint32_t parse_position(const char *position); 54uint32_t parse_position(const char *position);
70 55
71/** 56#endif
72 * Parse font.
73 */
74char *parse_font(const char *font);
75
76/**
77 * Initialize default sway config.
78 */
79struct config *init_config();
80
81/**
82 * Free config struct.
83 */
84void free_config(struct config *config);
85
86#endif /* _SWAYBAR_CONFIG_H */
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 @@
1#ifndef _SWAYBAR_EVENT_LOOP_H 1#ifndef _SWAYBAR_EVENT_LOOP_H
2#define _SWAYBAR_EVENT_LOOP_H 2#define _SWAYBAR_EVENT_LOOP_H
3
4#include <stdbool.h> 3#include <stdbool.h>
5#include <time.h> 4#include <time.h>
6 5
@@ -23,4 +22,5 @@ bool remove_timer(timer_t timer);
23void event_loop_poll(); 22void event_loop_poll();
24 23
25void init_event_loop(); 24void init_event_loop();
26#endif /*_SWAYBAR_EVENT_LOOP_H */ 25
26#endif
diff --git a/include/swaybar/ipc.h b/include/swaybar/ipc.h
index c11931d0..278baef0 100644
--- a/include/swaybar/ipc.h
+++ b/include/swaybar/ipc.h
@@ -1,23 +1,10 @@
1#ifndef _SWAYBAR_IPC_H 1#ifndef _SWAYBAR_IPC_H
2#define _SWAYBAR_IPC_H 2#define _SWAYBAR_IPC_H
3#include <stdbool.h>
4#include "swaybar/bar.h"
3 5
4#include "bar.h" 6void ipc_initialize(struct swaybar *bar, const char *bar_id);
5 7bool handle_ipc_event(struct swaybar *bar);
6/** 8void ipc_get_workspaces(struct swaybar *bar);
7 * Initialize ipc connection to sway and get sway state, outputs, bar_config.
8 */
9void ipc_bar_init(struct bar *bar, const char *bar_id);
10
11/**
12 * Handle ipc event from sway.
13 */
14bool handle_ipc_event(struct bar *bar);
15
16
17/**
18 * Send workspace command to sway
19 */
20void ipc_send_workspace_command(const char *workspace_name);
21
22#endif /* _SWAYBAR_IPC_H */
23 9
10#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 @@
1#ifndef _SWAYBAR_RENDER_H 1#ifndef _SWAYBAR_RENDER_H
2#define _SWAYBAR_RENDER_H 2#define _SWAYBAR_RENDER_H
3 3
4#include "config.h" 4struct swaybar;
5#include "bar.h" 5struct swaybar_output;
6struct swaybar_config;
6 7
7/** 8void render_frame(struct swaybar *bar, struct swaybar_output *output);
8 * Render swaybar.
9 */
10void render(struct output *output, struct config *config, struct status_line *line);
11 9
12/** 10#endif
13 * Set window height and modify internal spacing accordingly.
14 */
15void set_window_height(struct window *window, int height);
16
17/**
18 * Compute the size of a workspace name
19 */
20void workspace_button_size(struct window *window, const char *workspace_name, int *width, int *height);
21
22#endif /* _SWAYBAR_RENDER_H */
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 @@
1#ifndef _SWAYBAR_STATUS_LINE_H 1#ifndef _SWAYBAR_STATUS_LINE_H
2#define _SWAYBAR_STATUS_LINE_H 2#define _SWAYBAR_STATUS_LINE_H
3
4#include <stdint.h> 3#include <stdint.h>
4#include <stdio.h>
5#include <stdbool.h> 5#include <stdbool.h>
6
7#include "list.h"
8#include "bar.h" 6#include "bar.h"
9 7
10typedef enum {UNDEF, TEXT, I3BAR} command_protocol; 8enum status_protocol {
9 PROTOCOL_UNDEF,
10 PROTOCOL_TEXT,
11 PROTOCOL_I3BAR,
12};
11 13
12struct status_line { 14struct status_line {
13 list_t *block_line; 15 pid_t pid;
14 const char *text_line; 16 int read_fd, write_fd;
15 command_protocol protocol; 17 FILE *read, *write;
16 bool click_events;
17};
18 18
19struct status_block { 19 enum status_protocol protocol;
20 char *full_text, *short_text, *align; 20 const char *text;
21 bool urgent;
22 uint32_t color;
23 int min_width;
24 char *name, *instance;
25 bool separator;
26 int separator_block_width;
27 bool markup;
28 // Airblader features
29 uint32_t background;
30 uint32_t border;
31 int border_top;
32 int border_bottom;
33 int border_left;
34 int border_right;
35 21
36 // Set during rendering 22 char *buffer;
37 int x; 23 size_t buffer_size;
38 int width;
39}; 24};
40 25
41/** 26struct status_line *status_line_init(char *cmd);
42 * Initialize status line struct. 27void status_line_free(struct status_line *status);
43 */ 28bool handle_status_readable(struct status_line *status);
44struct status_line *init_status_line();
45
46/**
47 * handle status line activity.
48 */
49bool handle_status_line(struct bar *bar);
50
51/**
52 * Handle mouse clicks.
53 */
54bool status_line_mouse_event(struct bar *bar, int x, int y, uint32_t button);
55
56/**
57 * Free status line struct.
58 */
59void free_status_line(struct status_line *line);
60 29
61#endif /* _SWAYBAR_STATUS_LINE_H */ 30#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')
35libcap = dependency('libcap') 35libcap = dependency('libcap')
36libinput = dependency('libinput') 36libinput = dependency('libinput')
37math = cc.find_library('m') 37math = cc.find_library('m')
38rt = cc.find_library('rt')
38git = find_program('git', required: false) 39git = find_program('git', required: false)
39a2x = find_program('a2x', required: false) 40a2x = find_program('a2x', required: false)
40 41
@@ -99,8 +100,10 @@ subdir('protocols')
99subdir('common') 100subdir('common')
100subdir('sway') 101subdir('sway')
101subdir('swaymsg') 102subdir('swaymsg')
103
102subdir('client') 104subdir('client')
103subdir('swaybg') 105subdir('swaybg')
106subdir('swaybar')
104 107
105config = configuration_data() 108config = configuration_data()
106config.set('sysconfdir', join_paths(prefix, sysconfdir)) 109config.set('sysconfdir', join_paths(prefix, sysconfdir))
diff --git a/sway/commands.c b/sway/commands.c
index b52eb200..bcc777ed 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -91,68 +91,69 @@ void apply_seat_config(struct seat_config *seat) {
91 sway_input_manager_apply_seat_config(input_manager, seat); 91 sway_input_manager_apply_seat_config(input_manager, seat);
92} 92}
93 93
94/** 94/* Keep alphabetized */
95 * Check and add color to buffer.
96 *
97 * return error object, or NULL if color is valid.
98 */
99struct cmd_results *add_color(const char *name, char *buffer, const char *color) {
100 int len = strlen(color);
101 if (len != 7 && len != 9) {
102 return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
103 }
104
105 if (color[0] != '#') {
106 return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
107 }
108
109 int i;
110 for (i = 1; i < len; ++i) {
111 if (!isxdigit(color[i])) {
112 return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
113 }
114 }
115
116 // copy color to buffer
117 strncpy(buffer, color, len);
118 // add default alpha channel if color was defined without it
119 if (len == 7) {
120 buffer[7] = 'f';
121 buffer[8] = 'f';
122 }
123 buffer[9] = '\0';
124
125 return NULL;
126}
127
128/**
129 * handlers that can run in either config or command context
130 * Keep alphabetized
131 */
132static struct cmd_handler handlers[] = { 95static struct cmd_handler handlers[] = {
96 { "bar", cmd_bar },
133 { "bindcode", cmd_bindcode }, 97 { "bindcode", cmd_bindcode },
134 { "bindsym", cmd_bindsym }, 98 { "bindsym", cmd_bindsym },
135 { "exec", cmd_exec }, 99 { "exec", cmd_exec },
136 { "exec_always", cmd_exec_always }, 100 { "exec_always", cmd_exec_always },
137 { "include", cmd_include }, 101 { "include", cmd_include },
138 { "input", cmd_input }, 102 { "input", cmd_input },
103 { "mode", cmd_mode },
139 { "output", cmd_output }, 104 { "output", cmd_output },
140 { "seat", cmd_seat }, 105 { "seat", cmd_seat },
141 { "workspace", cmd_workspace }, 106 { "workspace", cmd_workspace },
142}; 107};
143 108
144/** 109static struct cmd_handler bar_handlers[] = {
145 * Commands that can *only* run in the config loading context 110 { "activate_button", bar_cmd_activate_button },
146 * Keep alphabetized 111 { "binding_mode_indicator", bar_cmd_binding_mode_indicator },
147 */ 112 { "bindsym", bar_cmd_bindsym },
113 { "colors", bar_cmd_colors },
114 { "context_button", bar_cmd_context_button },
115 { "font", bar_cmd_font },
116 { "height", bar_cmd_height },
117 { "hidden_state", bar_cmd_hidden_state },
118 { "icon_theme", bar_cmd_icon_theme },
119 { "id", bar_cmd_id },
120 { "mode", bar_cmd_mode },
121 { "modifier", bar_cmd_modifier },
122 { "output", bar_cmd_output },
123 { "pango_markup", bar_cmd_pango_markup },
124 { "position", bar_cmd_position },
125 { "secondary_button", bar_cmd_secondary_button },
126 { "separator_symbol", bar_cmd_separator_symbol },
127 { "status_command", bar_cmd_status_command },
128 { "strip_workspace_numbers", bar_cmd_strip_workspace_numbers },
129 { "swaybar_command", bar_cmd_swaybar_command },
130 { "tray_output", bar_cmd_tray_output },
131 { "tray_padding", bar_cmd_tray_padding },
132 { "workspace_buttons", bar_cmd_workspace_buttons },
133 { "wrap_scroll", bar_cmd_wrap_scroll },
134};
135
136static struct cmd_handler bar_colors_handlers[] = {
137 { "active_workspace", bar_colors_cmd_active_workspace },
138 { "background", bar_colors_cmd_background },
139 { "binding_mode", bar_colors_cmd_binding_mode },
140 { "focused_background", bar_colors_cmd_focused_background },
141 { "focused_separator", bar_colors_cmd_focused_separator },
142 { "focused_statusline", bar_colors_cmd_focused_statusline },
143 { "focused_workspace", bar_colors_cmd_focused_workspace },
144 { "inactive_workspace", bar_colors_cmd_inactive_workspace },
145 { "separator", bar_colors_cmd_separator },
146 { "statusline", bar_colors_cmd_statusline },
147 { "urgent_workspace", bar_colors_cmd_urgent_workspace },
148};
149
150/* Config-time only commands. Keep alphabetized */
148static struct cmd_handler config_handlers[] = { 151static struct cmd_handler config_handlers[] = {
149 { "set", cmd_set }, 152 { "set", cmd_set },
153 { "swaybg_command", cmd_swaybg_command },
150}; 154};
151 155
152/** 156/* Runtime-only commands. Keep alphabetized */
153 * Commands that can *not* run in the config loading context
154 * Keep alphabetized
155 */
156static struct cmd_handler command_handlers[] = { 157static struct cmd_handler command_handlers[] = {
157 { "exit", cmd_exit }, 158 { "exit", cmd_exit },
158 { "focus", cmd_focus }, 159 { "focus", cmd_focus },
@@ -200,13 +201,19 @@ static struct cmd_handler *find_handler(char *line, enum cmd_status block) {
200 201
201 bool config_loading = config->reading || !config->active; 202 bool config_loading = config->reading || !config->active;
202 203
203 if (block == CMD_BLOCK_INPUT) { 204 if (block == CMD_BLOCK_BAR) {
204 // input commands can run in either context 205 return bsearch(&d, bar_handlers,
206 sizeof(bar_handlers) / sizeof(struct cmd_handler),
207 sizeof(struct cmd_handler), handler_compare);
208 } else if (block == CMD_BLOCK_BAR_COLORS) {
209 return bsearch(&d, bar_colors_handlers,
210 sizeof(bar_colors_handlers) / sizeof(struct cmd_handler),
211 sizeof(struct cmd_handler), handler_compare);
212 } else if (block == CMD_BLOCK_INPUT) {
205 return bsearch(&d, input_handlers, 213 return bsearch(&d, input_handlers,
206 sizeof(input_handlers) / sizeof(struct cmd_handler), 214 sizeof(input_handlers) / sizeof(struct cmd_handler),
207 sizeof(struct cmd_handler), handler_compare); 215 sizeof(struct cmd_handler), handler_compare);
208 } else if (block == CMD_BLOCK_SEAT) { 216 } else if (block == CMD_BLOCK_SEAT) {
209 // seat commands can run in either context
210 return bsearch(&d, seat_handlers, 217 return bsearch(&d, seat_handlers,
211 sizeof(seat_handlers) / sizeof(struct cmd_handler), 218 sizeof(seat_handlers) / sizeof(struct cmd_handler),
212 sizeof(struct cmd_handler), handler_compare); 219 sizeof(struct cmd_handler), handler_compare);
@@ -558,3 +565,35 @@ const char *cmd_results_to_json(struct cmd_results *results) {
558 free(root); 565 free(root);
559 return json; 566 return json;
560} 567}
568
569/**
570 * Check and add color to buffer.
571 *
572 * return error object, or NULL if color is valid.
573 */
574struct cmd_results *add_color(const char *name,
575 char *buffer, const char *color) {
576 int len = strlen(color);
577 if (len != 7 && len != 9) {
578 return cmd_results_new(CMD_INVALID, name,
579 "Invalid color definition %s", color);
580 }
581 if (color[0] != '#') {
582 return cmd_results_new(CMD_INVALID, name,
583 "Invalid color definition %s", color);
584 }
585 for (int i = 1; i < len; ++i) {
586 if (!isxdigit(color[i])) {
587 return cmd_results_new(CMD_INVALID, name,
588 "Invalid color definition %s", color);
589 }
590 }
591 strncpy(buffer, color, len);
592 // add default alpha channel if color was defined without it
593 if (len == 7) {
594 buffer[7] = 'f';
595 buffer[8] = 'f';
596 }
597 buffer[9] = '\0';
598 return NULL;
599}
diff --git a/sway/commands/bar.c b/sway/commands/bar.c
new file mode 100644
index 00000000..ff111163
--- /dev/null
+++ b/sway/commands/bar.c
@@ -0,0 +1,57 @@
1#include <string.h>
2#include <strings.h>
3#include <wlr/util/log.h>
4#include "sway/commands.h"
5#include "sway/config.h"
6#include "util.h"
7
8struct cmd_results *cmd_bar(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "bar", EXPECTED_AT_LEAST, 1))) {
11 return error;
12 }
13
14 if (config->reading && strcmp("{", argv[0]) != 0) {
15 return cmd_results_new(CMD_INVALID, "bar",
16 "Expected '{' at start of bar config definition.");
17 }
18
19 if (!config->reading) {
20 if (argc > 1) {
21 if (strcasecmp("mode", argv[0]) == 0) {
22 return bar_cmd_mode(argc-1, argv + 1);
23 }
24
25 if (strcasecmp("hidden_state", argv[0]) == 0) {
26 return bar_cmd_hidden_state(argc-1, argv + 1);
27 }
28 }
29 return cmd_results_new(CMD_FAILURE, "bar", "Can only be used in config file.");
30 }
31
32 // Create new bar with default values
33 struct bar_config *bar = default_bar_config();
34 if (!bar) {
35 return cmd_results_new(CMD_FAILURE, "bar", "Unable to allocate bar state");
36 }
37
38 // set bar id
39 for (int i = 0; i < config->bars->length; ++i) {
40 if (bar == config->bars->items[i]) {
41 const int len = 5 + numlen(i); // "bar-" + i + \0
42 bar->id = malloc(len * sizeof(char));
43 if (bar->id) {
44 snprintf(bar->id, len, "bar-%d", i);
45 } else {
46 return cmd_results_new(CMD_FAILURE,
47 "bar", "Unable to allocate bar ID");
48 }
49 break;
50 }
51 }
52
53 // Set current bar
54 config->current_bar = bar;
55 wlr_log(L_DEBUG, "Configuring bar %s", bar->id);
56 return cmd_results_new(CMD_BLOCK_BAR, NULL, NULL);
57}
diff --git a/sway/commands/bar/activate_button.c b/sway/commands/bar/activate_button.c
new file mode 100644
index 00000000..7310e7ec
--- /dev/null
+++ b/sway/commands/bar/activate_button.c
@@ -0,0 +1,8 @@
1#include <stdlib.h>
2#include "sway/commands.h"
3#include "log.h"
4
5struct cmd_results *bar_cmd_activate_button(int argc, char **argv) {
6 // TODO TRAY
7 return cmd_results_new(CMD_INVALID, "activate_button", "TODO TRAY");
8}
diff --git a/sway/commands/bar/binding_mode_indicator.c b/sway/commands/bar/binding_mode_indicator.c
new file mode 100644
index 00000000..3ba5f33f
--- /dev/null
+++ b/sway/commands/bar/binding_mode_indicator.c
@@ -0,0 +1,27 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc,
9 "binding_mode_indicator", EXPECTED_EQUAL_TO, 1))) {
10 return error;
11 }
12 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE,
14 "binding_mode_indicator", "No bar defined.");
15 }
16 if (strcasecmp("yes", argv[0]) == 0) {
17 config->current_bar->binding_mode_indicator = true;
18 wlr_log(L_DEBUG, "Enabling binding mode indicator on bar: %s",
19 config->current_bar->id);
20 } else if (strcasecmp("no", argv[0]) == 0) {
21 config->current_bar->binding_mode_indicator = false;
22 wlr_log(L_DEBUG, "Disabling binding mode indicator on bar: %s",
23 config->current_bar->id);
24 }
25 return cmd_results_new(CMD_INVALID, "binding_mode_indicator",
26 "Invalid value %s", argv[0]);
27}
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 @@
1#include <stdlib.h>
2#include <string.h>
3#include "sway/commands.h"
4#include "sway/config.h"
5#include "list.h"
6#include "log.h"
7#include "stringop.h"
8
9struct cmd_results *bar_cmd_bindsym(int argc, char **argv) {
10 return cmd_results_new(CMD_FAILURE, "bindsym", "TODO"); // TODO
11}
diff --git a/sway/commands/bar/colors.c b/sway/commands/bar/colors.c
new file mode 100644
index 00000000..17ba9b7c
--- /dev/null
+++ b/sway/commands/bar/colors.c
@@ -0,0 +1,124 @@
1#include <string.h>
2#include "sway/commands.h"
3
4static struct cmd_results *parse_single_color(char **color,
5 const char *cmd_name, int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 1))) {
8 return error;
9 }
10 if (!*color && !(*color = malloc(10))) {
11 return NULL;
12 }
13 error = add_color(cmd_name, *color, argv[0]);
14 if (error) {
15 return error;
16 }
17 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
18}
19
20static struct cmd_results *parse_three_colors(char ***colors,
21 const char *cmd_name, int argc, char **argv) {
22 struct cmd_results *error = NULL;
23 if (argc != 3) {
24 return cmd_results_new(CMD_INVALID,
25 cmd_name, "Requires exactly three color values");
26 }
27 for (size_t i = 0; i < 3; i++) {
28 if (!*colors[i] && !(*(colors[i]) = malloc(10))) {
29 return NULL;
30 }
31 error = add_color(cmd_name, *(colors[i]), argv[i]);
32 if (error) {
33 return error;
34 }
35 }
36 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
37}
38
39struct cmd_results *bar_cmd_colors(int argc, char **argv) {
40 struct cmd_results *error = NULL;
41 if ((error = checkarg(argc, "colors", EXPECTED_EQUAL_TO, 1))) {
42 return error;
43 }
44 if (strcmp("{", argv[0]) != 0) {
45 return cmd_results_new(CMD_INVALID, "colors",
46 "Expected '{' at the start of colors config definition.");
47 }
48 return cmd_results_new(CMD_BLOCK_BAR_COLORS, NULL, NULL);
49}
50
51struct cmd_results *bar_colors_cmd_active_workspace(int argc, char **argv) {
52 char **colors[3] = {
53 &(config->current_bar->colors.active_workspace_border),
54 &(config->current_bar->colors.active_workspace_bg),
55 &(config->current_bar->colors.active_workspace_text)
56 };
57 return parse_three_colors(colors, "active_workspace", argc, argv);
58}
59
60struct cmd_results *bar_colors_cmd_background(int argc, char **argv) {
61 return parse_single_color(&(config->current_bar->colors.background),
62 "background", argc, argv);
63}
64
65struct cmd_results *bar_colors_cmd_focused_background(int argc, char **argv) {
66 return parse_single_color(&(config->current_bar->colors.focused_background),
67 "focused_background", argc, argv);
68}
69
70struct cmd_results *bar_colors_cmd_binding_mode(int argc, char **argv) {
71 char **colors[3] = {
72 &(config->current_bar->colors.binding_mode_border),
73 &(config->current_bar->colors.binding_mode_bg),
74 &(config->current_bar->colors.binding_mode_text)
75 };
76 return parse_three_colors(colors, "binding_mode", argc, argv);
77}
78
79struct cmd_results *bar_colors_cmd_focused_workspace(int argc, char **argv) {
80 char **colors[3] = {
81 &(config->current_bar->colors.focused_workspace_border),
82 &(config->current_bar->colors.focused_workspace_bg),
83 &(config->current_bar->colors.focused_workspace_text)
84 };
85 return parse_three_colors(colors, "focused_workspace", argc, argv);
86}
87
88struct cmd_results *bar_colors_cmd_inactive_workspace(int argc, char **argv) {
89 char **colors[3] = {
90 &(config->current_bar->colors.inactive_workspace_border),
91 &(config->current_bar->colors.inactive_workspace_bg),
92 &(config->current_bar->colors.inactive_workspace_text)
93 };
94 return parse_three_colors(colors, "inactive_workspace", argc, argv);
95}
96
97struct cmd_results *bar_colors_cmd_separator(int argc, char **argv) {
98 return parse_single_color(&(config->current_bar->colors.separator),
99 "separator", argc, argv);
100}
101
102struct cmd_results *bar_colors_cmd_focused_separator(int argc, char **argv) {
103 return parse_single_color(&(config->current_bar->colors.focused_separator),
104 "focused_separator", argc, argv);
105}
106
107struct cmd_results *bar_colors_cmd_statusline(int argc, char **argv) {
108 return parse_single_color(&(config->current_bar->colors.statusline),
109 "statusline", argc, argv);
110}
111
112struct cmd_results *bar_colors_cmd_focused_statusline(int argc, char **argv) {
113 return parse_single_color(&(config->current_bar->colors.focused_separator),
114 "focused_separator", argc, argv);
115}
116
117struct cmd_results *bar_colors_cmd_urgent_workspace(int argc, char **argv) {
118 char **colors[3] = {
119 &(config->current_bar->colors.urgent_workspace_border),
120 &(config->current_bar->colors.urgent_workspace_bg),
121 &(config->current_bar->colors.urgent_workspace_text)
122 };
123 return parse_three_colors(colors, "urgent_workspace", argc, argv);
124}
diff --git a/sway/commands/bar/context_button.c b/sway/commands/bar/context_button.c
new file mode 100644
index 00000000..3b76885a
--- /dev/null
+++ b/sway/commands/bar/context_button.c
@@ -0,0 +1,8 @@
1#include <stdlib.h>
2#include "sway/commands.h"
3#include "log.h"
4
5struct cmd_results *bar_cmd_context_button(int argc, char **argv) {
6 // TODO TRAY
7 return cmd_results_new(CMD_INVALID, "context_button", "TODO TRAY");
8}
diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c
new file mode 100644
index 00000000..80b7a593
--- /dev/null
+++ b/sway/commands/bar/font.c
@@ -0,0 +1,21 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h>
3#include "sway/commands.h"
4#include "log.h"
5#include "stringop.h"
6
7struct cmd_results *bar_cmd_font(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "font", EXPECTED_AT_LEAST, 1))) {
10 return error;
11 }
12 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "font", "No bar defined.");
14 }
15 char *font = join_args(argv, argc);
16 free(config->current_bar->font);
17 config->current_bar->font = strdup(font);
18 wlr_log(L_DEBUG, "Settings font '%s' for bar: %s",
19 config->current_bar->font, config->current_bar->id);
20 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
21}
diff --git a/sway/commands/bar/height.c b/sway/commands/bar/height.c
new file mode 100644
index 00000000..3160caed
--- /dev/null
+++ b/sway/commands/bar/height.c
@@ -0,0 +1,20 @@
1#include <stdlib.h>
2#include <string.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_height(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "height", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11 int height = atoi(argv[0]);
12 if (height < 0) {
13 return cmd_results_new(CMD_INVALID, "height",
14 "Invalid height value: %s", argv[0]);
15 }
16 config->current_bar->height = height;
17 wlr_log(L_DEBUG, "Setting bar height to %d on bar: %s",
18 height, config->current_bar->id);
19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
20}
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c
new file mode 100644
index 00000000..6641f184
--- /dev/null
+++ b/sway/commands/bar/hidden_state.c
@@ -0,0 +1,73 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include <strings.h>
4#include "sway/commands.h"
5#include "sway/config.h"
6#include "sway/ipc-server.h"
7#include "log.h"
8
9static struct cmd_results *bar_set_hidden_state(struct bar_config *bar,
10 const char *hidden_state) {
11 char *old_state = bar->hidden_state;
12 if (strcasecmp("toggle", hidden_state) == 0 && !config->reading) {
13 if (strcasecmp("hide", bar->hidden_state) == 0) {
14 bar->hidden_state = strdup("show");
15 } else if (strcasecmp("show", bar->hidden_state) == 0) {
16 bar->hidden_state = strdup("hide");
17 }
18 } else if (strcasecmp("hide", hidden_state) == 0) {
19 bar->hidden_state = strdup("hide");
20 } else if (strcasecmp("show", hidden_state) == 0) {
21 bar->hidden_state = strdup("show");
22 } else {
23 return cmd_results_new(CMD_INVALID, "hidden_state",
24 "Invalid value %s", hidden_state);
25 }
26 if (strcmp(old_state, bar->hidden_state) != 0) {
27 if (!config->reading) {
28 ipc_event_barconfig_update(bar);
29 }
30 wlr_log(L_DEBUG, "Setting hidden_state: '%s' for bar: %s",
31 bar->hidden_state, bar->id);
32 }
33 // free old mode
34 free(old_state);
35 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
36}
37
38struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
39 struct cmd_results *error = NULL;
40 if ((error = checkarg(argc, "hidden_state", EXPECTED_AT_LEAST, 1))) {
41 return error;
42 }
43 if ((error = checkarg(argc, "hidden_state", EXPECTED_LESS_THAN, 3))) {
44 return error;
45 }
46 if (config->reading && argc > 1) {
47 return cmd_results_new(CMD_INVALID, "hidden_state",
48 "Unexpected value %s in config mode", argv[1]);
49 }
50
51 const char *state = argv[0];
52 if (config->reading) {
53 return bar_set_hidden_state(config->current_bar, state);
54 }
55
56 const char *id = NULL;
57 if (argc == 2) {
58 id = argv[1];
59 }
60 struct bar_config *bar;
61 for (int i = 0; i < config->bars->length; ++i) {
62 bar = config->bars->items[i];
63 if (id && strcmp(id, bar->id) == 0) {
64 return bar_set_hidden_state(bar, state);
65 }
66
67 error = bar_set_hidden_state(bar, state);
68 if (error) {
69 return error;
70 }
71 }
72 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
73}
diff --git a/sway/commands/bar/icon_theme.c b/sway/commands/bar/icon_theme.c
new file mode 100644
index 00000000..44cd3076
--- /dev/null
+++ b/sway/commands/bar/icon_theme.c
@@ -0,0 +1,8 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include "sway/commands.h"
4
5struct cmd_results *bar_cmd_icon_theme(int argc, char **argv) {
6 // TODO TRAY
7 return cmd_results_new(CMD_INVALID, "icon_theme", "TODO TRAY");
8}
diff --git a/sway/commands/bar/id.c b/sway/commands/bar/id.c
new file mode 100644
index 00000000..c1e56f03
--- /dev/null
+++ b/sway/commands/bar/id.c
@@ -0,0 +1,30 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_id(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "id", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11
12 const char *name = argv[0];
13 const char *oldname = config->current_bar->id;
14 // check if id is used by a previously defined bar
15 for (int i = 0; i < config->bars->length; ++i) {
16 struct bar_config *find = config->bars->items[i];
17 if (strcmp(name, find->id) == 0 && config->current_bar != find) {
18 return cmd_results_new(CMD_FAILURE, "id",
19 "Id '%s' already defined for another bar. Id unchanged (%s).",
20 name, oldname);
21 }
22 }
23
24 wlr_log(L_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name);
25
26 // free old bar id
27 free(config->current_bar->id);
28 config->current_bar->id = strdup(name);
29 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
30}
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c
new file mode 100644
index 00000000..34bb0a4f
--- /dev/null
+++ b/sway/commands/bar/mode.c
@@ -0,0 +1,74 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include <strings.h>
4#include "sway/commands.h"
5#include "sway/config.h"
6#include "sway/ipc-server.h"
7#include "log.h"
8
9static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode) {
10 char *old_mode = bar->mode;
11 if (strcasecmp("toggle", mode) == 0 && !config->reading) {
12 if (strcasecmp("dock", bar->mode) == 0) {
13 bar->mode = strdup("hide");
14 } else if (strcasecmp("hide", bar->mode) == 0) {
15 bar->mode = strdup("dock");
16 }
17 } else if (strcasecmp("dock", mode) == 0) {
18 bar->mode = strdup("dock");
19 } else if (strcasecmp("hide", mode) == 0) {
20 bar->mode = strdup("hide");
21 } else if (strcasecmp("invisible", mode) == 0) {
22 bar->mode = strdup("invisible");
23 } else {
24 return cmd_results_new(CMD_INVALID, "mode", "Invalid value %s", mode);
25 }
26
27 if (strcmp(old_mode, bar->mode) != 0) {
28 if (!config->reading) {
29 ipc_event_barconfig_update(bar);
30 }
31 wlr_log(L_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id);
32 }
33
34 // free old mode
35 free(old_mode);
36 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
37}
38
39struct cmd_results *bar_cmd_mode(int argc, char **argv) {
40 struct cmd_results *error = NULL;
41 if ((error = checkarg(argc, "mode", EXPECTED_AT_LEAST, 1))) {
42 return error;
43 }
44 if ((error = checkarg(argc, "mode", EXPECTED_LESS_THAN, 3))) {
45 return error;
46 }
47 if (config->reading && argc > 1) {
48 return cmd_results_new(CMD_INVALID,
49 "mode", "Unexpected value %s in config mode", argv[1]);
50 }
51
52 const char *mode = argv[0];
53 if (config->reading) {
54 return bar_set_mode(config->current_bar, mode);
55 }
56
57 const char *id = NULL;
58 if (argc == 2) {
59 id = argv[1];
60 }
61
62 struct bar_config *bar;
63 for (int i = 0; i < config->bars->length; ++i) {
64 bar = config->bars->items[i];
65 if (id && strcmp(id, bar->id) == 0) {
66 return bar_set_mode(bar, mode);
67 }
68 error = bar_set_mode(bar, mode);
69 if (error) {
70 return error;
71 }
72 }
73 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
74}
diff --git a/sway/commands/bar/modifier.c b/sway/commands/bar/modifier.c
new file mode 100644
index 00000000..7ba4b125
--- /dev/null
+++ b/sway/commands/bar/modifier.c
@@ -0,0 +1,35 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "log.h"
4#include "stringop.h"
5#include "util.h"
6
7struct cmd_results *bar_cmd_modifier(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "modifier", EXPECTED_EQUAL_TO, 1))) {
10 return error;
11 }
12
13 if (!config->current_bar) {
14 return cmd_results_new(CMD_FAILURE, "modifier", "No bar defined.");
15 }
16
17 uint32_t mod = 0;
18 list_t *split = split_string(argv[0], "+");
19 for (int i = 0; i < split->length; ++i) {
20 uint32_t tmp_mod;
21 if ((tmp_mod = get_modifier_mask_by_name(split->items[i])) > 0) {
22 mod |= tmp_mod;
23 continue;
24 } else {
25 free_flat_list(split);
26 return cmd_results_new(CMD_INVALID, "modifier",
27 "Unknown modifier '%s'", split->items[i]);
28 }
29 }
30 free_flat_list(split);
31 config->current_bar->modifier = mod;
32 wlr_log(L_DEBUG,
33 "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]);
34 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
35}
diff --git a/sway/commands/bar/output.c b/sway/commands/bar/output.c
new file mode 100644
index 00000000..f7ca0aa4
--- /dev/null
+++ b/sway/commands/bar/output.c
@@ -0,0 +1,49 @@
1#define _XOPEN_SOURCE 500
2#include <stdbool.h>
3#include <string.h>
4#include "sway/commands.h"
5#include "list.h"
6#include "log.h"
7
8struct cmd_results *bar_cmd_output(int argc, char **argv) {
9 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "output", EXPECTED_EQUAL_TO, 1))) {
11 return error;
12 }
13 if (!config->current_bar) {
14 return cmd_results_new(CMD_FAILURE, "output", "No bar defined.");
15 }
16
17 const char *output = argv[0];
18 list_t *outputs = config->current_bar->outputs;
19 if (!outputs) {
20 outputs = create_list();
21 config->current_bar->outputs = outputs;
22 }
23
24 bool add_output = true;
25 if (strcmp("*", output) == 0) {
26 // remove all previous defined outputs and replace with '*'
27 for (int i = 0; i < outputs->length; ++i) {
28 free(outputs->items[i]);
29 list_del(outputs, i);
30 }
31 } else {
32 // only add output if not already defined with either the same
33 // name or as '*'
34 for (int i = 0; i < outputs->length; ++i) {
35 const char *find = outputs->items[i];
36 if (strcmp("*", find) == 0 || strcmp(output, find) == 0) {
37 add_output = false;
38 break;
39 }
40 }
41 }
42
43 if (add_output) {
44 list_add(outputs, strdup(output));
45 wlr_log(L_DEBUG, "Adding bar: '%s' to output '%s'",
46 config->current_bar->id, output);
47 }
48 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
49}
diff --git a/sway/commands/bar/pango_markup.c b/sway/commands/bar/pango_markup.c
new file mode 100644
index 00000000..480af724
--- /dev/null
+++ b/sway/commands/bar/pango_markup.c
@@ -0,0 +1,28 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_pango_markup(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "pango_markup", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11 if (!config->current_bar) {
12 return cmd_results_new(CMD_FAILURE, "pango_markup", "No bar defined.");
13 }
14 if (strcasecmp("enabled", argv[0]) == 0) {
15 config->current_bar->pango_markup = true;
16 wlr_log(L_DEBUG, "Enabling pango markup for bar: %s",
17 config->current_bar->id);
18 } else if (strcasecmp("disabled", argv[0]) == 0) {
19 config->current_bar->pango_markup = false;
20 wlr_log(L_DEBUG, "Disabling pango markup for bar: %s",
21 config->current_bar->id);
22 } else {
23 error = cmd_results_new(CMD_INVALID, "pango_markup",
24 "Invalid value %s", argv[0]);
25 return error;
26 }
27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
28}
diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c
new file mode 100644
index 00000000..9c580483
--- /dev/null
+++ b/sway/commands/bar/position.c
@@ -0,0 +1,26 @@
1#define _POSIX_C_SOURCE 200809L
2#include <string.h>
3#include <strings.h>
4#include "sway/commands.h"
5#include "log.h"
6
7struct cmd_results *bar_cmd_position(int argc, char **argv) {
8 struct cmd_results *error = NULL;
9 if ((error = checkarg(argc, "position", EXPECTED_EQUAL_TO, 1))) {
10 return error;
11 }
12 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE, "position", "No bar defined.");
14 }
15 char *valid[] = { "top", "bottom", "left", "right" };
16 for (size_t i = 0; i < sizeof(valid) / sizeof(valid[0]); ++i) {
17 if (strcasecmp(valid[i], argv[0]) == 0) {
18 wlr_log(L_DEBUG, "Setting bar position '%s' for bar: %s",
19 argv[0], config->current_bar->id);
20 config->current_bar->position = strdup(argv[0]);
21 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
22 }
23 }
24 return cmd_results_new(CMD_INVALID,
25 "position", "Invalid value %s", argv[0]);
26}
diff --git a/sway/commands/bar/secondary_button.c b/sway/commands/bar/secondary_button.c
new file mode 100644
index 00000000..449124cb
--- /dev/null
+++ b/sway/commands/bar/secondary_button.c
@@ -0,0 +1,8 @@
1#include <stdlib.h>
2#include "sway/commands.h"
3#include "log.h"
4
5struct cmd_results *bar_cmd_secondary_button(int argc, char **argv) {
6 // TODO TRAY
7 return cmd_results_new(CMD_INVALID, "secondary_button", "TODO TRAY");
8}
diff --git a/sway/commands/bar/separator_symbol.c b/sway/commands/bar/separator_symbol.c
new file mode 100644
index 00000000..1e08df6d
--- /dev/null
+++ b/sway/commands/bar/separator_symbol.c
@@ -0,0 +1,20 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_separator_symbol(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "separator_symbol", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11 if (!config->current_bar) {
12 return cmd_results_new(CMD_FAILURE,
13 "separator_symbol", "No bar defined.");
14 }
15 free(config->current_bar->separator_symbol);
16 config->current_bar->separator_symbol = strdup(argv[0]);
17 wlr_log(L_DEBUG, "Settings separator_symbol '%s' for bar: %s",
18 config->current_bar->separator_symbol, config->current_bar->id);
19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
20}
diff --git a/sway/commands/bar/status_command.c b/sway/commands/bar/status_command.c
new file mode 100644
index 00000000..5e199cde
--- /dev/null
+++ b/sway/commands/bar/status_command.c
@@ -0,0 +1,20 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "log.h"
4#include "stringop.h"
5
6struct cmd_results *bar_cmd_status_command(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "status_command", EXPECTED_AT_LEAST, 1))) {
9 return error;
10 }
11 if (!config->current_bar) {
12 return cmd_results_new(CMD_FAILURE,
13 "status_command", "No bar defined.");
14 }
15 free(config->current_bar->status_command);
16 config->current_bar->status_command = join_args(argv, argc);
17 wlr_log(L_DEBUG, "Feeding bar with status command: %s",
18 config->current_bar->status_command);
19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
20}
diff --git a/sway/commands/bar/strip_workspace_numbers.c b/sway/commands/bar/strip_workspace_numbers.c
new file mode 100644
index 00000000..4f24a356
--- /dev/null
+++ b/sway/commands/bar/strip_workspace_numbers.c
@@ -0,0 +1,29 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc,
9 "strip_workspace_numbers", EXPECTED_EQUAL_TO, 1))) {
10 return error;
11 }
12 if (!config->current_bar) {
13 return cmd_results_new(CMD_FAILURE,
14 "strip_workspace_numbers", "No bar defined.");
15 }
16 if (strcasecmp("yes", argv[0]) == 0) {
17 config->current_bar->strip_workspace_numbers = true;
18 wlr_log(L_DEBUG, "Stripping workspace numbers on bar: %s",
19 config->current_bar->id);
20 } else if (strcasecmp("no", argv[0]) == 0) {
21 config->current_bar->strip_workspace_numbers = false;
22 wlr_log(L_DEBUG, "Enabling workspace numbers on bar: %s",
23 config->current_bar->id);
24 } else {
25 return cmd_results_new(CMD_INVALID,
26 "strip_workspace_numbers", "Invalid value %s", argv[0]);
27 }
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29}
diff --git a/sway/commands/bar/swaybar_command.c b/sway/commands/bar/swaybar_command.c
new file mode 100644
index 00000000..520cdd11
--- /dev/null
+++ b/sway/commands/bar/swaybar_command.c
@@ -0,0 +1,20 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "log.h"
4#include "stringop.h"
5
6struct cmd_results *bar_cmd_swaybar_command(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "swaybar_command", EXPECTED_AT_LEAST, 1))) {
9 return error;
10 }
11 if (!config->current_bar) {
12 return cmd_results_new(CMD_FAILURE,
13 "swaybar_command", "No bar defined.");
14 }
15 free(config->current_bar->swaybar_command);
16 config->current_bar->swaybar_command = join_args(argv, argc);
17 wlr_log(L_DEBUG, "Using custom swaybar command: %s",
18 config->current_bar->swaybar_command);
19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
20}
diff --git a/sway/commands/bar/tray_output.c b/sway/commands/bar/tray_output.c
new file mode 100644
index 00000000..6ab16731
--- /dev/null
+++ b/sway/commands/bar/tray_output.c
@@ -0,0 +1,8 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
3#include "sway/commands.h"
4
5struct cmd_results *bar_cmd_tray_output(int argc, char **argv) {
6 // TODO TRAY
7 return cmd_results_new(CMD_INVALID, "tray_output", "TODO TRAY");
8}
diff --git a/sway/commands/bar/tray_padding.c b/sway/commands/bar/tray_padding.c
new file mode 100644
index 00000000..91c56f19
--- /dev/null
+++ b/sway/commands/bar/tray_padding.c
@@ -0,0 +1,9 @@
1#include <stdlib.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_tray_padding(int argc, char **argv) {
7 // TODO TRAY
8 return cmd_results_new(CMD_INVALID, "tray_padding", "TODO TRAY");
9}
diff --git a/sway/commands/bar/workspace_buttons.c b/sway/commands/bar/workspace_buttons.c
new file mode 100644
index 00000000..6edc3a0d
--- /dev/null
+++ b/sway/commands/bar/workspace_buttons.c
@@ -0,0 +1,28 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "workspace_buttons", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11 if (!config->current_bar) {
12 return cmd_results_new(CMD_FAILURE,
13 "workspace_buttons", "No bar defined.");
14 }
15 if (strcasecmp("yes", argv[0]) == 0) {
16 config->current_bar->workspace_buttons = true;
17 wlr_log(L_DEBUG, "Enabling workspace buttons on bar: %s",
18 config->current_bar->id);
19 } else if (strcasecmp("no", argv[0]) == 0) {
20 config->current_bar->workspace_buttons = false;
21 wlr_log(L_DEBUG, "Disabling workspace buttons on bar: %s",
22 config->current_bar->id);
23 } else {
24 return cmd_results_new(CMD_INVALID, "workspace_buttons",
25 "Invalid value %s", argv[0]);
26 }
27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
28}
diff --git a/sway/commands/bar/wrap_scroll.c b/sway/commands/bar/wrap_scroll.c
new file mode 100644
index 00000000..7386f82c
--- /dev/null
+++ b/sway/commands/bar/wrap_scroll.c
@@ -0,0 +1,27 @@
1#include <string.h>
2#include <strings.h>
3#include "sway/commands.h"
4#include "log.h"
5
6struct cmd_results *bar_cmd_wrap_scroll(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "wrap_scroll", EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11 if (!config->current_bar) {
12 return cmd_results_new(CMD_FAILURE, "wrap_scroll", "No bar defined.");
13 }
14 if (strcasecmp("yes", argv[0]) == 0) {
15 config->current_bar->wrap_scroll = true;
16 wlr_log(L_DEBUG, "Enabling wrap scroll on bar: %s",
17 config->current_bar->id);
18 } else if (strcasecmp("no", argv[0]) == 0) {
19 config->current_bar->wrap_scroll = false;
20 wlr_log(L_DEBUG, "Disabling wrap scroll on bar: %s",
21 config->current_bar->id);
22 } else {
23 return cmd_results_new(CMD_INVALID,
24 "wrap_scroll", "Invalid value %s", argv[0]);
25 }
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27}
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 @@
1#define _XOPEN_SOURCE 500
2#include <stdbool.h>
3#include <string.h>
4#include <strings.h>
5#include "sway/commands.h"
6#include "sway/config.h"
7#include "sway/ipc-server.h"
8#include "list.h"
9#include "log.h"
10
11struct cmd_results *cmd_mode(int argc, char **argv) {
12 struct cmd_results *error = NULL;
13 if ((error = checkarg(argc, "mode", EXPECTED_AT_LEAST, 1))) {
14 return error;
15 }
16
17 const char *mode_name = argv[0];
18 bool new_mode = (argc == 2 && strcmp(argv[1], "{") == 0);
19 if (new_mode && !config->reading) {
20 return cmd_results_new(CMD_FAILURE,
21 "mode", "Can only be used in config file.");
22 }
23 struct sway_mode *mode = NULL;
24 // Find mode
25 for (int i = 0; i < config->modes->length; ++i) {
26 struct sway_mode *test = config->modes->items[i];
27 if (strcasecmp(test->name, mode_name) == 0) {
28 mode = test;
29 break;
30 }
31 }
32 // Create mode if it doesn't exist
33 if (!mode && new_mode) {
34 mode = calloc(1, sizeof(struct sway_mode));
35 if (!mode) {
36 return cmd_results_new(CMD_FAILURE,
37 "mode", "Unable to allocate mode");
38 }
39 mode->name = strdup(mode_name);
40 mode->keysym_bindings = create_list();
41 mode->keycode_bindings = create_list();
42 list_add(config->modes, mode);
43 }
44 if (!mode) {
45 error = cmd_results_new(CMD_INVALID,
46 "mode", "Unknown mode `%s'", mode_name);
47 return error;
48 }
49 if ((config->reading && new_mode) || (!config->reading && !new_mode)) {
50 wlr_log(L_DEBUG, "Switching to mode `%s'",mode->name);
51 }
52 // Set current mode
53 config->current_mode = mode;
54 if (!new_mode) {
55 // trigger IPC mode event
56 ipc_event_mode(config->current_mode->name);
57 }
58 return cmd_results_new(new_mode ? CMD_BLOCK_MODE : CMD_SUCCESS, NULL, NULL);
59}
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index 8cef789b..5bca6cde 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -11,8 +11,7 @@ struct cmd_results *cmd_reload(int argc, char **argv) {
11 return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config."); 11 return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config.");
12 } 12 }
13 13
14 /* load_swaybars(); -- for when it's implemented */ 14 load_swaybars();
15
16 arrange_windows(&root_container, -1, -1); 15 arrange_windows(&root_container, -1, -1);
17 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 16 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
18} 17}
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 @@
1#include <string.h>
2#include "sway/commands.h"
3#include "log.h"
4#include "stringop.h"
5
6struct cmd_results *cmd_swaybg_command(int argc, char **argv) {
7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "swaybg_command", EXPECTED_AT_LEAST, 1))) {
9 return error;
10 }
11
12 if (config->swaybg_command) {
13 free(config->swaybg_command);
14 }
15 config->swaybg_command = join_args(argv, argc);
16 wlr_log(L_DEBUG, "Using custom swaybg command: %s",
17 config->swaybg_command);
18
19 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
20}
diff --git a/sway/config.c b/sway/config.c
index 0b29735a..347d9e73 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -403,12 +403,6 @@ bool load_main_config(const char *file, bool is_active) {
403 free_config(old_config); 403 free_config(old_config);
404 } 404 }
405 config->reading = false; 405 config->reading = false;
406
407 if (success) {
408 // TODO: bar
409 //update_active_bar_modifiers();
410 }
411
412 return success; 406 return success;
413} 407}
414 408
diff --git a/sway/config/bar.c b/sway/config/bar.c
new file mode 100644
index 00000000..48b2fc7c
--- /dev/null
+++ b/sway/config/bar.c
@@ -0,0 +1,238 @@
1#define _POSIX_C_SOURCE 200809L
2#define _XOPEN_SOURCE 700
3#include <stdio.h>
4#include <stdbool.h>
5#include <stdlib.h>
6#include <unistd.h>
7#include <wordexp.h>
8#include <sys/types.h>
9#include <sys/wait.h>
10#include <sys/stat.h>
11#include <signal.h>
12#include <strings.h>
13#include "sway/config.h"
14#include "stringop.h"
15#include "list.h"
16#include "log.h"
17
18static void terminate_swaybar(pid_t pid) {
19 int ret = kill(pid, SIGTERM);
20 if (ret != 0) {
21 wlr_log_errno(L_ERROR, "Unable to terminate swaybar %d", pid);
22 } else {
23 int status;
24 waitpid(pid, &status, 0);
25 }
26}
27
28void free_bar_config(struct bar_config *bar) {
29 if (!bar) {
30 return;
31 }
32 free(bar->mode);
33 free(bar->position);
34 free(bar->hidden_state);
35 free(bar->status_command);
36 free(bar->font);
37 free(bar->separator_symbol);
38 // TODO: Free mouse bindings
39 list_free(bar->bindings);
40 if (bar->outputs) {
41 free_flat_list(bar->outputs);
42 }
43 if (bar->pid != 0) {
44 terminate_swaybar(bar->pid);
45 }
46 free(bar->colors.background);
47 free(bar->colors.statusline);
48 free(bar->colors.separator);
49 free(bar->colors.focused_background);
50 free(bar->colors.focused_statusline);
51 free(bar->colors.focused_separator);
52 free(bar->colors.focused_workspace_border);
53 free(bar->colors.focused_workspace_bg);
54 free(bar->colors.focused_workspace_text);
55 free(bar->colors.active_workspace_border);
56 free(bar->colors.active_workspace_bg);
57 free(bar->colors.active_workspace_text);
58 free(bar->colors.inactive_workspace_border);
59 free(bar->colors.inactive_workspace_bg);
60 free(bar->colors.inactive_workspace_text);
61 free(bar->colors.urgent_workspace_border);
62 free(bar->colors.urgent_workspace_bg);
63 free(bar->colors.urgent_workspace_text);
64 free(bar->colors.binding_mode_border);
65 free(bar->colors.binding_mode_bg);
66 free(bar->colors.binding_mode_text);
67 free(bar);
68}
69
70struct bar_config *default_bar_config(void) {
71 struct bar_config *bar = NULL;
72 bar = malloc(sizeof(struct bar_config));
73 if (!bar) {
74 return NULL;
75 }
76 if (!(bar->mode = strdup("dock"))) goto cleanup;
77 if (!(bar->hidden_state = strdup("hide"))) goto cleanup;
78 bar->outputs = NULL;
79 bar->position = strdup("bottom");
80 if (!(bar->bindings = create_list())) goto cleanup;
81 if (!(bar->status_command = strdup("while :; do date +'%Y-%m-%d %l:%M:%S %p'; sleep 1; done"))) goto cleanup;
82 bar->pango_markup = false;
83 bar->swaybar_command = NULL;
84 bar->font = NULL;
85 bar->height = -1;
86 bar->workspace_buttons = true;
87 bar->wrap_scroll = false;
88 bar->separator_symbol = NULL;
89 bar->strip_workspace_numbers = false;
90 bar->binding_mode_indicator = true;
91 bar->verbose = false;
92 bar->pid = 0;
93 // set default colors
94 if (!(bar->colors.background = strndup("#000000ff", 9))) {
95 goto cleanup;
96 }
97 if (!(bar->colors.statusline = strndup("#ffffffff", 9))) {
98 goto cleanup;
99 }
100 if (!(bar->colors.separator = strndup("#666666ff", 9))) {
101 goto cleanup;
102 }
103 if (!(bar->colors.focused_workspace_border = strndup("#4c7899ff", 9))) {
104 goto cleanup;
105 }
106 if (!(bar->colors.focused_workspace_bg = strndup("#285577ff", 9))) {
107 goto cleanup;
108 }
109 if (!(bar->colors.focused_workspace_text = strndup("#ffffffff", 9))) {
110 goto cleanup;
111 }
112 if (!(bar->colors.active_workspace_border = strndup("#333333ff", 9))) {
113 goto cleanup;
114 }
115 if (!(bar->colors.active_workspace_bg = strndup("#5f676aff", 9))) {
116 goto cleanup;
117 }
118 if (!(bar->colors.active_workspace_text = strndup("#ffffffff", 9))) {
119 goto cleanup;
120 }
121 if (!(bar->colors.inactive_workspace_border = strndup("#333333ff", 9))) {
122 goto cleanup;
123 }
124 if (!(bar->colors.inactive_workspace_bg = strndup("#222222ff", 9))) {
125 goto cleanup;
126 }
127 if (!(bar->colors.inactive_workspace_text = strndup("#888888ff", 9))) {
128 goto cleanup;
129 }
130 if (!(bar->colors.urgent_workspace_border = strndup("#2f343aff", 9))) {
131 goto cleanup;
132 }
133 if (!(bar->colors.urgent_workspace_bg = strndup("#900000ff", 9))) {
134 goto cleanup;
135 }
136 if (!(bar->colors.urgent_workspace_text = strndup("#ffffffff", 9))) {
137 goto cleanup;
138 }
139 // if the following colors stay undefined, they fall back to background,
140 // statusline, separator and urgent_workspace_*.
141 bar->colors.focused_background = NULL;
142 bar->colors.focused_statusline = NULL;
143 bar->colors.focused_separator = NULL;
144 bar->colors.binding_mode_border = NULL;
145 bar->colors.binding_mode_bg = NULL;
146 bar->colors.binding_mode_text = NULL;
147
148 list_add(config->bars, bar);
149 return bar;
150cleanup:
151 free_bar_config(bar);
152 return NULL;
153}
154
155void invoke_swaybar(struct bar_config *bar) {
156 // Pipe to communicate errors
157 int filedes[2];
158 if (pipe(filedes) == -1) {
159 wlr_log(L_ERROR, "Pipe setup failed! Cannot fork into bar");
160 return;
161 }
162
163 bar->pid = fork();
164 if (bar->pid == 0) {
165 close(filedes[0]);
166
167 // run custom swaybar
168 size_t len = snprintf(NULL, 0, "%s -b %s",
169 bar->swaybar_command ? bar->swaybar_command : "swaybar",
170 bar->id);
171 char *command = malloc(len + 1);
172 if (!command) {
173 const char msg[] = "Unable to allocate swaybar command string";
174 size_t len = sizeof(msg);
175 if (write(filedes[1], &len, sizeof(int))) {};
176 if (write(filedes[1], msg, len)) {};
177 close(filedes[1]);
178 exit(1);
179 }
180 snprintf(command, len + 1, "%s -b %s",
181 bar->swaybar_command ? bar->swaybar_command : "swaybar",
182 bar->id);
183 char *const cmd[] = { "sh", "-c", command, NULL, };
184 close(filedes[1]);
185 execvp(cmd[0], cmd);
186 exit(1);
187 }
188 close(filedes[0]);
189 ssize_t len;
190 if (read(filedes[1], &len, sizeof(int)) == sizeof(int)) {
191 char *buf = malloc(len);
192 if(!buf) {
193 wlr_log(L_ERROR, "Cannot allocate error string");
194 return;
195 }
196 if (read(filedes[1], buf, len)) {
197 wlr_log(L_ERROR, "%s", buf);
198 }
199 free(buf);
200 }
201 close(filedes[1]);
202}
203
204static bool active_output(const char *name) {
205 struct sway_container *cont = NULL;
206 for (int i = 0; i < root_container.children->length; ++i) {
207 cont = root_container.children->items[i];
208 if (cont->type == C_OUTPUT && strcasecmp(name, cont->name) == 0) {
209 return true;
210 }
211 }
212 return false;
213}
214
215void load_swaybars() {
216 for (int i = 0; i < config->bars->length; ++i) {
217 struct bar_config *bar = config->bars->items[i];
218 bool apply = false;
219 if (bar->outputs) {
220 for (int j = 0; j < bar->outputs->length; ++j) {
221 char *o = bar->outputs->items[j];
222 if (!strcmp(o, "*") || active_output(o)) {
223 apply = true;
224 break;
225 }
226 }
227 } else {
228 apply = true;
229 }
230 if (apply) {
231 if (bar->pid != 0) {
232 terminate_swaybar(bar->pid);
233 }
234 wlr_log(L_DEBUG, "Invoking swaybar for bar id '%s'", bar->id);
235 invoke_swaybar(bar);
236 }
237 }
238}
diff --git a/sway/config/output.c b/sway/config/output.c
index 7fc79739..b4e56efa 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -180,19 +180,20 @@ void apply_output_config(struct output_config *oc, struct sway_container *output
180 wlr_log(L_DEBUG, "Setting background for output %d to %s", 180 wlr_log(L_DEBUG, "Setting background for output %d to %s",
181 output_i, oc->background); 181 output_i, oc->background);
182 182
183 size_t bufsize = 12; 183 size_t len = snprintf(NULL, 0, "%s %d %s %s",
184 char output_id[bufsize]; 184 config->swaybg_command ? config->swaybg_command : "swaybg",
185 snprintf(output_id, bufsize, "%d", output_i); 185 output_i, oc->background, oc->background_option);
186 output_id[bufsize-1] = 0; 186 char *command = malloc(len + 1);
187 187 if (!command) {
188 char *const cmd[] = { 188 wlr_log(L_DEBUG, "Unable to allocate swaybg command");
189 "./swaybg/swaybg", 189 return;
190 output_id, 190 }
191 oc->background, 191 snprintf(command, len + 1, "%s %d %s %s",
192 oc->background_option, 192 config->swaybg_command ? config->swaybg_command : "swaybg",
193 NULL, 193 output_i, oc->background, oc->background_option);
194 }; 194 wlr_log(L_DEBUG, "-> %s", command);
195 195
196 char *const cmd[] = { "sh", "-c", command, NULL };
196 output->sway_output->bg_pid = fork(); 197 output->sway_output->bg_pid = fork();
197 if (output->sway_output->bg_pid == 0) { 198 if (output->sway_output->bg_pid == 0) {
198 execvp(cmd[0], cmd); 199 execvp(cmd[0], cmd);
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index 137b3260..f7e5d19c 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -156,7 +156,6 @@ void arrange_layers(struct sway_output *output) {
156 struct wlr_box usable_area = { 0 }; 156 struct wlr_box usable_area = { 0 };
157 wlr_output_effective_resolution(output->wlr_output, 157 wlr_output_effective_resolution(output->wlr_output,
158 &usable_area.width, &usable_area.height); 158 &usable_area.width, &usable_area.height);
159 struct wlr_box usable_area_before = output->usable_area;
160 159
161 // Arrange exclusive surfaces from top->bottom 160 // Arrange exclusive surfaces from top->bottom
162 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], 161 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
@@ -167,11 +166,11 @@ void arrange_layers(struct sway_output *output) {
167 &usable_area, true); 166 &usable_area, true);
168 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], 167 arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
169 &usable_area, true); 168 &usable_area, true);
170 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
171 169
172 if (memcmp(&usable_area_before, 170 if (memcmp(&usable_area, &output->usable_area,
173 &usable_area, sizeof(struct wlr_box)) != 0) { 171 sizeof(struct wlr_box)) != 0) {
174 wlr_log(L_DEBUG, "arrange"); 172 wlr_log(L_DEBUG, "Usable area changed, rearranging output");
173 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
175 arrange_windows(output->swayc, -1, -1); 174 arrange_windows(output->swayc, -1, -1);
176 } 175 }
177 176
@@ -193,22 +192,10 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) {
193 struct sway_layer_surface *sway_layer = 192 struct sway_layer_surface *sway_layer =
194 wl_container_of(listener, sway_layer, output_destroy); 193 wl_container_of(listener, sway_layer, output_destroy);
195 wl_list_remove(&sway_layer->output_destroy.link); 194 wl_list_remove(&sway_layer->output_destroy.link);
196 wl_list_remove(&sway_layer->output_mode.link);
197 wl_list_remove(&sway_layer->output_transform.link);
198 sway_layer->layer_surface->output = NULL; 195 sway_layer->layer_surface->output = NULL;
199 wlr_layer_surface_close(sway_layer->layer_surface); 196 wlr_layer_surface_close(sway_layer->layer_surface);
200} 197}
201 198
202static void handle_output_mode(struct wl_listener *listener, void *data) {
203 struct wlr_output *output = data;
204 arrange_layers((struct sway_output *)output->data);
205}
206
207static void handle_output_transform(struct wl_listener *listener, void *data) {
208 struct wlr_output *output = data;
209 arrange_layers((struct sway_output *)output->data);
210}
211
212static void handle_surface_commit(struct wl_listener *listener, void *data) { 199static void handle_surface_commit(struct wl_listener *listener, void *data) {
213 struct sway_layer_surface *layer = 200 struct sway_layer_surface *layer =
214 wl_container_of(listener, layer, surface_commit); 201 wl_container_of(listener, layer, surface_commit);
@@ -233,6 +220,8 @@ static void unmap(struct wlr_layer_surface *layer_surface) {
233static void handle_destroy(struct wl_listener *listener, void *data) { 220static void handle_destroy(struct wl_listener *listener, void *data) {
234 struct sway_layer_surface *sway_layer = wl_container_of( 221 struct sway_layer_surface *sway_layer = wl_container_of(
235 listener, sway_layer, destroy); 222 listener, sway_layer, destroy);
223 wlr_log(L_DEBUG, "Layer surface destroyed (%s)",
224 sway_layer->layer_surface->namespace);
236 if (sway_layer->layer_surface->mapped) { 225 if (sway_layer->layer_surface->mapped) {
237 unmap(sway_layer->layer_surface); 226 unmap(sway_layer->layer_surface);
238 } 227 }
@@ -243,12 +232,10 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
243 wl_list_remove(&sway_layer->surface_commit.link); 232 wl_list_remove(&sway_layer->surface_commit.link);
244 if (sway_layer->layer_surface->output != NULL) { 233 if (sway_layer->layer_surface->output != NULL) {
245 wl_list_remove(&sway_layer->output_destroy.link); 234 wl_list_remove(&sway_layer->output_destroy.link);
246 wl_list_remove(&sway_layer->output_mode.link);
247 wl_list_remove(&sway_layer->output_transform.link);
248 } 235 }
249 struct sway_output *output = sway_layer->layer_surface->output->data; 236 struct sway_output *output = sway_layer->layer_surface->output->data;
250 arrange_layers(output);
251 free(sway_layer); 237 free(sway_layer);
238 arrange_layers(output);
252} 239}
253 240
254static void handle_map(struct wl_listener *listener, void *data) { 241static void handle_map(struct wl_listener *listener, void *data) {
@@ -289,14 +276,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
289 wl_signal_add(&layer_surface->output->events.destroy, 276 wl_signal_add(&layer_surface->output->events.destroy,
290 &sway_layer->output_destroy); 277 &sway_layer->output_destroy);
291 278
292 sway_layer->output_mode.notify = handle_output_mode;
293 wl_signal_add(&layer_surface->output->events.mode,
294 &sway_layer->output_mode);
295
296 sway_layer->output_transform.notify = handle_output_transform;
297 wl_signal_add(&layer_surface->output->events.transform,
298 &sway_layer->output_transform);
299
300 sway_layer->destroy.notify = handle_destroy; 279 sway_layer->destroy.notify = handle_destroy;
301 wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy); 280 wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy);
302 sway_layer->map.notify = handle_map; 281 sway_layer->map.notify = handle_map;
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 1273df59..a9c59684 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) {
268 arrange_windows(output->swayc, -1, -1); 268 arrange_windows(output->swayc, -1, -1);
269} 269}
270 270
271static void handle_output_transform(struct wl_listener *listener, void *data) {
272 struct sway_output *output = wl_container_of(listener, output, transform);
273 arrange_layers(output);
274 arrange_windows(output->swayc, -1, -1);
275}
276
271void handle_new_output(struct wl_listener *listener, void *data) { 277void handle_new_output(struct wl_listener *listener, void *data) {
272 struct sway_server *server = wl_container_of(listener, server, new_output); 278 struct sway_server *server = wl_container_of(listener, server, new_output);
273 struct wlr_output *wlr_output = data; 279 struct wlr_output *wlr_output = data;
@@ -306,6 +312,8 @@ void handle_new_output(struct wl_listener *listener, void *data) {
306 output->destroy.notify = handle_output_destroy; 312 output->destroy.notify = handle_output_destroy;
307 wl_signal_add(&wlr_output->events.mode, &output->mode); 313 wl_signal_add(&wlr_output->events.mode, &output->mode);
308 output->mode.notify = handle_output_mode; 314 output->mode.notify = handle_output_mode;
315 wl_signal_add(&wlr_output->events.transform, &output->transform);
316 output->transform.notify = handle_output_transform;
309 317
310 arrange_layers(output); 318 arrange_layers(output);
311 arrange_windows(&root_container, -1, -1); 319 arrange_windows(&root_container, -1, -1);
diff --git a/sway/input/seat.c b/sway/input/seat.c
index aa0b1d50..7cf0dd08 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -6,6 +6,7 @@
6#include "sway/input/cursor.h" 6#include "sway/input/cursor.h"
7#include "sway/input/input-manager.h" 7#include "sway/input/input-manager.h"
8#include "sway/input/keyboard.h" 8#include "sway/input/keyboard.h"
9#include "sway/ipc-server.h"
9#include "sway/output.h" 10#include "sway/output.h"
10#include "sway/tree/view.h" 11#include "sway/tree/view.h"
11#include "log.h" 12#include "log.h"
@@ -309,18 +310,30 @@ void sway_seat_set_focus(struct sway_seat *seat, struct sway_container *containe
309 if (container->type == C_VIEW) { 310 if (container->type == C_VIEW) {
310 struct sway_view *view = container->sway_view; 311 struct sway_view *view = container->sway_view;
311 view_set_activated(view, true); 312 view_set_activated(view, true);
312 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); 313 struct wlr_keyboard *keyboard =
314 wlr_seat_get_keyboard(seat->wlr_seat);
313 if (keyboard) { 315 if (keyboard) {
314 wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface, 316 wlr_seat_keyboard_notify_enter(seat->wlr_seat,
315 keyboard->keycodes, keyboard->num_keycodes, 317 view->surface, keyboard->keycodes,
316 &keyboard->modifiers); 318 keyboard->num_keycodes, &keyboard->modifiers);
317 } else { 319 } else {
318 wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface, 320 wlr_seat_keyboard_notify_enter(
319 NULL, 0, NULL); 321 seat->wlr_seat, view->surface, NULL, 0, NULL);
320 } 322 }
321 } 323 }
322 } 324 }
323 325
326 if (last_focus) {
327 struct sway_container *last_ws = last_focus;
328 if (last_ws && last_ws->type != C_WORKSPACE) {
329 last_ws = container_parent(last_focus, C_WORKSPACE);
330 }
331 if (last_ws) {
332 wlr_log(L_DEBUG, "sending workspace event");
333 ipc_event_workspace(last_ws, container, "focus");
334 }
335 }
336
324 if (last_focus && last_focus->type == C_VIEW && 337 if (last_focus && last_focus->type == C_VIEW &&
325 !sway_input_manager_has_focus(seat->input, last_focus)) { 338 !sway_input_manager_has_focus(seat->input, last_focus)) {
326 struct sway_view *view = last_focus->sway_view; 339 struct sway_view *view = last_focus->sway_view;
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 7caa2457..eab6399f 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -9,6 +9,7 @@
9#include "sway/input/seat.h" 9#include "sway/input/seat.h"
10#include <wlr/types/wlr_box.h> 10#include <wlr/types/wlr_box.h>
11#include <wlr/types/wlr_output.h> 11#include <wlr/types/wlr_output.h>
12#include "wlr-layer-shell-unstable-v1-protocol.h"
12 13
13json_object *ipc_json_get_version() { 14json_object *ipc_json_get_version() {
14 int major = 0, minor = 0, patch = 0; 15 int major = 0, minor = 0, patch = 0;
@@ -198,3 +199,136 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
198 199
199 return object; 200 return object;
200} 201}
202
203json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
204 if (!sway_assert(bar, "Bar must not be NULL")) {
205 return NULL;
206 }
207
208 json_object *json = json_object_new_object();
209 json_object_object_add(json, "id", json_object_new_string(bar->id));
210 json_object_object_add(json, "mode", json_object_new_string(bar->mode));
211 json_object_object_add(json, "hidden_state",
212 json_object_new_string(bar->hidden_state));
213 json_object_object_add(json, "position",
214 json_object_new_string(bar->position));
215 json_object_object_add(json, "status_command",
216 json_object_new_string(bar->status_command));
217 json_object_object_add(json, "font",
218 json_object_new_string((bar->font) ? bar->font : config->font));
219 if (bar->separator_symbol) {
220 json_object_object_add(json, "separator_symbol",
221 json_object_new_string(bar->separator_symbol));
222 }
223 json_object_object_add(json, "bar_height",
224 json_object_new_int(bar->height));
225 json_object_object_add(json, "wrap_scroll",
226 json_object_new_boolean(bar->wrap_scroll));
227 json_object_object_add(json, "workspace_buttons",
228 json_object_new_boolean(bar->workspace_buttons));
229 json_object_object_add(json, "strip_workspace_numbers",
230 json_object_new_boolean(bar->strip_workspace_numbers));
231 json_object_object_add(json, "binding_mode_indicator",
232 json_object_new_boolean(bar->binding_mode_indicator));
233 json_object_object_add(json, "verbose",
234 json_object_new_boolean(bar->verbose));
235 json_object_object_add(json, "pango_markup",
236 json_object_new_boolean(bar->pango_markup));
237
238 json_object *colors = json_object_new_object();
239 json_object_object_add(colors, "background",
240 json_object_new_string(bar->colors.background));
241 json_object_object_add(colors, "statusline",
242 json_object_new_string(bar->colors.statusline));
243 json_object_object_add(colors, "separator",
244 json_object_new_string(bar->colors.separator));
245
246 if (bar->colors.focused_background) {
247 json_object_object_add(colors, "focused_background",
248 json_object_new_string(bar->colors.focused_background));
249 } else {
250 json_object_object_add(colors, "focused_background",
251 json_object_new_string(bar->colors.background));
252 }
253
254 if (bar->colors.focused_statusline) {
255 json_object_object_add(colors, "focused_statusline",
256 json_object_new_string(bar->colors.focused_statusline));
257 } else {
258 json_object_object_add(colors, "focused_statusline",
259 json_object_new_string(bar->colors.statusline));
260 }
261
262 if (bar->colors.focused_separator) {
263 json_object_object_add(colors, "focused_separator",
264 json_object_new_string(bar->colors.focused_separator));
265 } else {
266 json_object_object_add(colors, "focused_separator",
267 json_object_new_string(bar->colors.separator));
268 }
269
270 json_object_object_add(colors, "focused_workspace_border",
271 json_object_new_string(bar->colors.focused_workspace_border));
272 json_object_object_add(colors, "focused_workspace_bg",
273 json_object_new_string(bar->colors.focused_workspace_bg));
274 json_object_object_add(colors, "focused_workspace_text",
275 json_object_new_string(bar->colors.focused_workspace_text));
276
277 json_object_object_add(colors, "inactive_workspace_border",
278 json_object_new_string(bar->colors.inactive_workspace_border));
279 json_object_object_add(colors, "inactive_workspace_bg",
280 json_object_new_string(bar->colors.inactive_workspace_bg));
281 json_object_object_add(colors, "inactive_workspace_text",
282 json_object_new_string(bar->colors.inactive_workspace_text));
283
284 json_object_object_add(colors, "active_workspace_border",
285 json_object_new_string(bar->colors.active_workspace_border));
286 json_object_object_add(colors, "active_workspace_bg",
287 json_object_new_string(bar->colors.active_workspace_bg));
288 json_object_object_add(colors, "active_workspace_text",
289 json_object_new_string(bar->colors.active_workspace_text));
290
291 json_object_object_add(colors, "urgent_workspace_border",
292 json_object_new_string(bar->colors.urgent_workspace_border));
293 json_object_object_add(colors, "urgent_workspace_bg",
294 json_object_new_string(bar->colors.urgent_workspace_bg));
295 json_object_object_add(colors, "urgent_workspace_text",
296 json_object_new_string(bar->colors.urgent_workspace_text));
297
298 if (bar->colors.binding_mode_border) {
299 json_object_object_add(colors, "binding_mode_border",
300 json_object_new_string(bar->colors.binding_mode_border));
301 } else {
302 json_object_object_add(colors, "binding_mode_border",
303 json_object_new_string(bar->colors.urgent_workspace_border));
304 }
305
306 if (bar->colors.binding_mode_bg) {
307 json_object_object_add(colors, "binding_mode_bg",
308 json_object_new_string(bar->colors.binding_mode_bg));
309 } else {
310 json_object_object_add(colors, "binding_mode_bg",
311 json_object_new_string(bar->colors.urgent_workspace_bg));
312 }
313
314 if (bar->colors.binding_mode_text) {
315 json_object_object_add(colors, "binding_mode_text",
316 json_object_new_string(bar->colors.binding_mode_text));
317 } else {
318 json_object_object_add(colors, "binding_mode_text",
319 json_object_new_string(bar->colors.urgent_workspace_text));
320 }
321
322 json_object_object_add(json, "colors", colors);
323
324 // Add outputs if defined
325 if (bar->outputs && bar->outputs->length > 0) {
326 json_object *outputs = json_object_new_array();
327 for (int i = 0; i < bar->outputs->length; ++i) {
328 const char *name = bar->outputs->items[i];
329 json_object_array_add(outputs, json_object_new_string(name));
330 }
331 json_object_object_add(json, "outputs", outputs);
332 }
333 return json;
334}
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index 50d0bcf3..394161af 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -21,6 +21,7 @@
21#include "sway/ipc-server.h" 21#include "sway/ipc-server.h"
22#include "sway/server.h" 22#include "sway/server.h"
23#include "sway/input/input-manager.h" 23#include "sway/input/input-manager.h"
24#include "sway/input/seat.h"
24#include "list.h" 25#include "list.h"
25#include "log.h" 26#include "log.h"
26 27
@@ -241,33 +242,10 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
241} 242}
242 243
243static void ipc_send_event(const char *json_string, enum ipc_command_type event) { 244static void ipc_send_event(const char *json_string, enum ipc_command_type event) {
244 static struct {
245 enum ipc_command_type event;
246 enum ipc_feature feature;
247 } security_mappings[] = {
248 { IPC_EVENT_WORKSPACE, IPC_FEATURE_EVENT_WORKSPACE },
249 { IPC_EVENT_OUTPUT, IPC_FEATURE_EVENT_OUTPUT },
250 { IPC_EVENT_MODE, IPC_FEATURE_EVENT_MODE },
251 { IPC_EVENT_WINDOW, IPC_FEATURE_EVENT_WINDOW },
252 { IPC_EVENT_BINDING, IPC_FEATURE_EVENT_BINDING },
253 { IPC_EVENT_INPUT, IPC_FEATURE_EVENT_INPUT }
254 };
255
256 uint32_t security_mask = 0;
257 for (size_t i = 0; i < sizeof(security_mappings) / sizeof(security_mappings[0]); ++i) {
258 if (security_mappings[i].event == event) {
259 security_mask = security_mappings[i].feature;
260 break;
261 }
262 }
263
264 int i; 245 int i;
265 struct ipc_client *client; 246 struct ipc_client *client;
266 for (i = 0; i < ipc_client_list->length; i++) { 247 for (i = 0; i < ipc_client_list->length; i++) {
267 client = ipc_client_list->items[i]; 248 client = ipc_client_list->items[i];
268 if (!(client->security_policy & security_mask)) {
269 continue;
270 }
271 if ((client->subscribed_events & event_mask(event)) == 0) { 249 if ((client->subscribed_events & event_mask(event)) == 0) {
272 continue; 250 continue;
273 } 251 }
@@ -279,6 +257,32 @@ static void ipc_send_event(const char *json_string, enum ipc_command_type event)
279 } 257 }
280} 258}
281 259
260void ipc_event_workspace(struct sway_container *old,
261 struct sway_container *new, const char *change) {
262 wlr_log(L_DEBUG, "Sending workspace::%s event", change);
263 json_object *obj = json_object_new_object();
264 json_object_object_add(obj, "change", json_object_new_string(change));
265 if (strcmp("focus", change) == 0) {
266 if (old) {
267 json_object_object_add(obj, "old",
268 ipc_json_describe_container_recursive(old));
269 } else {
270 json_object_object_add(obj, "old", NULL);
271 }
272 }
273
274 if (new) {
275 json_object_object_add(obj, "current",
276 ipc_json_describe_container_recursive(new));
277 } else {
278 json_object_object_add(obj, "current", NULL);
279 }
280
281 const char *json_string = json_object_to_json_string(obj);
282 ipc_send_event(json_string, IPC_EVENT_WORKSPACE);
283 json_object_put(obj);
284}
285
282void ipc_event_window(struct sway_container *window, const char *change) { 286void ipc_event_window(struct sway_container *window, const char *change) {
283 wlr_log(L_DEBUG, "Sending window::%s event", change); 287 wlr_log(L_DEBUG, "Sending window::%s event", change);
284 json_object *obj = json_object_new_object(); 288 json_object *obj = json_object_new_object();
@@ -287,8 +291,26 @@ void ipc_event_window(struct sway_container *window, const char *change) {
287 291
288 const char *json_string = json_object_to_json_string(obj); 292 const char *json_string = json_object_to_json_string(obj);
289 ipc_send_event(json_string, IPC_EVENT_WINDOW); 293 ipc_send_event(json_string, IPC_EVENT_WINDOW);
294 json_object_put(obj);
295}
290 296
291 json_object_put(obj); // free 297void ipc_event_barconfig_update(struct bar_config *bar) {
298 wlr_log(L_DEBUG, "Sending barconfig_update event");
299 json_object *json = ipc_json_describe_bar_config(bar);
300
301 const char *json_string = json_object_to_json_string(json);
302 ipc_send_event(json_string, IPC_EVENT_BARCONFIG_UPDATE);
303 json_object_put(json);
304}
305
306void ipc_event_mode(const char *mode) {
307 wlr_log(L_DEBUG, "Sending mode::%s event", mode);
308 json_object *obj = json_object_new_object();
309 json_object_object_add(obj, "change", json_object_new_string(mode));
310
311 const char *json_string = json_object_to_json_string(obj);
312 ipc_send_event(json_string, IPC_EVENT_MODE);
313 json_object_put(obj);
292} 314}
293 315
294int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { 316int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
@@ -357,6 +379,27 @@ void ipc_client_disconnect(struct ipc_client *client) {
357 free(client); 379 free(client);
358} 380}
359 381
382static void ipc_get_workspaces_callback(struct sway_container *workspace,
383 void *data) {
384 if (workspace->type != C_WORKSPACE) {
385 return;
386 }
387 json_object *workspace_json = ipc_json_describe_container(workspace);
388 // override the default focused indicator because
389 // it's set differently for the get_workspaces reply
390 struct sway_seat *seat =
391 sway_input_manager_get_default_seat(input_manager);
392 struct sway_container *focused_ws = sway_seat_get_focus(seat);
393 if (focused_ws->type != C_WORKSPACE) {
394 focused_ws = container_parent(focused_ws, C_WORKSPACE);
395 }
396 bool focused = workspace == focused_ws;
397 json_object_object_del(workspace_json, "focused");
398 json_object_object_add(workspace_json, "focused",
399 json_object_new_boolean(focused));
400 json_object_array_add((json_object *)data, workspace_json);
401}
402
360void ipc_client_handle_command(struct ipc_client *client) { 403void ipc_client_handle_command(struct ipc_client *client) {
361 if (!sway_assert(client != NULL, "client != NULL")) { 404 if (!sway_assert(client != NULL, "client != NULL")) {
362 return; 405 return;
@@ -412,6 +455,17 @@ void ipc_client_handle_command(struct ipc_client *client) {
412 goto exit_cleanup; 455 goto exit_cleanup;
413 } 456 }
414 457
458 case IPC_GET_WORKSPACES:
459 {
460 json_object *workspaces = json_object_new_array();
461 container_for_each_descendant_dfs(&root_container,
462 ipc_get_workspaces_callback, workspaces);
463 const char *json_string = json_object_to_json_string(workspaces);
464 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
465 json_object_put(workspaces); // free
466 goto exit_cleanup;
467 }
468
415 case IPC_SUBSCRIBE: 469 case IPC_SUBSCRIBE:
416 { 470 {
417 // TODO: Check if they're permitted to use these events 471 // TODO: Check if they're permitted to use these events
@@ -446,7 +500,6 @@ void ipc_client_handle_command(struct ipc_client *client) {
446 } 500 }
447 501
448 json_object_put(request); 502 json_object_put(request);
449
450 ipc_send_reply(client, "{\"success\": true}", 17); 503 ipc_send_reply(client, "{\"success\": true}", 17);
451 goto exit_cleanup; 504 goto exit_cleanup;
452 } 505 }
@@ -483,6 +536,41 @@ void ipc_client_handle_command(struct ipc_client *client) {
483 goto exit_cleanup; 536 goto exit_cleanup;
484 } 537 }
485 538
539 case IPC_GET_BAR_CONFIG:
540 {
541 if (!buf[0]) {
542 // Send list of configured bar IDs
543 json_object *bars = json_object_new_array();
544 for (int i = 0; i < config->bars->length; ++i) {
545 struct bar_config *bar = config->bars->items[i];
546 json_object_array_add(bars, json_object_new_string(bar->id));
547 }
548 const char *json_string = json_object_to_json_string(bars);
549 ipc_send_reply(client, json_string, (uint32_t)strlen(json_string));
550 json_object_put(bars); // free
551 } else {
552 // Send particular bar's details
553 struct bar_config *bar = NULL;
554 for (int i = 0; i < config->bars->length; ++i) {
555 bar = config->bars->items[i];
556 if (strcmp(buf, bar->id) == 0) {
557 break;
558 }
559 bar = NULL;
560 }
561 if (!bar) {
562 const char *error = "{ \"success\": false, \"error\": \"No bar with that ID\" }";
563 ipc_send_reply(client, error, (uint32_t)strlen(error));
564 goto exit_cleanup;
565 }
566 json_object *json = ipc_json_describe_bar_config(bar);
567 const char *json_string = json_object_to_json_string(json);
568 ipc_send_reply(client, json_string, (uint32_t)strlen(json_string));
569 json_object_put(json); // free
570 }
571 goto exit_cleanup;
572 }
573
486 default: 574 default:
487 wlr_log(L_INFO, "Unknown IPC command type %i", client->current_command); 575 wlr_log(L_INFO, "Unknown IPC command type %i", client->current_command);
488 goto exit_cleanup; 576 goto exit_cleanup;
diff --git a/sway/meson.build b/sway/meson.build
index 8bddb11b..1e7ee7ae 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -6,6 +6,7 @@ sway_sources = files(
6 'input/seat.c', 6 'input/seat.c',
7 'input/cursor.c', 7 'input/cursor.c',
8 'input/keyboard.c', 8 'input/keyboard.c',
9 'commands/bar.c',
9 'commands/bind.c', 10 'commands/bind.c',
10 'commands/exit.c', 11 'commands/exit.c',
11 'commands/exec.c', 12 'commands/exec.c',
@@ -15,10 +16,36 @@ sway_sources = files(
15 'commands/include.c', 16 'commands/include.c',
16 'commands/input.c', 17 'commands/input.c',
17 'commands/layout.c', 18 'commands/layout.c',
19 'commands/mode.c',
18 'commands/seat.c', 20 'commands/seat.c',
19 'commands/seat/attach.c', 21 'commands/seat/attach.c',
20 'commands/seat/fallback.c', 22 'commands/seat/fallback.c',
21 'commands/set.c', 23 'commands/set.c',
24 'commands/swaybg_command.c',
25 'commands/bar/activate_button.c',
26 'commands/bar/binding_mode_indicator.c',
27 'commands/bar/bindsym.c',
28 'commands/bar/colors.c',
29 'commands/bar/context_button.c',
30 'commands/bar/font.c',
31 'commands/bar/height.c',
32 'commands/bar/hidden_state.c',
33 'commands/bar/icon_theme.c',
34 'commands/bar/id.c',
35 'commands/bar/mode.c',
36 'commands/bar/modifier.c',
37 'commands/bar/output.c',
38 'commands/bar/pango_markup.c',
39 'commands/bar/position.c',
40 'commands/bar/secondary_button.c',
41 'commands/bar/separator_symbol.c',
42 'commands/bar/status_command.c',
43 'commands/bar/strip_workspace_numbers.c',
44 'commands/bar/swaybar_command.c',
45 'commands/bar/tray_output.c',
46 'commands/bar/tray_padding.c',
47 'commands/bar/workspace_buttons.c',
48 'commands/bar/wrap_scroll.c',
22 'commands/input/accel_profile.c', 49 'commands/input/accel_profile.c',
23 'commands/input/click_method.c', 50 'commands/input/click_method.c',
24 'commands/input/drag_lock.c', 51 'commands/input/drag_lock.c',
@@ -39,6 +66,7 @@ sway_sources = files(
39 'commands/reload.c', 66 'commands/reload.c',
40 'commands/workspace.c', 67 'commands/workspace.c',
41 'config.c', 68 'config.c',
69 'config/bar.c',
42 'config/output.c', 70 'config/output.c',
43 'config/seat.c', 71 'config/seat.c',
44 'config/input.c', 72 'config/input.c',
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 @@
8#include <wlr/render/gles2.h> 8#include <wlr/render/gles2.h>
9#include <wlr/types/wlr_compositor.h> 9#include <wlr/types/wlr_compositor.h>
10#include <wlr/types/wlr_layer_shell.h> 10#include <wlr/types/wlr_layer_shell.h>
11#include <wlr/types/wlr_screenshooter.h>
12#include <wlr/types/wlr_gamma_control.h>
11#include <wlr/types/wlr_wl_shell.h> 13#include <wlr/types/wlr_wl_shell.h>
12// TODO WLR: make Xwayland optional 14// TODO WLR: make Xwayland optional
13#include <wlr/xwayland.h> 15#include <wlr/xwayland.h>
@@ -45,10 +47,12 @@ bool server_init(struct sway_server *server) {
45 47
46 server->compositor = wlr_compositor_create( 48 server->compositor = wlr_compositor_create(
47 server->wl_display, server->renderer); 49 server->wl_display, server->renderer);
48
49 server->data_device_manager = 50 server->data_device_manager =
50 wlr_data_device_manager_create(server->wl_display); 51 wlr_data_device_manager_create(server->wl_display);
51 52
53 wlr_screenshooter_create(server->wl_display);
54 wlr_gamma_control_manager_create(server->wl_display);
55
52 server->new_output.notify = handle_new_output; 56 server->new_output.notify = handle_new_output;
53 wl_signal_add(&server->backend->events.new_output, &server->new_output); 57 wl_signal_add(&server->backend->events.new_output, &server->new_output);
54 58
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.
43 Sets variable $name to _value_. You can use the new variable in the arguments 43 Sets variable $name to _value_. You can use the new variable in the arguments
44 of future commands. 44 of future commands.
45 45
46**swaybg_command** <command>::
47 Executes custom bg command, default is _swaybg_.
48
46The following commands cannot be used directly in the configuration file. 49The following commands cannot be used directly in the configuration file.
47They are expected to be used with **bindsym** or at runtime through **swaymsg**(1). 50They are expected to be used with **bindsym** or at runtime through **swaymsg**(1).
48 51
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 6a6861f3..2eac812e 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -131,8 +131,8 @@ struct sway_container *container_output_create(
131 } 131 }
132 132
133 apply_output_config(oc, output); 133 apply_output_config(oc, output);
134
135 container_add_child(&root_container, output); 134 container_add_child(&root_container, output);
135 load_swaybars();
136 136
137 // Create workspace 137 // Create workspace
138 char *ws_name = workspace_next_name(output->name); 138 char *ws_name = workspace_next_name(output->name);
diff --git a/swaybar/bar.c b/swaybar/bar.c
index f12923a8..0fc41517 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -1,390 +1,198 @@
1#define _XOPEN_SOURCE 500 1#define _XOPEN_SOURCE 500
2#include <assert.h>
3#include <errno.h>
4#include <fcntl.h>
5#include <poll.h>
6#include <signal.h>
2#include <stdlib.h> 7#include <stdlib.h>
3#include <unistd.h>
4#include <string.h> 8#include <string.h>
5#include <fcntl.h>
6#include <errno.h>
7#include <sys/wait.h> 9#include <sys/wait.h>
8#include <signal.h> 10#include <unistd.h>
9#include <poll.h> 11#include <wayland-client.h>
10#ifdef __FreeBSD__ 12#include <wlr/util/log.h>
11#include <dev/evdev/input-event-codes.h>
12#else
13#include <linux/input-event-codes.h>
14#endif
15#ifdef ENABLE_TRAY
16#include <dbus/dbus.h>
17#include "swaybar/tray/sni_watcher.h"
18#include "swaybar/tray/tray.h"
19#include "swaybar/tray/sni.h"
20#endif
21#include "swaybar/ipc.h"
22#include "swaybar/render.h" 13#include "swaybar/render.h"
23#include "swaybar/config.h" 14#include "swaybar/config.h"
24#include "swaybar/status_line.h"
25#include "swaybar/event_loop.h" 15#include "swaybar/event_loop.h"
16#include "swaybar/status_line.h"
26#include "swaybar/bar.h" 17#include "swaybar/bar.h"
18#include "swaybar/ipc.h"
27#include "ipc-client.h" 19#include "ipc-client.h"
28#include "list.h" 20#include "list.h"
29#include "log.h" 21#include "pango.h"
22#include "pool-buffer.h"
23#include "wlr-layer-shell-unstable-v1-client-protocol.h"
30 24
31static void bar_init(struct bar *bar) { 25static void bar_init(struct swaybar *bar) {
32 bar->config = init_config(); 26 bar->config = init_config();
33 bar->status = init_status_line(); 27 wl_list_init(&bar->outputs);
34 bar->outputs = create_list();
35} 28}
36 29
37static void spawn_status_cmd_proc(struct bar *bar) { 30struct swaybar_output *new_output(const char *name) {
38 if (bar->config->status_command) { 31 struct swaybar_output *output = malloc(sizeof(struct swaybar_output));
39 int pipe_read_fd[2];
40 int pipe_write_fd[2];
41
42 if (pipe(pipe_read_fd) != 0) {
43 sway_log(L_ERROR, "Unable to create pipes for status_command fork");
44 return;
45 }
46 if (pipe(pipe_write_fd) != 0) {
47 sway_log(L_ERROR, "Unable to create pipe for status_command fork (write)");
48 close(pipe_read_fd[0]);
49 close(pipe_read_fd[1]);
50 return;
51 }
52
53 bar->status_command_pid = fork();
54 if (bar->status_command_pid == 0) {
55 close(pipe_read_fd[0]);
56 dup2(pipe_read_fd[1], STDOUT_FILENO);
57 close(pipe_read_fd[1]);
58
59 dup2(pipe_write_fd[0], STDIN_FILENO);
60 close(pipe_write_fd[0]);
61 close(pipe_write_fd[1]);
62
63 char *const cmd[] = {
64 "sh",
65 "-c",
66 bar->config->status_command,
67 NULL,
68 };
69 execvp(cmd[0], cmd);
70 return;
71 }
72
73 close(pipe_read_fd[1]);
74 bar->status_read_fd = pipe_read_fd[0];
75 fcntl(bar->status_read_fd, F_SETFL, O_NONBLOCK);
76
77 close(pipe_write_fd[0]);
78 bar->status_write_fd = pipe_write_fd[1];
79 fcntl(bar->status_write_fd, F_SETFL, O_NONBLOCK);
80 }
81}
82
83struct output *new_output(const char *name) {
84 struct output *output = malloc(sizeof(struct output));
85 output->name = strdup(name); 32 output->name = strdup(name);
86 output->window = NULL;
87 output->registry = NULL;
88 output->workspaces = create_list();
89#ifdef ENABLE_TRAY
90 output->items = create_list();
91#endif
92 return output; 33 return output;
93} 34}
94 35
95static void mouse_button_notify(struct window *window, int x, int y, 36static void layer_surface_configure(void *data,
96 uint32_t button, uint32_t state_w) { 37 struct zwlr_layer_surface_v1 *surface,
97 sway_log(L_DEBUG, "Mouse button %d clicked at %d %d %d", button, x, y, state_w); 38 uint32_t serial, uint32_t width, uint32_t height) {
98 if (!state_w) { 39 struct swaybar_output *output = data;
99 return; 40 output->width = width;
100 } 41 output->height = height;
101 42 zwlr_layer_surface_v1_ack_configure(surface, serial);
102 struct output *clicked_output = NULL; 43 render_frame(output->bar, output);
103 for (int i = 0; i < swaybar.outputs->length; i++) { 44}
104 struct output *output = swaybar.outputs->items[i];
105 if (window == output->window) {
106 clicked_output = output;
107 break;
108 }
109 }
110
111 if (!sway_assert(clicked_output != NULL, "Got pointer event for non-existing output")) {
112 return;
113 }
114
115 double button_x = 0.5;
116 for (int i = 0; i < clicked_output->workspaces->length; i++) {
117 struct workspace *workspace = clicked_output->workspaces->items[i];
118 int button_width, button_height;
119
120 workspace_button_size(window, workspace->name, &button_width, &button_height);
121 45
122 button_x += button_width; 46static void layer_surface_closed(void *_output,
123 if (x <= button_x) { 47 struct zwlr_layer_surface_v1 *surface) {
124 ipc_send_workspace_command(workspace->name); 48 // TODO: Deal with hotplugging
125 break; 49 struct swaybar_output *output = _output;
126 } 50 zwlr_layer_surface_v1_destroy(output->layer_surface);
127 } 51 wl_surface_destroy(output->surface);
52}
128 53
129 switch (button) { 54struct zwlr_layer_surface_v1_listener layer_surface_listener = {
130 case BTN_LEFT: 55 .configure = layer_surface_configure,
131 status_line_mouse_event(&swaybar, x, y, 1); 56 .closed = layer_surface_closed,
132 break; 57};
133 case BTN_MIDDLE: 58
134 status_line_mouse_event(&swaybar, x, y, 2); 59static void handle_global(void *data, struct wl_registry *registry,
135 break; 60 uint32_t name, const char *interface, uint32_t version) {
136 case BTN_RIGHT: 61 struct swaybar *bar = data;
137 status_line_mouse_event(&swaybar, x, y, 3); 62 if (strcmp(interface, wl_compositor_interface.name) == 0) {
138 break; 63 bar->compositor = wl_registry_bind(registry, name,
64 &wl_compositor_interface, 1);
65 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
66 bar->shm = wl_registry_bind(registry, name,
67 &wl_shm_interface, 1);
68 } else if (strcmp(interface, wl_output_interface.name) == 0) {
69 static size_t index = 0;
70 struct swaybar_output *output =
71 calloc(1, sizeof(struct swaybar_output));
72 output->bar = bar;
73 output->output = wl_registry_bind(registry, name,
74 &wl_output_interface, 1);
75 output->index = index++;
76 wl_list_init(&output->workspaces);
77 wl_list_insert(&bar->outputs, &output->link);
78 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
79 bar->layer_shell = wl_registry_bind(
80 registry, name, &zwlr_layer_shell_v1_interface, 1);
139 } 81 }
140
141#ifdef ENABLE_TRAY
142 tray_mouse_event(clicked_output, x, y, button, state_w);
143#endif
144
145} 82}
146 83
147static void mouse_scroll_notify(struct window *window, enum scroll_direction direction) { 84static void handle_global_remove(void *data, struct wl_registry *registry,
148 sway_log(L_DEBUG, "Mouse wheel scrolled %s", direction == SCROLL_UP ? "up" : "down"); 85 uint32_t name) {
86 // who cares
87}
149 88
150 // If there are status blocks and click_events are enabled 89static const struct wl_registry_listener registry_listener = {
151 // check if the position is within the status area and if so 90 .global = handle_global,
152 // tell the status line to output the event and skip workspace 91 .global_remove = handle_global_remove,
153 // switching below. 92};
154 int num_blocks = swaybar.status->block_line->length;
155 if (swaybar.status->click_events && num_blocks > 0) {
156 struct status_block *first_block = swaybar.status->block_line->items[0];
157 int x = window->pointer_input.last_x;
158 int y = window->pointer_input.last_y;
159 if (x > first_block->x) {
160 if (direction == SCROLL_UP) {
161 status_line_mouse_event(&swaybar, x, y, 4);
162 } else {
163 status_line_mouse_event(&swaybar, x, y, 5);
164 }
165 return;
166 }
167 }
168 93
169 if (!swaybar.config->wrap_scroll) { 94static void render_all_frames(struct swaybar *bar) {
170 // Find output this window lives on 95 struct swaybar_output *output;
171 int i; 96 wl_list_for_each(output, &bar->outputs, link) {
172 struct output *output = NULL; 97 render_frame(bar, output);
173 for (i = 0; i < swaybar.outputs->length; ++i) {
174 output = swaybar.outputs->items[i];
175 if (output->window == window) {
176 break;
177 }
178 }
179 if (!sway_assert(i != swaybar.outputs->length, "Unknown window in scroll event")) {
180 return;
181 }
182 int focused = -1;
183 for (i = 0; i < output->workspaces->length; ++i) {
184 struct workspace *ws = output->workspaces->items[i];
185 if (ws->focused) {
186 focused = i;
187 break;
188 }
189 }
190 if (!sway_assert(focused != -1, "Scroll wheel event received on inactive output")) {
191 return;
192 }
193 if ((focused == 0 && direction == SCROLL_UP) ||
194 (focused == output->workspaces->length - 1 && direction == SCROLL_DOWN)) {
195 // Do not wrap
196 return;
197 }
198 } 98 }
199
200 const char *workspace_name = direction == SCROLL_UP ? "prev_on_output" : "next_on_output";
201 ipc_send_workspace_command(workspace_name);
202} 99}
203 100
204void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id) { 101void bar_setup(struct swaybar *bar,
205 /* initialize bar with default values */ 102 const char *socket_path, const char *bar_id) {
206 bar_init(bar); 103 bar_init(bar);
207
208 /* Initialize event loop lists */
209 init_event_loop(); 104 init_event_loop();
210 105
211 /* connect to sway ipc */
212 bar->ipc_socketfd = ipc_open_socket(socket_path); 106 bar->ipc_socketfd = ipc_open_socket(socket_path);
213 bar->ipc_event_socketfd = ipc_open_socket(socket_path); 107 bar->ipc_event_socketfd = ipc_open_socket(socket_path);
214 108 ipc_initialize(bar, bar_id);
215 ipc_bar_init(bar, bar_id); 109 if (bar->config->status_command) {
216 110 bar->status = status_line_init(bar->config->status_command);
217 int i;
218 for (i = 0; i < bar->outputs->length; ++i) {
219 struct output *bar_output = bar->outputs->items[i];
220
221 bar_output->registry = registry_poll();
222
223 if (!bar_output->registry->desktop_shell) {
224 sway_abort("swaybar requires the compositor to support the desktop-shell extension.");
225 }
226
227 struct output_state *output = bar_output->registry->outputs->items[bar_output->idx];
228
229 bar_output->window = window_setup(bar_output->registry,
230 output->width / output->scale, 30, output->scale, false);
231 if (!bar_output->window) {
232 sway_abort("Failed to create window.");
233 }
234 desktop_shell_set_panel(bar_output->registry->desktop_shell,
235 output->output, bar_output->window->surface);
236 desktop_shell_set_panel_position(bar_output->registry->desktop_shell,
237 bar->config->position);
238
239 window_make_shell(bar_output->window);
240
241 /* set font */
242 bar_output->window->font = bar->config->font;
243
244 /* set mouse event callbacks */
245 bar_output->window->pointer_input.notify_button = mouse_button_notify;
246 bar_output->window->pointer_input.notify_scroll = mouse_scroll_notify;
247
248 /* set window height */
249 set_window_height(bar_output->window, bar->config->height);
250 }
251 /* spawn status command */
252 spawn_status_cmd_proc(bar);
253
254#ifdef ENABLE_TRAY
255 init_tray(bar);
256#endif
257}
258
259bool dirty = true;
260
261static void respond_ipc(int fd, short mask, void *_bar) {
262 struct bar *bar = (struct bar *)_bar;
263 sway_log(L_DEBUG, "Got IPC event.");
264 dirty = handle_ipc_event(bar);
265}
266
267static void respond_command(int fd, short mask, void *_bar) {
268 struct bar *bar = (struct bar *)_bar;
269 dirty = handle_status_line(bar);
270}
271
272static void respond_output(int fd, short mask, void *_output) {
273 struct output *output = (struct output *)_output;
274 if (wl_display_dispatch(output->registry->display) == -1) {
275 sway_log(L_ERROR, "failed to dispatch wl: %d", errno);
276 } 111 }
277}
278 112
279void bar_run(struct bar *bar) { 113 assert(bar->display = wl_display_connect(NULL));
280 add_event(bar->ipc_event_socketfd, POLLIN, respond_ipc, bar);
281 add_event(bar->status_read_fd, POLLIN, respond_command, bar);
282 114
283 int i; 115 struct wl_registry *registry = wl_display_get_registry(bar->display);
284 for (i = 0; i < bar->outputs->length; ++i) { 116 wl_registry_add_listener(registry, &registry_listener, bar);
285 struct output *output = bar->outputs->items[i]; 117 wl_display_roundtrip(bar->display);
286 add_event(wl_display_get_fd(output->registry->display), 118 assert(bar->compositor && bar->layer_shell && bar->shm);
287 POLLIN, respond_output, output);
288 }
289 119
290 while (1) { 120 // TODO: we might not necessarily be meant to do all of the outputs
291 if (dirty) { 121 struct swaybar_output *output;
292 int i; 122 wl_list_for_each(output, &bar->outputs, link) {
293 for (i = 0; i < bar->outputs->length; ++i) { 123 struct config_output *coutput;
294 struct output *output = bar->outputs->items[i]; 124 wl_list_for_each(coutput, &bar->config->outputs, link) {
295 if (window_prerender(output->window) && output->window->cairo) { 125 if (coutput->index != output->index) {
296 render(output, bar->config, bar->status); 126 continue;
297 window_render(output->window);
298 wl_display_flush(output->registry->display);
299 }
300 } 127 }
128 output->name = strdup(coutput->name);
129 assert(output->surface = wl_compositor_create_surface(
130 bar->compositor));
131 output->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
132 bar->layer_shell, output->surface, output->output,
133 ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel");
134 assert(output->layer_surface);
135 zwlr_layer_surface_v1_add_listener(output->layer_surface,
136 &layer_surface_listener, output);
137 zwlr_layer_surface_v1_set_anchor(output->layer_surface,
138 bar->config->position);
139 break;
301 } 140 }
302
303 dirty = false;
304
305 event_loop_poll();
306#ifdef ENABLE_TRAY
307 dispatch_dbus();
308#endif
309 } 141 }
142 ipc_get_workspaces(bar);
143 render_all_frames(bar);
310} 144}
311 145
312void free_workspaces(list_t *workspaces) { 146static void display_in(int fd, short mask, void *_bar) {
313 int i; 147 struct swaybar *bar = (struct swaybar *)_bar;
314 for (i = 0; i < workspaces->length; ++i) { 148 if (wl_display_dispatch(bar->display) == -1) {
315 struct workspace *ws = workspaces->items[i]; 149 bar_teardown(bar);
316 free(ws->name); 150 exit(0);
317 free(ws);
318 } 151 }
319 list_free(workspaces);
320} 152}
321 153
322static void free_output(struct output *output) { 154static void ipc_in(int fd, short mask, void *_bar) {
323 window_teardown(output->window); 155 struct swaybar *bar = (struct swaybar *)_bar;
324 if (output->registry) { 156 if (handle_ipc_event(bar)) {
325 registry_teardown(output->registry); 157 render_all_frames(bar);
326 } 158 }
159}
327 160
328 free(output->name); 161static void status_in(int fd, short mask, void *_bar) {
329 162 struct swaybar *bar = (struct swaybar *)_bar;
330 if (output->workspaces) { 163 if (handle_status_readable(bar->status)) {
331 free_workspaces(output->workspaces); 164 render_all_frames(bar);
332 } 165 }
333
334 free(output);
335} 166}
336 167
337static void free_outputs(list_t *outputs) { 168void bar_run(struct swaybar *bar) {
338 int i; 169 add_event(wl_display_get_fd(bar->display), POLLIN, display_in, bar);
339 for (i = 0; i < outputs->length; ++i) { 170 add_event(bar->ipc_event_socketfd, POLLIN, ipc_in, bar);
340 free_output(outputs->items[i]); 171 if (bar->status) {
172 add_event(bar->status->read_fd, POLLIN, status_in, bar);
173 }
174 while (1) {
175 event_loop_poll();
341 } 176 }
342 list_free(outputs);
343} 177}
344 178
345static void terminate_status_command(pid_t pid) { 179static void free_outputs(struct wl_list *list) {
346 if (pid) { 180 struct swaybar_output *output, *tmp;
347 // terminate status_command process 181 wl_list_for_each_safe(output, tmp, list, link) {
348 int ret = kill(pid, SIGTERM); 182 wl_list_remove(&output->link);
349 if (ret != 0) { 183 free(output->name);
350 sway_log(L_ERROR, "Unable to terminate status_command [pid: %d]", pid); 184 free(output);
351 } else {
352 int status;
353 waitpid(pid, &status, 0);
354 }
355 } 185 }
356} 186}
357 187
358void bar_teardown(struct bar *bar) { 188void bar_teardown(struct swaybar *bar) {
189 free_outputs(&bar->outputs);
359 if (bar->config) { 190 if (bar->config) {
360 free_config(bar->config); 191 free_config(bar->config);
361 } 192 }
362 193 close(bar->ipc_event_socketfd);
363 if (bar->outputs) { 194 close(bar->ipc_socketfd);
364 free_outputs(bar->outputs);
365 }
366
367 if (bar->status) { 195 if (bar->status) {
368 free_status_line(bar->status); 196 status_line_free(bar->status);
369 }
370
371 /* close sockets/pipes */
372 if (bar->status_read_fd) {
373 close(bar->status_read_fd);
374 }
375
376 if (bar->status_write_fd) {
377 close(bar->status_write_fd);
378 }
379
380 if (bar->ipc_socketfd) {
381 close(bar->ipc_socketfd);
382 } 197 }
383
384 if (bar->ipc_event_socketfd) {
385 close(bar->ipc_event_socketfd);
386 }
387
388 /* terminate status command process */
389 terminate_status_command(bar->status_command_pid);
390} 198}
diff --git a/swaybar/config.c b/swaybar/config.c
index 8fe552f2..9169ad27 100644
--- a/swaybar/config.c
+++ b/swaybar/config.c
@@ -1,40 +1,32 @@
1#define _XOPEN_SOURCE 500 1#define _XOPEN_SOURCE 500
2#include <stdlib.h> 2#include <stdlib.h>
3#include <string.h> 3#include <string.h>
4#include "wayland-desktop-shell-client-protocol.h"
5#include "log.h"
6#include "swaybar/config.h" 4#include "swaybar/config.h"
5#include "wlr-layer-shell-unstable-v1-client-protocol.h"
7 6
8uint32_t parse_position(const char *position) { 7uint32_t parse_position(const char *position) {
8 uint32_t horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
9 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
10 uint32_t vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
11 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
9 if (strcmp("top", position) == 0) { 12 if (strcmp("top", position) == 0) {
10 return DESKTOP_SHELL_PANEL_POSITION_TOP; 13 return ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | horiz;
11 } else if (strcmp("bottom", position) == 0) { 14 } else if (strcmp("bottom", position) == 0) {
12 return DESKTOP_SHELL_PANEL_POSITION_BOTTOM; 15 return ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | horiz;
13 } else if (strcmp("left", position) == 0) { 16 } else if (strcmp("left", position) == 0) {
14 return DESKTOP_SHELL_PANEL_POSITION_LEFT; 17 return ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | vert;
15 } else if (strcmp("right", position) == 0) { 18 } else if (strcmp("right", position) == 0) {
16 return DESKTOP_SHELL_PANEL_POSITION_RIGHT; 19 return ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | vert;
17 } else { 20 } else {
18 return DESKTOP_SHELL_PANEL_POSITION_BOTTOM; 21 return ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | horiz;
19 } 22 }
20} 23}
21 24
22char *parse_font(const char *font) { 25struct swaybar_config *init_config() {
23 char *new_font = NULL; 26 struct swaybar_config *config = calloc(1, sizeof(struct swaybar_config));
24 if (strncmp("pango:", font, 6) == 0) {
25 font += 6;
26 }
27
28 new_font = strdup(font);
29
30 return new_font;
31}
32
33struct config *init_config() {
34 struct config *config = calloc(1, sizeof(struct config));
35 config->status_command = NULL; 27 config->status_command = NULL;
36 config->pango_markup = false; 28 config->pango_markup = false;
37 config->position = DESKTOP_SHELL_PANEL_POSITION_BOTTOM; 29 config->position = parse_position("bottom");
38 config->font = strdup("monospace 10"); 30 config->font = strdup("monospace 10");
39 config->mode = NULL; 31 config->mode = NULL;
40 config->sep_symbol = NULL; 32 config->sep_symbol = NULL;
@@ -42,27 +34,16 @@ struct config *init_config() {
42 config->binding_mode_indicator = true; 34 config->binding_mode_indicator = true;
43 config->wrap_scroll = false; 35 config->wrap_scroll = false;
44 config->workspace_buttons = true; 36 config->workspace_buttons = true;
45 config->all_outputs = false; 37 wl_list_init(&config->outputs);
46 config->outputs = create_list();
47 38
48 /* height */ 39 /* height */
49 config->height = 0; 40 config->height = 0;
50 41
51#ifdef ENABLE_TRAY
52 config->tray_output = NULL;
53 config->icon_theme = NULL;
54 config->tray_padding = 2;
55 /**
56 * These constants are used by wayland and are defined in
57 * linux/input-event-codes.h
58 */
59 config->activate_button = 0x110; /* BTN_LEFT */
60 config->context_button = 0x111; /* BTN_RIGHT */
61#endif
62
63 /* colors */ 42 /* colors */
64 config->colors.background = 0x000000FF; 43 config->colors.background = 0x000000FF;
44 config->colors.focused_background = 0x000000FF;
65 config->colors.statusline = 0xFFFFFFFF; 45 config->colors.statusline = 0xFFFFFFFF;
46 config->colors.focused_statusline = 0xFFFFFFFF;
66 config->colors.separator = 0x666666FF; 47 config->colors.separator = 0x666666FF;
67 48
68 config->colors.focused_workspace.border = 0x4C7899FF; 49 config->colors.focused_workspace.border = 0x4C7899FF;
@@ -88,7 +69,7 @@ struct config *init_config() {
88 return config; 69 return config;
89} 70}
90 71
91void free_config(struct config *config) { 72void free_config(struct swaybar_config *config) {
92 free(config->status_command); 73 free(config->status_command);
93 free(config->font); 74 free(config->font);
94 free(config->mode); 75 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 @@
4#include <string.h> 4#include <string.h>
5#include <strings.h> 5#include <strings.h>
6#include <poll.h> 6#include <poll.h>
7#include "swaybar/bar.h" 7#include <time.h>
8#include "swaybar/event_loop.h" 8#include "swaybar/event_loop.h"
9#include "list.h" 9#include "list.h"
10#include "log.h"
11 10
12struct event_item { 11struct event_item {
13 void(*cb)(int fd, short mask, void *data); 12 void (*cb)(int fd, short mask, void *data);
14 void *data; 13 void *data;
15}; 14};
16 15
17struct timer_item { 16struct timer_item {
18 timer_t timer; 17 timer_t timer;
19 void(*cb)(timer_t timer, void *data); 18 void (*cb)(timer_t timer, void *data);
20 void *data; 19 void *data;
21}; 20};
22 21
@@ -138,7 +137,8 @@ void event_loop_poll() {
138void init_event_loop() { 137void init_event_loop() {
139 event_loop.fds.length = 0; 138 event_loop.fds.length = 0;
140 event_loop.fds.capacity = 10; 139 event_loop.fds.capacity = 10;
141 event_loop.fds.items = malloc(event_loop.fds.capacity * sizeof(struct pollfd)); 140 event_loop.fds.items = malloc(
141 event_loop.fds.capacity * sizeof(struct pollfd));
142 event_loop.items = create_list(); 142 event_loop.items = create_list();
143 event_loop.timers = create_list(); 143 event_loop.timers = create_list();
144} 144}
diff --git a/swaybar/ipc.c b/swaybar/ipc.c
index 2d2b3b69..a82904bd 100644
--- a/swaybar/ipc.c
+++ b/swaybar/ipc.c
@@ -1,37 +1,132 @@
1#define _XOPEN_SOURCE 500 1#define _XOPEN_SOURCE 500
2#include <limits.h>
2#include <string.h> 3#include <string.h>
3#include <strings.h> 4#include <strings.h>
4#include <json-c/json.h> 5#include <json-c/json.h>
6#include <wlr/util/log.h>
5#include "swaybar/config.h" 7#include "swaybar/config.h"
6#include "swaybar/ipc.h" 8#include "swaybar/ipc.h"
7#include "ipc-client.h" 9#include "ipc-client.h"
8#include "list.h"
9#include "log.h"
10 10
11void ipc_send_workspace_command(const char *workspace_name) { 11char *parse_font(const char *font) {
12 uint32_t size = strlen("workspace \"\"") + strlen(workspace_name) + 1; 12 char *new_font = NULL;
13 13 if (strncmp("pango:", font, 6) == 0) {
14 char command[size]; 14 font += 6;
15 sprintf(command, "workspace \"%s\"", workspace_name); 15 }
16 new_font = strdup(font);
17 return new_font;
18}
16 19
17 ipc_single_command(swaybar.ipc_socketfd, IPC_COMMAND, command, &size); 20static void ipc_parse_colors(
21 struct swaybar_config *config, json_object *colors) {
22 json_object *background, *statusline, *separator;
23 json_object *focused_background, *focused_statusline, *focused_separator;
24 json_object *focused_workspace_border, *focused_workspace_bg, *focused_workspace_text;
25 json_object *inactive_workspace_border, *inactive_workspace_bg, *inactive_workspace_text;
26 json_object *active_workspace_border, *active_workspace_bg, *active_workspace_text;
27 json_object *urgent_workspace_border, *urgent_workspace_bg, *urgent_workspace_text;
28 json_object *binding_mode_border, *binding_mode_bg, *binding_mode_text;
29 json_object_object_get_ex(colors, "background", &background);
30 json_object_object_get_ex(colors, "statusline", &statusline);
31 json_object_object_get_ex(colors, "separator", &separator);
32 json_object_object_get_ex(colors, "focused_background", &focused_background);
33 json_object_object_get_ex(colors, "focused_statusline", &focused_statusline);
34 json_object_object_get_ex(colors, "focused_separator", &focused_separator);
35 json_object_object_get_ex(colors, "focused_workspace_border", &focused_workspace_border);
36 json_object_object_get_ex(colors, "focused_workspace_bg", &focused_workspace_bg);
37 json_object_object_get_ex(colors, "focused_workspace_text", &focused_workspace_text);
38 json_object_object_get_ex(colors, "active_workspace_border", &active_workspace_border);
39 json_object_object_get_ex(colors, "active_workspace_bg", &active_workspace_bg);
40 json_object_object_get_ex(colors, "active_workspace_text", &active_workspace_text);
41 json_object_object_get_ex(colors, "inactive_workspace_border", &inactive_workspace_border);
42 json_object_object_get_ex(colors, "inactive_workspace_bg", &inactive_workspace_bg);
43 json_object_object_get_ex(colors, "inactive_workspace_text", &inactive_workspace_text);
44 json_object_object_get_ex(colors, "urgent_workspace_border", &urgent_workspace_border);
45 json_object_object_get_ex(colors, "urgent_workspace_bg", &urgent_workspace_bg);
46 json_object_object_get_ex(colors, "urgent_workspace_text", &urgent_workspace_text);
47 json_object_object_get_ex(colors, "binding_mode_border", &binding_mode_border);
48 json_object_object_get_ex(colors, "binding_mode_bg", &binding_mode_bg);
49 json_object_object_get_ex(colors, "binding_mode_text", &binding_mode_text);
50 if (background) {
51 config->colors.background = parse_color(
52 json_object_get_string(background));
53 }
54 if (statusline) {
55 config->colors.statusline = parse_color(
56 json_object_get_string(statusline));
57 }
58 if (separator) {
59 config->colors.separator = parse_color(
60 json_object_get_string(separator));
61 }
62 if (focused_background) {
63 config->colors.focused_background = parse_color(
64 json_object_get_string(focused_background));
65 }
66 if (focused_statusline) {
67 config->colors.focused_statusline = parse_color(
68 json_object_get_string(focused_statusline));
69 }
70 if (focused_separator) {
71 config->colors.focused_separator = parse_color(
72 json_object_get_string(focused_separator));
73 }
74 if (focused_workspace_border) {
75 config->colors.focused_workspace.border = parse_color(
76 json_object_get_string(focused_workspace_border));
77 }
78 if (focused_workspace_bg) {
79 config->colors.focused_workspace.background = parse_color(
80 json_object_get_string(focused_workspace_bg));
81 }
82 if (focused_workspace_text) {
83 config->colors.focused_workspace.text = parse_color(
84 json_object_get_string(focused_workspace_text));
85 }
86 if (active_workspace_border) {
87 config->colors.active_workspace.border = parse_color(
88 json_object_get_string(active_workspace_border));
89 }
90 if (active_workspace_bg) {
91 config->colors.active_workspace.background = parse_color(
92 json_object_get_string(active_workspace_bg));
93 }
94 if (active_workspace_text) {
95 config->colors.active_workspace.text = parse_color(
96 json_object_get_string(active_workspace_text));
97 }
98 if (inactive_workspace_border) {
99 config->colors.inactive_workspace.border = parse_color(
100 json_object_get_string(inactive_workspace_border));
101 }
102 if (inactive_workspace_bg) {
103 config->colors.inactive_workspace.background = parse_color(
104 json_object_get_string(inactive_workspace_bg));
105 }
106 if (inactive_workspace_text) {
107 config->colors.inactive_workspace.text = parse_color(
108 json_object_get_string(inactive_workspace_text));
109 }
110 if (binding_mode_border) {
111 config->colors.binding_mode.border = parse_color(
112 json_object_get_string(binding_mode_border));
113 }
114 if (binding_mode_bg) {
115 config->colors.binding_mode.background = parse_color(
116 json_object_get_string(binding_mode_bg));
117 }
118 if (binding_mode_text) {
119 config->colors.binding_mode.text = parse_color(
120 json_object_get_string(binding_mode_text));
121 }
18} 122}
19 123
20static void ipc_parse_config(struct config *config, const char *payload) { 124static void ipc_parse_config(
125 struct swaybar_config *config, const char *payload) {
21 json_object *bar_config = json_tokener_parse(payload); 126 json_object *bar_config = json_tokener_parse(payload);
22 json_object *markup, *mode, *hidden_bar, *position, *status_command; 127 json_object *markup, *mode, *hidden_bar, *position, *status_command;
23 json_object *font, *bar_height, *wrap_scroll, *workspace_buttons, *strip_workspace_numbers; 128 json_object *font, *bar_height, *wrap_scroll, *workspace_buttons, *strip_workspace_numbers;
24 json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol, *outputs; 129 json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol, *outputs;
25#ifdef ENABLE_TRAY
26 json_object *tray_output, *icon_theme, *tray_padding, *activate_button, *context_button;
27 json_object *secondary_button;
28 json_object_object_get_ex(bar_config, "tray_output", &tray_output);
29 json_object_object_get_ex(bar_config, "icon_theme", &icon_theme);
30 json_object_object_get_ex(bar_config, "tray_padding", &tray_padding);
31 json_object_object_get_ex(bar_config, "activate_button", &activate_button);
32 json_object_object_get_ex(bar_config, "context_button", &context_button);
33 json_object_object_get_ex(bar_config, "secondary_button", &secondary_button);
34#endif
35 json_object_object_get_ex(bar_config, "mode", &mode); 130 json_object_object_get_ex(bar_config, "mode", &mode);
36 json_object_object_get_ex(bar_config, "hidden_bar", &hidden_bar); 131 json_object_object_get_ex(bar_config, "hidden_bar", &hidden_bar);
37 json_object_object_get_ex(bar_config, "position", &position); 132 json_object_object_get_ex(bar_config, "position", &position);
@@ -47,230 +142,98 @@ static void ipc_parse_config(struct config *config, const char *payload) {
47 json_object_object_get_ex(bar_config, "colors", &colors); 142 json_object_object_get_ex(bar_config, "colors", &colors);
48 json_object_object_get_ex(bar_config, "outputs", &outputs); 143 json_object_object_get_ex(bar_config, "outputs", &outputs);
49 json_object_object_get_ex(bar_config, "pango_markup", &markup); 144 json_object_object_get_ex(bar_config, "pango_markup", &markup);
50
51 if (status_command) { 145 if (status_command) {
52 free(config->status_command); 146 free(config->status_command);
53 config->status_command = strdup(json_object_get_string(status_command)); 147 config->status_command = strdup(json_object_get_string(status_command));
54 } 148 }
55
56 if (position) { 149 if (position) {
57 config->position = parse_position(json_object_get_string(position)); 150 config->position = parse_position(json_object_get_string(position));
58 } 151 }
59
60 if (font) { 152 if (font) {
61 free(config->font); 153 free(config->font);
62 config->font = parse_font(json_object_get_string(font)); 154 config->font = parse_font(json_object_get_string(font));
63 } 155 }
64
65 if (sep_symbol) { 156 if (sep_symbol) {
66 free(config->sep_symbol); 157 free(config->sep_symbol);
67 config->sep_symbol = strdup(json_object_get_string(sep_symbol)); 158 config->sep_symbol = strdup(json_object_get_string(sep_symbol));
68 } 159 }
69
70 if (strip_workspace_numbers) { 160 if (strip_workspace_numbers) {
71 config->strip_workspace_numbers = json_object_get_boolean(strip_workspace_numbers); 161 config->strip_workspace_numbers = json_object_get_boolean(strip_workspace_numbers);
72 } 162 }
73
74 if (binding_mode_indicator) { 163 if (binding_mode_indicator) {
75 config->binding_mode_indicator = json_object_get_boolean(binding_mode_indicator); 164 config->binding_mode_indicator = json_object_get_boolean(binding_mode_indicator);
76 } 165 }
77
78 if (wrap_scroll) { 166 if (wrap_scroll) {
79 config->wrap_scroll = json_object_get_boolean(wrap_scroll); 167 config->wrap_scroll = json_object_get_boolean(wrap_scroll);
80 } 168 }
81
82 if (workspace_buttons) { 169 if (workspace_buttons) {
83 config->workspace_buttons = json_object_get_boolean(workspace_buttons); 170 config->workspace_buttons = json_object_get_boolean(workspace_buttons);
84 } 171 }
85
86 if (bar_height) { 172 if (bar_height) {
87 config->height = json_object_get_int(bar_height); 173 config->height = json_object_get_int(bar_height);
88 } 174 }
89
90 if (markup) { 175 if (markup) {
91 config->pango_markup = json_object_get_boolean(markup); 176 config->pango_markup = json_object_get_boolean(markup);
92 } 177 }
93 178
94#ifdef ENABLE_TRAY 179 struct config_output *output, *tmp;
95 if (tray_output) { 180 wl_list_for_each_safe(output, tmp, &config->outputs, link) {
96 free(config->tray_output); 181 wl_list_remove(&output->link);
97 config->tray_output = strdup(json_object_get_string(tray_output)); 182 free(output->name);
98 } 183 free(output);
99
100 if (icon_theme) {
101 free(config->icon_theme);
102 config->icon_theme = strdup(json_object_get_string(icon_theme));
103 }
104
105 if (tray_padding) {
106 config->tray_padding = json_object_get_int(tray_padding);
107 }
108
109 if (activate_button) {
110 config->activate_button = json_object_get_int(activate_button);
111 }
112
113 if (context_button) {
114 config->context_button = json_object_get_int(context_button);
115 }
116
117 if (secondary_button) {
118 config->secondary_button = json_object_get_int(secondary_button);
119 }
120#endif
121
122 // free previous outputs list
123 int i;
124 for (i = 0; i < config->outputs->length; ++i) {
125 free(config->outputs->items[i]);
126 } 184 }
127 list_free(config->outputs);
128 config->outputs = create_list();
129
130 if (outputs) { 185 if (outputs) {
131 int length = json_object_array_length(outputs); 186 int length = json_object_array_length(outputs);
132 json_object *output; 187 for (int i = 0; i < length; ++i) {
133 const char *output_str; 188 json_object *output = json_object_array_get_idx(outputs, i);
134 for (i = 0; i < length; ++i) { 189 const char *name = json_object_get_string(output);
135 output = json_object_array_get_idx(outputs, i); 190 if (strcmp("*", name) == 0) {
136 output_str = json_object_get_string(output); 191 // TODO: do we need to clear out the list here or something
137 if (strcmp("*", output_str) == 0) {
138 config->all_outputs = true;
139 break; 192 break;
140 } 193 }
141 list_add(config->outputs, strdup(output_str)); 194 struct config_output *coutput = calloc(
195 1, sizeof(struct config_output));
196 coutput->name = strdup(name);
197 coutput->index = SIZE_MAX;
198 wl_list_insert(&config->outputs, &coutput->link);
142 } 199 }
143 } else {
144 config->all_outputs = true;
145 } 200 }
146 201
147 if (colors) { 202 if (colors) {
148 json_object *background, *statusline, *separator; 203 ipc_parse_colors(config, colors);
149 json_object *focused_background, *focused_statusline, *focused_separator;
150 json_object *focused_workspace_border, *focused_workspace_bg, *focused_workspace_text;
151 json_object *inactive_workspace_border, *inactive_workspace_bg, *inactive_workspace_text;
152 json_object *active_workspace_border, *active_workspace_bg, *active_workspace_text;
153 json_object *urgent_workspace_border, *urgent_workspace_bg, *urgent_workspace_text;
154 json_object *binding_mode_border, *binding_mode_bg, *binding_mode_text;
155 json_object_object_get_ex(colors, "background", &background);
156 json_object_object_get_ex(colors, "statusline", &statusline);
157 json_object_object_get_ex(colors, "separator", &separator);
158 json_object_object_get_ex(colors, "focused_background", &focused_background);
159 json_object_object_get_ex(colors, "focused_statusline", &focused_statusline);
160 json_object_object_get_ex(colors, "focused_separator", &focused_separator);
161 json_object_object_get_ex(colors, "focused_workspace_border", &focused_workspace_border);
162 json_object_object_get_ex(colors, "focused_workspace_bg", &focused_workspace_bg);
163 json_object_object_get_ex(colors, "focused_workspace_text", &focused_workspace_text);
164 json_object_object_get_ex(colors, "active_workspace_border", &active_workspace_border);
165 json_object_object_get_ex(colors, "active_workspace_bg", &active_workspace_bg);
166 json_object_object_get_ex(colors, "active_workspace_text", &active_workspace_text);
167 json_object_object_get_ex(colors, "inactive_workspace_border", &inactive_workspace_border);
168 json_object_object_get_ex(colors, "inactive_workspace_bg", &inactive_workspace_bg);
169 json_object_object_get_ex(colors, "inactive_workspace_text", &inactive_workspace_text);
170 json_object_object_get_ex(colors, "urgent_workspace_border", &urgent_workspace_border);
171 json_object_object_get_ex(colors, "urgent_workspace_bg", &urgent_workspace_bg);
172 json_object_object_get_ex(colors, "urgent_workspace_text", &urgent_workspace_text);
173 json_object_object_get_ex(colors, "binding_mode_border", &binding_mode_border);
174 json_object_object_get_ex(colors, "binding_mode_bg", &binding_mode_bg);
175 json_object_object_get_ex(colors, "binding_mode_text", &binding_mode_text);
176 if (background) {
177 config->colors.background = parse_color(json_object_get_string(background));
178 }
179
180 if (statusline) {
181 config->colors.statusline = parse_color(json_object_get_string(statusline));
182 }
183
184 if (separator) {
185 config->colors.separator = parse_color(json_object_get_string(separator));
186 }
187
188 if (focused_background) {
189 config->colors.focused_background = parse_color(json_object_get_string(focused_background));
190 }
191
192 if (focused_statusline) {
193 config->colors.focused_statusline = parse_color(json_object_get_string(focused_statusline));
194 }
195
196 if (focused_separator) {
197 config->colors.focused_separator = parse_color(json_object_get_string(focused_separator));
198 }
199
200 if (focused_workspace_border) {
201 config->colors.focused_workspace.border = parse_color(json_object_get_string(focused_workspace_border));
202 }
203
204 if (focused_workspace_bg) {
205 config->colors.focused_workspace.background = parse_color(json_object_get_string(focused_workspace_bg));
206 }
207
208 if (focused_workspace_text) {
209 config->colors.focused_workspace.text = parse_color(json_object_get_string(focused_workspace_text));
210 }
211
212 if (active_workspace_border) {
213 config->colors.active_workspace.border = parse_color(json_object_get_string(active_workspace_border));
214 }
215
216 if (active_workspace_bg) {
217 config->colors.active_workspace.background = parse_color(json_object_get_string(active_workspace_bg));
218 }
219
220 if (active_workspace_text) {
221 config->colors.active_workspace.text = parse_color(json_object_get_string(active_workspace_text));
222 }
223
224 if (inactive_workspace_border) {
225 config->colors.inactive_workspace.border = parse_color(json_object_get_string(inactive_workspace_border));
226 }
227
228 if (inactive_workspace_bg) {
229 config->colors.inactive_workspace.background = parse_color(json_object_get_string(inactive_workspace_bg));
230 }
231
232 if (inactive_workspace_text) {
233 config->colors.inactive_workspace.text = parse_color(json_object_get_string(inactive_workspace_text));
234 }
235
236 if (binding_mode_border) {
237 config->colors.binding_mode.border = parse_color(json_object_get_string(binding_mode_border));
238 }
239
240 if (binding_mode_bg) {
241 config->colors.binding_mode.background = parse_color(json_object_get_string(binding_mode_bg));
242 }
243
244 if (binding_mode_text) {
245 config->colors.binding_mode.text = parse_color(json_object_get_string(binding_mode_text));
246 }
247 } 204 }
248 205
249 json_object_put(bar_config); 206 json_object_put(bar_config);
250} 207}
251 208
252static void ipc_update_workspaces(struct bar *bar) { 209static void free_workspaces(struct wl_list *list) {
253 int i; 210 struct swaybar_workspace *ws, *tmp;
254 for (i = 0; i < bar->outputs->length; ++i) { 211 wl_list_for_each_safe(ws, tmp, list, link) {
255 struct output *output = bar->outputs->items[i]; 212 wl_list_remove(&ws->link);
256 if (output->workspaces) { 213 free(ws->name);
257 free_workspaces(output->workspaces); 214 free(ws);
258 }
259 output->workspaces = create_list();
260 } 215 }
216}
261 217
218void ipc_get_workspaces(struct swaybar *bar) {
219 bar->focused_output = NULL;
220 struct swaybar_output *output;
221 wl_list_for_each(output, &bar->outputs, link) {
222 free_workspaces(&output->workspaces);
223 output->focused = false;
224 }
262 uint32_t len = 0; 225 uint32_t len = 0;
263 char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_WORKSPACES, NULL, &len); 226 char *res = ipc_single_command(bar->ipc_socketfd,
227 IPC_GET_WORKSPACES, NULL, &len);
264 json_object *results = json_tokener_parse(res); 228 json_object *results = json_tokener_parse(res);
265 if (!results) { 229 if (!results) {
266 free(res); 230 free(res);
267 return; 231 return;
268 } 232 }
269 233 size_t length = json_object_array_length(results);
270 int length = json_object_array_length(results);
271 json_object *ws_json; 234 json_object *ws_json;
272 json_object *num, *name, *visible, *focused, *out, *urgent; 235 json_object *num, *name, *visible, *focused, *out, *urgent;
273 for (i = 0; i < length; ++i) { 236 for (size_t i = 0; i < length; ++i) {
274 ws_json = json_object_array_get_idx(results, i); 237 ws_json = json_object_array_get_idx(results, i);
275 238
276 json_object_object_get_ex(ws_json, "num", &num); 239 json_object_object_get_ex(ws_json, "num", &num);
@@ -280,113 +243,95 @@ static void ipc_update_workspaces(struct bar *bar) {
280 json_object_object_get_ex(ws_json, "output", &out); 243 json_object_object_get_ex(ws_json, "output", &out);
281 json_object_object_get_ex(ws_json, "urgent", &urgent); 244 json_object_object_get_ex(ws_json, "urgent", &urgent);
282 245
283 int j; 246 wl_list_for_each(output, &bar->outputs, link) {
284 for (j = 0; j < bar->outputs->length; ++j) { 247 const char *ws_output = json_object_get_string(out);
285 struct output *output = bar->outputs->items[j]; 248 if (strcmp(ws_output, output->name) == 0) {
286 if (strcmp(json_object_get_string(out), output->name) == 0) { 249 struct swaybar_workspace *ws =
287 struct workspace *ws = malloc(sizeof(struct workspace)); 250 calloc(1, sizeof(struct swaybar_workspace));
288 ws->num = json_object_get_int(num); 251 ws->num = json_object_get_int(num);
289 ws->name = strdup(json_object_get_string(name)); 252 ws->name = strdup(json_object_get_string(name));
290 ws->visible = json_object_get_boolean(visible); 253 ws->visible = json_object_get_boolean(visible);
291 ws->focused = json_object_get_boolean(focused); 254 ws->focused = json_object_get_boolean(focused);
292 if (ws->focused) { 255 if (ws->focused) {
293 if (bar->focused_output) {
294 bar->focused_output->focused = false;
295 }
296 bar->focused_output = output;
297 output->focused = true; 256 output->focused = true;
298 } 257 }
299 ws->urgent = json_object_get_boolean(urgent); 258 ws->urgent = json_object_get_boolean(urgent);
300 list_add(output->workspaces, ws); 259 wl_list_insert(&output->workspaces, &ws->link);
301 } 260 }
302 } 261 }
303 } 262 }
304
305 json_object_put(results); 263 json_object_put(results);
306 free(res); 264 free(res);
307} 265}
308 266
309void ipc_bar_init(struct bar *bar, const char *bar_id) { 267static void ipc_get_outputs(struct swaybar *bar) {
310 // Get bar config 268 uint32_t len = 0;
311 uint32_t len = strlen(bar_id); 269 char *res = ipc_single_command(bar->ipc_socketfd,
312 char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_BAR_CONFIG, bar_id, &len); 270 IPC_GET_OUTPUTS, NULL, &len);
313
314 ipc_parse_config(bar->config, res);
315 free(res);
316
317 // Get outputs
318 len = 0;
319 res = ipc_single_command(bar->ipc_socketfd, IPC_GET_OUTPUTS, NULL, &len);
320 json_object *outputs = json_tokener_parse(res); 271 json_object *outputs = json_tokener_parse(res);
321 int i; 272 for (size_t i = 0; i < json_object_array_length(outputs); ++i) {
322 int length = json_object_array_length(outputs); 273 json_object *output = json_object_array_get_idx(outputs, i);
323 json_object *output, *output_name, *output_active; 274 json_object *output_name, *output_active;
324 const char *name;
325 bool active;
326 for (i = 0; i < length; ++i) {
327 output = json_object_array_get_idx(outputs, i);
328 json_object_object_get_ex(output, "name", &output_name); 275 json_object_object_get_ex(output, "name", &output_name);
329 json_object_object_get_ex(output, "active", &output_active); 276 json_object_object_get_ex(output, "active", &output_active);
330 name = json_object_get_string(output_name); 277 const char *name = json_object_get_string(output_name);
331 active = json_object_get_boolean(output_active); 278 bool active = json_object_get_boolean(output_active);
332 if (!active) { 279 if (!active) {
333 continue; 280 continue;
334 } 281 }
335 282 if (wl_list_empty(&bar->config->outputs)) {
336 bool use_output = false; 283 struct config_output *coutput = calloc(
337 if (bar->config->all_outputs) { 284 1, sizeof(struct config_output));
338 use_output = true; 285 coutput->name = strdup(name);
286 coutput->index = i;
287 wl_list_insert(&bar->config->outputs, &coutput->link);
339 } else { 288 } else {
340 int j = 0; 289 struct config_output *coutput;
341 for (j = 0; j < bar->config->outputs->length; ++j) { 290 wl_list_for_each(coutput, &bar->config->outputs, link) {
342 const char *conf_name = bar->config->outputs->items[j]; 291 if (strcmp(name, coutput->name) == 0) {
343 if (strcasecmp(name, conf_name) == 0) { 292 coutput->index = i;
344 use_output = true;
345 break; 293 break;
346 } 294 }
347 } 295 }
348 } 296 }
349
350 if (!use_output) {
351 continue;
352 }
353
354 // add bar to the output
355 struct output *bar_output = container_output_create(name);
356 bar_output->idx = i;
357 list_add(bar->outputs, bar_output);
358 } 297 }
359 free(res);
360 json_object_put(outputs); 298 json_object_put(outputs);
299 free(res);
300}
361 301
362 const char *subscribe_json = "[ \"workspace\", \"mode\" ]"; 302void ipc_initialize(struct swaybar *bar, const char *bar_id) {
363 len = strlen(subscribe_json); 303 uint32_t len = strlen(bar_id);
364 res = ipc_single_command(bar->ipc_event_socketfd, IPC_SUBSCRIBE, subscribe_json, &len); 304 char *res = ipc_single_command(bar->ipc_socketfd,
305 IPC_GET_BAR_CONFIG, bar_id, &len);
306 ipc_parse_config(bar->config, res);
365 free(res); 307 free(res);
308 ipc_get_outputs(bar);
366 309
367 ipc_update_workspaces(bar); 310 const char *subscribe = "[ \"workspace\", \"mode\" ]";
311 len = strlen(subscribe);
312 free(ipc_single_command(bar->ipc_event_socketfd,
313 IPC_SUBSCRIBE, subscribe, &len));
368} 314}
369 315
370bool handle_ipc_event(struct bar *bar) { 316bool handle_ipc_event(struct swaybar *bar) {
371 struct ipc_response *resp = ipc_recv_response(bar->ipc_event_socketfd); 317 struct ipc_response *resp = ipc_recv_response(bar->ipc_event_socketfd);
372 if (!resp) { 318 if (!resp) {
373 return false; 319 return false;
374 } 320 }
375 switch (resp->type) { 321 switch (resp->type) {
376 case IPC_EVENT_WORKSPACE: 322 case IPC_EVENT_WORKSPACE:
377 ipc_update_workspaces(bar); 323 ipc_get_workspaces(bar);
378 break; 324 break;
379 case IPC_EVENT_MODE: { 325 case IPC_EVENT_MODE: {
380 json_object *result = json_tokener_parse(resp->payload); 326 json_object *result = json_tokener_parse(resp->payload);
381 if (!result) { 327 if (!result) {
382 free_ipc_response(resp); 328 free_ipc_response(resp);
383 sway_log(L_ERROR, "failed to parse payload as json"); 329 wlr_log(L_ERROR, "failed to parse payload as json");
384 return false; 330 return false;
385 } 331 }
386 json_object *json_change; 332 json_object *json_change, *json_pango_markup;
387 if (json_object_object_get_ex(result, "change", &json_change)) { 333 if (json_object_object_get_ex(result, "change", &json_change)) {
388 const char *change = json_object_get_string(json_change); 334 const char *change = json_object_get_string(json_change);
389
390 free(bar->config->mode); 335 free(bar->config->mode);
391 if (strcmp(change, "default") == 0) { 336 if (strcmp(change, "default") == 0) {
392 bar->config->mode = NULL; 337 bar->config->mode = NULL;
@@ -394,9 +339,16 @@ bool handle_ipc_event(struct bar *bar) {
394 bar->config->mode = strdup(change); 339 bar->config->mode = strdup(change);
395 } 340 }
396 } else { 341 } else {
397 sway_log(L_ERROR, "failed to parse response"); 342 wlr_log(L_ERROR, "failed to parse response");
343 json_object_put(result);
344 free_ipc_response(resp);
345 return false;
346 }
347 if (json_object_object_get_ex(result,
348 "pango_markup", &json_pango_markup)) {
349 bar->config->mode_pango_markup = json_object_get_boolean(
350 json_pango_markup);
398 } 351 }
399
400 json_object_put(result); 352 json_object_put(result);
401 break; 353 break;
402 } 354 }
@@ -404,7 +356,6 @@ bool handle_ipc_event(struct bar *bar) {
404 free_ipc_response(resp); 356 free_ipc_response(resp);
405 return false; 357 return false;
406 } 358 }
407
408 free_ipc_response(resp); 359 free_ipc_response(resp);
409 return true; 360 return true;
410} 361}
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 @@
4#include <string.h> 4#include <string.h>
5#include <stdbool.h> 5#include <stdbool.h>
6#include <getopt.h> 6#include <getopt.h>
7#include <wlr/util/log.h>
7#include "swaybar/bar.h" 8#include "swaybar/bar.h"
8#include "ipc-client.h" 9#include "ipc-client.h"
9#include "log.h"
10 10
11/* global bar state */ 11static struct swaybar swaybar;
12struct bar swaybar;
13 12
14void sway_terminate(int exit_code) { 13void sig_handler(int signal) {
15 bar_teardown(&swaybar); 14 bar_teardown(&swaybar);
16 exit(exit_code); 15 exit(0);
17} 16}
18 17
19void sig_handler(int signal) { 18void sway_terminate(int code) {
20 bar_teardown(&swaybar); 19 bar_teardown(&swaybar);
21 exit(0); 20 exit(code);
22} 21}
23 22
24int main(int argc, char **argv) { 23int main(int argc, char **argv) {
@@ -75,20 +74,23 @@ int main(int argc, char **argv) {
75 } 74 }
76 } 75 }
77 76
78 if (!bar_id) {
79 sway_abort("No bar_id passed. Provide --bar_id or let sway start swaybar");
80 }
81
82 if (debug) { 77 if (debug) {
83 init_log(L_DEBUG); 78 wlr_log_init(L_DEBUG, NULL);
84 } else { 79 } else {
85 init_log(L_ERROR); 80 wlr_log_init(L_ERROR, NULL);
81 }
82
83 if (!bar_id) {
84 wlr_log(L_ERROR, "No bar_id passed. "
85 "Provide --bar_id or let sway start swaybar");
86 return 1;
86 } 87 }
87 88
88 if (!socket_path) { 89 if (!socket_path) {
89 socket_path = get_socketpath(); 90 socket_path = get_socketpath();
90 if (!socket_path) { 91 if (!socket_path) {
91 sway_abort("Unable to retrieve socket path"); 92 wlr_log(L_ERROR, "Unable to retrieve socket path");
93 return 1;
92 } 94 }
93 } 95 }
94 96
@@ -100,9 +102,6 @@ int main(int argc, char **argv) {
100 free(bar_id); 102 free(bar_id);
101 103
102 bar_run(&swaybar); 104 bar_run(&swaybar);
103
104 // gracefully shutdown swaybar and status_command
105 bar_teardown(&swaybar); 105 bar_teardown(&swaybar);
106
107 return 0; 106 return 0;
108} 107}
diff --git a/swaybar/meson.build b/swaybar/meson.build
new file mode 100644
index 00000000..d15e8b5c
--- /dev/null
+++ b/swaybar/meson.build
@@ -0,0 +1,27 @@
1executable(
2 'swaybar',
3 [
4 'bar.c',
5 'config.c',
6 'event_loop.c',
7 'ipc.c',
8 'main.c',
9 'render.c',
10 'status_line.c',
11 ],
12 include_directories: [sway_inc],
13 dependencies: [
14 cairo,
15 client_protos,
16 gdk_pixbuf,
17 jsonc,
18 math,
19 pango,
20 pangocairo,
21 rt,
22 wayland_client,
23 wlroots,
24 ],
25 link_with: [lib_sway_common, lib_sway_client],
26 install: true
27)
diff --git a/swaybar/render.c b/swaybar/render.c
index 6fc09078..3d9ef66b 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -1,228 +1,120 @@
1#include <limits.h>
1#include <stdlib.h> 2#include <stdlib.h>
2#include <stdint.h> 3#include <stdint.h>
3#include <string.h> 4#include <string.h>
4 5#include <wlr/util/log.h>
5#include "client/cairo.h" 6#include "cairo.h"
6#include "client/pango.h" 7#include "pango.h"
7#include "client/window.h" 8#include "pool-buffer.h"
9#include "swaybar/bar.h"
8#include "swaybar/config.h" 10#include "swaybar/config.h"
9#include "swaybar/status_line.h"
10#include "swaybar/render.h" 11#include "swaybar/render.h"
11#ifdef ENABLE_TRAY 12#include "swaybar/status_line.h"
12#include "swaybar/tray/tray.h" 13#include "wlr-layer-shell-unstable-v1-client-protocol.h"
13#include "swaybar/tray/sni.h"
14#endif
15#include "log.h"
16
17
18/* internal spacing */
19static int margin = 3;
20static int ws_horizontal_padding = 5;
21static double ws_vertical_padding = 1.5;
22static int ws_spacing = 1;
23
24/**
25 * Renders a sharp line of any width and height.
26 *
27 * The line is drawn from (x,y) to (x+width,y+height) where width/height is 0
28 * if the line has a width/height of one pixel, respectively.
29 */
30static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) {
31 cairo_set_source_u32(cairo, color);
32
33 if (width > 1 && height > 1) {
34 cairo_rectangle(cairo, x, y, width, height);
35 cairo_fill(cairo);
36 } else {
37 if (width == 1) {
38 x += 0.5;
39 height += y;
40 width = x;
41 }
42 14
43 if (height == 1) { 15static const int ws_horizontal_padding = 5;
44 y += 0.5; 16static const double ws_vertical_padding = 1.5;
45 width += x; 17static const double border_width = 1;
46 height = y;
47 }
48 18
49 cairo_move_to(cairo, x, y); 19static uint32_t render_status_line_text(cairo_t *cairo,
50 cairo_set_line_width(cairo, 1.0); 20 struct swaybar_config *config, struct status_line *status,
51 cairo_line_to(cairo, width, height); 21 bool focused, uint32_t width, uint32_t height) {
52 cairo_stroke(cairo); 22 if (!status->text) {
23 return 0;
53 } 24 }
25 cairo_set_source_u32(cairo, focused ?
26 config->colors.focused_statusline : config->colors.statusline);
27 static const int margin = 3;
28 int text_width, text_height;
29 get_text_size(cairo, config->font, &text_width, &text_height,
30 1, config->pango_markup, "%s", status->text);
31 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
32 if (height < ideal_height) {
33 return ideal_height;
34 }
35 double text_y = height / 2.0 - text_height / 2.0;
36 cairo_move_to(cairo, width - text_width - margin, (int)floor(text_y));
37 pango_printf(cairo, config->font, 1, config->pango_markup,
38 "%s", status->text);
39 return ideal_height;
54} 40}
55 41
56static void render_block(struct window *window, struct config *config, struct status_block *block, double *x, bool edge, bool is_focused) { 42static uint32_t render_status_line_i3bar(cairo_t *cairo,
57 int width, height, sep_width; 43 struct swaybar_config *config, struct status_line *status,
58 get_text_size(window->cairo, window->font, &width, &height, 44 bool focused, uint32_t width, uint32_t height) {
59 window->scale, block->markup, "%s", block->full_text); 45 // TODO
60 46 return 0;
61 int textwidth = width; 47}
62 double block_width = width;
63
64 if (width < block->min_width) {
65 width = block->min_width;
66 }
67
68 *x -= width;
69
70 if (block->border != 0 && block->border_left > 0) {
71 *x -= (block->border_left + margin);
72 block_width += block->border_left + margin;
73 }
74
75 if (block->border != 0 && block->border_right > 0) {
76 *x -= (block->border_right + margin);
77 block_width += block->border_right + margin;
78 }
79
80 // Add separator
81 if (!edge) {
82 if (config->sep_symbol) {
83 get_text_size(window->cairo, window->font, &sep_width, &height,
84 window->scale, false, "%s", config->sep_symbol);
85 if (sep_width > block->separator_block_width) {
86 block->separator_block_width = sep_width + margin * 2;
87 }
88 }
89
90 *x -= block->separator_block_width;
91 } else {
92 *x -= margin;
93 }
94
95 double pos = *x;
96
97 block->x = (int)pos;
98 block->width = (int)block_width;
99
100 // render background
101 if (block->background != 0x0) {
102 cairo_set_source_u32(window->cairo, block->background);
103 cairo_rectangle(window->cairo, pos - 0.5, 1, block_width, (window->height * window->scale) - 2);
104 cairo_fill(window->cairo);
105 }
106
107 // render top border
108 if (block->border != 0 && block->border_top > 0) {
109 render_sharp_line(window->cairo, block->border,
110 pos - 0.5,
111 1,
112 block_width,
113 block->border_top);
114 }
115
116 // render bottom border
117 if (block->border != 0 && block->border_bottom > 0) {
118 render_sharp_line(window->cairo, block->border,
119 pos - 0.5,
120 (window->height * window->scale) - 1 - block->border_bottom,
121 block_width,
122 block->border_bottom);
123 }
124
125 // render left border
126 if (block->border != 0 && block->border_left > 0) {
127 render_sharp_line(window->cairo, block->border,
128 pos - 0.5,
129 1,
130 block->border_left,
131 (window->height * window->scale) - 2);
132
133 pos += block->border_left + margin;
134 }
135
136 // render text
137 double offset = 0;
138
139 if (strncmp(block->align, "left", 5) == 0) {
140 offset = pos;
141 } else if (strncmp(block->align, "right", 5) == 0) {
142 offset = pos + width - textwidth;
143 } else if (strncmp(block->align, "center", 6) == 0) {
144 offset = pos + (width - textwidth) / 2;
145 }
146
147 cairo_move_to(window->cairo, offset, margin);
148 cairo_set_source_u32(window->cairo, block->color);
149 pango_printf(window->cairo, window->font, window->scale,
150 block->markup, "%s", block->full_text);
151
152 pos += width;
153
154 // render right border
155 if (block->border != 0 && block->border_right > 0) {
156 pos += margin;
157
158 render_sharp_line(window->cairo, block->border,
159 pos - 0.5,
160 1,
161 block->border_right,
162 (window->height * window->scale) - 2);
163
164 pos += block->border_right;
165 }
166 48
167 // render separator 49static uint32_t render_status_line(cairo_t *cairo,
168 if (!edge && block->separator) { 50 struct swaybar_config *config, struct status_line *status,
169 if (is_focused) { 51 bool focused, uint32_t width, uint32_t height) {
170 cairo_set_source_u32(window->cairo, config->colors.focused_separator); 52 switch (status->protocol) {
171 } else { 53 case PROTOCOL_TEXT:
172 cairo_set_source_u32(window->cairo, config->colors.separator); 54 return render_status_line_text(cairo,
173 } 55 config, status, focused, width, height);
174 if (config->sep_symbol) { 56 case PROTOCOL_I3BAR:
175 offset = pos + (block->separator_block_width - sep_width) / 2; 57 return render_status_line_i3bar(cairo,
176 cairo_move_to(window->cairo, offset, margin); 58 config, status, focused, width, height);
177 pango_printf(window->cairo, window->font, window->scale, 59 default:
178 false, "%s", config->sep_symbol); 60 return 0;
179 } else {
180 cairo_set_line_width(window->cairo, 1);
181 cairo_move_to(window->cairo, pos + block->separator_block_width/2,
182 margin);
183 cairo_line_to(window->cairo, pos + block->separator_block_width/2,
184 (window->height * window->scale) - margin);
185 cairo_stroke(window->cairo);
186 }
187 } 61 }
188
189} 62}
190 63
191static const char *strip_workspace_name(bool strip_num, const char *ws_name) { 64static uint32_t render_binding_mode_indicator(cairo_t *cairo,
192 bool strip = false; 65 struct swaybar_config *config, const char *mode, double x,
193 int i; 66 uint32_t height) {
67 int text_width, text_height;
68 get_text_size(cairo, config->font, &text_width, &text_height,
69 1, true, "%s", mode);
70 uint32_t ideal_height = text_height + ws_vertical_padding * 2
71 + border_width * 2;
72 if (height < ideal_height) {
73 return ideal_height;
74 }
75 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
76
77 cairo_set_source_u32(cairo, config->colors.binding_mode.background);
78 cairo_rectangle(cairo, x, 0, width, height);
79 cairo_fill(cairo);
80
81 cairo_set_source_u32(cairo, config->colors.binding_mode.border);
82 cairo_rectangle(cairo, x, 0, width, border_width);
83 cairo_fill(cairo);
84 cairo_rectangle(cairo, x, 0, border_width, height);
85 cairo_fill(cairo);
86 cairo_rectangle(cairo, x + width - border_width, 0, border_width, height);
87 cairo_fill(cairo);
88 cairo_rectangle(cairo, x, height - border_width, width, border_width);
89 cairo_fill(cairo);
90
91 double text_y = height / 2.0 - text_height / 2.0;
92 cairo_set_source_u32(cairo, config->colors.binding_mode.text);
93 cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y));
94 pango_printf(cairo, config->font, 1, true, "%s", mode);
95 return ideal_height;
96}
194 97
195 if (strip_num) { 98static const char *strip_workspace_number(const char *ws_name) {
196 int len = strlen(ws_name); 99 size_t len = strlen(ws_name);
197 for (i = 0; i < len; ++i) { 100 for (size_t i = 0; i < len; ++i) {
198 if (!('0' <= ws_name[i] && ws_name[i] <= '9')) { 101 if (ws_name[i] < '0' || ws_name[i] > '9') {
199 if (':' == ws_name[i] && i < len-1 && i > 0) { 102 if (':' == ws_name[i] && i < len - 1 && i > 0) {
200 strip = true; 103 return ws_name + i + 1;
201 ++i;
202 }
203 break;
204 } 104 }
105 return ws_name;
205 } 106 }
206 } 107 }
207
208 if (strip) {
209 return ws_name + i;
210 }
211
212 return ws_name; 108 return ws_name;
213} 109}
214 110
215void workspace_button_size(struct window *window, const char *workspace_name, int *width, int *height) { 111static uint32_t render_workspace_button(cairo_t *cairo,
216 const char *stripped_name = strip_workspace_name(swaybar.config->strip_workspace_numbers, workspace_name); 112 struct swaybar_config *config, struct swaybar_workspace *ws,
217 113 double *x, uint32_t height) {
218 get_text_size(window->cairo, window->font, width, height, 114 const char *name = ws->name;
219 window->scale, true, "%s", stripped_name); 115 if (config->strip_workspace_numbers) {
220 *width += 2 * ws_horizontal_padding; 116 name = strip_workspace_number(ws->name);
221 *height += 2 * ws_vertical_padding; 117 }
222}
223
224static void render_workspace_button(struct window *window, struct config *config, struct workspace *ws, double *x) {
225 const char *stripped_name = strip_workspace_name(config->strip_workspace_numbers, ws->name);
226 118
227 struct box_colors box_colors; 119 struct box_colors box_colors;
228 if (ws->urgent) { 120 if (ws->urgent) {
@@ -235,133 +127,124 @@ static void render_workspace_button(struct window *window, struct config *config
235 box_colors = config->colors.inactive_workspace; 127 box_colors = config->colors.inactive_workspace;
236 } 128 }
237 129
238 int width, height; 130 int text_width, text_height;
239 workspace_button_size(window, stripped_name, &width, &height); 131 get_text_size(cairo, config->font, &text_width, &text_height,
240 132 1, true, "%s", name);
241 // background 133 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
242 cairo_set_source_u32(window->cairo, box_colors.background); 134 + border_width * 2;
243 cairo_rectangle(window->cairo, *x, 1.5, width - 1, height); 135 if (height < ideal_height) {
244 cairo_fill(window->cairo); 136 return ideal_height;
245 137 }
246 // border 138 uint32_t width = ws_horizontal_padding * 2 + text_width + border_width * 2;
247 cairo_set_source_u32(window->cairo, box_colors.border); 139
248 cairo_rectangle(window->cairo, *x, 1.5, width - 1, height); 140 cairo_set_source_u32(cairo, box_colors.background);
249 cairo_stroke(window->cairo); 141 cairo_rectangle(cairo, *x, 0, width, height);
250 142 cairo_fill(cairo);
251 // text 143
252 cairo_set_source_u32(window->cairo, box_colors.text); 144 cairo_set_source_u32(cairo, box_colors.border);
253 cairo_move_to(window->cairo, (int)*x + ws_horizontal_padding, margin); 145 cairo_rectangle(cairo, *x, 0, width, border_width);
254 pango_printf(window->cairo, window->font, window->scale, 146 cairo_fill(cairo);
255 true, "%s", stripped_name); 147 cairo_rectangle(cairo, *x, 0, border_width, height);
256 148 cairo_fill(cairo);
257 *x += width + ws_spacing; 149 cairo_rectangle(cairo, *x + width - border_width, 0, border_width, height);
258} 150 cairo_fill(cairo);
259 151 cairo_rectangle(cairo, *x, height - border_width, width, border_width);
260static void render_binding_mode_indicator(struct window *window, struct config *config, double pos) { 152 cairo_fill(cairo);
261 int width, height; 153
262 get_text_size(window->cairo, window->font, &width, &height, 154 double text_y = height / 2.0 - text_height / 2.0;
263 window->scale, false, "%s", config->mode); 155 cairo_set_source_u32(cairo, box_colors.text);
264 156 cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y));
265 // background 157 pango_printf(cairo, config->font, 1, true, "%s", name);
266 cairo_set_source_u32(window->cairo, config->colors.binding_mode.background); 158
267 cairo_rectangle(window->cairo, pos, 1.5, width + ws_horizontal_padding * 2 - 1, 159 *x += width;
268 height + ws_vertical_padding * 2); 160 return ideal_height;
269 cairo_fill(window->cairo);
270
271 // border
272 cairo_set_source_u32(window->cairo, config->colors.binding_mode.border);
273 cairo_rectangle(window->cairo, pos, 1.5, width + ws_horizontal_padding * 2 - 1,
274 height + ws_vertical_padding * 2);
275 cairo_stroke(window->cairo);
276
277 // text
278 cairo_set_source_u32(window->cairo, config->colors.binding_mode.text);
279 cairo_move_to(window->cairo, (int)pos + ws_horizontal_padding, margin);
280 pango_printf(window->cairo, window->font, window->scale,
281 false, "%s", config->mode);
282} 161}
283 162
284void render(struct output *output, struct config *config, struct status_line *line) { 163static uint32_t render_to_cairo(cairo_t *cairo,
285 int i; 164 struct swaybar *bar, struct swaybar_output *output) {
286 165 struct swaybar_config *config = bar->config;
287 struct window *window = output->window;
288 cairo_t *cairo = window->cairo;
289 bool is_focused = output->focused;
290
291 // Clear
292 cairo_save(cairo);
293 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
294 cairo_paint(cairo);
295 cairo_restore(cairo);
296 166
297 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); 167 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
298 168 if (output->focused) {
299 // Background
300 if (is_focused) {
301 cairo_set_source_u32(cairo, config->colors.focused_background); 169 cairo_set_source_u32(cairo, config->colors.focused_background);
302 } else { 170 } else {
303 cairo_set_source_u32(cairo, config->colors.background); 171 cairo_set_source_u32(cairo, config->colors.background);
304 } 172 }
305 cairo_paint(cairo); 173 cairo_paint(cairo);
306 174
307#ifdef ENABLE_TRAY 175 uint32_t max_height = 0;
308 uint32_t tray_width = tray_render(output, config); 176 /*
309#else 177 * Each render_* function takes the actual height of the bar, and returns
310 const uint32_t tray_width = window->width * window->scale; 178 * the ideal height. If the actual height is too short, the render function
311#endif 179 * can do whatever it wants - the buffer won't be committed. If the actual
312 180 * height is too tall, the render function should adapt its drawing to
313 // Command output 181 * utilize the available space.
314 if (is_focused) { 182 */
315 cairo_set_source_u32(cairo, config->colors.focused_statusline); 183 double x = 0;
316 } else {
317 cairo_set_source_u32(cairo, config->colors.statusline);
318 }
319
320 int width, height;
321
322 if (line->protocol == TEXT) {
323 get_text_size(window->cairo, window->font, &width, &height,
324 window->scale, config->pango_markup, "%s", line->text_line);
325 cairo_move_to(cairo, tray_width - margin - width, margin);
326 pango_printf(window->cairo, window->font, window->scale,
327 config->pango_markup, "%s", line->text_line);
328 } else if (line->protocol == I3BAR && line->block_line) {
329 double pos = tray_width - 0.5;
330 bool edge = true;
331 for (i = line->block_line->length - 1; i >= 0; --i) {
332 struct status_block *block = line->block_line->items[i];
333 if (block->full_text && block->full_text[0]) {
334 render_block(window, config, block, &pos, edge, is_focused);
335 edge = false;
336 }
337 }
338 }
339
340 cairo_set_line_width(cairo, 1.0);
341 double x = 0.5;
342
343 // Workspaces
344 if (config->workspace_buttons) { 184 if (config->workspace_buttons) {
345 for (i = 0; i < output->workspaces->length; ++i) { 185 struct swaybar_workspace *ws;
346 struct workspace *ws = output->workspaces->items[i]; 186 wl_list_for_each_reverse(ws, &output->workspaces, link) {
347 render_workspace_button(window, config, ws, &x); 187 uint32_t h = render_workspace_button(
188 cairo, config, ws, &x, output->height);
189 max_height = h > max_height ? h : max_height;
348 } 190 }
349 } 191 }
350 192 if (config->binding_mode_indicator && config->mode) {
351 // binding mode indicator 193 uint32_t h = render_binding_mode_indicator(
352 if (config->mode && config->binding_mode_indicator) { 194 cairo, config, config->mode, x, output->height);
353 render_binding_mode_indicator(window, config, x); 195 max_height = h > max_height ? h : max_height;
196 }
197 if (bar->status) {
198 uint32_t h = render_status_line(cairo, config, bar->status,
199 output->focused, output->width, output->height);
200 max_height = h > max_height ? h : max_height;
354 } 201 }
202
203 return max_height > output->height ? max_height : output->height;
355} 204}
356 205
357void set_window_height(struct window *window, int height) { 206void render_frame(struct swaybar *bar,
358 int text_width, text_height; 207 struct swaybar_output *output) {
359 get_text_size(window->cairo, window->font, 208 cairo_surface_t *recorder = cairo_recording_surface_create(
360 &text_width, &text_height, window->scale, false, 209 CAIRO_CONTENT_COLOR_ALPHA, NULL);
361 "Test string for measuring purposes"); 210 cairo_t *cairo = cairo_create(recorder);
362 if (height > 0) { 211 cairo_save(cairo);
363 margin = (height - text_height) / 2; 212 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
364 ws_vertical_padding = margin - 1.5; 213 cairo_paint(cairo);
365 } 214 cairo_restore(cairo);
366 window->height = (text_height + margin * 2) / window->scale; 215 uint32_t height = render_to_cairo(cairo, bar, output);
216 if (bar->config->height >= 0 && height < (uint32_t)bar->config->height) {
217 height = bar->config->height;
218 }
219 if (height != output->height) {
220 // Reconfigure surface
221 zwlr_layer_surface_v1_set_size(
222 output->layer_surface, 0, height);
223 zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, height);
224 // TODO: this could infinite loop if the compositor assigns us a
225 // different height than what we asked for
226 wl_surface_commit(output->surface);
227 wl_display_roundtrip(bar->display);
228 } else {
229 // Replay recording into shm and send it off
230 output->current_buffer = get_next_buffer(bar->shm,
231 output->buffers, output->width, output->height);
232 cairo_t *shm = output->current_buffer->cairo;
233
234 cairo_save(shm);
235 cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR);
236 cairo_paint(shm);
237 cairo_restore(shm);
238
239 cairo_set_source_surface(shm, recorder, 0.0, 0.0);
240 cairo_paint(shm);
241
242 wl_surface_attach(output->surface,
243 output->current_buffer->buffer, 0, 0);
244 wl_surface_damage(output->surface, 0, 0, output->width, output->height);
245 wl_surface_commit(output->surface);
246 wl_display_roundtrip(bar->display);
247 }
248 cairo_surface_destroy(recorder);
249 cairo_destroy(cairo);
367} 250}
diff --git a/swaybar/status_line.c b/swaybar/status_line.c
index 87e90caf..3454f207 100644
--- a/swaybar/status_line.c
+++ b/swaybar/status_line.c
@@ -1,530 +1,81 @@
1#define _XOPEN_SOURCE 700 1#define _POSIX_C_SOURCE
2#include <fcntl.h>
2#include <stdlib.h> 3#include <stdlib.h>
3#include <string.h> 4#include <string.h>
5#include <stdio.h>
4#include <unistd.h> 6#include <unistd.h>
5#include <json-c/json.h> 7#include <wlr/util/log.h>
6
7#include "swaybar/config.h" 8#include "swaybar/config.h"
8#include "swaybar/status_line.h" 9#include "swaybar/status_line.h"
9#include "log.h" 10#include "readline.h"
10#include "util.h" 11
11 12bool handle_status_readable(struct status_line *status) {
12#define I3JSON_MAXDEPTH 4 13 char *line = read_line_buffer(status->read,
13#define I3JSON_UNKNOWN 0 14 status->buffer, status->buffer_size);
14#define I3JSON_ARRAY 1 15 switch (status->protocol) {
15#define I3JSON_STRING 2 16 case PROTOCOL_I3BAR:
16 17 // TODO
17struct { 18 break;
18 int bufsize; 19 case PROTOCOL_TEXT:
19 char *buffer; 20 status->text = line;
20 char *line_start; 21 return true;
21 char *parserpos; 22 case PROTOCOL_UNDEF:
22 bool escape; 23 if (!line) {
23 int depth; 24 return false;
24 int bar[I3JSON_MAXDEPTH+1];
25} i3json_state = { 0, NULL, NULL, NULL, false, 0, { I3JSON_UNKNOWN } };
26
27static char line[1024];
28static char line_rest[1024];
29
30static char event_buff[1024];
31
32static void free_status_block(void *item) {
33 if (!item) {
34 return;
35 }
36 struct status_block *sb = (struct status_block*)item;
37 if (sb->full_text) {
38 free(sb->full_text);
39 }
40 if (sb->short_text) {
41 free(sb->short_text);
42 }
43 if (sb->align) {
44 free(sb->align);
45 }
46 if (sb->name) {
47 free(sb->name);
48 }
49 if (sb->instance) {
50 free(sb->instance);
51 }
52 free(sb);
53}
54
55static void parse_json(struct bar *bar, const char *text) {
56 json_object *results = json_tokener_parse(text);
57 if (!results) {
58 sway_log(L_DEBUG, "Failed to parse json");
59 return;
60 }
61
62 if (json_object_array_length(results) < 1) {
63 return;
64 }
65
66 if (bar->status->block_line) {
67 list_foreach(bar->status->block_line, free_status_block);
68 list_free(bar->status->block_line);
69 }
70
71 bar->status->block_line = create_list();
72
73 int i;
74 for (i = 0; i < json_object_array_length(results); ++i) {
75 json_object *full_text, *short_text, *color, *min_width, *align, *urgent;
76 json_object *name, *instance, *separator, *separator_block_width;
77 json_object *background, *border, *border_top, *border_bottom;
78 json_object *border_left, *border_right, *markup;
79
80 json_object *json = json_object_array_get_idx(results, i);
81 if (!json) {
82 continue;
83 }
84
85 json_object_object_get_ex(json, "full_text", &full_text);
86 json_object_object_get_ex(json, "short_text", &short_text);
87 json_object_object_get_ex(json, "color", &color);
88 json_object_object_get_ex(json, "min_width", &min_width);
89 json_object_object_get_ex(json, "align", &align);
90 json_object_object_get_ex(json, "urgent", &urgent);
91 json_object_object_get_ex(json, "name", &name);
92 json_object_object_get_ex(json, "instance", &instance);
93 json_object_object_get_ex(json, "markup", &markup);
94 json_object_object_get_ex(json, "separator", &separator);
95 json_object_object_get_ex(json, "separator_block_width", &separator_block_width);
96 json_object_object_get_ex(json, "background", &background);
97 json_object_object_get_ex(json, "border", &border);
98 json_object_object_get_ex(json, "border_top", &border_top);
99 json_object_object_get_ex(json, "border_bottom", &border_bottom);
100 json_object_object_get_ex(json, "border_left", &border_left);
101 json_object_object_get_ex(json, "border_right", &border_right);
102
103 struct status_block *new = calloc(1, sizeof(struct status_block));
104
105 if (full_text) {
106 new->full_text = strdup(json_object_get_string(full_text));
107 }
108
109 if (short_text) {
110 new->short_text = strdup(json_object_get_string(short_text));
111 }
112
113 if (color) {
114 new->color = parse_color(json_object_get_string(color));
115 } else {
116 new->color = bar->config->colors.statusline;
117 }
118
119 if (min_width) {
120 json_type type = json_object_get_type(min_width);
121 if (type == json_type_int) {
122 new->min_width = json_object_get_int(min_width);
123 } else if (type == json_type_string) {
124 /* the width will be calculated when rendering */
125 new->min_width = 0;
126 }
127 }
128
129 if (align) {
130 new->align = strdup(json_object_get_string(align));
131 } else {
132 new->align = strdup("left");
133 }
134
135 if (urgent) {
136 new->urgent = json_object_get_int(urgent);
137 }
138
139 if (name) {
140 new->name = strdup(json_object_get_string(name));
141 }
142
143 if (instance) {
144 new->instance = strdup(json_object_get_string(instance));
145 }
146
147 if (markup) {
148 new->markup = false;
149 const char *markup_str = json_object_get_string(markup);
150 if (strcmp(markup_str, "pango") == 0) {
151 new->markup = true;
152 }
153 }
154
155 if (separator) {
156 new->separator = json_object_get_int(separator);
157 } else {
158 new->separator = true; // i3bar spec
159 }
160
161 if (separator_block_width) {
162 new->separator_block_width = json_object_get_int(separator_block_width);
163 } else {
164 new->separator_block_width = 9; // i3bar spec
165 }
166
167 // Airblader features
168 if (background) {
169 new->background = parse_color(json_object_get_string(background));
170 } else {
171 new->background = 0x0; // transparent
172 }
173
174 if (border) {
175 new->border = parse_color(json_object_get_string(border));
176 } else {
177 new->border = 0x0; // transparent
178 }
179
180 if (border_top) {
181 new->border_top = json_object_get_int(border_top);
182 } else {
183 new->border_top = 1;
184 }
185
186 if (border_bottom) {
187 new->border_bottom = json_object_get_int(border_bottom);
188 } else {
189 new->border_bottom = 1;
190 }
191
192 if (border_left) {
193 new->border_left = json_object_get_int(border_left);
194 } else {
195 new->border_left = 1;
196 }
197
198 if (border_right) {
199 new->border_right = json_object_get_int(border_right);
200 } else {
201 new->border_right = 1;
202 }
203
204 list_add(bar->status->block_line, new);
205 }
206
207 json_object_put(results);
208}
209
210// continue parsing from last parserpos
211static int i3json_parse(struct bar *bar) {
212 char *c = i3json_state.parserpos;
213 int handled = 0;
214 while (*c) {
215 if (i3json_state.bar[i3json_state.depth] == I3JSON_STRING) {
216 if (!i3json_state.escape && *c == '"') {
217 --i3json_state.depth;
218 }
219 i3json_state.escape = !i3json_state.escape && *c == '\\';
220 } else {
221 switch (*c) {
222 case '[':
223 ++i3json_state.depth;
224 if (i3json_state.depth > I3JSON_MAXDEPTH) {
225 sway_abort("JSON too deep");
226 }
227 i3json_state.bar[i3json_state.depth] = I3JSON_ARRAY;
228 if (i3json_state.depth == 2) {
229 i3json_state.line_start = c;
230 }
231 break;
232 case ']':
233 if (i3json_state.bar[i3json_state.depth] != I3JSON_ARRAY) {
234 sway_abort("JSON malformed");
235 }
236 --i3json_state.depth;
237 if (i3json_state.depth == 1) {
238 // c[1] is valid since c[0] != '\0'
239 char p = c[1];
240 c[1] = '\0';
241 parse_json(bar, i3json_state.line_start);
242 c[1] = p;
243 ++handled;
244 i3json_state.line_start = c+1;
245 }
246 break;
247 case '"':
248 ++i3json_state.depth;
249 if (i3json_state.depth > I3JSON_MAXDEPTH) {
250 sway_abort("JSON too deep");
251 }
252 i3json_state.bar[i3json_state.depth] = I3JSON_STRING;
253 break;
254 }
255 }
256 ++c;
257 }
258 i3json_state.parserpos = c;
259 return handled;
260}
261
262// Read line from file descriptor, only show the line tail if it is too long.
263// In non-blocking mode treat "no more data" as a linebreak.
264// If data after a line break has been read, return it in rest.
265// If rest is non-empty, then use that as the start of the next line.
266static int read_line_tail(int fd, char *buf, int nbyte, char *rest) {
267 if (fd < 0 || !buf || !nbyte) {
268 return -1;
269 }
270 int l;
271 char *buffer = malloc(nbyte*2+1);
272 char *readpos = buffer;
273 char *lf;
274 // prepend old data to new line if necessary
275 if (rest) {
276 l = strlen(rest);
277 if (l > nbyte) {
278 strcpy(buffer, rest + l - nbyte);
279 readpos += nbyte;
280 } else if (l) {
281 strcpy(buffer, rest);
282 readpos += l;
283 }
284 }
285 // read until a linefeed is found or no more data is available
286 while ((l = read(fd, readpos, nbyte)) > 0) {
287 readpos[l] = '\0';
288 lf = strchr(readpos, '\n');
289 if (lf) {
290 // linefeed found, replace with \0
291 *lf = '\0';
292 // give data from the end of the line, try to fill the buffer
293 if (lf-buffer > nbyte) {
294 strcpy(buf, lf - nbyte + 1);
295 } else {
296 strcpy(buf, buffer);
297 }
298 // we may have read data from the next line, save it to rest
299 if (rest) {
300 rest[0] = '\0';
301 strcpy(rest, lf + 1);
302 }
303 free(buffer);
304 return strlen(buf);
305 } else {
306 // no linefeed found, slide data back.
307 int overflow = readpos - buffer + l - nbyte;
308 if (overflow > 0) {
309 memmove(buffer, buffer + overflow , nbyte + 1);
310 }
311 }
312 }
313 if (l < 0) {
314 free(buffer);
315 return l;
316 }
317 readpos[l]='\0';
318 if (rest) {
319 rest[0] = '\0';
320 }
321 if (nbyte < readpos - buffer + l - 1) {
322 memcpy(buf, readpos - nbyte + l + 1, nbyte);
323 } else {
324 strncpy(buf, buffer, nbyte);
325 }
326 buf[nbyte-1] = '\0';
327 free(buffer);
328 return strlen(buf);
329}
330
331// make sure that enough buffer space is available starting from parserpos
332static void i3json_ensure_free(int min_free) {
333 int _step = 10240;
334 int r = min_free % _step;
335 if (r) {
336 min_free += _step - r;
337 }
338 if (!i3json_state.buffer) {
339 i3json_state.buffer = malloc(min_free);
340 i3json_state.bufsize = min_free;
341 i3json_state.parserpos = i3json_state.buffer;
342 } else {
343 int len = 0;
344 int pos = 0;
345 if (i3json_state.line_start) {
346 len = strlen(i3json_state.line_start);
347 pos = i3json_state.parserpos - i3json_state.line_start;
348 if (i3json_state.line_start != i3json_state.buffer) {
349 memmove(i3json_state.buffer, i3json_state.line_start, len+1);
350 }
351 } else {
352 len = strlen(i3json_state.buffer);
353 }
354 if (i3json_state.bufsize < len+min_free) {
355 i3json_state.bufsize += min_free;
356 if (i3json_state.bufsize > 1024000) {
357 sway_abort("Status line json too long or malformed.");
358 }
359 i3json_state.buffer = realloc(i3json_state.buffer, i3json_state.bufsize);
360 if (!i3json_state.buffer) {
361 sway_abort("Could not allocate json buffer");
362 }
363 } 25 }
364 if (i3json_state.line_start) { 26 if (line[0] == '{') {
365 i3json_state.line_start = i3json_state.buffer; 27 // TODO: JSON
366 i3json_state.parserpos = i3json_state.buffer + pos;
367 } else { 28 } else {
368 i3json_state.parserpos = i3json_state.buffer; 29 status->text = line;
30 status->protocol = PROTOCOL_TEXT;
369 } 31 }
370 }
371 if (!i3json_state.buffer) {
372 sway_abort("Could not allocate buffer.");
373 }
374}
375
376// append data and parse it.
377static int i3json_handle_data(struct bar *bar, char *data) {
378 int len = strlen(data);
379 i3json_ensure_free(len);
380 strcpy(i3json_state.parserpos, data);
381 return i3json_parse(bar);
382}
383
384// read data from fd and parse it.
385static int i3json_handle_fd(struct bar *bar) {
386 i3json_ensure_free(10240);
387 // get fresh data at the end of the buffer
388 int readlen = read(bar->status_read_fd, i3json_state.parserpos, 10239);
389 if (readlen < 0) {
390 return readlen;
391 }
392 i3json_state.parserpos[readlen] = '\0';
393 return i3json_parse(bar);
394}
395
396bool status_line_mouse_event(struct bar *bar, int x, int y, uint32_t button) {
397 sway_log(L_DEBUG, "status_line_mouse_event.");
398 if (!bar->status->click_events) {
399 sway_log(L_DEBUG, "click_events are not enabled.");
400 return false; 32 return false;
401 } 33 }
402
403 if (bar->status->protocol == I3BAR) {
404 sway_log(L_DEBUG, "Sending click event.");
405
406 // find clicked block
407 struct status_block *clicked_block = NULL;
408 struct status_block *current_block = NULL;
409 int num_blocks = bar->status->block_line->length;
410
411 if (num_blocks == 0) {
412 return false;
413 } else {
414 current_block = bar->status->block_line->items[0];
415 if (x < current_block->x) {
416 return false;
417 }
418 }
419
420 for (int i = 0; i < num_blocks; i++) {
421 current_block = bar->status->block_line->items[i];
422 if (x < (current_block->x + current_block->width)) {
423 clicked_block = current_block;
424 break;
425 }
426 }
427
428 if (!clicked_block || !clicked_block->name) {
429 return false;
430 }
431
432 // event example {"name":"capture","instance":"label","button":1,"x":3431,"y":18}
433
434 struct json_object *event_json = json_object_new_object();
435 json_object_object_add(event_json, "name", json_object_new_string(clicked_block->name));
436 if (clicked_block->instance) {
437 json_object_object_add(event_json, "instance", json_object_new_string(clicked_block->instance));
438 }
439 json_object_object_add(event_json, "button", json_object_new_int(button));
440 json_object_object_add(event_json, "x", json_object_new_int(x));
441 json_object_object_add(event_json, "y", json_object_new_int(y));
442
443 int len = snprintf(event_buff, sizeof(event_buff), "%s\n", json_object_to_json_string(event_json));
444
445 json_object_put(event_json);
446
447 if (len <= (int)sizeof(event_buff)) { // if not truncated
448 write(bar->status_write_fd, event_buff, len);
449 return true;
450 }
451 }
452
453 return false; 34 return false;
454} 35}
455 36
456bool handle_status_line(struct bar *bar) { 37struct status_line *status_line_init(char *cmd) {
457 bool dirty = false; 38 struct status_line *status = calloc(1, sizeof(struct status_line));
458 39 status->buffer_size = 4096;
459 switch (bar->status->protocol) { 40 status->buffer = malloc(status->buffer_size);
460 case I3BAR:
461 sway_log(L_DEBUG, "Got i3bar protocol.");
462 if (i3json_handle_fd(bar) > 0) {
463 dirty = true;
464 }
465 break;
466 case TEXT:
467 sway_log(L_DEBUG, "Got text protocol.");
468 read_line_tail(bar->status_read_fd, line, sizeof(line), line_rest);
469 dirty = true;
470 bar->status->text_line = line;
471 break;
472 case UNDEF:
473 sway_log(L_DEBUG, "Detecting protocol...");
474 if (read_line_tail(bar->status_read_fd, line, sizeof(line), line_rest) < 0) {
475 break;
476 }
477 dirty = true;
478 bar->status->text_line = line;
479 bar->status->protocol = TEXT;
480 if (line[0] == '{') {
481 // detect i3bar json protocol
482 json_object *proto = json_tokener_parse(line);
483 if (proto) {
484 41
485 json_object *version; 42 int pipe_read_fd[2];
486 if (json_object_object_get_ex(proto, "version", &version) 43 int pipe_write_fd[2];
487 && json_object_get_int(version) == 1 44 if (pipe(pipe_read_fd) != 0 || pipe(pipe_write_fd) != 0) {
488 ) { 45 wlr_log(L_ERROR, "Unable to create pipes for status_command fork");
489 sway_log(L_DEBUG, "Switched to i3bar protocol."); 46 exit(1);
490 bar->status->protocol = I3BAR; 47 }
491 }
492
493 json_object *click_events;
494 if (json_object_object_get_ex(proto, "click_events", &click_events)
495 && json_object_get_boolean(click_events)) {
496
497 sway_log(L_DEBUG, "Enabling click events.");
498 bar->status->click_events = true;
499 48
500 const char *events_array = "[\n"; 49 status->pid = fork();
501 write(bar->status_write_fd, events_array, strlen(events_array)); 50 if (status->pid == 0) {
502 } 51 dup2(pipe_read_fd[1], STDOUT_FILENO);
52 close(pipe_read_fd[0]);
53 close(pipe_read_fd[1]);
503 54
504 i3json_handle_data(bar, line_rest); 55 dup2(pipe_write_fd[0], STDIN_FILENO);
56 close(pipe_write_fd[0]);
57 close(pipe_write_fd[1]);
505 58
506 json_object_put(proto); 59 char *const _cmd[] = { "sh", "-c", cmd, NULL, };
507 } 60 execvp(_cmd[0], _cmd);
508 } 61 exit(1);
509 break;
510 } 62 }
511 63
512 return dirty; 64 close(pipe_read_fd[1]);
513} 65 status->read_fd = pipe_read_fd[0];
514 66 fcntl(status->read_fd, F_SETFL, O_NONBLOCK);
515struct status_line *init_status_line() { 67 close(pipe_write_fd[0]);
516 struct status_line *line = malloc(sizeof(struct status_line)); 68 status->write_fd = pipe_write_fd[1];
517 line->block_line = create_list(); 69 fcntl(status->write_fd, F_SETFL, O_NONBLOCK);
518 line->text_line = NULL;
519 line->protocol = UNDEF;
520 line->click_events = false;
521 70
522 return line; 71 status->read = fdopen(status->read_fd, "r");
72 status->write = fdopen(status->write_fd, "w");
73 return status;
523} 74}
524 75
525void free_status_line(struct status_line *line) { 76void status_line_free(struct status_line *status) {
526 if (line->block_line) { 77 close(status->read_fd);
527 list_foreach(line->block_line, free_status_block); 78 close(status->write_fd);
528 list_free(line->block_line); 79 kill(status->pid, SIGTERM);
529 } 80 free(status);
530} 81}
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 @@
1#define _XOPEN_SOURCE 700
2#include <stdio.h>
3#include <stdlib.h>
4#include <stdint.h>
5#include <stdbool.h>
6#include <poll.h>
7#include <signal.h>
8#include <time.h>
9#include <dbus/dbus.h>
10#include "swaybar/tray/dbus.h"
11#include "swaybar/event_loop.h"
12#include "log.h"
13
14DBusConnection *conn = NULL;
15
16static void dispatch_watch(int fd, short mask, void *data) {
17 sway_log(L_DEBUG, "Dispatching watch");
18 DBusWatch *watch = data;
19
20 if (!dbus_watch_get_enabled(watch)) {
21 return;
22 }
23
24 uint32_t flags = 0;
25
26 if (mask & POLLIN) {
27 flags |= DBUS_WATCH_READABLE;
28 } if (mask & POLLOUT) {
29 flags |= DBUS_WATCH_WRITABLE;
30 } if (mask & POLLHUP) {
31 flags |= DBUS_WATCH_HANGUP;
32 } if (mask & POLLERR) {
33 flags |= DBUS_WATCH_ERROR;
34 }
35
36 dbus_watch_handle(watch, flags);
37}
38
39static dbus_bool_t add_watch(DBusWatch *watch, void *_data) {
40 if (!dbus_watch_get_enabled(watch)) {
41 // Watch should not be polled
42 return TRUE;
43 }
44
45 short mask = 0;
46 uint32_t flags = dbus_watch_get_flags(watch);
47
48 if (flags & DBUS_WATCH_READABLE) {
49 mask |= POLLIN;
50 } if (flags & DBUS_WATCH_WRITABLE) {
51 mask |= POLLOUT;
52 }
53
54 int fd = dbus_watch_get_unix_fd(watch);
55
56 sway_log(L_DEBUG, "Adding DBus watch fd: %d", fd);
57 add_event(fd, mask, dispatch_watch, watch);
58
59 return TRUE;
60}
61
62static void remove_watch(DBusWatch *watch, void *_data) {
63 int fd = dbus_watch_get_unix_fd(watch);
64
65 remove_event(fd);
66}
67
68static void dispatch_timeout(timer_t timer, void *data) {
69 sway_log(L_DEBUG, "Dispatching DBus timeout");
70 DBusTimeout *timeout = data;
71
72 if (dbus_timeout_get_enabled(timeout)) {
73 dbus_timeout_handle(timeout);
74 }
75}
76
77static dbus_bool_t add_timeout(DBusTimeout *timeout, void *_data) {
78 if (!dbus_timeout_get_enabled(timeout)) {
79 return TRUE;
80 }
81
82 timer_t *timer = malloc(sizeof(timer_t));
83 if (!timer) {
84 sway_log(L_ERROR, "Cannot allocate memory");
85 return FALSE;
86 }
87 struct sigevent ev = {
88 .sigev_notify = SIGEV_NONE,
89 };
90
91 if (timer_create(CLOCK_MONOTONIC, &ev, timer)) {
92 sway_log(L_ERROR, "Could not create DBus timer");
93 return FALSE;
94 }
95
96 int interval = dbus_timeout_get_interval(timeout);
97 int interval_sec = interval / 1000;
98 int interval_msec = (interval_sec * 1000) - interval;
99
100 struct timespec period = {
101 (time_t) interval_sec,
102 ((long) interval_msec) * 1000 * 1000,
103 };
104 struct itimerspec time = {
105 period,
106 period,
107 };
108
109 timer_settime(*timer, 0, &time, NULL);
110
111 dbus_timeout_set_data(timeout, timer, NULL);
112
113 sway_log(L_DEBUG, "Adding DBus timeout. Interval: %ds %dms", interval_sec, interval_msec);
114 add_timer(*timer, dispatch_timeout, timeout);
115
116 return TRUE;
117}
118static void remove_timeout(DBusTimeout *timeout, void *_data) {
119 timer_t *timer = (timer_t *) dbus_timeout_get_data(timeout);
120 sway_log(L_DEBUG, "Removing DBus timeout.");
121
122 if (timer) {
123 remove_timer(*timer);
124 timer_delete(*timer);
125 free(timer);
126 }
127}
128
129static bool should_dispatch = true;
130
131static void dispatch_status(DBusConnection *connection, DBusDispatchStatus new_status,
132 void *_data) {
133 if (new_status == DBUS_DISPATCH_DATA_REMAINS) {
134 should_dispatch = true;
135 }
136}
137
138/* Public functions below */
139
140void dispatch_dbus() {
141 if (!should_dispatch || !conn) {
142 return;
143 }
144
145 DBusDispatchStatus status;
146
147 do {
148 status = dbus_connection_dispatch(conn);
149 } while (status == DBUS_DISPATCH_DATA_REMAINS);
150
151 if (status != DBUS_DISPATCH_COMPLETE) {
152 sway_log(L_ERROR, "Cannot dispatch dbus events: %d", status);
153 }
154
155 should_dispatch = false;
156}
157
158int dbus_init() {
159 DBusError error;
160 dbus_error_init(&error);
161
162 conn = dbus_bus_get(DBUS_BUS_SESSION, &error);
163 if (conn == NULL) {
164 sway_log(L_INFO, "Compiled with dbus support, but unable to connect to dbus");
165 sway_log(L_INFO, "swaybar will be unable to display tray icons.");
166 return -1;
167 }
168
169 dbus_connection_set_exit_on_disconnect(conn, FALSE);
170 if (dbus_error_is_set(&error)) {
171 sway_log(L_ERROR, "Cannot get bus connection: %s\n", error.message);
172 conn = NULL;
173 return -1;
174 }
175
176 sway_log(L_INFO, "Unique name: %s\n", dbus_bus_get_unique_name(conn));
177
178 // Will be called if dispatch status changes
179 dbus_connection_set_dispatch_status_function(conn, dispatch_status, NULL, NULL);
180
181 if (!dbus_connection_set_watch_functions(conn, add_watch, remove_watch,
182 NULL, NULL, NULL)) {
183 dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL);
184 sway_log(L_ERROR, "Failed to activate DBUS watch functions");
185 return -1;
186 }
187
188 if (!dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout,
189 NULL, NULL, NULL)) {
190 dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, NULL, NULL);
191 dbus_connection_set_timeout_functions(conn, NULL, NULL, NULL, NULL, NULL);
192 sway_log(L_ERROR, "Failed to activate DBUS timeout functions");
193 return -1;
194 }
195
196 return 0;
197}
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 @@
1#define _XOPEN_SOURCE 700
2#define _POSIX_C_SOURCE 200809L
3#include <stdio.h>
4#include <stdlib.h>
5#include <unistd.h>
6#include <string.h>
7#include <dirent.h>
8#include <sys/stat.h>
9#include <stdbool.h>
10#include <stdint.h>
11#include <limits.h>
12#include "swaybar/tray/icon.h"
13#include "swaybar/bar.h"
14#include "swaybar/config.h"
15#include "stringop.h"
16#include "log.h"
17
18/**
19 * REVIEW:
20 * This file repeats lots of "costly" operations that are the same for every
21 * icon. It's possible to create a dictionary or some other structure to cache
22 * these, though it may complicate things somewhat.
23 *
24 * Also parsing (index.theme) is currently pretty messy, so that could be made
25 * much better as well. Over all, things work, but are not optimal.
26 */
27
28/* Finds all themes that the given theme inherits */
29static list_t *find_inherits(const char *theme_dir) {
30 const char inherits[] = "Inherits";
31 const char index_name[] = "index.theme";
32 list_t *themes = create_list();
33 FILE *index = NULL;
34 char *path = malloc(strlen(theme_dir) + sizeof(index_name));
35 if (!path) {
36 goto fail;
37 }
38 if (!themes) {
39 goto fail;
40 }
41
42 strcpy(path, theme_dir);
43 strcat(path, index_name);
44
45 index = fopen(path, "r");
46 if (!index) {
47 goto fail;
48 }
49
50 char *buf = NULL;
51 size_t n = 0;
52 while (!feof(index) && getline(&buf, &n, index) != -1) {
53 if (n <= sizeof(inherits) + 1) {
54 continue;
55 }
56 if (strncmp(inherits, buf, sizeof(inherits) - 1) == 0) {
57 char *themestr = buf + sizeof(inherits);
58 themes = split_string(themestr, ",");
59 break;
60 }
61 }
62 free(buf);
63
64fail:
65 free(path);
66 if (index) {
67 fclose(index);
68 }
69 return themes;
70}
71
72static bool isdir(const char *path) {
73 struct stat statbuf;
74 if (stat(path, &statbuf) != -1) {
75 if (S_ISDIR(statbuf.st_mode)) {
76 return true;
77 }
78 }
79 return false;
80
81}
82
83/**
84 * Returns the directory of a given theme if it exists.
85 * The returned pointer must be freed.
86 */
87static char *find_theme_dir(const char *theme) {
88 char *basedir;
89 char *icon_dir;
90
91 if (!theme) {
92 return NULL;
93 }
94
95 if (!(icon_dir = malloc(1024))) {
96 sway_log(L_ERROR, "Out of memory!");
97 goto fail;
98 }
99
100 if ((basedir = getenv("HOME"))) {
101 if (snprintf(icon_dir, 1024, "%s/.icons/%s", basedir, theme) >= 1024) {
102 sway_log(L_ERROR, "Path too long to render");
103 // XXX perhaps just goto trying in /usr/share? This
104 // shouldn't happen anyway, but might with a long global
105 goto fail;
106 }
107
108 if (isdir(icon_dir)) {
109 return icon_dir;
110 }
111 }
112
113 if ((basedir = getenv("XDG_DATA_DIRS"))) {
114 if (snprintf(icon_dir, 1024, "%s/icons/%s", basedir, theme) >= 1024) {
115 sway_log(L_ERROR, "Path too long to render");
116 // ditto
117 goto fail;
118 }
119
120 if (isdir(icon_dir)) {
121 return icon_dir;
122 }
123 }
124
125 // Spec says use "/usr/share/pixmaps/", but I see everything in
126 // "/usr/share/icons/" look it both, I suppose.
127 if (snprintf(icon_dir, 1024, "/usr/share/pixmaps/%s", theme) >= 1024) {
128 sway_log(L_ERROR, "Path too long to render");
129 goto fail;
130 }
131 if (isdir(icon_dir)) {
132 return icon_dir;
133 }
134
135 if (snprintf(icon_dir, 1024, "/usr/share/icons/%s", theme) >= 1024) {
136 sway_log(L_ERROR, "Path too long to render");
137 goto fail;
138 }
139 if (isdir(icon_dir)) {
140 return icon_dir;
141 }
142
143fail:
144 free(icon_dir);
145 sway_log(L_ERROR, "Could not find dir for theme: %s", theme);
146 return NULL;
147}
148
149/**
150 * Returns all theme dirs needed to be looked in for an icon.
151 * Does not check for duplicates
152 */
153static list_t *find_all_theme_dirs(const char *theme) {
154 list_t *dirs = create_list();
155 if (!dirs) {
156 return NULL;
157 }
158 char *dir = find_theme_dir(theme);
159 if (dir) {
160 list_add(dirs, dir);
161 list_t *inherits = find_inherits(dir);
162 list_cat(dirs, inherits);
163 list_free(inherits);
164 }
165 dir = find_theme_dir("hicolor");
166 if (dir) {
167 list_add(dirs, dir);
168 }
169
170 return dirs;
171}
172
173struct subdir {
174 int size;
175 char name[];
176};
177
178static int subdir_str_cmp(const void *_subdir, const void *_str) {
179 const struct subdir *subdir = _subdir;
180 const char *str = _str;
181 return strcmp(subdir->name, str);
182}
183/**
184 * Helper to find_subdirs. Acts similar to `split_string(subdirs, ",")` but
185 * generates a list of struct subdirs
186 */
187static list_t *split_subdirs(char *subdir_str) {
188 list_t *subdir_list = create_list();
189 char *copy = strdup(subdir_str);
190 if (!subdir_list || !copy) {
191 list_free(subdir_list);
192 free(copy);
193 return NULL;
194 }
195
196 char *token;
197 token = strtok(copy, ",");
198 while(token) {
199 int len = strlen(token) + 1;
200 struct subdir *subdir =
201 malloc(sizeof(struct subdir) + sizeof(char [len]));
202 if (!subdir) {
203 // Return what we have
204 return subdir_list;
205 }
206 subdir->size = 0;
207 strcpy(subdir->name, token);
208
209 list_add(subdir_list, subdir);
210
211 token = strtok(NULL, ",");
212 }
213 free(copy);
214
215 return subdir_list;
216}
217/**
218 * Returns a list of all subdirectories of a theme.
219 * Take note: the subdir names are all relative to `theme_dir` and must be
220 * combined with it to form a valid directory.
221 *
222 * Each member of the list is of type (struct subdir *) this struct contains
223 * the name of the subdir, along with size information. These must be freed
224 * bye the caller.
225 *
226 * This currently ignores min and max sizes of icons.
227 */
228static list_t* find_theme_subdirs(const char *theme_dir) {
229 const char index_name[] = "/index.theme";
230 list_t *dirs = NULL;
231 char *path = malloc(strlen(theme_dir) + sizeof(index_name));
232 FILE *index = NULL;
233 if (!path) {
234 sway_log(L_ERROR, "Failed to allocate memory");
235 goto fail;
236 }
237
238 strcpy(path, theme_dir);
239 strcat(path, index_name);
240
241 index = fopen(path, "r");
242 if (!index) {
243 sway_log(L_ERROR, "Could not open file: %s", path);
244 goto fail;
245 }
246
247 char *buf = NULL;
248 size_t n = 0;
249 const char directories[] = "Directories";
250 while (!feof(index) && getline(&buf, &n, index) != -1) {
251 if (n <= sizeof(directories) + 1) {
252 continue;
253 }
254 if (strncmp(directories, buf, sizeof(directories) - 1) == 0) {
255 char *dirstr = buf + sizeof(directories);
256 dirs = split_subdirs(dirstr);
257 break;
258 }
259 }
260 // Now, find the size of each dir
261 struct subdir *current_subdir = NULL;
262 const char size[] = "Size";
263 while (!feof(index) && getline(&buf, &n, index) != -1) {
264 if (buf[0] == '[') {
265 int len = strlen(buf);
266 if (buf[len-1] == '\n') {
267 len--;
268 }
269 // replace ']'
270 buf[len-1] = '\0';
271
272 int index;
273 if ((index = list_seq_find(dirs, subdir_str_cmp, buf+1)) != -1) {
274 current_subdir = (dirs->items[index]);
275 }
276 }
277
278 if (strncmp(size, buf, sizeof(size) - 1) == 0) {
279 if (current_subdir) {
280 current_subdir->size = atoi(buf + sizeof(size));
281 }
282 }
283 }
284 free(buf);
285fail:
286 free(path);
287 if (index) {
288 fclose(index);
289 }
290 return dirs;
291}
292
293/* Returns the file of an icon given its name and size */
294static char *find_icon_file(const char *name, int size) {
295 int namelen = strlen(name);
296 list_t *dirs = find_all_theme_dirs(swaybar.config->icon_theme);
297 if (!dirs) {
298 return NULL;
299 }
300 int min_size_diff = INT_MAX;
301 char *current_file = NULL;
302
303 for (int i = 0; i < dirs->length; ++i) {
304 char *dir = dirs->items[i];
305 list_t *subdirs = find_theme_subdirs(dir);
306
307 if (!subdirs) {
308 continue;
309 }
310
311 for (int i = 0; i < subdirs->length; ++i) {
312 struct subdir *subdir = subdirs->items[i];
313
314 // Only use an unsized if we don't already have a
315 // canidate this should probably change to allow svgs
316 if (!subdir->size && current_file) {
317 continue;
318 }
319
320 int size_diff = abs(size - subdir->size);
321
322 if (size_diff >= min_size_diff) {
323 continue;
324 }
325
326 char *path = malloc(strlen(subdir->name) + strlen(dir) + 2);
327
328 strcpy(path, dir);
329 path[strlen(dir)] = '/';
330 strcpy(path + strlen(dir) + 1, subdir->name);
331
332 DIR *icons = opendir(path);
333 if (!icons) {
334 free(path);
335 continue;
336 }
337
338 struct dirent *direntry;
339 while ((direntry = readdir(icons)) != NULL) {
340 int len = strlen(direntry->d_name);
341 if (len <= namelen + 2) { //must have some ext
342 continue;
343 }
344 if (strncmp(direntry->d_name, name, namelen) == 0) {
345 char *ext = direntry->d_name + namelen + 1;
346#ifdef WITH_GDK_PIXBUF
347 if (strcmp(ext, "png") == 0 ||
348 strcmp(ext, "xpm") == 0 ||
349 strcmp(ext, "svg") == 0) {
350#else
351 if (strcmp(ext, "png") == 0) {
352#endif
353 free(current_file);
354 char *icon_path = malloc(strlen(path) + len + 2);
355
356 strcpy(icon_path, path);
357 icon_path[strlen(path)] = '/';
358 strcpy(icon_path + strlen(path) + 1, direntry->d_name);
359 current_file = icon_path;
360 min_size_diff = size_diff;
361 }
362 }
363 }
364 free(path);
365 closedir(icons);
366 }
367 free_flat_list(subdirs);
368 }
369 free_flat_list(dirs);
370
371 return current_file;
372}
373
374cairo_surface_t *find_icon(const char *name, int size) {
375 char *image_path = find_icon_file(name, size);
376 if (image_path == NULL) {
377 return NULL;
378 }
379
380 cairo_surface_t *image = NULL;
381#ifdef WITH_GDK_PIXBUF
382 GError *err = NULL;
383 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(image_path, &err);
384 if (!pixbuf) {
385 sway_log(L_ERROR, "Failed to load icon image: %s", err->message);
386 }
387 image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
388 g_object_unref(pixbuf);
389#else
390 // TODO make svg work? cairo supports it. maybe remove gdk alltogether
391 image = cairo_image_surface_create_from_png(image_path);
392#endif //WITH_GDK_PIXBUF
393 if (!image) {
394 sway_log(L_ERROR, "Could not read icon image");
395 return NULL;
396 }
397
398 free(image_path);
399 return image;
400}
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 @@
1#define _XOPEN_SOURCE 500
2#include <stdlib.h>
3#include <stdio.h>
4#include <string.h>
5#include <stdint.h>
6#include <stdbool.h>
7#include <dbus/dbus.h>
8#include <arpa/inet.h>
9#include <netinet/in.h>
10#include "swaybar/tray/dbus.h"
11#include "swaybar/tray/sni.h"
12#include "swaybar/tray/icon.h"
13#include "swaybar/bar.h"
14#include "client/cairo.h"
15#include "log.h"
16
17// Not sure what this is but cairo needs it.
18static const cairo_user_data_key_t cairo_user_data_key;
19
20struct sni_icon_ref *sni_icon_ref_create(struct StatusNotifierItem *item,
21 int height) {
22 struct sni_icon_ref *sni_ref = malloc(sizeof(struct sni_icon_ref));
23 if (!sni_ref) {
24 return NULL;
25 }
26 sni_ref->icon = cairo_image_surface_scale(item->image, height, height);
27 sni_ref->ref = item;
28
29 return sni_ref;
30}
31
32void sni_icon_ref_free(struct sni_icon_ref *sni_ref) {
33 if (!sni_ref) {
34 return;
35 }
36 cairo_surface_destroy(sni_ref->icon);
37 free(sni_ref);
38}
39
40/* Gets the pixmap of an icon */
41static void reply_icon(DBusPendingCall *pending, void *_data) {
42 struct StatusNotifierItem *item = _data;
43
44 DBusMessage *reply = dbus_pending_call_steal_reply(pending);
45
46 if (!reply) {
47 sway_log(L_ERROR, "Did not get reply");
48 goto bail;
49 }
50
51 int message_type = dbus_message_get_type(reply);
52
53 if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
54 char *msg;
55
56 dbus_message_get_args(reply, NULL,
57 DBUS_TYPE_STRING, &msg,
58 DBUS_TYPE_INVALID);
59
60 sway_log(L_ERROR, "Message is error: %s", msg);
61 goto bail;
62 }
63
64 DBusMessageIter iter;
65 DBusMessageIter variant; /* v[a(iiay)] */
66 DBusMessageIter array; /* a(iiay) */
67 DBusMessageIter d_struct; /* (iiay) */
68 DBusMessageIter icon; /* ay */
69
70 dbus_message_iter_init(reply, &iter);
71
72 // Each if here checks the types above before recursing
73 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
74 sway_log(L_ERROR, "Relpy type incorrect");
75 sway_log(L_ERROR, "Should be \"v\", is \"%s\"",
76 dbus_message_iter_get_signature(&iter));
77 goto bail;
78 }
79 dbus_message_iter_recurse(&iter, &variant);
80
81 if (strcmp("a(iiay)", dbus_message_iter_get_signature(&variant)) != 0) {
82 sway_log(L_ERROR, "Relpy type incorrect");
83 sway_log(L_ERROR, "Should be \"a(iiay)\", is \"%s\"",
84 dbus_message_iter_get_signature(&variant));
85 goto bail;
86 }
87
88 if (dbus_message_iter_get_element_count(&variant) == 0) {
89 // Can't recurse if there are no items
90 sway_log(L_INFO, "Item has no icon");
91 goto bail;
92 }
93 dbus_message_iter_recurse(&variant, &array);
94
95 dbus_message_iter_recurse(&array, &d_struct);
96
97 int width;
98 dbus_message_iter_get_basic(&d_struct, &width);
99 dbus_message_iter_next(&d_struct);
100
101 int height;
102 dbus_message_iter_get_basic(&d_struct, &height);
103 dbus_message_iter_next(&d_struct);
104
105 int len = dbus_message_iter_get_element_count(&d_struct);
106
107 if (!len) {
108 sway_log(L_ERROR, "No icon data");
109 goto bail;
110 }
111
112 // Also implies len % 4 == 0, useful below
113 if (len != width * height * 4) {
114 sway_log(L_ERROR, "Incorrect array size passed");
115 goto bail;
116 }
117
118 dbus_message_iter_recurse(&d_struct, &icon);
119
120 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
121 // FIXME support a variable stride
122 // (works on my machine though for all tested widths)
123 if (!sway_assert(stride == width * 4, "Stride must be equal to byte length")) {
124 goto bail;
125 }
126
127 // Data is by reference, no need to free
128 uint8_t *message_data;
129 dbus_message_iter_get_fixed_array(&icon, &message_data, &len);
130
131 uint8_t *image_data = malloc(stride * height);
132 if (!image_data) {
133 sway_log(L_ERROR, "Could not allocate memory for icon");
134 goto bail;
135 }
136
137 // Transform from network byte order to host byte order
138 // Assumptions are safe because the equality above
139 uint32_t *network = (uint32_t *) message_data;
140 uint32_t *host = (uint32_t *)image_data;
141 for (int i = 0; i < width * height; ++i) {
142 host[i] = ntohl(network[i]);
143 }
144
145 cairo_surface_t *image = cairo_image_surface_create_for_data(
146 image_data, CAIRO_FORMAT_ARGB32,
147 width, height, stride);
148
149 if (image) {
150 if (item->image) {
151 cairo_surface_destroy(item->image);
152 }
153 item->image = image;
154 // Free the image data on surface destruction
155 cairo_surface_set_user_data(image,
156 &cairo_user_data_key,
157 image_data,
158 free);
159 item->dirty = true;
160 dirty = true;
161
162 dbus_message_unref(reply);
163 dbus_pending_call_unref(pending);
164 return;
165 } else {
166 sway_log(L_ERROR, "Could not create image surface");
167 free(image_data);
168 }
169
170bail:
171 if (reply) {
172 dbus_message_unref(reply);
173 }
174 dbus_pending_call_unref(pending);
175 sway_log(L_ERROR, "Could not get icon from item");
176 return;
177}
178static void send_icon_msg(struct StatusNotifierItem *item) {
179 DBusPendingCall *pending;
180 DBusMessage *message = dbus_message_new_method_call(
181 item->name,
182 "/StatusNotifierItem",
183 "org.freedesktop.DBus.Properties",
184 "Get");
185 const char *iface;
186 if (item->kde_special_snowflake) {
187 iface = "org.kde.StatusNotifierItem";
188 } else {
189 iface = "org.freedesktop.StatusNotifierItem";
190 }
191 const char *prop = "IconPixmap";
192
193 dbus_message_append_args(message,
194 DBUS_TYPE_STRING, &iface,
195 DBUS_TYPE_STRING, &prop,
196 DBUS_TYPE_INVALID);
197
198 bool status =
199 dbus_connection_send_with_reply(conn, message, &pending, -1);
200
201 dbus_message_unref(message);
202
203 if (!(pending || status)) {
204 sway_log(L_ERROR, "Could not get item icon");
205 return;
206 }
207
208 dbus_pending_call_set_notify(pending, reply_icon, item, NULL);
209}
210
211/* Get an icon by its name */
212static void reply_icon_name(DBusPendingCall *pending, void *_data) {
213 struct StatusNotifierItem *item = _data;
214
215 DBusMessage *reply = dbus_pending_call_steal_reply(pending);
216
217 if (!reply) {
218 sway_log(L_INFO, "Got no icon name reply from item");
219 goto bail;
220 }
221
222 int message_type = dbus_message_get_type(reply);
223
224 if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
225 char *msg;
226
227 dbus_message_get_args(reply, NULL,
228 DBUS_TYPE_STRING, &msg,
229 DBUS_TYPE_INVALID);
230
231 sway_log(L_INFO, "Could not get icon name: %s", msg);
232 goto bail;
233 }
234
235 DBusMessageIter iter; /* v[s] */
236 DBusMessageIter variant; /* s */
237
238 dbus_message_iter_init(reply, &iter);
239 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
240 sway_log(L_ERROR, "Relpy type incorrect");
241 sway_log(L_ERROR, "Should be \"v\", is \"%s\"",
242 dbus_message_iter_get_signature(&iter));
243 goto bail;
244 }
245 dbus_message_iter_recurse(&iter, &variant);
246
247
248 if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_STRING) {
249 sway_log(L_ERROR, "Relpy type incorrect");
250 sway_log(L_ERROR, "Should be \"s\", is \"%s\"",
251 dbus_message_iter_get_signature(&iter));
252 goto bail;
253 }
254
255 char *icon_name;
256 dbus_message_iter_get_basic(&variant, &icon_name);
257
258 cairo_surface_t *image = find_icon(icon_name, 256);
259
260 if (image) {
261 sway_log(L_DEBUG, "Icon for %s found with size %d", icon_name,
262 cairo_image_surface_get_width(image));
263 if (item->image) {
264 cairo_surface_destroy(item->image);
265 }
266 item->image = image;
267 item->dirty = true;
268 dirty = true;
269
270 dbus_message_unref(reply);
271 dbus_pending_call_unref(pending);
272 return;
273 }
274
275bail:
276 if (reply) {
277 dbus_message_unref(reply);
278 }
279 dbus_pending_call_unref(pending);
280 // Now try the pixmap
281 send_icon_msg(item);
282 return;
283}
284static void send_icon_name_msg(struct StatusNotifierItem *item) {
285 DBusPendingCall *pending;
286 DBusMessage *message = dbus_message_new_method_call(
287 item->name,
288 "/StatusNotifierItem",
289 "org.freedesktop.DBus.Properties",
290 "Get");
291 const char *iface;
292 if (item->kde_special_snowflake) {
293 iface = "org.kde.StatusNotifierItem";
294 } else {
295 iface = "org.freedesktop.StatusNotifierItem";
296 }
297 const char *prop = "IconName";
298
299 dbus_message_append_args(message,
300 DBUS_TYPE_STRING, &iface,
301 DBUS_TYPE_STRING, &prop,
302 DBUS_TYPE_INVALID);
303
304 bool status =
305 dbus_connection_send_with_reply(conn, message, &pending, -1);
306
307 dbus_message_unref(message);
308
309 if (!(pending || status)) {
310 sway_log(L_ERROR, "Could not get item icon name");
311 return;
312 }
313
314 dbus_pending_call_set_notify(pending, reply_icon_name, item, NULL);
315}
316
317void get_icon(struct StatusNotifierItem *item) {
318 send_icon_name_msg(item);
319}
320
321void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
322 const char *iface =
323 (item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
324 : "org.freedesktop.StatusNotifierItem");
325 DBusMessage *message = dbus_message_new_method_call(
326 item->name,
327 "/StatusNotifierItem",
328 iface,
329 "Activate");
330
331 dbus_message_append_args(message,
332 DBUS_TYPE_INT32, &x,
333 DBUS_TYPE_INT32, &y,
334 DBUS_TYPE_INVALID);
335
336 dbus_connection_send(conn, message, NULL);
337
338 dbus_message_unref(message);
339}
340
341void sni_context_menu(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
342 const char *iface =
343 (item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
344 : "org.freedesktop.StatusNotifierItem");
345 DBusMessage *message = dbus_message_new_method_call(
346 item->name,
347 "/StatusNotifierItem",
348 iface,
349 "ContextMenu");
350
351 dbus_message_append_args(message,
352 DBUS_TYPE_INT32, &x,
353 DBUS_TYPE_INT32, &y,
354 DBUS_TYPE_INVALID);
355
356 dbus_connection_send(conn, message, NULL);
357
358 dbus_message_unref(message);
359}
360void sni_secondary(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
361 const char *iface =
362 (item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
363 : "org.freedesktop.StatusNotifierItem");
364 DBusMessage *message = dbus_message_new_method_call(
365 item->name,
366 "/StatusNotifierItem",
367 iface,
368 "SecondaryActivate");
369
370 dbus_message_append_args(message,
371 DBUS_TYPE_INT32, &x,
372 DBUS_TYPE_INT32, &y,
373 DBUS_TYPE_INVALID);
374
375 dbus_connection_send(conn, message, NULL);
376
377 dbus_message_unref(message);
378}
379
380static void get_unique_name(struct StatusNotifierItem *item) {
381 // I think that we're fine being sync here becaues the message is
382 // directly to the message bus. Could be async though.
383 DBusMessage *message = dbus_message_new_method_call(
384 "org.freedesktop.DBus",
385 "/org/freedesktop/DBus",
386 "org.freedesktop.DBus",
387 "GetNameOwner");
388
389 dbus_message_append_args(message,
390 DBUS_TYPE_STRING, &item->name,
391 DBUS_TYPE_INVALID);
392
393 DBusMessage *reply = dbus_connection_send_with_reply_and_block(
394 conn, message, -1, NULL);
395
396 dbus_message_unref(message);
397
398 if (!reply) {
399 sway_log(L_ERROR, "Could not get unique name for item: %s",
400 item->name);
401 return;
402 }
403
404 char *unique_name;
405 if (!dbus_message_get_args(reply, NULL,
406 DBUS_TYPE_STRING, &unique_name,
407 DBUS_TYPE_INVALID)) {
408 sway_log(L_ERROR, "Error parsing method args");
409 } else {
410 if (item->unique_name) {
411 free(item->unique_name);
412 }
413 item->unique_name = strdup(unique_name);
414 }
415
416 dbus_message_unref(reply);
417}
418
419struct StatusNotifierItem *sni_create(const char *name) {
420 // Make sure `name` is well formed
421 if (!dbus_validate_bus_name(name, NULL)) {
422 sway_log(L_INFO, "Name (%s) is not a bus name. We cannot create an item.", name);
423 return NULL;
424 }
425
426 struct StatusNotifierItem *item = malloc(sizeof(struct StatusNotifierItem));
427 item->name = strdup(name);
428 item->unique_name = NULL;
429 item->image = NULL;
430 item->dirty = false;
431
432 // If it doesn't use this name then assume that it uses the KDE spec
433 // This is because xembed-sni-proxy uses neither "org.freedesktop" nor
434 // "org.kde" and just gives us the items "unique name"
435 //
436 // We could use this to our advantage and fill out the "unique name"
437 // field with the given name if it is neither freedesktop or kde, but
438 // that's makes us rely on KDE hackyness which is bad practice
439 const char freedesktop_name[] = "org.freedesktop";
440 if (strncmp(name, freedesktop_name, sizeof(freedesktop_name) - 1) != 0) {
441 item->kde_special_snowflake = true;
442 } else {
443 item->kde_special_snowflake = false;
444 }
445
446 get_icon(item);
447
448 get_unique_name(item);
449
450 return item;
451}
452/* Return 0 if `item` has a name of `str` */
453int sni_str_cmp(const void *_item, const void *_str) {
454 const struct StatusNotifierItem *item = _item;
455 const char *str = _str;
456
457 return strcmp(item->name, str);
458}
459/* Returns 0 if `item` has a unique name of `str` */
460int sni_uniq_cmp(const void *_item, const void *_str) {
461 const struct StatusNotifierItem *item = _item;
462 const char *str = _str;
463
464 if (!item->unique_name) {
465 return false;
466 }
467 return strcmp(item->unique_name, str);
468}
469void sni_free(struct StatusNotifierItem *item) {
470 if (!item) {
471 return;
472 }
473 free(item->name);
474 if (item->unique_name) {
475 free(item->unique_name);
476 }
477 if (item->image) {
478 cairo_surface_destroy(item->image);
479 }
480 free(item);
481}
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 @@
1#define _XOPEN_SOURCE 500
2#include <unistd.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <stdbool.h>
7#include <dbus/dbus.h>
8#include "swaybar/tray/dbus.h"
9#include "list.h"
10#include "log.h"
11
12static list_t *items = NULL;
13static list_t *hosts = NULL;
14
15/**
16 * Describes the function of the StatusNotifierWatcher
17 * See https://freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierWatcher/
18 *
19 * We also implement KDE's special snowflake protocol, it's like this but with
20 * all occurrences 'freedesktop' replaced with 'kde'. There is no KDE introspect.
21 */
22static const char *interface_xml =
23 "<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'"
24 "'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>"
25 "<node>"
26 " <interface name='org.freedesktop.DBus.Introspectable'>"
27 " <method name='Introspect'>"
28 " <arg name='xml_data' direction='out' type='s'/>"
29 " </method>"
30 " </interface>"
31 " <interface name='org.freedesktop.DBus.Properties'>"
32 " <method name='Get'>"
33 " <arg name='interface' direction='in' type='s'/>"
34 " <arg name='propname' direction='in' type='s'/>"
35 " <arg name='value' direction='out' type='v'/>"
36 " </method>"
37 " <method name='Set'>"
38 " <arg name='interface' direction='in' type='s'/>"
39 " <arg name='propname' direction='in' type='s'/>"
40 " <arg name='value' direction='in' type='v'/>"
41 " </method>"
42 " <method name='GetAll'>"
43 " <arg name='interface' direction='in' type='s'/>"
44 " <arg name='props' direction='out' type='a{sv}'/>"
45 " </method>"
46 " </interface>"
47 " <interface name='org.freedesktop.StatusNotifierWatcher'>"
48 " <method name='RegisterStatusNotifierItem'>"
49 " <arg type='s' name='service' direction='in'/>"
50 " </method>"
51 " <method name='RegisterStatusNotifierHost'>"
52 " <arg type='s' name='service' direction='in'/>"
53 " </method>"
54 " <property name='RegisteredStatusNotifierItems' type='as' access='read'/>"
55 " <property name='IsStatusNotifierHostRegistered' type='b' access='read'/>"
56 " <property name='ProtocolVersion' type='i' access='read'/>"
57 " <signal name='StatusNotifierItemRegistered'>"
58 " <arg type='s' name='service' direction='out'/>"
59 " </signal>"
60 " <signal name='StatusNotifierItemUnregistered'>"
61 " <arg type='s' name='service' direction='out'/>"
62 " </signal>"
63 " <signal name='StatusNotifierHostRegistered'>"
64 " <arg type='' name='service' direction='out'/>"
65 " </signal>"
66 " </interface>"
67 "</node>";
68
69static void host_registered_signal(DBusConnection *connection) {
70 // Send one signal for each protocol
71 DBusMessage *signal = dbus_message_new_signal(
72 "/StatusNotifierWatcher",
73 "org.freedesktop.StatusNotifierWatcher",
74 "StatusNotifierHostRegistered");
75
76 dbus_connection_send(connection, signal, NULL);
77 dbus_message_unref(signal);
78
79
80 signal = dbus_message_new_signal(
81 "/StatusNotifierWatcher",
82 "org.kde.StatusNotifierWatcher",
83 "StatusNotifierHostRegistered");
84
85 dbus_connection_send(connection, signal, NULL);
86 dbus_message_unref(signal);
87}
88static void item_registered_signal(DBusConnection *connection, const char *name) {
89 DBusMessage *signal = dbus_message_new_signal(
90 "/StatusNotifierWatcher",
91 "org.freedesktop.StatusNotifierWatcher",
92 "StatusNotifierItemRegistered");
93 dbus_message_append_args(signal,
94 DBUS_TYPE_STRING, &name,
95 DBUS_TYPE_INVALID);
96 dbus_connection_send(connection, signal, NULL);
97 dbus_message_unref(signal);
98
99 signal = dbus_message_new_signal(
100 "/StatusNotifierWatcher",
101 "org.kde.StatusNotifierWatcher",
102 "StatusNotifierItemRegistered");
103 dbus_message_append_args(signal,
104 DBUS_TYPE_STRING, &name,
105 DBUS_TYPE_INVALID);
106 dbus_connection_send(connection, signal, NULL);
107 dbus_message_unref(signal);
108}
109static void item_unregistered_signal(DBusConnection *connection, const char *name) {
110 DBusMessage *signal = dbus_message_new_signal(
111 "/StatusNotifierWatcher",
112 "org.freedesktop.StatusNotifierWatcher",
113 "StatusNotifierItemUnregistered");
114 dbus_message_append_args(signal,
115 DBUS_TYPE_STRING, &name,
116 DBUS_TYPE_INVALID);
117 dbus_connection_send(connection, signal, NULL);
118 dbus_message_unref(signal);
119
120 signal = dbus_message_new_signal(
121 "/StatusNotifierWatcher",
122 "org.kde.StatusNotifierWatcher",
123 "StatusNotifierItemUnregistered");
124 dbus_message_append_args(signal,
125 DBUS_TYPE_STRING, &name,
126 DBUS_TYPE_INVALID);
127 dbus_connection_send(connection, signal, NULL);
128 dbus_message_unref(signal);
129}
130
131static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) {
132 DBusMessage *reply;
133
134 reply = dbus_message_new_method_return(request);
135 dbus_message_append_args(reply,
136 DBUS_TYPE_STRING, &interface_xml,
137 DBUS_TYPE_INVALID);
138 dbus_connection_send(connection, reply, NULL);
139 dbus_message_unref(reply);
140}
141
142static void register_item(DBusConnection *connection, DBusMessage *message) {
143 DBusError error;
144 char *name;
145
146 dbus_error_init(&error);
147 if (!dbus_message_get_args(message, &error,
148 DBUS_TYPE_STRING, &name,
149 DBUS_TYPE_INVALID)) {
150 sway_log(L_ERROR, "Error parsing method args: %s\n", error.message);
151 }
152
153 sway_log(L_INFO, "RegisterStatusNotifierItem called with \"%s\"\n", name);
154
155 // Don't add duplicate or not real item
156 if (!dbus_validate_bus_name(name, NULL)) {
157 sway_log(L_INFO, "This item is not valid, we cannot keep track of it.");
158 return;
159 }
160
161 if (list_seq_find(items, (int (*)(const void *, const void *))strcmp, name) != -1) {
162 return;
163 }
164 if (!dbus_bus_name_has_owner(connection, name, &error)) {
165 return;
166 }
167
168 list_add(items, strdup(name));
169 item_registered_signal(connection, name);
170
171 // It's silly, but xembedsniproxy wants a reply for this function
172 DBusMessage *reply = dbus_message_new_method_return(message);
173 dbus_connection_send(connection, reply, NULL);
174 dbus_message_unref(reply);
175}
176
177static void register_host(DBusConnection *connection, DBusMessage *message) {
178 DBusError error;
179 char *name;
180
181 dbus_error_init(&error);
182 if (!dbus_message_get_args(message, &error,
183 DBUS_TYPE_STRING, &name,
184 DBUS_TYPE_INVALID)) {
185 sway_log(L_ERROR, "Error parsing method args: %s\n", error.message);
186 }
187
188 sway_log(L_INFO, "RegisterStatusNotifierHost called with \"%s\"\n", name);
189
190 // Don't add duplicate or not real host
191 if (!dbus_validate_bus_name(name, NULL)) {
192 sway_log(L_INFO, "This item is not valid, we cannot keep track of it.");
193 return;
194 }
195
196
197 if (list_seq_find(hosts, (int (*)(const void *, const void *))strcmp, name) != -1) {
198 return;
199 }
200 if (!dbus_bus_name_has_owner(connection, name, &error)) {
201 return;
202 }
203
204 list_add(hosts, strdup(name));
205 host_registered_signal(connection);
206}
207
208static void get_property(DBusConnection *connection, DBusMessage *message) {
209 DBusError error;
210 char *interface;
211 char *property;
212
213 dbus_error_init(&error);
214 if (!dbus_message_get_args(message, &error,
215 DBUS_TYPE_STRING, &interface,
216 DBUS_TYPE_STRING, &property,
217 DBUS_TYPE_INVALID)) {
218 sway_log(L_ERROR, "Error parsing prop args: %s\n", error.message);
219 return;
220 }
221
222 if (strcmp(property, "RegisteredStatusNotifierItems") == 0) {
223 sway_log(L_INFO, "Replying with items\n");
224 DBusMessage *reply;
225 reply = dbus_message_new_method_return(message);
226 DBusMessageIter iter;
227 DBusMessageIter sub;
228 DBusMessageIter subsub;
229
230 dbus_message_iter_init_append(reply, &iter);
231
232 dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
233 "as", &sub);
234 dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY,
235 "s", &subsub);
236
237 for (int i = 0; i < items->length; ++i) {
238 dbus_message_iter_append_basic(&subsub,
239 DBUS_TYPE_STRING, &items->items[i]);
240 }
241
242 dbus_message_iter_close_container(&sub, &subsub);
243 dbus_message_iter_close_container(&iter, &sub);
244
245 dbus_connection_send(connection, reply, NULL);
246 dbus_message_unref(reply);
247 } else if (strcmp(property, "IsStatusNotifierHostRegistered") == 0) {
248 DBusMessage *reply;
249 DBusMessageIter iter;
250 DBusMessageIter sub;
251 int registered = (hosts == NULL || hosts->length == 0) ? 0 : 1;
252
253 reply = dbus_message_new_method_return(message);
254
255 dbus_message_iter_init_append(reply, &iter);
256
257 dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
258 "b", &sub);
259 dbus_message_iter_append_basic(&sub,
260 DBUS_TYPE_BOOLEAN, &registered);
261
262 dbus_message_iter_close_container(&iter, &sub);
263
264 dbus_connection_send(connection, reply, NULL);
265 dbus_message_unref(reply);
266 } else if (strcmp(property, "ProtocolVersion") == 0) {
267 DBusMessage *reply;
268 DBusMessageIter iter;
269 DBusMessageIter sub;
270 const int version = 0;
271
272 reply = dbus_message_new_method_return(message);
273
274 dbus_message_iter_init_append(reply, &iter);
275
276 dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
277 "i", &sub);
278 dbus_message_iter_append_basic(&sub,
279 DBUS_TYPE_INT32, &version);
280
281 dbus_message_iter_close_container(&iter, &sub);
282 dbus_connection_send(connection, reply, NULL);
283 dbus_message_unref(reply);
284 }
285}
286
287static void set_property(DBusConnection *connection, DBusMessage *message) {
288 // All properties are read only and we don't allow new properties
289 return;
290}
291
292static void get_all(DBusConnection *connection, DBusMessage *message) {
293 DBusMessage *reply;
294 reply = dbus_message_new_method_return(message);
295 DBusMessageIter iter; /* a{v} */
296 DBusMessageIter arr;
297 DBusMessageIter dict;
298 DBusMessageIter sub;
299 DBusMessageIter subsub;
300 int registered = (hosts == NULL || hosts->length == 0) ? 0 : 1;
301 const int version = 0;
302 const char *prop;
303
304 // Could clean this up with a function for each prop
305 dbus_message_iter_init_append(reply, &iter);
306 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
307 "{sv}", &arr);
308
309 prop = "RegisteredStatusNotifierItems";
310 dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY,
311 NULL, &dict);
312 dbus_message_iter_append_basic(&dict,
313 DBUS_TYPE_STRING, &prop);
314 dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT,
315 "as", &sub);
316 dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY,
317 "s", &subsub);
318 for (int i = 0; i < items->length; ++i) {
319 dbus_message_iter_append_basic(&subsub,
320 DBUS_TYPE_STRING, &items->items[i]);
321 }
322 dbus_message_iter_close_container(&sub, &subsub);
323 dbus_message_iter_close_container(&dict, &sub);
324 dbus_message_iter_close_container(&arr, &dict);
325
326 prop = "IsStatusNotifierHostRegistered";
327 dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY,
328 NULL, &dict);
329 dbus_message_iter_append_basic(&dict,
330 DBUS_TYPE_STRING, &prop);
331 dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT,
332 "b", &sub);
333 dbus_message_iter_append_basic(&sub,
334 DBUS_TYPE_BOOLEAN, &registered);
335 dbus_message_iter_close_container(&dict, &sub);
336 dbus_message_iter_close_container(&arr, &dict);
337
338 prop = "ProtocolVersion";
339 dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY,
340 NULL, &dict);
341 dbus_message_iter_append_basic(&dict,
342 DBUS_TYPE_STRING, &prop);
343 dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT,
344 "i", &sub);
345 dbus_message_iter_append_basic(&sub,
346 DBUS_TYPE_INT32, &version);
347 dbus_message_iter_close_container(&dict, &sub);
348 dbus_message_iter_close_container(&arr, &dict);
349
350 dbus_message_iter_close_container(&iter, &arr);
351
352 dbus_connection_send(connection, reply, NULL);
353 dbus_message_unref(reply);
354}
355
356static DBusHandlerResult message_handler(DBusConnection *connection,
357 DBusMessage *message, void *data) {
358 const char *interface_name = dbus_message_get_interface(message);
359 const char *member_name = dbus_message_get_member(message);
360
361 // In order of the xml above
362 if (strcmp(interface_name, "org.freedesktop.DBus.Introspectable") == 0 &&
363 strcmp(member_name, "Introspect") == 0) {
364 // We don't have an introspect for KDE
365 respond_to_introspect(connection, message);
366 return DBUS_HANDLER_RESULT_HANDLED;
367 } else if (strcmp(interface_name, "org.freedesktop.DBus.Properties") == 0) {
368 if (strcmp(member_name, "Get") == 0) {
369 get_property(connection, message);
370 return DBUS_HANDLER_RESULT_HANDLED;
371 } else if (strcmp(member_name, "Set") == 0) {
372 set_property(connection, message);
373 return DBUS_HANDLER_RESULT_HANDLED;
374 } else if (strcmp(member_name, "GetAll") == 0) {
375 get_all(connection, message);
376 return DBUS_HANDLER_RESULT_HANDLED;
377 } else {
378 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
379 }
380 } else if (strcmp(interface_name, "org.freedesktop.StatusNotifierWatcher") == 0 ||
381 strcmp(interface_name, "org.kde.StatusNotifierWatcher") == 0) {
382 if (strcmp(member_name, "RegisterStatusNotifierItem") == 0) {
383 register_item(connection, message);
384 return DBUS_HANDLER_RESULT_HANDLED;
385 } else if (strcmp(member_name, "RegisterStatusNotifierHost") == 0) {
386 register_host(connection, message);
387 return DBUS_HANDLER_RESULT_HANDLED;
388 } else {
389 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
390 }
391 }
392 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
393}
394
395static DBusHandlerResult signal_handler(DBusConnection *connection,
396 DBusMessage *message, void *_data) {
397 if (dbus_message_is_signal(message, "org.freedesktop.DBus", "NameOwnerChanged")) {
398 // Only eat the message if it is name that we are watching
399 const char *name;
400 const char *old_owner;
401 const char *new_owner;
402 int index;
403 if (!dbus_message_get_args(message, NULL,
404 DBUS_TYPE_STRING, &name,
405 DBUS_TYPE_STRING, &old_owner,
406 DBUS_TYPE_STRING, &new_owner,
407 DBUS_TYPE_INVALID)) {
408 sway_log(L_ERROR, "Error getting LostName args");
409 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
410 }
411 if (strcmp(new_owner, "") != 0) {
412 // Name is not lost
413 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
414 }
415 if ((index = list_seq_find(items, (int (*)(const void *, const void *))strcmp, name)) != -1) {
416 sway_log(L_INFO, "Status Notifier Item lost %s", name);
417 free(items->items[index]);
418 list_del(items, index);
419 item_unregistered_signal(connection, name);
420
421 return DBUS_HANDLER_RESULT_HANDLED;
422 }
423 if ((index = list_seq_find(hosts, (int (*)(const void *, const void *))strcmp, name)) != -1) {
424 sway_log(L_INFO, "Status Notifier Host lost %s", name);
425 free(hosts->items[index]);
426 list_del(hosts, index);
427
428 return DBUS_HANDLER_RESULT_HANDLED;
429 }
430 }
431 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
432}
433
434static const DBusObjectPathVTable vtable = {
435 .message_function = message_handler,
436 .unregister_function = NULL,
437};
438
439int init_sni_watcher() {
440 DBusError error;
441 dbus_error_init(&error);
442 if (!conn) {
443 sway_log(L_ERROR, "Connection is null, cannot initiate StatusNotifierWatcher");
444 return -1;
445 }
446
447 items = create_list();
448 hosts = create_list();
449
450 int status = dbus_bus_request_name(conn, "org.freedesktop.StatusNotifierWatcher",
451 DBUS_NAME_FLAG_REPLACE_EXISTING,
452 &error);
453 if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
454 sway_log(L_DEBUG, "Got watcher name");
455 } else if (status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
456 sway_log(L_INFO, "Could not get watcher name, it may start later");
457 }
458 if (dbus_error_is_set(&error)) {
459 sway_log(L_ERROR, "dbus err getting watcher name: %s\n", error.message);
460 return -1;
461 }
462
463 status = dbus_bus_request_name(conn, "org.kde.StatusNotifierWatcher",
464 DBUS_NAME_FLAG_REPLACE_EXISTING,
465 &error);
466 if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
467 sway_log(L_DEBUG, "Got kde watcher name");
468 } else if (status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
469 sway_log(L_INFO, "Could not get kde watcher name, it may start later");
470 }
471 if (dbus_error_is_set(&error)) {
472 sway_log(L_ERROR, "dbus err getting kde watcher name: %s\n", error.message);
473 return -1;
474 }
475
476 dbus_connection_try_register_object_path(conn,
477 "/StatusNotifierWatcher",
478 &vtable, NULL, &error);
479 if (dbus_error_is_set(&error)) {
480 sway_log(L_ERROR, "dbus_err: %s\n", error.message);
481 return -1;
482 }
483
484 dbus_bus_add_match(conn,
485 "type='signal',\
486 sender='org.freedesktop.DBus',\
487 interface='org.freedesktop.DBus',\
488 member='NameOwnerChanged'",
489 &error);
490
491 if (dbus_error_is_set(&error)) {
492 sway_log(L_ERROR, "DBus error getting match args: %s", error.message);
493 }
494
495 dbus_connection_add_filter(conn, signal_handler, NULL, NULL);
496 return 0;
497}
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 @@
1#define _XOPEN_SOURCE 700
2#include <unistd.h>
3#include <stdlib.h>
4#include <string.h>
5#include <sys/wait.h>
6#include <dbus/dbus.h>
7#include "swaybar/bar.h"
8#include "swaybar/tray/tray.h"
9#include "swaybar/tray/dbus.h"
10#include "swaybar/tray/sni.h"
11#include "swaybar/tray/sni_watcher.h"
12#include "swaybar/bar.h"
13#include "swaybar/config.h"
14#include "list.h"
15#include "log.h"
16
17struct tray *tray;
18
19static void register_host(char *name) {
20 DBusMessage *message;
21
22 message = dbus_message_new_method_call(
23 "org.freedesktop.StatusNotifierWatcher",
24 "/StatusNotifierWatcher",
25 "org.freedesktop.StatusNotifierWatcher",
26 "RegisterStatusNotifierHost");
27 if (!message) {
28 sway_log(L_ERROR, "Cannot allocate dbus method call");
29 return;
30 }
31
32 dbus_message_append_args(message,
33 DBUS_TYPE_STRING, &name,
34 DBUS_TYPE_INVALID);
35
36 dbus_connection_send(conn, message, NULL);
37
38 dbus_message_unref(message);
39}
40
41static void get_items_reply(DBusPendingCall *pending, void *_data) {
42 DBusMessage *reply = dbus_pending_call_steal_reply(pending);
43
44 if (!reply) {
45 sway_log(L_ERROR, "Got no items reply from sni watcher");
46 goto bail;
47 }
48
49 int message_type = dbus_message_get_type(reply);
50
51 if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
52 char *msg;
53
54 dbus_message_get_args(reply, NULL,
55 DBUS_TYPE_STRING, &msg,
56 DBUS_TYPE_INVALID);
57
58 sway_log(L_ERROR, "Message is error: %s", msg);
59 goto bail;
60 }
61
62 DBusMessageIter iter;
63 DBusMessageIter variant;
64 DBusMessageIter array;
65
66 dbus_message_iter_init(reply, &iter);
67 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
68 sway_log(L_ERROR, "Replyed with wrong type, not v(as)");
69 goto bail;
70 }
71 dbus_message_iter_recurse(&iter, &variant);
72 if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY ||
73 dbus_message_iter_get_element_type(&variant) != DBUS_TYPE_STRING) {
74 sway_log(L_ERROR, "Replyed with wrong type, not v(as)");
75 goto bail;
76 }
77
78 // Clear list
79 list_foreach(tray->items, (void (*)(void *))sni_free);
80 list_free(tray->items);
81 tray->items = create_list();
82
83 // O(n) function, could be faster dynamically reading values
84 int len = dbus_message_iter_get_element_count(&variant);
85
86 dbus_message_iter_recurse(&variant, &array);
87 for (int i = 0; i < len; i++) {
88 const char *name;
89 dbus_message_iter_get_basic(&array, &name);
90
91 struct StatusNotifierItem *item = sni_create(name);
92
93 if (item) {
94 sway_log(L_DEBUG, "Item registered with host: %s", name);
95 list_add(tray->items, item);
96 dirty = true;
97 }
98 }
99
100bail:
101 dbus_message_unref(reply);
102 dbus_pending_call_unref(pending);
103 return;
104}
105static void get_items() {
106 DBusPendingCall *pending;
107 DBusMessage *message = dbus_message_new_method_call(
108 "org.freedesktop.StatusNotifierWatcher",
109 "/StatusNotifierWatcher",
110 "org.freedesktop.DBus.Properties",
111 "Get");
112
113 const char *iface = "org.freedesktop.StatusNotifierWatcher";
114 const char *prop = "RegisteredStatusNotifierItems";
115 dbus_message_append_args(message,
116 DBUS_TYPE_STRING, &iface,
117 DBUS_TYPE_STRING, &prop,
118 DBUS_TYPE_INVALID);
119
120 bool status =
121 dbus_connection_send_with_reply(conn, message, &pending, -1);
122 dbus_message_unref(message);
123
124 if (!(pending || status)) {
125 sway_log(L_ERROR, "Could not get items");
126 return;
127 }
128
129 dbus_pending_call_set_notify(pending, get_items_reply, NULL, NULL);
130}
131
132static DBusHandlerResult signal_handler(DBusConnection *connection,
133 DBusMessage *message, void *_data) {
134 if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierWatcher",
135 "StatusNotifierItemRegistered")) {
136 const char *name;
137 if (!dbus_message_get_args(message, NULL,
138 DBUS_TYPE_STRING, &name,
139 DBUS_TYPE_INVALID)) {
140 sway_log(L_ERROR, "Error getting StatusNotifierItemRegistered args");
141 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
142 }
143
144 if (list_seq_find(tray->items, sni_str_cmp, name) == -1) {
145 struct StatusNotifierItem *item = sni_create(name);
146
147 if (item) {
148 list_add(tray->items, item);
149 dirty = true;
150 }
151 }
152
153 return DBUS_HANDLER_RESULT_HANDLED;
154 } else if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierWatcher",
155 "StatusNotifierItemUnregistered")) {
156 const char *name;
157 if (!dbus_message_get_args(message, NULL,
158 DBUS_TYPE_STRING, &name,
159 DBUS_TYPE_INVALID)) {
160 sway_log(L_ERROR, "Error getting StatusNotifierItemUnregistered args");
161 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
162 }
163
164 int index;
165 if ((index = list_seq_find(tray->items, sni_str_cmp, name)) != -1) {
166 sni_free(tray->items->items[index]);
167 list_del(tray->items, index);
168 dirty = true;
169 } else {
170 // If it's not in our list, then our list is incorrect.
171 // Fetch all items again
172 sway_log(L_INFO, "Host item list incorrect, refreshing");
173 get_items();
174 }
175
176 return DBUS_HANDLER_RESULT_HANDLED;
177 } else if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierItem",
178 "NewIcon") || dbus_message_is_signal(message,
179 "org.kde.StatusNotifierItem", "NewIcon")) {
180 const char *name;
181 int index;
182 struct StatusNotifierItem *item;
183
184 name = dbus_message_get_sender(message);
185 if ((index = list_seq_find(tray->items, sni_uniq_cmp, name)) != -1) {
186 item = tray->items->items[index];
187 sway_log(L_INFO, "NewIcon signal from item %s", item->name);
188 get_icon(item);
189 }
190
191 return DBUS_HANDLER_RESULT_HANDLED;
192 }
193 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
194}
195
196static int init_host() {
197 tray = (struct tray *)malloc(sizeof(tray));
198
199 tray->items = create_list();
200
201 DBusError error;
202 dbus_error_init(&error);
203 char *name = NULL;
204 if (!conn) {
205 sway_log(L_ERROR, "Connection is null, cannot init SNI host");
206 goto err;
207 }
208 name = calloc(sizeof(char), 256);
209
210 if (!name) {
211 sway_log(L_ERROR, "Cannot allocate name");
212 goto err;
213 }
214
215 pid_t pid = getpid();
216 if (snprintf(name, 256, "org.freedesktop.StatusNotifierHost-%d", pid)
217 >= 256) {
218 sway_log(L_ERROR, "Cannot get host name because string is too short."
219 "This should not happen");
220 goto err;
221 }
222
223 // We want to be the sole owner of this name
224 if (dbus_bus_request_name(conn, name, DBUS_NAME_FLAG_DO_NOT_QUEUE,
225 &error) != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
226 sway_log(L_ERROR, "Cannot get host name and start the tray");
227 goto err;
228 }
229 if (dbus_error_is_set(&error)) {
230 sway_log(L_ERROR, "Dbus err getting host name: %s\n", error.message);
231 goto err;
232 }
233 sway_log(L_DEBUG, "Got host name");
234
235 register_host(name);
236
237 get_items();
238
239 // Perhaps use addmatch helper functions like wlc does?
240 dbus_bus_add_match(conn,
241 "type='signal',\
242 sender='org.freedesktop.StatusNotifierWatcher',\
243 member='StatusNotifierItemRegistered'",
244 &error);
245 if (dbus_error_is_set(&error)) {
246 sway_log(L_ERROR, "dbus_err: %s", error.message);
247 goto err;
248 }
249 dbus_bus_add_match(conn,
250 "type='signal',\
251 sender='org.freedesktop.StatusNotifierWatcher',\
252 member='StatusNotifierItemUnregistered'",
253 &error);
254 if (dbus_error_is_set(&error)) {
255 sway_log(L_ERROR, "dbus_err: %s", error.message);
256 return -1;
257 }
258
259 // SNI matches
260 dbus_bus_add_match(conn,
261 "type='signal',\
262 interface='org.freedesktop.StatusNotifierItem',\
263 member='NewIcon'",
264 &error);
265 if (dbus_error_is_set(&error)) {
266 sway_log(L_ERROR, "dbus_err %s", error.message);
267 goto err;
268 }
269 dbus_bus_add_match(conn,
270 "type='signal',\
271 interface='org.kde.StatusNotifierItem',\
272 member='NewIcon'",
273 &error);
274 if (dbus_error_is_set(&error)) {
275 sway_log(L_ERROR, "dbus_err %s", error.message);
276 goto err;
277 }
278
279 dbus_connection_add_filter(conn, signal_handler, NULL, NULL);
280
281 free(name);
282 return 0;
283
284err:
285 // TODO better handle errors
286 free(name);
287 return -1;
288}
289
290void tray_mouse_event(struct output *output, int x, int y,
291 uint32_t button, uint32_t state) {
292
293 struct window *window = output->window;
294 uint32_t tray_padding = swaybar.config->tray_padding;
295 int tray_width = window->width * window->scale;
296
297 for (int i = 0; i < output->items->length; ++i) {
298 struct sni_icon_ref *item =
299 output->items->items[i];
300 int icon_width = cairo_image_surface_get_width(item->icon);
301
302 tray_width -= tray_padding;
303 if (x <= tray_width && x >= tray_width - icon_width) {
304 if (button == swaybar.config->activate_button) {
305 sni_activate(item->ref, x, y);
306 } else if (button == swaybar.config->context_button) {
307 sni_context_menu(item->ref, x, y);
308 } else if (button == swaybar.config->secondary_button) {
309 sni_secondary(item->ref, x, y);
310 }
311 break;
312 }
313 tray_width -= icon_width;
314 }
315}
316
317uint32_t tray_render(struct output *output, struct config *config) {
318 struct window *window = output->window;
319 cairo_t *cairo = window->cairo;
320
321 // Tray icons
322 uint32_t tray_padding = config->tray_padding;
323 uint32_t tray_width = window->width * window->scale;
324 const int item_size = (window->height * window->scale) - (2 * tray_padding);
325
326 if (item_size < 0) {
327 // Can't render items if the padding is too large
328 return tray_width;
329 }
330
331 if (config->tray_output && strcmp(config->tray_output, output->name) != 0) {
332 return tray_width;
333 }
334
335 for (int i = 0; i < tray->items->length; ++i) {
336 struct StatusNotifierItem *item =
337 tray->items->items[i];
338 if (!item->image) {
339 continue;
340 }
341
342 struct sni_icon_ref *render_item = NULL;
343 int j;
344 for (j = i; j < output->items->length; ++j) {
345 struct sni_icon_ref *ref =
346 output->items->items[j];
347 if (ref->ref == item) {
348 render_item = ref;
349 break;
350 } else {
351 sni_icon_ref_free(ref);
352 list_del(output->items, j);
353 }
354 }
355
356 if (!render_item) {
357 render_item = sni_icon_ref_create(item, item_size);
358 list_add(output->items, render_item);
359 } else if (item->dirty) {
360 // item needs re-render
361 sni_icon_ref_free(render_item);
362 output->items->items[j] = render_item =
363 sni_icon_ref_create(item, item_size);
364 }
365
366 tray_width -= tray_padding;
367 tray_width -= item_size;
368
369 cairo_operator_t op = cairo_get_operator(cairo);
370 cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
371 cairo_set_source_surface(cairo, render_item->icon, tray_width, tray_padding);
372 cairo_rectangle(cairo, tray_width, tray_padding, item_size, item_size);
373 cairo_fill(cairo);
374 cairo_set_operator(cairo, op);
375
376 item->dirty = false;
377 }
378
379
380 if (tray_width != window->width * window->scale) {
381 tray_width -= tray_padding;
382 }
383
384 return tray_width;
385}
386
387void init_tray(struct bar *bar) {
388 if (!bar->config->tray_output || strcmp(bar->config->tray_output, "none") != 0) {
389 /* Connect to the D-Bus */
390 dbus_init();
391
392 /* Start the SNI watcher */
393 init_sni_watcher();
394
395 /* Start the SNI host */
396 init_host();
397 }
398}