///
///	@file player.c	@brief A play plugin for VDR.
///
///	Copyright (c) 2012, 2013 by Johns.  All Rights Reserved.
///
///	Contributor(s): Dennis Bendlin
///
///	License: AGPLv3
///
///	This program is free software: you can redistribute it and/or modify
///	it under the terms of the GNU Affero General Public License as
///	published by the Free Software Foundation, either 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 Affero General Public License for more details.
///
///	$Id: 23f35b1a9358694e2b022aed1eff081887bc3f16 $
//////////////////////////////////////////////////////////////////////////////

#include <sys/types.h>
#include <sys/wait.h>

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#include <poll.h>
#include <pthread.h>

#include <libintl.h>
#define _(str) gettext(str)		///< gettext shortcut
#define _N(str) str			///< gettext_noop shortcut

#include "player.h"
#include "video.h"
#include "misc.h"

//////////////////////////////////////////////////////////////////////////////

const char *ConfigBrowserRoot = "/";	///< browser starting point

static char ConfigOsdOverlay;		///< show osd overlay
static char ConfigUseSlave;		///< external player use slave mode
static char ConfigFullscreen;		///< external player uses fullscreen
static char *ConfigVideoOut;		///< video out device
static char *ConfigAudioOut;		///< audio out device
static char *ConfigAudioMixer;		///< audio mixer device
static char *ConfigMixerChannel;	///< audio mixer channel
static const char *ConfigMplayer = "/usr/bin/mplayer";	///< mplayer executable
static const char *ConfigMplayerArguments;	///< extra mplayer arguments
static const char *ConfigX11Display = ":0.0";	///< x11 display

    /// DVD-Drive for mplayer
static const char *ConfigMplayerDevice = "/dev/dvd";
static uint32_t ConfigColorKey = 0x00020507;	///< color key

//////////////////////////////////////////////////////////////////////////////
//	Osd
//////////////////////////////////////////////////////////////////////////////

/**
**	Open OSD.
*/
void OsdOpen(void)
{
    Debug(3, "plex: %s\n", __FUNCTION__);

    VideoWindowShow();
}

/**
**	Close OSD.
*/
void OsdClose(void)
{
    Debug(3, "plex: %s\n", __FUNCTION__);

    VideoWindowHide();
    VideoWindowClear();
}

/**
**	Clear OSD.
*/
void OsdClear(void)
{
    Debug(3, "plex: %s\n", __FUNCTION__);

    VideoWindowClear();
}

/**
**	Get OSD size and aspect.
**
**	@param width[OUT]	width of OSD
**	@param height[OUT]	height of OSD
**	@param aspect[OUT]	aspect ratio (4/3, 16/9, ...) of OSD
*/
void GetOsdSize(int *width, int *height, double *aspect)
{
    VideoGetOsdSize(width, height);
    *aspect = 16.0 / 9.0 / (double)*width * (double)*height;
}

/**
**	Draw osd pixmap
*/
void OsdDrawARGB(int x, int y, int w, int h, const uint8_t * argb)
{
    Debug(3, "plex: %s %d,%d %d,%d\n", __FUNCTION__, x, y, w, h);

    VideoDrawARGB(x, y, w, h, argb);
}

//////////////////////////////////////////////////////////////////////////////
//	External player
//////////////////////////////////////////////////////////////////////////////

static pid_t PlayerPid;			///< player pid
static pthread_t PlayerThread;		///< player decode thread

static char PlayerPipeBuf[4096];	///< pipe buffer
static int PlayerPipeCnt;		///< pipe buffer count
static int PlayerPipeIdx;		///< pipe buffer index
static int PlayerPipeOut[2];		///< player write pipe
static int PlayerPipeIn[2];		///< player read pipe

static int PlayerVolume = -1;		///< volume 0 - 100

char PlayerDvdNav;			///< dvdnav active
char PlayerPaused;			///< player paused
char PlayerSpeed;			///< player playback speed
int PlayerCurrent;			///< current postion in seconds
int PlayerTotal;			///< total length in seconds
char PlayerTitle[256];			///< title from meta data
char PlayerFilename[256];		///< filename

