summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.ja.md2
-rw-r--r--README.md8
-rw-r--r--common/pango.c53
-rw-r--r--common/readline.c25
-rw-r--r--include/pango.h14
-rw-r--r--include/sway/commands.h1
-rw-r--r--include/sway/config.h4
-rw-r--r--include/sway/tree/view.h2
-rw-r--r--include/swaybar/event_loop.h4
-rw-r--r--include/swaybar/status_line.h32
-rw-r--r--sway/commands.c14
-rw-r--r--sway/commands/create_output.c45
-rw-r--r--sway/config/bar.c26
-rw-r--r--sway/config/input.c7
-rw-r--r--sway/desktop/render.c11
-rw-r--r--sway/desktop/transaction.c11
-rw-r--r--sway/ipc-json.c2
-rw-r--r--sway/main.c1
-rw-r--r--sway/meson.build1
-rw-r--r--sway/tree/output.c2
-rw-r--r--sway/tree/view.c10
-rw-r--r--swaybar/bar.c112
-rw-r--r--swaybar/event_loop.c4
-rw-r--r--swaybar/i3bar.c216
-rw-r--r--swaybar/render.c33
-rw-r--r--swaybar/status_line.c148
26 files changed, 444 insertions, 344 deletions
diff --git a/README.ja.md b/README.ja.md
index 75d29c73..b0488c53 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -5,7 +5,7 @@ i3互換な[Wayland](http://wayland.freedesktop.org/)コンポジタです。
5[FAQ](https://github.com/swaywm/sway/wiki)も合わせてご覧ください。 5[FAQ](https://github.com/swaywm/sway/wiki)も合わせてご覧ください。
6[IRC チャンネル](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net)もあります。 6[IRC チャンネル](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net)もあります。
7 7
8**注意**: Swayは現在*凍結中*であり、Swayとwlrootsの統合が完了するまで、新たな機能は追加されません。バグフィックスは行われます。詳しくは[この記事](https://drewdevault.com/2017/10/09/Future-of-sway.html)をご覧ください。wlrootsとの統合状況については、[このチケット](https://github.com/swaywm/sway/issues/1390)をご覧ください。 8**注意**: Swayは現在*凍結中*であり、wlcからwlrootsへの移植が完了するまで新たな機能は追加されません。2018年9月以降に発見されるバグは0.15では対応されません。詳しくは[この記事](https://drewdevault.com/2017/10/09/Future-of-sway.html)をご覧ください。wlrootsとの統合状況については、[このチケット](https://github.com/swaywm/sway/issues/1390)をご覧ください。
9 9
10[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) 10[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png)
11 11
diff --git a/README.md b/README.md
index e277e6bd..57bc4b79 100644
--- a/README.md
+++ b/README.md
@@ -9,10 +9,10 @@ Read the [FAQ](https://github.com/swaywm/sway/wiki). Join the
9[IRC channel](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on 9[IRC channel](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on
10irc.freenode.net). 10irc.freenode.net).
11 11
12**Notice**: You are viewing the **unstable** and **unsupported** master branch 12**Notice**: work is well underway to port sway to
13of sway, where work is ongoing to port it to 13[wlroots](https://github.com/swaywm/wlroots). This is **unstable** and
14[wlroots](https://github.com/swaywm/wlroots). The supported branch is the 0.15 14**unsupported** - we accept patches, but are not fond of bug reports. We are no
15branch, and end users are encouraged to use the stable releases cut from it. 15longer accepting bugs for 0.15.
16 16
17If you'd like to support sway development, please contribute to [SirCmpwn's 17If you'd like to support sway development, please contribute to [SirCmpwn's
18Patreon page](https://patreon.com/sircmpwn). 18Patreon page](https://patreon.com/sircmpwn).
diff --git a/common/pango.c b/common/pango.c
index ea71ac4a..5afd72d8 100644
--- a/common/pango.c
+++ b/common/pango.c
@@ -7,66 +7,45 @@
7#include <stdlib.h> 7#include <stdlib.h>
8#include <string.h> 8#include <string.h>
9#include "log.h" 9#include "log.h"
10#include "stringop.h"
10 11
11int escape_markup_text(const char *src, char *dest, int dest_length) { 12size_t escape_markup_text(const char *src, char *dest) {
12 int length = 0; 13 size_t length = 0;
14 if (dest) {
15 dest[0] = '\0';
16 }
13 17
14 while (src[0]) { 18 while (src[0]) {
15 switch (src[0]) { 19 switch (src[0]) {
16 case '&': 20 case '&':
17 length += 5; 21 length += 5;
18 if (dest && dest_length - length >= 0) { 22 lenient_strcat(dest, "&amp;");
19 dest += sprintf(dest, "%s", "&amp;");
20 } else {
21 dest_length = -1;
22 }
23 break; 23 break;
24 case '<': 24 case '<':
25 length += 4; 25 length += 4;
26 if (dest && dest_length - length >= 0) { 26 lenient_strcat(dest, "&lt;");
27 dest += sprintf(dest, "%s", "&lt;");
28 } else {
29 dest_length = -1;
30 }
31 break; 27 break;
32 case '>': 28 case '>':
33 length += 4; 29 length += 4;
34 if (dest && dest_length - length >= 0) { 30 lenient_strcat(dest, "&gt;");
35 dest += sprintf(dest, "%s", "&gt;");
36 } else {
37 dest_length = -1;
38 }
39 break; 31 break;
40 case '\'': 32 case '\'':
41 length += 6; 33 length += 6;
42 if (dest && dest_length - length >= 0) { 34 lenient_strcat(dest, "&apos;");
43 dest += sprintf(dest, "%s", "&apos;");
44 } else {
45 dest_length = -1;
46 }
47 break; 35 break;
48 case '"': 36 case '"':
49 length += 6; 37 length += 6;
50 if (dest && dest_length - length >= 0) { 38 lenient_strcat(dest, "&quot;");
51 dest += sprintf(dest, "%s", "&quot;");
52 } else {
53 dest_length = -1;
54 }
55 break; 39 break;
56 default: 40 default:
57 length += 1; 41 if (dest) {
58 if (dest && dest_length - length >= 0) { 42 dest[length] = *src;
59 *(dest++) = *src; 43 dest[length + 1] = '\0';
60 } else {
61 dest_length = -1;
62 } 44 }
45 length += 1;
63 } 46 }
64 src++; 47 src++;
65 } 48 }
66 // if we could not fit the escaped string in dest, return -1
67 if (dest && dest_length == -1) {
68 return -1;
69 }
70 return length; 49 return length;
71} 50}
72 51
@@ -78,7 +57,7 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
78 char *buf; 57 char *buf;
79 GError *error = NULL; 58 GError *error = NULL;
80 if (pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, &error)) { 59 if (pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, &error)) {
81 pango_layout_set_markup(layout, buf, -1); 60 pango_layout_set_text(layout, buf, -1);
82 free(buf); 61 free(buf);
83 } else { 62 } else {
84 wlr_log(WLR_ERROR, "pango_parse_markup '%s' -> error %s", text, 63 wlr_log(WLR_ERROR, "pango_parse_markup '%s' -> error %s", text,
diff --git a/common/readline.c b/common/readline.c
index a2c69018..58652429 100644
--- a/common/readline.c
+++ b/common/readline.c
@@ -70,28 +70,3 @@ char *peek_line(FILE *file, int line_offset, long *position) {
70 fseek(file, pos, SEEK_SET); 70 fseek(file, pos, SEEK_SET);
71 return line; 71 return line;
72} 72}
73
74char *read_line_buffer(FILE *file, char *string, size_t string_len) {
75 size_t length = 0;
76 if (!string) {
77 return NULL;
78 }
79 while (1) {
80 int c = getc(file);
81 if (c == EOF || c == '\n' || c == '\0') {
82 break;
83 }
84 if (c == '\r') {
85 continue;
86 }
87 string[length++] = c;
88 if (string_len <= length) {
89 return NULL;
90 }
91 }
92 if (length + 1 == string_len) {
93 return NULL;
94 }
95 string[length] = '\0';
96 return string;
97}
diff --git a/include/pango.h b/include/pango.h
index 09a535a5..6ab83c16 100644
--- a/include/pango.h
+++ b/include/pango.h
@@ -6,17 +6,13 @@
6#include <cairo/cairo.h> 6#include <cairo/cairo.h>
7#include <pango/pangocairo.h> 7#include <pango/pangocairo.h>
8 8
9/* Utility function which escape characters a & < > ' ". 9/**
10 * Utility function which escape characters a & < > ' ".
10 * 11 *
11 * If the dest parameter is NULL, then the function returns the length of 12 * The function returns the length of the escaped string, optionally writing the
12 * of the escaped src string. The dest_length doesn't matter. 13 * escaped string to dest if provided.
13 *
14 * If the dest parameter is not NULL then the fuction escapes the src string
15 * an puts the escaped string in dest and returns the lenght of the escaped string.
16 * The dest_length parameter is the size of dest array. If the size of dest is not
17 * enough, then the function returns -1.
18 */ 14 */
19int escape_markup_text(const char *src, char *dest, int dest_length); 15size_t escape_markup_text(const char *src, char *dest);
20PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, 16PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
21 const char *text, double scale, bool markup); 17 const char *text, double scale, bool markup);
22void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, 18void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
diff --git a/include/sway/commands.h b/include/sway/commands.h
index e51b12fd..226cf932 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -103,6 +103,7 @@ sway_cmd cmd_client_urgent;
103sway_cmd cmd_client_placeholder; 103sway_cmd cmd_client_placeholder;
104sway_cmd cmd_client_background; 104sway_cmd cmd_client_background;
105sway_cmd cmd_commands; 105sway_cmd cmd_commands;
106sway_cmd cmd_create_output;
106sway_cmd cmd_debuglog; 107sway_cmd cmd_debuglog;
107sway_cmd cmd_default_border; 108sway_cmd cmd_default_border;
108sway_cmd cmd_default_floating_border; 109sway_cmd cmd_default_floating_border;
diff --git a/include/sway/config.h b/include/sway/config.h
index b53c1f1f..36d78ec6 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -512,9 +512,7 @@ void free_sway_binding(struct sway_binding *sb);
512 512
513void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); 513void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding);
514 514
515void load_swaybars(); 515void load_swaybars(void);
516
517void invoke_swaybar(struct bar_config *bar);
518 516
519void terminate_swaybg(pid_t pid); 517void terminate_swaybg(pid_t pid);
520 518
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 439dc1bf..d10251dd 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -61,6 +61,8 @@ struct sway_view {
61 struct sway_container *container; // NULL if unmapped and transactions finished 61 struct sway_container *container; // NULL if unmapped and transactions finished
62 struct wlr_surface *surface; // NULL for unmapped views 62 struct wlr_surface *surface; // NULL for unmapped views
63 63
64 pid_t pid;
65
64 // Geometry of the view itself (excludes borders) in layout coordinates 66 // Geometry of the view itself (excludes borders) in layout coordinates
65 double x, y; 67 double x, y;
66 int width, height; 68 int width, height;
diff --git a/include/swaybar/event_loop.h b/include/swaybar/event_loop.h
index 99f6ed36..47be5b79 100644
--- a/include/swaybar/event_loop.h
+++ b/include/swaybar/event_loop.h
@@ -19,8 +19,8 @@ bool remove_event(int fd);
19bool remove_timer(timer_t timer); 19bool remove_timer(timer_t timer);
20 20
21// Blocks and returns after sending callbacks 21// Blocks and returns after sending callbacks
22void event_loop_poll(); 22void event_loop_poll(void);
23 23
24void init_event_loop(); 24void init_event_loop(void);
25 25
26#endif 26#endif
diff --git a/include/swaybar/status_line.h b/include/swaybar/status_line.h
index 150267cd..d3eabdf6 100644
--- a/include/swaybar/status_line.h
+++ b/include/swaybar/status_line.h
@@ -1,5 +1,6 @@
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#include <json-c/json.h>
3#include <stdint.h> 4#include <stdint.h>
4#include <stdio.h> 5#include <stdio.h>
5#include <stdbool.h> 6#include <stdbool.h>
@@ -12,28 +13,6 @@ enum status_protocol {
12 PROTOCOL_I3BAR, 13 PROTOCOL_I3BAR,
13}; 14};
14 15
15struct text_protocol_state {
16 char *buffer;
17 size_t buffer_size;
18};
19
20enum json_node_type {
21 JSON_NODE_UNKNOWN,
22 JSON_NODE_ARRAY,
23 JSON_NODE_STRING,
24};
25
26struct i3bar_protocol_state {
27 bool click_events;
28 char *buffer;
29 size_t buffer_size;
30 size_t buffer_index;
31 const char *current_node;
32 bool escape;
33 size_t depth;
34 enum json_node_type nodes[16];
35};
36
37struct i3bar_block { 16struct i3bar_block {
38 struct wl_list link; 17 struct wl_list link;
39 int ref_count; 18 int ref_count;
@@ -63,8 +42,13 @@ struct status_line {
63 const char *text; 42 const char *text;
64 struct wl_list blocks; // i3bar_block::link 43 struct wl_list blocks; // i3bar_block::link
65 44
66 struct text_protocol_state text_state; 45 bool click_events;
67 struct i3bar_protocol_state i3bar_state; 46 char *buffer;
47 size_t buffer_size;
48 size_t buffer_index;
49 bool started;
50 bool expecting_comma;
51 json_tokener *tokener;
68}; 52};
69 53
70struct status_line *status_line_init(char *cmd); 54struct status_line *status_line_init(char *cmd);
diff --git a/sway/commands.c b/sway/commands.c
index 41e1c653..07169f1e 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -143,6 +143,7 @@ static struct cmd_handler config_handlers[] = {
143/* Runtime-only commands. Keep alphabetized */ 143/* Runtime-only commands. Keep alphabetized */
144static struct cmd_handler command_handlers[] = { 144static struct cmd_handler command_handlers[] = {
145 { "border", cmd_border }, 145 { "border", cmd_border },
146 { "create_output", cmd_create_output },
146 { "exit", cmd_exit }, 147 { "exit", cmd_exit },
147 { "floating", cmd_floating }, 148 { "floating", cmd_floating },
148 { "fullscreen", cmd_fullscreen }, 149 { "fullscreen", cmd_fullscreen },
@@ -215,18 +216,23 @@ struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers,
215 216
216static void set_config_node(struct sway_node *node) { 217static void set_config_node(struct sway_node *node) {
217 config->handler_context.node = node; 218 config->handler_context.node = node;
219 config->handler_context.container = NULL;
220 config->handler_context.workspace = NULL;
221
222 if (node == NULL) {
223 return;
224 }
225
218 switch (node->type) { 226 switch (node->type) {
219 case N_CONTAINER: 227 case N_CONTAINER:
220 config->handler_context.container = node->sway_container; 228 config->handler_context.container = node->sway_container;
221 config->handler_context.workspace = node->sway_container->workspace; 229 config->handler_context.workspace = node->sway_container->workspace;
222 break; 230 break;
223 case N_WORKSPACE: 231 case N_WORKSPACE:
224 config->handler_context.container = NULL;
225 config->handler_context.workspace = node->sway_workspace; 232 config->handler_context.workspace = node->sway_workspace;
226 break; 233 break;
227 default: 234 case N_ROOT:
228 config->handler_context.container = NULL; 235 case N_OUTPUT:
229 config->handler_context.workspace = NULL;
230 break; 236 break;
231 } 237 }
232} 238}
diff --git a/sway/commands/create_output.c b/sway/commands/create_output.c
new file mode 100644
index 00000000..1c2464ea
--- /dev/null
+++ b/sway/commands/create_output.c
@@ -0,0 +1,45 @@
1#include <wlr/config.h>
2#include <wlr/backend/multi.h>
3#include <wlr/backend/wayland.h>
4#ifdef WLR_HAS_X11_BACKEND
5#include <wlr/backend/x11.h>
6#endif
7#include "sway/commands.h"
8#include "sway/server.h"
9#include "log.h"
10
11static void create_output(struct wlr_backend *backend, void *data) {
12 bool *done = data;
13 if (*done) {
14 return;
15 }
16
17 if (wlr_backend_is_wl(backend)) {
18 wlr_wl_output_create(backend);
19 *done = true;
20 }
21#ifdef WLR_HAS_X11_BACKEND
22 else if (wlr_backend_is_x11(backend)) {
23 wlr_x11_output_create(backend);
24 *done = true;
25 }
26#endif
27}
28
29/**
30 * This command is intended for developer use only.
31 */
32struct cmd_results *cmd_create_output(int argc, char **argv) {
33 sway_assert(wlr_backend_is_multi(server.backend),
34 "Expected a multi backend");
35
36 bool done = false;
37 wlr_multi_for_each_backend(server.backend, create_output, &done);
38
39 if (!done) {
40 return cmd_results_new(CMD_INVALID, "create_output",
41 "Can only create outputs for Wayland or X11 backends");
42 }
43
44 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
45}
diff --git a/sway/config/bar.c b/sway/config/bar.c
index f83b37d1..48a632fb 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -165,7 +165,7 @@ cleanup:
165 return NULL; 165 return NULL;
166} 166}
167 167
168void invoke_swaybar(struct bar_config *bar) { 168static void invoke_swaybar(struct bar_config *bar) {
169 // Pipe to communicate errors 169 // Pipe to communicate errors
170 int filedes[2]; 170 int filedes[2];
171 if (pipe(filedes) == -1) { 171 if (pipe(filedes) == -1) {
@@ -219,27 +219,13 @@ void invoke_swaybar(struct bar_config *bar) {
219 close(filedes[1]); 219 close(filedes[1]);
220} 220}
221 221
222void load_swaybars() { 222void load_swaybars(void) {
223 for (int i = 0; i < config->bars->length; ++i) { 223 for (int i = 0; i < config->bars->length; ++i) {
224 struct bar_config *bar = config->bars->items[i]; 224 struct bar_config *bar = config->bars->items[i];
225 bool apply = false; 225 if (bar->pid != 0) {
226 if (bar->outputs) { 226 terminate_swaybar(bar->pid);
227 for (int j = 0; j < bar->outputs->length; ++j) {
228 char *o = bar->outputs->items[j];
229 if (!strcmp(o, "*") || output_by_name(o)) {
230 apply = true;
231 break;
232 }
233 }
234 } else {
235 apply = true;
236 }
237 if (apply) {
238 if (bar->pid != 0) {
239 terminate_swaybar(bar->pid);
240 }
241 wlr_log(WLR_DEBUG, "Invoking swaybar for bar id '%s'", bar->id);
242 invoke_swaybar(bar);
243 } 227 }
228 wlr_log(WLR_DEBUG, "Invoking swaybar for bar id '%s'", bar->id);
229 invoke_swaybar(bar);
244 } 230 }
245} 231}
diff --git a/sway/config/input.c b/sway/config/input.c
index 9885e85c..ad5b96c8 100644
--- a/sway/config/input.c
+++ b/sway/config/input.c
@@ -140,6 +140,13 @@ void free_input_config(struct input_config *ic) {
140 return; 140 return;
141 } 141 }
142 free(ic->identifier); 142 free(ic->identifier);
143 free(ic->xkb_layout);
144 free(ic->xkb_model);
145 free(ic->xkb_options);
146 free(ic->xkb_rules);
147 free(ic->xkb_variant);
148 free(ic->mapped_from_region);
149 free(ic->mapped_to_output);
143 free(ic); 150 free(ic);
144} 151}
145 152
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index 1d2f445d..af4e2905 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -914,12 +914,17 @@ void output_render(struct sway_output *output, struct timespec *when,
914 struct wlr_output *wlr_output = output->wlr_output; 914 struct wlr_output *wlr_output = output->wlr_output;
915 915
916 struct wlr_renderer *renderer = 916 struct wlr_renderer *renderer =
917 wlr_backend_get_renderer(wlr_output->backend); 917 wlr_backend_get_renderer(wlr_output->backend);
918 if (!sway_assert(renderer != NULL, 918 if (!sway_assert(renderer != NULL,
919 "expected the output backend to have a renderer")) { 919 "expected the output backend to have a renderer")) {
920 return; 920 return;
921 } 921 }
922 922
923 struct sway_workspace *workspace = output->current.active_workspace;
924 if (workspace == NULL) {
925 return;
926 }
927
923 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); 928 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
924 929
925 if (!pixman_region32_not_empty(damage)) { 930 if (!pixman_region32_not_empty(damage)) {
@@ -935,13 +940,11 @@ void output_render(struct sway_output *output, struct timespec *when,
935 pixman_region32_union_rect(damage, damage, 0, 0, width, height); 940 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
936 } 941 }
937 942
938 struct sway_workspace *workspace = output->current.active_workspace;
939 struct sway_container *fullscreen_con = workspace->current.fullscreen;
940
941 if (output_has_opaque_overlay_layer_surface(output)) { 943 if (output_has_opaque_overlay_layer_surface(output)) {
942 goto render_overlay; 944 goto render_overlay;
943 } 945 }
944 946
947 struct sway_container *fullscreen_con = workspace->current.fullscreen;
945 if (fullscreen_con) { 948 if (fullscreen_con) {
946 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; 949 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
947 950
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index d747e279..797f6b4c 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -6,6 +6,7 @@
6#include <string.h> 6#include <string.h>
7#include <time.h> 7#include <time.h>
8#include <wlr/types/wlr_buffer.h> 8#include <wlr/types/wlr_buffer.h>
9#include "sway/config.h"
9#include "sway/debug.h" 10#include "sway/debug.h"
10#include "sway/desktop.h" 11#include "sway/desktop.h"
11#include "sway/desktop/idle_inhibit_v1.h" 12#include "sway/desktop/idle_inhibit_v1.h"
@@ -390,6 +391,16 @@ static bool should_configure(struct sway_node *node,
390 } 391 }
391 struct sway_container_state *cstate = &node->sway_container->current; 392 struct sway_container_state *cstate = &node->sway_container->current;
392 struct sway_container_state *istate = instruction->container_state; 393 struct sway_container_state *istate = instruction->container_state;
394#ifdef HAVE_XWAYLAND
395 // Xwayland views are position-aware and need to be reconfigured
396 // when their position changes.
397 if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) {
398 if (cstate->view_x != istate->view_x ||
399 cstate->view_y != istate->view_y) {
400 return true;
401 }
402 }
403#endif
393 if (cstate->view_width == istate->view_width && 404 if (cstate->view_width == istate->view_width &&
394 cstate->view_height == istate->view_height) { 405 cstate->view_height == istate->view_height) {
395 return false; 406 return false;
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 52278be2..f054ac9f 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -221,6 +221,8 @@ static const char *describe_container_border(enum sway_container_border border)
221} 221}
222 222
223static void ipc_json_describe_view(struct sway_container *c, json_object *object) { 223static void ipc_json_describe_view(struct sway_container *c, json_object *object) {
224 json_object_object_add(object, "pid", json_object_new_int(c->view->pid));
225
224 const char *app_id = view_get_app_id(c->view); 226 const char *app_id = view_get_app_id(c->view);
225 json_object_object_add(object, "app_id", 227 json_object_object_add(object, "app_id",
226 app_id ? json_object_new_string(app_id) : NULL); 228 app_id ? json_object_new_string(app_id) : NULL);
diff --git a/sway/main.c b/sway/main.c
index fb4f0d8c..3d7cd158 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -424,6 +424,7 @@ int main(int argc, char **argv) {
424 } 424 }
425 425
426 config->active = true; 426 config->active = true;
427 load_swaybars();
427 // Execute commands until there are none left 428 // Execute commands until there are none left
428 wlr_log(WLR_DEBUG, "Running deferred commands"); 429 wlr_log(WLR_DEBUG, "Running deferred commands");
429 while (config->cmd_queue->length) { 430 while (config->cmd_queue->length) {
diff --git a/sway/meson.build b/sway/meson.build
index 01c83a33..d67a4c64 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -35,6 +35,7 @@ sway_sources = files(
35 'commands/bind.c', 35 'commands/bind.c',
36 'commands/border.c', 36 'commands/border.c',
37 'commands/client.c', 37 'commands/client.c',
38 'commands/create_output.c',
38 'commands/default_border.c', 39 'commands/default_border.c',
39 'commands/default_floating_border.c', 40 'commands/default_floating_border.c',
40 'commands/default_orientation.c', 41 'commands/default_orientation.c',
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 1976ad51..06933dc4 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -109,8 +109,6 @@ void output_enable(struct sway_output *output, struct output_config *oc) {
109 109
110 wl_signal_emit(&root->events.new_node, &output->node); 110 wl_signal_emit(&root->events.new_node, &output->node);
111 111
112 load_swaybars();
113
114 arrange_layers(output); 112 arrange_layers(output);
115 arrange_root(); 113 arrange_root();
116} 114}
diff --git a/sway/tree/view.c b/sway/tree/view.c
index e4e1c161..f61f5c84 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -470,6 +470,7 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
470 wl_resource_get_client(view->surface->resource); 470 wl_resource_get_client(view->surface->resource);
471 wl_client_get_credentials(client, &pid, NULL, NULL); 471 wl_client_get_credentials(client, &pid, NULL, NULL);
472#endif 472#endif
473 view->pid = pid;
473 ws = root_workspace_for_pid(pid); 474 ws = root_workspace_for_pid(pid);
474 if (ws) { 475 if (ws) {
475 return ws; 476 return ws;
@@ -784,14 +785,9 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) {
784} 785}
785 786
786static char *escape_title(char *buffer) { 787static char *escape_title(char *buffer) {
787 int length = escape_markup_text(buffer, NULL, 0); 788 size_t length = escape_markup_text(buffer, NULL);
788 char *escaped_title = calloc(length + 1, sizeof(char)); 789 char *escaped_title = calloc(length + 1, sizeof(char));
789 int result = escape_markup_text(buffer, escaped_title, length); 790 escape_markup_text(buffer, escaped_title);
790 if (result != length) {
791 wlr_log(WLR_ERROR, "Could not escape title: %s", buffer);
792 free(escaped_title);
793 return buffer;
794 }
795 free(buffer); 791 free(buffer);
796 return escaped_title; 792 return escaped_title;
797} 793}
diff --git a/swaybar/bar.c b/swaybar/bar.c
index 3ae730f7..69069f40 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -48,8 +48,13 @@ static void swaybar_output_free(struct swaybar_output *output) {
48 return; 48 return;
49 } 49 }
50 wlr_log(WLR_DEBUG, "Removing output %s", output->name); 50 wlr_log(WLR_DEBUG, "Removing output %s", output->name);
51 zwlr_layer_surface_v1_destroy(output->layer_surface); 51 if (output->layer_surface != NULL) {
52 wl_surface_destroy(output->surface); 52 zwlr_layer_surface_v1_destroy(output->layer_surface);
53 }
54 if (output->surface != NULL) {
55 wl_surface_destroy(output->surface);
56 }
57 zxdg_output_v1_destroy(output->xdg_output);
53 wl_output_destroy(output->output); 58 wl_output_destroy(output->output);
54 destroy_buffer(&output->buffers[0]); 59 destroy_buffer(&output->buffers[0]);
55 destroy_buffer(&output->buffers[1]); 60 destroy_buffer(&output->buffers[1]);
@@ -283,6 +288,37 @@ const struct wl_seat_listener seat_listener = {
283 .name = seat_handle_name, 288 .name = seat_handle_name,
284}; 289};
285 290
291static void add_layer_surface(struct swaybar_output *output) {
292 if (output->surface != NULL) {
293 return;
294 }
295 struct swaybar *bar = output->bar;
296
297 output->surface = wl_compositor_create_surface(bar->compositor);
298 assert(output->surface);
299 output->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
300 bar->layer_shell, output->surface, output->output,
301 ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel");
302 assert(output->layer_surface);
303 zwlr_layer_surface_v1_add_listener(output->layer_surface,
304 &layer_surface_listener, output);
305 zwlr_layer_surface_v1_set_anchor(output->layer_surface,
306 bar->config->position);
307}
308
309static bool bar_uses_output(struct swaybar *bar, const char *name) {
310 if (bar->config->all_outputs) {
311 return true;
312 }
313 struct config_output *coutput;
314 wl_list_for_each(coutput, &bar->config->outputs, link) {
315 if (strcmp(coutput->name, name) == 0) {
316 return true;
317 }
318 }
319 return false;
320}
321
286static void output_geometry(void *data, struct wl_output *output, int32_t x, 322static void output_geometry(void *data, struct wl_output *output, int32_t x,
287 int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel, 323 int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel,
288 const char *make, const char *model, int32_t transform) { 324 const char *make, const char *model, int32_t transform) {
@@ -326,7 +362,22 @@ static void xdg_output_handle_logical_size(void *data,
326 362
327static void xdg_output_handle_done(void *data, 363static void xdg_output_handle_done(void *data,
328 struct zxdg_output_v1 *xdg_output) { 364 struct zxdg_output_v1 *xdg_output) {
329 // Who cares 365 struct swaybar_output *output = data;
366 struct swaybar *bar = output->bar;
367
368 assert(output->name != NULL);
369 if (!bar_uses_output(bar, output->name)) {
370 swaybar_output_free(output);
371 return;
372 }
373
374 if (wl_list_empty(&output->link)) {
375 wl_list_remove(&output->link);
376 wl_list_insert(&bar->outputs, &output->link);
377
378 add_layer_surface(output);
379 render_frame(bar, output);
380 }
330} 381}
331 382
332static void xdg_output_handle_name(void *data, 383static void xdg_output_handle_name(void *data,
@@ -349,17 +400,15 @@ struct zxdg_output_v1_listener xdg_output_listener = {
349 .description = xdg_output_handle_description, 400 .description = xdg_output_handle_description,
350}; 401};
351 402
352static bool bar_uses_output(struct swaybar *bar, const char *name) { 403static void add_xdg_output(struct swaybar_output *output) {
353 if (bar->config->all_outputs) { 404 if (output->xdg_output != NULL) {
354 return true; 405 return;
355 }
356 struct config_output *coutput;
357 wl_list_for_each(coutput, &bar->config->outputs, link) {
358 if (strcmp(coutput->name, name) == 0) {
359 return true;
360 }
361 } 406 }
362 return false; 407 assert(output->bar->xdg_output_manager != NULL);
408 output->xdg_output = zxdg_output_manager_v1_get_xdg_output(
409 output->bar->xdg_output_manager, output->output);
410 zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener,
411 output);
363} 412}
364 413
365static void handle_global(void *data, struct wl_registry *registry, 414static void handle_global(void *data, struct wl_registry *registry,
@@ -386,7 +435,10 @@ static void handle_global(void *data, struct wl_registry *registry,
386 output->wl_name = name; 435 output->wl_name = name;
387 wl_list_init(&output->workspaces); 436 wl_list_init(&output->workspaces);
388 wl_list_init(&output->hotspots); 437 wl_list_init(&output->hotspots);
389 wl_list_insert(&bar->outputs, &output->link); 438 wl_list_init(&output->link);
439 if (bar->xdg_output_manager != NULL) {
440 add_xdg_output(output);
441 }
390 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { 442 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
391 bar->layer_shell = wl_registry_bind( 443 bar->layer_shell = wl_registry_bind(
392 registry, name, &zwlr_layer_shell_v1_interface, 1); 444 registry, name, &zwlr_layer_shell_v1_interface, 1);
@@ -416,7 +468,9 @@ static const struct wl_registry_listener registry_listener = {
416static void render_all_frames(struct swaybar *bar) { 468static void render_all_frames(struct swaybar *bar) {
417 struct swaybar_output *output; 469 struct swaybar_output *output;
418 wl_list_for_each(output, &bar->outputs, link) { 470 wl_list_for_each(output, &bar->outputs, link) {
419 render_frame(bar, output); 471 if (output->surface != NULL) {
472 render_frame(bar, output);
473 }
420 } 474 }
421} 475}
422 476
@@ -443,23 +497,10 @@ void bar_setup(struct swaybar *bar,
443 497
444 struct swaybar_output *output; 498 struct swaybar_output *output;
445 wl_list_for_each(output, &bar->outputs, link) { 499 wl_list_for_each(output, &bar->outputs, link) {
446 output->xdg_output = zxdg_output_manager_v1_get_xdg_output( 500 add_xdg_output(output);
447 bar->xdg_output_manager, output->output);
448 zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener,
449 output);
450 } 501 }
451 wl_display_roundtrip(bar->display); 502 wl_display_roundtrip(bar->display);
452 503
453 struct swaybar_output *output_tmp;
454 wl_list_for_each_safe(output, output_tmp, &bar->outputs, link) {
455 if (!bar_uses_output(bar, output->name)) {
456 zxdg_output_v1_destroy(output->xdg_output);
457 wl_output_destroy(output->output);
458 wl_list_remove(&output->link);
459 free(output);
460 }
461 }
462
463 struct swaybar_pointer *pointer = &bar->pointer; 504 struct swaybar_pointer *pointer = &bar->pointer;
464 505
465 int max_scale = 1; 506 int max_scale = 1;
@@ -479,18 +520,6 @@ void bar_setup(struct swaybar *bar,
479 pointer->cursor_surface = wl_compositor_create_surface(bar->compositor); 520 pointer->cursor_surface = wl_compositor_create_surface(bar->compositor);
480 assert(pointer->cursor_surface); 521 assert(pointer->cursor_surface);
481 522
482 wl_list_for_each(output, &bar->outputs, link) {
483 output->surface = wl_compositor_create_surface(bar->compositor);
484 assert(output->surface);
485 output->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
486 bar->layer_shell, output->surface, output->output,
487 ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel");
488 assert(output->layer_surface);
489 zwlr_layer_surface_v1_add_listener(output->layer_surface,
490 &layer_surface_listener, output);
491 zwlr_layer_surface_v1_set_anchor(output->layer_surface,
492 bar->config->position);
493 }
494 ipc_get_workspaces(bar); 523 ipc_get_workspaces(bar);
495 render_all_frames(bar); 524 render_all_frames(bar);
496} 525}
@@ -529,6 +558,7 @@ void bar_run(struct swaybar *bar) {
529 } 558 }
530 while (1) { 559 while (1) {
531 event_loop_poll(); 560 event_loop_poll();
561 wl_display_flush(bar->display);
532 } 562 }
533} 563}
534 564
diff --git a/swaybar/event_loop.c b/swaybar/event_loop.c
index bc4053be..686b9962 100644
--- a/swaybar/event_loop.c
+++ b/swaybar/event_loop.c
@@ -105,7 +105,7 @@ bool remove_timer(timer_t timer) {
105 return false; 105 return false;
106} 106}
107 107
108void event_loop_poll() { 108void event_loop_poll(void) {
109 poll(event_loop.fds.items, event_loop.fds.length, -1); 109 poll(event_loop.fds.items, event_loop.fds.length, -1);
110 110
111 for (int i = 0; i < event_loop.fds.length; ++i) { 111 for (int i = 0; i < event_loop.fds.length; ++i) {
@@ -146,7 +146,7 @@ void event_loop_poll() {
146 } 146 }
147} 147}
148 148
149void init_event_loop() { 149void init_event_loop(void) {
150 event_loop.fds.length = 0; 150 event_loop.fds.length = 0;
151 event_loop.fds.capacity = 10; 151 event_loop.fds.capacity = 10;
152 event_loop.fds.items = malloc( 152 event_loop.fds.items = malloc(
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c
index 0becae5d..325aa61a 100644
--- a/swaybar/i3bar.c
+++ b/swaybar/i3bar.c
@@ -1,6 +1,7 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <json-c/json.h> 2#include <json-c/json.h>
3#include <linux/input-event-codes.h> 3#include <linux/input-event-codes.h>
4#include <ctype.h>
4#include <stdlib.h> 5#include <stdlib.h>
5#include <string.h> 6#include <string.h>
6#include <unistd.h> 7#include <unistd.h>
@@ -24,24 +25,19 @@ void i3bar_block_unref(struct i3bar_block *block) {
24 } 25 }
25} 26}
26 27
27static bool i3bar_parse_json(struct status_line *status, const char *text) { 28static void i3bar_parse_json(struct status_line *status,
29 struct json_object *json_array) {
28 struct i3bar_block *block, *tmp; 30 struct i3bar_block *block, *tmp;
29 wl_list_for_each_safe(block, tmp, &status->blocks, link) { 31 wl_list_for_each_safe(block, tmp, &status->blocks, link) {
30 wl_list_remove(&block->link); 32 wl_list_remove(&block->link);
31 i3bar_block_unref(block); 33 i3bar_block_unref(block);
32 } 34 }
33 json_object *results = json_tokener_parse(text); 35 for (size_t i = 0; i < json_object_array_length(json_array); ++i) {
34 if (!results) {
35 status_error(status, "[failed to parse i3bar json]");
36 return false;
37 }
38 wlr_log(WLR_DEBUG, "Got i3bar json: '%s'", text);
39 for (size_t i = 0; i < json_object_array_length(results); ++i) {
40 json_object *full_text, *short_text, *color, *min_width, *align, *urgent; 36 json_object *full_text, *short_text, *color, *min_width, *align, *urgent;
41 json_object *name, *instance, *separator, *separator_block_width; 37 json_object *name, *instance, *separator, *separator_block_width;
42 json_object *background, *border, *border_top, *border_bottom; 38 json_object *background, *border, *border_top, *border_bottom;
43 json_object *border_left, *border_right, *markup; 39 json_object *border_left, *border_right, *markup;
44 json_object *json = json_object_array_get_idx(results, i); 40 json_object *json = json_object_array_get_idx(json_array, i);
45 if (!json) { 41 if (!json) {
46 continue; 42 continue;
47 } 43 }
@@ -110,96 +106,160 @@ static bool i3bar_parse_json(struct status_line *status, const char *text) {
110 json_object_get_int(border_right) : 1; 106 json_object_get_int(border_right) : 1;
111 wl_list_insert(&status->blocks, &block->link); 107 wl_list_insert(&status->blocks, &block->link);
112 } 108 }
113 json_object_put(results);
114 return true;
115} 109}
116 110
117bool i3bar_handle_readable(struct status_line *status) { 111bool i3bar_handle_readable(struct status_line *status) {
118 struct i3bar_protocol_state *state = &status->i3bar_state; 112 while (!status->started) { // look for opening bracket
119 113 for (size_t c = 0; c < status->buffer_index; ++c) {
120 char *cur = &state->buffer[state->buffer_index]; 114 if (status->buffer[c] == '[') {
121 ssize_t n = read(status->read_fd, cur, 115 status->started = true;
122 state->buffer_size - state->buffer_index); 116 status->buffer_index -= ++c;
123 if (n == -1) { 117 memmove(status->buffer, &status->buffer[c], status->buffer_index);
124 status_error(status, "[failed to read from status command]"); 118 break;
125 return false; 119 } else if (!isspace(status->buffer[c])) {
126 } 120 wlr_log(WLR_DEBUG, "Invalid i3bar json: expected '[' but encountered '%c'",
121 status->buffer[c]);
122 status_error(status, "[invalid i3bar json]");
123 return true;
124 }
125 }
126 if (status->started) {
127 break;
128 }
127 129
128 if (n == (ssize_t)(state->buffer_size - state->buffer_index)) { 130 errno = 0;
129 state->buffer_size = state->buffer_size * 2; 131 ssize_t read_bytes = read(status->read_fd, status->buffer, status->buffer_size);
130 char *new_buffer = realloc(state->buffer, state->buffer_size); 132 if (read_bytes > -1) {
131 if (!new_buffer) { 133 status->buffer_index = read_bytes;
132 free(state->buffer); 134 } else if (errno == EAGAIN) {
133 status_error(status, "[failed to allocate buffer]"); 135 return false;
136 } else {
137 status_error(status, "[error reading from status command]");
134 return true; 138 return true;
135 } 139 }
136 state->current_node += new_buffer - state->buffer;
137 cur += new_buffer - state->buffer;
138 state->buffer = new_buffer;
139 } 140 }
140 141
141 cur[n] = '\0'; 142 struct json_object *last_object = NULL;
142 bool redraw = false; 143 struct json_object *test_object;
143 while (*cur) { 144 size_t buffer_pos = 0;
144 if (state->nodes[state->depth] == JSON_NODE_STRING) { 145 while (true) {
145 if (!state->escape && *cur == '"') { 146 // since the incoming stream is an infinite array
146 --state->depth; 147 // parsing is split into two parts
148 // first, attempt to parse the current object, reading more if the
149 // parser indicates that the current object is incomplete, and failing
150 // if the parser fails
151 // second, look for separating comma, ignoring whitespace, failing if
152 // any other characters are encountered
153 if (status->expecting_comma) {
154 for (; buffer_pos < status->buffer_index; ++buffer_pos) {
155 if (status->buffer[buffer_pos] == ',') {
156 status->expecting_comma = false;
157 ++buffer_pos;
158 break;
159 } else if (!isspace(status->buffer[buffer_pos])) {
160 wlr_log(WLR_DEBUG, "Invalid i3bar json: expected ',' but encountered '%c'",
161 status->buffer[buffer_pos]);
162 status_error(status, "[invalid i3bar json]");
163 return true;
164 }
165 }
166 if (buffer_pos < status->buffer_index) {
167 continue; // look for new object without reading more input
147 } 168 }
148 state->escape = !state->escape && *cur == '\\'; 169 buffer_pos = status->buffer_index = 0;
149 } else { 170 } else {
150 switch (*cur) { 171 test_object = json_tokener_parse_ex(status->tokener,
151 case '[': 172 &status->buffer[buffer_pos], status->buffer_index - buffer_pos);
152 ++state->depth; 173 enum json_tokener_error err = json_tokener_get_error(status->tokener);
153 if (state->depth > 174 if (err == json_tokener_success) {
154 sizeof(state->nodes) / sizeof(state->nodes[0])) { 175 if (json_object_get_type(test_object) == json_type_array) {
155 status_error(status, "[i3bar json too deep]"); 176 if (last_object) {
156 return false; 177 json_object_put(last_object);
157 } 178 }
158 state->nodes[state->depth] = JSON_NODE_ARRAY; 179 last_object = test_object;
159 if (state->depth == 1) { 180 } else {
160 state->current_node = cur; 181 json_object_put(test_object);
161 } 182 }
162 break; 183
163 case ']': 184 // in order to print the json for debugging purposes
164 if (state->nodes[state->depth] != JSON_NODE_ARRAY) { 185 // the last character is temporarily replaced with a null character
165 status_error(status, "[failed to parse i3bar json]"); 186 // (the last character is used in case the buffer is full)
166 return false; 187 char *last_char_pos =
188 &status->buffer[buffer_pos + status->tokener->char_offset - 1];
189 char last_char = *last_char_pos;
190 while (isspace(last_char)) {
191 last_char = *--last_char_pos;
167 } 192 }
168 --state->depth; 193 *last_char_pos = '\0';
169 if (state->depth == 0) { 194 size_t offset = strspn(&status->buffer[buffer_pos], " \f\n\r\t\v");
170 // cur[1] is valid since cur[0] != '\0' 195 wlr_log(WLR_DEBUG, "Received i3bar json: '%s%c'",
171 char p = cur[1]; 196 &status->buffer[buffer_pos + offset], last_char);
172 cur[1] = '\0'; 197 *last_char_pos = last_char;
173 redraw = i3bar_parse_json( 198
174 status, state->current_node) || redraw; 199 buffer_pos += status->tokener->char_offset;
175 cur[1] = p; 200 status->expecting_comma = true;
176 memmove(state->buffer, cur, 201
177 state->buffer_size - (cur - state->buffer)); 202 if (buffer_pos < status->buffer_index) {
178 cur = state->buffer; 203 continue; // look for comma without reading more input
179 state->current_node = cur + 1;
180 } 204 }
181 break; 205 buffer_pos = status->buffer_index = 0;
182 case '"': 206 } else if (err == json_tokener_continue) {
183 ++state->depth; 207 json_tokener_reset(status->tokener);
184 if (state->depth > 208 if (status->buffer_index < status->buffer_size) {
185 sizeof(state->nodes) / sizeof(state->nodes[0])) { 209 // move the object to the start of the buffer
186 status_error(status, "[i3bar json too deep]"); 210 status->buffer_index -= buffer_pos;
187 return false; 211 memmove(status->buffer, &status->buffer[buffer_pos],
212 status->buffer_index);
213 buffer_pos = 0;
214 } else {
215 // expand buffer
216 status->buffer_size *= 2;
217 char *new_buffer = realloc(status->buffer, status->buffer_size);
218 if (new_buffer) {
219 status->buffer = new_buffer;
220 } else {
221 free(status->buffer);
222 status_error(status, "[failed to allocate buffer]");
223 return true;
224 }
188 } 225 }
189 state->nodes[state->depth] = JSON_NODE_STRING; 226 } else {
190 break; 227 char last_char = status->buffer[status->buffer_index - 1];
228 status->buffer[status->buffer_index - 1] = '\0';
229 wlr_log(WLR_DEBUG, "Failed to parse i3bar json - %s: '%s%c'",
230 json_tokener_error_desc(err), &status->buffer[buffer_pos], last_char);
231 status_error(status, "[failed to parse i3bar json]");
232 return true;
191 } 233 }
192 } 234 }
193 ++cur; 235
236 errno = 0;
237 ssize_t read_bytes = read(status->read_fd, &status->buffer[status->buffer_index],
238 status->buffer_size - status->buffer_index);
239 if (read_bytes > -1) {
240 status->buffer_index += read_bytes;
241 } else if (errno == EAGAIN) {
242 break;
243 } else {
244 status_error(status, "[error reading from status command]");
245 return true;
246 }
247 }
248
249 if (last_object) {
250 wlr_log(WLR_DEBUG, "Rendering last received json");
251 i3bar_parse_json(status, last_object);
252 json_object_put(last_object);
253 return true;
254 } else {
255 return false;
194 } 256 }
195 state->buffer_index = cur - state->buffer;
196 return redraw;
197} 257}
198 258
199enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, 259enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
200 struct i3bar_block *block, int x, int y, enum x11_button button) { 260 struct i3bar_block *block, int x, int y, enum x11_button button) {
201 wlr_log(WLR_DEBUG, "block %s clicked", block->name ? block->name : "(nil)"); 261 wlr_log(WLR_DEBUG, "block %s clicked", block->name ? block->name : "(nil)");
202 if (!block->name || !status->i3bar_state.click_events) { 262 if (!block->name || !status->click_events) {
203 return HOTSPOT_PROCESS; 263 return HOTSPOT_PROCESS;
204 } 264 }
205 265
@@ -214,7 +274,7 @@ enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
214 json_object_object_add(event_json, "button", json_object_new_int(button)); 274 json_object_object_add(event_json, "button", json_object_new_int(button));
215 json_object_object_add(event_json, "x", json_object_new_int(x)); 275 json_object_object_add(event_json, "x", json_object_new_int(x));
216 json_object_object_add(event_json, "y", json_object_new_int(y)); 276 json_object_object_add(event_json, "y", json_object_new_int(y));
217 if (dprintf(status->write_fd, "%s\n", 277 if (dprintf(status->write_fd, "%s,\n",
218 json_object_to_json_string(event_json)) < 0) { 278 json_object_to_json_string(event_json)) < 0) {
219 status_error(status, "[failed to write click event]"); 279 status_error(status, "[failed to write click event]");
220 } 280 }
diff --git a/swaybar/render.c b/swaybar/render.c
index 2d848bfa..26db80cb 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -1,4 +1,5 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
2#include <limits.h> 3#include <limits.h>
3#include <stdlib.h> 4#include <stdlib.h>
4#include <stdint.h> 5#include <stdint.h>
@@ -120,14 +121,14 @@ static void i3bar_block_unref_callback(void *data) {
120} 121}
121 122
122static uint32_t render_status_block(cairo_t *cairo, 123static uint32_t render_status_block(cairo_t *cairo,
123 struct swaybar_config *config, struct swaybar_output *output, 124 struct swaybar_output *output, struct i3bar_block *block, double *x,
124 struct i3bar_block *block, double *x,
125 uint32_t surface_height, bool focused, bool edge) { 125 uint32_t surface_height, bool focused, bool edge) {
126 if (!block->full_text || !*block->full_text) { 126 if (!block->full_text || !*block->full_text) {
127 return 0; 127 return 0;
128 } 128 }
129 129
130 uint32_t height = surface_height * output->scale; 130 uint32_t height = surface_height * output->scale;
131 struct swaybar_config *config = output->bar->config;
131 132
132 int text_width, text_height; 133 int text_width, text_height;
133 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 134 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
@@ -177,16 +178,18 @@ static uint32_t render_status_block(cairo_t *cairo,
177 *x -= margin; 178 *x -= margin;
178 } 179 }
179 180
180 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 181 if (output->bar->status->click_events) {
181 hotspot->x = *x; 182 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
182 hotspot->y = 0; 183 hotspot->x = *x;
183 hotspot->width = width; 184 hotspot->y = 0;
184 hotspot->height = height; 185 hotspot->width = width;
185 hotspot->callback = block_hotspot_callback; 186 hotspot->height = height;
186 hotspot->destroy = i3bar_block_unref_callback; 187 hotspot->callback = block_hotspot_callback;
187 hotspot->data = block; 188 hotspot->destroy = i3bar_block_unref_callback;
188 block->ref_count++; 189 hotspot->data = block;
189 wl_list_insert(&output->hotspots, &hotspot->link); 190 block->ref_count++;
191 wl_list_insert(&output->hotspots, &hotspot->link);
192 }
190 193
191 double pos = *x; 194 double pos = *x;
192 if (block->background) { 195 if (block->background) {
@@ -268,7 +271,7 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo,
268 bool edge = true; 271 bool edge = true;
269 struct i3bar_block *block; 272 struct i3bar_block *block;
270 wl_list_for_each(block, &status->blocks, link) { 273 wl_list_for_each(block, &status->blocks, link) {
271 uint32_t h = render_status_block(cairo, config, output, 274 uint32_t h = render_status_block(cairo, output,
272 block, x, surface_height, focused, edge); 275 block, x, surface_height, focused, edge);
273 max_height = h > max_height ? h : max_height; 276 max_height = h > max_height ? h : max_height;
274 edge = false; 277 edge = false;
@@ -478,6 +481,8 @@ static uint32_t render_to_cairo(cairo_t *cairo,
478} 481}
479 482
480void render_frame(struct swaybar *bar, struct swaybar_output *output) { 483void render_frame(struct swaybar *bar, struct swaybar_output *output) {
484 assert(output->surface != NULL);
485
481 struct swaybar_hotspot *hotspot, *tmp; 486 struct swaybar_hotspot *hotspot, *tmp;
482 wl_list_for_each_safe(hotspot, tmp, &output->hotspots, link) { 487 wl_list_for_each_safe(hotspot, tmp, &output->hotspots, link) {
483 if (hotspot->destroy) { 488 if (hotspot->destroy) {
@@ -505,7 +510,6 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) {
505 // TODO: this could infinite loop if the compositor assigns us a 510 // TODO: this could infinite loop if the compositor assigns us a
506 // different height than what we asked for 511 // different height than what we asked for
507 wl_surface_commit(output->surface); 512 wl_surface_commit(output->surface);
508 wl_display_roundtrip(bar->display);
509 } else if (height > 0) { 513 } else if (height > 0) {
510 // Replay recording into shm and send it off 514 // Replay recording into shm and send it off
511 output->current_buffer = get_next_buffer(bar->shm, 515 output->current_buffer = get_next_buffer(bar->shm,
@@ -531,7 +535,6 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) {
531 wl_surface_damage(output->surface, 0, 0, 535 wl_surface_damage(output->surface, 0, 0,
532 output->width, output->height); 536 output->width, output->height);
533 wl_surface_commit(output->surface); 537 wl_surface_commit(output->surface);
534 wl_display_roundtrip(bar->display);
535 } 538 }
536 cairo_surface_destroy(recorder); 539 cairo_surface_destroy(recorder);
537 cairo_destroy(cairo); 540 cairo_destroy(cairo);
diff --git a/swaybar/status_line.c b/swaybar/status_line.c
index 3ba990bd..48b43248 100644
--- a/swaybar/status_line.c
+++ b/swaybar/status_line.c
@@ -7,86 +7,106 @@
7#include <unistd.h> 7#include <unistd.h>
8#include <wlr/util/log.h> 8#include <wlr/util/log.h>
9#include "swaybar/config.h" 9#include "swaybar/config.h"
10#include "swaybar/event_loop.h"
10#include "swaybar/status_line.h" 11#include "swaybar/status_line.h"
11#include "readline.h" 12#include "readline.h"
12 13
14static void status_line_close_fds(struct status_line *status) {
15 if (status->read_fd != -1) {
16 remove_event(status->read_fd);
17 close(status->read_fd);
18 status->read_fd = -1;
19 }
20 if (status->write_fd != -1) {
21 close(status->write_fd);
22 status->write_fd = -1;
23 }
24}
25
13void status_error(struct status_line *status, const char *text) { 26void status_error(struct status_line *status, const char *text) {
14 close(status->read_fd); 27 status_line_close_fds(status);
15 close(status->write_fd);
16 status->protocol = PROTOCOL_ERROR; 28 status->protocol = PROTOCOL_ERROR;
17 status->text = text; 29 status->text = text;
18} 30}
19 31
20bool status_handle_readable(struct status_line *status) { 32bool status_handle_readable(struct status_line *status) {
21 char *line; 33 ssize_t read_bytes = 1;
22 switch (status->protocol) { 34 switch (status->protocol) {
23 case PROTOCOL_ERROR:
24 return false;
25 case PROTOCOL_I3BAR:
26 if (i3bar_handle_readable(status) > 0) {
27 return true;
28 }
29 break;
30 case PROTOCOL_TEXT:
31 line = read_line_buffer(status->read,
32 status->text_state.buffer, status->text_state.buffer_size);
33 if (!line) {
34 status_error(status, "[error reading from status command]");
35 } else {
36 status->text = line;
37 }
38 return true;
39 case PROTOCOL_UNDEF: 35 case PROTOCOL_UNDEF:
40 line = read_line_buffer(status->read, 36 errno = 0;
41 status->text_state.buffer, status->text_state.buffer_size); 37 read_bytes = getline(&status->buffer,
42 if (!line) { 38 &status->buffer_size, status->read);
39 if (errno == EAGAIN) {
40 clearerr(status->read);
41 } else if (errno) {
43 status_error(status, "[error reading from status command]"); 42 status_error(status, "[error reading from status command]");
44 return false; 43 return true;
45 } 44 }
46 if (line[0] == '{') { 45
47 json_object *proto = json_tokener_parse(line); 46 // the header must be sent completely the first time round
48 if (proto) { 47 json_object *header, *version;
49 json_object *version; 48 if (status->buffer[read_bytes - 1] == '\n'
50 if (json_object_object_get_ex(proto, "version", &version) 49 && (header = json_tokener_parse(status->buffer))
51 && json_object_get_int(version) == 1) { 50 && json_object_object_get_ex(header, "version", &version)
52 wlr_log(WLR_DEBUG, "Switched to i3bar protocol."); 51 && json_object_get_int(version) == 1) {
53 status->protocol = PROTOCOL_I3BAR; 52 wlr_log(WLR_DEBUG, "Using i3bar protocol.");
54 } 53 status->protocol = PROTOCOL_I3BAR;
55 json_object *click_events; 54
56 if (json_object_object_get_ex( 55 json_object *click_events;
57 proto, "click_events", &click_events) 56 if (json_object_object_get_ex(header, "click_events", &click_events)
58 && json_object_get_boolean(click_events)) { 57 && json_object_get_boolean(click_events)) {
59 wlr_log(WLR_DEBUG, "Enabled click events."); 58 wlr_log(WLR_DEBUG, "Enabling click events.");
60 status->i3bar_state.click_events = true; 59 status->click_events = true;
61 const char *events_array = "[\n"; 60 if (write(status->write_fd, "[\n", 2) != 2) {
62 ssize_t len = strlen(events_array); 61 status_error(status, "[failed to write to status command]");
63 if (write(status->write_fd, events_array, len) != len) { 62 json_object_put(header);
64 status_error(status, 63 return true;
65 "[failed to write to status command]");
66 }
67 } 64 }
68 json_object_put(proto);
69 } 65 }
66 json_object_put(header);
70 67
71 status->protocol = PROTOCOL_I3BAR;
72 free(status->text_state.buffer);
73 wl_list_init(&status->blocks); 68 wl_list_init(&status->blocks);
74 status->i3bar_state.buffer_size = 4096; 69 status->tokener = json_tokener_new();
75 status->i3bar_state.buffer = 70 read_bytes = getdelim(&status->buffer, &status->buffer_size, EOF, status->read);
76 malloc(status->i3bar_state.buffer_size); 71 if (read_bytes > 0) {
77 } else { 72 status->buffer_index = read_bytes;
78 status->protocol = PROTOCOL_TEXT; 73 return i3bar_handle_readable(status);
79 status->text = line; 74 } else {
75 return false;
76 }
77 }
78
79 wlr_log(WLR_DEBUG, "Using text protocol.");
80 status->protocol = PROTOCOL_TEXT;
81 status->text = status->buffer;
82 // intentional fall-through
83 case PROTOCOL_TEXT:
84 errno = 0;
85 while (true) {
86 if (status->buffer[read_bytes - 1] == '\n') {
87 status->buffer[read_bytes - 1] = '\0';
88 }
89 read_bytes = getline(&status->buffer,
90 &status->buffer_size, status->read);
91 if (errno == EAGAIN) {
92 clearerr(status->read);
93 return true;
94 } else if (errno) {
95 status_error(status, "[error reading from status command]");
96 return true;
97 }
80 } 98 }
81 return true; 99 case PROTOCOL_I3BAR:
100 return i3bar_handle_readable(status);
101 default:
102 return false;
82 } 103 }
83 return false;
84} 104}
85 105
86struct status_line *status_line_init(char *cmd) { 106struct status_line *status_line_init(char *cmd) {
87 struct status_line *status = calloc(1, sizeof(struct status_line)); 107 struct status_line *status = calloc(1, sizeof(struct status_line));
88 status->text_state.buffer_size = 8192; 108 status->buffer_size = 8192;
89 status->text_state.buffer = malloc(status->text_state.buffer_size); 109 status->buffer = malloc(status->buffer_size);
90 110
91 int pipe_read_fd[2]; 111 int pipe_read_fd[2];
92 int pipe_write_fd[2]; 112 int pipe_write_fd[2];
@@ -123,20 +143,16 @@ struct status_line *status_line_init(char *cmd) {
123} 143}
124 144
125void status_line_free(struct status_line *status) { 145void status_line_free(struct status_line *status) {
126 close(status->read_fd); 146 status_line_close_fds(status);
127 close(status->write_fd);
128 kill(status->pid, SIGTERM); 147 kill(status->pid, SIGTERM);
129 switch (status->protocol) { 148 if (status->protocol == PROTOCOL_I3BAR) {
130 case PROTOCOL_I3BAR:;
131 struct i3bar_block *block, *tmp; 149 struct i3bar_block *block, *tmp;
132 wl_list_for_each_safe(block, tmp, &status->blocks, link) { 150 wl_list_for_each_safe(block, tmp, &status->blocks, link) {
151 wl_list_remove(&block->link);
133 i3bar_block_unref(block); 152 i3bar_block_unref(block);
134 } 153 }
135 free(status->i3bar_state.buffer); 154 json_tokener_free(status->tokener);
136 break;
137 default:
138 free(status->text_state.buffer);
139 break;
140 } 155 }
156 free(status->buffer);
141 free(status); 157 free(status);
142} 158}