don't fake screen edge with erosion (debug trick)
[sparrow.git] / gtk-app.c
blobc86171012f57bc0aca2a31d5db26255e618d116a
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 static GdkCursor *cursor = NULL;
15 GdkWindow *w = GDK_WINDOW(widget->window);
16 if (cursor == NULL){
17 GdkDisplay *display = gdk_display_get_default();
18 cursor = gdk_cursor_new_for_display(display, GDK_BLANK_CURSOR);
19 gdk_window_set_cursor(w, cursor);
23 static void
24 post_tee_pipeline(GstPipeline *pipeline, GstElement *tee, GstElement *sink,
25 int rngseed, int colour, int timer, int debug){
26 GstElement *queue = gst_element_factory_make ("queue", NULL);
27 GstElement *sparrow = gst_element_factory_make("sparrow", NULL);
28 GstElement *caps_posteriori = gst_element_factory_make("capsfilter", NULL);
29 GstElement *cs_posteriori = gst_element_factory_make("ffmpegcolorspace", NULL);
31 g_object_set(G_OBJECT(caps_posteriori), "caps",
32 gst_caps_new_simple ("video/x-raw-rgb",
33 COMMON_CAPS), NULL);
35 g_object_set(G_OBJECT(sparrow),
36 "timer", timer,
37 "debug", debug,
38 "rngseed", rngseed,
39 "colour", colour,
40 //"reload", "dumpfiles/gtk.dump",
41 //"save", "dumpfiles/gtk.dump",
42 NULL);
44 gst_bin_add_many (GST_BIN(pipeline),
45 queue,
46 sparrow,
47 caps_posteriori,
48 cs_posteriori,
49 sink,
50 NULL);
52 gst_element_link_many(tee,
53 queue,
54 sparrow,
55 caps_posteriori,
56 cs_posteriori,
57 sink,
58 NULL);
61 static GstElement *
62 pre_tee_pipeline(GstPipeline *pipeline){
63 if (pipeline == NULL){
64 pipeline = GST_PIPELINE(gst_pipeline_new("sparrow_pipeline"));
66 GstElement *src = gst_element_factory_make("v4l2src", NULL);
67 GstElement *caps_priori = gst_element_factory_make("capsfilter", NULL);
68 GstElement *cs_priori = gst_element_factory_make("ffmpegcolorspace", NULL);
69 GstElement *caps_interiori = gst_element_factory_make("capsfilter", NULL);
70 GstElement *tee = gst_element_factory_make ("tee", NULL);
72 g_object_set(G_OBJECT(caps_priori), "caps",
73 gst_caps_new_simple ("video/x-raw-yuv",
74 "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC('Y', 'U', 'Y', '2'),
75 COMMON_CAPS), NULL);
77 g_object_set(G_OBJECT(caps_interiori), "caps",
78 gst_caps_new_simple ("video/x-raw-rgb",
79 COMMON_CAPS), NULL);
81 gst_bin_add_many(GST_BIN(pipeline),
82 src,
83 caps_priori,
84 cs_priori,
85 //caps_interiori,
86 tee,
87 NULL);
89 gst_element_link_many(src,
90 caps_priori,
91 cs_priori,
92 //caps_interiori,
93 tee,
94 NULL);
95 return tee;
99 static GstPipeline *
100 make_multi_pipeline(windows_t *windows, int count)
102 GstPipeline *pipeline = GST_PIPELINE(gst_pipeline_new("sparrow_pipeline"));
103 GstElement *tee = pre_tee_pipeline(pipeline);
105 int i;
106 for (i = 0; i < count; i++){
107 GstElement *sink = windows->sinks[i];
108 //args are:
109 //(pipeline, tee, sink, int rngseed, int colour, timer flag, int debug flag)
110 /* timer should only run on one of them. colour >= 3 is undefined */
111 int debug = option_debug && i == 0;
112 post_tee_pipeline(pipeline, tee, sink, i, i + 1, i == 0, debug);
114 return pipeline;
118 static void
119 bus_call(GstBus * bus, GstMessage *msg, gpointer data)
121 windows_t *windows = (windows_t *)data;
122 if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ELEMENT &&
123 gst_structure_has_name(msg->structure, "prepare-xwindow-id")){
124 g_print("Got prepare-xwindow-id msg. option screens: %d\n", option_screens);
125 for (int i = 0; i < option_screens; i++){
126 gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(windows->sinks[i]),
127 windows->xwindows[i]);
128 g_print("connected sink %d to window %lu\n", i, windows->xwindows[i]);
129 hide_mouse(windows->gtk_windows[i]);
134 static void
135 toggle_fullscreen(GtkWidget *widget){
136 GdkWindowState state = gdk_window_get_state(GDK_WINDOW(widget->window));
137 if (state == GDK_WINDOW_STATE_FULLSCREEN){
138 gtk_window_unfullscreen(GTK_WINDOW(widget));
140 else{
141 gtk_window_fullscreen(GTK_WINDOW(widget));
145 static gboolean
146 key_press_event_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
148 g_print("got key %c\n", event->keyval);
149 switch (event->keyval){
150 case 'f':
151 toggle_fullscreen(widget);
152 break;
153 case 'q':
154 g_signal_emit_by_name(widget, "destroy");
155 break;
156 default:
157 break;
159 return TRUE;
162 void destroy_cb(GtkWidget * widget, gpointer data)
164 GMainLoop *loop = (GMainLoop*) data;
165 g_print("Window destroyed\n");
166 g_main_loop_quit(loop);
169 static void
170 video_widget_realize_cb(GtkWidget *widget, gpointer data)
172 windows_t *windows = (windows_t *)data;
173 int r = windows->realised;
174 if (r < MAX_SCREENS){
175 windows->xwindows[r] = GDK_WINDOW_XID(GDK_WINDOW(widget->window));
176 g_print("realised window %d with XID %lu\n", r, windows->xwindows[r]);
178 else {
179 g_print("wtf, there seem to be %d windows!\n", r);
181 windows->realised++;
182 hide_mouse(widget);
186 static void
187 set_up_window(GMainLoop *loop, GtkWidget *window, int screen_no){
188 static const GdkColor black = {0, 0, 0, 0};
189 gtk_window_set_default_size(GTK_WINDOW(window), WIDTH, HEIGHT);
191 if (option_fullscreen){
192 gtk_window_fullscreen(GTK_WINDOW(window));
195 /*if more than one screen is requested, set the screen number.
196 otherwise let it fall were it falls */
197 if (option_screens > 1){
198 /* "screen" is not the same thing as "monitor" */
199 GdkScreen * screen = gdk_screen_get_default();
200 int width = gdk_screen_get_width(screen);
201 //int monitor = gdk_screen_get_monitor_at_point(screen,
202 // width / 2 + width * screen_no, 50);
203 gtk_window_move(GTK_WINDOW(window), (width / 2 * screen_no + 50), 50);
206 //GdkDisplay *display = gdk_display_get_default();
207 //GdkScreen *screen = gdk_display_get_screen(display, screen_no);
208 //gtk_window_set_screen(GTK_WINDOW(window), screen);
209 hide_mouse(window);
212 // attach key press signal to key press callback
213 gtk_widget_set_events(window, GDK_KEY_PRESS_MASK);
214 g_signal_connect(G_OBJECT(window), "key-press-event", G_CALLBACK(key_press_event_cb), NULL);
215 g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy_cb), loop);
217 gtk_widget_modify_bg(window, GTK_STATE_NORMAL, &black);
218 gtk_widget_show_all(window);
219 hide_mouse(window);
223 static GOptionEntry entries[] =
225 { "full-screen", 'f', 0, G_OPTION_ARG_NONE, &option_fullscreen, "run full screen", NULL },
226 { "screens", 's', 2, G_OPTION_ARG_INT, &option_screens, "Use this many screens", "S" },
227 { "debug", 'd', 2, G_OPTION_ARG_NONE, &option_debug, "Save debug images in /tmp", NULL },
228 // { "overlay", 'o', 0, G_OPTION_ARG_NONE, &option_overlay, "Use some kind of overlay", NULL },
229 { NULL, 0, 0, 0, NULL, NULL, NULL }
233 gint main (gint argc, gchar *argv[])
235 //initialise threads before any gtk stuff (because not using gtk_init)
236 g_thread_init(NULL);
237 /*this is more complicated than plain gtk_init/gst_init, so that options from
238 all over can be gathered and presented together.
240 GOptionGroup *gst_opts = gst_init_get_option_group();
241 GOptionGroup *gtk_opts = gtk_get_option_group(TRUE);
242 GOptionContext *ctx = g_option_context_new("...!");
243 g_option_context_add_main_entries(ctx, entries, NULL);
244 g_option_context_add_group(ctx, gst_opts);
245 g_option_context_add_group(ctx, gtk_opts);
246 GError *error = NULL;
247 if (!g_option_context_parse(ctx, &argc, &argv, &error)){
248 g_print ("Error initializing: %s\n", GST_STR_NULL(error->message));
249 exit (1);
251 g_option_context_free(ctx);
253 GMainLoop *loop = g_main_loop_new(NULL, FALSE);
255 windows_t windows;
256 windows.realised = 0;
258 int i;
259 for (i = 0; i < option_screens; i++){
260 GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
261 g_signal_connect(window, "realize",
262 G_CALLBACK(video_widget_realize_cb), &windows);
263 /* set up sink here */
264 GstElement *sink = gst_element_factory_make("ximagesink", NULL);
265 set_up_window(loop, window, i);
266 windows.gtk_windows[i] = window;
267 windows.sinks[i] = sink;
270 GstElement *pipeline = (GstElement *)make_multi_pipeline(&windows, option_screens);
272 GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
273 gst_bus_add_watch(bus, (GstBusFunc)bus_call, &windows);
274 gst_object_unref(bus);
276 gst_element_set_state(pipeline, GST_STATE_PLAYING);
278 g_main_loop_run(loop);
280 gst_element_set_state (pipeline, GST_STATE_NULL);
281 gst_object_unref (pipeline);
282 return 0;