Desktop
/* $Id: player.c,v 1.22 2010/10/07 21:20:41 khorben Exp $ */
/* Copyright (c) 2010 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Desktop Player */
/* 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, version 3 of the License.
*
* 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 <http://www.gnu.org/licenses/>. */
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <libgen.h>
#include <errno.h>
#include <libintl.h>
#include <gdk/gdkkeysyms.h>
#include "callbacks.h"
#include "../config.h"
#include "player.h"
#define _(string) gettext(string)
#define N_(string) (string)
/* types */
#ifndef EMBEDDED
struct _menu
{
char * name;
GtkSignalFunc callback;
char * stock;
unsigned int accel;
};
struct _menubar
{
char * name;
struct _menu * menu;
};
#endif
/* constants */
#ifndef EMBEDDED
struct _menu _menu_file[] =
{
{ N_("_Open..."), G_CALLBACK(on_file_open), GTK_STOCK_OPEN, GDK_O },
{ "", NULL, NULL, 0 },
{ N_("_Properties"), G_CALLBACK(on_file_properties),
GTK_STOCK_PROPERTIES, 0 },
{ "", NULL, NULL, 0 },
{ N_("_Close"), G_CALLBACK(on_file_close), GTK_STOCK_CLOSE, GDK_W },
{ NULL, NULL, NULL, 0 }
};
struct _menu _menu_edit[] =
{
{ N_("_Preferences"), G_CALLBACK(on_edit_preferences),
GTK_STOCK_PREFERENCES, GDK_P },
{ NULL, NULL, NULL, 0 }
};
struct _menu _menu_view[] =
{
{ N_("_Playlist"), G_CALLBACK(on_view_playlist), NULL, GDK_L },
# if GTK_CHECK_VERSION(2, 8, 0)
{ N_("_Fullscreen"), G_CALLBACK(on_view_fullscreen),
GTK_STOCK_FULLSCREEN,
# else
{ N_("_Fullscreen"), G_CALLBACK(on_view_fullscreen), NULL,
# endif
GDK_F },
{ NULL, NULL, NULL, 0 }
};
static struct _menu _menu_help[] =
{
# if GTK_CHECK_VERSION(2, 6, 0)
{ N_("_About"), G_CALLBACK(on_help_about), GTK_STOCK_ABOUT, 0 },
# else
{ N_("_About"), G_CALLBACK(on_help_about), NULL, 0 },
# endif
{ NULL, NULL, NULL, 0 }
};
static struct _menubar _menubar[] =
{
{ N_("_File"), _menu_file },
{ N_("_Edit"), _menu_edit },
{ N_("_View"), _menu_view },
{ N_("_Help"), _menu_help },
{ NULL, NULL }
};
#endif
/* Player */
/* private */
/* prototypes */
/* accessors */
static void _player_set_progress(Player * player, unsigned int progress);
/* useful */
static int _player_error(char const * message, int ret);
static void _player_reset(Player * player);
/* callbacks */
static gboolean _command_read(GIOChannel * source, GIOCondition condition,
gpointer data);
static gboolean _command_timeout(gpointer data);
static gboolean _command_write(GIOChannel * source, GIOCondition condition,
gpointer data);
/* functions */
/* accessors */
static void _player_set_progress(Player * player, unsigned int progress)
{
gdouble fraction;
static char buf[16];
fraction = progress <= 100 ? progress : 100;
fraction /= 100;
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(player->progress),
fraction);
snprintf(buf, sizeof(buf), "%d%%", progress);
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(player->progress), buf);
}
/* useful */
/* player_error */
static int _player_error(char const * message, int ret)
{
fputs("player: ", stderr);
perror(message);
return ret;
}
/* player_reset */
static void _player_reset(Player * player)
{
if(player->filename != NULL)
free(player->filename);
player->filename = NULL;
player->width = 0;
player->height = 0;
player->audio_bitrate = 0;
player->audio_channels = 0;
if(player->audio_codec != NULL)
free(player->audio_codec);
player->audio_codec = NULL;
player->audio_rate = 0;
player->video_aspect = 0.0;
player->video_bitrate = 0;
if(player->video_codec != NULL)
free(player->video_codec);
player->video_codec = NULL;
player->video_fps = 0.0;
player->video_rate = 0;
_player_set_progress(player, 0);
}
/* player_command */
static int _player_command(Player * player, char const * cmd, size_t cmd_len)
{
char * p;
if(player->pid == -1)
{
fputs(PACKAGE ": mplayer not running\n", stderr);
return 1;
}
#ifdef DEBUG
fprintf(stderr, "%s%d%s\"%s\"\n", "DEBUG: pid ", player->pid,
": write ", cmd);
#endif
if((p = realloc(player->buf, player->buf_len + cmd_len)) == NULL)
return _player_error("malloc", 1);
player->buf = p;
memcpy(&p[player->buf_len], cmd, cmd_len);
player->buf_len += cmd_len;
g_io_add_watch(player->channel[1], G_IO_OUT, _command_write, player);
return 0;
}
/* callbacks */
/* command_read */
static void _read_parse(Player * player, char const * buf);
static gboolean _command_read(GIOChannel * source, GIOCondition condition,
gpointer data)
{
Player * player = data;
static char buf[512];
static size_t buf_len = 0;
gsize read;
size_t i;
size_t j;
if(condition != G_IO_IN)
{
player_error(player, "", 0); /* FIXME */
gtk_main_quit();
return FALSE; /* FIXME report error */
}
if(g_io_channel_read(source, &buf[buf_len], sizeof(buf) - buf_len,
&read) != G_IO_ERROR_NONE)
{
player_error(player, "", 0); /* FIXME */
gtk_main_quit();
return FALSE; /* FIXME report error */
}
if(read == 0)
{
player->read_id = 0;
return FALSE; /* FIXME end of file? */
}
buf_len += read;
j = 0;
for(i = 0; i < buf_len; i++)
{
if(buf[i] != '\n')
continue;
buf[i] = '\0';
_read_parse(player, &buf[j]);
j = i + 1;
}
buf_len -= j;
memmove(buf, &buf[j], buf_len);
return TRUE;
}
static void _read_parse(Player * player, char const * buf)
{
unsigned int u32;
gdouble db;
char str[256];
if(sscanf(buf, "ANS_PERCENT_POSITION=%u\n", &u32) == 1)
_player_set_progress(player, u32);
else if(sscanf(buf, "ID_AUDIO_BITRATE=%u\n", &u32) == 1)
player->audio_bitrate = u32;
else if(sscanf(buf, "ID_AUDIO_CODEC=%255s", str) == 1)
{
if(player->audio_codec != NULL)
free(player->audio_codec);
player->audio_codec = strdup(str);
}
else if(sscanf(buf, "ID_AUDIO_NCH=%u\n", &u32) == 1)
player->audio_channels = u32;
else if(sscanf(buf, "ID_AUDIO_RATE=%u\n", &u32) == 1)
player->audio_rate = u32;
else if(sscanf(buf, "ID_LENGTH=%lf\n", &db) == 1)
player->length = db;
else if(sscanf(buf, "ID_VIDEO_ASPECT=%lf\n", &db) == 1)
player->video_aspect = db;
else if(sscanf(buf, "ID_VIDEO_BITRATE=%u\n", &u32) == 1)
player->video_bitrate = u32;
else if(sscanf(buf, "ID_VIDEO_CODEC=%255s", str) == 1)
{
if(player->video_codec != NULL)
free(player->video_codec);
player->video_codec = strdup(str);
}
else if(sscanf(buf, "ID_VIDEO_FPS=%lf\n", &db) == 1)
player->video_fps = db;
else if(sscanf(buf, "ID_VIDEO_HEIGHT=%u\n", &u32) == 1)
player_set_size(player, -1, u32);
else if(sscanf(buf, "ID_VIDEO_RATE=%u\n", &u32) == 1)
player->video_rate = u32;
else if(sscanf(buf, "ID_VIDEO_WIDTH=%u\n", &u32) == 1)
player_set_size(player, u32, -1);
#ifdef DEBUG
else
fprintf(stderr, "DEBUG: unknown output \"%s\"\n", buf);
#endif
}
/* command_timeout */
static gboolean _command_timeout(gpointer data)
{
Player * player = data;
static const char cmd[] = "pausing_keep get_percent_pos\n";
_player_command(player, cmd, sizeof(cmd) - 1);
return TRUE;
}
/* command_write */
static gboolean _command_write(GIOChannel * source, GIOCondition condition,
gpointer data)
{
Player * player = data;
gsize written;
char * p;
if(condition != G_IO_OUT)
{
player_error(player, "", 0); /* FIXME */
gtk_main_quit();
return FALSE; /* FIXME report error */
}
if(g_io_channel_write(source, player->buf, player->buf_len, &written)
!= G_IO_ERROR_NONE)
{
player_error(player, "", 0); /* FIXME */
gtk_main_quit();
return FALSE; /* FIXME report error */
}
#ifdef DEBUG
fprintf(stderr, "DEBUG: wrote %zu bytes\n", written);
#endif
player->buf_len -= written;
memmove(player->buf, &player->buf[written], player->buf_len);
if(player->buf_len == 0)
return FALSE;
if((p = realloc(player->buf, player->buf_len)) != NULL)
player->buf = p;
return TRUE;
}
/* public */
/* player_new */
static int _player_error(char const * message, int ret);
#ifndef EMBEDDED
static GtkWidget * _new_menubar(Player * player);
#endif
static void _new_mplayer(Player * player);
static void _new_column_text(GtkWidget * view, char const * title, int id);
#if !GTK_CHECK_VERSION(2, 12, 0)
static void gtk_widget_set_tooltip_text(GtkWidget * widget, const char * text);
#endif
Player * player_new(void)
{
Player * player;
GtkWidget * widget;
GtkWidget * vbox;
GtkWidget * hbox;
GtkWidget * toolbar;
GtkToolItem * toolitem;
if((player = malloc(sizeof(*player))) == NULL)
return NULL;
/* view */
player->paused = 0;
player->fullscreen = 0;
/* current file */
player->filename = NULL;
player->audio_codec = NULL;
player->video_codec = NULL;
/* mplayer */
player->pid = -1;
if(pipe(player->fd[0]) != 0 || pipe(player->fd[1]) != 0)
{
player_error(player, strerror(errno), 0);
free(player);
return NULL;
}
player->buf = NULL;
player->buf_len = 0;
/* callbacks */
player->read_id = 0;
player->timeout_id = 0;
/* widgets */
player->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size(GTK_WINDOW(player->window), 512, 384);
#if GTK_CHECK_VERSION(2, 6, 0)
gtk_window_set_icon_name(GTK_WINDOW(player->window), "multimedia");
#endif
gtk_window_set_title(GTK_WINDOW(player->window), _("Player"));
gtk_widget_realize(player->window);
g_signal_connect(G_OBJECT(player->window), "delete-event", G_CALLBACK(
on_player_closex), player);
vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(player->window), vbox);
#ifndef EMBEDDED
player->menubar = _new_menubar(player);
gtk_box_pack_start(GTK_BOX(vbox), player->menubar, FALSE, FALSE, 0);
#endif
/* view */
player->view_window = gtk_socket_new();
gtk_box_pack_start(GTK_BOX(vbox), player->view_window, TRUE, TRUE, 0);
#ifndef EMBEDDED
/* statusbar */
player->statusbar = gtk_statusbar_new();
player->statusbar_id = 0;
gtk_box_pack_end(GTK_BOX(vbox), player->statusbar, FALSE, FALSE, 0);
#endif
/* toolbar */
toolbar = gtk_toolbar_new();
gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
player->tb_previous = gtk_tool_button_new_from_stock(
GTK_STOCK_MEDIA_PREVIOUS);
g_signal_connect(player->tb_previous, "clicked", G_CALLBACK(
on_previous), player);
gtk_widget_set_tooltip_text(GTK_WIDGET(player->tb_previous),
_("Previous"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), player->tb_previous, -1);
player->tb_rewind = gtk_tool_button_new_from_stock(
GTK_STOCK_MEDIA_REWIND);
g_signal_connect(player->tb_rewind, "clicked", G_CALLBACK(
on_rewind), player);
gtk_widget_set_tooltip_text(GTK_WIDGET(player->tb_rewind),
_("Rewind"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), player->tb_rewind, -1);
player->tb_play = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_PLAY);
g_signal_connect(player->tb_play, "clicked", G_CALLBACK(on_play),
player);
gtk_widget_set_tooltip_text(GTK_WIDGET(player->tb_play), _("Play"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), player->tb_play, -1);
player->tb_pause = gtk_tool_button_new_from_stock(
GTK_STOCK_MEDIA_PAUSE);
g_signal_connect(player->tb_pause, "clicked", G_CALLBACK(on_pause),
player);
gtk_widget_set_tooltip_text(GTK_WIDGET(player->tb_pause), _("Pause"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), player->tb_pause, -1);
player->tb_stop = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_STOP);
g_signal_connect(player->tb_stop, "clicked", G_CALLBACK(on_stop),
player);
gtk_widget_set_tooltip_text(GTK_WIDGET(player->tb_stop), _("Stop"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), player->tb_stop, -1);
player->tb_forward = gtk_tool_button_new_from_stock(
GTK_STOCK_MEDIA_FORWARD);
g_signal_connect(player->tb_forward, "clicked", G_CALLBACK(
on_forward), player);
gtk_widget_set_tooltip_text(GTK_WIDGET(player->tb_forward),
_("Forward"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), player->tb_forward, -1);
player->tb_next = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_NEXT);
g_signal_connect(player->tb_next, "clicked", G_CALLBACK(
on_next), player);
gtk_widget_set_tooltip_text(GTK_WIDGET(player->tb_next), _("Next"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), player->tb_next, -1);
toolitem = gtk_tool_item_new();
gtk_tool_item_set_expand(toolitem, TRUE);
player->progress = gtk_progress_bar_new();
_player_set_progress(player, 0);
gtk_container_add(GTK_CONTAINER(toolitem), player->progress);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
widget = gtk_image_new_from_icon_name("stock_fullscreen",
GTK_ICON_SIZE_SMALL_TOOLBAR);
player->tb_fullscreen = gtk_tool_button_new(widget, _("Fullscreen"));
g_signal_connect(player->tb_fullscreen, "clicked", G_CALLBACK(
on_fullscreen), player);
gtk_widget_set_tooltip_text(GTK_WIDGET(player->tb_fullscreen),
_("Fullscreen"));
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), player->tb_fullscreen, -1);
gtk_box_pack_end(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
gtk_widget_show_all(player->window);
/* playlist */
/* FIXME make it dockable */
player->pl_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(player->pl_window), _("Playlist"));
g_signal_connect(G_OBJECT(player->pl_window), "delete-event",
G_CALLBACK(on_playlist_closex), player);
vbox = gtk_vbox_new(FALSE, 0);
/* view */
player->pl_store = gtk_list_store_new(PL_NUM_COLS,
G_TYPE_BOOLEAN, /* enabled */
GDK_TYPE_PIXBUF,/* icon */
G_TYPE_STRING, /* filename */
G_TYPE_UINT, /* track number */
G_TYPE_STRING, /* artist */
G_TYPE_STRING, /* album */
G_TYPE_STRING); /* name */
widget = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
player->pl_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(
player->pl_store));
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(player->pl_view), TRUE);
gtk_tree_view_append_column(GTK_TREE_VIEW(player->pl_view),
gtk_tree_view_column_new_with_attributes("",
gtk_cell_renderer_pixbuf_new(), "pixbuf",
PL_COL_ICON, NULL));
_new_column_text(player->pl_view, _("Artist"), PL_COL_ARTIST);
_new_column_text(player->pl_view, _("Album"), PL_COL_ALBUM);
_new_column_text(player->pl_view, _("Title"), PL_COL_TITLE);
gtk_container_add(GTK_CONTAINER(widget), player->pl_view);
gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0);
hbox = gtk_hbox_new(TRUE, 0);
widget = gtk_button_new_from_stock(GTK_STOCK_OPEN);
/* FIXME implement signal */
gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 4);
widget = gtk_button_new_from_stock(GTK_STOCK_SAVE);
/* FIXME implement signal */
gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 4);
widget = gtk_button_new_from_stock(GTK_STOCK_ADD);
/* FIXME implement signal */
gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 4);
widget = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
/* FIXME implement signal */
gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 4);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 4);
gtk_container_add(GTK_CONTAINER(player->pl_window), vbox);
#ifdef DEBUG
gtk_widget_show_all(player->pl_window);
#else
gtk_widget_show_all(vbox);
#endif
/* mplayer */
_new_mplayer(player);
return player;
}
#ifndef EMBEDDED
static GtkWidget * _new_menubar(Player * player)
{
GtkWidget * tb_menubar;
GtkAccelGroup * group;
GtkWidget * menu;
GtkWidget * menubar;
GtkWidget * menuitem;
unsigned int i;
unsigned int j;
struct _menu * p;
tb_menubar = gtk_menu_bar_new();
group = gtk_accel_group_new();
for(i = 0; _menubar[i].name != NULL; i++)
{
menubar = gtk_menu_item_new_with_mnemonic(_(_menubar[i].name));
menu = gtk_menu_new();
for(j = 0; _menubar[i].menu[j].name != NULL; j++)
{
p = &_menubar[i].menu[j];
if(p->name[0] == '\0')
menuitem = gtk_separator_menu_item_new();
else if(p->stock == NULL)
menuitem = gtk_menu_item_new_with_mnemonic(
_(p->name));
else
menuitem = gtk_image_menu_item_new_from_stock(
p->stock, NULL);
if(p->callback != NULL)
g_signal_connect(G_OBJECT(menuitem), "activate",
G_CALLBACK(p->callback),
player);
else
gtk_widget_set_sensitive(menuitem, FALSE);
if(p->accel != 0)
gtk_widget_add_accelerator(menuitem, "activate",
group, p->accel,
GDK_CONTROL_MASK,
GTK_ACCEL_VISIBLE);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
}
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menubar), menu);
gtk_menu_bar_append(GTK_MENU_BAR(tb_menubar), menubar);
}
gtk_window_add_accel_group(GTK_WINDOW(player->window), group);
return tb_menubar;
}
#endif
static void _new_mplayer(Player * player)
{
char buf[] = "pausing loadfile splash.png 0\nframe_step\n";
char wid[16];
char * argv[] = { "mplayer", "-slave", "-wid", NULL, "-quiet",
"-idle", "-framedrop", "-softvol", "-identify",
"-noconsolecontrols", "-nomouseinput", NULL };
argv[3] = wid;
_player_reset(player);
snprintf(wid, sizeof(wid), "%u", gtk_socket_get_id(GTK_SOCKET(
player->view_window)));
if((player->pid = fork()) == -1)
{
player_error(player, strerror(errno), 0);
return;
}
if(player->pid == 0) /* child */
{
close(player->fd[0][0]);
close(player->fd[1][1]);
if(dup2(player->fd[1][0], 0) == -1)
exit(_player_error("dup2", 2));
if(dup2(player->fd[0][1], 1) == -1)
exit(_player_error("dup2", 2));
execvp(argv[0], argv);
exit(_player_error(argv[0], 2));
}
close(player->fd[0][1]);
close(player->fd[1][0]);
player->channel[0] = g_io_channel_unix_new(player->fd[0][0]);
player->read_id = g_io_add_watch(player->channel[0], G_IO_IN,
_command_read, player);
player->channel[1] = g_io_channel_unix_new(player->fd[1][1]);
_player_command(player, buf, sizeof(buf) - 1);
player->paused = 1;
}
static void _new_column_text(GtkWidget * view, char const * title, int id)
{
GtkTreeViewColumn * column;
GtkCellRenderer * renderer;
renderer = gtk_cell_renderer_text_new();
g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
column = gtk_tree_view_column_new_with_attributes(title, renderer,
"text", id, NULL);
#if GTK_CHECK_VERSION(2, 4, 0)
gtk_tree_view_column_set_expand(column, TRUE);
#endif
gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_column_set_sort_column_id(column, id);
gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
}
#if !GTK_CHECK_VERSION(2, 12, 0)
static void gtk_widget_set_tooltip_text(GtkWidget * widget, const char * text)
{
static GtkTooltips * tooltips = NULL;
if(tooltips == NULL)
{
tooltips = gtk_tooltips_new();
gtk_tooltips_enable(tooltips);
}
gtk_tooltips_set_tip(tooltips, widget, text, NULL);
}
#endif
/* player_delete */
void player_delete(Player * player)
{
char cmd[] = "quit\n";
if(player->read_id != 0)
g_source_remove(player->read_id);
if(player->timeout_id != 0)
g_source_remove(player->timeout_id);
_player_command(player, cmd, sizeof(cmd) - 1);
g_io_channel_shutdown(player->channel[1], TRUE, NULL);
kill(player->pid, SIGTERM);
free(player);
}
/* accessors */
int player_get_fullscreen(Player * player)
{
return player->fullscreen ? 1 : 0;
}
void player_set_fullscreen(Player * player, int fullscreen)
{
if(fullscreen)
{
if(player->fullscreen)
return;
#ifndef EMBEDDED
gtk_widget_hide(player->menubar);
gtk_widget_hide(player->statusbar);
#endif
gtk_window_fullscreen(GTK_WINDOW(player->window));
player->fullscreen = !player->fullscreen;
return;
}
if(!player->fullscreen)
return;
gtk_window_unfullscreen(GTK_WINDOW(player->window));
#ifndef EMBEDDED
gtk_widget_show(player->menubar);
gtk_widget_show(player->statusbar);
#endif
player->fullscreen = !player->fullscreen;
}
/* player_set_size */
void player_set_size(Player * player, int width, int height)
{
if(width < 0)
width = player->width;
if(height < 0)
height = player->height;
gtk_widget_set_size_request(player->view_window, width, height);
player->width = width;
player->height = height;
}
/* useful */
/* player_error */
int player_error(Player * player, char const * message, int ret)
{
GtkWidget * dialog;
dialog = gtk_message_dialog_new(GTK_WINDOW(player->window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s",
#if GTK_CHECK_VERSION(2, 6, 0)
_("Error"));
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
"%s",
#endif
message);
gtk_window_set_title(GTK_WINDOW(dialog), _("Error"));
g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(
gtk_widget_destroy), NULL);
gtk_dialog_run(GTK_DIALOG(dialog));
return ret;
}
int player_sigchld(Player * player)
{
pid_t pid;
int status;
if(player->pid == -1)
return 1;
if((pid = waitpid(player->pid, &status, WNOHANG)) == -1)
return player_error(player, "waitpid", 1);
if(pid == 0)
return 1;
fputs("player: mplayer ", stderr);
if(WIFEXITED(status))
fprintf(stderr, "%d%s%u\n", pid, ": exited with code ",
WEXITSTATUS(status));
else
fprintf(stderr, "%d%s", pid, ": Unknown state\n");
player->pid = -1;
return 0;
}
/* playlist management */
void player_next(Player * player)
{
char cmd[] = "pt_step 1\n";
_player_command(player, cmd, sizeof(cmd)-1);
}
void player_previous(Player * player)
{
char cmd[] = "pt_step -1\n";
_player_command(player, cmd, sizeof(cmd)-1);
}
int player_open(Player * player, char const * filename)
{
char cmd[512];
size_t len;
char * p;
_player_reset(player);
if((player->filename = strdup(filename)) == NULL)
return player_error(player, strerror(errno), 1);
len = snprintf(cmd, sizeof(cmd), "%s%s%s", "pausing loadfile \"",
player->filename, "\" 0\nframe_step\n");
if(len >= sizeof(cmd))
{
fputs("player: String too long\n", stderr);
return 1;
}
if(_player_command(player, cmd, len) != 0)
return 1;
player->paused = 1;
p = strdup(filename);
snprintf(cmd, sizeof(cmd), "%s - %s", _("Player"), p != NULL
? basename(p) : filename);
free(p);
gtk_window_set_title(GTK_WINDOW(player->window), cmd);
return 0;
}
int player_open_dialog(Player * player)
{
int ret;
GtkWidget * dialog;
char * filename = NULL;
dialog = gtk_file_chooser_dialog_new(_("Open file..."),
GTK_WINDOW(player->window),
GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(
dialog));
gtk_widget_destroy(dialog);
if(filename == NULL)
return 1;
ret = player_open(player, filename);
g_free(filename);
return ret;
}
void player_queue_add(Player * player, char const * filename)
{
char cmd[512];
size_t len;
len = snprintf(cmd, sizeof(cmd), "%s%s%s", "loadfile \"", filename,
"\" 1\n");
if(len >= sizeof(cmd))
fputs("player: String too long\n", stderr);
else
_player_command(player, cmd, len);
}
/* player_play */
int player_play(Player * player)
{
char cmd[512];
size_t len;
if(player->filename == NULL)
return 0;
/* FIXME escape double quotes in filename? */
if(player->paused == 1)
{
strcpy(cmd, "pause\n");
len = 6;
}
else if((len = snprintf(cmd, sizeof(cmd), "%s%s%s", "loadfile \"",
player->filename, "\" 0\n"))
>= sizeof(cmd))
{
fputs("player: String too long\n", stderr);
return 1;
}
else
_player_reset(player);
if(_player_command(player, cmd, len) != 0)
return 1;
player->paused = 0;
if(player->read_id == 0)
player->read_id = g_io_add_watch(player->channel[0], G_IO_IN,
_command_read, player);
if(player->timeout_id == 0)
player->timeout_id = g_timeout_add(500, _command_timeout,
player);
return 0;
}
void player_pause(Player * player)
{
char cmd[] = "pause\n";
if(player->filename == NULL)
return;
if(player->paused != 0)
{
if(player->timeout_id == 0)
player->timeout_id = g_timeout_add(500,
_command_timeout, player);
}
else if(player->timeout_id != 0)
{
g_source_remove(player->timeout_id);
player->timeout_id = 0;
}
_player_command(player, cmd, sizeof(cmd) - 1);
player->paused = player->paused == 1 ? 0 : 1;
}
void player_stop(Player * player)
{
char cmd[] = "pausing loadfile splash.png 0\nframe_step\n";
_player_command(player, cmd, sizeof(cmd)-1);
_player_set_progress(player, 0);
player->paused = 0; /* FIXME also needs a stopped state */
if(player->read_id != 0)
{
g_source_remove(player->read_id);
player->read_id = 0;
}
if(player->timeout_id != 0)
{
g_source_remove(player->timeout_id);
player->timeout_id = 0;
}
}
void player_rewind(Player * player)
{
char cmd[] = "speed_incr -0.5\n";
if(player->filename == NULL)
return;
_player_command(player, cmd, sizeof(cmd)-1);
}
void player_forward(Player * player)
{
char cmd[] = "speed_incr 0.5\n";
if(player->filename == NULL)
return;
_player_command(player, cmd, sizeof(cmd)-1);
}