Convert README to README.rst for Github co-hosting
[sparrow.git] / gtksparrow.c
blob2e280a2035b205918dfa51d1584428462c387b0c
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"
12 GstElement *global_pipeline = NULL;
14 static GstPipeline *
15 make_multi_pipeline(GstElement **sinks, int count)
17 GstPipeline *pipeline = GST_PIPELINE(gst_pipeline_new("sparrow_pipeline"));
18 GstElement *tee = pre_tee_pipeline(pipeline);
19 char *reload = NULL;
20 char *save = NULL;
21 int i;
22 for (i = 0; i < count; i++){
23 GstElement *sink = gst_element_factory_make("ximagesink", NULL);
24 sinks[i] = sink;
25 //args are:
26 //(pipeline, tee, sink, int rngseed, int colour, timer flag, int debug flag)
27 /* timer should only run on one of them. colour >= 3 is undefined */
28 int debug = option_debug == i;
29 int timer = option_timer == i;
30 if (option_reload != NULL){
31 if (option_reload[i] == NULL){
32 g_critical("You can't reload some screens and not others!");
33 exit(1);
35 reload = option_reload[i];
37 if (option_save && option_save[i]){
38 save = option_save[i];
40 post_tee_pipeline(pipeline, tee, sink, i, i + 1, timer, debug, save, reload);
42 if (option_avi){
43 /*add a branch saving the video to a file */
44 mjpeg_branch(pipeline, tee);
47 return pipeline;
51 static void
52 toggle_fullscreen(GtkWidget *widget){
53 GdkWindowState state = gdk_window_get_state(GDK_WINDOW(widget->window));
54 if (state == GDK_WINDOW_STATE_FULLSCREEN){
55 gtk_window_unfullscreen(GTK_WINDOW(widget));
57 else{
58 gtk_window_fullscreen(GTK_WINDOW(widget));
62 static gboolean
63 key_press_event_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
65 switch (event->keyval){
66 case 'f':
67 toggle_fullscreen(widget);
68 break;
69 case 'q':
70 g_signal_emit_by_name(widget, "destroy");
71 break;
72 default:
73 break;
75 return TRUE;
78 static void
79 window_closed(GtkWidget *widget, GdkEvent *event, gpointer user_data)
81 gtk_widget_hide_all(widget);
82 gst_element_set_state(global_pipeline, GST_STATE_NULL);
83 gtk_main_quit();
88 static GtkWidget *
89 set_up_window(GstElement *sink, int screen_no){
90 static const GdkColor black = {0, 0, 0, 0};
92 GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
93 g_signal_connect(G_OBJECT(window), "delete-event",
94 G_CALLBACK(window_closed), (gpointer)sink);
97 gtk_window_set_default_size(GTK_WINDOW(window), WIDTH, HEIGHT);
99 GtkWidget *video_window = window;
100 //GtkWidget *video_window = gtk_drawing_area_new();
101 //gtk_container_add(GTK_CONTAINER(window), video_window);
102 gtk_widget_set_double_buffered(video_window, FALSE);
103 gtk_container_set_border_width(GTK_CONTAINER(window), 0);
106 gtk_widget_show_all(window);
107 gtk_widget_realize(window);
109 GdkWindow *gdk_window = gtk_widget_get_window(video_window);
111 XID xid = GDK_WINDOW_XID(gdk_window);
112 gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(sink), xid);
114 /*if more than one screen is requested, set the screen position.
115 otherwise let it fall were it falls */
116 if (option_screens > 1 || option_first_screen){
117 /* "screen" is not the same thing as "monitor" */
118 GdkScreen * screen = gdk_screen_get_default();
119 int width = gdk_screen_get_width(screen);
120 /* XXX window selection is specific to equally sized windows arranged
121 horizontally. This could be generalised, perhaps using trial and
122 error */
123 gtk_window_move(GTK_WINDOW(window),
124 (width / 2 * screen_no + 200) % width, 50);
126 if (option_fullscreen){
127 gtk_window_fullscreen(GTK_WINDOW(window));
130 gtk_widget_modify_bg(window, GTK_STATE_NORMAL, &black);
131 g_signal_connect(G_OBJECT(video_window), "key-press-event",
132 G_CALLBACK(key_press_event_cb), NULL);
133 g_signal_connect(G_OBJECT(window), "destroy",
134 G_CALLBACK(window_closed), (gpointer)sink);
137 hide_mouse(window);
138 return window;
142 gint main (gint argc, gchar *argv[])
144 //initialise threads before any gtk stuff (because not using gtk_init)
145 g_thread_init(NULL);
146 /*this is more complicated than plain gtk_init/gst_init, so that options from
147 all over can be gathered and presented together.
149 GOptionGroup *gst_opts = gst_init_get_option_group();
150 GOptionGroup *gtk_opts = gtk_get_option_group(TRUE);
151 GOptionContext *ctx = g_option_context_new("...!");
152 g_option_context_add_main_entries(ctx, entries, NULL);
153 g_option_context_add_group(ctx, gst_opts);
154 g_option_context_add_group(ctx, gtk_opts);
155 GError *error = NULL;
156 if (!g_option_context_parse(ctx, &argc, &argv, &error)){
157 g_print ("Error initializing: %s\n", GST_STR_NULL(error->message));
158 exit(1);
160 g_option_context_free(ctx);
162 /*get the pipeline */
163 GstElement *sinks[MAX_SCREENS];
164 global_pipeline = (GstElement *)make_multi_pipeline(sinks, option_screens);
166 /*get the windows */
167 int i;
168 for (i = 0; i < option_screens; i++){
169 set_up_window(sinks[i], i + option_first_screen);
172 GstStateChangeReturn sret = gst_element_set_state(global_pipeline, GST_STATE_PLAYING);
173 if (sret == GST_STATE_CHANGE_FAILURE)
174 gst_element_set_state (global_pipeline, GST_STATE_NULL);
175 else
176 gtk_main();
178 gst_object_unref(global_pipeline);
179 return 0;