2 * Copyright (c) 2014 Bert Burgemeister <trebbu@googlemail.com>
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
39 #define VERSION "0.1.1"
41 #define WHITESPACE " \t\n"
60 "[-u glade-builder-file.ui] "
74 eql(const char *s1
, const char *s2
)
76 return s1
!= NULL
&& s2
!= NULL
&& strcmp(s1
, s2
) == 0;
80 * Send GUI feedback to global stream "out". The message format is
81 * "<origin>:<section> <data ...>". The variadic arguments are
82 * strings; last argument must be NULL. We're being patient with
83 * receivers which may intermittently close their end of the fifo, and
84 * make a couple of retries if an error occurs.
87 send_msg(GtkBuildable
*obj
, const char *section
, ...)
93 for (nsec
= 1e6
; nsec
< 1e9
; nsec
<<= 3) {
94 va_start(ap
, section
);
95 fprintf(out
, "%s:%s ", gtk_buildable_get_name(obj
), section
);
96 while ((data
= va_arg(ap
, char *)) != NULL
) {
100 while ((c
= data
[i
++]) != '\0')
102 fprintf(out
, "\\\\");
111 fprintf(stderr
, "Send error; retrying\n");
113 nanosleep(&(struct timespec
){0, nsec
}, NULL
);
121 * Callback that hides the window the originator is in
124 cb_hide_toplevel(GtkBuildable
*obj
, gpointer user_data
)
126 GtkWidget
*toplevel
= gtk_widget_get_toplevel(GTK_WIDGET(obj
));
128 if (gtk_widget_is_toplevel(toplevel
))
129 gtk_widget_hide(toplevel
);
133 * Callback that sends a message describing the user selection of a
137 cb_send_dialog_selection(GtkBuildable
*obj
, gpointer user_data
)
139 GtkWidget
*toplevel
= gtk_widget_get_toplevel(GTK_WIDGET(obj
));
141 if (gtk_widget_is_toplevel(toplevel
))
143 if (GTK_IS_FILE_CHOOSER(toplevel
)) {
144 send_msg(GTK_BUILDABLE(toplevel
),
146 gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(toplevel
)), NULL
);
147 send_msg(GTK_BUILDABLE(toplevel
),
149 gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(toplevel
)), NULL
);
150 } /* responses of other dialogs go here */
155 * Callback that sends in a message the content of the text buffer
156 * passed in user_data
159 cb_send_text(GtkBuildable
*obj
, gpointer user_data
)
163 gtk_text_buffer_get_bounds(user_data
, &a
, &b
);
164 send_msg(obj
, "text", gtk_text_buffer_get_text(user_data
, &a
, &b
, FALSE
), NULL
);
168 * Callback that sends in a message the highlighted text from the text
169 * buffer which was passed in user_data
172 cb_send_text_selection(GtkBuildable
*obj
, gpointer user_data
)
176 gtk_text_buffer_get_selection_bounds(user_data
, &a
, &b
);
177 send_msg(obj
, "text", gtk_text_buffer_get_text(user_data
, &a
, &b
, FALSE
), NULL
);
181 * cb_0(), cb_1(), ... call this function to do their work: Send
182 * message(s) whose nature depends on the arguments passed
185 do_callback(GtkBuildable
*obj
, gpointer user_data
, const char *section
)
187 if (GTK_IS_ENTRY(obj
)) {
188 send_msg(obj
, section
, gtk_entry_get_text(GTK_ENTRY(obj
)), NULL
);
189 } else if (GTK_IS_MENU_ITEM(obj
)) {
190 const char *item_name
;
191 char dialog_name
[BUFLEN
];
193 item_name
= gtk_buildable_get_name(obj
);
194 strncpy(dialog_name
, item_name
, BUFLEN
- 1);
195 strncpy(dialog_name
+ strlen(item_name
), "_dialog", BUFLEN
- 1 - strlen(item_name
));
196 if (gtk_builder_get_object(user_data
, dialog_name
) != NULL
)
197 gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(user_data
, dialog_name
)));
199 send_msg(obj
, section
, gtk_menu_item_get_label(GTK_MENU_ITEM(obj
)), NULL
);
200 } else if (GTK_IS_COMBO_BOX_TEXT(obj
))
201 send_msg(obj
, section
, gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(obj
)), NULL
);
202 else if (GTK_IS_RANGE(obj
)) {
205 snprintf(str
, BUFLEN
, "%f", gtk_range_get_value(GTK_RANGE(obj
)));
206 send_msg(obj
, section
, str
, NULL
);
207 } else if (GTK_IS_TOGGLE_BUTTON(obj
))
208 send_msg(obj
, section
, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(obj
))?"on":"off", NULL
);
209 else if (GTK_IS_BUTTON(obj
))
210 send_msg(obj
, section
, "clicked", NULL
);
211 else if (GTK_IS_FILE_CHOOSER_BUTTON(obj
))
212 send_msg(obj
, section
, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(obj
)), NULL
);
213 else if (GTK_IS_CALENDAR(obj
)) {
214 unsigned int year
= 0, month
= 0, day
= 0;
217 gtk_calendar_get_date(GTK_CALENDAR(obj
), &year
, &month
, &day
);
218 snprintf(date
, 11, "%04u-%02u-%02u", year
, ++month
, day
);
219 send_msg(obj
, section
, date
, NULL
);
220 } else if (GTK_IS_TREE_VIEW_COLUMN(obj
))
221 send_msg(obj
, section
, "clicked", NULL
);
222 else if (GTK_IS_TREE_VIEW(obj
)) {
229 gtk_tree_view_get_cursor(GTK_TREE_VIEW(obj
), &path
, NULL
);
230 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(obj
));
231 gtk_tree_model_get_iter(model
, &iter
, path
);
232 path_s
= gtk_tree_path_to_string(path
);
233 for (col
= 0; col
< gtk_tree_model_get_n_columns(model
); col
++) {
234 GValue value
= G_VALUE_INIT
;
238 gtk_tree_model_get_value(model
, &iter
, col
, &value
);
239 col_type
= gtk_tree_model_get_column_type(model
, col
);
242 snprintf(str
, BUFLEN
, " %d %d", col
, g_value_get_int(&value
));
243 send_msg(obj
, section
, path_s
, str
, NULL
);
246 snprintf(str
, BUFLEN
, " %d %ld", col
, g_value_get_long(&value
));
247 send_msg(obj
, section
, path_s
, str
, NULL
);
250 snprintf(str
, BUFLEN
, " %d %" PRId64
, col
, g_value_get_int64(&value
));
251 send_msg(obj
, section
, path_s
, str
, NULL
);
254 snprintf(str
, BUFLEN
, " %d %u", col
, g_value_get_uint(&value
));
255 send_msg(obj
, section
, path_s
, str
, NULL
);
258 snprintf(str
, BUFLEN
, " %d %lu", col
, g_value_get_ulong(&value
));
259 send_msg(obj
, section
, path_s
, str
, NULL
);
262 snprintf(str
, BUFLEN
, " %d %" PRIu64
, col
, g_value_get_uint64(&value
));
263 send_msg(obj
, section
, path_s
, str
, NULL
);
266 snprintf(str
, BUFLEN
, " %d %d", col
, g_value_get_boolean(&value
));
267 send_msg(obj
, section
, path_s
, str
, NULL
);
270 snprintf(str
, BUFLEN
, " %d %f", col
, g_value_get_float(&value
));
271 send_msg(obj
, section
, path_s
, str
, NULL
);
274 snprintf(str
, BUFLEN
, " %d %f", col
, g_value_get_double(&value
));
275 send_msg(obj
, section
, path_s
, str
, NULL
);
278 snprintf(str
, BUFLEN
, " %d ", col
);
279 send_msg(obj
, section
, path_s
, str
, g_value_get_string(&value
), NULL
);
282 fprintf(stderr
, "Column %d not implemented: %s\n", col
, G_VALUE_TYPE_NAME(&value
));
285 g_value_unset(&value
);
288 gtk_tree_path_free(path
);
290 } else if (GTK_IS_WINDOW(obj
))
291 gtk_widget_hide_on_delete(GTK_WIDGET(obj
));
293 fprintf(stderr
, "Ignoring callback %s from %s\n", section
, gtk_buildable_get_name(obj
));
297 * Callback functions cb_0(), cb_1(), cb_2(), and cb_3() for use in
298 * simple widgets. They're all identical except for sending different
301 #define MAKE_CB_N(n) \
302 void cb_##n(GtkBuildable *obj, gpointer user_data) \
303 {do_callback(obj, user_data, #n);} \
313 * Store a line from stream into buf, which should have been malloc'd
314 * to bufsize. Enlarge buf and bufsize if necessary.
317 read_buf(FILE *stream
, char **buf
, size_t *bufsize
)
326 nanosleep(&(struct timespec
){0, 1e7
}, NULL
);
331 if (i
>= *bufsize
- 1)
332 if ((*buf
= realloc(*buf
, *bufsize
= *bufsize
* 2)) == NULL
) {
333 fprintf(stderr
, "Out of memory (%s in %s)\n", __func__
, __FILE__
);
337 switch (c
= getc(stream
)) {
338 case 'n': (*buf
)[i
++] = '\n'; break;
339 case 'r': (*buf
)[i
++] = '\r'; break;
340 default: (*buf
)[i
++] = c
; break;
353 ignoring_command(const char *msg
)
355 fprintf(stderr
, "Ignoring command \"%s\"\n", msg
);
359 * Parse command pointed to by ud, and act on ui accordingly. Sets
360 * ud->digested = true if done. This will run once per command inside
364 update_ui(struct ui_data
*ud
)
366 char name
[ud
->msg_size
], type
[ud
->msg_size
], action
[ud
->msg_size
];
371 data_start
= strlen(ud
->msg
);
372 name
[0] = type
[0] = action
[0] = '\0';
374 " %[0-9a-zA-Z_]:%[0-9a-zA-Z_]:%[0-9a-zA-Z_]%*[ \t] %n",
375 name
, type
, action
, &data_start
);
376 data
= ud
->msg
+ data_start
;
377 obj
= (gtk_builder_get_object(ud
->builder
, name
));
378 if (eql(action
, "main_quit")) {
380 return G_SOURCE_REMOVE
;
383 ignoring_command(ud
->msg
);
384 else if (eql(type
, "label")) {
385 if (eql(action
, "set_text"))
386 gtk_label_set_text(GTK_LABEL(obj
), data
);
388 ignoring_command(ud
->msg
);
389 } else if (eql(type
, "image")) {
392 gtk_image_get_icon_name(GTK_IMAGE(obj
), NULL
, &size
);
393 if (eql(action
, "set_from_file"))
394 gtk_image_set_from_file(GTK_IMAGE(obj
), data
);
395 else if (eql(action
, "set_from_icon_name"))
396 gtk_image_set_from_icon_name(GTK_IMAGE(obj
), data
, size
);
398 ignoring_command(ud
->msg
);
399 } else if (eql(type
, "text_view")) {
400 GtkTextBuffer
*textbuf
;
401 GtkTextIter iter
, a
, b
;
403 textbuf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(obj
));
404 if (eql(action
, "set_text"))
405 gtk_text_buffer_set_text(textbuf
, data
, -1);
406 else if (eql(action
, "delete")) {
407 gtk_text_buffer_get_bounds(textbuf
, &a
, &b
);
408 gtk_text_buffer_delete(textbuf
, &a
, &b
);
409 } else if (eql(action
, "insert_at_cursor"))
410 gtk_text_buffer_insert_at_cursor(textbuf
, data
, -1);
411 else if (eql(action
, "place_cursor")) {
412 if (eql(data
, "end"))
413 gtk_text_buffer_get_end_iter(textbuf
, &iter
);
414 else /* numeric offset */
415 gtk_text_buffer_get_iter_at_offset(textbuf
, &iter
, strtol(data
, NULL
, 10));
416 gtk_text_buffer_place_cursor(textbuf
, &iter
);
417 } else if (eql(action
, "place_cursor_at_line")) {
418 gtk_text_buffer_get_iter_at_line(textbuf
, &iter
, strtol(data
, NULL
, 10));
419 gtk_text_buffer_place_cursor(textbuf
, &iter
);
420 } else if (eql(action
, "scroll_to_cursor"))
421 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(obj
), gtk_text_buffer_get_insert(textbuf
), 0.0, 0, 0.0, 0.0);
423 ignoring_command(ud
->msg
);
424 } else if (eql(type
, "button")) {
425 if (eql(action
, "set_label"))
426 gtk_button_set_label(GTK_BUTTON(obj
), data
);
428 ignoring_command(ud
->msg
);
429 } else if (eql(type
, "toggle_button") || eql(type
, "radio_button") || eql(type
, "check_button")) {
430 if (eql(action
, "set_label"))
431 gtk_button_set_label(GTK_BUTTON(obj
), data
);
432 else if (eql(action
, "set_active"))
433 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(obj
), strtol(data
, NULL
, 10));
435 ignoring_command(ud
->msg
);
436 } else if (eql(type
, "spin_button") || eql(type
, "entry")) {
437 if (eql(action
, "set_text"))
438 gtk_entry_set_text(GTK_ENTRY(obj
), data
);
440 ignoring_command(ud
->msg
);
441 } else if (eql(type
, "scale")) {
442 if (eql(action
, "set_value"))
443 gtk_range_set_value(GTK_RANGE(obj
), strtod(data
, NULL
));
445 ignoring_command(ud
->msg
);
446 } else if (eql(type
, "progress_bar")) {
447 if (eql(action
, "set_text"))
448 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(obj
), data
);
449 else if (eql(action
, "set_fraction"))
450 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(obj
), strtod(data
, NULL
));
452 ignoring_command(ud
->msg
);
453 } else if (eql(type
, "spinner")) {
454 if (eql(action
, "start"))
455 gtk_spinner_start(GTK_SPINNER(obj
));
456 else if (eql(action
, "stop"))
457 gtk_spinner_stop(GTK_SPINNER(obj
));
459 ignoring_command(ud
->msg
);
460 } else if (eql(type
, "combo_box_text")) {
461 if (eql(action
, "prepend_text"))
462 gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(obj
), data
);
463 else if (eql(action
, "append_text"))
464 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(obj
), data
);
465 else if (eql(action
, "remove"))
466 gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(obj
), strtol(data
, NULL
, 10));
467 else if (eql(action
, "insert_text")) {
468 char *position
, *text
;
470 position
= strtok(data
, WHITESPACE
);
471 text
= strtok(NULL
, WHITESPACE
);
472 gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(obj
), strtol(position
, NULL
, 10), text
);
474 ignoring_command(ud
->msg
);
475 } else if (eql(type
, "statusbar")) {
476 if (eql(action
, "push"))
477 gtk_statusbar_push(GTK_STATUSBAR(obj
), 0, data
);
478 else if (eql(action
, "pop"))
479 gtk_statusbar_pop(GTK_STATUSBAR(obj
), 0);
481 ignoring_command(ud
->msg
);
482 } else if (eql(type
, "calendar")) {
483 int year
= 0, month
= 0, day
= 0;
485 if (eql(action
, "select_date")) {
486 sscanf(data
, "%d-%d-%d", &year
, &month
, &day
);
487 if (month
> -1 && month
<= 11 && day
> 0 && day
<= 31) {
488 gtk_calendar_select_month(GTK_CALENDAR(obj
), --month
, year
);
489 gtk_calendar_select_day(GTK_CALENDAR(obj
), day
);
491 ignoring_command(ud
->msg
);
492 } else if (eql(action
, "mark_day")) {
493 day
= strtol(data
, NULL
, 10);
494 if (day
> 0 && day
<= 31)
495 gtk_calendar_mark_day(GTK_CALENDAR(obj
), day
);
497 ignoring_command(ud
->msg
);
498 } else if (eql(action
, "clear_marks"))
499 gtk_calendar_clear_marks(GTK_CALENDAR(obj
));
501 ignoring_command(ud
->msg
);
502 } else if (eql(type
, "tree_view")) {
503 GtkTreeIter iter0
, iter1
;
507 char *tokens
, *arg0_s
, *arg1_s
, *arg2_s
, *endptr
;
508 int arg0_n
= 0, arg1_n
= 0;
509 bool arg0_n_valid
= false, arg1_n_valid
= false;
510 bool iter0_valid
= false, iter1_valid
= false;
512 view
= GTK_TREE_VIEW(obj
);
513 model
= gtk_tree_view_get_model(view
);
514 store
= GTK_LIST_STORE(model
);
515 if ((tokens
= malloc(strlen(data
) + 1)) == NULL
) {
516 fprintf(stderr
, "Out of memory (%s in %s)\n", __func__
, __FILE__
);
519 strcpy(tokens
, data
);
520 arg0_s
= strtok(tokens
, WHITESPACE
);
521 arg1_s
= strtok(NULL
, WHITESPACE
);
522 arg2_s
= strtok(NULL
, "\n");
527 (arg0_n_valid
= (arg0_n
= strtol(arg0_s
, &endptr
, 10)) >= 0 &&
528 errno
== 0 && endptr
!= arg0_s
) &&
529 gtk_tree_model_get_iter_from_string(model
, &iter0
, arg0_s
);
534 (arg1_n_valid
= (arg1_n
= strtol(arg1_s
, &endptr
, 10)) >= 0 &&
535 errno
== 0 && endptr
!= arg1_s
) &&
536 gtk_tree_model_get_iter_from_string(model
, &iter1
, arg1_s
);
537 if (eql(action
, "set") && iter0_valid
&& arg1_n_valid
&&
538 arg1_n
< gtk_tree_model_get_n_columns(model
)) {
543 col_type
= gtk_tree_model_get_column_type(model
, arg1_n
);
554 n
= strtoll(arg2_s
, &endptr
, 10);
555 if (!errno
&& endptr
!= arg2_s
)
556 gtk_list_store_set(store
, &iter0
, arg1_n
, n
, -1);
558 ignoring_command(ud
->msg
);
564 d
= strtod(arg2_s
, &endptr
);
565 if (!errno
&& endptr
!= arg2_s
)
566 gtk_list_store_set(store
, &iter0
, arg1_n
, d
, -1);
568 ignoring_command(ud
->msg
);
569 gtk_list_store_set(store
, &iter0
, arg1_n
, strtod(arg2_s
, NULL
), -1);
572 gtk_list_store_set(store
, &iter0
, arg1_n
, arg2_s
, -1);
575 fprintf(stderr
, "Column %d: %s not implemented\n", arg1_n
, g_type_name(col_type
));
578 } else if (eql(action
, "scroll") && arg0_n_valid
&& arg1_n_valid
)
579 gtk_tree_view_scroll_to_cell (view
,
580 gtk_tree_path_new_from_string(arg0_s
),
581 gtk_tree_view_get_column(view
, arg1_n
),
583 else if (eql(action
, "insert_row"))
584 if (eql(arg0_s
, "end"))
585 gtk_list_store_insert_before(store
, &iter1
, NULL
);
586 else if (iter0_valid
)
587 gtk_list_store_insert_before(store
, &iter1
, &iter0
);
589 ignoring_command(ud
->msg
);
590 else if (eql(action
, "move_row") && iter0_valid
)
591 if (eql(arg1_s
, "end"))
592 gtk_list_store_move_before(store
, &iter0
, NULL
);
593 else if (iter1_valid
)
594 gtk_list_store_move_before(store
, &iter0
, &iter1
);
596 ignoring_command(ud
->msg
);
597 else if (eql(action
, "remove_row") && iter0_valid
)
598 gtk_list_store_remove(store
, &iter0
);
600 ignoring_command(ud
->msg
);
602 } else if (eql(type
, "window")) {
603 if (eql(action
, "set_title"))
604 gtk_window_set_title(GTK_WINDOW(obj
), data
);
606 ignoring_command(ud
->msg
);
608 ignoring_command(ud
->msg
);
609 ud
->msg_digested
= true;
610 return G_SOURCE_REMOVE
;
614 * Read lines from global stream "in" and perform the appropriate
618 digest_msg(void *builder
)
624 if ((ud
.msg
= malloc(ud
.msg_size
= 32)) == NULL
) {
625 fprintf(stderr
, "Out of memory (%s in %s)\n", __func__
, __FILE__
);
628 read_buf(in
, &ud
.msg
, &ud
.msg_size
);
629 sscanf(ud
.msg
, " %c", &first_char
);
630 ud
.builder
= builder
;
631 if (first_char
!= '#') {
632 ud
.msg_digested
= false;
633 gdk_threads_add_timeout(1, (GSourceFunc
)update_ui
, &ud
);
634 while (!ud
.msg_digested
)
635 nanosleep(&(struct timespec
){0, 1e6
}, NULL
);
643 * Create a fifo if necessary, and open it. Give up if the file
644 * exists but is not a fifo
647 fifo(const char *name
, const char *mode
)
654 if (!S_ISFIFO(sb
.st_mode
))
655 if (name
!= NULL
&& mkfifo(name
, 0666) != 0) {
656 perror("making fifo");
664 fd
= open(name
, O_RDWR
| O_NONBLOCK
);
666 perror("opening fifo");
669 stream
= fdopen(fd
, "r");
676 /* fopen blocks if there is no reader so here is one */
677 fd
= open(name
, O_RDONLY
| O_NONBLOCK
);
679 perror("opening fifo");
682 stream
= fopen(name
, "w");
683 /* unblocking done */
691 if (stream
== NULL
) {
692 perror("opening fifo");
695 setvbuf(stream
, NULL
, _IOLBF
, 0);
701 main(int argc
, char *argv
[])
703 char opt
, *ui_file
= NULL
;
704 char *in_fifo
= NULL
, *out_fifo
= NULL
;
707 GError
*error
= NULL
;
708 GObject
*window
= NULL
;
712 while ((opt
= getopt(argc
, argv
, "hi:o:u:V")) != -1) {
714 case 'i': in_fifo
= optarg
; break;
715 case 'o': out_fifo
= optarg
; break;
716 case 'u': ui_file
= optarg
; break;
717 case 'V': version(); break;
720 default: usage(argv
); break;
724 ui_file
= "pipeglade.ui";
726 builder
= gtk_builder_new();
727 if (gtk_builder_add_from_file(builder
, ui_file
, &error
) == 0) {
728 fprintf(stderr
, "%s\n", error
->message
);
731 in
= fifo(in_fifo
, "r");
732 out
= fifo(out_fifo
, "w");
733 gtk_builder_connect_signals(builder
, builder
);
734 pthread_create(&receiver
, NULL
, digest_msg
, (void*) builder
);
735 window
= (gtk_builder_get_object(builder
, "window"));
736 if (!GTK_IS_WIDGET(window
)) {
737 fprintf(stderr
, "No toplevel window named \'window\'\n");
740 gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder
, "window")));
750 pthread_cancel(receiver
);
751 pthread_join(receiver
, NULL
);