//////////////////////////////////////////////////////////////////////////////
//	Slave
//////////////////////////////////////////////////////////////////////////////

/**
**	Parse player output.
**
**	@param data	line pointer (\0 terminated)
**	@param size	line length
*/
static void PlayerParseLine(const char *data, int size)
{
    Debug(4, "play/parse: |%.*s|\n", size, data);
    (void)size;

    // data is \0 terminated
    if (!strncasecmp(data, "DVDNAV_TITLE_IS_MENU", 20)) {
	PlayerDvdNav = 1;
    } else if (!strncasecmp(data, "DVDNAV_TITLE_IS_MOVIE", 21)) {
	PlayerDvdNav = 2;
    } else if (!strncasecmp(data, "ID_DVD_VOLUME_ID=", 17)) {
	Debug(3, "DVD_VOLUME = %s\n", data + 17);
    } else if (!strncasecmp(data, "ID_AID_", 7)) {
	char lang[64];
	int aid;

	if (sscanf(data, "ID_AID_%d_LANG=%s", &aid, lang) == 2) {
	    Debug(3, "AID(%d) = %s\n", aid, lang);
	}
    } else if (!strncasecmp(data, "ID_SID_", 7)) {
	char lang[64];
	int sid;

	if (sscanf(data, "ID_SID_%d_LANG=%s", &sid, lang) == 2) {
	    Debug(3, "SID(%d) = %s\n", sid, lang);
	}
    } else if (!strncasecmp(data, "ANS_META_TITLE=", 14)) {
	if (sscanf(data, "ANS_META_TITLE='%[^\t\n]", PlayerTitle) == 1) {
	    PlayerTitle[strlen(PlayerTitle) - 1] = 0;
	    Debug(3, "PlayerTitle= %s\n", PlayerTitle);
	}
    } else if (!strncasecmp(data, "ANS_FILENAME=", 12)) {
	if (sscanf(data, "ANS_FILENAME='%[^\t\n]", PlayerFilename) == 1) {
	    PlayerFilename[strlen(PlayerFilename) - 1] = 0;
	    Debug(3, "PlayerFilename= %s\n", PlayerFilename);
	}
    } else if (!strncasecmp(data, "ANS_LENGTH=", 10)) {
	if (sscanf(data, "ANS_LENGTH=%d", &PlayerTotal) == 1) {
	    Debug(3, "PlayerTotal=%d\n", PlayerTotal);
	}
    } else if (!strncasecmp(data, "ANS_TIME_POSITION=", 17)) {
	if (sscanf(data, "ANS_TIME_POSITION=%d", &PlayerCurrent) == 1) {
	    Debug(3, "PlayerCurrent=%d\n", PlayerCurrent);
	}
    }
}

/**
**	Poll input pipe.
*/
static void PlayerPollPipe(void)
{
    struct pollfd poll_fds[1];
    int n;
    int i;
    int l;

    // check if something to read
    poll_fds[0].fd = PlayerPipeOut[0];
    poll_fds[0].events = POLLIN;

    switch (poll(poll_fds, 1, 0)) {
	case 0:			// timeout
	    return;
	case -1:			// error
	    Error(_("play/player: poll failed: %s\n"), strerror(errno));
	    return;
	default:			// ready
	    break;
    }

    // fill buffer
    if ((n = read(PlayerPipeOut[0], PlayerPipeBuf + PlayerPipeCnt,
		sizeof(PlayerPipeBuf) - PlayerPipeCnt)) < 0) {
	Error(_("play/player: read failed: %s\n"), strerror(errno));
	return;
    }

    PlayerPipeCnt += n;
    l = 0;
    for (i = PlayerPipeIdx; i < PlayerPipeCnt; ++i) {
	if (PlayerPipeBuf[i] == '\n') {
	    PlayerPipeBuf[i] = '\0';
	    PlayerParseLine(PlayerPipeBuf + PlayerPipeIdx, i - PlayerPipeIdx);
	    PlayerPipeIdx = i + 1;
	    l = PlayerPipeIdx;
	}
    }

    if (l) {				// remove consumed bytes
	if (PlayerPipeCnt - l) {
	    memmove(PlayerPipeBuf, PlayerPipeBuf + l, PlayerPipeCnt - l);
	}
	PlayerPipeCnt -= l;
	PlayerPipeIdx -= l;
    } else if (PlayerPipeCnt == sizeof(PlayerPipeBuf)) {
	// no '\n' in buffer use it as single line
	PlayerPipeBuf[sizeof(PlayerPipeBuf) - 1] = '\0';
	PlayerParseLine(PlayerPipeBuf + PlayerPipeIdx, PlayerPipeCnt);
	PlayerPipeIdx = 0;
	PlayerPipeCnt = 0;
    }
}

