Remove explicit support for GtkInfoBar
[pipeglade.git] / pipeglade.c
blobe4efc4bd5f2cb86be7a411a27ab397a843043049
1 /*
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.
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <gtk/gtk.h>
27 #include <inttypes.h>
28 #include <locale.h>
29 #include <pthread.h>
30 #include <stdio.h>
31 #include <stdarg.h>
32 #include <stdbool.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #include <time.h>
37 #include <unistd.h>
39 #define VERSION "2.1.0"
40 #define BUFLEN 256
41 #define WHITESPACE " \t\n"
43 static FILE *in;
44 static FILE *out;
45 struct ui_data {
46 GtkBuilder *builder;
47 size_t msg_size;
48 char *msg;
49 bool msg_digested;
52 static void
53 usage(char **argv)
55 fprintf(stderr,
56 "usage: %s "
57 "[-h] "
58 "[-i in-fifo] "
59 "[-o out-fifo] "
60 "[-u glade-builder-file.ui] "
61 "[-G] "
62 "[-V]\n",
63 argv[0]);
64 exit(EXIT_SUCCESS);
67 static void
68 version(void)
70 printf(VERSION "\n");
71 exit(EXIT_SUCCESS);
74 static void
75 gtk_versions(void)
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());
80 exit(EXIT_SUCCESS);
83 static bool
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.
96 static void
97 send_msg(GtkBuildable *obj, const char *section, ...)
99 va_list ap;
100 char *data;
101 long nsec;
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) {
107 size_t i = 0;
108 char c;
110 while ((c = data[i++]) != '\0')
111 if (c == '\\')
112 fprintf(out, "\\\\");
113 else if (c == '\n')
114 fprintf(out, "\\n");
115 else
116 putc(c, out);
118 va_end(ap);
119 putc('\n', out);
120 if (ferror(out)) {
121 fprintf(stderr, "send error; retrying\n");
122 clearerr(out);
123 nanosleep(&(struct timespec){0, nsec}, NULL);
124 putc('\n', out);
125 } else
126 break;
131 * Callback that hides the window the originator is in
133 void
134 cb_hide_toplevel(GtkBuildable *obj, gpointer user_data)
136 GtkWidget *toplevel = gtk_widget_get_toplevel(GTK_WIDGET(obj));
138 (void)user_data;
139 if (gtk_widget_is_toplevel(toplevel))
140 gtk_widget_hide(toplevel);
144 * Callback that sends a message describing the user selection of a
145 * dialog
147 void
148 cb_send_dialog_selection(GtkBuildable *obj, gpointer user_data)
150 GtkWidget *toplevel = gtk_widget_get_toplevel(GTK_WIDGET(obj));
152 (void)user_data;
153 if (gtk_widget_is_toplevel(toplevel))
155 if (GTK_IS_FILE_CHOOSER(toplevel)) {
156 send_msg(GTK_BUILDABLE(toplevel),
157 "file",
158 gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(toplevel)), NULL);
159 send_msg(GTK_BUILDABLE(toplevel),
160 "folder",
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
170 void
171 cb_send_text(GtkBuildable *obj, gpointer user_data)
173 GtkTextIter a, b;
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
183 void
184 cb_send_text_selection(GtkBuildable *obj, gpointer user_data)
186 GtkTextIter a, b;
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;
194 const char *section;
198 * send_tree_row_msg serves as an argument for
199 * gtk_tree_selection_selected_foreach()
201 static void
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;
207 int col;
209 for (col = 0; col < gtk_tree_model_get_n_columns(model); col++) {
210 GValue value = G_VALUE_INIT;
211 GType col_type;
212 char str[BUFLEN];
214 gtk_tree_model_get_value(model, iter, col, &value);
215 col_type = gtk_tree_model_get_column_type(model, col);
216 switch (col_type) {
217 case G_TYPE_INT:
218 snprintf(str, BUFLEN, " %d %d", col, g_value_get_int(&value));
219 send_msg(obj, section, path_s, str, NULL);
220 break;
221 case G_TYPE_LONG:
222 snprintf(str, BUFLEN, " %d %ld", col, g_value_get_long(&value));
223 send_msg(obj, section, path_s, str, NULL);
224 break;
225 case G_TYPE_INT64:
226 snprintf(str, BUFLEN, " %d %" PRId64, col, g_value_get_int64(&value));
227 send_msg(obj, section, path_s, str, NULL);
228 break;
229 case G_TYPE_UINT:
230 snprintf(str, BUFLEN, " %d %u", col, g_value_get_uint(&value));
231 send_msg(obj, section, path_s, str, NULL);
232 break;
233 case G_TYPE_ULONG:
234 snprintf(str, BUFLEN, " %d %lu", col, g_value_get_ulong(&value));
235 send_msg(obj, section, path_s, str, NULL);
236 break;
237 case G_TYPE_UINT64:
238 snprintf(str, BUFLEN, " %d %" PRIu64, col, g_value_get_uint64(&value));
239 send_msg(obj, section, path_s, str, NULL);
240 break;
241 case G_TYPE_BOOLEAN:
242 snprintf(str, BUFLEN, " %d %d", col, g_value_get_boolean(&value));
243 send_msg(obj, section, path_s, str, NULL);
244 break;
245 case G_TYPE_FLOAT:
246 snprintf(str, BUFLEN, " %d %f", col, g_value_get_float(&value));
247 send_msg(obj, section, path_s, str, NULL);
248 break;
249 case G_TYPE_DOUBLE:
250 snprintf(str, BUFLEN, " %d %f", col, g_value_get_double(&value));
251 send_msg(obj, section, path_s, str, NULL);
252 break;
253 case G_TYPE_STRING:
254 snprintf(str, BUFLEN, " %d ", col);
255 send_msg(obj, section, path_s, str, g_value_get_string(&value), NULL);
256 break;
257 default:
258 fprintf(stderr, "column %d not implemented: %s\n", col, G_VALUE_TYPE_NAME(&value));
259 break;
261 g_value_unset(&value);
263 g_free(path_s);
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
270 * ...:force_cb.
272 static void
273 do_callback(GtkBuildable *obj, gpointer user_data, const char *section)
275 char str[BUFLEN];
276 const char *item_name;
277 GdkRGBA color;
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)));
289 else
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),
322 section});
323 } else if (GTK_IS_WINDOW(obj))
324 gtk_widget_hide_on_delete(GTK_WIDGET(obj));
325 else
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
332 * section strings.
334 #define MAKE_CB_N(n) \
335 void cb_##n(GtkBuildable *obj, gpointer user_data) \
336 {do_callback(obj, user_data, #n);} \
338 MAKE_CB_N(0)
339 MAKE_CB_N(1)
340 MAKE_CB_N(2)
341 MAKE_CB_N(3)
343 #undef MAKE_CB_N
346 * Store a line from stream into buf, which should have been malloc'd
347 * to bufsize. Enlarge buf and bufsize if necessary.
349 static size_t
350 read_buf(FILE *stream, char **buf, size_t *bufsize)
352 size_t i = 0;
353 int c;
355 for (;;) {
356 c = getc(stream);
357 if (c == EOF) {
358 i = 0;
359 nanosleep(&(struct timespec){0, 1e7}, NULL);
360 continue;
362 if (c == '\n')
363 break;
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__);
367 abort();
369 if (c == '\\')
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;
375 else
376 (*buf)[i++] = c;
378 (*buf)[i] = '\0';
379 return i;
383 * Error message
385 static void
386 ign_cmd(GType type, const char *msg)
388 const char *name, *pad = " ";
390 if (type == G_TYPE_INVALID) {
391 name = "";
392 pad = "";
394 else
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
402 * gtk_main_loop()
404 static gboolean
405 update_ui(struct ui_data *ud)
407 char name[ud->msg_size], action[ud->msg_size];
408 char *data;
409 int data_start = strlen(ud->msg);
410 GObject *obj = NULL;
411 GType type = G_TYPE_INVALID;
412 GdkRGBA color;
414 name[0] = action[0] = '\0';
415 sscanf(ud->msg,
416 " %[0-9a-zA-Z_]:%[0-9a-zA-Z_]%*[ \t] %n",
417 name, action, &data_start);
418 if (eql(action, "main_quit")) {
419 gtk_main_quit();
420 goto done;
422 obj = (gtk_builder_get_object(ud->builder, name));
423 if (obj == NULL) {
424 ign_cmd(type, ud->msg);
425 goto done;
427 if (eql(action, "force_cb")) {
428 do_callback(GTK_BUILDABLE(obj), NULL, "forced");
429 goto done;
431 data = ud->msg + data_start;
432 if (eql(action, "set_sensitive")) {
433 gtk_widget_set_sensitive(GTK_WIDGET(obj), strtol(data, NULL, 10));
434 goto done;
435 } else if (eql(action, "set_visible")) {
436 gtk_widget_set_visible(GTK_WIDGET(obj), strtol(data, NULL, 10));
437 goto done;
438 } else if (eql(action, "override_font")) {
439 if (data[0]) {
440 PangoFontDescription *font = pango_font_description_from_string(data);
442 gtk_widget_override_font(GTK_WIDGET(obj), font);
443 pango_font_description_free(font);
444 } else
445 gtk_widget_override_font(GTK_WIDGET(obj), NULL);
446 goto done;
447 } else if (eql(action, "override_color")) {
448 if (data[0]) {
449 gdk_rgba_parse(&color, data);
450 gtk_widget_override_color(GTK_WIDGET(obj), GTK_STATE_FLAG_NORMAL, &color);
451 } else
452 gtk_widget_override_color(GTK_WIDGET(obj), GTK_STATE_FLAG_NORMAL, NULL);
453 goto done;
454 } else if (eql(action, "override_background_color")) {
455 if (data[0]) {
456 gdk_rgba_parse(&color, data);
457 gtk_widget_override_background_color(GTK_WIDGET(obj), GTK_STATE_FLAG_NORMAL, &color);
458 } else
459 gtk_widget_override_background_color(GTK_WIDGET(obj), GTK_STATE_FLAG_NORMAL, NULL);
460 goto done;
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);
466 else
467 ign_cmd(type, ud->msg);
468 } else if (type == GTK_TYPE_IMAGE) {
469 GtkIconSize size;
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);
476 else
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));
480 GtkTextIter a, b;
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.);
500 else
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));
505 else
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);
512 else
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);
517 else
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);
522 else
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);
529 else
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);
534 else
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);
540 } else
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);
545 else
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));
550 else
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));
557 else
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);
562 else
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));
567 else
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));
574 else
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));
581 else
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);
595 } else
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);
602 else
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);
612 } else
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);
618 else
619 ign_cmd(type, ud->msg);
620 } else if (eql(action, "clear_marks"))
621 gtk_calendar_clear_marks(GTK_CALENDAR(obj));
622 else
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__);
636 abort();
638 strcpy(tokens, data);
639 arg0_s = strtok(tokens, WHITESPACE);
640 arg1_s = strtok(NULL, WHITESPACE);
641 arg2_s = strtok(NULL, "\n");
642 errno = 0;
643 endptr = NULL;
644 iter0_valid =
645 arg0_s != NULL &&
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);
649 errno = 0;
650 endptr = NULL;
651 iter1_valid =
652 arg1_s != NULL &&
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);
659 long long int n;
660 double d;
662 switch (col_type) {
663 case G_TYPE_BOOLEAN:
664 case G_TYPE_INT:
665 case G_TYPE_LONG:
666 case G_TYPE_INT64:
667 case G_TYPE_UINT:
668 case G_TYPE_ULONG:
669 case G_TYPE_UINT64:
670 errno = 0;
671 endptr = NULL;
672 n = strtoll(arg2_s, &endptr, 10);
673 if (!errno && endptr != arg2_s)
674 gtk_list_store_set(store, &iter0, arg1_n, n, -1);
675 else
676 ign_cmd(type, ud->msg);
677 break;
678 case G_TYPE_FLOAT:
679 case G_TYPE_DOUBLE:
680 errno = 0;
681 endptr = NULL;
682 d = strtod(arg2_s, &endptr);
683 if (!errno && endptr != arg2_s)
684 gtk_list_store_set(store, &iter0, arg1_n, d, -1);
685 else
686 ign_cmd(type, ud->msg);
687 gtk_list_store_set(store, &iter0, arg1_n, strtod(arg2_s, NULL), -1);
688 break;
689 case G_TYPE_STRING:
690 gtk_list_store_set(store, &iter0, arg1_n, arg2_s, -1);
691 break;
692 default:
693 fprintf(stderr, "column %d: %s not implemented\n", arg1_n, g_type_name(col_type));
694 break;
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),
700 0, 0., 0.);
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);
706 else
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);
713 else
714 ign_cmd(type, ud->msg);
715 else if (eql(action, "remove_row") && iter0_valid)
716 gtk_list_store_remove(store, &iter0);
717 else
718 ign_cmd(type, ud->msg);
719 free(tokens);
720 } else if (type == GTK_TYPE_WINDOW) {
721 if (eql(action, "set_title"))
722 gtk_window_set_title(GTK_WINDOW(obj), data);
723 else
724 ign_cmd(type, ud->msg);
725 } else
726 ign_cmd(type, ud->msg);
727 done:
728 ud->msg_digested = true;
729 return G_SOURCE_REMOVE;
732 static void
733 free_at(void **mem)
735 free(*mem);
739 * Read lines from global stream "in" and perform the appropriate
740 * actions on the GUI
742 static void *
743 digest_msg(void *builder)
745 for (;;) {
746 char first_char = '\0';
747 struct ui_data ud;
749 if ((ud.msg = malloc(ud.msg_size = 32)) == NULL ) {
750 fprintf(stderr, "out of memory (%s in %s)\n", __func__, __FILE__);
751 abort();
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);
767 return NULL;
771 * Create a fifo if necessary, and open it. Give up if the file
772 * exists but is not a fifo
774 static FILE *
775 fifo(const char *name, const char *mode)
777 struct stat sb;
778 int fd;
779 FILE *stream;
781 if (name != NULL && (stat(name, &sb), !S_ISFIFO(sb.st_mode)))
782 if (mkfifo(name, 0666) != 0) {
783 perror("making fifo");
784 exit(EXIT_FAILURE);
786 switch (mode[0]) {
787 case 'r':
788 if (name == NULL)
789 stream = stdin;
790 else {
791 fd = open(name, O_RDWR | O_NONBLOCK);
792 if (fd < 0) {
793 perror("opening fifo");
794 exit(EXIT_FAILURE);
796 stream = fdopen(fd, "r");
798 break;
799 case 'w':
800 if (name == NULL)
801 stream = stdout;
802 else {
803 /* fopen blocks if there is no reader, so here is one */
804 fd = open(name, O_RDONLY | O_NONBLOCK);
805 if (fd < 0) {
806 perror("opening fifo");
807 exit(EXIT_FAILURE);
809 stream = fopen(name, "w");
810 /* unblocking done */
811 close(fd);
813 break;
814 default:
815 abort();
816 break;
818 if (stream == NULL) {
819 perror("opening fifo");
820 exit(EXIT_FAILURE);
821 } else {
822 setvbuf(stream, NULL, _IOLBF, 0);
823 return stream;
828 main(int argc, char *argv[])
830 char opt;
831 char *in_fifo = NULL, *out_fifo = NULL, *ui_file = NULL;
832 GtkBuilder *builder;
833 pthread_t receiver;
834 GError *error = NULL;
835 GObject *window = NULL;
837 in = NULL;
838 out = NULL;
839 while ((opt = getopt(argc, argv, "hi:o:u:GV")) != -1) {
840 switch (opt) {
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;
846 case '?':
847 case 'h':
848 default: usage(argv); break;
851 if (argv[optind] != NULL) {
852 fprintf(stderr, "illegal parameter '%s'\n", argv[optind]);
853 usage(argv);
855 if (ui_file == NULL)
856 ui_file = "pipeglade.ui";
857 gtk_init(0, NULL);
858 builder = gtk_builder_new();
859 if (gtk_builder_add_from_file(builder, ui_file, &error) == 0) {
860 fprintf(stderr, "%s\n", error->message);
861 exit(EXIT_FAILURE);
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");
870 exit(EXIT_FAILURE);
872 gtk_widget_show(GTK_WIDGET(window));
873 gtk_main();
874 if (in != stdin) {
875 fclose(in);
876 unlink(in_fifo);
878 if (out != stdout) {
879 fclose(out);
880 unlink(out_fifo);
882 pthread_cancel(receiver);
883 pthread_join(receiver, NULL);
884 exit(EXIT_SUCCESS);