From b5e35ad70bd171c9fd4748d8c3fcba7f60ccb4e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 11 Jun 2015 18:42:38 +0200 Subject: [PATCH] playback/player: gtk: Port to GtkApplication And also clean up some other things. https://github.com/sdroege/gst-player/issues/56 --- playback/player/gtk/gtk-play.c | 390 +++++++++++++++++++++++---------- 1 file changed, 279 insertions(+), 111 deletions(-) diff --git a/playback/player/gtk/gtk-play.c b/playback/player/gtk/gtk-play.c index 8ae4fc3219..df0f5b9a34 100644 --- a/playback/player/gtk/gtk-play.c +++ b/playback/player/gtk/gtk-play.c @@ -42,15 +42,22 @@ #define APP_NAME "gtk-play" +typedef GtkApplication GtkPlayApp; +typedef GtkApplicationClass GtkPlayAppClass; + +G_DEFINE_TYPE (GtkPlayApp, gtk_play_app, GTK_TYPE_APPLICATION); + typedef struct { + GtkApplicationWindow parent; + GstPlayer *player; gchar *uri; GList *uris; GList *current_uri; - GtkWidget *window; + GtkWidget *play_pause_button; GtkWidget *prev_button, *next_button; GtkWidget *seekbar; @@ -67,9 +74,27 @@ typedef struct gboolean playing; gboolean loop; gboolean fullscreen; + gboolean visual; gint toolbar_hide_timeout; } GtkPlay; +typedef GtkApplicationWindowClass GtkPlayClass; + +G_DEFINE_TYPE (GtkPlay, gtk_play, GTK_TYPE_APPLICATION_WINDOW); + +enum +{ + PROP_0, + PROP_LOOP, + PROP_FULLSCREEN, + PROP_VISUAL, + PROP_URIS, + + LAST_PROP +}; + +static GParamSpec *gtk_play_properties[LAST_PROP] = { NULL, }; + enum { COL_TEXT = 0, @@ -104,9 +129,9 @@ static void set_title (GtkPlay * play, const gchar * title) { if (title == NULL) { - gtk_window_set_title (GTK_WINDOW (play->window), APP_NAME); + gtk_window_set_title (GTK_WINDOW (play), APP_NAME); } else { - gtk_window_set_title (GTK_WINDOW (play->window), title); + gtk_window_set_title (GTK_WINDOW (play), title); } } @@ -114,7 +139,6 @@ static void delete_event_cb (GtkWidget * widget, GdkEvent * event, GtkPlay * play) { gst_player_stop (play->player); - gtk_main_quit (); } static void @@ -227,7 +251,6 @@ color_balance_channel_button_press_cb (GtkWidget * widget, static void color_balance_dialog (GtkPlay * play) { - GtkWidget *parent; GtkWidget *dialog; GtkWidget *content; GtkWidget *box; @@ -237,10 +260,9 @@ color_balance_dialog (GtkPlay * play) gdouble value; guint i; - parent = gtk_window_new (GTK_WINDOW_TOPLEVEL); - dialog = gtk_dialog_new_with_buttons ("Color Balance", GTK_WINDOW (parent), - GTK_DIALOG_MODAL, "_Close", GTK_RESPONSE_CLOSE, NULL); - gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent)); + dialog = gtk_dialog_new_with_buttons ("Color Balance", GTK_WINDOW (play), + GTK_DIALOG_DESTROY_WITH_PARENT, "_Close", GTK_RESPONSE_CLOSE, NULL); + gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (play)); content = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); @@ -273,7 +295,6 @@ color_balance_dialog (GtkPlay * play) gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); - gtk_widget_destroy (parent); } static void @@ -296,7 +317,14 @@ open_file_dialog (GtkPlay * play, gboolean multi) GtkWidget *chooser; GtkWidget *parent; - parent = gtk_window_new (GTK_WINDOW_TOPLEVEL); + if (play) { + parent = GTK_WIDGET (play); + } else { + parent = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_application_add_window (GTK_APPLICATION (g_application_get_default ()), + GTK_WINDOW (parent)); + } + chooser = gtk_file_chooser_dialog_new ("Select files to play", NULL, GTK_FILE_CHOOSER_ACTION_OPEN, "_Cancel", GTK_RESPONSE_CANCEL, "_Open", GTK_RESPONSE_ACCEPT, NULL); @@ -315,7 +343,9 @@ open_file_dialog (GtkPlay * play, gboolean multi) } gtk_widget_destroy (chooser); - gtk_widget_destroy (parent); + if (!play) + gtk_widget_destroy (parent); + return uris; } @@ -603,6 +633,7 @@ create_media_info_window (GtkPlay * play, GstPlayerMediaInfo * info) gtk_window_set_default_size (GTK_WINDOW (window), 550, 450); gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER); gtk_container_set_border_width (GTK_CONTAINER (window), 10); + gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (play)); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8); gtk_container_add (GTK_CONTAINER (window), vbox); @@ -673,9 +704,10 @@ toolbar_hide_func (GtkPlay * play) /* hide the mouse pointer */ cursor = - gdk_cursor_new_for_display (gtk_widget_get_display (play->window), + gdk_cursor_new_for_display (gtk_widget_get_display (GTK_WIDGET (play)), GDK_BLANK_CURSOR); - gdk_window_set_cursor (gtk_widget_get_window (play->window), cursor); + gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (play->video_area)), + cursor); g_object_unref (cursor); play->toolbar_hide_timeout = 0; @@ -689,7 +721,7 @@ fullscreen_toggle_cb (GtkToggleButton * widget, GtkPlay * play) if (gtk_toggle_button_get_active (widget)) { image = gtk_image_new_from_icon_name ("view-restore", GTK_ICON_SIZE_BUTTON); - gtk_window_fullscreen (GTK_WINDOW (play->window)); + gtk_window_fullscreen (GTK_WINDOW (play)); gtk_button_set_image (GTK_BUTTON (play->fullscreen_button), image); /* start timer to hide toolbar */ @@ -706,7 +738,7 @@ fullscreen_toggle_cb (GtkToggleButton * widget, GtkPlay * play) image = gtk_image_new_from_icon_name ("view-fullscreen", GTK_ICON_SIZE_BUTTON); - gtk_window_unfullscreen (GTK_WINDOW (play->window)); + gtk_window_unfullscreen (GTK_WINDOW (play)); gtk_button_set_image (GTK_BUTTON (play->fullscreen_button), image); } } @@ -998,8 +1030,7 @@ create_tracks_menu (GtkPlay * play, GstPlayerMediaInfo * media_info, GType type) static void player_quit_clicked_cb (GtkButton * button, GtkPlay * play) { - gst_player_stop (play->player); - gtk_main_quit (); + gtk_widget_destroy (GTK_WIDGET (play)); } static void @@ -1187,8 +1218,8 @@ gtk_show_toolbar_cb (GtkWidget * widget, GdkEvent * event, GtkPlay * play) } /* show mouse pointer */ - gdk_window_set_cursor (gtk_widget_get_window (play->window), - play->default_cursor); + gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET + (play->video_area)), play->default_cursor); gtk_widget_show (play->toolbar); play->toolbar_hide_timeout = g_timeout_add_seconds (5, @@ -1205,10 +1236,13 @@ create_ui (GtkPlay * play) GtkWidget *controls, *main_hbox, *main_vbox; GstElement *playbin, *video_sink, *gl_sink; - play->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - g_signal_connect (G_OBJECT (play->window), "delete-event", + gtk_window_set_default_size (GTK_WINDOW (play), 640, 480); + + g_signal_connect (G_OBJECT (play), "delete-event", G_CALLBACK (delete_event_cb), play); set_title (play, APP_NAME); + gtk_application_add_window (GTK_APPLICATION (g_application_get_default ()), + GTK_WINDOW (play)); gl_sink = gst_element_factory_make ("gtkglsink", NULL); if (gl_sink) { @@ -1337,23 +1371,11 @@ create_ui (GtkPlay * play) main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_box_pack_start (GTK_BOX (main_vbox), main_hbox, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (main_vbox), controls, FALSE, FALSE, 0); - gtk_container_add (GTK_CONTAINER (play->window), main_vbox); + gtk_container_add (GTK_CONTAINER (play), main_vbox); - gtk_widget_realize (play->video_area); + if (!gl_sink) + gtk_widget_realize (play->video_area); gtk_widget_hide (play->video_area); - - gtk_widget_show_all (play->window); - - play->default_cursor = gdk_window_get_cursor - (gtk_widget_get_window (play->toolbar)); -} - -static void -play_clear (GtkPlay * play) -{ - g_free (play->uri); - g_list_free_full (play->uris, g_free); - g_object_unref (play->player); } static void @@ -1562,100 +1584,246 @@ player_volume_changed_cb (GstPlayer * player, GtkPlay * play) } } -int -main (gint argc, gchar ** argv) +static void +gtk_play_set_property (GObject * object, guint prop_id, const GValue * value, + GParamSpec * pspec) { - GtkPlay play; - gchar **file_names = NULL; - GOptionContext *ctx; - gboolean vis = FALSE; - GOptionEntry options[] = { - {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &file_names, - "Files to play"}, - {"loop", 'l', 0, G_OPTION_ARG_NONE, &play.loop, "Repeat all"}, - {"fullscreen", 'f', 0, G_OPTION_ARG_NONE, &play.fullscreen, - "Show the player in fullscreen"}, - {"visual", 'v', 0, G_OPTION_ARG_NONE, &vis, - "Show visualization when there is no video stream"}, - {NULL} - }; - guint list_length = 0; - GError *err = NULL; + GtkPlay *self = (GtkPlay *) object; -#if defined (GDK_WINDOWING_X11) - XInitThreads (); -#endif - - memset (&play, 0, sizeof (play)); - - g_set_prgname (APP_NAME); - - ctx = g_option_context_new ("FILE|URI"); - g_option_context_add_main_entries (ctx, options, NULL); - g_option_context_add_group (ctx, gtk_get_option_group (TRUE)); - g_option_context_add_group (ctx, gst_init_get_option_group ()); - if (!g_option_context_parse (ctx, &argc, &argv, &err)) { - g_print ("Error initializing: %s\n", GST_STR_NULL (err->message)); - return 1; + switch (prop_id) { + case PROP_LOOP: + self->loop = g_value_get_boolean (value); + break; + case PROP_FULLSCREEN: + self->fullscreen = g_value_get_boolean (value); + break; + case PROP_VISUAL: + self->visual = g_value_get_boolean (value); + break; + case PROP_URIS: + self->uris = g_value_get_pointer (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; } - g_option_context_free (ctx); +} - // FIXME: Add support for playlists and stuff - /* Parse the list of the file names we have to play. */ - if (!file_names) { - play.uris = open_file_dialog (&play, TRUE); - if (!play.uris) - return 0; - } else { - guint i; +static void +show_cb (GtkWidget * widget, gpointer user_data) +{ + GtkPlay *self = (GtkPlay *) widget; - list_length = g_strv_length (file_names); - for (i = 0; i < list_length; i++) { - play.uris = - g_list_append (play.uris, - gst_uri_is_valid (file_names[i]) ? - g_strdup (file_names[i]) : gst_filename_to_uri (file_names[i], NULL)); - } + self->default_cursor = gdk_window_get_cursor + (gtk_widget_get_window (GTK_WIDGET (self))); - g_strfreev (file_names); - file_names = NULL; - } + play_current_uri (self, g_list_first (self->uris), NULL); +} - play.player = gst_player_new (); - play.playing = TRUE; +static GObject * +gtk_play_constructor (GType type, guint n_construct_params, + GObjectConstructParam * construct_params) +{ + GtkPlay *self; + gchar **p; - g_object_set (play.player, "dispatch-to-main-context", TRUE, NULL); + self = + (GtkPlay *) G_OBJECT_CLASS (gtk_play_parent_class)->constructor (type, + n_construct_params, construct_params); - create_ui (&play); + self->player = gst_player_new (); + self->playing = TRUE; + + g_object_set (self->player, "dispatch-to-main-context", TRUE, NULL); + + create_ui (self); /* if visualization is enabled then use the first element */ - if (vis) { + if (self->visual) { GstPlayerVisualization **viss; viss = gst_player_visualizations_get (); if (viss && *viss) { - gst_player_set_visualization (play.player, (*viss)->name); - gst_player_set_visualization_enabled (play.player, TRUE); + gst_player_set_visualization (self->player, (*viss)->name); + gst_player_set_visualization_enabled (self->player, TRUE); } if (viss) gst_player_visualizations_free (viss); } - g_signal_connect (play.player, "position-updated", - G_CALLBACK (position_updated_cb), &play); - g_signal_connect (play.player, "duration-changed", - G_CALLBACK (duration_changed_cb), &play); - g_signal_connect (play.player, "end-of-stream", G_CALLBACK (eos_cb), &play); - g_signal_connect (play.player, "media-info-updated", - G_CALLBACK (media_info_updated_cb), &play); - g_signal_connect (play.player, "volume-changed", - G_CALLBACK (player_volume_changed_cb), &play); + g_signal_connect (self->player, "position-updated", + G_CALLBACK (position_updated_cb), self); + g_signal_connect (self->player, "duration-changed", + G_CALLBACK (duration_changed_cb), self); + g_signal_connect (self->player, "end-of-stream", G_CALLBACK (eos_cb), self); + g_signal_connect (self->player, "media-info-updated", + G_CALLBACK (media_info_updated_cb), self); + g_signal_connect (self->player, "volume-changed", + G_CALLBACK (player_volume_changed_cb), self); - play_current_uri (&play, g_list_first (play.uris), NULL); + g_signal_connect (G_OBJECT (self), "show", G_CALLBACK (show_cb), NULL); - gtk_main (); - - play_clear (&play); - - return 0; + return G_OBJECT (self); +} + +static void +gtk_play_dispose (GObject * object) +{ + GtkPlay *self = (GtkPlay *) object; + + if (self->uri) + g_free (self->uri); + self->uri = NULL; + + if (self->uris) + g_list_free_full (self->uris, g_free); + self->uris = NULL; + if (self->player) { + gst_player_stop (self->player); + g_object_unref (self->player); + } + self->player = NULL; + + G_OBJECT_CLASS (gtk_play_parent_class)->dispose (object); +} + +static void +gtk_play_init (GtkPlay * self) +{ +} + +static void +gtk_play_class_init (GtkPlayClass * klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructor = gtk_play_constructor; + object_class->dispose = gtk_play_dispose; + object_class->set_property = gtk_play_set_property; + + gtk_play_properties[PROP_LOOP] = + g_param_spec_boolean ("loop", "Loop", "Loop the playlist", + FALSE, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + gtk_play_properties[PROP_FULLSCREEN] = + g_param_spec_boolean ("fullscreen", "Fullscreen", "Fullscreen mode", + FALSE, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + gtk_play_properties[PROP_VISUAL] = + g_param_spec_boolean ("visual", "Visual", "Use Visualizations", FALSE, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + gtk_play_properties[PROP_URIS] = + g_param_spec_pointer ("uris", "URIs", "URIs to play", + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, LAST_PROP, + gtk_play_properties); +} + +static gint +gtk_play_app_command_line (GApplication * application, + GApplicationCommandLine * command_line) +{ + GtkPlayApp *self = (GtkPlayApp *) application; + GVariantDict *options; + GtkPlay *play; + GList *uris = NULL; + gboolean loop = FALSE, visual = FALSE, fullscreen = FALSE; + gchar **uris_array = NULL; + + options = g_application_command_line_get_options_dict (command_line); + + g_variant_dict_lookup (options, "loop", "b", &loop); + g_variant_dict_lookup (options, "visual", "b", &visual); + g_variant_dict_lookup (options, "fullscreen", "b", &fullscreen); + g_variant_dict_lookup (options, G_OPTION_REMAINING, "^a&ay", &uris_array); + + if (uris_array) { + gchar **p; + + p = uris_array; + while (*p) { + uris = + g_list_prepend (uris, + gst_uri_is_valid (*p) ? + g_strdup (*p) : gst_filename_to_uri (*p, NULL)); + p++; + } + uris = g_list_reverse (uris); + } else { + uris = open_file_dialog (NULL, TRUE); + } + + if (!uris) + return -1; + + play = + g_object_new (gtk_play_get_type (), "loop", loop, "fullscreen", + fullscreen, "visual", visual, "uris", uris, NULL); + gtk_widget_show_all (GTK_WIDGET (play)); + + return + G_APPLICATION_CLASS (gtk_play_app_parent_class)->command_line + (application, command_line); +} + +static void +gtk_play_app_init (GtkPlayApp * self) +{ +} + +static void +gtk_play_app_class_init (GtkPlayAppClass * klass) +{ + GApplicationClass *application_class = G_APPLICATION_CLASS (klass); + + application_class->command_line = gtk_play_app_command_line; +} + +GtkPlayApp * +gtk_play_app_new (void) +{ + GtkPlayApp *self; + GOptionEntry options[] = { + {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, NULL, + "Files to play"}, + {"loop", 'l', 0, G_OPTION_ARG_NONE, NULL, "Repeat all"}, + {"fullscreen", 'f', 0, G_OPTION_ARG_NONE, NULL, + "Show the player in fullscreen"}, + {"visual", 'v', 0, G_OPTION_ARG_NONE, NULL, + "Show visualization when there is no video stream"}, + {NULL} + }; + + g_set_prgname (APP_NAME); + g_set_application_name (APP_NAME); + + self = g_object_new (gtk_play_app_get_type (), + "application-id", "org.freedesktop.gstreamer.GTKPlay", + "flags", G_APPLICATION_HANDLES_COMMAND_LINE, + "register-session", TRUE, NULL); + + g_application_set_default (G_APPLICATION (self)); + g_application_add_main_option_entries (G_APPLICATION (self), options); + g_application_add_option_group (G_APPLICATION (self), + gst_init_get_option_group ()); + + return self; +} + +int +main (gint argc, gchar ** argv) +{ + GtkPlayApp *app; + gint status; + +#if defined (GDK_WINDOWING_X11) + XInitThreads (); +#endif + + app = gtk_play_app_new (); + status = g_application_run (G_APPLICATION (app), argc, argv);; + g_object_unref (app); + + return status; }