#define MPLAYER_MAX_ARGS 64		///< number of arguments supported

/**
**	Execute external player.
**
**	@param filename	path and name of file to play
*/
static void PlayerExec(const char *filename)
{
    const char *args[MPLAYER_MAX_ARGS];
    int argn;
    char wid_buf[32];
    char volume_buf[32];
    int i;

    if (ConfigUseSlave) {		// connect pipe to stdin/stdout
	dup2(PlayerPipeIn[0], STDIN_FILENO);
	close(PlayerPipeIn[0]);
	close(PlayerPipeIn[1]);
	close(PlayerPipeOut[0]);
	dup2(PlayerPipeOut[1], STDOUT_FILENO);
	dup2(PlayerPipeOut[1], STDERR_FILENO);
	close(PlayerPipeOut[1]);
    }
    // close all file handles
    for (i = getdtablesize() - 1; i > STDERR_FILENO; i--) {
	close(i);
    }

    // export DISPLAY=

    args[0] = ConfigMplayer;
    args[1] = "-quiet";
    args[2] = "-msglevel";
    // FIXME: play with the options
#ifdef DEBUG
    args[3] = "all=6:global=4:cplayer=4:identify=4";
#else
    args[3] = "all=2:global=4:cplayer=2:identify=4";
#endif
    if (ConfigOsdOverlay) {
	args[4] = "-noontop";
    } else {
	args[4] = "-ontop";
    }
    args[5] = "-noborder";
    if (ConfigDisableRemote) {
	args[6] = "-lirc";
    } else {
	args[6] = "-nolirc";
    }
    args[7] = "-nojoystick";		// disable all unwanted inputs
    args[8] = "-noar";
    args[9] = "-nomouseinput";
    args[10] = "-nograbpointer";
    args[11] = "-noconsolecontrols";
    args[12] = "-fixed-vo";
    args[13] = "-sid";			// subtitle selection
    args[14] = "0";
    args[15] = "-slang";
    args[16] = "de,en";			// FIXME: use VDR config
    args[17] = "-alang";
    args[18] = "de,en";			// FIXME: use VDR config
    argn = 19;
    if (ConfigMplayerDevice) {		// dvd-device
	args[argn++] = "-dvd-device";
	args[argn++] = ConfigMplayerDevice;
    }
    if (!strncasecmp(filename, "cdda://", 7)) {
	args[argn++] = "-cache";	// cdrom needs cache
	args[argn++] = "1000";
    } else {
	args[argn++] = "-nocache";	// dvdnav needs nocache
    }
    if (ConfigUseSlave) {
	args[argn++] = "-slave";
	//args[argn++] = "-idle";
    }
    if (ConfigOsdOverlay) {		// no mplayer osd with overlay
	args[argn++] = "-osdlevel";
	args[argn++] = "0";
    }
    if (ConfigFullscreen) {
	args[argn++] = "-fs";
	args[argn++] = "-zoom";
    } else {
	args[argn++] = "-nofs";
    }
    if (VideoGetPlayWindow()) {
	snprintf(wid_buf, sizeof(wid_buf), "%d", VideoGetPlayWindow());
	args[argn++] = "-wid";
	args[argn++] = wid_buf;
    }
    if (ConfigVideoOut) {
	args[argn++] = "-vo";
	args[argn++] = ConfigVideoOut;
	// add options based on selected video out
	if (!strncmp(ConfigVideoOut, "vdpau", 5)) {
	    args[argn++] = "-vc";
	    args[argn++] =
		"ffmpeg12vdpau,ffwmv3vdpau,ffvc1vdpau,ffh264vdpau,ffodivxvdpau,";
	} else if (!strncmp(ConfigVideoOut, "vaapi", 5)) {
	    args[argn++] = "-va";
	    args[argn++] = "vaapi";
	}
    }
    if (ConfigAudioOut) {
	args[argn++] = "-ao";
	args[argn++] = ConfigAudioOut;
	// FIXME: -ac hwac3,hwdts,hwmpa,
    }
    if (ConfigAudioMixer) {
	args[argn++] = "-mixer";
	args[argn++] = ConfigAudioMixer;
    }
    if (ConfigMixerChannel) {
	args[argn++] = "-mixer-channel";
	args[argn++] = ConfigMixerChannel;
    }
    if (ConfigX11Display) {
	args[argn++] = "-display";
	args[argn++] = ConfigX11Display;
    }
    if (PlayerVolume != -1) {
	// FIXME: here could be a problem with LANG
	snprintf(volume_buf, sizeof(volume_buf), "%.2f",
	    (PlayerVolume * 100.0) / 255);
	args[argn++] = "-volume";
	args[argn++] = volume_buf;
    }
    //	split X server arguments string into words
    if (ConfigMplayerArguments) {
	const char *sval;
	char *buf;
	char *s;

	sval = ConfigMplayerArguments;
#ifndef __FreeBSD__
	s = buf = strdupa(sval);
#else
	s = buf = alloca(strlen(sval) + 1);
	strcpy(buf, sval);
#endif
	while ((sval = strsep(&s, " \t"))) {
	    args[argn++] = sval;

	    if (argn == MPLAYER_MAX_ARGS - 3) {	// argument overflow
		Error(_("play: too many arguments for mplayer\n"));
		// argn = 1;
		break;
	    }
	}
    }
    args[argn++] = "--";
    args[argn++] = filename;
    args[argn] = NULL;
#ifdef DEBUG
    if (argn + 1 >= (int)(sizeof(args) / sizeof(*args))) {
	Debug(3, "play: too many arguments %d\n", argn);
    }
    for (i = 0; i < argn; ++i) {
	Debug(3, "%s", args[i]);
    }
#endif

    execvp(args[0], (char *const *)args);

    // shouldn't be reached
    Error(_("play: execvp of '%s' failed: %s\n"), args[0], strerror(errno));
    exit(-1);
}

