From 2eaef802061836dd83bbf753174fcb8488b2d8cd Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Wed, 12 Sep 2018 08:28:28 +0100 Subject: [PATCH] i3bar: count references to blocks This prevents blocks from being destroyed before their hotspots are destroyed, in case it is used for a pending click event that fires between the bar receiving a new status, which destroys the block, and the bar rendering the new status, which destroys the hotspot; this problem can be easily produced by scrolling on a block that immediately causes a new status to be sent, with multiple outputs --- include/swaybar/status_line.h | 3 ++- swaybar/i3bar.c | 13 ++++++++++--- swaybar/render.c | 7 ++++++- swaybar/status_line.c | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/include/swaybar/status_line.h b/include/swaybar/status_line.h index de9b98d79..150267cd2 100644 --- a/include/swaybar/status_line.h +++ b/include/swaybar/status_line.h @@ -36,6 +36,7 @@ struct i3bar_protocol_state { struct i3bar_block { struct wl_list link; + int ref_count; char *full_text, *short_text, *align; bool urgent; uint32_t *color; @@ -73,7 +74,7 @@ void status_line_free(struct status_line *status); bool i3bar_handle_readable(struct status_line *status); enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, struct i3bar_block *block, int x, int y, enum x11_button button); -void i3bar_block_free(struct i3bar_block *block); +void i3bar_block_unref(struct i3bar_block *block); enum x11_button wl_button_to_x11_button(uint32_t button); enum x11_button wl_axis_to_x11_button(uint32_t axis, wl_fixed_t value); diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c index ae37eeb9f..1345ee9b4 100644 --- a/swaybar/i3bar.c +++ b/swaybar/i3bar.c @@ -8,11 +8,10 @@ #include "swaybar/config.h" #include "swaybar/status_line.h" -void i3bar_block_free(struct i3bar_block *block) { +static void i3bar_block_free(struct i3bar_block *block) { if (!block) { return; } - wl_list_remove(&block->link); free(block->full_text); free(block->short_text); free(block->align); @@ -22,10 +21,17 @@ void i3bar_block_free(struct i3bar_block *block) { free(block); } +void i3bar_block_unref(struct i3bar_block *block) { + if (--block->ref_count == 0) { + i3bar_block_free(block); + } +} + static bool i3bar_parse_json(struct status_line *status, const char *text) { struct i3bar_block *block, *tmp; wl_list_for_each_safe(block, tmp, &status->blocks, link) { - i3bar_block_free(block); + wl_list_remove(&block->link); + i3bar_block_unref(block); } json_object *results = json_tokener_parse(text); if (!results) { @@ -61,6 +67,7 @@ static bool i3bar_parse_json(struct status_line *status, const char *text) { json_object_object_get_ex(json, "border_right", &border_right); struct i3bar_block *block = calloc(1, sizeof(struct i3bar_block)); + block->ref_count = 1; block->full_text = full_text ? strdup(json_object_get_string(full_text)) : NULL; block->short_text = short_text ? diff --git a/swaybar/render.c b/swaybar/render.c index 7303e70ff..2d848bfa3 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -115,6 +115,10 @@ static enum hotspot_event_handling block_hotspot_callback(struct swaybar_output return i3bar_block_send_click(status, block, x, y, button); } +static void i3bar_block_unref_callback(void *data) { + i3bar_block_unref(data); +} + static uint32_t render_status_block(cairo_t *cairo, struct swaybar_config *config, struct swaybar_output *output, struct i3bar_block *block, double *x, @@ -179,8 +183,9 @@ static uint32_t render_status_block(cairo_t *cairo, hotspot->width = width; hotspot->height = height; hotspot->callback = block_hotspot_callback; - hotspot->destroy = NULL; + hotspot->destroy = i3bar_block_unref_callback; hotspot->data = block; + block->ref_count++; wl_list_insert(&output->hotspots, &hotspot->link); double pos = *x; diff --git a/swaybar/status_line.c b/swaybar/status_line.c index bc47580b9..3ba990bdd 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c @@ -130,7 +130,7 @@ void status_line_free(struct status_line *status) { case PROTOCOL_I3BAR:; struct i3bar_block *block, *tmp; wl_list_for_each_safe(block, tmp, &status->blocks, link) { - i3bar_block_free(block); + i3bar_block_unref(block); } free(status->i3bar_state.buffer); break;