Merge lp:~smspillaz/xig/xig.clean-up-after-clients into lp:xig
- xig.clean-up-after-clients
- Merge into trunk
Status: | Work in progress |
---|---|
Proposed branch: | lp:~smspillaz/xig/xig.clean-up-after-clients |
Merge into: | lp:xig |
Prerequisite: | lp:~smspillaz/xig/xig.real-get-property-reply-message-length |
Diff against target: |
349 lines (+174/-58) 4 files modified
src/xig-remote-client-private.h (+5/-0) src/xig-remote-client.c (+24/-1) src/xig-server.c (+67/-19) src/xig-window.c (+78/-38) |
To merge this branch: | bzr merge lp:~smspillaz/xig/xig.clean-up-after-clients |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Robert Ancell | Needs Fixing | ||
Review via email: mp+89533@code.launchpad.net |
Commit message
Description of the change
Remove window id's left behind by clients due to buggy X extension libraries or by SIGKILL.
Remove stale selections
Sam Spilsbury (smspillaz) wrote : | # |
The server already maintains a list of resources, so when the client goes away we need to figure out which resources the client owns and get rid of them on the server side lists too.
Robert Ancell (robert-ancell) wrote : | # |
From me via IRC:
"What you really should do is the server should connected to the destroyed signal for the resources and remove them that way
so you're explicitly destroying the references from both sides, but the server should be implicitly destroying them
(or the other way around). The patch has both the server and client responsible which is dangerous if one of them gets out of sync.
So there's two ways we can do this - the client destroys itself, and the server detects that and destroys all its resources, or the client destroys all its resources then itself, and the server doesn't care"
and Sam said "So I think the best way to handle this is to have a XigClientResource which XigSelection and XigDrawable inherit"
Which is correct.
Unmerged revisions
- 255. By Sam Spilsbury
-
Clean up after clients - remove stale resource id's and selections. Also
handle the edge case where clients die and leave stuff behind. - 254. By Sam Spilsbury
-
Write message length according to format, not actual length as that's too much
Preview Diff
1 | === modified file 'src/xig-remote-client-private.h' | |||
2 | --- src/xig-remote-client-private.h 2011-12-01 23:46:20 +0000 | |||
3 | +++ src/xig-remote-client-private.h 2012-01-21 11:59:25 +0000 | |||
4 | @@ -23,4 +23,9 @@ | |||
5 | 23 | 23 | ||
6 | 24 | void _xig_remote_client_circulate_request (XigRemoteClient *client, XigWindow *window, XigCirculatePlace place); | 24 | void _xig_remote_client_circulate_request (XigRemoteClient *client, XigWindow *window, XigCirculatePlace place); |
7 | 25 | 25 | ||
8 | 26 | GList * _xig_remote_client_get_owned_drawables (XigRemoteClient *client); | ||
9 | 27 | |||
10 | 28 | void _xig_remote_client_add_drawable (XigRemoteClient *client, XigDrawable *drawable); | ||
11 | 29 | void _xig_remote_client_remove_drawable (XigRemoteClient *client, XigDrawable *drawable); | ||
12 | 30 | |||
13 | 26 | #endif /* _XIG_REMOTE_CLIENT_PRIVATE_H_ */ | 31 | #endif /* _XIG_REMOTE_CLIENT_PRIVATE_H_ */ |
14 | 27 | 32 | ||
15 | === modified file 'src/xig-remote-client.c' | |||
16 | --- src/xig-remote-client.c 2012-01-10 16:45:57 +0000 | |||
17 | +++ src/xig-remote-client.c 2012-01-21 11:59:25 +0000 | |||
18 | @@ -47,6 +47,9 @@ | |||
19 | 47 | 47 | ||
20 | 48 | /* Event masks */ | 48 | /* Event masks */ |
21 | 49 | GHashTable *window_events; | 49 | GHashTable *window_events; |
22 | 50 | |||
23 | 51 | /* Owned windows */ | ||
24 | 52 | GHashTable *owned_drawables; | ||
25 | 50 | }; | 53 | }; |
26 | 51 | 54 | ||
27 | 52 | enum | 55 | enum |
28 | @@ -287,6 +290,24 @@ | |||
29 | 287 | xig_codec_feed_circulate_request (XIG_CODEC (client), &message); | 290 | xig_codec_feed_circulate_request (XIG_CODEC (client), &message); |
30 | 288 | } | 291 | } |
31 | 289 | 292 | ||
32 | 293 | GList * | ||
33 | 294 | _xig_remote_client_get_owned_drawables (XigRemoteClient *client) | ||
34 | 295 | { | ||
35 | 296 | return g_hash_table_get_keys (client->priv->owned_drawables); | ||
36 | 297 | } | ||
37 | 298 | |||
38 | 299 | void | ||
39 | 300 | _xig_remote_client_add_drawable (XigRemoteClient *client, XigDrawable *drawable) | ||
40 | 301 | { | ||
41 | 302 | g_hash_table_insert (client->priv->owned_drawables, GINT_TO_POINTER (xig_drawable_get_id (drawable)), g_object_ref (drawable)); | ||
42 | 303 | } | ||
43 | 304 | |||
44 | 305 | void | ||
45 | 306 | _xig_remote_client_remove_drawable (XigRemoteClient *client, XigDrawable *drawable) | ||
46 | 307 | { | ||
47 | 308 | g_hash_table_remove (client->priv->owned_drawables, GINT_TO_POINTER (xig_drawable_get_id (drawable))); | ||
48 | 309 | } | ||
49 | 310 | |||
50 | 290 | static void | 311 | static void |
51 | 291 | circulate_notify_cb (XigWindow *window, XigRemoteClient *client) | 312 | circulate_notify_cb (XigWindow *window, XigRemoteClient *client) |
52 | 292 | { | 313 | { |
53 | @@ -412,7 +433,7 @@ | |||
54 | 412 | if (n_read < 0) | 433 | if (n_read < 0) |
55 | 413 | g_warning ("Error reading from socket: %s", strerror (errno)); | 434 | g_warning ("Error reading from socket: %s", strerror (errno)); |
56 | 414 | else if (n_read == 0) | 435 | else if (n_read == 0) |
58 | 415 | { | 436 | { |
59 | 416 | g_signal_emit (client, signals[DISCONNECTED], 0); | 437 | g_signal_emit (client, signals[DISCONNECTED], 0); |
60 | 417 | return FALSE; | 438 | return FALSE; |
61 | 418 | } | 439 | } |
62 | @@ -1574,6 +1595,7 @@ | |||
63 | 1574 | 1595 | ||
64 | 1575 | reply.sequence_number = message->sequence_number; | 1596 | reply.sequence_number = message->sequence_number; |
65 | 1576 | focus = xig_server_get_input_focus (client->priv->server, &reply.revert_to); | 1597 | focus = xig_server_get_input_focus (client->priv->server, &reply.revert_to); |
66 | 1598 | |||
67 | 1577 | reply.focus = focus ? xig_window_get_id (focus) : XIG_WINDOW_ID_NONE; | 1599 | reply.focus = focus ? xig_window_get_id (focus) : XIG_WINDOW_ID_NONE; |
68 | 1578 | 1600 | ||
69 | 1579 | xig_codec_feed_get_input_focus_reply (XIG_CODEC (codec), &reply); | 1601 | xig_codec_feed_get_input_focus_reply (XIG_CODEC (codec), &reply); |
70 | @@ -2582,6 +2604,7 @@ | |||
71 | 2582 | client->priv->server = server; | 2604 | client->priv->server = server; |
72 | 2583 | client->priv->socket = g_object_ref (socket); | 2605 | client->priv->socket = g_object_ref (socket); |
73 | 2584 | client->priv->channel = g_io_channel_unix_new (g_socket_get_fd (socket)); | 2606 | client->priv->channel = g_io_channel_unix_new (g_socket_get_fd (socket)); |
74 | 2607 | client->priv->owned_drawables = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); | ||
75 | 2585 | g_io_add_watch (client->priv->channel, G_IO_IN, socket_data_cb, client); | 2608 | g_io_add_watch (client->priv->channel, G_IO_IN, socket_data_cb, client); |
76 | 2586 | 2609 | ||
77 | 2587 | return client; | 2610 | return client; |
78 | 2588 | 2611 | ||
79 | === modified file 'src/xig-server.c' | |||
80 | --- src/xig-server.c 2011-12-02 10:34:45 +0000 | |||
81 | +++ src/xig-server.c 2012-01-21 11:59:25 +0000 | |||
82 | @@ -150,12 +150,78 @@ | |||
83 | 150 | server->priv->listen_tcp = listen_tcp; | 150 | server->priv->listen_tcp = listen_tcp; |
84 | 151 | } | 151 | } |
85 | 152 | 152 | ||
86 | 153 | static gboolean | ||
87 | 154 | _xig_server_hash_table_remove_selection_for_window (gpointer key, | ||
88 | 155 | gpointer owner, | ||
89 | 156 | gpointer user_data) | ||
90 | 157 | { | ||
91 | 158 | return owner == user_data ? TRUE : FALSE; | ||
92 | 159 | } | ||
93 | 160 | |||
94 | 161 | void | ||
95 | 162 | _xig_server_remove_window (XigServer *server, XigWindow *window) | ||
96 | 163 | { | ||
97 | 164 | g_hash_table_remove (server->priv->drawables, GINT_TO_POINTER (xig_drawable_get_id (XIG_DRAWABLE (window)))); | ||
98 | 165 | |||
99 | 166 | /* Remove any selection owners too */ | ||
100 | 167 | g_hash_table_foreach_remove (server->priv->selection_owners, _xig_server_hash_table_remove_selection_for_window, (gpointer) window); | ||
101 | 168 | |||
102 | 169 | /* Unset the focus window (revert to root window */ | ||
103 | 170 | if (window == server->priv->focus_window) | ||
104 | 171 | server->priv->focus_window = NULL; | ||
105 | 172 | } | ||
106 | 173 | |||
107 | 174 | void | ||
108 | 175 | _xig_server_add_window (XigServer *server, XigWindow *window) | ||
109 | 176 | { | ||
110 | 177 | g_hash_table_insert (server->priv->drawables, GINT_TO_POINTER (xig_drawable_get_id (XIG_DRAWABLE (window))), g_object_ref (window)); | ||
111 | 178 | } | ||
112 | 179 | |||
113 | 180 | void | ||
114 | 181 | _xig_server_add_pixmap (XigServer *server, XigPixmap *pixmap) | ||
115 | 182 | { | ||
116 | 183 | g_hash_table_insert (server->priv->drawables, GINT_TO_POINTER (xig_drawable_get_id (XIG_DRAWABLE (pixmap))), g_object_ref (pixmap)); | ||
117 | 184 | } | ||
118 | 185 | |||
119 | 186 | void | ||
120 | 187 | _xig_server_remove_pixmap (XigServer *server, XigPixmap *pixmap) | ||
121 | 188 | { | ||
122 | 189 | g_hash_table_remove (server->priv->drawables, GINT_TO_POINTER (xig_drawable_get_id (XIG_DRAWABLE (pixmap)))); | ||
123 | 190 | } | ||
124 | 191 | |||
125 | 192 | |||
126 | 153 | static void | 193 | static void |
127 | 154 | xig_remote_client_disconnected_cb (XigRemoteClient *client, XigServer *server) | 194 | xig_remote_client_disconnected_cb (XigRemoteClient *client, XigServer *server) |
128 | 155 | { | 195 | { |
129 | 156 | g_signal_handlers_disconnect_matched (client, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, server); | 196 | g_signal_handlers_disconnect_matched (client, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, server); |
130 | 157 | server->priv->clients = g_list_remove (server->priv->clients, client); | 197 | server->priv->clients = g_list_remove (server->priv->clients, client); |
131 | 158 | g_signal_emit (server, signals[CLIENT_DISCONNECTED], 0, client); | 198 | g_signal_emit (server, signals[CLIENT_DISCONNECTED], 0, client); |
132 | 199 | |||
133 | 200 | GList *list = _xig_remote_client_get_owned_drawables (client); | ||
134 | 201 | GList *orig = list; | ||
135 | 202 | |||
136 | 203 | while (list) | ||
137 | 204 | { | ||
138 | 205 | gint id = GPOINTER_TO_INT (list->data); | ||
139 | 206 | XigDrawable *drawable = g_hash_table_lookup (server->priv->drawables, GINT_TO_POINTER (id)); | ||
140 | 207 | |||
141 | 208 | if (XIG_IS_WINDOW (drawable)) | ||
142 | 209 | { | ||
143 | 210 | XigWindow *window = XIG_WINDOW (drawable); | ||
144 | 211 | |||
145 | 212 | _xig_server_remove_window (server, window); | ||
146 | 213 | } | ||
147 | 214 | else if (XIG_IS_PIXMAP (drawable)) | ||
148 | 215 | { | ||
149 | 216 | XigPixmap *pixmap = XIG_PIXMAP (drawable); | ||
150 | 217 | |||
151 | 218 | _xig_server_remove_pixmap (server, pixmap); | ||
152 | 219 | } | ||
153 | 220 | list = g_list_next (list); | ||
154 | 221 | } | ||
155 | 222 | |||
156 | 223 | g_list_free (orig); | ||
157 | 224 | |||
158 | 159 | g_object_unref (client); | 225 | g_object_unref (client); |
159 | 160 | } | 226 | } |
160 | 161 | 227 | ||
161 | @@ -257,12 +323,6 @@ | |||
162 | 257 | } | 323 | } |
163 | 258 | 324 | ||
164 | 259 | void | 325 | void |
165 | 260 | _xig_server_add_window (XigServer *server, XigWindow *window) | ||
166 | 261 | { | ||
167 | 262 | g_hash_table_insert (server->priv->drawables, GINT_TO_POINTER (xig_drawable_get_id (XIG_DRAWABLE (window))), g_object_ref (window)); | ||
168 | 263 | } | ||
169 | 264 | |||
170 | 265 | void | ||
171 | 266 | _xig_server_set_selection_owner (XigServer *server, const gchar *name, XigWindow *window) | 326 | _xig_server_set_selection_owner (XigServer *server, const gchar *name, XigWindow *window) |
172 | 267 | { | 327 | { |
173 | 268 | g_hash_table_insert (server->priv->selection_owners, g_strdup (name), g_object_ref (window)); | 328 | g_hash_table_insert (server->priv->selection_owners, g_strdup (name), g_object_ref (window)); |
174 | @@ -274,18 +334,6 @@ | |||
175 | 274 | return g_hash_table_lookup (server->priv->selection_owners, name); | 334 | return g_hash_table_lookup (server->priv->selection_owners, name); |
176 | 275 | } | 335 | } |
177 | 276 | 336 | ||
178 | 277 | void | ||
179 | 278 | _xig_server_add_pixmap (XigServer *server, XigPixmap *pixmap) | ||
180 | 279 | { | ||
181 | 280 | g_hash_table_insert (server->priv->drawables, GINT_TO_POINTER (xig_drawable_get_id (XIG_DRAWABLE (pixmap))), g_object_ref (pixmap)); | ||
182 | 281 | } | ||
183 | 282 | |||
184 | 283 | void | ||
185 | 284 | _xig_server_remove_pixmap (XigServer *server, XigPixmap *pixmap) | ||
186 | 285 | { | ||
187 | 286 | g_hash_table_remove (server->priv->drawables, GINT_TO_POINTER (xig_drawable_get_id (XIG_DRAWABLE (pixmap)))); | ||
188 | 287 | } | ||
189 | 288 | |||
190 | 289 | XigDrawable * | 337 | XigDrawable * |
191 | 290 | xig_server_get_drawable (XigServer *server, guint32 drawable) | 338 | xig_server_get_drawable (XigServer *server, guint32 drawable) |
192 | 291 | { | 339 | { |
193 | @@ -346,7 +394,7 @@ | |||
194 | 346 | void | 394 | void |
195 | 347 | xig_server_set_input_focus (XigServer *server, XigWindow *window, guint8 revert_to) | 395 | xig_server_set_input_focus (XigServer *server, XigWindow *window, guint8 revert_to) |
196 | 348 | { | 396 | { |
198 | 349 | server->priv->focus_window = g_object_ref (window); | 397 | server->priv->focus_window = window; |
199 | 350 | // FIXME: Catch delete event | 398 | // FIXME: Catch delete event |
200 | 351 | server->priv->focus_revert_to = revert_to; | 399 | server->priv->focus_revert_to = revert_to; |
201 | 352 | } | 400 | } |
202 | 353 | 401 | ||
203 | === modified file 'src/xig-window.c' | |||
204 | --- src/xig-window.c 2012-01-10 17:14:41 +0000 | |||
205 | +++ src/xig-window.c 2012-01-21 11:59:25 +0000 | |||
206 | @@ -76,6 +76,76 @@ | |||
207 | 76 | }; | 76 | }; |
208 | 77 | static guint signals[LAST_SIGNAL] = { 0 }; | 77 | static guint signals[LAST_SIGNAL] = { 0 }; |
209 | 78 | 78 | ||
210 | 79 | static void | ||
211 | 80 | destroy_recursive (XigWindow *window) | ||
212 | 81 | { | ||
213 | 82 | GList *link; | ||
214 | 83 | |||
215 | 84 | /* Destroy all children first */ | ||
216 | 85 | for (link = window->priv->children; link; link = link->next) | ||
217 | 86 | { | ||
218 | 87 | XigWindow *w = link->data; | ||
219 | 88 | destroy_recursive (w); | ||
220 | 89 | } | ||
221 | 90 | |||
222 | 91 | g_signal_emit (window, signals[DESTROY_NOTIFY], 0); | ||
223 | 92 | g_signal_emit (window->priv->parent, signals[CHILD_DESTROY_NOTIFY], 0, window); | ||
224 | 93 | |||
225 | 94 | g_object_unref (window); | ||
226 | 95 | } | ||
227 | 96 | |||
228 | 97 | void | ||
229 | 98 | xig_window_destroy (XigWindow *window) | ||
230 | 99 | { | ||
231 | 100 | window->priv->parent->priv->children = g_list_remove (window->priv->parent->priv->children, window); | ||
232 | 101 | destroy_recursive (window); | ||
233 | 102 | } | ||
234 | 103 | |||
235 | 104 | void | ||
236 | 105 | xig_window_destroy_subwindows (XigWindow *window) | ||
237 | 106 | { | ||
238 | 107 | GList *link; | ||
239 | 108 | for (link = window->priv->children; link; link = link->next) | ||
240 | 109 | { | ||
241 | 110 | XigWindow *w = link->data; | ||
242 | 111 | destroy_recursive (w); | ||
243 | 112 | } | ||
244 | 113 | g_list_free (window->priv->children); | ||
245 | 114 | window->priv->children = NULL; | ||
246 | 115 | } | ||
247 | 116 | |||
248 | 117 | /* When clients disconnect, they are meant to (implicitly, through xcb) | ||
249 | 118 | * remove any resources that they held. When a client dies, it can't do that | ||
250 | 119 | * so clean up after it */ | ||
251 | 120 | static void | ||
252 | 121 | xig_window_client_disconnected_cb (XigServer *server, XigRemoteClient *client, XigWindow *window) | ||
253 | 122 | { | ||
254 | 123 | GList *list = _xig_remote_client_get_owned_drawables (client); | ||
255 | 124 | GList *orig = list; | ||
256 | 125 | |||
257 | 126 | while (list) | ||
258 | 127 | { | ||
259 | 128 | gint id = GPOINTER_TO_INT (list->data); | ||
260 | 129 | |||
261 | 130 | GList *child = window->priv->children; | ||
262 | 131 | |||
263 | 132 | while (child) | ||
264 | 133 | { | ||
265 | 134 | if (xig_window_get_id (XIG_WINDOW (child->data)) == id) | ||
266 | 135 | { | ||
267 | 136 | xig_window_destroy (XIG_WINDOW (child->data)); | ||
268 | 137 | break; | ||
269 | 138 | } | ||
270 | 139 | |||
271 | 140 | child = g_list_next (child); | ||
272 | 141 | } | ||
273 | 142 | |||
274 | 143 | list = g_list_next (list); | ||
275 | 144 | } | ||
276 | 145 | |||
277 | 146 | g_list_free (orig); | ||
278 | 147 | } | ||
279 | 148 | |||
280 | 79 | XigWindow * | 149 | XigWindow * |
281 | 80 | _xig_window_new (XigServer *server, XigRemoteClient *client, guint32 wid, XigWindow *parent, XigWindowClassType class, gint16 x, gint16 y, guint16 width, guint16 height, guint16 border_width, XigVisual *visual) | 150 | _xig_window_new (XigServer *server, XigRemoteClient *client, guint32 wid, XigWindow *parent, XigWindowClassType class, gint16 x, gint16 y, guint16 width, guint16 height, guint16 border_width, XigVisual *visual) |
282 | 81 | { | 151 | { |
283 | @@ -97,6 +167,10 @@ | |||
284 | 97 | window->priv->border_width = border_width; | 167 | window->priv->border_width = border_width; |
285 | 98 | window->priv->visual = g_object_ref (visual); | 168 | window->priv->visual = g_object_ref (visual); |
286 | 99 | 169 | ||
287 | 170 | if (client) | ||
288 | 171 | _xig_remote_client_add_drawable (client, XIG_DRAWABLE (window)); | ||
289 | 172 | g_signal_connect (server, "client-disconnected", G_CALLBACK (xig_window_client_disconnected_cb), window); | ||
290 | 173 | |||
291 | 100 | if (parent) | 174 | if (parent) |
292 | 101 | g_signal_emit (parent, signals[CREATE_NOTIFY], 0, window); | 175 | g_signal_emit (parent, signals[CREATE_NOTIFY], 0, window); |
293 | 102 | 176 | ||
294 | @@ -695,44 +769,6 @@ | |||
295 | 695 | } | 769 | } |
296 | 696 | 770 | ||
297 | 697 | static void | 771 | static void |
298 | 698 | destroy_recursive (XigWindow *window) | ||
299 | 699 | { | ||
300 | 700 | GList *link; | ||
301 | 701 | |||
302 | 702 | /* Destroy all children first */ | ||
303 | 703 | for (link = window->priv->children; link; link = link->next) | ||
304 | 704 | { | ||
305 | 705 | XigWindow *w = link->data; | ||
306 | 706 | destroy_recursive (w); | ||
307 | 707 | } | ||
308 | 708 | |||
309 | 709 | g_signal_emit (window, signals[DESTROY_NOTIFY], 0); | ||
310 | 710 | g_signal_emit (window->priv->parent, signals[CHILD_DESTROY_NOTIFY], 0, window); | ||
311 | 711 | |||
312 | 712 | g_object_unref (window); | ||
313 | 713 | } | ||
314 | 714 | |||
315 | 715 | void | ||
316 | 716 | xig_window_destroy (XigWindow *window) | ||
317 | 717 | { | ||
318 | 718 | window->priv->parent->priv->children = g_list_remove (window->priv->parent->priv->children, window); | ||
319 | 719 | destroy_recursive (window); | ||
320 | 720 | } | ||
321 | 721 | |||
322 | 722 | void | ||
323 | 723 | xig_window_destroy_subwindows (XigWindow *window) | ||
324 | 724 | { | ||
325 | 725 | GList *link; | ||
326 | 726 | for (link = window->priv->children; link; link = link->next) | ||
327 | 727 | { | ||
328 | 728 | XigWindow *w = link->data; | ||
329 | 729 | destroy_recursive (w); | ||
330 | 730 | } | ||
331 | 731 | g_list_free (window->priv->children); | ||
332 | 732 | window->priv->children = NULL; | ||
333 | 733 | } | ||
334 | 734 | |||
335 | 735 | static void | ||
336 | 736 | xig_window_init (XigWindow *window) | 772 | xig_window_init (XigWindow *window) |
337 | 737 | { | 773 | { |
338 | 738 | window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window, xig_window_get_type (), XigWindowPrivate); | 774 | window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window, xig_window_get_type (), XigWindowPrivate); |
339 | @@ -753,6 +789,10 @@ | |||
340 | 753 | xig_window_finalize (GObject *object) | 789 | xig_window_finalize (GObject *object) |
341 | 754 | { | 790 | { |
342 | 755 | XigWindow *window = (XigWindow *) object; | 791 | XigWindow *window = (XigWindow *) object; |
343 | 792 | |||
344 | 793 | if (window->priv->client) | ||
345 | 794 | _xig_remote_client_remove_drawable (window->priv->client, XIG_DRAWABLE (window)); | ||
346 | 795 | |||
347 | 756 | if (window->priv->visual) | 796 | if (window->priv->visual) |
348 | 757 | g_object_unref (window->priv->visual); | 797 | g_object_unref (window->priv->visual); |
349 | 758 | if (window->priv->background_pixmap) | 798 | if (window->priv->background_pixmap) |
This patch seems a little back to front... I'd expect the client to maintain the list of resources rather than have the server call back to it. I need to think this one over a bit more (though the concept is 100% correct of course).