/**
**	Execute external player.
**
**	@param filename	path and name of file to play
*/
static void PlayerForkAndExec(const char *filename)
{
    pid_t pid;

    if (ConfigUseSlave) {
	if (pipe(PlayerPipeIn)) {
	    Error(_("play: pipe failed: %s\n"), strerror(errno));
	    return;
	}
	if (pipe(PlayerPipeOut)) {
	    Error(_("play: pipe failed: %s\n"), strerror(errno));
	    return;
	}
    }

    if ((pid = fork()) == -1) {
	Error(_("play: fork failed: %s\n"), strerror(errno));
	return;
    }
    if (!pid) {				// child
	PlayerExec(filename);
	return;
    }
    PlayerPid = pid;			// parent
    setpgid(pid, 0);

    if (ConfigUseSlave) {
	close(PlayerPipeIn[0]);
	close(PlayerPipeOut[1]);
    }

    Debug(3, "play: child pid=%d\n", pid);
}

/**
**	Close pipes.
*/
static void PlayerClosePipes(void)
{
    if (ConfigUseSlave) {
	if (PlayerPipeIn[0] != -1) {
	    close(PlayerPipeIn[0]);
	}
	if (PlayerPipeOut[1] != -1) {
	    close(PlayerPipeOut[1]);
	}
    }
}

