generalise cluster pruning routines
[sparrow.git] / gtk-app.c
blobc2d84ec5b651e1dafbf8f6f46aa803511385df42
1 /*
2 initially based on an example by Tristan Matthews
3 http://tristanswork.blogspot.com/2008/09/fullscreen-video-in-gstreamer-with-gtk.html
4 */
5 #include <gst/gst.h>
6 #include <gtk/gtk.h>
7 #include <gst/interfaces/xoverlay.h>
8 #include <gdk/gdk.h>
9 #include <gdk/gdkx.h>
10 #include "gtk-app.h"
13 static void hide_mouse(GtkWidget *widget){
14 GdkWindow *w = GDK_WINDOW(widget->window);
15 GdkDisplay *display = gdk_display_get_default();
16 GdkCursor *cursor = gdk_cursor_new_for_display(display, GDK_BLANK_CURSOR);
17 gdk_window_set_cursor(w, cursor);
18 gdk_cursor_unref (cursor);
21 static void
22 post_tee_pipeline(GstPipeline *pipeline, GstElement *tee, GstElement *sink,
23 int rngseed, int colour, int timer, int debug, char *save, char *reload){
24 GstElement *queue = gst_element_factory_make("queue", NULL);
25 GstElement *sparrow = gst_element_factory_make("sparrow", NULL);
26 GstElement *caps_posteriori = gst_element_factory_make("capsfilter", NULL);
27 GstElement *cs_posteriori = gst_element_factory_make("ffmpegcolorspace", NULL);
29 g_object_set(G_OBJECT(caps_posteriori), "caps",
30 gst_caps_new_simple ("video/x-raw-rgb",
31 COMMON_CAPS), NULL);
33 g_object_set(G_OBJECT(sparrow),
34 "timer", timer,
35 "debug", debug,
36 "rngseed", rngseed,
37 "colour", colour,
38 NULL);
39 if (reload){
40 g_object_set(G_OBJECT(sparrow),
41 "reload", reload,
42 NULL);
44 if (save){
45 g_object_set(G_OBJECT(sparrow),
46 "save", save,
47 NULL);
50 gst_bin_add_many (GST_BIN(pipeline),
51 queue,
52 sparrow,
53 caps_posteriori,
54 cs_posteriori,
55 sink,
56 NULL);
58 gst_element_link_many(tee,
59 queue,
60 sparrow,
61 caps_posteriori,
62 cs_posteriori,
63 sink,
64 NULL);
67 static GstElement *
68 pre_tee_pipeline(GstPipeline *pipeline){
69 if (pipeline == NULL){
70 pipeline = GST_PIPELINE(gst_pipeline_new("sparrow_pipeline"));
72 GstElement *src = gst_element_factory_make("v4l2src", NULL);
73 GstElement *caps_priori = gst_element_factory_make("capsfilter", NULL);
74 GstElement *cs_priori = gst_element_factory_make("ffmpegcolorspace", NULL);
75 GstElement *caps_interiori = gst_element_factory_make("capsfilter", NULL);
76 GstElement *tee = gst_element_factory_make ("tee", NULL);
78 g_object_set(G_OBJECT(caps_priori), "caps",
79 gst_caps_new_simple ("video/x-raw-yuv",
80 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('Y', 'U', 'Y', '2'),
81 COMMON_CAPS), NULL);
83 g_object_set(G_OBJECT(caps_interiori), "caps",
84 gst_caps_new_simple ("video/x-raw-rgb",
85 COMMON_CAPS), NULL);
87 gst_bin_add_many(GST_BIN(pipeline),
88 src,
89 caps_priori,
90 cs_priori,
91 //caps_interiori,
92 tee,
93 NULL);
95 gst_element_link_many(src,
96 caps_priori,
97 cs_priori,
98 //caps_interiori,
99 tee,
100 NULL);
101 return tee;
105 static GstPipeline *
106 make_multi_pipeline(windows_t *windows, int count)
108 GstPipeline *pipeline = GST_PIPELINE(gst_pipeline_new("sparrow_pipeline"));
109 GstElement *tee = pre_tee_pipeline(pipeline);
110 char *reload = NULL;
111 char *save = NULL;
112 int i;
113 for (i = 0; i < count; i++){
114 GstElement *sink = windows->sinks[i];
115 //args are:
116 //(pipeline, tee, sink, int rngseed, int colour, timer flag, int debug flag)
117 /* timer should only run on one of them. colour >= 3 is undefined */
118 int debug = option_debug == i;
119 if (option_reload != NULL){
120 if (option_reload[i] == NULL){
121 g_critical("You can't reload some screens and not others!");
122 exit(1);
124 reload = option_reload[i];
126 if (option_save && option_save[i]){
127 save = option_save[i];
129 post_tee_pipeline(pipeline, tee, sink, i, i + 1, i == 0, debug, save, reload);
131 return pipeline;
135 static void
136 bus_call(GstBus * bus, GstMessage *msg, gpointer data)
138 windows_t *windows = (windows_t *)data;
139 if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ELEMENT &&
140 gst_structure_has_name(msg->structure, "prepare-xwindow-id")){
141 g_print("Got prepare-xwindow-id msg. option screens: %d\n", option_screens);
142 for (int i = 0; i < option_screens; i++){
143 gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(windows->sinks[i]),
144 windows->xwindows[i]);
145 g_print("connected sink %d to window %lu\n", i, windows->xwindows[i]);
146 hide_mouse(windows->gtk_windows[i]);
151 static void
152 toggle_fullscreen(GtkWidget *widget){
153 GdkWindowState state = gdk_window_get_state(GDK_WINDOW(widget->window));
154 if (state == GDK_WINDOW_STATE_FULLSCREEN){
155 gtk_window_unfullscreen(GTK_WINDOW(widget));
157 else{
158 gtk_window_fullscreen(GTK_WINDOW(widget));
162 static gboolean
163 key_press_event_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
165 g_print("got key %c\n", event->keyval);
166 switch (event->keyval){
167 case 'f':
168 toggle_fullscreen(widget);
169 break;
170 case 'q':
171 g_signal_emit_by_name(widget, "destroy");
172 break;
173 default:
174 break;
176 return TRUE;
179 void destroy_cb(GtkWidget * widget, gpointer data)
181 GMainLoop *loop = (GMainLoop*) data;
182 g_print("Window destroyed\n");
183 g_main_loop_quit(loop);
186 static void
187 video_widget_realize_cb(GtkWidget *widget, gpointer data)
189 windows_t *windows = (windows_t *)data;
190 int r = windows->realised;
191 if (r < MAX_SCREENS){
192 windows->xwindows[r] = GDK_WINDOW_XID(GDK_WINDOW(widget->window));
193 g_print("realised window %d with XID %lu\n", r, windows->xwindows[r]);
195 else {
196 g_print("wtf, there seem to be %d windows!\n", r);
198 windows->realised++;
199 hide_mouse(widget);
203 static void
204 set_up_window(GMainLoop *loop, GtkWidget *window, int screen_no){
205 static const GdkColor black = {0, 0, 0, 0};
206 gtk_window_set_default_size(GTK_WINDOW(window), WIDTH, HEIGHT);
208 if (option_fullscreen){
209 gtk_window_fullscreen(GTK_WINDOW(window));
212 /*if more than one screen is requested, set the screen number.
213 otherwise let it fall were it falls */
214 if (option_screens > 1){
215 /* "screen" is not the same thing as "monitor" */
216 GdkScreen * screen = gdk_screen_get_default();
217 int width = gdk_screen_get_width(screen);
218 /* XXX window selection is specific to equally sized windows arranged
219 horizontally. This could be generalised, perhaps using trial and
220 error */
221 gtk_window_move(GTK_WINDOW(window),
222 (width / 2 * screen_no + 50), 50);
225 // attach key press signal to key press callback
226 gtk_widget_set_events(window, GDK_KEY_PRESS_MASK);
227 g_signal_connect(G_OBJECT(window), "key-press-event", G_CALLBACK(key_press_event_cb), NULL);
228 g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy_cb), loop);
230 gtk_widget_modify_bg(window, GTK_STATE_NORMAL, &black);
231 gtk_widget_show_all(window);
232 hide_mouse(window);
236 static GOptionEntry entries[] =
238 { "full-screen", 'f', 0, G_OPTION_ARG_NONE, &option_fullscreen, "run full screen", NULL },
239 { "screens", 's', 0, G_OPTION_ARG_INT, &option_screens, "Use this many screens", "S" },
240 { "debug", 'd', 0, G_OPTION_ARG_INT, &option_debug, "Save screen's debug images in /tmp", "SCREEN" },
241 { "reload", 'r', 0, G_OPTION_ARG_FILENAME_ARRAY, &option_reload,
242 "load calibration data from FILE (one per screen)", "FILE" },
243 { "save", 'S', 0, G_OPTION_ARG_FILENAME_ARRAY, &option_save,
244 "save calibration data to FILE (one per screen)", "FILE" },
245 // { "overlay", 'o', 0, G_OPTION_ARG_NONE, &option_overlay, "Use some kind of overlay", NULL },
246 { NULL, 0, 0, 0, NULL, NULL, NULL }
250 gint main (gint argc, gchar *argv[])
252 //initialise threads before any gtk stuff (because not using gtk_init)
253 g_thread_init(NULL);
254 /*this is more complicated than plain gtk_init/gst_init, so that options from
255 all over can be gathered and presented together.
257 GOptionGroup *gst_opts = gst_init_get_option_group();
258 GOptionGroup *gtk_opts = gtk_get_option_group(TRUE);
259 GOptionContext *ctx = g_option_context_new("...!");
260 g_option_context_add_main_entries(ctx, entries, NULL);
261 g_option_context_add_group(ctx, gst_opts);
262 g_option_context_add_group(ctx, gtk_opts);
263 GError *error = NULL;
264 if (!g_option_context_parse(ctx, &argc, &argv, &error)){
265 g_print ("Error initializing: %s\n", GST_STR_NULL(error->message));
266 exit (1);
268 g_option_context_free(ctx);
270 GMainLoop *loop = g_main_loop_new(NULL, FALSE);
272 windows_t windows;
273 windows.realised = 0;
275 int i;
276 for (i = 0; i < option_screens; i++){
277 GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
278 g_signal_connect(window, "realize",
279 G_CALLBACK(video_widget_realize_cb), &windows);
280 /* set up sink here */
281 GstElement *sink = gst_element_factory_make("ximagesink", NULL);
282 set_up_window(loop, window, i);
283 windows.gtk_windows[i] = window;
284 windows.sinks[i] = sink;
287 GstElement *pipeline = (GstElement *)make_multi_pipeline(&windows, option_screens);
289 GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
290 gst_bus_add_watch(bus, (GstBusFunc)bus_call, &windows);
291 gst_object_unref(bus);
293 gst_element_set_state(pipeline, GST_STATE_PLAYING);
295 g_main_loop_run(loop);
297 gst_element_set_state (pipeline, GST_STATE_NULL);
298 gst_object_unref (pipeline);
299 return 0;