diff options
-rw-r--r-- | README.ja.md | 2 | ||||
-rw-r--r-- | include/sway/config.h | 4 | ||||
-rw-r--r-- | sway/config/bar.c | 26 | ||||
-rw-r--r-- | sway/main.c | 1 | ||||
-rw-r--r-- | sway/tree/output.c | 2 | ||||
-rw-r--r-- | swaybar/bar.c | 112 | ||||
-rw-r--r-- | swaybar/i3bar.c | 17 | ||||
-rw-r--r-- | swaybar/render.c | 5 |
8 files changed, 97 insertions, 72 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/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 | ||
513 | void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); | 513 | void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); |
514 | 514 | ||
515 | void load_swaybars(); | 515 | void load_swaybars(void); |
516 | |||
517 | void invoke_swaybar(struct bar_config *bar); | ||
518 | 516 | ||
519 | void terminate_swaybg(pid_t pid); | 517 | void terminate_swaybg(pid_t pid); |
520 | 518 | ||
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 | ||
168 | void invoke_swaybar(struct bar_config *bar) { | 168 | static 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 | ||
222 | void load_swaybars() { | 222 | void 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/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/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/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 | ||
291 | static 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 | |||
309 | static 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 | |||
286 | static void output_geometry(void *data, struct wl_output *output, int32_t x, | 322 | static 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 | ||
327 | static void xdg_output_handle_done(void *data, | 363 | static 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 | ||
332 | static void xdg_output_handle_name(void *data, | 383 | static 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 | ||
352 | static bool bar_uses_output(struct swaybar *bar, const char *name) { | 403 | static 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 | ||
365 | static void handle_global(void *data, struct wl_registry *registry, | 414 | static 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 = { | |||
416 | static void render_all_frames(struct swaybar *bar) { | 468 | static 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/i3bar.c b/swaybar/i3bar.c index 88404703..325aa61a 100644 --- a/swaybar/i3bar.c +++ b/swaybar/i3bar.c | |||
@@ -117,7 +117,9 @@ bool i3bar_handle_readable(struct status_line *status) { | |||
117 | memmove(status->buffer, &status->buffer[c], status->buffer_index); | 117 | memmove(status->buffer, &status->buffer[c], status->buffer_index); |
118 | break; | 118 | break; |
119 | } else if (!isspace(status->buffer[c])) { | 119 | } else if (!isspace(status->buffer[c])) { |
120 | status_error(status, "[invalid json]"); | 120 | wlr_log(WLR_DEBUG, "Invalid i3bar json: expected '[' but encountered '%c'", |
121 | status->buffer[c]); | ||
122 | status_error(status, "[invalid i3bar json]"); | ||
121 | return true; | 123 | return true; |
122 | } | 124 | } |
123 | } | 125 | } |
@@ -155,6 +157,8 @@ bool i3bar_handle_readable(struct status_line *status) { | |||
155 | ++buffer_pos; | 157 | ++buffer_pos; |
156 | break; | 158 | break; |
157 | } else if (!isspace(status->buffer[buffer_pos])) { | 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]); | ||
158 | status_error(status, "[invalid i3bar json]"); | 162 | status_error(status, "[invalid i3bar json]"); |
159 | return true; | 163 | return true; |
160 | } | 164 | } |
@@ -166,7 +170,8 @@ bool i3bar_handle_readable(struct status_line *status) { | |||
166 | } else { | 170 | } else { |
167 | test_object = json_tokener_parse_ex(status->tokener, | 171 | test_object = json_tokener_parse_ex(status->tokener, |
168 | &status->buffer[buffer_pos], status->buffer_index - buffer_pos); | 172 | &status->buffer[buffer_pos], status->buffer_index - buffer_pos); |
169 | if (json_tokener_get_error(status->tokener) == json_tokener_success) { | 173 | enum json_tokener_error err = json_tokener_get_error(status->tokener); |
174 | if (err == json_tokener_success) { | ||
170 | if (json_object_get_type(test_object) == json_type_array) { | 175 | if (json_object_get_type(test_object) == json_type_array) { |
171 | if (last_object) { | 176 | if (last_object) { |
172 | json_object_put(last_object); | 177 | json_object_put(last_object); |
@@ -198,12 +203,14 @@ bool i3bar_handle_readable(struct status_line *status) { | |||
198 | continue; // look for comma without reading more input | 203 | continue; // look for comma without reading more input |
199 | } | 204 | } |
200 | buffer_pos = status->buffer_index = 0; | 205 | buffer_pos = status->buffer_index = 0; |
201 | } else if (json_tokener_get_error(status->tokener) == json_tokener_continue) { | 206 | } else if (err == json_tokener_continue) { |
207 | json_tokener_reset(status->tokener); | ||
202 | if (status->buffer_index < status->buffer_size) { | 208 | if (status->buffer_index < status->buffer_size) { |
203 | // move the object to the start of the buffer | 209 | // move the object to the start of the buffer |
204 | status->buffer_index -= buffer_pos; | 210 | status->buffer_index -= buffer_pos; |
205 | memmove(status->buffer, &status->buffer[buffer_pos], | 211 | memmove(status->buffer, &status->buffer[buffer_pos], |
206 | status->buffer_index); | 212 | status->buffer_index); |
213 | buffer_pos = 0; | ||
207 | } else { | 214 | } else { |
208 | // expand buffer | 215 | // expand buffer |
209 | status->buffer_size *= 2; | 216 | status->buffer_size *= 2; |
@@ -217,6 +224,10 @@ bool i3bar_handle_readable(struct status_line *status) { | |||
217 | } | 224 | } |
218 | } | 225 | } |
219 | } else { | 226 | } else { |
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); | ||
220 | status_error(status, "[failed to parse i3bar json]"); | 231 | status_error(status, "[failed to parse i3bar json]"); |
221 | return true; | 232 | return true; |
222 | } | 233 | } |
diff --git a/swaybar/render.c b/swaybar/render.c index 97690338..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> |
@@ -480,6 +481,8 @@ static uint32_t render_to_cairo(cairo_t *cairo, | |||
480 | } | 481 | } |
481 | 482 | ||
482 | void render_frame(struct swaybar *bar, struct swaybar_output *output) { | 483 | void render_frame(struct swaybar *bar, struct swaybar_output *output) { |
484 | assert(output->surface != NULL); | ||
485 | |||
483 | struct swaybar_hotspot *hotspot, *tmp; | 486 | struct swaybar_hotspot *hotspot, *tmp; |
484 | wl_list_for_each_safe(hotspot, tmp, &output->hotspots, link) { | 487 | wl_list_for_each_safe(hotspot, tmp, &output->hotspots, link) { |
485 | if (hotspot->destroy) { | 488 | if (hotspot->destroy) { |
@@ -507,7 +510,6 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) { | |||
507 | // TODO: this could infinite loop if the compositor assigns us a | 510 | // TODO: this could infinite loop if the compositor assigns us a |
508 | // different height than what we asked for | 511 | // different height than what we asked for |
509 | wl_surface_commit(output->surface); | 512 | wl_surface_commit(output->surface); |
510 | wl_display_roundtrip(bar->display); | ||
511 | } else if (height > 0) { | 513 | } else if (height > 0) { |
512 | // Replay recording into shm and send it off | 514 | // Replay recording into shm and send it off |
513 | output->current_buffer = get_next_buffer(bar->shm, | 515 | output->current_buffer = get_next_buffer(bar->shm, |
@@ -533,7 +535,6 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) { | |||
533 | wl_surface_damage(output->surface, 0, 0, | 535 | wl_surface_damage(output->surface, 0, 0, |
534 | output->width, output->height); | 536 | output->width, output->height); |
535 | wl_surface_commit(output->surface); | 537 | wl_surface_commit(output->surface); |
536 | wl_display_roundtrip(bar->display); | ||
537 | } | 538 | } |
538 | cairo_surface_destroy(recorder); | 539 | cairo_surface_destroy(recorder); |
539 | cairo_destroy(cairo); | 540 | cairo_destroy(cairo); |