//////////////////////////////////////////////////////////////////////////////
//	Thread
//////////////////////////////////////////////////////////////////////////////

/**
**	External player handler thread.
**
**	@param dummy	dummy pointer
*/
static void *PlayerHandlerThread(void *dummy)
{
    Debug(3, "play: player thread started\n");

    // Need: thread for video poll: while (PlayerIsRunning())
    for (;;) {
	if (ConfigUseSlave && PlayerIsRunning()) {
	    PlayerPollPipe();
	    // FIXME: wait only if pipe not ready
	}
	if (ConfigOsdOverlay) {
	    VideoPollEvents(10);
	} else {
	    usleep(10 * 1000);
	}
    }

    Debug(3, "play: player thread stopped\n");
    PlayerThread = 0;

    return dummy;
}

/**
**	Initialize player threads.
*/
static void PlayerThreadInit(void)
{
    pthread_create(&PlayerThread, NULL, PlayerHandlerThread, NULL);
    //pthread_setname_np(PlayerThread, "play player");
}

/**
**	Exit and cleanup player threads.
*/
static void PlayerThreadExit(void)
{
    if (PlayerThread) {
	void *retval;

	Debug(3, "play: player thread canceled\n");
	if (pthread_cancel(PlayerThread)) {
	    Error(_("play: can't queue cancel player thread\n"));
	}
	if (pthread_join(PlayerThread, &retval) || retval != PTHREAD_CANCELED) {
	    Error(_("play: can't cancel player thread\n"));
	}
	PlayerThread = 0;
    }
}

/**
**	Send command to player.
**
**	@param formst	printf format string
*/
static void SendCommand(const char *format, ...)
{
    va_list va;
    char buf[256];
    int n;

    if (!PlayerPid) {
	return;
    }
    if (PlayerPipeIn[1] == -1) {
	Error(_("play: no pipe to send command available\n"));
	return;
    }
    va_start(va, format);
    n = vsnprintf(buf, sizeof(buf), format, va);
    va_end(va);

    Debug(3, "play: send '%.*s'\n", n - 1, buf);

    // FIXME: poll pipe if ready
    if (write(PlayerPipeIn[1], buf, n) != n) {
	fprintf(stderr, "play: write failed\n");
    }
}

/**
**	Send player quit.
*/
void PlayerSendQuit(void)
{
    if (ConfigUseSlave) {
	SendCommand("quit\n");
    }
}

/**
**	Send player pause.
*/
void PlayerSendPause(void)
{
    if (ConfigUseSlave) {
	SendCommand("pause\n");
    }
}

/**
**	Send player speed_set.
**
**	@param speed	mplayer speed
*/
void PlayerSendSetSpeed(int speed)
{
    if (ConfigUseSlave) {
	SendCommand("pausing_keep speed_set %d\n", speed);
    }
}

/**
**	Send player seek.
**
**	@param seconds	seek in seconds relative to current position
*/
void PlayerSendSeek(int seconds)
{
    if (ConfigUseSlave) {
	SendCommand("pausing_keep seek %+d 0\n", seconds);
    }
}

/**
**	Send player volume.
*/
void PlayerSendVolume(void)
{
    if (ConfigUseSlave) {
	// FIXME: %.2f could have a problem with LANG
	SendCommand("pausing_keep volume %.2f 1\n",
	    (PlayerVolume * 100.0) / 255);
    }
}

/**
**	Send switch audio track.
*/
void PlayerSendSwitchAudio(void)
{
    if (ConfigUseSlave) {
	SendCommand("pausing_keep switch_audio\n");
    }
}

/**
**	Send select subtitle.
*/
void PlayerSendSubSelect(void)
{
    if (ConfigUseSlave) {
	SendCommand("pausing_keep sub_select\n");
    }
}

/**
**	Send up for dvdnav.
*/
void PlayerSendDvdNavUp(void)
{
    if (ConfigUseSlave) {
	SendCommand("pausing_keep dvdnav up\n");
    }
}

/**
**	Send down for dvdnav.
*/
void PlayerSendDvdNavDown(void)
{
    if (ConfigUseSlave) {
	SendCommand("pausing_keep dvdnav down\n");
    }
}

