implement gui/tui
[gst-scaletempo-demo-rj.git] / src / gui.c
blobc4dec86b4332dbc425382e052ab735b0b5e8fecd
1 /* gui.c
2 * Copyright (C) 2008 Rov Juvano <rovjuvano@users.sourceforge.net>
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
22 #include <string.h>
23 #include <math.h>
24 #include <gtk/gtk.h>
25 #include <gdk/gdkx.h>
26 #include <gst/interfaces/xoverlay.h>
28 #include "gui.h"
30 #define STATUSBAR_TIMEOUT 2000
32 static GstElement *pipeline;
34 static GtkWidget *_rate_entry;
35 static GtkWidget *_image_playing_start;
36 static GtkWidget *_image_playing_pause;
37 static GtkWidget *_play_pause;
38 static GtkWidget *_status_bar;
39 static GtkWidget *_pos_label;
40 static GtkWidget *_video_window;
41 static gboolean _stop_updating_position;
44 /* Callback for status bar */
45 static int
46 pop_sb_cb (gpointer data) {
47 gtk_statusbar_remove (GTK_STATUSBAR (_status_bar), 0, GPOINTER_TO_UINT (data));
48 return 0;
51 gboolean update_position (gpointer data) {
52 gint64 pos;
53 if (get_position(data, &pos)) {
54 gchar text[32];
55 snprintf(text, 32, "%" GST_TIME_FORMAT, GST_TIME_ARGS (pos));
56 gtk_label_set_text (GTK_LABEL (_pos_label), text);
57 } else {
58 gtk_label_set_text (GTK_LABEL (_pos_label), "Could not determine position");
60 if (_stop_updating_position) {
61 _stop_updating_position = 0;
62 return 0;
64 return 1;
68 /* Callbacks from key presses */
69 static void
70 gui_change_rate (gfloat rate, gboolean abs)
72 change_rate(pipeline, rate, abs);
75 static void
76 gui_seek (GstElement * pipeline, gint seconds, gboolean abs)
78 gchar msg[32];
79 snprintf(msg, 32, (abs?"Seeking to %i":"Seeking %i seconds"), seconds);
80 guint msg_id = gtk_statusbar_push (GTK_STATUSBAR (_status_bar), 0, msg);
81 g_timeout_add (STATUSBAR_TIMEOUT, pop_sb_cb, GUINT_TO_POINTER (msg_id));
83 seek(pipeline, seconds, abs);
86 static void
87 gui_stop(GstElement * pipeline) {
88 gtk_main_quit ();
92 /* Gtk Callbacks */
93 static void
94 faster_cb (GtkButton * button, gpointer data)
96 gui_change_rate(1.0594630943593, FALSE);
97 guint msg_id = gtk_statusbar_push (GTK_STATUSBAR (_status_bar), 0, "Faster.");
98 g_timeout_add(STATUSBAR_TIMEOUT, pop_sb_cb, GUINT_TO_POINTER (msg_id));
101 static void
102 slower_cb (GtkButton * button, gpointer data)
104 gui_change_rate(0.943874312681694, FALSE);
105 guint msg_id = gtk_statusbar_push (GTK_STATUSBAR (_status_bar), 0, "S l o w e r.");
106 g_timeout_add (STATUSBAR_TIMEOUT, pop_sb_cb, GUINT_TO_POINTER (msg_id));
109 static void
110 change_rate_cb (GtkWidget * widget, gpointer data)
112 double rate;
113 sscanf (gtk_entry_get_text (GTK_ENTRY (widget)), "%lf", &rate);
114 gui_change_rate(rate, TRUE);
117 static void
118 play_pause_cb (GtkButton * button, gpointer data)
120 if (gtk_button_get_image (GTK_BUTTON (button)) == _image_playing_pause ) {
121 pause (pipeline);
122 } else {
123 play (pipeline);
127 static void
128 rw_button_cb (GtkButton * button, gpointer data)
130 gui_seek(pipeline, -30, FALSE);
133 static void
134 ff_button_cb (GtkButton * button, gpointer data)
136 gui_seek(pipeline, 30, FALSE);
140 /* Callbacks from signals */
141 static void
142 gui_rate_changed (GstElement * pipeline, gdouble new_rate, gpointer data) {
143 gchar e[6];
144 snprintf(e, 6, "%3.2f", new_rate);
145 gtk_entry_set_text (GTK_ENTRY (_rate_entry), e);
147 gchar msg[32];
148 snprintf(msg, 32, "Rate changed to %3.2f", new_rate);
149 guint msg_id = gtk_statusbar_push (GTK_STATUSBAR (_status_bar), 0, msg);
150 g_timeout_add (STATUSBAR_TIMEOUT, pop_sb_cb, GUINT_TO_POINTER (msg_id));
153 static void
154 gui_playing_started (GstElement * pipeline) {
155 guint msg_id = gtk_statusbar_push (GTK_STATUSBAR (_status_bar), 0, "PLAYING Started");
156 gtk_button_set_image (GTK_BUTTON (_play_pause), _image_playing_pause);
157 gtk_button_set_label (GTK_BUTTON (_play_pause), "_Pause");
158 g_timeout_add (STATUSBAR_TIMEOUT, pop_sb_cb, GUINT_TO_POINTER (msg_id));
159 g_timeout_add(1000, update_position, pipeline);
162 static void
163 gui_playing_paused (GstElement * pipeline) {
164 guint msg_id = gtk_statusbar_push (GTK_STATUSBAR (_status_bar), 0, "PLAYING Paused");
165 gtk_button_set_image (GTK_BUTTON (_play_pause), _image_playing_start);
166 gtk_button_set_label (GTK_BUTTON (_play_pause), "_Play");
167 g_timeout_add (STATUSBAR_TIMEOUT, pop_sb_cb, GUINT_TO_POINTER (msg_id));
168 _stop_updating_position = 1;
172 GdkFilterReturn
173 key_press (GtkWidget *widget, GdkEventKey *event)
175 switch (event->keyval) {
176 case 'q':
177 gtk_main_quit ();
178 break;
179 case ']':
180 gui_change_rate(1.059463094352953, FALSE);
181 break;
182 case '}':
183 gui_change_rate(2.0, FALSE);
184 break;
185 case '[':
186 gui_change_rate(0.9438743126816935, FALSE);
187 break;
188 case '{':
189 gui_change_rate(.5, FALSE);
190 break;
191 case 65288:
192 case '|':
193 gui_change_rate(1.0, TRUE);
194 break;
195 case '-':
196 gui_change_rate(-1, TRUE);
197 break;
198 case 65361:
199 case 'j':
200 gui_seek(pipeline, -5, FALSE);
201 break;
202 case 65363:
203 case 'k':
204 gui_seek(pipeline, 5, FALSE);
205 break;
206 case 65362:
207 case 'J':
208 gui_seek(pipeline, -30, FALSE);
209 break;
210 case 65364:
211 case 'K':
212 gui_seek(pipeline, 30, FALSE);
213 break;
214 case '0':
215 gui_seek(pipeline, 0, TRUE);
216 break;
217 case ' ':
218 play_pause (pipeline);
219 break;
220 case 65293:
221 case '\n':
222 case '\r':
223 case '\f': {
224 gui_seek(pipeline, 0, FALSE);
225 gint64 pos;
226 GstFormat fmt = GST_FORMAT_TIME;
227 if ( !gst_element_query_position (pipeline, &fmt, &pos) ) {
228 g_print ("position query failed!\n");
230 g_print ("%" GST_TIME_FORMAT "\n", GST_TIME_ARGS(pos));
231 break;
233 default: {
234 g_print ("char: %s (%i)", event->string, event->keyval);
235 return GDK_FILTER_CONTINUE;
238 return GDK_FILTER_REMOVE;
241 static GtkWidget *create_arrow_button( GtkArrowType arrow_type,
242 GtkShadowType shadow_type )
244 GtkWidget *button;
245 GtkWidget *arrow;
247 button = gtk_button_new ();
248 arrow = gtk_arrow_new (arrow_type, shadow_type);
250 gtk_container_add (GTK_CONTAINER (button), arrow);
252 gtk_widget_show (button);
253 gtk_widget_show (arrow);
255 return button;
258 static GstBusSyncReply
259 create_window (GstBus * bus, GstMessage * message, GtkWidget * window)
261 if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT ||
262 !gst_structure_has_name (message->structure, "prepare-xwindow-id"))
263 return GST_BUS_PASS;
265 gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (GST_MESSAGE_SRC (message)),
266 GDK_WINDOW_XWINDOW (window->window));
267 gst_x_overlay_handle_events (GST_X_OVERLAY (GST_MESSAGE_SRC (message)), TRUE);
270 const GstStructure *s = gst_message_get_structure (message);
271 g_print ("\nmessage from \"%s\" (%s): ",
272 GST_STR_NULL (gst_element_get_name (GST_MESSAGE_SRC (message))),
273 gst_message_type_get_name (GST_MESSAGE_TYPE (message)));
274 if (s) {
275 gchar *sstr = gst_structure_to_string (s);
276 g_print ("%s\n", sstr);
277 g_free (sstr);
278 } else {
279 g_print ("no message details\n");
283 return GST_BUS_PASS;
286 void
287 gui_build(int argc, char *argv[], gpointer **window_ref) {
288 gtk_init (&argc, &argv);
289 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
290 *window_ref = (gpointer)window;
292 GtkWidget *play_pause_button = gtk_button_new_with_mnemonic("_Pause");
293 GtkWidget *play_image = gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_SMALL_TOOLBAR);
294 GtkWidget *pause_image = gtk_image_new_from_stock(GTK_STOCK_MEDIA_PAUSE, GTK_ICON_SIZE_SMALL_TOOLBAR);
295 gtk_button_set_image (GTK_BUTTON (play_pause_button), pause_image);
296 GtkRequisition play_size;
297 gtk_widget_size_request (play_pause_button, &play_size);
298 gtk_widget_set_size_request (play_pause_button, play_size.width, -1);
300 GtkWidget *stop_button = gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP);
301 GtkWidget *rw_button = gtk_button_new_from_stock(GTK_STOCK_MEDIA_REWIND);
302 GtkWidget *ff_button = gtk_button_new_from_stock(GTK_STOCK_MEDIA_FORWARD);
304 GtkWidget *rate_entry = gtk_entry_new ();
305 GtkWidget *rate_label = gtk_label_new ("Rate:");
306 gtk_entry_set_max_length (GTK_ENTRY (rate_entry), 5);
307 gtk_entry_set_text (GTK_ENTRY (rate_entry), "1.0");
308 gtk_entry_set_width_chars (GTK_ENTRY (rate_entry), 5);
309 GtkWidget *faster_button = create_arrow_button (GTK_ARROW_UP, GTK_SHADOW_OUT);
310 GtkWidget *slower_button = create_arrow_button (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
311 GtkWidget *rate_box = gtk_hbox_new (FALSE, 0);
312 gtk_box_pack_start (GTK_BOX (rate_box), rate_label, FALSE, FALSE, 2);
313 gtk_box_pack_start (GTK_BOX (rate_box), slower_button, FALSE, FALSE, 2);
314 gtk_box_pack_start (GTK_BOX (rate_box), rate_entry, FALSE, FALSE, 2);
315 gtk_box_pack_start (GTK_BOX (rate_box), faster_button, FALSE, FALSE, 2);
317 GtkWidget *controls_box = gtk_hbox_new (FALSE, 0);
318 gtk_box_pack_start (GTK_BOX (controls_box), rate_box, FALSE, FALSE, 2);
319 gtk_box_pack_start (GTK_BOX (controls_box), play_pause_button, FALSE, FALSE, 2);
320 gtk_box_pack_start (GTK_BOX (controls_box), stop_button, FALSE, FALSE, 2);
321 gtk_box_pack_start (GTK_BOX (controls_box), rw_button, FALSE, FALSE, 2);
322 gtk_box_pack_start (GTK_BOX (controls_box), ff_button, FALSE, FALSE, 2);
324 GtkWidget *video_window = gtk_drawing_area_new ();
325 gtk_drawing_area_size (GTK_DRAWING_AREA (video_window), 640, 360);
326 g_object_set (G_OBJECT (video_window), "can-focus", TRUE, NULL);
328 GtkWidget *pos_label = gtk_label_new ("Position");
329 gtk_misc_set_alignment (GTK_MISC (pos_label), 1, 0.5);
330 gtk_label_set_width_chars (GTK_LABEL (pos_label), 20);
331 GtkWidget *status_bar = gtk_statusbar_new ();
332 GtkWidget *status_line = status_bar;
333 gtk_box_pack_start (GTK_BOX (status_bar), pos_label, FALSE, FALSE, 2);
334 gtk_box_reorder_child (GTK_BOX (status_bar), pos_label, 0);
336 gtk_window_set_title (GTK_WINDOW (window), "Scaletempo Demo");
337 GtkWidget *toplevel_box = gtk_vbox_new (FALSE, 0);
338 gtk_container_set_border_width (GTK_CONTAINER (toplevel_box), 3);
339 gtk_container_add (GTK_CONTAINER (window), toplevel_box);
340 gtk_box_pack_start (GTK_BOX (toplevel_box), controls_box, FALSE, FALSE, 2);
341 gtk_box_pack_start (GTK_BOX (toplevel_box), video_window, TRUE, TRUE, 2);
342 gtk_box_pack_start (GTK_BOX (toplevel_box), status_line, FALSE, FALSE, 2);
344 gtk_widget_ref (play_image);
345 gtk_widget_ref (pause_image);
346 _rate_entry = rate_entry;
347 _play_pause = play_pause_button;
348 _image_playing_start = play_image;
349 _image_playing_pause = pause_image;
350 _video_window = video_window;
351 _status_bar = status_bar;
352 _pos_label = pos_label;
354 g_signal_connect (G_OBJECT (window), "destroy", gtk_main_quit, NULL);
355 g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK(gtk_widget_destroyed), window_ref);
356 gtk_widget_set_events (video_window, GDK_KEY_PRESS_MASK);
357 gtk_signal_connect (GTK_OBJECT (video_window), "key_press_event", GTK_SIGNAL_FUNC (key_press), NULL);
359 g_signal_connect (G_OBJECT(faster_button), "clicked", G_CALLBACK (faster_cb), NULL);
360 g_signal_connect (G_OBJECT(slower_button), "clicked", G_CALLBACK (slower_cb), NULL);
361 g_signal_connect (G_OBJECT(play_pause_button), "clicked", G_CALLBACK (play_pause_cb), NULL);
362 g_signal_connect (G_OBJECT(stop_button), "clicked", G_CALLBACK (gtk_main_quit), NULL);
363 g_signal_connect (G_OBJECT(rw_button), "clicked", G_CALLBACK (rw_button_cb), NULL);
364 g_signal_connect (G_OBJECT(ff_button), "clicked", G_CALLBACK (ff_button_cb), NULL);
365 g_signal_connect (G_OBJECT(rate_entry), "activate", G_CALLBACK (change_rate_cb), NULL);
367 gtk_widget_show_all (window);
368 gtk_statusbar_push (GTK_STATUSBAR (status_bar), 0, "Welcome to the jungle.");
369 gtk_widget_grab_focus (video_window);
372 void
373 gui_play_uri (const gchar *uri, const gchar *filter_name)
375 pipeline = build_pipeline (uri, filter_name);
376 if (!pipeline) {
377 g_print("An error occured building pipeline for: %s\n", uri);
378 return;
381 GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
382 gst_bus_set_sync_handler (bus, (GstBusSyncHandler) create_window, _video_window);
383 //g_signal_connect (bus, "message::element", G_CALLBACK (create_window), _video_window);
384 gst_object_unref (bus);
386 set_rate_changed_cb (gui_rate_changed);
387 set_playing_started_cb (gui_playing_started);
388 set_playing_paused_cb (gui_playing_paused);
389 set_stop_cb (gui_stop);
391 play (pipeline);
392 gtk_main ();
394 gst_element_set_state (pipeline, GST_STATE_NULL);
395 gst_object_unref (pipeline);