diff --git a/examples/clapper-gtk/audio/simple/python/example.py b/examples/clapper-gtk/audio/simple/python/example.py new file mode 100755 index 00000000..b477cc9a --- /dev/null +++ b/examples/clapper-gtk/audio/simple/python/example.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +import gi +gi.require_version('Adw', '1') +gi.require_version('Clapper', '0.0') +gi.require_version('ClapperGtk', '0.0') +gi.require_version('Gtk', '4.0') +from gi.repository import Adw, Clapper, ClapperGtk, Gtk + +Clapper.init(None) + +def on_activate(app): + # Create our widgets. + win = Gtk.ApplicationWindow(application=app, title='Clapper Audio', default_width=640, default_height=96) + audio = ClapperGtk.Audio() + box = Gtk.Box(valign=Gtk.Align.CENTER, margin_start=8, margin_end=8, spacing=4) + prev_btn = ClapperGtk.PreviousItemButton() + play_btn = ClapperGtk.TogglePlayButton() + next_btn = ClapperGtk.NextItemButton() + seek_bar = ClapperGtk.SeekBar() + + # Add media for playback. First media item in queue will be automatically selected. + item = Clapper.MediaItem(uri='https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3') + audio.props.player.props.queue.add_item(item) + + item = Clapper.MediaItem(uri='https://www.learningcontainer.com/wp-content/uploads/2020/02/Kalimba.mp3') + audio.props.player.props.queue.add_item(item) + + # Assemble window. + box.append(prev_btn) + box.append(play_btn) + box.append(next_btn) + box.append(seek_bar) + audio.set_child(box) + win.set_child(audio) + win.present() + + # Not too loud. Mind the ears. + audio.props.player.props.volume = 0.7 + + # Start playback. + audio.props.player.play() + +# Create a new application. +app = Adw.Application(application_id='com.example.ClapperAudio') +app.connect('activate', on_activate) + +# Run the application. +app.run(None) diff --git a/examples/clapper-gtk/download_cache/python/example.py b/examples/clapper-gtk/video/download_cache/python/example.py similarity index 100% rename from examples/clapper-gtk/download_cache/python/example.py rename to examples/clapper-gtk/video/download_cache/python/example.py diff --git a/examples/clapper-gtk/simple/python/example.py b/examples/clapper-gtk/video/simple/python/example.py similarity index 100% rename from examples/clapper-gtk/simple/python/example.py rename to examples/clapper-gtk/video/simple/python/example.py diff --git a/src/bin/clapper-app/clapper-app-window.c b/src/bin/clapper-app/clapper-app-window.c index 3641f959..04145119 100644 --- a/src/bin/clapper-app/clapper-app-window.c +++ b/src/bin/clapper-app/clapper-app-window.c @@ -231,7 +231,7 @@ video_map_cb (GtkWidget *widget, ClapperAppWindow *self) GST_TRACE_OBJECT (self, "Video map"); - player = clapper_gtk_video_get_player (CLAPPER_GTK_VIDEO_CAST (self->video)); + player = clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self->video)); g_signal_connect (player, "notify::volume", G_CALLBACK (_player_volume_changed_cb), self); @@ -252,7 +252,7 @@ video_unmap_cb (GtkWidget *widget, ClapperAppWindow *self) GST_TRACE_OBJECT (self, "Video unmap"); - player = clapper_gtk_video_get_player (CLAPPER_GTK_VIDEO_CAST (self->video)); + player = clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self->video)); g_signal_handlers_disconnect_by_func (player, _player_volume_changed_cb, self); g_signal_handlers_disconnect_by_func (player, _player_speed_changed_cb, self); @@ -524,7 +524,7 @@ drag_update_cb (GtkGestureDrag *drag, static inline void _alter_volume (ClapperAppWindow *self, gdouble dy) { - ClapperPlayer *player = clapper_gtk_video_get_player (CLAPPER_GTK_VIDEO_CAST (self->video)); + ClapperPlayer *player = clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self->video)); gdouble volume = clapper_player_get_volume (player); /* We do not want for volume to change too suddenly */ @@ -547,7 +547,7 @@ _alter_volume (ClapperAppWindow *self, gdouble dy) static inline void _alter_speed (ClapperAppWindow *self, gdouble dx) { - ClapperPlayer *player = clapper_gtk_video_get_player (CLAPPER_GTK_VIDEO_CAST (self->video)); + ClapperPlayer *player = clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self->video)); gdouble speed = clapper_player_get_speed (player); speed -= dx * 0.02; @@ -571,8 +571,7 @@ _begin_seek_operation (ClapperAppWindow *self) if (self->seeking) return FALSE; - player = clapper_gtk_video_get_player ( - CLAPPER_GTK_VIDEO_CAST (self->video)); + player = clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self->video)); queue = clapper_player_get_queue (player); current_item = clapper_queue_get_current_item (queue); @@ -600,8 +599,8 @@ static void _end_seek_operation (ClapperAppWindow *self) { if (self->seeking && self->current_duration > 0) { - ClapperPlayer *player = clapper_gtk_video_get_player ( - CLAPPER_GTK_VIDEO_CAST (self->video)); + ClapperPlayer *player = clapper_gtk_av_get_player ( + CLAPPER_GTK_AV_CAST (self->video)); clapper_player_seek_custom (player, self->pending_position, g_settings_get_int (self->settings, "seek-method")); @@ -764,8 +763,8 @@ _handle_seek_key_press (ClapperAppWindow *self, gboolean forward) static void _handle_chapter_key_press (ClapperAppWindow *self, gboolean forward) { - ClapperPlayer *player = clapper_gtk_video_get_player ( - CLAPPER_GTK_VIDEO_CAST (self->video)); + ClapperPlayer *player = clapper_gtk_av_get_player ( + CLAPPER_GTK_AV_CAST (self->video)); ClapperQueue *queue = clapper_player_get_queue (player); ClapperMediaItem *current_item = clapper_queue_get_current_item (queue); ClapperTimeline *timeline; @@ -855,8 +854,8 @@ _handle_chapter_key_press (ClapperAppWindow *self, gboolean forward) static void _handle_item_key_press (ClapperAppWindow *self, gboolean forward) { - ClapperPlayer *player = clapper_gtk_video_get_player ( - CLAPPER_GTK_VIDEO_CAST (self->video)); + ClapperPlayer *player = clapper_gtk_av_get_player ( + CLAPPER_GTK_AV_CAST (self->video)); ClapperQueue *queue = clapper_player_get_queue (player); guint prev_index, index; @@ -864,7 +863,7 @@ _handle_item_key_press (ClapperAppWindow *self, gboolean forward) prev_index = clapper_queue_get_current_index (queue); gtk_widget_activate_action (self->video, - (forward) ? "video.next-item" : "video.previous-item", NULL); + (forward) ? "av.next-item" : "av.previous-item", NULL); index = clapper_queue_get_current_index (queue); /* Notify only when changed */ @@ -881,14 +880,14 @@ _handle_speed_key_press (ClapperAppWindow *self, gboolean forward) forward ^= (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL); gtk_widget_activate_action (self->video, - (forward) ? "video.speed-up" : "video.speed-down", NULL); + (forward) ? "av.speed-up" : "av.speed-down", NULL); } static inline void _handle_progression_key_press (ClapperAppWindow *self) { - ClapperPlayer *player = clapper_gtk_video_get_player ( - CLAPPER_GTK_VIDEO_CAST (self->video)); + ClapperPlayer *player = clapper_gtk_av_get_player ( + CLAPPER_GTK_AV_CAST (self->video)); ClapperQueue *queue = clapper_player_get_queue (player); ClapperQueueProgressionMode mode; const gchar *icon = NULL, *label = NULL; @@ -908,11 +907,11 @@ key_pressed_cb (GtkEventControllerKey *controller, guint keyval, switch (keyval) { case GDK_KEY_Up: if ((state & GDK_MODIFIER_MASK) == 0) - gtk_widget_activate_action (self->video, "video.volume-up", NULL); + gtk_widget_activate_action (self->video, "av.volume-up", NULL); break; case GDK_KEY_Down: if ((state & GDK_MODIFIER_MASK) == 0) - gtk_widget_activate_action (self->video, "video.volume-down", NULL); + gtk_widget_activate_action (self->video, "av.volume-down", NULL); break; case GDK_KEY_Left: if ((state & GDK_MODIFIER_MASK) == 0) { @@ -943,7 +942,7 @@ key_pressed_cb (GtkEventControllerKey *controller, guint keyval, case GDK_KEY_space: case GDK_KEY_k: if (!self->key_held && (state & GDK_MODIFIER_MASK) == 0) - gtk_widget_activate_action (self->video, "video.toggle-play", NULL); + gtk_widget_activate_action (self->video, "av.toggle-play", NULL); break; case GDK_KEY_less: if (!self->key_held) // Needs seek (action is slow) @@ -955,7 +954,7 @@ key_pressed_cb (GtkEventControllerKey *controller, guint keyval, break; case GDK_KEY_m: if (!self->key_held && (state & GDK_MODIFIER_MASK) == 0) - gtk_widget_activate_action (self->video, "video.toggle-mute", NULL); + gtk_widget_activate_action (self->video, "av.toggle-mute", NULL); break; case GDK_KEY_p: if (!self->key_held && (state & GDK_MODIFIER_MASK) == 0) @@ -1123,7 +1122,7 @@ clapper_app_window_get_video (ClapperAppWindow *self) ClapperPlayer * clapper_app_window_get_player (ClapperAppWindow *self) { - return clapper_gtk_video_get_player (CLAPPER_GTK_VIDEO_CAST (self->video)); + return clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self->video)); } ClapperAppWindowExtraOptions * diff --git a/src/lib/clapper-gtk/clapper-gtk-audio.c b/src/lib/clapper-gtk/clapper-gtk-audio.c new file mode 100644 index 00000000..ffb1038a --- /dev/null +++ b/src/lib/clapper-gtk/clapper-gtk-audio.c @@ -0,0 +1,268 @@ +/* Clapper GTK Integration Library + * Copyright (C) 2025 Rafał Dzięgiel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * . + */ + +/** + * ClapperGtkAudio: + * + * A GTK widget for audio playback with Clapper API. + * + * #ClapperGtkAudio is a widget meant for integrating audio playback + * within GTK application. It exposes [class@Clapper.Player] through its + * base class [property@ClapperGtk.Av:player] property. + * + * Other widgets (buttons, seek bar, etc.) provided by `ClapperGtk` library, once placed + * anywhere inside audio container (including nesting within another widget like [class@Gtk.Box]) + * will automatically control #ClapperGtkAudio they are within. This allows to freely create + * custom UI best suited for specific application. + * + * # Basic usage + * + * A typical use case is to embed audio widget as part of your app where audio playback + * is needed (can be even the very first child of the window). Get the [class@Clapper.Player] + * belonging to the AV widget and start adding new [class@Clapper.MediaItem] items to the + * [class@Clapper.Queue] for playback. For more information please refer to the Clapper + * playback library documentation. + * + * # Actions + * + * You can use built-in actions of parent [class@ClapperGtk.Av]. + * See its documentation for the list of available ones. + * + * # ClapperGtkAudio as GtkBuildable + * + * #ClapperGtkAudio implementation of the [iface@Gtk.Buildable] interface supports + * placing a single widget (which might then hold multiple widgets) as `` element. + * + * ```xml + * + * + * + * horizontal + * + * + * + * + * + * + * + * + * + * + * + * + * ``` + * + * Since: 0.10 + */ + +#include "config.h" + +#include "clapper-gtk-audio.h" + +#define GST_CAT_DEFAULT clapper_gtk_audio_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +struct _ClapperGtkAudio +{ + ClapperGtkAv parent; + + GtkWidget *child; +}; + +static void +clapper_gtk_audio_add_child (GtkBuildable *buildable, + GtkBuilder *builder, GObject *child, const char *type) +{ + if (GTK_IS_WIDGET (child)) { + clapper_gtk_audio_set_child (CLAPPER_GTK_AUDIO (buildable), GTK_WIDGET (child)); + } else { + GtkBuildableIface *parent_iface = g_type_interface_peek_parent (GTK_BUILDABLE_GET_IFACE (buildable)); + parent_iface->add_child (buildable, builder, child, type); + } +} + +static void +_buildable_iface_init (GtkBuildableIface *iface) +{ + iface->add_child = clapper_gtk_audio_add_child; +} + +#define parent_class clapper_gtk_audio_parent_class +G_DEFINE_TYPE_WITH_CODE (ClapperGtkAudio, clapper_gtk_audio, CLAPPER_GTK_TYPE_AV, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, _buildable_iface_init)) + +enum +{ + PROP_0, + PROP_CHILD, + PROP_LAST +}; + +static GParamSpec *param_specs[PROP_LAST] = { NULL, }; + +static inline void +_unparent_child (ClapperGtkAudio *self) +{ + GtkWidget *child; + + if ((child = gtk_widget_get_first_child (GTK_WIDGET (self)))) + gtk_widget_unparent (child); +} + +/** + * clapper_gtk_audio_new: + * + * Creates a new #ClapperGtkAudio instance. + * + * Newly created audio widget will also have set "scaletempo" GStreamer element + * as default audio filter on its [class@Clapper.Player] and disable video and + * subtitle streams. This can be changed after construction by setting + * corresponding player properties. + * + * Returns: a new audio #GtkWidget. + */ +GtkWidget * +clapper_gtk_audio_new (void) +{ + return g_object_new (CLAPPER_GTK_TYPE_AUDIO, NULL); +} + +/** + * clapper_gtk_audio_set_child: + * @audio: a #ClapperGtkAudio + * @child: (nullable): a #GtkWidget + * + * Set a child #GtkWidget of @audio. + */ +void +clapper_gtk_audio_set_child (ClapperGtkAudio *self, GtkWidget *child) +{ + g_return_if_fail (CLAPPER_GTK_IS_AUDIO (self)); + g_return_if_fail (GTK_IS_WIDGET (child)); + + _unparent_child (self); + if (child) + gtk_widget_set_parent (child, GTK_WIDGET (self)); +} + +/** + * clapper_gtk_audio_get_child: + * @audio: a #ClapperGtkAudio + * + * Get a child #GtkWidget of @audio. + * + * Returns: (transfer none) (nullable): #GtkWidget set as child. + */ +GtkWidget * +clapper_gtk_audio_get_child (ClapperGtkAudio *self) +{ + g_return_val_if_fail (CLAPPER_GTK_IS_AUDIO (self), NULL); + + return gtk_widget_get_first_child (GTK_WIDGET (self)); +} + +static void +clapper_gtk_audio_init (ClapperGtkAudio *self) +{ +} + +static void +clapper_gtk_audio_constructed (GObject *object) +{ + ClapperGtkAudio *self = CLAPPER_GTK_AUDIO_CAST (object); + ClapperPlayer *player; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + player = clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self)); + + clapper_player_set_video_enabled (player, FALSE); + clapper_player_set_subtitles_enabled (player, FALSE); +} + +static void +clapper_gtk_audio_dispose (GObject *object) +{ + ClapperGtkAudio *self = CLAPPER_GTK_AUDIO_CAST (object); + + _unparent_child (self); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +clapper_gtk_audio_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + ClapperGtkAudio *self = CLAPPER_GTK_AUDIO_CAST (object); + + switch (prop_id) { + case PROP_CHILD: + g_value_set_object (value, clapper_gtk_audio_get_child (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +clapper_gtk_audio_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + ClapperGtkAudio *self = CLAPPER_GTK_AUDIO_CAST (object); + + switch (prop_id) { + case PROP_CHILD: + clapper_gtk_audio_set_child (self, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +clapper_gtk_audio_class_init (ClapperGtkAudioClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GtkWidgetClass *widget_class = (GtkWidgetClass *) klass; + + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergtkaudio", GST_DEBUG_FG_MAGENTA, + "Clapper GTK Audio"); + + gobject_class->constructed = clapper_gtk_audio_constructed; + gobject_class->get_property = clapper_gtk_audio_get_property; + gobject_class->set_property = clapper_gtk_audio_set_property; + gobject_class->dispose = clapper_gtk_audio_dispose; + + /** + * ClapperGtkAudio:child: + * + * The child widget of `ClapperGtkAudio`. + */ + param_specs[PROP_CHILD] = g_param_spec_object ("child", + NULL, NULL, GTK_TYPE_WIDGET, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (gobject_class, PROP_LAST, param_specs); + + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); + gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GENERIC); + gtk_widget_class_set_css_name (widget_class, "clapper-gtk-audio"); +} diff --git a/src/lib/clapper-gtk/clapper-gtk-audio.h b/src/lib/clapper-gtk/clapper-gtk-audio.h new file mode 100644 index 00000000..12ae4397 --- /dev/null +++ b/src/lib/clapper-gtk/clapper-gtk-audio.h @@ -0,0 +1,49 @@ +/* Clapper GTK Integration Library + * Copyright (C) 2025 Rafał Dzięgiel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * . + */ + +#pragma once + +#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include + +#include +#include + +G_BEGIN_DECLS + +#define CLAPPER_GTK_TYPE_AUDIO (clapper_gtk_audio_get_type()) +#define CLAPPER_GTK_AUDIO_CAST(obj) ((ClapperGtkAudio *)(obj)) + +CLAPPER_GTK_API +G_DECLARE_FINAL_TYPE (ClapperGtkAudio, clapper_gtk_audio, CLAPPER_GTK, AUDIO, ClapperGtkAv) + +CLAPPER_GTK_API +GtkWidget * clapper_gtk_audio_new (void); + +CLAPPER_GTK_API +void clapper_gtk_audio_set_child (ClapperGtkAudio *audio, GtkWidget *child); + +CLAPPER_GTK_API +GtkWidget * clapper_gtk_audio_get_child (ClapperGtkAudio *audio); + +G_END_DECLS diff --git a/src/lib/clapper-gtk/clapper-gtk-av.c b/src/lib/clapper-gtk/clapper-gtk-av.c new file mode 100644 index 00000000..ffd686b3 --- /dev/null +++ b/src/lib/clapper-gtk/clapper-gtk-av.c @@ -0,0 +1,645 @@ +/* Clapper GTK Integration Library + * Copyright (C) 2025 Rafał Dzięgiel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * . + */ + +/** + * ClapperGtkAv: + * + * A base class for GTK audio and video widgets. + * + * See its descendants: [class@ClapperGtk.Audio] and [class@ClapperGtk.Video]. + * + * # Actions + * + * #ClapperGtkAv defines a set of built-in actions: + * + * ```yaml + * - "av.toggle-play": toggle play/pause + * - "av.play": start/resume playback + * - "av.pause": pause playback + * - "av.stop": stop playback + * - "av.seek": seek to position (variant "d") + * - "av.seek-custom": seek to position using seek method (variant "(di)") + * - "av.toggle-mute": toggle mute state + * - "av.set-mute": set mute state (variant "b") + * - "av.volume-up": increase volume by 2% + * - "av.volume-down": decrease volume by 2% + * - "av.set-volume": set volume to specified value (variant "d") + * - "av.speed-up": increase speed (from 0.05x - 2x range to nearest quarter) + * - "av.speed-down": decrease speed (from 0.05x - 2x range to nearest quarter) + * - "av.set-speed": set speed to specified value (variant "d") + * - "av.previous-item": select previous item in queue + * - "av.next-item": select next item in queue + * - "av.select-item": select item at specified index in queue (variant "u") + * ``` + * + * Since: 0.10 + */ + +#include "config.h" + +#include + +#include "clapper-gtk-av.h" + +#define PERCENTAGE_ROUND(a) (round ((gdouble) a / 0.01) * 0.01) + +#define DEFAULT_AUTO_INHIBIT FALSE + +#define GST_CAT_DEFAULT clapper_gtk_av_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +typedef struct _ClapperGtkAvPrivate ClapperGtkAvPrivate; + +struct _ClapperGtkAvPrivate +{ + ClapperPlayer *player; + gboolean auto_inhibit; + + guint inhibit_cookie; +}; + +#define parent_class clapper_gtk_av_parent_class +G_DEFINE_TYPE_WITH_PRIVATE (ClapperGtkAv, clapper_gtk_av, GTK_TYPE_WIDGET) + +enum +{ + PROP_0, + PROP_PLAYER, + PROP_AUTO_INHIBIT, + PROP_INHIBITED, + PROP_LAST +}; + +static gboolean provider_added = FALSE; +static GParamSpec *param_specs[PROP_LAST] = { NULL, }; + +static void +toggle_play_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + ClapperPlayer *player = clapper_gtk_av_get_player (self); + + switch (clapper_player_get_state (player)) { + case CLAPPER_PLAYER_STATE_PLAYING: + clapper_player_pause (player); + break; + case CLAPPER_PLAYER_STATE_STOPPED: + case CLAPPER_PLAYER_STATE_PAUSED: + clapper_player_play (player); + break; + default: + break; + } +} + +static void +play_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + ClapperPlayer *player = clapper_gtk_av_get_player (self); + + clapper_player_play (player); +} + +static void +pause_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + ClapperPlayer *player = clapper_gtk_av_get_player (self); + + clapper_player_pause (player); +} + +static void +stop_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + ClapperPlayer *player = clapper_gtk_av_get_player (self); + + clapper_player_stop (player); +} + +static void +seek_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + ClapperPlayer *player = clapper_gtk_av_get_player (self); + gdouble position = g_variant_get_double (parameter); + + clapper_player_seek (player, position); +} + +static void +seek_custom_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + ClapperPlayer *player = clapper_gtk_av_get_player (self); + ClapperPlayerSeekMethod method = CLAPPER_PLAYER_SEEK_METHOD_NORMAL; + gdouble position = 0; + + g_variant_get (parameter, "(di)", &position, &method); + clapper_player_seek_custom (player, position, method); +} + +static void +toggle_mute_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + ClapperPlayer *player = clapper_gtk_av_get_player (self); + + clapper_player_set_mute (player, !clapper_player_get_mute (player)); +} + +static void +set_mute_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + ClapperPlayer *player = clapper_gtk_av_get_player (self); + gboolean mute = g_variant_get_boolean (parameter); + + clapper_player_set_mute (player, mute); +} + +static void +volume_up_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + ClapperPlayer *player = clapper_gtk_av_get_player (self); + gdouble volume = (clapper_player_get_volume (player) + 0.02); + + if (volume > 2.0) + volume = 2.0; + + clapper_player_set_volume (player, PERCENTAGE_ROUND (volume)); +} + +static void +volume_down_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + ClapperPlayer *player = clapper_gtk_av_get_player (self); + gdouble volume = (clapper_player_get_volume (player) - 0.02); + + if (volume < 0) + volume = 0; + + clapper_player_set_volume (player, PERCENTAGE_ROUND (volume)); +} + +static void +set_volume_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + ClapperPlayer *player = clapper_gtk_av_get_player (self); + gdouble volume = g_variant_get_double (parameter); + + clapper_player_set_volume (player, volume); +} + +static void +speed_up_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + ClapperPlayer *player = clapper_gtk_av_get_player (self); + gdouble dest, speed = clapper_player_get_speed (player); + + if (speed >= 2.0) + return; + + dest = 0.25; + while (speed >= dest) + dest += 0.25; + + if (dest > 2.0) + dest = 2.0; + + clapper_player_set_speed (player, dest); +} + +static void +speed_down_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + ClapperPlayer *player = clapper_gtk_av_get_player (self); + gdouble dest, speed = clapper_player_get_speed (player); + + if (speed <= 0.05) + return; + + dest = 2.0; + while (speed <= dest) + dest -= 0.25; + + if (dest < 0.05) + dest = 0.05; + + clapper_player_set_speed (player, dest); +} + +static void +set_speed_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + ClapperPlayer *player = clapper_gtk_av_get_player (self); + gdouble speed = g_variant_get_double (parameter); + + clapper_player_set_speed (player, speed); +} + +static void +previous_item_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + ClapperPlayer *player = clapper_gtk_av_get_player (self); + + clapper_queue_select_previous_item (clapper_player_get_queue (player)); +} + +static void +next_item_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + ClapperPlayer *player = clapper_gtk_av_get_player (self); + + clapper_queue_select_next_item (clapper_player_get_queue (player)); +} + +static void +select_item_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + ClapperPlayer *player = clapper_gtk_av_get_player (self); + guint index = g_variant_get_uint32 (parameter); + + clapper_queue_select_index (clapper_player_get_queue (player), index); +} + +static void +_ensure_css_provider (void) +{ + GdkDisplay *display; + + if (provider_added) + return; + + display = gdk_display_get_default (); + + if (G_LIKELY (display != NULL)) { + GtkCssProvider *provider = gtk_css_provider_new (); + gtk_css_provider_load_from_resource (provider, + CLAPPER_GTK_RESOURCE_PREFIX "/css/styles.css"); + + gtk_style_context_add_provider_for_display (display, + (GtkStyleProvider *) provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1); + g_object_unref (provider); + + provider_added = TRUE; + } +} + +static inline void +_set_inhibit_session (ClapperGtkAv *self, gboolean inhibit) +{ + ClapperGtkAvPrivate *priv = clapper_gtk_av_get_instance_private (self); + GtkRoot *root; + GApplication *app; + gboolean inhibited = (priv->inhibit_cookie != 0); + + if (inhibited == inhibit) + return; + + GST_DEBUG_OBJECT (self, "Trying to %sinhibit session...", (inhibit) ? "" : "un"); + + root = gtk_widget_get_root (GTK_WIDGET (self)); + + if (!root && !GTK_IS_WINDOW (root)) { + GST_WARNING_OBJECT (self, "Cannot %sinhibit session " + "without root window", (inhibit) ? "" : "un"); + return; + } + + /* NOTE: Not using application from window prop, + * as it goes away early when unrooting */ + app = g_application_get_default (); + + if (!app && !GTK_IS_APPLICATION (app)) { + GST_WARNING_OBJECT (self, "Cannot %sinhibit session " + "without window application set", (inhibit) ? "" : "un"); + return; + } + + if (inhibited) { + gtk_application_uninhibit (GTK_APPLICATION (app), priv->inhibit_cookie); + priv->inhibit_cookie = 0; + } + if (inhibit) { + priv->inhibit_cookie = gtk_application_inhibit (GTK_APPLICATION (app), + GTK_WINDOW (root), GTK_APPLICATION_INHIBIT_IDLE, + "Media is playing"); + } + + GST_DEBUG_OBJECT (self, "Session %sinhibited", (inhibit) ? "" : "un"); + g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_INHIBITED]); +} + +static void +_player_state_changed_cb (ClapperPlayer *player, + GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkAv *self) +{ + ClapperGtkAvPrivate *priv = clapper_gtk_av_get_instance_private (self); + + if (priv->auto_inhibit) { + ClapperPlayerState state = clapper_player_get_state (player); + _set_inhibit_session (self, state == CLAPPER_PLAYER_STATE_PLAYING); + } +} + +/** + * clapper_gtk_av_get_player: + * @av: a #ClapperGtkAv + * + * Get #ClapperPlayer used by this #ClapperGtkAv instance. + * + * Returns: (transfer none): a #ClapperPlayer used by widget. + * + * Since: 0.10 + */ +ClapperPlayer * +clapper_gtk_av_get_player (ClapperGtkAv *self) +{ + ClapperGtkAvPrivate *priv; + + g_return_val_if_fail (CLAPPER_GTK_IS_AV (self), NULL); + + priv = clapper_gtk_av_get_instance_private (self); + + return priv->player; +} + +/** + * clapper_gtk_av_set_auto_inhibit: + * @av: a #ClapperGtkAv + * @inhibit: whether to enable automatic session inhibit + * + * Set whether widget should try to automatically inhibit session + * from idling (and possibly screen going black) when media is playing. + * + * Since: 0.10 + */ +void +clapper_gtk_av_set_auto_inhibit (ClapperGtkAv *self, gboolean inhibit) +{ + ClapperGtkAvPrivate *priv; + + g_return_if_fail (CLAPPER_GTK_IS_AV (self)); + + priv = clapper_gtk_av_get_instance_private (self); + + if (priv->auto_inhibit != inhibit) { + priv->auto_inhibit = inhibit; + + /* Uninhibit if we were auto inhibited earlier */ + if (!priv->auto_inhibit) + _set_inhibit_session (self, FALSE); + + g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_AUTO_INHIBIT]); + } +} + +/** + * clapper_gtk_av_get_auto_inhibit: + * @av: a #ClapperGtkAv + * + * Get whether automatic session inhibit is enabled. + * + * Returns: %TRUE if enabled, %FALSE otherwise. + * + * Since: 0.10 + */ +gboolean +clapper_gtk_av_get_auto_inhibit (ClapperGtkAv *self) +{ + ClapperGtkAvPrivate *priv; + + g_return_val_if_fail (CLAPPER_GTK_IS_AV (self), FALSE); + + priv = clapper_gtk_av_get_instance_private (self); + + return priv->auto_inhibit; +} + +/** + * clapper_gtk_av_get_inhibited: + * @av: a #ClapperGtkAv + * + * Get whether session is currently inhibited by + * [property@ClapperGtk.Av:auto-inhibit]. + * + * Returns: %TRUE if inhibited, %FALSE otherwise. + * + * Since: 0.10 + */ +gboolean +clapper_gtk_av_get_inhibited (ClapperGtkAv *self) +{ + ClapperGtkAvPrivate *priv; + + g_return_val_if_fail (CLAPPER_GTK_IS_AV (self), FALSE); + + priv = clapper_gtk_av_get_instance_private (self); + + return (priv->inhibit_cookie != 0); +} + +static void +clapper_gtk_av_root (GtkWidget *widget) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + ClapperGtkAvPrivate *priv = clapper_gtk_av_get_instance_private (self); + + _ensure_css_provider (); + + GTK_WIDGET_CLASS (parent_class)->root (widget); + + if (priv->auto_inhibit) { + ClapperPlayerState state = clapper_player_get_state (priv->player); + _set_inhibit_session (self, state == CLAPPER_PLAYER_STATE_PLAYING); + } +} + +static void +clapper_gtk_av_unroot (GtkWidget *widget) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (widget); + + _set_inhibit_session (self, FALSE); + + GTK_WIDGET_CLASS (parent_class)->unroot (widget); +} + +static void +clapper_gtk_av_init (ClapperGtkAv *self) +{ + ClapperGtkAvPrivate *priv = clapper_gtk_av_get_instance_private (self); + + priv->auto_inhibit = DEFAULT_AUTO_INHIBIT; +} + +static void +clapper_gtk_av_constructed (GObject *object) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (object); + ClapperGtkAvPrivate *priv = clapper_gtk_av_get_instance_private (self); + GstElement *afilter; + + priv->player = clapper_player_new (); + + g_signal_connect (priv->player, "notify::state", + G_CALLBACK (_player_state_changed_cb), self); + + afilter = gst_element_factory_make ("scaletempo", NULL); + if (G_LIKELY (afilter != NULL)) + clapper_player_set_audio_filter (priv->player, afilter); + + G_OBJECT_CLASS (parent_class)->constructed (object); +} + +static void +clapper_gtk_av_dispose (GObject *object) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (object); + ClapperGtkAvPrivate *priv = clapper_gtk_av_get_instance_private (self); + + /* Something else might still be holding a reference on the player, + * thus we should disconnect everything before disposing template */ + if (priv->player) { + g_signal_handlers_disconnect_by_func (priv->player, + _player_state_changed_cb, self); + } + + gst_clear_object (&priv->player); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +clapper_gtk_av_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (object); + + switch (prop_id) { + case PROP_PLAYER: + g_value_set_object (value, clapper_gtk_av_get_player (self)); + break; + case PROP_AUTO_INHIBIT: + g_value_set_boolean (value, clapper_gtk_av_get_auto_inhibit (self)); + break; + case PROP_INHIBITED: + g_value_set_boolean (value, clapper_gtk_av_get_inhibited (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +clapper_gtk_av_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + ClapperGtkAv *self = CLAPPER_GTK_AV_CAST (object); + + switch (prop_id) { + case PROP_AUTO_INHIBIT: + clapper_gtk_av_set_auto_inhibit (self, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +clapper_gtk_av_class_init (ClapperGtkAvClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GtkWidgetClass *widget_class = (GtkWidgetClass *) klass; + + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergtkav", GST_DEBUG_FG_MAGENTA, + "Clapper GTK AV"); + + widget_class->root = clapper_gtk_av_root; + widget_class->unroot = clapper_gtk_av_unroot; + + gobject_class->constructed = clapper_gtk_av_constructed; + gobject_class->get_property = clapper_gtk_av_get_property; + gobject_class->set_property = clapper_gtk_av_set_property; + gobject_class->dispose = clapper_gtk_av_dispose; + + /** + * ClapperGtkAv:player: + * + * A #ClapperPlayer used by widget. + */ + param_specs[PROP_PLAYER] = g_param_spec_object ("player", + NULL, NULL, CLAPPER_TYPE_PLAYER, + G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + /** + * ClapperGtkAv:auto-inhibit: + * + * Try to automatically inhibit session when media is playing. + */ + param_specs[PROP_AUTO_INHIBIT] = g_param_spec_boolean ("auto-inhibit", + NULL, NULL, DEFAULT_AUTO_INHIBIT, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + /** + * ClapperGtkAv:inhibited: + * + * Get whether session is currently inhibited by playback. + */ + param_specs[PROP_INHIBITED] = g_param_spec_boolean ("inhibited", + NULL, NULL, FALSE, + G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (gobject_class, PROP_LAST, param_specs); + + gtk_widget_class_install_action (widget_class, "av.toggle-play", NULL, toggle_play_action_cb); + gtk_widget_class_install_action (widget_class, "av.play", NULL, play_action_cb); + gtk_widget_class_install_action (widget_class, "av.pause", NULL, pause_action_cb); + gtk_widget_class_install_action (widget_class, "av.stop", NULL, stop_action_cb); + gtk_widget_class_install_action (widget_class, "av.seek", "d", seek_action_cb); + gtk_widget_class_install_action (widget_class, "av.seek-custom", "(di)", seek_custom_action_cb); + gtk_widget_class_install_action (widget_class, "av.toggle-mute", NULL, toggle_mute_action_cb); + gtk_widget_class_install_action (widget_class, "av.set-mute", "b", set_mute_action_cb); + gtk_widget_class_install_action (widget_class, "av.volume-up", NULL, volume_up_action_cb); + gtk_widget_class_install_action (widget_class, "av.volume-down", NULL, volume_down_action_cb); + gtk_widget_class_install_action (widget_class, "av.set-volume", "d", set_volume_action_cb); + gtk_widget_class_install_action (widget_class, "av.speed-up", NULL, speed_up_action_cb); + gtk_widget_class_install_action (widget_class, "av.speed-down", NULL, speed_down_action_cb); + gtk_widget_class_install_action (widget_class, "av.set-speed", "d", set_speed_action_cb); + gtk_widget_class_install_action (widget_class, "av.previous-item", NULL, previous_item_action_cb); + gtk_widget_class_install_action (widget_class, "av.next-item", NULL, next_item_action_cb); + gtk_widget_class_install_action (widget_class, "av.select-item", "u", select_item_action_cb); + + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); + gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GENERIC); + gtk_widget_class_set_css_name (widget_class, "clapper-gtk-av"); +} diff --git a/src/lib/clapper-gtk/clapper-gtk-av.h b/src/lib/clapper-gtk/clapper-gtk-av.h new file mode 100644 index 00000000..03552fc8 --- /dev/null +++ b/src/lib/clapper-gtk/clapper-gtk-av.h @@ -0,0 +1,60 @@ +/* Clapper GTK Integration Library + * Copyright (C) 2025 Rafał Dzięgiel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * . + */ + +#pragma once + +#if !defined(__CLAPPER_GTK_INSIDE__) && !defined(CLAPPER_GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include +#include + +#include + +G_BEGIN_DECLS + +#define CLAPPER_GTK_TYPE_AV (clapper_gtk_av_get_type()) +#define CLAPPER_GTK_AV_CAST(obj) ((ClapperGtkAv *)(obj)) + +CLAPPER_GTK_API +G_DECLARE_DERIVABLE_TYPE (ClapperGtkAv, clapper_gtk_av, CLAPPER_GTK, AV, GtkWidget) + +struct _ClapperGtkAvClass +{ + GtkWidgetClass parent_class; + + /*< private >*/ + gpointer padding[4]; +}; + +CLAPPER_GTK_API +ClapperPlayer * clapper_gtk_av_get_player (ClapperGtkAv *av); + +CLAPPER_GTK_API +void clapper_gtk_av_set_auto_inhibit (ClapperGtkAv *av, gboolean inhibit); + +CLAPPER_GTK_API +gboolean clapper_gtk_av_get_auto_inhibit (ClapperGtkAv *av); + +CLAPPER_GTK_API +gboolean clapper_gtk_av_get_inhibited (ClapperGtkAv *av); + +G_END_DECLS diff --git a/src/lib/clapper-gtk/clapper-gtk-next-item-button.c b/src/lib/clapper-gtk/clapper-gtk-next-item-button.c index ffb4960f..1087aa2b 100644 --- a/src/lib/clapper-gtk/clapper-gtk-next-item-button.c +++ b/src/lib/clapper-gtk/clapper-gtk-next-item-button.c @@ -82,7 +82,7 @@ clapper_gtk_next_item_button_init (ClapperGtkNextItemButton *self) { gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE); gtk_button_set_icon_name (GTK_BUTTON (self), "media-skip-forward-symbolic"); - gtk_actionable_set_action_name (GTK_ACTIONABLE (self), "video.next-item"); + gtk_actionable_set_action_name (GTK_ACTIONABLE (self), "av.next-item"); } static void diff --git a/src/lib/clapper-gtk/clapper-gtk-previous-item-button.c b/src/lib/clapper-gtk/clapper-gtk-previous-item-button.c index 82fc78ab..f6153860 100644 --- a/src/lib/clapper-gtk/clapper-gtk-previous-item-button.c +++ b/src/lib/clapper-gtk/clapper-gtk-previous-item-button.c @@ -82,7 +82,7 @@ clapper_gtk_previous_item_button_init (ClapperGtkPreviousItemButton *self) { gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE); gtk_button_set_icon_name (GTK_BUTTON (self), "media-skip-backward-symbolic"); - gtk_actionable_set_action_name (GTK_ACTIONABLE (self), "video.previous-item"); + gtk_actionable_set_action_name (GTK_ACTIONABLE (self), "av.previous-item"); } static void diff --git a/src/lib/clapper-gtk/clapper-gtk-toggle-play-button.c b/src/lib/clapper-gtk/clapper-gtk-toggle-play-button.c index 2e09a42d..59725521 100644 --- a/src/lib/clapper-gtk/clapper-gtk-toggle-play-button.c +++ b/src/lib/clapper-gtk/clapper-gtk-toggle-play-button.c @@ -83,7 +83,7 @@ static void clapper_gtk_toggle_play_button_init (ClapperGtkTogglePlayButton *self) { gtk_button_set_icon_name (GTK_BUTTON (self), PLAY_ICON_NAME); - gtk_actionable_set_action_name (GTK_ACTIONABLE (self), "video.toggle-play"); + gtk_actionable_set_action_name (GTK_ACTIONABLE (self), "av.toggle-play"); } static void diff --git a/src/lib/clapper-gtk/clapper-gtk-utils.c b/src/lib/clapper-gtk/clapper-gtk-utils.c index 30319235..1c4c78de 100644 --- a/src/lib/clapper-gtk/clapper-gtk-utils.c +++ b/src/lib/clapper-gtk/clapper-gtk-utils.c @@ -21,7 +21,7 @@ #include #include "clapper-gtk-utils-private.h" -#include "clapper-gtk-video.h" +#include "clapper-gtk-av.h" static gboolean initialized = FALSE; @@ -29,18 +29,18 @@ static gboolean initialized = FALSE; * clapper_gtk_get_player_from_ancestor: * @widget: a #GtkWidget * - * Get [class@Clapper.Player] used by [class@ClapperGtk.Video] ancestor of @widget. + * Get [class@Clapper.Player] used by [class@ClapperGtk.Av] ancestor of @widget. * * This utility is a convenience wrapper for calling [method@Gtk.Widget.get_ancestor] - * of type `CLAPPER_GTK_TYPE_VIDEO` and [method@ClapperGtk.Video.get_player] with + * of type `CLAPPER_GTK_TYPE_AV` and [method@ClapperGtk.Av.get_player] with * additional %NULL checking and type casting. * * This is meant to be used mainly for custom widget development as an easy access to the * underlying parent [class@Clapper.Player] object. If you want to get the player from - * [class@ClapperGtk.Video] widget itself, use [method@ClapperGtk.Video.get_player] instead. + * [class@ClapperGtk.Av] widget itself, use [method@ClapperGtk.Av.get_player] instead. * * Rememeber that this function will return %NULL when widget does not have - * a [class@ClapperGtk.Video] ancestor in widget hierarchy (widget is not yet placed). + * a [class@ClapperGtk.Av] ancestor in widget hierarchy (widget is not yet placed). * * Returns: (transfer none) (nullable): a #ClapperPlayer from ancestor of a @widget. */ @@ -52,8 +52,8 @@ clapper_gtk_get_player_from_ancestor (GtkWidget *widget) g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); - if ((parent = gtk_widget_get_ancestor (widget, CLAPPER_GTK_TYPE_VIDEO))) - player = clapper_gtk_video_get_player (CLAPPER_GTK_VIDEO_CAST (parent)); + if ((parent = gtk_widget_get_ancestor (widget, CLAPPER_GTK_TYPE_AV))) + player = clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (parent)); return player; } diff --git a/src/lib/clapper-gtk/clapper-gtk-video.c b/src/lib/clapper-gtk/clapper-gtk-video.c index c30fb038..1b83c0b8 100644 --- a/src/lib/clapper-gtk/clapper-gtk-video.c +++ b/src/lib/clapper-gtk/clapper-gtk-video.c @@ -22,8 +22,8 @@ * A ready to be used GTK video widget implementing Clapper API. * * #ClapperGtkVideo is the main widget exposed by `ClapperGtk` API. It both displays - * videos played by [class@Clapper.Player] (exposed as its property) and manages - * revealing and fading of any additional widgets overlaid on top of it. + * videos played by [class@Clapper.Player] (exposed as [property@ClapperGtk.Av:player] property) + * and manages revealing and fading of any additional widgets overlaid on top of it. * * Other widgets provided by `ClapperGtk` library, once placed anywhere on video * (including nesting within another widget like [class@Gtk.Box]) will automatically @@ -34,7 +34,7 @@ * # Basic usage * * A typical use case is to embed video widget as part of your app where video playback - * is needed. Get the [class@Clapper.Player] belonging to the video widget and start adding + * is needed. Get the [class@Clapper.Player] belonging to the AV widget and start adding * new [class@Clapper.MediaItem] items to the [class@Clapper.Queue] for playback. * For more information please refer to the Clapper playback library documentation. * @@ -46,27 +46,8 @@ * * # Actions * - * #ClapperGtkVideo defines a set of built-in actions: - * - * ```yaml - * - "video.toggle-play": toggle play/pause - * - "video.play": start/resume playback - * - "video.pause": pause playback - * - "video.stop": stop playback - * - "video.seek": seek to position (variant "d") - * - "video.seek-custom": seek to position using seek method (variant "(di)") - * - "video.toggle-mute": toggle mute state - * - "video.set-mute": set mute state (variant "b") - * - "video.volume-up": increase volume by 2% - * - "video.volume-down": decrease volume by 2% - * - "video.set-volume": set volume to specified value (variant "d") - * - "video.speed-up": increase speed (from 0.05x - 2x range to nearest quarter) - * - "video.speed-down": decrease speed (from 0.05x - 2x range to nearest quarter) - * - "video.set-speed": set speed to specified value (variant "d") - * - "video.previous-item": select previous item in queue - * - "video.next-item": select next item in queue - * - "video.select-item": select item at specified index in queue (variant "u") - * ``` + * You can use built-in actions of parent [class@ClapperGtk.Av]. + * See its documentation, for the list of available ones. * * # ClapperGtkVideo as GtkBuildable * @@ -93,8 +74,6 @@ #include "config.h" -#include - #include "clapper-gtk-enums.h" #include "clapper-gtk-video.h" #include "clapper-gtk-lead-container.h" @@ -102,11 +81,8 @@ #include "clapper-gtk-buffering-animation-private.h" #include "clapper-gtk-video-placeholder-private.h" -#define PERCENTAGE_ROUND(a) (round ((gdouble) a / 0.01) * 0.01) - #define DEFAULT_FADE_DELAY 3000 #define DEFAULT_TOUCH_FADE_DELAY 5000 -#define DEFAULT_AUTO_INHIBIT FALSE #define MIN_MOTION_DELAY 100000 @@ -115,7 +91,7 @@ GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); struct _ClapperGtkVideo { - GtkWidget parent; + ClapperGtkAv parent; GtkWidget *overlay; GtkWidget *status; @@ -125,10 +101,8 @@ struct _ClapperGtkVideo GtkGesture *click_gesture; /* Props */ - ClapperPlayer *player; guint fade_delay; guint touch_fade_delay; - gboolean auto_inhibit; GPtrArray *overlays; GPtrArray *fading_overlays; @@ -140,8 +114,6 @@ struct _ClapperGtkVideo guint fade_timeout; gboolean reveal, revealed; - guint inhibit_cookie; - /* Current pointer coords and type */ gdouble x, y; gboolean is_touch; @@ -174,17 +146,14 @@ _buildable_iface_init (GtkBuildableIface *iface) } #define parent_class clapper_gtk_video_parent_class -G_DEFINE_TYPE_WITH_CODE (ClapperGtkVideo, clapper_gtk_video, GTK_TYPE_WIDGET, +G_DEFINE_TYPE_WITH_CODE (ClapperGtkVideo, clapper_gtk_video, CLAPPER_GTK_TYPE_AV, G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, _buildable_iface_init)) enum { PROP_0, - PROP_PLAYER, PROP_FADE_DELAY, PROP_TOUCH_FADE_DELAY, - PROP_AUTO_INHIBIT, - PROP_INHIBITED, PROP_LAST }; @@ -195,209 +164,111 @@ enum SIGNAL_LAST }; -static gboolean provider_added = FALSE; static GParamSpec *param_specs[PROP_LAST] = { NULL, }; static guint signals[SIGNAL_LAST] = { 0, }; +/* FIXME: 1.0: Remove these compat actions, since they were moved to base class */ + static void toggle_play_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) { - ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget); - ClapperPlayer *player = clapper_gtk_video_get_player (self); - - switch (clapper_player_get_state (player)) { - case CLAPPER_PLAYER_STATE_PLAYING: - clapper_player_pause (player); - break; - case CLAPPER_PLAYER_STATE_STOPPED: - case CLAPPER_PLAYER_STATE_PAUSED: - clapper_player_play (player); - break; - default: - break; - } + gtk_widget_activate_action_variant (widget, "av.toggle-play", parameter); } static void play_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) { - ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget); - ClapperPlayer *player = clapper_gtk_video_get_player (self); - - clapper_player_play (player); + gtk_widget_activate_action_variant (widget, "av.play", parameter); } static void pause_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) { - ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget); - ClapperPlayer *player = clapper_gtk_video_get_player (self); - - clapper_player_pause (player); + gtk_widget_activate_action_variant (widget, "av.pause", parameter); } static void stop_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) { - ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget); - ClapperPlayer *player = clapper_gtk_video_get_player (self); - - clapper_player_stop (player); + gtk_widget_activate_action_variant (widget, "av.stop", parameter); } static void seek_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) { - ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget); - ClapperPlayer *player = clapper_gtk_video_get_player (self); - gdouble position = g_variant_get_double (parameter); - - clapper_player_seek (player, position); + gtk_widget_activate_action_variant (widget, "av.seek", parameter); } static void seek_custom_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) { - ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget); - ClapperPlayer *player = clapper_gtk_video_get_player (self); - ClapperPlayerSeekMethod method = CLAPPER_PLAYER_SEEK_METHOD_NORMAL; - gdouble position = 0; - - g_variant_get (parameter, "(di)", &position, &method); - clapper_player_seek_custom (player, position, method); + gtk_widget_activate_action_variant (widget, "av.seek-custom", parameter); } static void toggle_mute_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) { - ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget); - ClapperPlayer *player = clapper_gtk_video_get_player (self); - - clapper_player_set_mute (player, !clapper_player_get_mute (player)); + gtk_widget_activate_action_variant (widget, "av.toggle-mute", parameter); } static void set_mute_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) { - ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget); - ClapperPlayer *player = clapper_gtk_video_get_player (self); - gboolean mute = g_variant_get_boolean (parameter); - - clapper_player_set_mute (player, mute); + gtk_widget_activate_action_variant (widget, "av.set-mute", parameter); } static void volume_up_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) { - ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget); - ClapperPlayer *player = clapper_gtk_video_get_player (self); - gdouble volume = (clapper_player_get_volume (player) + 0.02); - - if (volume > 2.0) - volume = 2.0; - - clapper_player_set_volume (player, PERCENTAGE_ROUND (volume)); + gtk_widget_activate_action_variant (widget, "av.volume-up", parameter); } static void volume_down_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) { - ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget); - ClapperPlayer *player = clapper_gtk_video_get_player (self); - gdouble volume = (clapper_player_get_volume (player) - 0.02); - - if (volume < 0) - volume = 0; - - clapper_player_set_volume (player, PERCENTAGE_ROUND (volume)); + gtk_widget_activate_action_variant (widget, "av.volume-down", parameter); } static void set_volume_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) { - ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget); - ClapperPlayer *player = clapper_gtk_video_get_player (self); - gdouble volume = g_variant_get_double (parameter); - - clapper_player_set_volume (player, volume); + gtk_widget_activate_action_variant (widget, "av.set-volume", parameter); } static void speed_up_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) { - ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget); - ClapperPlayer *player = clapper_gtk_video_get_player (self); - gdouble dest, speed = clapper_player_get_speed (player); - - if (speed >= 2.0) - return; - - dest = 0.25; - while (speed >= dest) - dest += 0.25; - - if (dest > 2.0) - dest = 2.0; - - clapper_player_set_speed (player, dest); + gtk_widget_activate_action_variant (widget, "av.speed-up", parameter); } static void speed_down_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) { - ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget); - ClapperPlayer *player = clapper_gtk_video_get_player (self); - gdouble dest, speed = clapper_player_get_speed (player); - - if (speed <= 0.05) - return; - - dest = 2.0; - while (speed <= dest) - dest -= 0.25; - - if (dest < 0.05) - dest = 0.05; - - clapper_player_set_speed (player, dest); + gtk_widget_activate_action_variant (widget, "av.speed-down", parameter); } static void set_speed_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) { - ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget); - ClapperPlayer *player = clapper_gtk_video_get_player (self); - gdouble speed = g_variant_get_double (parameter); - - clapper_player_set_speed (player, speed); + gtk_widget_activate_action_variant (widget, "av.set-speed", parameter); } static void previous_item_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) { - ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget); - ClapperPlayer *player = clapper_gtk_video_get_player (self); - - clapper_queue_select_previous_item (clapper_player_get_queue (player)); + gtk_widget_activate_action_variant (widget, "av.previous-item", parameter); } static void next_item_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) { - ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget); - ClapperPlayer *player = clapper_gtk_video_get_player (self); - - clapper_queue_select_next_item (clapper_player_get_queue (player)); + gtk_widget_activate_action_variant (widget, "av.next-item", parameter); } static void select_item_action_cb (GtkWidget *widget, const gchar *action_name, GVariant *parameter) { - ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget); - ClapperPlayer *player = clapper_gtk_video_get_player (self); - guint index = g_variant_get_uint32 (parameter); - - clapper_queue_select_index (clapper_player_get_queue (player), index); + gtk_widget_activate_action_variant (widget, "av.select-item", parameter); } static void @@ -871,73 +742,6 @@ touch_released_cb (GtkGestureClick *click, gint n_press, _reset_fade_timeout (self); } -static void -_ensure_css_provider (void) -{ - GdkDisplay *display; - - if (provider_added) - return; - - display = gdk_display_get_default (); - - if (G_LIKELY (display != NULL)) { - GtkCssProvider *provider = gtk_css_provider_new (); - gtk_css_provider_load_from_resource (provider, - CLAPPER_GTK_RESOURCE_PREFIX "/css/styles.css"); - - gtk_style_context_add_provider_for_display (display, - (GtkStyleProvider *) provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION - 1); - g_object_unref (provider); - - provider_added = TRUE; - } -} - -static inline void -_set_inhibit_session (ClapperGtkVideo *self, gboolean inhibit) -{ - GtkRoot *root; - GApplication *app; - gboolean inhibited = (self->inhibit_cookie != 0); - - if (inhibited == inhibit) - return; - - GST_DEBUG_OBJECT (self, "Trying to %sinhibit session...", (inhibit) ? "" : "un"); - - root = gtk_widget_get_root (GTK_WIDGET (self)); - - if (!root && !GTK_IS_WINDOW (root)) { - GST_WARNING_OBJECT (self, "Cannot %sinhibit session " - "without root window", (inhibit) ? "" : "un"); - return; - } - - /* NOTE: Not using application from window prop, - * as it goes away early when unrooting */ - app = g_application_get_default (); - - if (!app && !GTK_IS_APPLICATION (app)) { - GST_WARNING_OBJECT (self, "Cannot %sinhibit session " - "without window application set", (inhibit) ? "" : "un"); - return; - } - - if (inhibited) { - gtk_application_uninhibit (GTK_APPLICATION (app), self->inhibit_cookie); - self->inhibit_cookie = 0; - } - if (inhibit) { - self->inhibit_cookie = gtk_application_inhibit (GTK_APPLICATION (app), - GTK_WINDOW (root), GTK_APPLICATION_INHIBIT_IDLE, - "Video is playing"); - } - - GST_DEBUG_OBJECT (self, "Session %sinhibited", (inhibit) ? "" : "un"); - g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_INHIBITED]); -} - static inline void _set_buffering_animation_enabled (ClapperGtkVideo *self, gboolean enabled) { @@ -963,9 +767,6 @@ _player_state_changed_cb (ClapperPlayer *player, { ClapperPlayerState state = clapper_player_get_state (player); - if (self->auto_inhibit) - _set_inhibit_session (self, state == CLAPPER_PLAYER_STATE_PLAYING); - _set_buffering_animation_enabled (self, state == CLAPPER_PLAYER_STATE_BUFFERING); } @@ -1106,7 +907,7 @@ _fading_overlay_revealed_cb (GtkRevealer *revealer, * * Creates a new #ClapperGtkVideo instance. * - * Newly created video widget will also set some default GStreamer elements + * Newly created video widget will also have set some default GStreamer elements * on its [class@Clapper.Player]. This includes Clapper own video sink and * a "scaletempo" element as audio filter. Both can still be changed after * construction by setting corresponding player properties. @@ -1187,19 +988,21 @@ clapper_gtk_video_add_fading_overlay (ClapperGtkVideo *self, GtkWidget *widget) } /** - * clapper_gtk_video_get_player: + * clapper_gtk_video_get_player: (skip) * @video: a #ClapperGtkVideo * * Get #ClapperPlayer used by this #ClapperGtkVideo instance. * * Returns: (transfer none): a #ClapperPlayer used by video. + * + * Deprecated: 0.10: Use [method@ClapperGtk.Av.get_player] instead. */ ClapperPlayer * clapper_gtk_video_get_player (ClapperGtkVideo *self) { g_return_val_if_fail (CLAPPER_GTK_IS_VIDEO (self), NULL); - return self->player; + return clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self)); } /** @@ -1275,60 +1078,58 @@ clapper_gtk_video_get_touch_fade_delay (ClapperGtkVideo *self) } /** - * clapper_gtk_video_set_auto_inhibit: + * clapper_gtk_video_set_auto_inhibit: (skip) * @video: a #ClapperGtkVideo * @inhibit: whether to enable automatic session inhibit * * Set whether video should try to automatically inhibit session * from idling (and possibly screen going black) when video is playing. + * + * Deprecated: 0.10: Use [method@ClapperGtk.Av.set_auto_inhibit] instead. */ void clapper_gtk_video_set_auto_inhibit (ClapperGtkVideo *self, gboolean inhibit) { g_return_if_fail (CLAPPER_GTK_IS_VIDEO (self)); - if (self->auto_inhibit != inhibit) { - self->auto_inhibit = inhibit; - - /* Uninhibit if we were auto inhibited earlier */ - if (!self->auto_inhibit) - _set_inhibit_session (self, FALSE); - - g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_AUTO_INHIBIT]); - } + clapper_gtk_av_set_auto_inhibit (CLAPPER_GTK_AV_CAST (self), inhibit); } /** - * clapper_gtk_video_get_auto_inhibit: + * clapper_gtk_video_get_auto_inhibit: (skip) * @video: a #ClapperGtkVideo * * Get whether automatic session inhibit is enabled. * * Returns: %TRUE if enabled, %FALSE otherwise. + * + * Deprecated: 0.10: Use [method@ClapperGtk.Av.get_auto_inhibit] instead. */ gboolean clapper_gtk_video_get_auto_inhibit (ClapperGtkVideo *self) { g_return_val_if_fail (CLAPPER_GTK_IS_VIDEO (self), FALSE); - return self->auto_inhibit; + return clapper_gtk_av_get_auto_inhibit (CLAPPER_GTK_AV_CAST (self)); } /** - * clapper_gtk_video_get_inhibited: + * clapper_gtk_video_get_inhibited: (skip) * @video: a #ClapperGtkVideo * * Get whether session is currently inhibited by - * [property@ClapperGtk.Video:auto-inhibit]. + * [property@ClapperGtk.Av:auto-inhibit]. * * Returns: %TRUE if inhibited, %FALSE otherwise. + * + * Deprecated: 0.10: Use [method@ClapperGtk.Av.get_inhibited] instead. */ gboolean clapper_gtk_video_get_inhibited (ClapperGtkVideo *self) { g_return_val_if_fail (CLAPPER_GTK_IS_VIDEO (self), FALSE); - return (self->inhibit_cookie != 0); + return clapper_gtk_av_get_inhibited (CLAPPER_GTK_AV_CAST (self)); } static void @@ -1337,8 +1138,6 @@ clapper_gtk_video_root (GtkWidget *widget) ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (widget); GtkRoot *root; - _ensure_css_provider (); - GTK_WIDGET_CLASS (parent_class)->root (widget); root = gtk_widget_get_root (widget); @@ -1350,11 +1149,6 @@ clapper_gtk_video_root (GtkWidget *widget) G_CALLBACK (_window_is_active_cb), self); _window_is_active_cb (window, NULL, self); } - - if (self->auto_inhibit) { - ClapperPlayerState state = clapper_player_get_state (self->player); - _set_inhibit_session (self, state == CLAPPER_PLAYER_STATE_PLAYING); - } } static void @@ -1368,8 +1162,6 @@ clapper_gtk_video_unroot (GtkWidget *widget) _window_is_active_cb, self); } - _set_inhibit_session (self, FALSE); - GTK_WIDGET_CLASS (parent_class)->unroot (widget); } @@ -1385,7 +1177,6 @@ clapper_gtk_video_init (ClapperGtkVideo *self) self->fade_delay = DEFAULT_FADE_DELAY; self->touch_fade_delay = DEFAULT_TOUCH_FADE_DELAY; - self->auto_inhibit = DEFAULT_AUTO_INHIBIT; /* Ensure private types */ g_type_ensure (CLAPPER_GTK_TYPE_STATUS); @@ -1400,15 +1191,18 @@ static void clapper_gtk_video_constructed (GObject *object) { ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (object); - GstElement *afilter, *vsink; + GstElement *vsink; + ClapperPlayer *player; ClapperQueue *queue; - self->player = clapper_player_new (); - queue = clapper_player_get_queue (self->player); + G_OBJECT_CLASS (parent_class)->constructed (object); - g_signal_connect (self->player, "notify::state", + player = clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self)); + queue = clapper_player_get_queue (player); + + g_signal_connect (player, "notify::state", G_CALLBACK (_player_state_changed_cb), self); - g_signal_connect (self->player, "notify::video-sink", + g_signal_connect (player, "notify::video-sink", G_CALLBACK (_video_sink_changed_cb), self); vsink = gst_element_factory_make ("clappersink", NULL); @@ -1428,28 +1222,23 @@ clapper_gtk_video_constructed (GObject *object) } } - clapper_player_set_video_sink (self->player, vsink); + clapper_player_set_video_sink (player, vsink); } - afilter = gst_element_factory_make ("scaletempo", NULL); - if (G_LIKELY (afilter != NULL)) - clapper_player_set_audio_filter (self->player, afilter); - - g_signal_connect (self->player, "error", + g_signal_connect (player, "error", G_CALLBACK (_player_error_cb), self); - g_signal_connect (self->player, "missing-plugin", + g_signal_connect (player, "missing-plugin", G_CALLBACK (_player_missing_plugin_cb), self); g_signal_connect (queue, "notify::current-item", G_CALLBACK (_queue_current_item_changed_cb), self); - - G_OBJECT_CLASS (parent_class)->constructed (object); } static void clapper_gtk_video_dispose (GObject *object) { ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (object); + ClapperPlayer *player; if (self->notify_revealed_id != 0) { GtkRevealer *revealer = GTK_REVEALER (g_ptr_array_index (self->fading_overlays, 0)); @@ -1460,18 +1249,20 @@ clapper_gtk_video_dispose (GObject *object) g_clear_handle_id (&self->fade_timeout, g_source_remove); + player = clapper_gtk_av_get_player (CLAPPER_GTK_AV_CAST (self)); + /* Something else might still be holding a reference on the player, * thus we should disconnect everything before disposing template */ - if (self->player) { - ClapperQueue *queue = clapper_player_get_queue (self->player); + if (player) { // NULL if dispose run multiple times + ClapperQueue *queue = clapper_player_get_queue (player); - g_signal_handlers_disconnect_by_func (self->player, + g_signal_handlers_disconnect_by_func (player, _player_state_changed_cb, self); - g_signal_handlers_disconnect_by_func (self->player, + g_signal_handlers_disconnect_by_func (player, _video_sink_changed_cb, self); - g_signal_handlers_disconnect_by_func (self->player, + g_signal_handlers_disconnect_by_func (player, _player_error_cb, self); - g_signal_handlers_disconnect_by_func (self->player, + g_signal_handlers_disconnect_by_func (player, _player_missing_plugin_cb, self); g_signal_handlers_disconnect_by_func (queue, @@ -1481,7 +1272,6 @@ clapper_gtk_video_dispose (GObject *object) gtk_widget_dispose_template (GTK_WIDGET (object), CLAPPER_GTK_TYPE_VIDEO); g_clear_pointer (&self->overlay, gtk_widget_unparent); - gst_clear_object (&self->player); G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -1504,21 +1294,12 @@ clapper_gtk_video_get_property (GObject *object, guint prop_id, ClapperGtkVideo *self = CLAPPER_GTK_VIDEO_CAST (object); switch (prop_id) { - case PROP_PLAYER: - g_value_set_object (value, clapper_gtk_video_get_player (self)); - break; case PROP_FADE_DELAY: g_value_set_uint (value, clapper_gtk_video_get_fade_delay (self)); break; case PROP_TOUCH_FADE_DELAY: g_value_set_uint (value, clapper_gtk_video_get_touch_fade_delay (self)); break; - case PROP_AUTO_INHIBIT: - g_value_set_boolean (value, clapper_gtk_video_get_auto_inhibit (self)); - break; - case PROP_INHIBITED: - g_value_set_boolean (value, clapper_gtk_video_get_inhibited (self)); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1538,9 +1319,6 @@ clapper_gtk_video_set_property (GObject *object, guint prop_id, case PROP_TOUCH_FADE_DELAY: clapper_gtk_video_set_touch_fade_delay (self, g_value_get_uint (value)); break; - case PROP_AUTO_INHIBIT: - clapper_gtk_video_set_auto_inhibit (self, g_value_get_boolean (value)); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1565,15 +1343,6 @@ clapper_gtk_video_class_init (ClapperGtkVideoClass *klass) gobject_class->dispose = clapper_gtk_video_dispose; gobject_class->finalize = clapper_gtk_video_finalize; - /** - * ClapperGtkVideo:player: - * - * A #ClapperPlayer used by video. - */ - param_specs[PROP_PLAYER] = g_param_spec_object ("player", - NULL, NULL, CLAPPER_TYPE_PLAYER, - G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); - /** * ClapperGtkVideo:fade-delay: * @@ -1593,24 +1362,6 @@ clapper_gtk_video_class_init (ClapperGtkVideoClass *klass) NULL, NULL, 1, G_MAXUINT, DEFAULT_TOUCH_FADE_DELAY, G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); - /** - * ClapperGtkVideo:auto-inhibit: - * - * Try to automatically inhibit session when video is playing. - */ - param_specs[PROP_AUTO_INHIBIT] = g_param_spec_boolean ("auto-inhibit", - NULL, NULL, DEFAULT_AUTO_INHIBIT, - G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); - - /** - * ClapperGtkVideo:inhibited: - * - * Get whether session is currently inhibited by the video. - */ - param_specs[PROP_INHIBITED] = g_param_spec_boolean ("inhibited", - NULL, NULL, FALSE, - G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); - /** * ClapperGtkVideo::toggle-fullscreen: * @video: a #ClapperGtkVideo @@ -1642,6 +1393,8 @@ clapper_gtk_video_class_init (ClapperGtkVideoClass *klass) g_object_class_install_properties (gobject_class, PROP_LAST, param_specs); + /* FIXME: 1.0: Remove these actions, since they were moved to + * base class AV widget, but are here for compat reasons. */ gtk_widget_class_install_action (widget_class, "video.toggle-play", NULL, toggle_play_action_cb); gtk_widget_class_install_action (widget_class, "video.play", NULL, play_action_cb); gtk_widget_class_install_action (widget_class, "video.pause", NULL, pause_action_cb); diff --git a/src/lib/clapper-gtk/clapper-gtk-video.h b/src/lib/clapper-gtk/clapper-gtk-video.h index 3c0ce6fb..157f9ca3 100644 --- a/src/lib/clapper-gtk/clapper-gtk-video.h +++ b/src/lib/clapper-gtk/clapper-gtk-video.h @@ -27,6 +27,7 @@ #include #include +#include #include G_BEGIN_DECLS @@ -35,7 +36,7 @@ G_BEGIN_DECLS #define CLAPPER_GTK_VIDEO_CAST(obj) ((ClapperGtkVideo *)(obj)) CLAPPER_GTK_API -G_DECLARE_FINAL_TYPE (ClapperGtkVideo, clapper_gtk_video, CLAPPER_GTK, VIDEO, GtkWidget) +G_DECLARE_FINAL_TYPE (ClapperGtkVideo, clapper_gtk_video, CLAPPER_GTK, VIDEO, ClapperGtkAv) CLAPPER_GTK_API GtkWidget * clapper_gtk_video_new (void); @@ -46,7 +47,7 @@ void clapper_gtk_video_add_overlay (ClapperGtkVideo *video, GtkWidget *widget); CLAPPER_GTK_API void clapper_gtk_video_add_fading_overlay (ClapperGtkVideo *video, GtkWidget *widget); -CLAPPER_GTK_API +CLAPPER_GTK_DEPRECATED_FOR(clapper_gtk_av_get_player) ClapperPlayer * clapper_gtk_video_get_player (ClapperGtkVideo *video); CLAPPER_GTK_API @@ -61,13 +62,13 @@ void clapper_gtk_video_set_touch_fade_delay (ClapperGtkVideo *video, guint delay CLAPPER_GTK_API guint clapper_gtk_video_get_touch_fade_delay (ClapperGtkVideo *video); -CLAPPER_GTK_API +CLAPPER_GTK_DEPRECATED_FOR(clapper_gtk_av_set_auto_inhibit) void clapper_gtk_video_set_auto_inhibit (ClapperGtkVideo *video, gboolean inhibit); -CLAPPER_GTK_API +CLAPPER_GTK_DEPRECATED_FOR(clapper_gtk_av_get_auto_inhibit) gboolean clapper_gtk_video_get_auto_inhibit (ClapperGtkVideo *video); -CLAPPER_GTK_API +CLAPPER_GTK_DEPRECATED_FOR(clapper_gtk_av_get_inhibited) gboolean clapper_gtk_video_get_inhibited (ClapperGtkVideo *video); G_END_DECLS diff --git a/src/lib/clapper-gtk/clapper-gtk.h b/src/lib/clapper-gtk/clapper-gtk.h index dace6487..c22e67f8 100644 --- a/src/lib/clapper-gtk/clapper-gtk.h +++ b/src/lib/clapper-gtk/clapper-gtk.h @@ -23,6 +23,8 @@ #include #include +#include +#include #include #include #include diff --git a/src/lib/clapper-gtk/meson.build b/src/lib/clapper-gtk/meson.build index 026422dc..1cc234c9 100644 --- a/src/lib/clapper-gtk/meson.build +++ b/src/lib/clapper-gtk/meson.build @@ -90,6 +90,8 @@ clappergtk_conf_inc = [ clappergtk_headers = [ 'clapper-gtk.h', + 'clapper-gtk-audio.h', + 'clapper-gtk-av.h', 'clapper-gtk-enums.h', 'clapper-gtk-billboard.h', 'clapper-gtk-container.h', @@ -109,6 +111,8 @@ clappergtk_headers = [ clappergtk_visibility_header, ] clappergtk_sources = [ + 'clapper-gtk-audio.c', + 'clapper-gtk-av.c', 'clapper-gtk-billboard.c', 'clapper-gtk-buffering-animation.c', 'clapper-gtk-buffering-paintable.c', diff --git a/src/lib/clapper-gtk/ui/clapper-gtk-video.ui b/src/lib/clapper-gtk/ui/clapper-gtk-video.ui index 8ee8db7d..ddd2dcf7 100644 --- a/src/lib/clapper-gtk/ui/clapper-gtk-video.ui +++ b/src/lib/clapper-gtk/ui/clapper-gtk-video.ui @@ -1,6 +1,6 @@ -