/**
**	Send left for dvdnav.
*/
void PlayerSendDvdNavLeft(void)
{
    if (ConfigUseSlave) {
	SendCommand("pausing_keep dvdnav left\n");
    }
}

/**
**	Send right for dvdnav.
*/
void PlayerSendDvdNavRight(void)
{
    if (ConfigUseSlave) {
	SendCommand("pausing_keep dvdnav right\n");
    }
}

/**
**	Send select for dvdnav.
*/
void PlayerSendDvdNavSelect(void)
{
    if (ConfigUseSlave) {
	SendCommand("pausing_keep dvdnav select\n");
    }
}

/**
**	Send prev for dvdnav.
*/
void PlayerSendDvdNavPrev(void)
{
    if (ConfigUseSlave) {
	SendCommand("pausing_keep dvdnav prev\n");
    }
}

/**
**	Send menu for dvdnav.
*/
void PlayerSendDvdNavMenu(void)
{
    if (ConfigUseSlave) {
	SendCommand("pausing_keep dvdnav menu\n");
    }
}

/**
**	Get length in seconds.
*/
void PlayerGetLength(void)
{
    if (ConfigUseSlave) {
	SendCommand("get_time_length\n");
    }
}

/**
**	Get current position in seconds.
*/
void PlayerGetCurrentPosition(void)
{
    if (ConfigUseSlave) {
	SendCommand("get_time_pos\n");
    }
}

/**
**	Get title from meta data.
*/
void PlayerGetMetaTitle(void)
{
    if (ConfigUseSlave) {
	SendCommand("get_meta_title\n");
    }
}

/**
**	Get filename.
*/
void PlayerGetFilename(void)
{
    if (ConfigUseSlave) {
	SendCommand("get_file_name\n");
    }
}

/**
**	Start external player.
**
**	@param filename	path and name of file to play
*/
void PlayerStart(const char *filename)
{
    PlayerPipeCnt = 0;			// reset to defaults
    PlayerPipeIdx = 0;

    PlayerPipeIn[0] = -1;
    PlayerPipeIn[1] = -1;
    PlayerPipeOut[0] = -1;
    PlayerPipeOut[1] = -1;
    PlayerPid = 0;

    PlayerPaused = 0;
    PlayerSpeed = 1;

    PlayerDvdNav = 0;

    if (ConfigOsdOverlay) {		// overlay wanted
	VideoSetColorKey(ConfigColorKey);
	VideoInit(ConfigX11Display);
	EnableDummyDevice();
    }
    PlayerForkAndExec(filename);

    if (ConfigOsdOverlay || ConfigUseSlave) {
	PlayerThreadInit();
    }
}

/**
**	Stop external player.
*/
void PlayerStop(void)
{
    PlayerThreadExit();

    //
    //	stop mplayer, if it is still running.
    //
    if (PlayerIsRunning()) {
	int waittime;
	int timeout;

	waittime = 0;
	timeout = 500;			// 0.5s

	kill(PlayerPid, SIGTERM);
	// wait for player finishing, with timeout
	while (PlayerIsRunning() && waittime++ < timeout) {
	    usleep(1 * 1000);
	}
	if (PlayerIsRunning()) {	// still running
	    waittime = 0;
	    timeout = 500;		// 0.5s

	    kill(PlayerPid, SIGKILL);
	    // wait for player finishing, with timeout
	    while (PlayerIsRunning() && waittime++ < timeout) {
		usleep(1 * 1000);
	    }
	    if (PlayerIsRunning()) {
		Error(_("play: can't stop player\n"));
	    }
	}
    }
    PlayerPid = 0;
    PlayerClosePipes();

    if (ConfigOsdOverlay) {
	DisableDummyDevice();
	VideoExit();
    }
}

