/**
* @file
* Simple \e GtkVlcPlayer widget for playing media files via libVLC.
* It supports fullscreen mode, a simple API, emitting signals on position and
* length changes, as well as \e GtkAdjustment support for connecting the player
* with scales and other widgets easily.
*/
/*
* Copyright (C) 2012 Otto-von-Guericke-Universität Magdeburg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include
#ifdef HAVE_WINDOWS_H
#include
#include
#endif
#include
#include
#include
#ifdef G_OS_WIN32
#include
#else
#include
#endif
#include
#include
#include "cclosure-marshallers.h"
#include "gtk-vlc-player.h"
static void gtk_vlc_player_class_init(GtkVlcPlayerClass *klass);
static inline libvlc_instance_t *create_vlc_instance(void);
static void gtk_vlc_player_init(GtkVlcPlayer *klass);
static void gtk_vlc_player_dispose(GObject *gobject);
static void gtk_vlc_player_finalize(GObject *gobject);
#ifdef G_OS_WIN32
static BOOL CALLBACK enumerate_vlc_windows_cb(HWND hWndvlc, LPARAM lParam);
static gboolean poll_vlc_event_window_cb(gpointer data);
#endif
static void widget_on_realize(GtkWidget *widget, gpointer data);
static gboolean widget_on_click(GtkWidget *widget, GdkEventButton *event,
gpointer data);
static void time_adj_on_value_changed(GtkAdjustment *adj, gpointer user_data);
static void time_adj_on_changed(GtkAdjustment *adj, gpointer user_data);
static void vol_adj_on_value_changed(GtkAdjustment *adj, gpointer user_data);
static inline void set_transient_toplevel_window(GtkWindow *target,
GtkWidget *widget);
static void update_time(GtkVlcPlayer *player, gint64 new_time);
static void update_length(GtkVlcPlayer *player, gint64 new_length);
static void vlc_time_changed(const struct libvlc_event_t *event,
void *userdata);
static void vlc_length_changed(const struct libvlc_event_t *event,
void *userdata);
static void vlc_player_load_media(GtkVlcPlayer *player, libvlc_media_t *media);
/** @private */
#define POLL_VLC_EVENT_WINDOW_INTERVAL 100 /* milliseconds */
/** @private */
#define GOBJECT_UNREF_SAFE(VAR) G_STMT_START { \
if ((VAR) != NULL) { \
g_object_unref(VAR); \
VAR = NULL; \
} \
} G_STMT_END
/** @private */
#define GTK_VLC_PLAYER_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE((obj), GTK_TYPE_VLC_PLAYER, GtkVlcPlayerPrivate))
/** @private */
struct _GtkVlcPlayerPrivate {
GtkObject *time_adjustment;
gulong time_adj_on_value_changed_id;
gulong time_adj_on_changed_id;
GtkObject *volume_adjustment;
gulong vol_adj_on_value_changed_id;
libvlc_instance_t *vlc_inst;
libvlc_media_player_t *media_player;
gboolean isFullscreen;
GtkWidget *fullscreen_window;
};
/** @private */
enum {
TIME_CHANGED_SIGNAL,
LENGTH_CHANGED_SIGNAL,
LAST_SIGNAL
};
static guint gtk_vlc_player_signals[LAST_SIGNAL] = {0, 0};
/**
* @private
* Will create \e gtk_vlc_player_get_type and set
* \e gtk_vlc_player_parent_class
*/
G_DEFINE_TYPE(GtkVlcPlayer, gtk_vlc_player, GTK_TYPE_ALIGNMENT);
static void
gtk_vlc_player_class_init(GtkVlcPlayerClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
gobject_class->dispose = gtk_vlc_player_dispose;
gobject_class->finalize = gtk_vlc_player_finalize;
gtk_vlc_player_signals[TIME_CHANGED_SIGNAL] =
g_signal_new("time-changed",
G_TYPE_FROM_CLASS(klass),
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET(GtkVlcPlayerClass, time_changed),
NULL, NULL,
gtk_vlc_player_marshal_VOID__INT64,
G_TYPE_NONE, 1, G_TYPE_INT64);
gtk_vlc_player_signals[LENGTH_CHANGED_SIGNAL] =
g_signal_new("length-changed",
G_TYPE_FROM_CLASS(klass),
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET(GtkVlcPlayerClass, length_changed),
NULL, NULL,
gtk_vlc_player_marshal_VOID__INT64,
G_TYPE_NONE, 1, G_TYPE_INT64);
g_type_class_add_private(klass, sizeof(GtkVlcPlayerPrivate));
}
static inline libvlc_instance_t *
create_vlc_instance(void)
{
gchar **vlc_argv;
gint vlc_argc = 0;
libvlc_instance_t *ret;
vlc_argv = g_malloc_n(2, sizeof(vlc_argv[0]));
vlc_argv[vlc_argc++] = g_strdup(g_get_prgname());
#if LIBVLC_VERSION_INT < LIBVLC_VERSION(2,0,0,0)
if (g_getenv("VLC_PLUGIN_PATH") != NULL) {
vlc_argv = g_realloc_n(vlc_argv,
vlc_argc + 2 + 1, sizeof(vlc_argv[0]));
vlc_argv[vlc_argc++] = g_strdup("--plugin-path");
vlc_argv[vlc_argc++] = g_strdup(g_getenv("VLC_PLUGIN_PATH"));
}
#endif
vlc_argv[vlc_argc] = NULL;
ret = libvlc_new((int)vlc_argc, (const char *const *)vlc_argv);
g_strfreev(vlc_argv);
return ret;
}
static void
gtk_vlc_player_init(GtkVlcPlayer *klass)
{
GtkWidget *drawing_area;
GdkColor color;
libvlc_event_manager_t *evman;
klass->priv = GTK_VLC_PLAYER_GET_PRIVATE(klass);
gtk_alignment_set(GTK_ALIGNMENT(klass), 0., 0., 1., 1.);
drawing_area = gtk_drawing_area_new();
gdk_color_parse("black", &color);
gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &color);
g_signal_connect(G_OBJECT(drawing_area), "realize",
G_CALLBACK(widget_on_realize), klass);
gtk_widget_add_events(drawing_area, GDK_BUTTON_PRESS_MASK);
g_signal_connect(G_OBJECT(drawing_area), "button-press-event",
G_CALLBACK(widget_on_click), klass);
gtk_container_add(GTK_CONTAINER(klass), drawing_area);
gtk_widget_show(drawing_area);
/*
* drawing area will be destroyed automatically with the
* GtkContainer/GtkVlcPlayer
* (it is derived from GtkObject and has one reference after adding it
* to the container).
*/
klass->priv->time_adjustment = gtk_adjustment_new(0., 0., 0.,
GTK_VLC_PLAYER_TIME_ADJ_STEP,
GTK_VLC_PLAYER_TIME_ADJ_PAGE,
0.);
g_object_ref_sink(klass->priv->time_adjustment);
klass->priv->time_adj_on_value_changed_id =
g_signal_connect(G_OBJECT(klass->priv->time_adjustment),
"value-changed",
G_CALLBACK(time_adj_on_value_changed), klass);
klass->priv->time_adj_on_changed_id =
g_signal_connect(G_OBJECT(klass->priv->time_adjustment),
"changed",
G_CALLBACK(time_adj_on_changed), klass);
klass->priv->volume_adjustment = gtk_adjustment_new(1., 0., 1.,
GTK_VLC_PLAYER_VOL_ADJ_STEP,
GTK_VLC_PLAYER_VOL_ADJ_PAGE,
0.);
g_object_ref_sink(klass->priv->volume_adjustment);
klass->priv->vol_adj_on_value_changed_id =
g_signal_connect(G_OBJECT(klass->priv->volume_adjustment),
"value-changed",
G_CALLBACK(vol_adj_on_value_changed), klass);
klass->priv->vlc_inst = create_vlc_instance();
klass->priv->media_player = libvlc_media_player_new(klass->priv->vlc_inst);
/* sign up for time updates */
evman = libvlc_media_player_event_manager(klass->priv->media_player);
libvlc_event_attach(evman, libvlc_MediaPlayerTimeChanged,
vlc_time_changed, klass);
libvlc_event_attach(evman, libvlc_MediaPlayerLengthChanged,
vlc_length_changed, klass);
klass->priv->isFullscreen = FALSE;
klass->priv->fullscreen_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_object_ref_sink(klass->priv->fullscreen_window);
gtk_window_set_deletable(GTK_WINDOW(klass->priv->fullscreen_window),
FALSE);
gtk_window_set_decorated(GTK_WINDOW(klass->priv->fullscreen_window),
FALSE);
gtk_window_set_skip_taskbar_hint(GTK_WINDOW(klass->priv->fullscreen_window),
TRUE);
gtk_window_set_skip_pager_hint(GTK_WINDOW(klass->priv->fullscreen_window),
TRUE);
gtk_window_set_keep_above(GTK_WINDOW(klass->priv->fullscreen_window),
TRUE);
}
static void
gtk_vlc_player_dispose(GObject *gobject)
{
GtkVlcPlayer *player = GTK_VLC_PLAYER(gobject);
/*
* destroy might be called more than once, but we have only one
* reference for each object
*/
if (player->priv->time_adjustment != NULL) {
g_signal_handler_disconnect(G_OBJECT(player->priv->time_adjustment),
player->priv->time_adj_on_changed_id);
g_signal_handler_disconnect(G_OBJECT(player->priv->time_adjustment),
player->priv->time_adj_on_value_changed_id);
g_object_unref(player->priv->time_adjustment);
player->priv->time_adjustment = NULL;
}
if (player->priv->volume_adjustment != NULL) {
g_signal_handler_disconnect(G_OBJECT(player->priv->volume_adjustment),
player->priv->vol_adj_on_value_changed_id);
g_object_unref(player->priv->volume_adjustment);
player->priv->volume_adjustment = NULL;
}
GOBJECT_UNREF_SAFE(player->priv->fullscreen_window);
/* Chain up to the parent class */
G_OBJECT_CLASS(gtk_vlc_player_parent_class)->dispose(gobject);
}
static void
gtk_vlc_player_finalize(GObject *gobject)
{
GtkVlcPlayer *player = GTK_VLC_PLAYER(gobject);
libvlc_media_player_release(player->priv->media_player);
libvlc_release(player->priv->vlc_inst);
/* Chain up to the parent class */
G_OBJECT_CLASS(gtk_vlc_player_parent_class)->finalize(gobject);
}
#ifdef G_OS_WIN32
static BOOL CALLBACK
enumerate_vlc_windows_cb(HWND hWndvlc, LPARAM lParam)
{
EnableWindow(hWndvlc, FALSE);
*(gboolean *)lParam = FALSE;
return TRUE;
}
/**
* @brief Callback for polling the availability of a libVLC event window.
*
* If found, it is disabled and the polling stops. This results in mouse click
* events being delivered to the GtkDrawingArea widget.
*
* @param data User data (\e GtkVlcPlayer widget)
* @return \c TRUE to continue polling, \c FALSE to stop
*/
static gboolean
poll_vlc_event_window_cb(gpointer data)
{
GtkWidget *drawing_area = gtk_bin_get_child(GTK_BIN(data));
GdkWindow *window = gtk_widget_get_window(drawing_area);
gboolean ret = TRUE;
EnumChildWindows(GDK_WINDOW_HWND(window),
enumerate_vlc_windows_cb, (LPARAM)&ret);
return ret;
}
static void
widget_on_realize(GtkWidget *widget, gpointer user_data)
{
GtkVlcPlayer *player = GTK_VLC_PLAYER(user_data);
GdkWindow *window = gtk_widget_get_window(widget);
libvlc_media_player_set_hwnd(player->priv->media_player,
GDK_WINDOW_HWND(window));
}
#else
static void
widget_on_realize(GtkWidget *widget, gpointer user_data)
{
GtkVlcPlayer *player = GTK_VLC_PLAYER(user_data);
GdkWindow *window = gtk_widget_get_window(widget);
libvlc_media_player_set_xwindow(player->priv->media_player,
GDK_WINDOW_XID(window));
}
#endif
static gboolean
widget_on_click(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
{
GtkVlcPlayer *player = GTK_VLC_PLAYER(user_data);
GtkWidget *fullscreen_window = player->priv->fullscreen_window;
if (fullscreen_window == NULL)
return TRUE;
if (event->type != GDK_2BUTTON_PRESS || event->button != 1)
return TRUE;
if (player->priv->isFullscreen) {
gtk_widget_reparent(widget, GTK_WIDGET(player));
gtk_widget_show(widget);
gtk_window_unfullscreen(GTK_WINDOW(fullscreen_window));
gtk_widget_hide(fullscreen_window);
player->priv->isFullscreen = FALSE;
} else {
set_transient_toplevel_window(GTK_WINDOW(fullscreen_window),
GTK_WIDGET(player));
gtk_window_fullscreen(GTK_WINDOW(fullscreen_window));
gtk_widget_show(fullscreen_window);
gtk_widget_reparent(widget, fullscreen_window);
gtk_widget_show(widget);
player->priv->isFullscreen = TRUE;
}
return TRUE;
}
static void
time_adj_on_value_changed(GtkAdjustment *adj, gpointer user_data)
{
gtk_vlc_player_seek(GTK_VLC_PLAYER(user_data),
(gint64)gtk_adjustment_get_value(adj));
}
static void
time_adj_on_changed(GtkAdjustment *adj, gpointer user_data)
{
GtkVlcPlayer *player = GTK_VLC_PLAYER(user_data);
/* avoid signal handler recursion */
g_signal_handler_block(G_OBJECT(adj),
player->priv->time_adj_on_changed_id);
gtk_adjustment_set_upper(adj, (gdouble)gtk_vlc_player_get_length(player) +
gtk_adjustment_get_page_size(adj));
g_signal_handler_unblock(G_OBJECT(adj),
player->priv->time_adj_on_changed_id);
}
static void
vol_adj_on_value_changed(GtkAdjustment *adj, gpointer user_data)
{
gtk_vlc_player_set_volume(GTK_VLC_PLAYER(user_data),
gtk_adjustment_get_value(adj));
}
static inline void
set_transient_toplevel_window(GtkWindow *target, GtkWidget *widget)
{
GtkWidget *toplevel = gtk_widget_get_toplevel(widget);
if (gtk_widget_is_toplevel(toplevel) && GTK_IS_WINDOW(toplevel))
gtk_window_set_transient_for(target, GTK_WINDOW(toplevel));
}
static void
update_time(GtkVlcPlayer *player, gint64 new_time)
{
g_signal_emit(player, gtk_vlc_player_signals[TIME_CHANGED_SIGNAL], 0,
new_time);
if (player->priv->time_adjustment != NULL) {
/* ensure that time_adj_on_value_changed() will not be executed */
g_signal_handler_block(G_OBJECT(player->priv->time_adjustment),
player->priv->time_adj_on_value_changed_id);
gtk_adjustment_set_value(GTK_ADJUSTMENT(player->priv->time_adjustment),
(gdouble)new_time);
g_signal_handler_unblock(G_OBJECT(player->priv->time_adjustment),
player->priv->time_adj_on_value_changed_id);
}
}
static void
update_length(GtkVlcPlayer *player, gint64 new_length)
{
g_signal_emit(player, gtk_vlc_player_signals[LENGTH_CHANGED_SIGNAL], 0,
new_length);
if (player->priv->time_adjustment != NULL) {
GtkAdjustment *adj = GTK_ADJUSTMENT(player->priv->time_adjustment);
/* ensure that time_adj_on_changed() will not be executed */
g_signal_handler_block(G_OBJECT(adj),
player->priv->time_adj_on_changed_id);
gtk_adjustment_set_upper(adj, (gdouble)new_length +
gtk_adjustment_get_page_size(adj));
g_signal_handler_unblock(G_OBJECT(adj),
player->priv->time_adj_on_changed_id);
}
}
static void
vlc_time_changed(const struct libvlc_event_t *event, void *user_data)
{
assert(event->type == libvlc_MediaPlayerTimeChanged);
/* VLC callbacks are invoked from another thread! */
gdk_threads_enter();
update_time(GTK_VLC_PLAYER(user_data),
(gint64)event->u.media_player_time_changed.new_time);
gdk_threads_leave();
}
static void
vlc_length_changed(const struct libvlc_event_t *event, void *user_data)
{
assert(event->type == libvlc_MediaPlayerLengthChanged);
/* VLC callbacks are invoked from another thread! */
gdk_threads_enter();
update_length(GTK_VLC_PLAYER(user_data),
(gint64)event->u.media_player_length_changed.new_length);
gdk_threads_leave();
}
static void
vlc_player_load_media(GtkVlcPlayer *player, libvlc_media_t *media)
{
libvlc_media_parse(media);
libvlc_media_player_set_media(player->priv->media_player, media);
/* NOTE: media was parsed so get_duration works */
update_length(player, (gint64)libvlc_media_get_duration(media));
update_time(player, 0);
}
/*
* API
*/
/**
* @brief Construct new \e GtkVlcPlayer widget instance.
*
* @return New \e GtkVlcPlayer widget instance
*/
GtkWidget *
gtk_vlc_player_new(void)
{
return GTK_WIDGET(g_object_new(GTK_TYPE_VLC_PLAYER, NULL));
}
/**
* @brief Load media with specified filename into player widget
*
* It does not start playing until playback is started or toggled.
* "time-changed" and "length-changed" signals will be emitted immediately
* after successfully loading the media. The time-adjustment will also be
* reconfigured appropriately.
*
* @param player \e GtkVlcPlayer instance to load file into.
* @param file \e Filename to load
* @return \c TRUE on success, else \c FALSE
*/
gboolean
gtk_vlc_player_load_filename(GtkVlcPlayer *player, const gchar *file)
{
libvlc_media_t *media;
media = libvlc_media_new_path(player->priv->vlc_inst,
(const char *)file);
if (media == NULL)
return FALSE;
vlc_player_load_media(player, media);
libvlc_media_release(media);
return TRUE;
}
/**
* @brief Load media with specified URI into player widget
*
* It is otherwise identical to \ref gtk_vlc_player_load_filename.
*
* @sa gtk_vlc_player_load_filename
*
* @param player \e GtkVlcPlayer instance to load media into.
* @param uri \e URI to load
* @return \c TRUE on success, else \c FALSE
*/
gboolean
gtk_vlc_player_load_uri(GtkVlcPlayer *player, const gchar *uri)
{
libvlc_media_t *media;
media = libvlc_media_new_location(player->priv->vlc_inst,
(const char *)uri);
if (media == NULL)
return FALSE;
vlc_player_load_media(player, media);
libvlc_media_release(media);
return TRUE;
}
/**
* @brief Play back media if playback is currently paused
*
* If it is already playing, do nothing.
* In playback mode, there will be constant "time-changed" signal emissions
* and the time-adjustment's value will be set accordingly.
*
* @param player \e GtkVlcPlayer instance
*/
void
gtk_vlc_player_play(GtkVlcPlayer *player)
{
if (libvlc_media_player_play(player->priv->media_player) < 0)
return;
/*
* Workaround to get mouse click events on the drawing area widget
* that provides the low-level window for libVLC.
* On Win32, libVLC creates an event window (in a different thread)
* after playback start that "swallows" all click events.
* So we have to poll for the availability of that window and disable
* it.
*/
#ifdef G_OS_WIN32
g_timeout_add(POLL_VLC_EVENT_WINDOW_INTERVAL,
poll_vlc_event_window_cb, player);
#endif
}
/**
* @brief Pause media playback if it is currently playing
*
* If it is already paused, do nothing.
*
* @param player \e GtkVlcPlayer instance
*/
void
gtk_vlc_player_pause(GtkVlcPlayer *player)
{
libvlc_media_player_pause(player->priv->media_player);
}
/**
* @brief Toggle media playback
*
* If it is currently playing, pause playback. If it is currently paused,
* start playback.
*
* @sa gtk_vlc_player_play
* @sa gtk_vlc_player_pause
*
* @param player \e GtkVlcPlayer instance
* @return \c TRUE if media is playing \b after the operation,
* \c FALSE if it is paused.
*/
gboolean
gtk_vlc_player_toggle(GtkVlcPlayer *player)
{
if (libvlc_media_player_is_playing(player->priv->media_player))
gtk_vlc_player_pause(player);
else
gtk_vlc_player_play(player);
return (gboolean)libvlc_media_player_is_playing(player->priv->media_player);
}
/**
* @brief Stop media playback
*
* A "time-changed" signal will be emitted and the time-adjustment will be
* reset appropriately.
*
* @param player \e GtkVlcPlayer instance
*/
void
gtk_vlc_player_stop(GtkVlcPlayer *player)
{
gtk_vlc_player_pause(player);
libvlc_media_player_stop(player->priv->media_player);
update_time(player, 0);
}
/**
* @brief Set point of time in playback
*
* @param player \e GtkVlcPlayer instance
* @param time New position in media (milliseconds)
*/
void
gtk_vlc_player_seek(GtkVlcPlayer *player, gint64 time)
{
libvlc_media_player_set_time(player->priv->media_player,
(libvlc_time_t)time);
}
/**
* @brief Set audio volume of playback
*
* @param player \e GtkVlcPlayer instance
* @param volume New volume of playback. It must be between 0.0 (0%) and 2.0 (200%)
* of the original audio volume.
*/
void
gtk_vlc_player_set_volume(GtkVlcPlayer *player, gdouble volume)
{
libvlc_audio_set_volume(player->priv->media_player, (int)(volume*100.));
}
/**
* @brief Get media length
*
* @param player \e GtkVlcPlayer instance
* @return Length of media (in milliseconds)
*/
gint64
gtk_vlc_player_get_length(GtkVlcPlayer *player)
{
return (gint64)libvlc_media_player_get_length(player->priv->media_player);
}
/**
* @brief Get time-adjustment currently used by \e GtkVlcPlayer
*
* The time-adjustment is an alternative to signal-callbacks and using the API
* for synchronizing the \e GtkVlcPlayer widget's current playback position with
* another widget (e.g. a \e GtkScale).
* The adjustment's value is in milliseconds.
* The widget will initialize one on construction, so there \e should always be
* an adjustment to get.
*
* @sa gtk_vlc_player_seek
* @sa GtkVlcPlayerClass::time_changed GtkVlcPlayerClass::length_changed
*
* @param player \e GtkVlcPlayer instance
* @return Currently used time-adjustment
*/
GtkAdjustment *
gtk_vlc_player_get_time_adjustment(GtkVlcPlayer *player)
{
return player->priv->time_adjustment != NULL
? GTK_ADJUSTMENT(player->priv->time_adjustment)
: NULL;
}
/**
* @brief Change time-adjustment used by \e GtkVlcPlayer
*
* The old adjustment will be
* unreferenced (and possibly destroyed) and a reference to the new
* adjustment will be fetched.
*
* @sa gtk_vlc_player_get_time_adjustment
*
* @param player \e GtkVlcPlayer instance
* @param adj New \e GtkAdjustment to use as time-adjustment.
*/
void
gtk_vlc_player_set_time_adjustment(GtkVlcPlayer *player, GtkAdjustment *adj)
{
if (player->priv->time_adjustment == NULL)
return;
g_signal_handler_disconnect(G_OBJECT(player->priv->time_adjustment),
player->priv->time_adj_on_changed_id);
g_signal_handler_disconnect(G_OBJECT(player->priv->time_adjustment),
player->priv->time_adj_on_value_changed_id);
g_object_unref(player->priv->time_adjustment);
player->priv->time_adjustment = GTK_OBJECT(adj);
g_object_ref_sink(player->priv->time_adjustment);
/*
* NOTE: not setting value and upper properly, as well as setting the
* page-size might cause problems if adjustment is set after loading/playing
* a media or setting it on other widgets
*/
gtk_adjustment_configure(GTK_ADJUSTMENT(player->priv->time_adjustment),
0., 0., 0.,
GTK_VLC_PLAYER_TIME_ADJ_STEP,
GTK_VLC_PLAYER_TIME_ADJ_PAGE,
0.);
player->priv->time_adj_on_value_changed_id =
g_signal_connect(G_OBJECT(player->priv->time_adjustment),
"value-changed",
G_CALLBACK(time_adj_on_value_changed), player);
player->priv->time_adj_on_changed_id =
g_signal_connect(G_OBJECT(player->priv->time_adjustment),
"changed",
G_CALLBACK(time_adj_on_changed), player);
}
/**
* @brief Get volume-adjustment currently used by \e GtkVlcPlayer
*
* The volume-adjustment is an alternative to signal-callbacks and using the
* API for synchronizing the \e GtkVlcPlayer widget's current playback volume
* with another widget (e.g. a \e GtkVolumeButton).
* The adjustment's value is a percentage of gain to apply to the original
* signal (0.0 is 0%, 2.0 is 200%).
* The widget will initialize a volume-adjustment on construction, so there will
* always be an adjustment to get.
* In contrast to the time-adjustment, the volume-adjustment's value will never
* be changed by the \e GtkVlcPlayer widget.
*
* @sa gtk_vlc_player_set_volume
*
* @param player \e GtkVlcPlayer instance
* @return Currently used volume-adjustment
*/
GtkAdjustment *
gtk_vlc_player_get_volume_adjustment(GtkVlcPlayer *player)
{
return player->priv->volume_adjustment != NULL
? GTK_ADJUSTMENT(player->priv->volume_adjustment)
: NULL;
}
/**
* @brief Change volume-adjustment used by \e GtkVlcPlayer
*
* The old adjustment will be unreferenced (and possibly destroyed) and a
* reference to the new adjustment will be fetched.
*
* @sa gtk_vlc_player_get_volume_adjustment
*
* @param player \e GtkVlcPlayer instance
* @param adj New \e GtkAdjustment to use as volume-adjustment.
*/
void
gtk_vlc_player_set_volume_adjustment(GtkVlcPlayer *player, GtkAdjustment *adj)
{
if (player->priv->volume_adjustment == NULL)
return;
g_signal_handler_disconnect(G_OBJECT(player->priv->volume_adjustment),
player->priv->vol_adj_on_value_changed_id);
g_object_unref(player->priv->volume_adjustment);
player->priv->volume_adjustment = GTK_OBJECT(adj);
g_object_ref_sink(player->priv->volume_adjustment);
gtk_adjustment_configure(GTK_ADJUSTMENT(player->priv->volume_adjustment),
1., 0., 1.,
GTK_VLC_PLAYER_VOL_ADJ_STEP,
GTK_VLC_PLAYER_VOL_ADJ_PAGE,
0.);
player->priv->vol_adj_on_value_changed_id =
g_signal_connect(G_OBJECT(player->priv->volume_adjustment),
"value-changed",
G_CALLBACK(vol_adj_on_value_changed), player);
}