2 * Copyright (c) 2014, 2015 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 "1.2.0"
41 #define WHITESPACE " \t\n"
60 "[-u glade-builder-file.ui] "
77 printf("GTK+ v%d.%d.%d (running v%d.%d.%d)\n",
78 GTK_MAJOR_VERSION
, GTK_MINOR_VERSION
, GTK_MICRO_VERSION
,
79 gtk_get_major_version(), gtk_get_minor_version(), gtk_get_micro_version());
84 eql(const char *s1
, const char *s2
)
86 return s1
!= NULL
&& s2
!= NULL
&& strcmp(s1
, s2
) == 0;
90 * Send GUI feedback to global stream "out". The message format is
91 * "<origin>:<section> <data ...>". The variadic arguments are
92 * strings; last argument must be NULL. We're being patient with
93 * receivers which may intermittently close their end of the fifo, and
94 * make a couple of retries if an error occurs.
97 send_msg(GtkBuildable
*obj
, const char *section
, ...)
103 for (nsec
= 1e6
; nsec
< 1e9
; nsec
<<= 3) {
104 va_start(ap
, section
);
105 fprintf(out
, "%s:%s ", gtk_buildable_get_name(obj
), section
);
106 while ((data
= va_arg(ap
, char *)) != NULL
) {
110 while ((c
= data
[i
++]) != '\0')
112 fprintf(out
, "\\\\");
121 fprintf(stderr
, "Send error; retrying\n");
123 nanosleep(&(struct timespec
){0, nsec
}, NULL
);
131 * Callback that hides the window the originator is in
134 cb_hide_toplevel(GtkBuildable
*obj
, gpointer user_data
)
136 GtkWidget
*toplevel
= gtk_widget_get_toplevel(GTK_WIDGET(obj
));
138 if (gtk_widget_is_toplevel(toplevel
))
139 gtk_widget_hide(toplevel
);
143 * Callback that sends a message describing the user selection of a
147 cb_send_dialog_selection(GtkBuildable
*obj
, gpointer user_data
)
149 GtkWidget
*toplevel
= gtk_widget_get_toplevel(GTK_WIDGET(obj
));
151 if (gtk_widget_is_toplevel(toplevel
))
153 if (GTK_IS_FILE_CHOOSER(toplevel
)) {
154 send_msg(GTK_BUILDABLE(toplevel
),
156 gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(toplevel
)), NULL
);
157 send_msg(GTK_BUILDABLE(toplevel
),
159 gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(toplevel
)), NULL
);
160 } /* responses of other dialogs go here */
165 * Callback that sends in a message the content of the text buffer
166 * passed in user_data
169 cb_send_text(GtkBuildable
*obj
, gpointer user_data
)
173 gtk_text_buffer_get_bounds(user_data
, &a
, &b
);
174 send_msg(obj
, "text", gtk_text_buffer_get_text(user_data
, &a
, &b
, FALSE
), NULL
);
178 * Callback that sends in a message the highlighted text from the text
179 * buffer which was passed in user_data
182 cb_send_text_selection(GtkBuildable
*obj
, gpointer user_data
)
186 gtk_text_buffer_get_selection_bounds(user_data
, &a
, &b
);
187 send_msg(obj
, "text", gtk_text_buffer_get_text(user_data
, &a
, &b
, FALSE
), NULL
);
190 struct selection_data
{
191 GtkBuildable
*buildable
;
196 * send_tree_row_msg serves as an argument for
197 * gtk_tree_selection_selected_foreach()
200 send_tree_row_msg(GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, struct selection_data
*data
)
207 obj
= (data
->buildable
);
208 section
= data
->section
;
209 path_s
= gtk_tree_path_to_string(path
);
210 for (col
= 0; col
< gtk_tree_model_get_n_columns(model
); col
++) {
211 GValue value
= G_VALUE_INIT
;
215 gtk_tree_model_get_value(model
, iter
, col
, &value
);
216 col_type
= gtk_tree_model_get_column_type(model
, col
);
219 snprintf(str
, BUFLEN
, " %d %d", col
, g_value_get_int(&value
));
220 send_msg(obj
, section
, path_s
, str
, NULL
);
223 snprintf(str
, BUFLEN
, " %d %ld", col
, g_value_get_long(&value
));
224 send_msg(obj
, section
, path_s
, str
, NULL
);
227 snprintf(str
, BUFLEN
, " %d %" PRId64
, col
, g_value_get_int64(&value
));
228 send_msg(obj
, section
, path_s
, str
, NULL
);
231 snprintf(str
, BUFLEN
, " %d %u", col
, g_value_get_uint(&value
));
232 send_msg(obj
, section
, path_s
, str
, NULL
);
235 snprintf(str
, BUFLEN
, " %d %lu", col
, g_value_get_ulong(&value
));
236 send_msg(obj
, section
, path_s
, str
, NULL
);
239 snprintf(str
, BUFLEN
, " %d %" PRIu64
, col
, g_value_get_uint64(&value
));
240 send_msg(obj
, section
, path_s
, str
, NULL
);
243 snprintf(str
, BUFLEN
, " %d %d", col
, g_value_get_boolean(&value
));
244 send_msg(obj
, section
, path_s
, str
, NULL
);
247 snprintf(str
, BUFLEN
, " %d %f", col
, g_value_get_float(&value
));
248 send_msg(obj
, section
, path_s
, str
, NULL
);
251 snprintf(str
, BUFLEN
, " %d %f", col
, g_value_get_double(&value
));
252 send_msg(obj
, section
, path_s
, str
, NULL
);
255 snprintf(str
, BUFLEN
, " %d ", col
);
256 send_msg(obj
, section
, path_s
, str
, g_value_get_string(&value
), NULL
);
259 fprintf(stderr
, "Column %d not implemented: %s\n", col
, G_VALUE_TYPE_NAME(&value
));
262 g_value_unset(&value
);
268 * cb_0(), cb_1(), etc. call this function to do their work: Send
269 * message(s) whose nature depends on the arguments passed. A call to
270 * this function will also be initiated by the user command
274 do_callback(GtkBuildable
*obj
, gpointer user_data
, const char *section
)
277 const char *item_name
;
279 GtkTreeView
*tree_view
;
280 unsigned int year
= 0, month
= 0, day
= 0;
282 if (GTK_IS_ENTRY(obj
)) {
283 send_msg(obj
, section
, gtk_entry_get_text(GTK_ENTRY(obj
)), NULL
);
284 } else if (GTK_IS_MENU_ITEM(obj
)) {
285 item_name
= gtk_buildable_get_name(obj
);
286 strncpy(str
, item_name
, BUFLEN
- 1);
287 strncpy(str
+ strlen(item_name
), "_dialog", BUFLEN
- 1 - strlen(item_name
));
288 if (gtk_builder_get_object(user_data
, str
) != NULL
)
289 gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(user_data
, str
)));
291 send_msg(obj
, section
, gtk_menu_item_get_label(GTK_MENU_ITEM(obj
)), NULL
);
292 } else if (GTK_IS_COMBO_BOX_TEXT(obj
))
293 send_msg(obj
, section
, gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(obj
)), NULL
);
294 else if (GTK_IS_RANGE(obj
)) {
295 snprintf(str
, BUFLEN
, "%f", gtk_range_get_value(GTK_RANGE(obj
)));
296 send_msg(obj
, section
, str
, NULL
);
297 } else if (GTK_IS_TOGGLE_BUTTON(obj
))
298 send_msg(obj
, section
, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(obj
))?"on":"off", NULL
);
299 else if (GTK_IS_COLOR_BUTTON(obj
)) {
300 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(obj
), &color
);
301 send_msg(obj
, section
, gdk_rgba_to_string(&color
), NULL
);
302 } else if (GTK_IS_FONT_BUTTON(obj
))
303 send_msg(obj
, section
, gtk_font_button_get_font_name(GTK_FONT_BUTTON(obj
)), NULL
);
304 else if (GTK_IS_FILE_CHOOSER_BUTTON(obj
))
305 send_msg(obj
, section
, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(obj
)), NULL
);
306 else if (GTK_IS_BUTTON(obj
))
307 send_msg(obj
, section
, "clicked", NULL
);
308 else if (GTK_IS_CALENDAR(obj
)) {
309 gtk_calendar_get_date(GTK_CALENDAR(obj
), &year
, &month
, &day
);
310 snprintf(str
, BUFLEN
, "%04u-%02u-%02u", year
, ++month
, day
);
311 send_msg(obj
, section
, str
, NULL
);
312 } else if (GTK_IS_TREE_VIEW_COLUMN(obj
))
313 send_msg(obj
, section
, "clicked", NULL
);
314 else if (GTK_IS_TREE_SELECTION(obj
)) {
315 tree_view
= gtk_tree_selection_get_tree_view(GTK_TREE_SELECTION(obj
));
316 send_msg(GTK_BUILDABLE(tree_view
), section
, "clicked", NULL
);
317 gtk_tree_selection_selected_foreach(
318 GTK_TREE_SELECTION(obj
),
319 (GtkTreeSelectionForeachFunc
)send_tree_row_msg
,
320 &(struct selection_data
){GTK_BUILDABLE(tree_view
),
322 } else if (GTK_IS_WINDOW(obj
))
323 gtk_widget_hide_on_delete(GTK_WIDGET(obj
));
325 fprintf(stderr
, "Ignoring callback %s from %s\n", section
, gtk_buildable_get_name(obj
));
329 * Callback functions cb_0(), cb_1(), cb_2(), and cb_3() for use in
330 * simple widgets. They're all identical except for sending different
333 #define MAKE_CB_N(n) \
334 void cb_##n(GtkBuildable *obj, gpointer user_data) \
335 {do_callback(obj, user_data, #n);} \
345 * Store a line from stream into buf, which should have been malloc'd
346 * to bufsize. Enlarge buf and bufsize if necessary.
349 read_buf(FILE *stream
, char **buf
, size_t *bufsize
)
358 nanosleep(&(struct timespec
){0, 1e7
}, NULL
);
363 if (i
>= *bufsize
- 1)
364 if ((*buf
= realloc(*buf
, *bufsize
= *bufsize
* 2)) == NULL
) {
365 fprintf(stderr
, "Out of memory (%s in %s)\n", __func__
, __FILE__
);
369 switch (c
= getc(stream
)) {
370 case 'n': (*buf
)[i
++] = '\n'; break;
371 case 'r': (*buf
)[i
++] = '\r'; break;
372 default: (*buf
)[i
++] = c
; break;
385 ign_cmd(GType type
, const char *msg
)
387 const char *name
, *pad
= " ";
389 if (type
== G_TYPE_INVALID
) {
394 name
= g_type_name(type
);
395 fprintf(stderr
, "Ignoring %s%scommand \"%s\"\n", name
, pad
, msg
);
399 * Parse command pointed to by ud, and act on ui accordingly. Sets
400 * ud->digested = true if done. Runs once per command inside
404 update_ui(struct ui_data
*ud
)
406 char name
[ud
->msg_size
], action
[ud
->msg_size
];
410 GType type
= G_TYPE_INVALID
;
412 data_start
= strlen(ud
->msg
);
413 name
[0] = action
[0] = '\0';
415 " %[0-9a-zA-Z_]:%[0-9a-zA-Z_]%*[ \t] %n",
416 name
, action
, &data_start
);
417 if (eql(action
, "main_quit")) {
419 ud
->msg_digested
= true;
420 return G_SOURCE_REMOVE
;
422 obj
= (gtk_builder_get_object(ud
->builder
, name
));
424 ign_cmd(type
, ud
->msg
);
425 ud
->msg_digested
= true;
426 return G_SOURCE_REMOVE
;
428 if (eql(action
, "force_cb")) {
429 do_callback(GTK_BUILDABLE(obj
), NULL
, "forced");
430 ud
->msg_digested
= true;
431 return G_SOURCE_REMOVE
;
433 data
= ud
->msg
+ data_start
;
434 if (eql(action
, "set_sensitive")) {
435 gtk_widget_set_sensitive(GTK_WIDGET(obj
), strtol(data
, NULL
, 10));
436 ud
->msg_digested
= true;
437 return G_SOURCE_REMOVE
;
438 } else if (eql(action
, "set_visible")) {
439 gtk_widget_set_visible(GTK_WIDGET(obj
), strtol(data
, NULL
, 10));
440 ud
->msg_digested
= true;
441 return G_SOURCE_REMOVE
;
443 type
= G_TYPE_FROM_INSTANCE(obj
);
444 if (type
== GTK_TYPE_LABEL
) {
445 if (eql(action
, "set_text"))
446 gtk_label_set_text(GTK_LABEL(obj
), data
);
448 ign_cmd(type
, ud
->msg
);
449 } else if (type
== GTK_TYPE_IMAGE
) {
452 gtk_image_get_icon_name(GTK_IMAGE(obj
), NULL
, &size
);
453 if (eql(action
, "set_from_file"))
454 gtk_image_set_from_file(GTK_IMAGE(obj
), data
);
455 else if (eql(action
, "set_from_icon_name"))
456 gtk_image_set_from_icon_name(GTK_IMAGE(obj
), data
, size
);
458 ign_cmd(type
, ud
->msg
);
459 } else if (type
== GTK_TYPE_TEXT_VIEW
) {
460 GtkTextBuffer
*textbuf
;
461 GtkTextIter iter
, a
, b
;
463 textbuf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(obj
));
464 if (eql(action
, "set_text"))
465 gtk_text_buffer_set_text(textbuf
, data
, -1);
466 else if (eql(action
, "delete")) {
467 gtk_text_buffer_get_bounds(textbuf
, &a
, &b
);
468 gtk_text_buffer_delete(textbuf
, &a
, &b
);
469 } else if (eql(action
, "insert_at_cursor"))
470 gtk_text_buffer_insert_at_cursor(textbuf
, data
, -1);
471 else if (eql(action
, "place_cursor")) {
472 if (eql(data
, "end"))
473 gtk_text_buffer_get_end_iter(textbuf
, &iter
);
474 else /* numeric offset */
475 gtk_text_buffer_get_iter_at_offset(textbuf
, &iter
, strtol(data
, NULL
, 10));
476 gtk_text_buffer_place_cursor(textbuf
, &iter
);
477 } else if (eql(action
, "place_cursor_at_line")) {
478 gtk_text_buffer_get_iter_at_line(textbuf
, &iter
, strtol(data
, NULL
, 10));
479 gtk_text_buffer_place_cursor(textbuf
, &iter
);
480 } else if (eql(action
, "scroll_to_cursor"))
481 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(obj
), gtk_text_buffer_get_insert(textbuf
), 0.0, 0, 0.0, 0.0);
483 ign_cmd(type
, ud
->msg
);
484 } else if (type
== GTK_TYPE_BUTTON
) {
485 if (eql(action
, "set_label"))
486 gtk_button_set_label(GTK_BUTTON(obj
), data
);
488 ign_cmd(type
, ud
->msg
);
489 } else if (type
== GTK_TYPE_FILE_CHOOSER_DIALOG
) {
490 if (eql(action
, "set_filename"))
491 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(obj
), data
);
492 else if (eql(action
, "set_current_name"))
493 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(obj
), data
);
495 ign_cmd(type
, ud
->msg
);
496 } else if (type
== GTK_TYPE_FILE_CHOOSER_BUTTON
) {
497 if (eql(action
, "set_filename"))
498 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(obj
), data
);
500 ign_cmd(type
, ud
->msg
);
501 } else if (type
== GTK_TYPE_COLOR_BUTTON
) {
502 if (eql(action
, "set_color")) {
505 gdk_rgba_parse(&color
, data
);
506 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(obj
), &color
);
508 ign_cmd(type
, ud
->msg
);
509 } else if (type
== GTK_TYPE_FONT_BUTTON
) {
510 if (eql(action
, "set_font_name"))
511 gtk_font_button_set_font_name(GTK_FONT_BUTTON(obj
), data
);
513 ign_cmd(type
, ud
->msg
);
514 } else if (type
== GTK_TYPE_TOGGLE_BUTTON
|| type
== GTK_TYPE_RADIO_BUTTON
|| type
== GTK_TYPE_CHECK_BUTTON
) {
515 if (eql(action
, "set_label"))
516 gtk_button_set_label(GTK_BUTTON(obj
), data
);
517 else if (eql(action
, "set_active"))
518 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(obj
), strtol(data
, NULL
, 10));
520 ign_cmd(type
, ud
->msg
);
521 } else if (type
== GTK_TYPE_SPIN_BUTTON
|| type
== GTK_TYPE_ENTRY
) {
522 if (eql(action
, "set_text"))
523 gtk_entry_set_text(GTK_ENTRY(obj
), data
);
525 ign_cmd(type
, ud
->msg
);
526 } else if (type
== GTK_TYPE_SCALE
) {
527 if (eql(action
, "set_value"))
528 gtk_range_set_value(GTK_RANGE(obj
), strtod(data
, NULL
));
530 ign_cmd(type
, ud
->msg
);
531 } else if (type
== GTK_TYPE_PROGRESS_BAR
) {
532 if (eql(action
, "set_text"))
533 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(obj
), data
);
534 else if (eql(action
, "set_fraction"))
535 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(obj
), strtod(data
, NULL
));
537 ign_cmd(type
, ud
->msg
);
538 } else if (type
== GTK_TYPE_SPINNER
) {
539 if (eql(action
, "start"))
540 gtk_spinner_start(GTK_SPINNER(obj
));
541 else if (eql(action
, "stop"))
542 gtk_spinner_stop(GTK_SPINNER(obj
));
544 ign_cmd(type
, ud
->msg
);
545 } else if (type
== GTK_TYPE_COMBO_BOX_TEXT
) {
546 if (eql(action
, "prepend_text"))
547 gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(obj
), data
);
548 else if (eql(action
, "append_text"))
549 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(obj
), data
);
550 else if (eql(action
, "remove"))
551 gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(obj
), strtol(data
, NULL
, 10));
552 else if (eql(action
, "insert_text")) {
553 char *position
, *text
;
555 position
= strtok(data
, WHITESPACE
);
556 text
= strtok(NULL
, WHITESPACE
);
557 gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(obj
), strtol(position
, NULL
, 10), text
);
559 ign_cmd(type
, ud
->msg
);
560 } else if (type
== GTK_TYPE_STATUSBAR
) {
561 if (eql(action
, "push"))
562 gtk_statusbar_push(GTK_STATUSBAR(obj
), 0, data
);
563 else if (eql(action
, "pop"))
564 gtk_statusbar_pop(GTK_STATUSBAR(obj
), 0);
566 ign_cmd(type
, ud
->msg
);
567 } else if (type
== GTK_TYPE_CALENDAR
) {
568 int year
= 0, month
= 0, day
= 0;
570 if (eql(action
, "select_date")) {
571 sscanf(data
, "%d-%d-%d", &year
, &month
, &day
);
572 if (month
> -1 && month
<= 11 && day
> 0 && day
<= 31) {
573 gtk_calendar_select_month(GTK_CALENDAR(obj
), --month
, year
);
574 gtk_calendar_select_day(GTK_CALENDAR(obj
), day
);
576 ign_cmd(type
, ud
->msg
);
577 } else if (eql(action
, "mark_day")) {
578 day
= strtol(data
, NULL
, 10);
579 if (day
> 0 && day
<= 31)
580 gtk_calendar_mark_day(GTK_CALENDAR(obj
), day
);
582 ign_cmd(type
, ud
->msg
);
583 } else if (eql(action
, "clear_marks"))
584 gtk_calendar_clear_marks(GTK_CALENDAR(obj
));
586 ign_cmd(type
, ud
->msg
);
587 } else if (type
== GTK_TYPE_TREE_VIEW
) {
588 GtkTreeIter iter0
, iter1
;
592 char *tokens
, *arg0_s
, *arg1_s
, *arg2_s
, *endptr
;
593 int arg0_n
= 0, arg1_n
= 0;
594 bool arg0_n_valid
= false, arg1_n_valid
= false;
595 bool iter0_valid
= false, iter1_valid
= false;
597 view
= GTK_TREE_VIEW(obj
);
598 model
= gtk_tree_view_get_model(view
);
599 store
= GTK_LIST_STORE(model
);
600 if ((tokens
= malloc(strlen(data
) + 1)) == NULL
) {
601 fprintf(stderr
, "Out of memory (%s in %s)\n", __func__
, __FILE__
);
604 strcpy(tokens
, data
);
605 arg0_s
= strtok(tokens
, WHITESPACE
);
606 arg1_s
= strtok(NULL
, WHITESPACE
);
607 arg2_s
= strtok(NULL
, "\n");
612 (arg0_n_valid
= (arg0_n
= strtol(arg0_s
, &endptr
, 10)) >= 0 &&
613 errno
== 0 && endptr
!= arg0_s
) &&
614 gtk_tree_model_get_iter_from_string(model
, &iter0
, arg0_s
);
619 (arg1_n_valid
= (arg1_n
= strtol(arg1_s
, &endptr
, 10)) >= 0 &&
620 errno
== 0 && endptr
!= arg1_s
) &&
621 gtk_tree_model_get_iter_from_string(model
, &iter1
, arg1_s
);
622 if (eql(action
, "set") && iter0_valid
&& arg1_n_valid
&&
623 arg1_n
< gtk_tree_model_get_n_columns(model
)) {
628 col_type
= gtk_tree_model_get_column_type(model
, arg1_n
);
639 n
= strtoll(arg2_s
, &endptr
, 10);
640 if (!errno
&& endptr
!= arg2_s
)
641 gtk_list_store_set(store
, &iter0
, arg1_n
, n
, -1);
643 ign_cmd(type
, ud
->msg
);
649 d
= strtod(arg2_s
, &endptr
);
650 if (!errno
&& endptr
!= arg2_s
)
651 gtk_list_store_set(store
, &iter0
, arg1_n
, d
, -1);
653 ign_cmd(type
, ud
->msg
);
654 gtk_list_store_set(store
, &iter0
, arg1_n
, strtod(arg2_s
, NULL
), -1);
657 gtk_list_store_set(store
, &iter0
, arg1_n
, arg2_s
, -1);
660 fprintf(stderr
, "Column %d: %s not implemented\n", arg1_n
, g_type_name(col_type
));
663 } else if (eql(action
, "scroll") && arg0_n_valid
&& arg1_n_valid
)
664 gtk_tree_view_scroll_to_cell (view
,
665 gtk_tree_path_new_from_string(arg0_s
),
666 gtk_tree_view_get_column(view
, arg1_n
),
668 else if (eql(action
, "insert_row"))
669 if (eql(arg0_s
, "end"))
670 gtk_list_store_insert_before(store
, &iter1
, NULL
);
671 else if (iter0_valid
)
672 gtk_list_store_insert_before(store
, &iter1
, &iter0
);
674 ign_cmd(type
, ud
->msg
);
675 else if (eql(action
, "move_row") && iter0_valid
)
676 if (eql(arg1_s
, "end"))
677 gtk_list_store_move_before(store
, &iter0
, NULL
);
678 else if (iter1_valid
)
679 gtk_list_store_move_before(store
, &iter0
, &iter1
);
681 ign_cmd(type
, ud
->msg
);
682 else if (eql(action
, "remove_row") && iter0_valid
)
683 gtk_list_store_remove(store
, &iter0
);
685 ign_cmd(type
, ud
->msg
);
687 } else if (type
== GTK_TYPE_WINDOW
) {
688 if (eql(action
, "set_title"))
689 gtk_window_set_title(GTK_WINDOW(obj
), data
);
691 ign_cmd(type
, ud
->msg
);
693 ign_cmd(type
, ud
->msg
);
694 ud
->msg_digested
= true;
695 return G_SOURCE_REMOVE
;
699 * Read lines from global stream "in" and perform the appropriate
703 digest_msg(void *builder
)
709 if ((ud
.msg
= malloc(ud
.msg_size
= 32)) == NULL
) {
710 fprintf(stderr
, "Out of memory (%s in %s)\n", __func__
, __FILE__
);
713 read_buf(in
, &ud
.msg
, &ud
.msg_size
);
714 sscanf(ud
.msg
, " %c", &first_char
);
715 ud
.builder
= builder
;
716 if (first_char
!= '#') {
717 ud
.msg_digested
= false;
718 gdk_threads_add_timeout(1, (GSourceFunc
)update_ui
, &ud
);
719 while (!ud
.msg_digested
)
720 nanosleep(&(struct timespec
){0, 1e6
}, NULL
);
728 * Create a fifo if necessary, and open it. Give up if the file
729 * exists but is not a fifo
732 fifo(const char *name
, const char *mode
)
739 if (!S_ISFIFO(sb
.st_mode
))
740 if (name
!= NULL
&& mkfifo(name
, 0666) != 0) {
741 perror("making fifo");
749 fd
= open(name
, O_RDWR
| O_NONBLOCK
);
751 perror("opening fifo");
754 stream
= fdopen(fd
, "r");
761 /* fopen blocks if there is no reader so here is one */
762 fd
= open(name
, O_RDONLY
| O_NONBLOCK
);
764 perror("opening fifo");
767 stream
= fopen(name
, "w");
768 /* unblocking done */
776 if (stream
== NULL
) {
777 perror("opening fifo");
780 setvbuf(stream
, NULL
, _IOLBF
, 0);
786 main(int argc
, char *argv
[])
788 char opt
, *ui_file
= NULL
;
789 char *in_fifo
= NULL
, *out_fifo
= NULL
;
792 GError
*error
= NULL
;
793 GObject
*window
= NULL
;
797 while ((opt
= getopt(argc
, argv
, "hi:o:u:GV")) != -1) {
799 case 'i': in_fifo
= optarg
; break;
800 case 'o': out_fifo
= optarg
; break;
801 case 'u': ui_file
= optarg
; break;
802 case 'G': gtk_versions(); break;
803 case 'V': version(); break;
806 default: usage(argv
); break;
809 if (argv
[optind
] != NULL
) {
810 fprintf(stderr
, "illegal parameter '%s'\n", argv
[optind
]);
814 ui_file
= "pipeglade.ui";
816 builder
= gtk_builder_new();
817 if (gtk_builder_add_from_file(builder
, ui_file
, &error
) == 0) {
818 fprintf(stderr
, "%s\n", error
->message
);
821 in
= fifo(in_fifo
, "r");
822 out
= fifo(out_fifo
, "w");
823 gtk_builder_connect_signals(builder
, builder
);
824 pthread_create(&receiver
, NULL
, digest_msg
, (void*) builder
);
825 window
= (gtk_builder_get_object(builder
, "window"));
826 if (!GTK_IS_WIDGET(window
)) {
827 fprintf(stderr
, "No toplevel window named \'window\'\n");
830 gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder
, "window")));
840 pthread_cancel(receiver
);
841 pthread_join(receiver
, NULL
);