/**
**	Is external player still running?
*/
int PlayerIsRunning(void)
{
    pid_t wpid;
    int status;

    if (!PlayerPid) {			// no player
	return 0;
    }

    wpid = waitpid(PlayerPid, &status, WNOHANG);
    if (wpid <= 0) {
	return 1;
    }
    if (WIFEXITED(status)) {
	Debug(3, "play: player exited (%d)\n", WEXITSTATUS(status));
    }
    if (WIFSIGNALED(status)) {
	Debug(3, "play: player killed (%d)\n", WTERMSIG(status));
    }
    PlayerPid = 0;
    return 0;
}

/**
**	Set player volume.
**
**	@param volume	new volume (0..255)
*/
void PlayerSetVolume(int volume)
{
    Debug(3, "player: set volume=%d\n", volume);

    if (PlayerVolume != volume) {
	PlayerVolume = volume;
	if (PlayerPid) {
	    PlayerSendVolume();
	}
    }
}

//////////////////////////////////////////////////////////////////////////////
//	Device/Plugin C part
//////////////////////////////////////////////////////////////////////////////

/**
**	Return command line help string.
*/
const char *CommandLineHelp(void)
{
    return "  -% device\tmplayer dvd device\n"
	"  -/\t/dir\tbrowser root directory\n"
	"  -a audio\tmplayer -ao (alsa:device=hw=0.0) overwrites mplayer.conf\n"
	"  -d display\tX11 display (default :0.0) overwrites $DISPLAY\n"
	"  -f\t\tmplayer fullscreen playback\n"
	"  -g geometry\tx11 window geometry wxh+x+y\n"
	"  -k colorkey\tvideo color key (default=0x020507, mplayer2=0x76B901)\n"
	"  -m mplayer\tfilename of mplayer executable\n"
	"  -M args\targuments for mplayer\n"
	"  -o\t\tosd overlay experiments\n" "  -s\t\tmplayer slave mode\n"
	"  -v video\tmplayer -vo (vdpau:deint=4:hqscaling=1) overwrites mplayer.conf\n";
}

/**
**	Process the command line arguments.
**
**	@param argc	number of arguments
**	@param argv	arguments vector
*/
int ProcessArgs(int argc, char *const argv[])
{
    const char *s;

    //
    //	Parse arguments.
    //
#ifdef __FreeBSD__
    if (!strcmp(*argv, "play")) {
	++argv;
	--argc;
    }
#endif
    if ((s = getenv("DISPLAY"))) {
	ConfigX11Display = s;
    }

    for (;;) {
	switch (getopt(argc, argv, "-%:/:a:b:d:fg:k:m:M:osv:")) {
	    case '%':			// dvd-device
		ConfigMplayerDevice = optarg;
		continue;
	    case '/':			// browser root
		ConfigBrowserRoot = optarg;
		continue;
	    case 'a':			// audio out
		ConfigAudioOut = optarg;
		continue;
	    case 'd':			// display x11
		ConfigX11Display = optarg;
		continue;
	    case 'f':			// fullscreen mode
		ConfigFullscreen = 1;
		continue;
	    case 'g':			// geometry
		VideoSetGeometry(optarg);
		continue;
	    case 'k':			// color key
		ConfigColorKey = strtol(optarg, NULL, 0);
		continue;
	    case 'm':			// mplayer executable
		ConfigMplayer = optarg;
		continue;
	    case 'M':			// mplayer extra arguments
		ConfigMplayerArguments = optarg;
		continue;
	    case 'o':			// osd / overlay
		ConfigOsdOverlay = 1;
		continue;
	    case 's':			// slave mode
		ConfigUseSlave = 1;
		continue;
	    case 'v':			// video out
		ConfigVideoOut = optarg;
		continue;
	    case EOF:
		break;
	    case '-':
		fprintf(stderr, _("We need no long options\n"));
		return 0;
	    case ':':
		fprintf(stderr, _("Missing argument for option '%c'\n"),
		    optopt);
		return 0;
	    default:
		fprintf(stderr, _("Unkown option '%c'\n"), optopt);
		return 0;
	}
	break;
    }
    while (optind < argc) {
	fprintf(stderr, _("Unhandled argument '%s'\n"), argv[optind++]);
    }

    return 1;
}