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.
27 #include <gtk/gtkunixprint.h>
39 #include <sys/select.h>
44 #define VERSION "4.1.0"
46 #define WHITESPACE " \t\n"
47 #define MAIN_WIN "main"
54 "[-u glade-file.ui] " \
61 "out of memory: %s (%s:%d)\n", \
62 __func__, __FILE__, __LINE__); \
76 * Print a formatted message to stream s and give up with status
79 bye(int status
, FILE *s
, const char *fmt
, ...)
92 eql(const char *s1
, const char *s2
)
94 return s1
!= NULL
&& s2
!= NULL
&& strcmp(s1
, s2
) == 0;
98 widget_name(void *obj
)
100 return gtk_buildable_get_name(GTK_BUILDABLE(obj
));
104 * Send GUI feedback to global stream "out". The message format is
105 * "<origin>:<tag> <data ...>". The variadic arguments are
106 * strings; last argument must be NULL.
109 send_msg(GtkBuildable
*obj
, const char *tag
, ...)
113 const char *w_name
= widget_name(obj
);
115 int ofd
= fileno(out
);
116 struct timeval timeout
= {1, 0};
120 if (select(ofd
+ 1, NULL
, &wfds
, NULL
, &timeout
) == 1) {
122 fprintf(out
, "%s:%s ", w_name
, tag
);
123 while ((data
= va_arg(ap
, char *)) != NULL
) {
127 while ((c
= data
[i
++]) != '\0')
129 fprintf(out
, "\\\\");
139 "send error; discarding feedback message %s:%s\n",
144 * Callback that sends user's selection from a file dialog
147 cb_send_file_chooser_dialog_selection(gpointer user_data
)
149 send_msg(user_data
, "file",
150 gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(user_data
)), NULL
);
151 send_msg(user_data
, "folder",
152 gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(user_data
)), NULL
);
156 * Callback that sends in a message the content of the text buffer
157 * passed in user_data
160 cb_send_text(GtkBuildable
*obj
, gpointer user_data
)
164 gtk_text_buffer_get_bounds(user_data
, &a
, &b
);
165 send_msg(obj
, "text", gtk_text_buffer_get_text(user_data
, &a
, &b
, FALSE
), NULL
);
169 * Callback that sends in a message the highlighted text from the text
170 * buffer which was passed in user_data
173 cb_send_text_selection(GtkBuildable
*obj
, gpointer user_data
)
177 gtk_text_buffer_get_selection_bounds(user_data
, &a
, &b
);
178 send_msg(obj
, "text", gtk_text_buffer_get_text(user_data
, &a
, &b
, FALSE
), NULL
);
182 * send_tree_row_msg serves as an argument for
183 * gtk_tree_selection_selected_foreach()
186 send_tree_row_msg(GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, GtkBuildable
*obj
)
188 char *path_s
= gtk_tree_path_to_string(path
);
191 for (col
= 0; col
< gtk_tree_model_get_n_columns(model
); col
++) {
192 GValue value
= G_VALUE_INIT
;
196 gtk_tree_model_get_value(model
, iter
, col
, &value
);
197 col_type
= gtk_tree_model_get_column_type(model
, col
);
200 snprintf(str
, BUFLEN
, " %d %d", col
, g_value_get_int(&value
));
201 send_msg(obj
, "gint", path_s
, str
, NULL
);
204 snprintf(str
, BUFLEN
, " %d %ld", col
, g_value_get_long(&value
));
205 send_msg(obj
, "glong", path_s
, str
, NULL
);
208 snprintf(str
, BUFLEN
, " %d %" PRId64
, col
, g_value_get_int64(&value
));
209 send_msg(obj
, "gint64", path_s
, str
, NULL
);
212 snprintf(str
, BUFLEN
, " %d %u", col
, g_value_get_uint(&value
));
213 send_msg(obj
, "guint", path_s
, str
, NULL
);
216 snprintf(str
, BUFLEN
, " %d %lu", col
, g_value_get_ulong(&value
));
217 send_msg(obj
, "gulong", path_s
, str
, NULL
);
220 snprintf(str
, BUFLEN
, " %d %" PRIu64
, col
, g_value_get_uint64(&value
));
221 send_msg(obj
, "guint64", path_s
, str
, NULL
);
224 snprintf(str
, BUFLEN
, " %d %d", col
, g_value_get_boolean(&value
));
225 send_msg(obj
, "gboolean", path_s
, str
, NULL
);
228 snprintf(str
, BUFLEN
, " %d %f", col
, g_value_get_float(&value
));
229 send_msg(obj
, "gfloat", path_s
, str
, NULL
);
232 snprintf(str
, BUFLEN
, " %d %f", col
, g_value_get_double(&value
));
233 send_msg(obj
, "gdouble", path_s
, str
, NULL
);
236 snprintf(str
, BUFLEN
, " %d ", col
);
237 send_msg(obj
, "gchararray", path_s
, str
, g_value_get_string(&value
), NULL
);
240 fprintf(stderr
, "column %d not implemented: %s\n", col
, G_VALUE_TYPE_NAME(&value
));
243 g_value_unset(&value
);
249 * Callback that sends message(s) whose nature depends on the
250 * arguments passed. A call to this function will also be initiated
251 * by the user command ...:force.
254 cb(GtkBuildable
*obj
, gpointer user_data
)
258 GtkTreeView
*tree_view
;
259 unsigned int year
= 0, month
= 0, day
= 0;
261 if (GTK_IS_ENTRY(obj
))
262 send_msg(obj
, user_data
, gtk_entry_get_text(GTK_ENTRY(obj
)), NULL
);
263 else if (GTK_IS_MENU_ITEM(obj
))
264 send_msg(obj
, user_data
, gtk_menu_item_get_label(GTK_MENU_ITEM(obj
)), NULL
);
265 else if (GTK_IS_RANGE(obj
)) {
266 snprintf(str
, BUFLEN
, "%f", gtk_range_get_value(GTK_RANGE(obj
)));
267 send_msg(obj
, user_data
, str
, NULL
);
268 } else if (GTK_IS_SWITCH(obj
))
269 send_msg(obj
, gtk_switch_get_active(GTK_SWITCH(obj
)) ? "1" : "0", NULL
);
270 else if (GTK_IS_TOGGLE_BUTTON(obj
))
271 send_msg(obj
, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(obj
)) ? "1" : "0", NULL
);
272 else if (GTK_IS_COLOR_BUTTON(obj
)) {
273 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(obj
), &color
);
274 send_msg(obj
, user_data
, gdk_rgba_to_string(&color
), NULL
);
275 } else if (GTK_IS_FONT_BUTTON(obj
))
276 send_msg(obj
, user_data
, gtk_font_button_get_font_name(GTK_FONT_BUTTON(obj
)), NULL
);
277 else if (GTK_IS_FILE_CHOOSER_BUTTON(obj
))
278 send_msg(obj
, user_data
, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(obj
)), NULL
);
279 else if (GTK_IS_BUTTON(obj
) || GTK_IS_TREE_VIEW_COLUMN(obj
) || GTK_IS_SOCKET(obj
))
280 send_msg(obj
, user_data
, NULL
);
281 else if (GTK_IS_CALENDAR(obj
)) {
282 gtk_calendar_get_date(GTK_CALENDAR(obj
), &year
, &month
, &day
);
283 snprintf(str
, BUFLEN
, "%04u-%02u-%02u", year
, ++month
, day
);
284 send_msg(obj
, user_data
, str
, NULL
);
285 } else if (GTK_IS_TREE_SELECTION(obj
)) {
286 tree_view
= gtk_tree_selection_get_tree_view(GTK_TREE_SELECTION(obj
));
287 send_msg(GTK_BUILDABLE(tree_view
), user_data
, NULL
);
288 gtk_tree_selection_selected_foreach(GTK_TREE_SELECTION(obj
),
289 (GtkTreeSelectionForeachFunc
)send_tree_row_msg
,
290 GTK_BUILDABLE(tree_view
));
292 fprintf(stderr
, "ignoring callback from %s\n", widget_name(obj
));
296 * Callback like cb(), but returning true.
299 cb_true(GtkBuildable
*obj
, gpointer user_data
)
306 * Store a line from stream s into buf, which should have been malloc'd
307 * to bufsize. Enlarge buf and bufsize if necessary.
310 read_buf(FILE *s
, char **buf
, size_t *bufsize
)
315 int ifd
= fileno(in
);
321 select(ifd
+ 1, &rfds
, NULL
, NULL
, NULL
);
325 if (i
>= *bufsize
- 1)
326 if ((*buf
= realloc(*buf
, *bufsize
= *bufsize
* 2)) == NULL
)
331 case 'n': (*buf
)[i
++] = '\n'; break;
332 case 'r': (*buf
)[i
++] = '\r'; break;
333 default: (*buf
)[i
++] = c
; break;
335 } else if (c
== '\\')
348 ign_cmd(GType type
, const char *msg
)
350 const char *name
, *pad
= " ";
352 if (type
== G_TYPE_INVALID
) {
357 name
= g_type_name(type
);
358 fprintf(stderr
, "ignoring %s%scommand \"%s\"\n", name
, pad
, msg
);
362 * Drawing on a GtkDrawingArea
389 * One single element of a drawing
392 struct draw_op
*next
;
393 struct draw_op
*prev
;
400 * The content of all GtkDrawingAreas
402 static struct drawing
{
403 struct drawing
*next
;
404 struct drawing
*prev
;
406 struct draw_op
*draw_ops
;
410 * Sets of arguments for various drawing functions
412 struct rectangle_args
{
427 struct curve_to_args
{
436 struct move_to_args
{
441 struct set_source_rgba_args
{
445 struct set_dash_args
{
450 struct set_line_cap_args
{
451 cairo_line_cap_t line_cap
;
454 struct set_line_join_args
{
455 cairo_line_join_t line_join
;
458 struct set_line_width_args
{
462 struct show_text_args
{
467 struct set_font_size_args
{
472 draw(cairo_t
*cr
, enum cairo_fn op
, void *op_args
)
476 struct rectangle_args
*args
= op_args
;
478 cairo_rectangle(cr
, args
->x
, args
->y
, args
->width
, args
->height
);
482 struct arc_args
*args
= op_args
;
484 cairo_arc(cr
, args
->x
, args
->y
, args
->radius
, args
->angle1
, args
->angle2
);
488 struct arc_args
*args
= op_args
;
490 cairo_arc_negative(cr
, args
->x
, args
->y
, args
->radius
, args
->angle1
, args
->angle2
);
494 struct curve_to_args
*args
= op_args
;
496 cairo_curve_to(cr
, args
->x1
, args
->y1
, args
->x2
, args
->y2
, args
->x3
, args
->y3
);
500 struct curve_to_args
*args
= op_args
;
502 cairo_curve_to(cr
, args
->x1
, args
->y1
, args
->x2
, args
->y2
, args
->x3
, args
->y3
);
506 struct move_to_args
*args
= op_args
;
508 cairo_line_to(cr
, args
->x
, args
->y
);
512 struct move_to_args
*args
= op_args
;
514 cairo_rel_line_to(cr
, args
->x
, args
->y
);
518 struct move_to_args
*args
= op_args
;
520 cairo_move_to(cr
, args
->x
, args
->y
);
524 struct move_to_args
*args
= op_args
;
526 cairo_rel_move_to(cr
, args
->x
, args
->y
);
530 cairo_close_path(cr
);
532 case SET_SOURCE_RGBA
: {
533 struct set_source_rgba_args
*args
= op_args
;
535 gdk_cairo_set_source_rgba(cr
, &args
->color
);
539 struct set_dash_args
*args
= op_args
;
541 cairo_set_dash(cr
, args
->dashes
, args
->num_dashes
, 0);
545 struct set_line_cap_args
*args
= op_args
;
547 cairo_set_line_cap(cr
, args
->line_cap
);
550 case SET_LINE_JOIN
: {
551 struct set_line_join_args
*args
= op_args
;
553 cairo_set_line_join(cr
, args
->line_join
);
556 case SET_LINE_WIDTH
: {
557 struct set_line_width_args
*args
= op_args
;
559 cairo_set_line_width(cr
, args
->width
);
566 cairo_fill_preserve(cr
);
571 case STROKE_PRESERVE
:
572 cairo_stroke_preserve(cr
);
575 struct show_text_args
*args
= op_args
;
577 cairo_show_text(cr
, args
->text
);
580 case SET_FONT_SIZE
: {
581 struct set_font_size_args
*args
= op_args
;
583 cairo_set_font_size(cr
, args
->size
);
593 set_draw_op(struct draw_op
*op
, char* action
, char *data
)
595 if (eql(action
, "rectangle")) {
596 struct rectangle_args
*args
;
598 if ((args
= malloc(sizeof(*args
))) == NULL
)
602 if (sscanf(data
, "%u %lf %lf %lf %lf", &op
->id
, &args
->x
, &args
->y
, &args
->width
, &args
->height
) != 5)
604 } else if (eql(action
, "arc")) {
605 struct arc_args
*args
;
608 if ((args
= malloc(sizeof(*args
))) == NULL
)
612 if (sscanf(data
, "%u %lf %lf %lf %lf %lf", &op
->id
, &args
->x
, &args
->y
, &args
->radius
, °1
, °2
) != 6)
614 args
->angle1
= deg1
* (M_PI
/ 180.);
615 args
->angle2
= deg2
* (M_PI
/ 180.);
616 } else if (eql(action
, "arc_negative")) {
617 struct arc_args
*args
;
620 if ((args
= malloc(sizeof(*args
))) == NULL
)
622 op
->op
= ARC_NEGATIVE
;
624 if (sscanf(data
, "%u %lf %lf %lf %lf %lf", &op
->id
, &args
->x
, &args
->y
, &args
->radius
, °1
, °2
) != 6)
626 args
->angle1
= deg1
* (M_PI
/ 180.);
627 args
->angle2
= deg2
* (M_PI
/ 180.);
628 } else if (eql(action
, "curve_to")) {
629 struct curve_to_args
*args
;
631 if ((args
= malloc(sizeof(*args
))) == NULL
)
635 if (sscanf(data
, "%u %lf %lf %lf %lf %lf %lf", &op
->id
, &args
->x1
, &args
->y1
, &args
->x2
, &args
->y2
, &args
->x3
, &args
->y3
) != 7)
637 } else if (eql(action
, "rel_curve_to")) {
638 struct curve_to_args
*args
;
640 if ((args
= malloc(sizeof(*args
))) == NULL
)
642 op
->op
= REL_CURVE_TO
;
644 if (sscanf(data
, "%u %lf %lf %lf %lf %lf %lf", &op
->id
, &args
->x1
, &args
->y1
, &args
->x2
, &args
->y2
, &args
->x3
, &args
->y3
) != 7)
646 } else if (eql(action
, "line_to")) {
647 struct move_to_args
*args
;
649 if ((args
= malloc(sizeof(*args
))) == NULL
)
653 if (sscanf(data
, "%u %lf %lf", &op
->id
, &args
->x
, &args
->y
) != 3)
655 } else if (eql(action
, "rel_line_to")) {
656 struct move_to_args
*args
;
658 if ((args
= malloc(sizeof(*args
))) == NULL
)
660 op
->op
= REL_LINE_TO
;
662 if (sscanf(data
, "%u %lf %lf", &op
->id
, &args
->x
, &args
->y
) != 3)
664 } else if (eql(action
, "move_to")) {
665 struct move_to_args
*args
;
667 if ((args
= malloc(sizeof(*args
))) == NULL
)
671 if (sscanf(data
, "%u %lf %lf", &op
->id
, &args
->x
, &args
->y
) != 3)
673 } else if (eql(action
, "rel_move_to")) {
674 struct move_to_args
*args
;
676 if ((args
= malloc(sizeof(*args
))) == NULL
)
678 op
->op
= REL_MOVE_TO
;
680 if (sscanf(data
, "%u %lf %lf", &op
->id
, &args
->x
, &args
->y
) != 3)
682 } else if (eql(action
, "close_path")) {
684 if (sscanf(data
, "%u", &op
->id
) != 1)
687 } else if (eql(action
, "set_source_rgba")) {
688 struct set_source_rgba_args
*args
;
691 if ((args
= malloc(sizeof(*args
))) == NULL
)
693 op
->op
= SET_SOURCE_RGBA
;
695 if ((sscanf(data
, "%u %n", &op
->id
, &c_start
) < 1))
697 gdk_rgba_parse(&args
->color
, data
+ c_start
);
698 } else if (eql(action
, "set_dash")) {
699 struct set_dash_args
*args
;
703 if (sscanf(data
, "%u %n", &op
->id
, &d_start
) < 1)
705 next
= end
= data
+ d_start
;
711 } while (next
!= end
);
712 if ((args
= malloc(sizeof(*args
) + n
* sizeof(args
->dashes
[0]))) == NULL
)
716 args
->num_dashes
= n
;
717 for (i
= 0, next
= data
+ d_start
; i
< n
; i
++, next
= end
) {
718 args
->dashes
[i
] = strtod(next
, &end
);
720 } else if (eql(action
, "set_line_cap")) {
721 struct set_line_cap_args
*args
;
724 if ((args
= malloc(sizeof(*args
))) == NULL
)
726 op
->op
= SET_LINE_CAP
;
728 if (sscanf(data
, "%u %6s", &op
->id
, str
) != 2)
730 if (eql(str
, "butt"))
731 args
->line_cap
= CAIRO_LINE_CAP_BUTT
;
732 else if (eql(str
, "round"))
733 args
->line_cap
= CAIRO_LINE_CAP_ROUND
;
734 else if (eql(str
, "square"))
735 args
->line_cap
= CAIRO_LINE_CAP_SQUARE
;
738 } else if (eql(action
, "set_line_join")) {
739 struct set_line_join_args
*args
;
742 if ((args
= malloc(sizeof(*args
))) == NULL
)
744 op
->op
= SET_LINE_JOIN
;
746 if (sscanf(data
, "%u %5s", &op
->id
, str
) != 2)
748 if (eql(str
, "miter"))
749 args
->line_join
= CAIRO_LINE_JOIN_MITER
;
750 else if (eql(str
, "round"))
751 args
->line_join
= CAIRO_LINE_JOIN_ROUND
;
752 else if (eql(str
, "bevel"))
753 args
->line_join
= CAIRO_LINE_JOIN_BEVEL
;
756 } else if (eql(action
, "set_line_width")) {
757 struct set_line_width_args
*args
;
759 if ((args
= malloc(sizeof(*args
))) == NULL
)
761 op
->op
= SET_LINE_WIDTH
;
763 if (sscanf(data
, "%u %lf", &op
->id
, &args
->width
) != 2)
765 } else if (eql(action
, "fill")) {
767 if (sscanf(data
, "%u", &op
->id
) != 1)
770 } else if (eql(action
, "fill_preserve")) {
771 op
->op
= FILL_PRESERVE
;
772 if (sscanf(data
, "%u", &op
->id
) != 1)
775 } else if (eql(action
, "stroke")) {
777 if (sscanf(data
, "%u", &op
->id
) != 1)
780 } else if (eql(action
, "stroke_preserve")) {
781 op
->op
= STROKE_PRESERVE
;
782 if (sscanf(data
, "%u", &op
->id
) != 1)
785 } else if (eql(action
, "show_text")) {
786 struct show_text_args
*args
;
789 if (sscanf(data
, "%u %n", &op
->id
, &start
) < 1)
791 len
= strlen(data
+ start
) + 1;
792 if ((args
= malloc(sizeof(*args
) + len
* sizeof(args
->text
[0]))) == NULL
)
796 args
->len
= len
; /* not used */
797 strncpy(args
->text
, (data
+ start
), len
);
798 } else if (eql(action
, "set_font_size")) {
799 struct set_font_size_args
*args
;
801 if ((args
= malloc(sizeof(*args
))) == NULL
)
803 op
->op
= SET_FONT_SIZE
;
805 if (sscanf(data
, "%u %lf", &op
->id
, &args
->size
) != 2)
813 * Add another element to widget's list of drawing operations
816 ins_draw_op(GtkWidget
*widget
, char *action
, char *data
)
818 struct draw_op
*op
, *last_op
;
821 if ((op
= malloc(sizeof(*op
))) == NULL
)
824 if (!set_draw_op(op
, action
, data
)) {
829 for (d
= drawings
; d
!= NULL
; d
= d
->next
)
830 if (d
->widget
== widget
)
833 if ((d
= malloc(sizeof(*d
))) == NULL
)
835 if (drawings
== NULL
) {
843 } else if (d
->draw_ops
== NULL
) {
847 for (last_op
= d
->draw_ops
; last_op
->next
!= NULL
; last_op
= last_op
->next
);
854 * Remove all elements with the given id from widget's list of drawing
858 rem_draw_op(GtkWidget
*widget
, char *data
)
860 struct draw_op
*op
, *next_op
;
864 if (sscanf(data
, "%u", &id
) != 1)
866 for (d
= drawings
; d
!= NULL
; d
= d
->next
)
867 if (d
->widget
== widget
)
874 if (op
->prev
== NULL
)
875 d
->draw_ops
= next_op
;
887 * Callback that draws on a GtkDrawingArea
890 cb_draw(GtkWidget
*widget
, cairo_t
*cr
, gpointer data
)
896 for (p
= drawings
; p
!= NULL
; p
= p
->next
)
897 if (p
->widget
== widget
)
898 for (op
= p
->draw_ops
; op
!= NULL
; op
= op
->next
)
899 draw(cr
, op
->op
, op
->op_args
);
904 * One style provider for each widget
906 struct style_provider
{
907 struct style_provider
*next
;
908 struct style_provider
*prev
;
910 GtkCssProvider
*provider
;
912 } *widget_style_providers
= NULL
;
915 * Change the style of the widget passed
918 update_widget_style(GtkWidget
*widget
, const char *name
, const char *data
)
920 GtkStyleContext
*context
;
921 struct style_provider
*sp
;
922 const char *prefix
= "* {", *suffix
= "}";
925 sz
= strlen(prefix
) + strlen(suffix
) + strlen(data
) + 1;
926 context
= gtk_widget_get_style_context(widget
);
927 for (sp
= widget_style_providers
; !eql(name
, sp
->name
); sp
= sp
->next
);
928 gtk_style_context_remove_provider(context
, GTK_STYLE_PROVIDER(sp
->provider
));
929 free(sp
->style_decl
);
930 if ((sp
->style_decl
= malloc(sz
)) == NULL
)
932 strcpy(sp
->style_decl
, prefix
);
933 strcat(sp
->style_decl
, data
);
934 strcat(sp
->style_decl
, suffix
);
935 gtk_style_context_add_provider(context
,
936 GTK_STYLE_PROVIDER(sp
->provider
),
937 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
938 gtk_css_provider_load_from_data(sp
->provider
, sp
->style_decl
, -1, NULL
);
942 * Update various kinds of widgets according to the respective action
946 update_button(GtkButton
*button
, const char *action
,
947 const char *data
, const char *whole_msg
)
949 if (eql(action
, "set_label"))
950 gtk_button_set_label(button
, data
);
952 ign_cmd(GTK_TYPE_BUTTON
, whole_msg
);
956 update_calendar(GtkCalendar
*calendar
, const char *action
,
957 const char *data
, const char *whole_msg
)
959 int year
= 0, month
= 0, day
= 0;
961 if (eql(action
, "select_date")) {
962 sscanf(data
, "%d-%d-%d", &year
, &month
, &day
);
963 if (month
> -1 && month
<= 11 && day
> 0 && day
<= 31) {
964 gtk_calendar_select_month(calendar
, --month
, year
);
965 gtk_calendar_select_day(calendar
, day
);
967 ign_cmd(GTK_TYPE_CALENDAR
, whole_msg
);
968 } else if (eql(action
, "mark_day")) {
969 day
= strtol(data
, NULL
, 10);
970 if (day
> 0 && day
<= 31)
971 gtk_calendar_mark_day(calendar
, day
);
973 ign_cmd(GTK_TYPE_CALENDAR
, whole_msg
);
974 } else if (eql(action
, "clear_marks"))
975 gtk_calendar_clear_marks(calendar
);
977 ign_cmd(GTK_TYPE_CALENDAR
, whole_msg
);
981 update_color_button(GtkColorChooser
*chooser
, const char *action
,
982 const char *data
, const char *whole_msg
)
986 if (eql(action
, "set_color")) {
987 gdk_rgba_parse(&color
, data
);
988 gtk_color_chooser_set_rgba(chooser
, &color
);
990 ign_cmd(GTK_TYPE_COLOR_BUTTON
, whole_msg
);
994 update_combo_box_text(GtkComboBoxText
*combobox
, const char *action
,
995 char *data
, const char *whole_msg
)
997 if (eql(action
, "prepend_text"))
998 gtk_combo_box_text_prepend_text(combobox
, data
);
999 else if (eql(action
, "append_text"))
1000 gtk_combo_box_text_append_text(combobox
, data
);
1001 else if (eql(action
, "remove"))
1002 gtk_combo_box_text_remove(combobox
, strtol(data
, NULL
, 10));
1003 else if (eql(action
, "insert_text")) {
1004 char *position
= strtok(data
, WHITESPACE
);
1005 char *text
= strtok(NULL
, WHITESPACE
);
1007 gtk_combo_box_text_insert_text(combobox
, strtol(position
, NULL
, 10), text
);
1009 ign_cmd(GTK_TYPE_COMBO_BOX_TEXT
, whole_msg
);
1013 update_frame(GtkFrame
*frame
, const char *action
,
1014 const char *data
, const char *whole_msg
)
1016 if (eql(action
, "set_label"))
1017 gtk_frame_set_label(frame
, data
);
1019 ign_cmd(GTK_TYPE_FRAME
, whole_msg
);
1023 update_drawing_area(GtkWidget
*widget
, char *action
,
1024 char *data
, const char *whole_msg
)
1026 if (eql(action
, "remove")) {
1027 if (!rem_draw_op(widget
, data
))
1028 ign_cmd(GTK_TYPE_DRAWING_AREA
, whole_msg
);
1029 } else if (eql(action
, "refresh")) {
1030 gint width
= gtk_widget_get_allocated_width (widget
);
1031 gint height
= gtk_widget_get_allocated_height (widget
);
1033 gtk_widget_queue_draw_area(widget
, 0, 0, width
, height
);
1034 } else if (ins_draw_op(widget
, action
, data
));
1036 ign_cmd(GTK_TYPE_DRAWING_AREA
, whole_msg
);
1040 update_entry(GtkEntry
*entry
, const char *action
,
1041 const char *data
, const char *whole_msg
, GType type
)
1043 if (eql(action
, "set_text"))
1044 gtk_entry_set_text(entry
, data
);
1045 else if (eql(action
, "set_placeholder_text"))
1046 gtk_entry_set_placeholder_text(entry
, data
);
1048 ign_cmd(type
, whole_msg
);
1052 update_label(GtkLabel
*label
, const char *action
,
1053 const char *data
, const char *whole_msg
)
1055 if (eql(action
, "set_text"))
1056 gtk_label_set_text(label
, data
);
1058 ign_cmd(GTK_TYPE_LABEL
, whole_msg
);
1062 update_expander(GtkExpander
*expander
, const char *action
,
1063 const char *data
, const char *whole_msg
)
1065 if (eql(action
, "set_expanded"))
1066 gtk_expander_set_expanded(expander
, strtol(data
, NULL
, 10));
1067 else if (eql(action
, "set_label"))
1068 gtk_expander_set_label(expander
, data
);
1070 ign_cmd(GTK_TYPE_EXPANDER
, whole_msg
);
1074 update_file_chooser_button(GtkFileChooser
*chooser
, const char *action
,
1075 const char *data
, const char *whole_msg
)
1077 if (eql(action
, "set_filename"))
1078 gtk_file_chooser_set_filename(chooser
, data
);
1080 ign_cmd(GTK_TYPE_FILE_CHOOSER_BUTTON
, whole_msg
);
1084 update_file_chooser_dialog(GtkFileChooser
*chooser
, const char *action
,
1085 const char *data
, const char *whole_msg
)
1087 if (eql(action
, "set_filename"))
1088 gtk_file_chooser_set_filename(chooser
, data
);
1089 else if (eql(action
, "set_current_name"))
1090 gtk_file_chooser_set_current_name(chooser
, data
);
1092 ign_cmd(GTK_TYPE_FILE_CHOOSER_DIALOG
, whole_msg
);
1096 update_font_button(GtkFontButton
*font_button
, const char *action
,
1097 const char *data
, const char *whole_msg
)
1099 if (eql(action
, "set_font_name"))
1100 gtk_font_button_set_font_name(font_button
, data
);
1102 ign_cmd(GTK_TYPE_FONT_BUTTON
, whole_msg
);
1106 update_print_dialog(GtkPrintUnixDialog
*dialog
, const char *action
,
1107 const char *data
, const char *whole_msg
)
1110 GtkPrinter
*printer
;
1111 GtkPrintSettings
*settings
;
1112 GtkPageSetup
*page_setup
;
1115 if (eql(action
, "print")) {
1116 response_id
= gtk_dialog_run(GTK_DIALOG(dialog
));
1117 switch (response_id
) {
1118 case GTK_RESPONSE_OK
:
1119 printer
= gtk_print_unix_dialog_get_selected_printer(dialog
);
1120 settings
= gtk_print_unix_dialog_get_settings(dialog
);
1121 page_setup
= gtk_print_unix_dialog_get_page_setup(dialog
);
1122 job
= gtk_print_job_new(data
, printer
, settings
, page_setup
);
1123 if (gtk_print_job_set_source_file(job
, data
, NULL
))
1124 gtk_print_job_send(job
, NULL
, NULL
, NULL
);
1126 ign_cmd(GTK_TYPE_PRINT_UNIX_DIALOG
, whole_msg
);
1127 g_clear_object(&settings
);
1128 g_clear_object(&job
);
1130 case GTK_RESPONSE_CANCEL
:
1131 case GTK_RESPONSE_DELETE_EVENT
:
1134 fprintf(stderr
, "%s sent an unexpected response id (%d)\n",
1135 widget_name(GTK_WIDGET(dialog
)), response_id
);
1138 gtk_widget_hide(GTK_WIDGET(dialog
));
1140 ign_cmd(GTK_TYPE_PRINT_UNIX_DIALOG
, whole_msg
);
1144 update_image(GtkImage
*image
, const char *action
,
1145 const char *data
, const char *whole_msg
)
1149 gtk_image_get_icon_name(image
, NULL
, &size
);
1150 if (eql(action
, "set_from_file"))
1151 gtk_image_set_from_file(image
, data
);
1152 else if (eql(action
, "set_from_icon_name"))
1153 gtk_image_set_from_icon_name(image
, data
, size
);
1155 ign_cmd(GTK_TYPE_IMAGE
, whole_msg
);
1159 update_notebook(GtkNotebook
*notebook
, const char *action
,
1160 const char *data
, const char *whole_msg
)
1162 if (eql(action
, "set_current_page"))
1163 gtk_notebook_set_current_page(notebook
, strtol(data
, NULL
, 10));
1165 ign_cmd(GTK_TYPE_NOTEBOOK
, whole_msg
);
1169 update_progress_bar(GtkProgressBar
*progressbar
, const char *action
,
1170 const char *data
, const char *whole_msg
)
1172 if (eql(action
, "set_text"))
1173 gtk_progress_bar_set_text(progressbar
, *data
== '\0' ? NULL
: data
);
1174 else if (eql(action
, "set_fraction"))
1175 gtk_progress_bar_set_fraction(progressbar
, strtod(data
, NULL
));
1177 ign_cmd(GTK_TYPE_PROGRESS_BAR
, whole_msg
);
1181 update_scale(GtkRange
*range
, const char *action
,
1182 const char *data
, const char *whole_msg
)
1184 if (eql(action
, "set_value"))
1185 gtk_range_set_value(range
, strtod(data
, NULL
));
1187 ign_cmd(GTK_TYPE_SCALE
, whole_msg
);
1191 update_spinner(GtkSpinner
*spinner
, const char *action
, const char *whole_msg
)
1193 if (eql(action
, "start"))
1194 gtk_spinner_start(spinner
);
1195 else if (eql(action
, "stop"))
1196 gtk_spinner_stop(spinner
);
1198 ign_cmd(GTK_TYPE_SPINNER
, whole_msg
);
1202 update_statusbar(GtkStatusbar
*statusbar
, const char *action
,
1203 const char *data
, const char *whole_msg
)
1205 if (eql(action
, "push"))
1206 gtk_statusbar_push(statusbar
, 0, data
);
1207 else if (eql(action
, "pop"))
1208 gtk_statusbar_pop(statusbar
, 0);
1209 else if (eql(action
, "remove_all"))
1210 gtk_statusbar_remove_all(statusbar
, 0);
1212 ign_cmd(GTK_TYPE_STATUSBAR
, whole_msg
);
1216 update_switch(GtkSwitch
*switcher
, const char *action
,
1217 const char *data
, const char *whole_msg
)
1219 if (eql(action
, "set_active"))
1220 gtk_switch_set_active(switcher
, strtol(data
, NULL
, 10));
1222 ign_cmd(GTK_TYPE_SWITCH
, whole_msg
);
1226 update_text_view(GtkTextView
*view
, const char *action
,
1227 const char *data
, const char *whole_msg
)
1229 GtkTextBuffer
*textbuf
= gtk_text_view_get_buffer(view
);
1232 if (eql(action
, "set_text"))
1233 gtk_text_buffer_set_text(textbuf
, data
, -1);
1234 else if (eql(action
, "delete")) {
1235 gtk_text_buffer_get_bounds(textbuf
, &a
, &b
);
1236 gtk_text_buffer_delete(textbuf
, &a
, &b
);
1237 } else if (eql(action
, "insert_at_cursor"))
1238 gtk_text_buffer_insert_at_cursor(textbuf
, data
, -1);
1239 else if (eql(action
, "place_cursor")) {
1240 if (eql(data
, "end"))
1241 gtk_text_buffer_get_end_iter(textbuf
, &a
);
1242 else /* numeric offset */
1243 gtk_text_buffer_get_iter_at_offset(textbuf
, &a
, strtol(data
, NULL
, 10));
1244 gtk_text_buffer_place_cursor(textbuf
, &a
);
1245 } else if (eql(action
, "place_cursor_at_line")) {
1246 gtk_text_buffer_get_iter_at_line(textbuf
, &a
, strtol(data
, NULL
, 10));
1247 gtk_text_buffer_place_cursor(textbuf
, &a
);
1248 } else if (eql(action
, "scroll_to_cursor"))
1249 gtk_text_view_scroll_to_mark(view
, gtk_text_buffer_get_insert(textbuf
), 0., 0, 0., 0.);
1251 ign_cmd(GTK_TYPE_TEXT_VIEW
, whole_msg
);
1255 update_toggle_button(GtkToggleButton
*toggle
, const char *action
,
1256 const char *data
, const char *whole_msg
, GType type
)
1258 if (eql(action
, "set_label"))
1259 gtk_button_set_label(GTK_BUTTON(toggle
), data
);
1260 else if (eql(action
, "set_active"))
1261 gtk_toggle_button_set_active(toggle
, strtol(data
, NULL
, 10));
1263 ign_cmd(type
, whole_msg
);
1267 update_tree_view(GtkTreeView
*view
, const char *action
,
1268 const char *data
, const char *whole_msg
)
1270 GtkTreeModel
*model
= gtk_tree_view_get_model(view
);
1271 GtkListStore
*store
= GTK_LIST_STORE(model
);
1272 GtkTreeIter iter0
, iter1
;
1273 char *tokens
, *arg0_s
, *arg1_s
, *arg2_s
, *endptr
;
1274 int arg0_n
= 0, arg1_n
= 0;
1275 bool arg0_n_valid
= false, arg1_n_valid
= false;
1276 bool iter0_valid
= false, iter1_valid
= false;
1278 if ((tokens
= malloc(strlen(data
) + 1)) == NULL
)
1280 strcpy(tokens
, data
);
1281 arg0_s
= strtok(tokens
, WHITESPACE
);
1282 arg1_s
= strtok(NULL
, WHITESPACE
);
1283 arg2_s
= strtok(NULL
, "\n");
1288 (arg0_n_valid
= (arg0_n
= strtol(arg0_s
, &endptr
, 10)) >= 0 &&
1289 errno
== 0 && endptr
!= arg0_s
) &&
1290 gtk_tree_model_get_iter_from_string(model
, &iter0
, arg0_s
);
1295 (arg1_n_valid
= (arg1_n
= strtol(arg1_s
, &endptr
, 10)) >= 0 &&
1296 errno
== 0 && endptr
!= arg1_s
) &&
1297 gtk_tree_model_get_iter_from_string(model
, &iter1
, arg1_s
);
1298 if (eql(action
, "set") && iter0_valid
&& arg1_n_valid
&&
1299 arg1_n
< gtk_tree_model_get_n_columns(model
)) {
1300 GType col_type
= gtk_tree_model_get_column_type(model
, arg1_n
);
1305 case G_TYPE_BOOLEAN
:
1314 n
= strtoll(arg2_s
, &endptr
, 10);
1315 if (!errno
&& endptr
!= arg2_s
)
1316 gtk_list_store_set(store
, &iter0
, arg1_n
, n
, -1);
1318 ign_cmd(GTK_TYPE_TREE_VIEW
, whole_msg
);
1324 d
= strtod(arg2_s
, &endptr
);
1325 if (!errno
&& endptr
!= arg2_s
)
1326 gtk_list_store_set(store
, &iter0
, arg1_n
, d
, -1);
1328 ign_cmd(GTK_TYPE_TREE_VIEW
, whole_msg
);
1329 gtk_list_store_set(store
, &iter0
, arg1_n
, strtod(arg2_s
, NULL
), -1);
1332 gtk_list_store_set(store
, &iter0
, arg1_n
, arg2_s
, -1);
1335 fprintf(stderr
, "column %d: %s not implemented\n", arg1_n
, g_type_name(col_type
));
1338 } else if (eql(action
, "scroll") && arg0_n_valid
&& arg1_n_valid
)
1339 gtk_tree_view_scroll_to_cell (view
,
1340 gtk_tree_path_new_from_string(arg0_s
),
1341 gtk_tree_view_get_column(view
, arg1_n
),
1343 else if (eql(action
, "insert_row"))
1344 if (eql(arg0_s
, "end"))
1345 gtk_list_store_insert_before(store
, &iter1
, NULL
);
1346 else if (iter0_valid
)
1347 gtk_list_store_insert_before(store
, &iter1
, &iter0
);
1349 ign_cmd(GTK_TYPE_TREE_VIEW
, whole_msg
);
1350 else if (eql(action
, "move_row") && iter0_valid
)
1351 if (eql(arg1_s
, "end"))
1352 gtk_list_store_move_before(store
, &iter0
, NULL
);
1353 else if (iter1_valid
)
1354 gtk_list_store_move_before(store
, &iter0
, &iter1
);
1356 ign_cmd(GTK_TYPE_TREE_VIEW
, whole_msg
);
1357 else if (eql(action
, "remove_row") && iter0_valid
)
1358 gtk_list_store_remove(store
, &iter0
);
1360 ign_cmd(GTK_TYPE_TREE_VIEW
, whole_msg
);
1365 update_socket(GtkSocket
*socket
, const char *action
,
1366 const char *data
, const char *whole_msg
)
1372 if (eql(action
, "id")) {
1373 id
= gtk_socket_get_id(socket
);
1374 snprintf(str
, BUFLEN
, "%lu", id
);
1375 send_msg(GTK_BUILDABLE(socket
), "id", str
, NULL
);
1377 ign_cmd(GTK_TYPE_SOCKET
, whole_msg
);
1381 update_window(GtkWindow
*window
, const char *action
,
1382 const char *data
, const char *whole_msg
)
1384 if (eql(action
, "set_title"))
1385 gtk_window_set_title(window
, data
);
1387 ign_cmd(GTK_TYPE_WINDOW
, whole_msg
);
1391 fake_ui_activity(GObject
*obj
, const char *whole_msg
, GType type
)
1393 if (!GTK_IS_WIDGET(obj
))
1394 ign_cmd(type
, whole_msg
);
1395 else if (GTK_IS_ENTRY(obj
) || GTK_IS_SPIN_BUTTON(obj
))
1396 cb(GTK_BUILDABLE(obj
), "text");
1397 else if (GTK_IS_SCALE(obj
))
1398 cb(GTK_BUILDABLE(obj
), "value");
1399 else if (GTK_IS_CALENDAR(obj
))
1400 cb(GTK_BUILDABLE(obj
), "clicked");
1401 else if (GTK_IS_FILE_CHOOSER_BUTTON(obj
))
1402 cb(GTK_BUILDABLE(obj
), "file");
1403 else if (!gtk_widget_activate(GTK_WIDGET(obj
)))
1404 ign_cmd(type
, whole_msg
);
1408 * Parse command pointed to by ud, and act on ui accordingly. Set
1409 * ud->digested = true if done. Runs once per command inside
1413 update_ui(struct ui_data
*ud
)
1415 char name
[ud
->msg_size
], action
[ud
->msg_size
];
1417 int data_start
= strlen(ud
->msg
);
1418 GObject
*obj
= NULL
;
1419 GType type
= G_TYPE_INVALID
;
1421 name
[0] = action
[0] = '\0';
1423 " %[0-9a-zA-Z_]:%[0-9a-zA-Z_]%*1[ \t]%n",
1424 name
, action
, &data_start
);
1425 if (eql(action
, "main_quit")) {
1429 if ((obj
= (gtk_builder_get_object(ud
->builder
, name
))) == NULL
) {
1430 ign_cmd(type
, ud
->msg
);
1433 type
= G_TYPE_FROM_INSTANCE(obj
);
1434 data
= ud
->msg
+ data_start
;
1435 if (eql(action
, "force"))
1436 fake_ui_activity(obj
, ud
->msg
, type
);
1437 else if (eql(action
, "set_sensitive"))
1438 gtk_widget_set_sensitive(GTK_WIDGET(obj
), strtol(data
, NULL
, 10));
1439 else if (eql(action
, "set_visible"))
1440 gtk_widget_set_visible(GTK_WIDGET(obj
), strtol(data
, NULL
, 10));
1441 else if (eql(action
, "style"))
1442 update_widget_style(GTK_WIDGET(obj
), name
, data
);
1443 else if (type
== GTK_TYPE_LABEL
)
1444 update_label(GTK_LABEL(obj
), action
, data
, ud
->msg
);
1445 else if (type
== GTK_TYPE_IMAGE
)
1446 update_image(GTK_IMAGE(obj
), action
, data
, ud
->msg
);
1447 else if (type
== GTK_TYPE_TEXT_VIEW
)
1448 update_text_view(GTK_TEXT_VIEW(obj
), action
, data
, ud
->msg
);
1449 else if (type
== GTK_TYPE_NOTEBOOK
)
1450 update_notebook(GTK_NOTEBOOK(obj
), action
, data
, ud
->msg
);
1451 else if (type
== GTK_TYPE_EXPANDER
)
1452 update_expander(GTK_EXPANDER(obj
), action
, data
, ud
->msg
);
1453 else if (type
== GTK_TYPE_FRAME
)
1454 update_frame(GTK_FRAME(obj
), action
, data
, ud
->msg
);
1455 else if (type
== GTK_TYPE_BUTTON
)
1456 update_button(GTK_BUTTON(obj
), action
, data
, ud
->msg
);
1457 else if (type
== GTK_TYPE_FILE_CHOOSER_DIALOG
)
1458 update_file_chooser_dialog(GTK_FILE_CHOOSER(obj
), action
, data
, ud
->msg
);
1459 else if (type
== GTK_TYPE_FILE_CHOOSER_BUTTON
)
1460 update_file_chooser_button(GTK_FILE_CHOOSER(obj
), action
, data
, ud
->msg
);
1461 else if (type
== GTK_TYPE_COLOR_BUTTON
)
1462 update_color_button(GTK_COLOR_CHOOSER(obj
), action
, data
, ud
->msg
);
1463 else if (type
== GTK_TYPE_FONT_BUTTON
)
1464 update_font_button(GTK_FONT_BUTTON(obj
), action
, data
, ud
->msg
);
1465 else if (type
== GTK_TYPE_PRINT_UNIX_DIALOG
)
1466 update_print_dialog(GTK_PRINT_UNIX_DIALOG(obj
), action
, data
, ud
->msg
);
1467 else if (type
== GTK_TYPE_SWITCH
)
1468 update_switch(GTK_SWITCH(obj
), action
, data
, ud
->msg
);
1469 else if (type
== GTK_TYPE_TOGGLE_BUTTON
|| type
== GTK_TYPE_RADIO_BUTTON
|| type
== GTK_TYPE_CHECK_BUTTON
)
1470 update_toggle_button(GTK_TOGGLE_BUTTON(obj
), action
, data
, ud
->msg
, type
);
1471 else if (type
== GTK_TYPE_SPIN_BUTTON
|| type
== GTK_TYPE_ENTRY
)
1472 update_entry(GTK_ENTRY(obj
), action
, data
, ud
->msg
, type
);
1473 else if (type
== GTK_TYPE_SCALE
)
1474 update_scale(GTK_RANGE(obj
), action
, data
, ud
->msg
);
1475 else if (type
== GTK_TYPE_PROGRESS_BAR
)
1476 update_progress_bar(GTK_PROGRESS_BAR(obj
), action
, data
, ud
->msg
);
1477 else if (type
== GTK_TYPE_SPINNER
)
1478 update_spinner(GTK_SPINNER(obj
), action
, ud
->msg
);
1479 else if (type
== GTK_TYPE_COMBO_BOX_TEXT
)
1480 update_combo_box_text(GTK_COMBO_BOX_TEXT(obj
), action
, data
, ud
->msg
);
1481 else if (type
== GTK_TYPE_STATUSBAR
)
1482 update_statusbar(GTK_STATUSBAR(obj
), action
, data
, ud
->msg
);
1483 else if (type
== GTK_TYPE_CALENDAR
)
1484 update_calendar(GTK_CALENDAR(obj
), action
, data
, ud
->msg
);
1485 else if (type
== GTK_TYPE_TREE_VIEW
)
1486 update_tree_view(GTK_TREE_VIEW(obj
), action
, data
, ud
->msg
);
1487 else if (type
== GTK_TYPE_DRAWING_AREA
)
1488 update_drawing_area(GTK_WIDGET(obj
), action
, data
, ud
->msg
);
1489 else if (type
== GTK_TYPE_SOCKET
)
1490 update_socket(GTK_SOCKET(obj
), action
, data
, ud
->msg
);
1491 else if (type
== GTK_TYPE_WINDOW
)
1492 update_window(GTK_WINDOW(obj
), action
, data
, ud
->msg
);
1494 ign_cmd(type
, ud
->msg
);
1496 ud
->msg_digested
= true;
1497 return G_SOURCE_REMOVE
;
1507 * Read lines from global stream "in" and perform the appropriate
1508 * actions on the GUI
1511 digest_msg(void *builder
)
1514 char first_char
= '\0';
1517 if ((ud
.msg
= malloc(ud
.msg_size
= 32)) == NULL
)
1519 pthread_cleanup_push((void(*)(void *))free_at
, &ud
.msg
);
1520 pthread_testcancel();
1521 read_buf(in
, &ud
.msg
, &ud
.msg_size
);
1522 sscanf(ud
.msg
, " %c", &first_char
);
1523 ud
.builder
= builder
;
1524 if (first_char
!= '#') {
1525 ud
.msg_digested
= false;
1526 pthread_testcancel();
1527 gdk_threads_add_timeout(1, (GSourceFunc
)update_ui
, &ud
);
1528 while (!ud
.msg_digested
)
1529 nanosleep(&(struct timespec
){0, 1e6
}, NULL
);
1531 pthread_cleanup_pop(1);
1537 * Create a fifo if necessary, and open it. Give up if the file
1538 * exists but is not a fifo
1541 fifo(const char *name
, const char *mode
)
1548 if (name
!= NULL
&& (stat(name
, &sb
), !S_ISFIFO(sb
.st_mode
)))
1549 if (mkfifo(name
, 0666) != 0)
1550 bye(EXIT_FAILURE
, stderr
,
1551 "making fifo: %s\n", strerror(errno
));
1558 fd
= open(name
, O_RDWR
| O_NONBLOCK
);
1560 bye(EXIT_FAILURE
, stderr
,
1561 "opening fifo: %s\n", strerror(errno
));
1562 s
= fdopen(fd
, "r");
1570 s
= fopen(name
, "w+");
1577 bye(EXIT_FAILURE
, stderr
, "opening fifo: %s\n", strerror(errno
));
1579 setvbuf(s
, NULL
, bufmode
, 0);
1584 * Remove suffix from name; find the object named like this
1587 obj_sans_suffix(const char *suffix
, const char *name
, gpointer
*builder
)
1590 char str
[BUFLEN
+ 1] = {'\0'};
1592 str_l
= suffix
- name
;
1593 strncpy(str
, name
, str_l
< BUFLEN
? str_l
: BUFLEN
);
1594 return gtk_builder_get_object(GTK_BUILDER(builder
), str
);
1598 connect_widget_signals(gpointer
*obj
, gpointer
*builder
)
1600 const char *name
= NULL
;
1601 char *suffix
= NULL
;
1603 GType type
= G_TYPE_INVALID
;
1605 type
= G_TYPE_FROM_INSTANCE(obj
);
1606 if (GTK_IS_BUILDABLE(obj
))
1607 name
= widget_name(obj
);
1608 if (type
== GTK_TYPE_TREE_VIEW_COLUMN
)
1609 g_signal_connect(obj
, "clicked", G_CALLBACK(cb
), "clicked");
1610 else if (type
== GTK_TYPE_BUTTON
) {
1611 /* Button associated with a GtkTextView. */
1612 if ((suffix
= strstr(name
, "_send_text")) != NULL
&&
1613 GTK_IS_TEXT_VIEW(obj2
= obj_sans_suffix(suffix
, name
, builder
)))
1614 g_signal_connect(obj
, "clicked", G_CALLBACK(cb_send_text
),
1615 gtk_text_view_get_buffer(GTK_TEXT_VIEW(obj2
)));
1616 else if ((suffix
= strstr(name
, "_send_selection")) != NULL
&&
1617 GTK_IS_TEXT_VIEW(obj2
= obj_sans_suffix(suffix
, name
, builder
)))
1618 g_signal_connect(obj
, "clicked", G_CALLBACK(cb_send_text_selection
),
1619 gtk_text_view_get_buffer(GTK_TEXT_VIEW(obj2
)));
1620 /* Buttons associated with (and part of) a GtkDialog.
1621 * (We shun response ids which could be returned from
1622 * gtk_dialog_run() because that would require the
1623 * user to define those response ids in Glade,
1625 else if ((suffix
= strstr(name
, "_cancel")) != NULL
&&
1626 GTK_IS_DIALOG(obj2
= obj_sans_suffix(suffix
, name
, builder
)))
1627 if (eql(widget_name(obj2
), MAIN_WIN
))
1628 g_signal_connect(obj
, "clicked", G_CALLBACK(gtk_main_quit
), NULL
);
1630 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(gtk_widget_hide
), obj2
);
1631 else if ((suffix
= strstr(name
, "_ok")) != NULL
&&
1632 GTK_IS_DIALOG(obj2
= obj_sans_suffix(suffix
, name
, builder
))) {
1633 if (GTK_IS_FILE_CHOOSER_DIALOG(obj2
))
1634 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(cb_send_file_chooser_dialog_selection
), obj2
);
1635 else /* generic button */
1636 g_signal_connect(obj
, "clicked", G_CALLBACK(cb
), "clicked");
1637 if (eql(widget_name(obj2
), MAIN_WIN
))
1638 g_signal_connect(obj
, "clicked", G_CALLBACK(gtk_main_quit
), NULL
);
1640 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(gtk_widget_hide
), obj2
);
1641 } else if ((suffix
= strstr(name
, "_apply")) != NULL
&&
1642 GTK_IS_FILE_CHOOSER_DIALOG(obj2
= obj_sans_suffix(suffix
, name
, builder
)))
1643 g_signal_connect_swapped(obj
, "clicked", G_CALLBACK(cb_send_file_chooser_dialog_selection
), obj2
);
1644 else /* generic button */
1645 g_signal_connect(obj
, "clicked", G_CALLBACK(cb
), "clicked");
1647 else if (GTK_IS_MENU_ITEM(obj
))
1648 if ((suffix
= strstr(name
, "_invoke")) != NULL
&&
1649 GTK_IS_DIALOG(obj2
= obj_sans_suffix(suffix
, name
, builder
)))
1650 g_signal_connect_swapped(obj
, "activate", G_CALLBACK(gtk_widget_show
), obj2
);
1652 g_signal_connect(obj
, "activate", G_CALLBACK(cb
), "active");
1653 else if (GTK_IS_WINDOW(obj
))
1654 if (eql(name
, MAIN_WIN
))
1655 g_signal_connect(obj
, "delete-event", G_CALLBACK(gtk_main_quit
), NULL
);
1657 g_signal_connect(obj
, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete
), NULL
);
1658 else if (type
== GTK_TYPE_FILE_CHOOSER_BUTTON
)
1659 g_signal_connect(obj
, "file-set", G_CALLBACK(cb
), "file");
1660 else if (type
== GTK_TYPE_COLOR_BUTTON
)
1661 g_signal_connect(obj
, "color-set", G_CALLBACK(cb
), "color");
1662 else if (type
== GTK_TYPE_FONT_BUTTON
)
1663 g_signal_connect(obj
, "font-set", G_CALLBACK(cb
), "font");
1664 else if (type
== GTK_TYPE_SWITCH
)
1665 g_signal_connect(obj
, "notify::active", G_CALLBACK(cb
), NULL
);
1666 else if (type
== GTK_TYPE_TOGGLE_BUTTON
|| type
== GTK_TYPE_RADIO_BUTTON
|| type
== GTK_TYPE_CHECK_BUTTON
)
1667 g_signal_connect(obj
, "toggled", G_CALLBACK(cb
), NULL
);
1668 else if (type
== GTK_TYPE_SPIN_BUTTON
|| type
== GTK_TYPE_ENTRY
)
1669 g_signal_connect(obj
, "changed", G_CALLBACK(cb
), "text");
1670 else if (type
== GTK_TYPE_SCALE
)
1671 g_signal_connect(obj
, "value-changed", G_CALLBACK(cb
), "value");
1672 else if (type
== GTK_TYPE_CALENDAR
) {
1673 g_signal_connect(obj
, "day-selected-double-click", G_CALLBACK(cb
), "doubleclicked");
1674 g_signal_connect(obj
, "day-selected", G_CALLBACK(cb
), "clicked");
1675 } else if (type
== GTK_TYPE_TREE_SELECTION
)
1676 g_signal_connect(obj
, "changed", G_CALLBACK(cb
), "clicked");
1677 else if (type
== GTK_TYPE_SOCKET
) {
1678 g_signal_connect(obj
, "plug-added", G_CALLBACK(cb
), "plug-added");
1679 g_signal_connect(obj
, "plug-removed", G_CALLBACK(cb_true
), "plug-removed");
1680 } else if (type
== GTK_TYPE_DRAWING_AREA
)
1681 g_signal_connect(obj
, "draw", G_CALLBACK(cb_draw
), NULL
);
1685 * We keep a list of one widget style provider for each widget
1688 add_widget_style_provider(gpointer
*obj
, void *data
)
1690 const char *name
= NULL
;
1691 struct style_provider
*sp
= NULL
, *last_sp
= NULL
;
1692 GtkStyleContext
*context
;
1695 if (!GTK_IS_WIDGET(obj
))
1697 if ((sp
= malloc(sizeof(struct style_provider
))) == NULL
)
1699 name
= widget_name(obj
);
1701 sp
->provider
= gtk_css_provider_new();
1702 if ((sp
->style_decl
= malloc(sizeof('\0'))) == NULL
)
1704 *(sp
->style_decl
) = '\0';
1705 context
= gtk_widget_get_style_context(GTK_WIDGET(obj
));
1706 gtk_style_context_add_provider(context
,
1707 GTK_STYLE_PROVIDER(sp
->provider
),
1708 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
1709 gtk_css_provider_load_from_data(sp
->provider
, sp
->style_decl
, -1, NULL
);
1710 if (widget_style_providers
== NULL
) {
1711 widget_style_providers
= sp
;
1712 insque(widget_style_providers
, NULL
);
1714 for (last_sp
= widget_style_providers
;
1715 last_sp
->next
!= NULL
;
1716 last_sp
= last_sp
->next
);
1717 insque(sp
, last_sp
);
1722 prepare_widgets(GtkBuilder
*builder
)
1724 GSList
*objects
= NULL
;
1726 objects
= gtk_builder_get_objects(builder
);
1727 g_slist_foreach(objects
, (GFunc
)connect_widget_signals
, builder
);
1728 g_slist_foreach(objects
, (GFunc
)add_widget_style_provider
, NULL
);
1729 g_slist_free(objects
);
1733 main(int argc
, char *argv
[])
1736 char *in_fifo
= NULL
, *out_fifo
= NULL
, *ui_file
= NULL
;
1737 char *xid_s
= NULL
, xid_s2
[BUFLEN
];
1739 GtkWidget
*plug
, *body
;
1740 GtkBuilder
*builder
;
1742 GError
*error
= NULL
;
1743 GObject
*main_window
= NULL
;
1745 /* Disable runtime GLIB deprecation warnings: */
1746 setenv("G_ENABLE_DIAGNOSTIC", "0", 0);
1749 while ((opt
= getopt(argc
, argv
, "he:i:o:u:GV")) != -1) {
1751 case 'e': xid_s
= optarg
; break;
1752 case 'i': in_fifo
= optarg
; break;
1753 case 'o': out_fifo
= optarg
; break;
1754 case 'u': ui_file
= optarg
; break;
1755 case 'G': bye(EXIT_SUCCESS
, stdout
, "GTK+ v%d.%d.%d (running v%d.%d.%d)\n",
1756 GTK_MAJOR_VERSION
, GTK_MINOR_VERSION
, GTK_MICRO_VERSION
,
1757 gtk_get_major_version(), gtk_get_minor_version(),
1758 gtk_get_micro_version());
1760 case 'V': bye(EXIT_SUCCESS
, stdout
, "%s\n", VERSION
); break;
1761 case 'h': bye(EXIT_SUCCESS
, stdout
, USAGE
); break;
1763 default: bye(EXIT_FAILURE
, stderr
, USAGE
); break;
1766 if (argv
[optind
] != NULL
)
1767 bye(EXIT_FAILURE
, stderr
,
1768 "illegal parameter '%s'\n" USAGE
, argv
[optind
]);
1769 if (ui_file
== NULL
)
1770 ui_file
= "pipeglade.ui";
1772 builder
= gtk_builder_new();
1773 if (gtk_builder_add_from_file(builder
, ui_file
, &error
) == 0)
1774 bye(EXIT_FAILURE
, stderr
, "%s\n", error
->message
);
1775 in
= fifo(in_fifo
, "r");
1776 out
= fifo(out_fifo
, "w");
1777 pthread_create(&receiver
, NULL
, digest_msg
, (void*)builder
);
1778 main_window
= gtk_builder_get_object(builder
, MAIN_WIN
);
1779 if (!GTK_IS_WINDOW(main_window
))
1780 bye(EXIT_FAILURE
, stderr
,
1781 "no toplevel window named \'" MAIN_WIN
"\'\n");
1782 prepare_widgets(builder
);
1783 if (xid_s
== NULL
) /* standalone */
1784 gtk_widget_show(GTK_WIDGET(main_window
));
1785 else { /* We're being XEmbedded */
1786 xid
= strtoul(xid_s
, NULL
, 10);
1787 snprintf(xid_s2
, BUFLEN
, "%lu", xid
);
1788 if (!eql(xid_s
, xid_s2
))
1789 bye(EXIT_FAILURE
, stderr
,
1790 "%s is not a valid XEmbed socket id\n", xid_s
);
1791 body
= gtk_bin_get_child(GTK_BIN(main_window
));
1792 gtk_container_remove(GTK_CONTAINER(main_window
), body
);
1793 plug
= gtk_plug_new(xid
);
1794 if (!gtk_plug_get_embedded(GTK_PLUG(plug
)))
1795 bye(EXIT_FAILURE
, stderr
,
1796 "unable to embed into XEmbed socket %s\n", xid_s
);
1797 gtk_container_add(GTK_CONTAINER(plug
), body
);
1798 gtk_widget_show(plug
);
1805 if (out
!= stdout
) {
1809 pthread_cancel(receiver
);
1810 pthread_join(receiver
, NULL
);