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 "2.1.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
));
139 if (gtk_widget_is_toplevel(toplevel
))
140 gtk_widget_hide(toplevel
);
144 * Callback that sends a message describing the user selection of a
148 cb_send_dialog_selection(GtkBuildable
*obj
, gpointer user_data
)
150 GtkWidget
*toplevel
= gtk_widget_get_toplevel(GTK_WIDGET(obj
));
153 if (gtk_widget_is_toplevel(toplevel
))
155 if (GTK_IS_FILE_CHOOSER(toplevel
)) {
156 send_msg(GTK_BUILDABLE(toplevel
),
158 gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(toplevel
)), NULL
);
159 send_msg(GTK_BUILDABLE(toplevel
),
161 gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(toplevel
)), NULL
);
162 } /* responses of other dialogs go here */
167 * Callback that sends in a message the content of the text buffer
168 * passed in user_data
171 cb_send_text(GtkBuildable
*obj
, gpointer user_data
)
175 gtk_text_buffer_get_bounds(user_data
, &a
, &b
);
176 send_msg(obj
, "text", gtk_text_buffer_get_text(user_data
, &a
, &b
, FALSE
), NULL
);
180 * Callback that sends in a message the highlighted text from the text
181 * buffer which was passed in user_data
184 cb_send_text_selection(GtkBuildable
*obj
, gpointer user_data
)
188 gtk_text_buffer_get_selection_bounds(user_data
, &a
, &b
);
189 send_msg(obj
, "text", gtk_text_buffer_get_text(user_data
, &a
, &b
, FALSE
), NULL
);
192 struct selection_data
{
193 GtkBuildable
*buildable
;
198 * send_tree_row_msg serves as an argument for
199 * gtk_tree_selection_selected_foreach()
202 send_tree_row_msg(GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, struct selection_data
*data
)
204 char *path_s
= gtk_tree_path_to_string(path
);
205 const char *section
= data
->section
;
206 GtkBuildable
*obj
= data
->buildable
;
209 for (col
= 0; col
< gtk_tree_model_get_n_columns(model
); col
++) {
210 GValue value
= G_VALUE_INIT
;
214 gtk_tree_model_get_value(model
, iter
, col
, &value
);
215 col_type
= gtk_tree_model_get_column_type(model
, col
);
218 snprintf(str
, BUFLEN
, " %d %d", col
, g_value_get_int(&value
));
219 send_msg(obj
, section
, path_s
, str
, NULL
);
222 snprintf(str
, BUFLEN
, " %d %ld", col
, g_value_get_long(&value
));
223 send_msg(obj
, section
, path_s
, str
, NULL
);
226 snprintf(str
, BUFLEN
, " %d %" PRId64
, col
, g_value_get_int64(&value
));
227 send_msg(obj
, section
, path_s
, str
, NULL
);
230 snprintf(str
, BUFLEN
, " %d %u", col
, g_value_get_uint(&value
));
231 send_msg(obj
, section
, path_s
, str
, NULL
);
234 snprintf(str
, BUFLEN
, " %d %lu", col
, g_value_get_ulong(&value
));
235 send_msg(obj
, section
, path_s
, str
, NULL
);
238 snprintf(str
, BUFLEN
, " %d %" PRIu64
, col
, g_value_get_uint64(&value
));
239 send_msg(obj
, section
, path_s
, str
, NULL
);
242 snprintf(str
, BUFLEN
, " %d %d", col
, g_value_get_boolean(&value
));
243 send_msg(obj
, section
, path_s
, str
, NULL
);
246 snprintf(str
, BUFLEN
, " %d %f", col
, g_value_get_float(&value
));
247 send_msg(obj
, section
, path_s
, str
, NULL
);
250 snprintf(str
, BUFLEN
, " %d %f", col
, g_value_get_double(&value
));
251 send_msg(obj
, section
, path_s
, str
, NULL
);
254 snprintf(str
, BUFLEN
, " %d ", col
);
255 send_msg(obj
, section
, path_s
, str
, g_value_get_string(&value
), NULL
);
258 fprintf(stderr
, "column %d not implemented: %s\n", col
, G_VALUE_TYPE_NAME(&value
));
261 g_value_unset(&value
);
267 * cb_0(), cb_1(), etc. call this function to do their work: Send
268 * message(s) whose nature depends on the arguments passed. A call to
269 * this function will also be initiated by the user command
273 do_callback(GtkBuildable
*obj
, gpointer user_data
, const char *section
)
276 const char *item_name
;
278 GtkTreeView
*tree_view
;
279 unsigned int year
= 0, month
= 0, day
= 0;
281 if (GTK_IS_ENTRY(obj
)) {
282 send_msg(obj
, section
, gtk_entry_get_text(GTK_ENTRY(obj
)), NULL
);
283 } else if (GTK_IS_MENU_ITEM(obj
)) {
284 item_name
= gtk_buildable_get_name(obj
);
285 strncpy(str
, item_name
, BUFLEN
- 1);
286 strncpy(str
+ strlen(item_name
), "_dialog", BUFLEN
- 1 - strlen(item_name
));
287 if (gtk_builder_get_object(user_data
, str
) != NULL
)
288 gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(user_data
, str
)));
290 send_msg(obj
, section
, gtk_menu_item_get_label(GTK_MENU_ITEM(obj
)), NULL
);
291 } else if (GTK_IS_COMBO_BOX_TEXT(obj
))
292 send_msg(obj
, section
, gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(obj
)), NULL
);
293 else if (GTK_IS_RANGE(obj
)) {
294 snprintf(str
, BUFLEN
, "%f", gtk_range_get_value(GTK_RANGE(obj
)));
295 send_msg(obj
, section
, str
, NULL
);
296 } else if (GTK_IS_SWITCH(obj
))
297 send_msg(obj
, section
, gtk_switch_get_active(GTK_SWITCH(obj
))?"1":"0", NULL
);
298 else if (GTK_IS_TOGGLE_BUTTON(obj
))
299 send_msg(obj
, section
, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(obj
))?"1":"0", NULL
);
300 else if (GTK_IS_COLOR_BUTTON(obj
)) {
301 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(obj
), &color
);
302 send_msg(obj
, section
, gdk_rgba_to_string(&color
), NULL
);
303 } else if (GTK_IS_FONT_BUTTON(obj
))
304 send_msg(obj
, section
, gtk_font_button_get_font_name(GTK_FONT_BUTTON(obj
)), NULL
);
305 else if (GTK_IS_FILE_CHOOSER_BUTTON(obj
))
306 send_msg(obj
, section
, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(obj
)), NULL
);
307 else if (GTK_IS_BUTTON(obj
))
308 send_msg(obj
, section
, "clicked", NULL
);
309 else if (GTK_IS_CALENDAR(obj
)) {
310 gtk_calendar_get_date(GTK_CALENDAR(obj
), &year
, &month
, &day
);
311 snprintf(str
, BUFLEN
, "%04u-%02u-%02u", year
, ++month
, day
);
312 send_msg(obj
, section
, str
, NULL
);
313 } else if (GTK_IS_TREE_VIEW_COLUMN(obj
))
314 send_msg(obj
, section
, "clicked", NULL
);
315 else if (GTK_IS_TREE_SELECTION(obj
)) {
316 tree_view
= gtk_tree_selection_get_tree_view(GTK_TREE_SELECTION(obj
));
317 send_msg(GTK_BUILDABLE(tree_view
), section
, "clicked", NULL
);
318 gtk_tree_selection_selected_foreach(
319 GTK_TREE_SELECTION(obj
),
320 (GtkTreeSelectionForeachFunc
)send_tree_row_msg
,
321 &(struct selection_data
){GTK_BUILDABLE(tree_view
),
323 } else if (GTK_IS_WINDOW(obj
))
324 gtk_widget_hide_on_delete(GTK_WIDGET(obj
));
326 fprintf(stderr
, "ignoring callback %s from %s\n", section
, gtk_buildable_get_name(obj
));
330 * Callback functions cb_0(), cb_1(), cb_2(), and cb_3() for use in
331 * simple widgets. They're all identical except for sending different
334 #define MAKE_CB_N(n) \
335 void cb_##n(GtkBuildable *obj, gpointer user_data) \
336 {do_callback(obj, user_data, #n);} \
346 * Store a line from stream into buf, which should have been malloc'd
347 * to bufsize. Enlarge buf and bufsize if necessary.
350 read_buf(FILE *stream
, char **buf
, size_t *bufsize
)
359 nanosleep(&(struct timespec
){0, 1e7
}, NULL
);
364 if (i
>= *bufsize
- 1)
365 if ((*buf
= realloc(*buf
, *bufsize
= *bufsize
* 2)) == NULL
) {
366 fprintf(stderr
, "out of memory (%s in %s)\n", __func__
, __FILE__
);
370 switch (c
= getc(stream
)) {
371 case 'n': (*buf
)[i
++] = '\n'; break;
372 case 'r': (*buf
)[i
++] = '\r'; break;
373 default: (*buf
)[i
++] = c
; break;
386 ign_cmd(GType type
, const char *msg
)
388 const char *name
, *pad
= " ";
390 if (type
== G_TYPE_INVALID
) {
395 name
= g_type_name(type
);
396 fprintf(stderr
, "ignoring %s%scommand \"%s\"\n", name
, pad
, msg
);
400 * Parse command pointed to by ud, and act on ui accordingly. Set
401 * ud->digested = true if done. Runs once per command inside
405 update_ui(struct ui_data
*ud
)
407 char name
[ud
->msg_size
], action
[ud
->msg_size
];
409 int data_start
= strlen(ud
->msg
);
411 GType type
= G_TYPE_INVALID
;
414 name
[0] = action
[0] = '\0';
416 " %[0-9a-zA-Z_]:%[0-9a-zA-Z_]%*[ \t] %n",
417 name
, action
, &data_start
);
418 if (eql(action
, "main_quit")) {
422 obj
= (gtk_builder_get_object(ud
->builder
, name
));
424 ign_cmd(type
, ud
->msg
);
427 if (eql(action
, "force_cb")) {
428 do_callback(GTK_BUILDABLE(obj
), NULL
, "forced");
431 data
= ud
->msg
+ data_start
;
432 if (eql(action
, "set_sensitive")) {
433 gtk_widget_set_sensitive(GTK_WIDGET(obj
), strtol(data
, NULL
, 10));
435 } else if (eql(action
, "set_visible")) {
436 gtk_widget_set_visible(GTK_WIDGET(obj
), strtol(data
, NULL
, 10));
438 } else if (eql(action
, "override_font")) {
440 PangoFontDescription
*font
= pango_font_description_from_string(data
);
442 gtk_widget_override_font(GTK_WIDGET(obj
), font
);
443 pango_font_description_free(font
);
445 gtk_widget_override_font(GTK_WIDGET(obj
), NULL
);
447 } else if (eql(action
, "override_color")) {
449 gdk_rgba_parse(&color
, data
);
450 gtk_widget_override_color(GTK_WIDGET(obj
), GTK_STATE_FLAG_NORMAL
, &color
);
452 gtk_widget_override_color(GTK_WIDGET(obj
), GTK_STATE_FLAG_NORMAL
, NULL
);
454 } else if (eql(action
, "override_background_color")) {
456 gdk_rgba_parse(&color
, data
);
457 gtk_widget_override_background_color(GTK_WIDGET(obj
), GTK_STATE_FLAG_NORMAL
, &color
);
459 gtk_widget_override_background_color(GTK_WIDGET(obj
), GTK_STATE_FLAG_NORMAL
, NULL
);
462 type
= G_TYPE_FROM_INSTANCE(obj
);
463 if (type
== GTK_TYPE_LABEL
) {
464 if (eql(action
, "set_text"))
465 gtk_label_set_text(GTK_LABEL(obj
), data
);
467 ign_cmd(type
, ud
->msg
);
468 } else if (type
== GTK_TYPE_IMAGE
) {
471 gtk_image_get_icon_name(GTK_IMAGE(obj
), NULL
, &size
);
472 if (eql(action
, "set_from_file"))
473 gtk_image_set_from_file(GTK_IMAGE(obj
), data
);
474 else if (eql(action
, "set_from_icon_name"))
475 gtk_image_set_from_icon_name(GTK_IMAGE(obj
), data
, size
);
477 ign_cmd(type
, ud
->msg
);
478 } else if (type
== GTK_TYPE_TEXT_VIEW
) {
479 GtkTextBuffer
*textbuf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(obj
));
482 if (eql(action
, "set_text"))
483 gtk_text_buffer_set_text(textbuf
, data
, -1);
484 else if (eql(action
, "delete")) {
485 gtk_text_buffer_get_bounds(textbuf
, &a
, &b
);
486 gtk_text_buffer_delete(textbuf
, &a
, &b
);
487 } else if (eql(action
, "insert_at_cursor"))
488 gtk_text_buffer_insert_at_cursor(textbuf
, data
, -1);
489 else if (eql(action
, "place_cursor")) {
490 if (eql(data
, "end"))
491 gtk_text_buffer_get_end_iter(textbuf
, &a
);
492 else /* numeric offset */
493 gtk_text_buffer_get_iter_at_offset(textbuf
, &a
, strtol(data
, NULL
, 10));
494 gtk_text_buffer_place_cursor(textbuf
, &a
);
495 } else if (eql(action
, "place_cursor_at_line")) {
496 gtk_text_buffer_get_iter_at_line(textbuf
, &a
, strtol(data
, NULL
, 10));
497 gtk_text_buffer_place_cursor(textbuf
, &a
);
498 } else if (eql(action
, "scroll_to_cursor"))
499 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(obj
), gtk_text_buffer_get_insert(textbuf
), 0., 0, 0., 0.);
501 ign_cmd(type
, ud
->msg
);
502 } else if (type
== GTK_TYPE_NOTEBOOK
) {
503 if (eql(action
, "set_current_page"))
504 gtk_notebook_set_current_page(GTK_NOTEBOOK(obj
), strtol(data
, NULL
, 10));
506 ign_cmd(type
, ud
->msg
);
507 } else if (type
== GTK_TYPE_EXPANDER
) {
508 if (eql(action
, "set_expanded"))
509 gtk_expander_set_expanded(GTK_EXPANDER(obj
), strtol(data
, NULL
, 10));
510 else if (eql(action
, "set_label"))
511 gtk_expander_set_label(GTK_EXPANDER(obj
), data
);
513 ign_cmd(type
, ud
->msg
);
514 } else if (type
== GTK_TYPE_FRAME
) {
515 if (eql(action
, "set_label"))
516 gtk_frame_set_label(GTK_FRAME(obj
), data
);
518 ign_cmd(type
, ud
->msg
);
519 } else if (type
== GTK_TYPE_BUTTON
) {
520 if (eql(action
, "set_label"))
521 gtk_button_set_label(GTK_BUTTON(obj
), data
);
523 ign_cmd(type
, ud
->msg
);
524 } else if (type
== GTK_TYPE_FILE_CHOOSER_DIALOG
) {
525 if (eql(action
, "set_filename"))
526 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(obj
), data
);
527 else if (eql(action
, "set_current_name"))
528 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(obj
), data
);
530 ign_cmd(type
, ud
->msg
);
531 } else if (type
== GTK_TYPE_FILE_CHOOSER_BUTTON
) {
532 if (eql(action
, "set_filename"))
533 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(obj
), data
);
535 ign_cmd(type
, ud
->msg
);
536 } else if (type
== GTK_TYPE_COLOR_BUTTON
) {
537 if (eql(action
, "set_color")) {
538 gdk_rgba_parse(&color
, data
);
539 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(obj
), &color
);
541 ign_cmd(type
, ud
->msg
);
542 } else if (type
== GTK_TYPE_FONT_BUTTON
) {
543 if (eql(action
, "set_font_name"))
544 gtk_font_button_set_font_name(GTK_FONT_BUTTON(obj
), data
);
546 ign_cmd(type
, ud
->msg
);
547 } else if (type
== GTK_TYPE_SWITCH
) {
548 if (eql(action
, "set_active"))
549 gtk_switch_set_active(GTK_SWITCH(obj
), strtol(data
, NULL
, 10));
551 ign_cmd(type
, ud
->msg
);
552 } else if (type
== GTK_TYPE_TOGGLE_BUTTON
|| type
== GTK_TYPE_RADIO_BUTTON
|| type
== GTK_TYPE_CHECK_BUTTON
) {
553 if (eql(action
, "set_label"))
554 gtk_button_set_label(GTK_BUTTON(obj
), data
);
555 else if (eql(action
, "set_active"))
556 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(obj
), strtol(data
, NULL
, 10));
558 ign_cmd(type
, ud
->msg
);
559 } else if (type
== GTK_TYPE_SPIN_BUTTON
|| type
== GTK_TYPE_ENTRY
) {
560 if (eql(action
, "set_text"))
561 gtk_entry_set_text(GTK_ENTRY(obj
), data
);
563 ign_cmd(type
, ud
->msg
);
564 } else if (type
== GTK_TYPE_SCALE
) {
565 if (eql(action
, "set_value"))
566 gtk_range_set_value(GTK_RANGE(obj
), strtod(data
, NULL
));
568 ign_cmd(type
, ud
->msg
);
569 } else if (type
== GTK_TYPE_PROGRESS_BAR
) {
570 if (eql(action
, "set_text"))
571 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(obj
), data
);
572 else if (eql(action
, "set_fraction"))
573 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(obj
), strtod(data
, NULL
));
575 ign_cmd(type
, ud
->msg
);
576 } else if (type
== GTK_TYPE_SPINNER
) {
577 if (eql(action
, "start"))
578 gtk_spinner_start(GTK_SPINNER(obj
));
579 else if (eql(action
, "stop"))
580 gtk_spinner_stop(GTK_SPINNER(obj
));
582 ign_cmd(type
, ud
->msg
);
583 } else if (type
== GTK_TYPE_COMBO_BOX_TEXT
) {
584 if (eql(action
, "prepend_text"))
585 gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(obj
), data
);
586 else if (eql(action
, "append_text"))
587 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(obj
), data
);
588 else if (eql(action
, "remove"))
589 gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(obj
), strtol(data
, NULL
, 10));
590 else if (eql(action
, "insert_text")) {
591 char *position
= strtok(data
, WHITESPACE
);
592 char *text
= strtok(NULL
, WHITESPACE
);
594 gtk_combo_box_text_insert_text(GTK_COMBO_BOX_TEXT(obj
), strtol(position
, NULL
, 10), text
);
596 ign_cmd(type
, ud
->msg
);
597 } else if (type
== GTK_TYPE_STATUSBAR
) {
598 if (eql(action
, "push"))
599 gtk_statusbar_push(GTK_STATUSBAR(obj
), 0, data
);
600 else if (eql(action
, "pop"))
601 gtk_statusbar_pop(GTK_STATUSBAR(obj
), 0);
603 ign_cmd(type
, ud
->msg
);
604 } else if (type
== GTK_TYPE_CALENDAR
) {
605 int year
= 0, month
= 0, day
= 0;
607 if (eql(action
, "select_date")) {
608 sscanf(data
, "%d-%d-%d", &year
, &month
, &day
);
609 if (month
> -1 && month
<= 11 && day
> 0 && day
<= 31) {
610 gtk_calendar_select_month(GTK_CALENDAR(obj
), --month
, year
);
611 gtk_calendar_select_day(GTK_CALENDAR(obj
), day
);
613 ign_cmd(type
, ud
->msg
);
614 } else if (eql(action
, "mark_day")) {
615 day
= strtol(data
, NULL
, 10);
616 if (day
> 0 && day
<= 31)
617 gtk_calendar_mark_day(GTK_CALENDAR(obj
), day
);
619 ign_cmd(type
, ud
->msg
);
620 } else if (eql(action
, "clear_marks"))
621 gtk_calendar_clear_marks(GTK_CALENDAR(obj
));
623 ign_cmd(type
, ud
->msg
);
624 } else if (type
== GTK_TYPE_TREE_VIEW
) {
625 GtkTreeView
*view
= GTK_TREE_VIEW(obj
);
626 GtkTreeModel
*model
= gtk_tree_view_get_model(view
);
627 GtkListStore
*store
= GTK_LIST_STORE(model
);
628 GtkTreeIter iter0
, iter1
;
629 char *tokens
, *arg0_s
, *arg1_s
, *arg2_s
, *endptr
;
630 int arg0_n
= 0, arg1_n
= 0;
631 bool arg0_n_valid
= false, arg1_n_valid
= false;
632 bool iter0_valid
= false, iter1_valid
= false;
634 if ((tokens
= malloc(strlen(data
) + 1)) == NULL
) {
635 fprintf(stderr
, "out of memory (%s in %s)\n", __func__
, __FILE__
);
638 strcpy(tokens
, data
);
639 arg0_s
= strtok(tokens
, WHITESPACE
);
640 arg1_s
= strtok(NULL
, WHITESPACE
);
641 arg2_s
= strtok(NULL
, "\n");
646 (arg0_n_valid
= (arg0_n
= strtol(arg0_s
, &endptr
, 10)) >= 0 &&
647 errno
== 0 && endptr
!= arg0_s
) &&
648 gtk_tree_model_get_iter_from_string(model
, &iter0
, arg0_s
);
653 (arg1_n_valid
= (arg1_n
= strtol(arg1_s
, &endptr
, 10)) >= 0 &&
654 errno
== 0 && endptr
!= arg1_s
) &&
655 gtk_tree_model_get_iter_from_string(model
, &iter1
, arg1_s
);
656 if (eql(action
, "set") && iter0_valid
&& arg1_n_valid
&&
657 arg1_n
< gtk_tree_model_get_n_columns(model
)) {
658 GType col_type
= gtk_tree_model_get_column_type(model
, arg1_n
);
672 n
= strtoll(arg2_s
, &endptr
, 10);
673 if (!errno
&& endptr
!= arg2_s
)
674 gtk_list_store_set(store
, &iter0
, arg1_n
, n
, -1);
676 ign_cmd(type
, ud
->msg
);
682 d
= strtod(arg2_s
, &endptr
);
683 if (!errno
&& endptr
!= arg2_s
)
684 gtk_list_store_set(store
, &iter0
, arg1_n
, d
, -1);
686 ign_cmd(type
, ud
->msg
);
687 gtk_list_store_set(store
, &iter0
, arg1_n
, strtod(arg2_s
, NULL
), -1);
690 gtk_list_store_set(store
, &iter0
, arg1_n
, arg2_s
, -1);
693 fprintf(stderr
, "column %d: %s not implemented\n", arg1_n
, g_type_name(col_type
));
696 } else if (eql(action
, "scroll") && arg0_n_valid
&& arg1_n_valid
)
697 gtk_tree_view_scroll_to_cell (view
,
698 gtk_tree_path_new_from_string(arg0_s
),
699 gtk_tree_view_get_column(view
, arg1_n
),
701 else if (eql(action
, "insert_row"))
702 if (eql(arg0_s
, "end"))
703 gtk_list_store_insert_before(store
, &iter1
, NULL
);
704 else if (iter0_valid
)
705 gtk_list_store_insert_before(store
, &iter1
, &iter0
);
707 ign_cmd(type
, ud
->msg
);
708 else if (eql(action
, "move_row") && iter0_valid
)
709 if (eql(arg1_s
, "end"))
710 gtk_list_store_move_before(store
, &iter0
, NULL
);
711 else if (iter1_valid
)
712 gtk_list_store_move_before(store
, &iter0
, &iter1
);
714 ign_cmd(type
, ud
->msg
);
715 else if (eql(action
, "remove_row") && iter0_valid
)
716 gtk_list_store_remove(store
, &iter0
);
718 ign_cmd(type
, ud
->msg
);
720 } else if (type
== GTK_TYPE_WINDOW
) {
721 if (eql(action
, "set_title"))
722 gtk_window_set_title(GTK_WINDOW(obj
), data
);
724 ign_cmd(type
, ud
->msg
);
726 ign_cmd(type
, ud
->msg
);
728 ud
->msg_digested
= true;
729 return G_SOURCE_REMOVE
;
739 * Read lines from global stream "in" and perform the appropriate
743 digest_msg(void *builder
)
746 char first_char
= '\0';
749 if ((ud
.msg
= malloc(ud
.msg_size
= 32)) == NULL
) {
750 fprintf(stderr
, "out of memory (%s in %s)\n", __func__
, __FILE__
);
753 pthread_cleanup_push((void(*)(void *))free_at
, &ud
.msg
);
754 pthread_testcancel();
755 read_buf(in
, &ud
.msg
, &ud
.msg_size
);
756 sscanf(ud
.msg
, " %c", &first_char
);
757 ud
.builder
= builder
;
758 if (first_char
!= '#') {
759 ud
.msg_digested
= false;
760 pthread_testcancel();
761 gdk_threads_add_timeout(1, (GSourceFunc
)update_ui
, &ud
);
762 while (!ud
.msg_digested
)
763 nanosleep(&(struct timespec
){0, 1e6
}, NULL
);
765 pthread_cleanup_pop(1);
771 * Create a fifo if necessary, and open it. Give up if the file
772 * exists but is not a fifo
775 fifo(const char *name
, const char *mode
)
781 if (name
!= NULL
&& (stat(name
, &sb
), !S_ISFIFO(sb
.st_mode
)))
782 if (mkfifo(name
, 0666) != 0) {
783 perror("making fifo");
791 fd
= open(name
, O_RDWR
| O_NONBLOCK
);
793 perror("opening fifo");
796 stream
= fdopen(fd
, "r");
803 /* fopen blocks if there is no reader, so here is one */
804 fd
= open(name
, O_RDONLY
| O_NONBLOCK
);
806 perror("opening fifo");
809 stream
= fopen(name
, "w");
810 /* unblocking done */
818 if (stream
== NULL
) {
819 perror("opening fifo");
822 setvbuf(stream
, NULL
, _IOLBF
, 0);
828 main(int argc
, char *argv
[])
831 char *in_fifo
= NULL
, *out_fifo
= NULL
, *ui_file
= NULL
;
834 GError
*error
= NULL
;
835 GObject
*window
= NULL
;
839 while ((opt
= getopt(argc
, argv
, "hi:o:u:GV")) != -1) {
841 case 'i': in_fifo
= optarg
; break;
842 case 'o': out_fifo
= optarg
; break;
843 case 'u': ui_file
= optarg
; break;
844 case 'G': gtk_versions(); break;
845 case 'V': version(); break;
848 default: usage(argv
); break;
851 if (argv
[optind
] != NULL
) {
852 fprintf(stderr
, "illegal parameter '%s'\n", argv
[optind
]);
856 ui_file
= "pipeglade.ui";
858 builder
= gtk_builder_new();
859 if (gtk_builder_add_from_file(builder
, ui_file
, &error
) == 0) {
860 fprintf(stderr
, "%s\n", error
->message
);
863 in
= fifo(in_fifo
, "r");
864 out
= fifo(out_fifo
, "w");
865 gtk_builder_connect_signals(builder
, builder
);
866 pthread_create(&receiver
, NULL
, digest_msg
, (void*)builder
);
867 window
= (gtk_builder_get_object(builder
, "window"));
868 if (!GTK_IS_WIDGET(window
)) {
869 fprintf(stderr
, "no toplevel window named \'window\'\n");
872 gtk_widget_show(GTK_WIDGET(window
));
882 pthread_cancel(receiver
);
883 pthread_join(receiver
, NULL
);