i3 - improved tiling WM


i3bar: Reduce noise to tray clients

Patch status: needinfo

Patch by Tony Crisci

Long description:

Try not to send messages to tray clients which could raise errors on the
client side, causing some clients to die.

BadWindow errors can be caused by sending tray clients messages pointing
to false manager selections and by race conditions related to setting
the tray manager selection multiple times per loading of i3.

Expands support for running multiple bar instances.

Fixes an issue which may cause tray clients to appear on a bar which
does not have the tray manager selection.

To apply this patch, use:
curl http://cr.i3wm.org/patch/302/raw.patch | git am

b/i3bar/src/xcb.c

24
@@ -53,6 +53,9 @@ xcb_window_t     xcb_root;
25
 static xcb_window_t selwin = XCB_NONE;
26
 static xcb_intern_atom_reply_t *tray_reply = NULL;
27
 
28
+/* set to true when selwin has the tray manager selection */
29
+static bool has_tray_selection= false;
30
+
31
 /* This is needed for integration with libi3 */
32
 xcb_connection_t *conn;
33
 
34
@@ -1078,6 +1081,9 @@ void init_xcb_late(char *fontname) {
35
  *
36
  */
37
 static void send_tray_clientmessage(void) {
38
+    if (!has_tray_selection)
39
+        return;
40
+
41
     uint8_t buffer[32] = { 0 };
42
     xcb_client_message_event_t *ev = (xcb_client_message_event_t*)buffer;
43
 
44
@@ -1104,13 +1110,41 @@ static void send_tray_clientmessage(void) {
45
  *
46
  */
47
 void init_tray(void) {
48
-    DLOG("Initializing system tray functionality\n");
49
+    DLOG("Starting tray initialization.\n");
50
     /* request the tray manager atom for the X11 display we are running on */
51
     char atomname[strlen("_NET_SYSTEM_TRAY_S") + 11];
52
     snprintf(atomname, strlen("_NET_SYSTEM_TRAY_S") + 11, "_NET_SYSTEM_TRAY_S%d", screen);
53
     xcb_intern_atom_cookie_t tray_cookie;
54
-    if (tray_reply == NULL)
55
+
56
+    if (tray_reply == NULL) {
57
         tray_cookie = xcb_intern_atom(xcb_connection, 0, strlen(atomname), atomname);
58
+        tray_reply = xcb_intern_atom_reply(xcb_connection, tray_cookie, NULL);
59
+
60
+        if (!tray_reply) {
61
+            ELOG("Could not get atom %s\n", atomname);
62
+            exit(EXIT_FAILURE);
63
+        }
64
+    }
65
+
66
+    /* be nice and back off if anybody owns the tray already */
67
+    xcb_get_selection_owner_cookie_t current_sel_cookie;
68
+    xcb_get_selection_owner_reply_t *current_sel_reply;
69
+
70
+    current_sel_cookie = xcb_get_selection_owner(xcb_connection, tray_reply->atom);
71
+    current_sel_reply = xcb_get_selection_owner_reply(xcb_connection, current_sel_cookie, NULL);
72
+
73
+    if (current_sel_reply == NULL) {
74
+        DLOG("Could not get selection owner for %s\n", atomname);
75
+        exit(EXIT_FAILURE);
76
+    }
77
+
78
+    if (current_sel_reply->owner) {
79
+        DLOG("Tray owned by 0x%08x, not configuring tray.\n", current_sel_reply->owner);
80
+        free(current_sel_reply);
81
+        return;
82
+    }
83
+
84
+    free(current_sel_reply);
85
 
86
     /* tray support: we need a window to own the selection */
87
     selwin = xcb_generate_id(xcb_connection);
88
@@ -1141,13 +1175,6 @@ void init_tray(void) {
89
 
90
     init_tray_colors();
91
 
92
-    if (tray_reply == NULL) {
93
-        if (!(tray_reply = xcb_intern_atom_reply(xcb_connection, tray_cookie, NULL))) {
94
-            ELOG("Could not get atom %s\n", atomname);
95
-            exit(EXIT_FAILURE);
96
-        }
97
-    }
98
-
99
     xcb_set_selection_owner(xcb_connection,
100
                             selwin,
101
                             tray_reply->atom,
102
@@ -1163,16 +1190,17 @@ void init_tray(void) {
103
         exit(EXIT_FAILURE);
104
     }
105
 
106
-    if (selreply->owner != selwin) {
107
-        ELOG("Could not set the %s selection. " \
108
-             "Maybe another tray is already running?\n", atomname);
109
+    if (selreply->owner == selwin) {
110
+        DLOG("Tray initialized. Tray selection owned by 0x%08x.\n", selwin);
111
+        has_tray_selection = true;
112
+        send_tray_clientmessage();
113
+    } else {
114
+        ELOG("Could not set the %s selection. Cannot initialize tray.\n", atomname);
115
         /* NOTE that this error is not fatal. We just can’t provide tray
116
          * functionality */
117
-        free(selreply);
118
-        return;
119
     }
120
 
121
-    send_tray_clientmessage();
122
+    free(selreply);
123
 }
124
 
125
 /*
126
@@ -1273,7 +1301,7 @@ void get_atoms(void) {
127
  *
128
  */
129
 void kick_tray_clients(i3_output *output) {
130
-    if (TAILQ_EMPTY(output->trayclients))
131
+    if (!has_tray_selection || TAILQ_EMPTY(output->trayclients))
132
         return;
133
 
134
     trayclient *trayclient;
135
@@ -1304,7 +1332,8 @@ void kick_tray_clients(i3_output *output) {
136
 
137
     xcb_send_event(conn, false, selwin, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (char*)event);
138
 
139
-    send_tray_clientmessage();
140
+    if (has_tray_selection)
141
+        send_tray_clientmessage();
142
 }
143
 
144
 /*
145
@@ -1319,7 +1348,9 @@ void destroy_window(i3_output *output) {
146
         return;
147
     }
148
 
149
-    kick_tray_clients(output);
150
+    if (has_tray_selection)
151
+        kick_tray_clients(output);
152
+
153
     xcb_destroy_window(xcb_connection, output->bar);
154
     output->bar = XCB_NONE;
155
 }