From 2eaef802061836dd83bbf753174fcb8488b2d8cd Mon Sep 17 00:00:00 2001
From: Ian Fan <ianfan0@gmail.com>
Date: Wed, 12 Sep 2018 08:28:28 +0100
Subject: 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
---
 swaybar/i3bar.c       | 13 ++++++++++---
 swaybar/render.c      |  7 ++++++-
 swaybar/status_line.c |  2 +-
 3 files changed, 17 insertions(+), 5 deletions(-)

(limited to 'swaybar')

diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c
index ae37eeb9..1345ee9b 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 7303e70f..2d848bfa 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 bc47580b..3ba990bd 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;
-- 
cgit v1.2.3