summaryrefslogtreecommitdiff
path: root/play.cpp
diff options
context:
space:
mode:
authorJohns <johns98@gmx.net>2013-02-15 16:32:32 +0100
committerJohns <johns98@gmx.net>2013-02-15 16:32:32 +0100
commiteb4b8437553ff7666eceadd09c6cb9dd8255380e (patch)
tree1f38b1dbdbdf094e6ad4e8819945b69f59f7e1a1 /play.cpp
parent70415bf8dd9eeed5f70854f66e0eab085f80e7c0 (diff)
downloadvdr-plugin-play-eb4b8437553ff7666eceadd09c6cb9dd8255380e.tar.gz
vdr-plugin-play-eb4b8437553ff7666eceadd09c6cb9dd8255380e.tar.bz2
First complete version imported.
Diffstat (limited to 'play.cpp')
-rw-r--r--play.cpp1654
1 files changed, 1654 insertions, 0 deletions
diff --git a/play.cpp b/play.cpp
new file mode 100644
index 0000000..8fc1808
--- /dev/null
+++ b/play.cpp
@@ -0,0 +1,1654 @@
+///
+/// @file play.cpp @brief A play plugin for VDR.
+///
+/// Copyright (c) 2012, 2013 by Johns. All Rights Reserved.
+///
+/// Contributor(s):
+///
+/// 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$
+//////////////////////////////////////////////////////////////////////////////
+
+#include <vdr/interface.h>
+#include <vdr/plugin.h>
+#include <vdr/player.h>
+#include <vdr/osd.h>
+#include <vdr/shutdown.h>
+#include <vdr/status.h>
+#include <vdr/videodir.h>
+
+#ifdef HAVE_CONFIG
+#include "config.h"
+#endif
+
+#include "play_service.h"
+extern "C"
+{
+#include "readdir.h"
+#include "video.h"
+#include "player.h"
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+ /// vdr-plugin version number.
+ /// Makefile extracts the version number for generating the file name
+ /// for the distribution archive.
+static const char *const VERSION = "0.0.14"
+#ifdef GIT_REV
+ "-GIT" GIT_REV
+#endif
+ ;
+
+ /// vdr-plugin description.
+static const char *const DESCRIPTION = trNOOP("A play plugin");
+
+ /// vdr-plugin text of main menu entry
+static const char *MAINMENUENTRY = trNOOP("Play");
+
+//////////////////////////////////////////////////////////////////////////////
+
+static char ConfigHideMainMenuEntry; ///< hide main menu entry
+
+//////////////////////////////////////////////////////////////////////////////
+// C Callbacks
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+** Device plugin remote class.
+*/
+class cMyRemote:public cRemote
+{
+ public:
+
+ /**
+ ** Soft device remote class constructor.
+ **
+ ** @param name remote name
+ */
+ cMyRemote(const char *name):cRemote(name)
+ {
+ }
+
+ /**
+ ** Put keycode into vdr event queue.
+ **
+ ** @param code key code
+ ** @param repeat flag key repeated
+ ** @param release flag key released
+ */
+ bool Put(const char *code, bool repeat = false, bool release = false) {
+ return cRemote::Put(code, repeat, release);
+ }
+};
+
+/**
+** Feed key press as remote input (called from C part).
+**
+** @param keymap target keymap "XKeymap" name
+** @param key pressed/released key name
+** @param repeat repeated key flag
+** @param release released key flag
+*/
+extern "C" void FeedKeyPress(const char *keymap, const char *key, int repeat,
+ int release)
+{
+ cRemote *remote;
+ cMyRemote *csoft;
+
+ if (!keymap || !key) {
+ return;
+ }
+ // find remote
+ for (remote = Remotes.First(); remote; remote = Remotes.Next(remote)) {
+ if (!strcmp(remote->Name(), keymap)) {
+ break;
+ }
+ }
+ // if remote not already exists, create it
+ if (remote) {
+ csoft = (cMyRemote *) remote;
+ } else {
+ dsyslog("[play]%s: remote '%s' not found\n", __FUNCTION__, keymap);
+ csoft = new cMyRemote(keymap);
+ }
+
+ //dsyslog("[play]%s %s, %s\n", __FUNCTION__, keymap, key);
+ if (key[1]) { // no single character
+ csoft->Put(key, repeat, release);
+ } else if (!csoft->Put(key, repeat, release)) {
+ cRemote::Put(KBDKEY(key[0])); // feed it for edit mode
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// C Callbacks for diashow
+//////////////////////////////////////////////////////////////////////////////
+
+#if 0
+
+/**
+** Draw rectangle.
+*/
+extern "C" void DrawRectangle(int x1, int y1, int x2, int y2, uint32_t argb)
+{
+ //GlobalDiashow->Osd->DrawRectangle(x1, y1, x2, y2, argb);
+}
+
+/**
+** Draw text.
+**
+** @param FIXME:
+*/
+extern "C" void DrawText(int x, int y, const char *s, uint32_t fg, uint32_t bg,
+ int w, int h, int align)
+{
+ const cFont *font;
+
+ font = cFont::GetFont(fontOsd);
+ //GlobalDiashow->Osd->DrawText(x, y, s, fg, bg, font, w, h, align);
+}
+
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+// Play
+//////////////////////////////////////////////////////////////////////////////
+
+static char DvdNav; ///< dvdnav active
+static int PlayerVolume = -1; ///< volume 0 - 100
+static char PlayerPaused; ///< player paused
+static char PlayerSpeed; ///< player playback speed
+
+#define PlayerSendQuit()
+#define PlayerSendPause()
+#define PlayerSendVolume()
+#define PlayerSendSetSpeed(x)
+#define PlayerSendSeek(x)
+#define SendCommand(x)
+
+//////////////////////////////////////////////////////////////////////////////
+// cPlayer
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+** Player class.
+*/
+class cMyPlayer:public cPlayer
+{
+ private:
+ char *FileName; ///< file to play
+
+ public:
+ cMyPlayer(const char *); ///< player constructor
+ virtual ~ cMyPlayer(); ///< player destructor
+ void Activate(bool); ///< player attached/detached
+ /// get current replay mode
+ virtual bool GetReplayMode(bool &, bool &, int &);
+};
+
+/**
+** Player constructor.
+**
+** @param filename path and name of file to play
+*/
+cMyPlayer::cMyPlayer(const char *filename)
+:cPlayer(pmExtern_THIS_SHOULD_BE_AVOIDED)
+{
+ dsyslog("play/%s: '%s'\n", __FUNCTION__, filename);
+
+ PlayerVolume = cDevice::CurrentVolume();
+ dsyslog("play: initial volume %d\n", PlayerVolume);
+
+ FileName = strdup(filename);
+}
+
+/**
+** Player destructor.
+*/
+cMyPlayer::~cMyPlayer()
+{
+ dsyslog("%s: end\n", __FUNCTION__);
+
+ PlayerStop();
+ free(FileName);
+}
+
+/**
+** Player attached or detached.
+*/
+void cMyPlayer::Activate(bool on)
+{
+ dsyslog("[play]%s: '%s' %d\n", __FUNCTION__, FileName, on);
+
+ if (on) {
+ PlayerStart(FileName);
+ } else {
+ PlayerStop();
+ }
+}
+
+/**
+** Get current replay mode.
+*/
+bool cMyPlayer::GetReplayMode(bool & play, bool & forward, int &speed)
+{
+ play = !PlayerPaused;
+ forward = true;
+ speed = play ? PlayerSpeed : -1;
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// cStatus
+//////////////////////////////////////////////////////////////////////////////
+
+class cMyStatus:public cStatus
+{
+ public:
+ cMyStatus(void);
+
+ protected:
+ virtual void SetVolume(int, bool); ///< volume changed
+
+ //bool GetVolume(int &, bool &);
+};
+
+static int Volume; ///< current volume
+cMyStatus *Status; ///< status monitor for volume
+
+/**
+** Status constructor.
+*/
+cMyStatus::cMyStatus(void)
+{
+ Volume = 0;
+}
+
+/**
+** Called if volume is set.
+*/
+void cMyStatus::SetVolume(int volume, bool absolute)
+{
+ dsyslog("play: volume %d %s\n", volume, absolute ? "abs" : "rel");
+
+ if (absolute) {
+ Volume = volume;
+ } else {
+ Volume += volume;
+ }
+
+ if (Volume != PlayerVolume) {
+ PlayerVolume = Volume;
+ PlayerSendVolume();
+ }
+}
+
+/**
+** Get volume.
+bool cMyStatus::GetVolume(int &volume, bool &mute)
+{
+}
+*/
+
+//////////////////////////////////////////////////////////////////////////////
+// cControl
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+** Our player control class.
+*/
+class cMyControl:public cControl
+{
+ private:
+ cMyPlayer * Player; ///< our player
+ cSkinDisplayReplay *Display; ///< our osd display
+ void ShowReplayMode(void); ///< display replay mode
+ void ShowProgress(void); ///< display progress bar
+ virtual void Show(void); ///< show replay control
+ virtual void Hide(void); ///< hide replay control
+
+ public:
+ cMyControl(const char *);
+ virtual ~ cMyControl();
+
+ virtual eOSState ProcessKey(eKeys); ///< handle keyboard input
+
+};
+
+/**
+** Show replay mode.
+*/
+void cMyControl::ShowReplayMode(void)
+{
+ // use vdr setup
+ if (Display || (Setup.ShowReplayMode && !cOsd::IsOpen())) {
+ bool play;
+ bool forward;
+ int speed;
+
+ if (GetReplayMode(play, forward, speed)) {
+ if (!Display) {
+ // no need to show normal play
+ if (play && forward && speed == 1) {
+ return;
+ }
+ Display = Skins.Current()->DisplayReplay(true);
+ }
+ Display->SetMode(play, forward, speed);
+ }
+ }
+}
+
+/**
+** Show progress.
+*/
+void cMyControl::ShowProgress(void)
+{
+ // FIXME:
+}
+
+/**
+** Show control.
+*/
+void cMyControl::Show(void)
+{
+ dsyslog("%s:\n", __FUNCTION__);
+ if (!Display) {
+ ShowProgress();
+ }
+}
+
+/**
+** Control constructor.
+**
+** @param filename pathname of file to play.
+*/
+cMyControl::cMyControl(const char *filename)
+:cControl(Player = new cMyPlayer(filename))
+{
+ Display = NULL;
+ Status = new cMyStatus; // start monitoring volume
+
+ //LastSkipKey = kNone;
+ //LastSkipSeconds = REPLAYCONTROLSKIPSECONDS;
+ //LastSkipTimeout.Set(0);
+ cStatus::MsgReplaying(this, filename, filename, true);
+
+ cDevice::PrimaryDevice()->ClrAvailableTracks(true);
+}
+
+/**
+** Control destructor.
+*/
+cMyControl::~cMyControl()
+{
+ dsyslog("%s\n", __FUNCTION__);
+
+ delete Player;
+ delete Display;
+ delete Status;
+
+ Hide();
+ cStatus::MsgReplaying(this, NULL, NULL, false);
+ //Stop();
+}
+
+/**
+** Hide control.
+*/
+void cMyControl::Hide(void)
+{
+ dsyslog("%s:\n", __FUNCTION__);
+
+ if (Display) {
+ delete Display;
+
+ Display = NULL;
+ SetNeedsFastResponse(false);
+ }
+}
+
+/**
+** Process keyboard input.
+**
+** @param key pressed or releaded key
+*/
+eOSState cMyControl::ProcessKey(eKeys key)
+{
+ eOSState state;
+
+ dsyslog("%s: %d\n", __FUNCTION__, key);
+
+ if (!PlayerIsRunning()) { // check if player is still alive
+ dsyslog("play: player died\n");
+ Hide();
+ //FIXME: Stop();
+ return osEnd;
+ }
+ //state=cOsdMenu::ProcessKey(key);
+ state = osContinue;
+ switch ((int)key) { // cast to shutup g++ warnings
+ case kUp:
+ if (DvdNav) {
+ SendCommand("pausing_keep dvdnav up\n");
+ break;
+ }
+ case kPlay:
+ Hide();
+ if (PlayerSpeed != 1) {
+ PlayerSendSetSpeed(PlayerSpeed = 1);
+ }
+ if (PlayerPaused) {
+ PlayerSendPause();
+ PlayerPaused ^= 1;
+ }
+ ShowReplayMode();
+ break;
+
+ case kDown:
+ if (DvdNav) {
+ SendCommand("pausing_keep dvdnav down\n");
+ break;
+ }
+ case kPause:
+ PlayerSendPause();
+ PlayerPaused ^= 1;
+ ShowReplayMode();
+ break;
+
+ case kFastRew | k_Release:
+ case kLeft | k_Release:
+ if (Setup.MultiSpeedMode) {
+ break;
+ }
+ // FIXME:
+ break;
+ case kLeft:
+ if (DvdNav) {
+ SendCommand("pausing_keep dvdnav left\n");
+ break;
+ }
+ case kFastRew:
+ if (PlayerSpeed > 1) {
+ PlayerSendSetSpeed(PlayerSpeed /= 2);
+ } else {
+ PlayerSendSeek(-10);
+ }
+ ShowReplayMode();
+ break;
+ case kRight:
+ if (DvdNav) {
+ SendCommand("pausing_keep dvdnav right\n");
+ break;
+ }
+ case kFastFwd:
+ if (PlayerSpeed < 32) {
+ PlayerSendSetSpeed(PlayerSpeed *= 2);
+ }
+ ShowReplayMode();
+ break;
+
+ case kRed:
+ // FIXME: TimeSearch();
+ break;
+
+#ifdef USE_JUMPINGSECONDS
+ case kGreen | k_Repeat:
+ PlayerSendSeek(-Setup.JumpSecondsRepeat);
+ break;
+ case kGreen:
+ PlayerSendSeek(-Setup.JumpSeconds);
+ break;
+ case k1 | k_Repeat:
+ case k1:
+ PlayerSendSeek(-Setup.JumpSecondsSlow);
+ break;
+ case k3 | k_Repeat:
+ case k3:
+ PlayerSendSeek(Setup.JumpSecondsSlow);
+ break;
+ case kYellow | k_Repeat:
+ PlayerSendSeek(Setup.JumpSecondsRepeat);
+ break;
+ case kYellow:
+ PlayerSendSeek(Setup.JumpSeconds);
+ break;
+#else
+ case kGreen | k_Repeat:
+ case kGreen:
+ PlayerSendSeek(-60);
+ break;
+ case kYellow | k_Repeat:
+ case kYellow:
+ PlayerSendSeek(+60);
+ break;
+#endif /* JUMPINGSECONDS */
+#ifdef USE_LIEMIKUUTIO
+#ifndef USE_JUMPINGSECONDS
+ case k1 | k_Repeat:
+ case k1:
+ PlayerSendSeek(-20);
+ break;
+ case k3 | k_Repeat:
+ case k3:
+ PlayerSendSeek(+20);
+ break;
+#endif /* JUMPINGSECONDS */
+#endif
+
+ case kStop:
+ case kBlue:
+ Hide();
+ // FIXME: Stop();
+ return osEnd;
+
+ case kOk:
+ if (DvdNav) {
+ SendCommand("pausing_keep dvdnav select\n");
+ // FIXME: DvdNav = 0;
+ break;
+ }
+ // FIXME: full mode
+ ShowReplayMode();
+ break;
+
+ case kBack:
+ if (DvdNav > 1) {
+ SendCommand("pausing_keep dvdnav prev\n");
+ break;
+ }
+ PlayerSendQuit();
+ // FIXME: need to select old directory and index
+ cRemote::CallPlugin("play");
+ return osBack;
+
+ case kMenu:
+ if (DvdNav) {
+ SendCommand("pausing_keep dvdnav menu\n");
+ break;
+ }
+ break;
+
+ case kAudio: // VDR: eats the keys
+ case k7:
+ // FIXME: audio menu
+ SendCommand("pausing_keep switch_audio\n");
+ break;
+ case kSubtitles: // VDR: eats the keys
+ case k9:
+ // FIXME: subtitle menu
+ SendCommand("pausing_keep sub_select\n");
+ break;
+
+ default:
+ break;
+ }
+
+ return state;
+}
+
+/**
+** Play a file.
+**
+** @param filename path and file name
+*/
+static void PlayFile(const char *filename)
+{
+ dsyslog("play: play file '%s'\n", filename);
+ cControl::Launch(new cMyControl(filename));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// cOsdMenu
+//////////////////////////////////////////////////////////////////////////////
+
+static char ShowBrowser; ///< flag show browser
+static const char *BrowserStartDir; ///< browser start directory
+static const NameFilter *BrowserFilters; ///< browser name filters
+
+/**
+** Table of supported video suffixes.
+*/
+static const NameFilter VideoFilters[] = {
+#define FILTER(x) { sizeof(x) - 1, x }
+ FILTER(".ts"), FILTER(".avi"), FILTER(".flv"), FILTER(".iso"),
+ FILTER(".m4v"), FILTER(".mkv"), FILTER(".mov"), FILTER(".mp4"),
+ FILTER(".mpg"), FILTER(".vdr"), FILTER(".vob"), FILTER(".wmv"),
+#undef FILTER
+ {
+ 0, NULL}
+};
+
+/**
+** Table of supported audio suffixes.
+*/
+static const NameFilter AudioFilters[] = {
+#define FILTER(x) { sizeof(x) - 1, x }
+ FILTER(".flac"), FILTER(".mp3"), FILTER(".ogg"), FILTER(".wav"),
+#undef FILTER
+ {
+ 0, NULL}
+};
+
+/**
+** Table of supported image suffixes.
+*/
+static const NameFilter ImageFilters[] = {
+#define FILTER(x) { sizeof(x) - 1, x }
+ FILTER(".cbr"), FILTER(".cbz"), FILTER(".zip"), FILTER(".rar"),
+ FILTER(".jpg"), FILTER(".png"),
+#undef FILTER
+ {
+ 0, NULL}
+};
+
+/**
+** Menu class.
+*/
+class cBrowser:public cOsdMenu
+{
+ private:
+ int DirStackCount; ///< elements in directory stack
+ char **DirStack; ///< current path directory stack
+ const NameFilter *Filter; ///< current filter
+
+ /// Create a browser menu for current directory
+ void CreateMenu(void);
+ /// Create a browser menu for new directory
+ void NewDir(const char *, const NameFilter *);
+ /// Handle menu level up
+ eOSState LevelUp(void);
+ /// Handle menu item selection
+ eOSState Selected(void);
+
+ public:
+ cBrowser(const char *, const char *, const NameFilter *);
+ virtual ~ cBrowser();
+ virtual eOSState ProcessKey(eKeys);
+};
+
+/**
+** Add item to menu. Called from C.
+**
+** @param obj cBrowser object
+** @param text menu text
+*/
+extern "C" void cBrowser__Add(void *obj, const char *text)
+{
+ cBrowser *menu;
+
+ // fucking stupid C++ can't assign void* without warning:
+ menu = (typeof(menu)) obj;
+ menu->Add(new cOsdItem(text));
+}
+
+/**
+** Create browser directory menu.
+*/
+void cBrowser::CreateMenu(void)
+{
+ // FIXME: should show only directory name in title
+ //SetTitle(DirStack[0]);
+ Skins.Message(mtStatus, tr("Scanning directory..."));
+
+ if (DirStackCount > 1) {
+ // FIXME: should show only path
+ Add(new cOsdItem(DirStack[0]));
+ }
+ ReadDirectory(DirStack[0], 1, NULL, cBrowser__Add, this);
+ ReadDirectory(DirStack[0], 0, Filter, cBrowser__Add, this);
+ // FIXME: handle errors!
+
+ Display(); // display build menu
+ Skins.Message(mtStatus, NULL); // clear read message
+}
+
+/**
+** Create directory menu.
+**
+** @param path directory path file name
+** @param filter name selection filter
+*/
+void cBrowser::NewDir(const char *path, const NameFilter * filter)
+{
+ int n;
+ char *pathname;
+
+ n = strlen(path);
+#if 1
+ // FIXME: force caller to do
+ if (path[n - 1] == '/') { // force '/' terminated
+ pathname = strdup(path);
+ } else {
+ pathname = (char *)malloc(n + 2);
+ stpcpy(stpcpy(pathname, path), "/");
+ }
+#endif
+
+ // push on directory stack
+ DirStack =
+ (typeof(DirStack)) realloc(DirStack,
+ (DirStackCount + 1) * sizeof(*DirStack));
+ memmove(DirStack + 1, DirStack, DirStackCount * sizeof(*DirStack));
+ DirStackCount++;
+ DirStack[0] = pathname;
+ Filter = filter;
+
+ CreateMenu();
+}
+
+/**
+** Menu constructor.
+**
+** @param title menu title
+** @param path directory path file name
+** @param filter name selection filter
+*/
+cBrowser::cBrowser(const char *title, const char *path,
+ const NameFilter * filter)
+:cOsdMenu(title)
+{
+ dsyslog("[play]%s:\n", __FUNCTION__);
+
+ DirStack = NULL;
+ DirStackCount = 0;
+ Filter = filter;
+
+ NewDir(path, filter);
+}
+
+/**
+** Menu destructor.
+*/
+cBrowser::~cBrowser()
+{
+ int i;
+
+ dsyslog("[play]%s:\n", __FUNCTION__);
+
+ // free the stored directory stack
+ for (i = 0; i < DirStackCount; ++i) {
+ free(DirStack[i]);
+ }
+ free(DirStack);
+}
+
+/**
+** Handle level up.
+*/
+eOSState cBrowser::LevelUp(void)
+{
+ char *down;
+ char *name;
+
+ if (DirStackCount <= 1) { // top level reached
+ return osEnd;
+ }
+ // go level up
+ --DirStackCount;
+ down = DirStack[0];
+ memmove(DirStack, DirStack + 1, DirStackCount * sizeof(*DirStack));
+
+ Clear();
+ CreateMenu();
+
+ // select item, where we gone down
+ down[strlen(down) - 1] = '\0'; // remove trailing '/'
+ name = strrchr(down, '/');
+ if (name) {
+ cOsdItem *item;
+ const char *text;
+ int i;
+
+ for (i = 0; (item = Get(i)); ++i) {
+ text = item->Text();
+ if (!strcmp(text, name + 1)) {
+ SetCurrent(item);
+ // FIXME: Display already called!
+ Display(); // display build menu
+ break;
+ }
+ }
+ }
+
+ free(down);
+
+ return osContinue;
+}
+
+/**
+** Handle selected item.
+*/
+eOSState cBrowser::Selected(void)
+{
+ int current;
+ const cOsdItem *item;
+ const char *text;
+ char *filename;
+ char *tmp;
+
+ current = Current(); // get current menu item index
+ item = Get(current);
+ text = item->Text();
+
+ if (current == 0 && DirStackCount > 1) {
+ return LevelUp();
+ }
+ // +2: \0 + #
+ filename = (char *)malloc(strlen(DirStack[0]) + strlen(text) + 2);
+ // path is '/' terminated
+ tmp = stpcpy(stpcpy(filename, DirStack[0]), text);
+ if (!IsDirectory(filename)) {
+ if (IsArchive(filename)) { // handle archives
+ stpcpy(tmp, "#");
+ Clear();
+ NewDir(filename, Filter);
+ free(filename);
+ // FIXME: if dir fails use keep old!
+ return osContinue;
+ }
+ PlayFile(filename);
+ free(filename);
+ return osEnd;
+ }
+ // handle DVD image
+ if (!strcmp(text, "AUDIO_TS") || !strcmp(text, "VIDEO_TS")) {
+ free(filename);
+ tmp = (char *)malloc(sizeof("dvdnav:///") + strlen(DirStack[0]));
+ strcpy(stpcpy(tmp, "dvdnav:///"), DirStack[0]);
+ PlayFile(tmp);
+ free(tmp);
+ return osEnd;
+ }
+ stpcpy(tmp, "/"); // append '/'
+ Clear();
+ NewDir(filename, Filter);
+ free(filename);
+ // FIXME: if dir fails use keep old!
+ return osContinue;
+}
+
+/**
+** Handle Menu key event.
+**
+** @param key key event
+*/
+eOSState cBrowser::ProcessKey(eKeys key)
+{
+ eOSState state;
+
+ //
+ // call standard function
+ state = cOsdMenu::ProcessKey(key);
+ dsyslog("[play]%s: %x - %x\n", __FUNCTION__, state, key);
+
+ switch (state) {
+ case osUnknown:
+ switch (key) {
+ case kOk:
+ return Selected();
+ case kBack:
+ return LevelUp();
+ default:
+ break;
+ }
+ break;
+ case osBack:
+ state = LevelUp();
+ if (state == osEnd) { // top level reached
+ ShowBrowser = 0;
+ return osPlugin;
+ }
+ default:
+ break;
+ }
+ return state;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// cOsdMenu
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+** Play plugin menu class.
+*/
+class cPlayMenu:public cOsdMenu
+{
+ private:
+ public:
+ cPlayMenu(const char *, int = 0, int = 0, int = 0, int = 0, int = 0);
+ virtual ~ cPlayMenu();
+ virtual eOSState ProcessKey(eKeys);
+};
+
+/**
+** Play menu constructor.
+*/
+cPlayMenu::cPlayMenu(const char *title, int c0, int c1, int c2, int c3, int c4)
+:cOsdMenu(title, c0, c1, c2, c3, c4)
+{
+ SetHasHotkeys();
+
+ Add(new cOsdItem(hk(tr("Browse")), osUser1));
+ Add(new cOsdItem(hk(tr("Play optical disc")), osUser2));
+ Add(new cOsdItem(""));
+ Add(new cOsdItem(""));
+ Add(new cOsdItem(hk(tr("Play audio CD")), osUser5));
+ Add(new cOsdItem(hk(tr("Play video DVD")), osUser6));
+ Add(new cOsdItem(hk(tr("Browse audio")), osUser7));
+ Add(new cOsdItem(hk(tr("Browse image")), osUser8));
+ Add(new cOsdItem(hk(tr("Browse video")), osUser9));
+}
+
+/**
+** Play menu destructor.
+*/
+cPlayMenu::~cPlayMenu()
+{
+}
+
+/**
+** Handle play plugin menu key event.
+**
+** @param key key event
+*/
+eOSState cPlayMenu::ProcessKey(eKeys key)
+{
+ eOSState state;
+
+ dsyslog("[play]%s: %x\n", __FUNCTION__, key);
+
+ // call standard function
+ state = cOsdMenu::ProcessKey(key);
+
+ switch (state) {
+ case osUser1:
+ ShowBrowser = 1;
+ BrowserStartDir = ConfigBrowserRoot;
+ BrowserFilters = NULL;
+ return osPlugin; // restart with OSD browser
+
+ case osUser3: // play audio cdrom
+ PlayFile("cdda://");
+ return osEnd;
+ case osUser4: // play dvd
+ PlayFile("dvdnav://");
+ return osEnd;
+
+ case osUser5:
+ ShowBrowser = 1;
+ BrowserStartDir = ConfigBrowserRoot;
+ BrowserFilters = AudioFilters;
+ return osPlugin; // restart with OSD browser
+ case osUser6:
+ ShowBrowser = 1;
+ BrowserStartDir = ConfigBrowserRoot;
+ BrowserFilters = ImageFilters;
+ return osPlugin; // restart with OSD browser
+ case osUser7:
+ ShowBrowser = 1;
+ BrowserStartDir = ConfigBrowserRoot;
+ BrowserFilters = VideoFilters;
+ return osPlugin; // restart with OSD browser
+
+#if 0
+ case osUser9:
+ free(ShowDiashow);
+ ShowDiashow = strdup(VideoDirectory);
+ return osPlugin; // restart with OSD browser
+#endif
+
+ default:
+ break;
+ }
+ return state;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// cOsd
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+** My device plugin OSD class.
+*/
+class cMyOsd:public cOsd
+{
+ public:
+ static volatile char Dirty; ///< flag force redraw everything
+ int OsdLevel; ///< current osd level
+
+ cMyOsd(int, int, uint); ///< osd constructor
+ virtual ~ cMyOsd(void); ///< osd destructor
+ virtual void Flush(void); ///< commits all data to the hardware
+ virtual void SetActive(bool); ///< sets OSD to be the active one
+};
+
+volatile char cMyOsd::Dirty; ///< flag force redraw everything
+
+/**
+** Sets this OSD to be the active one.
+**
+** @param on true on, false off
+**
+** @note only needed as workaround for text2skin plugin with
+** undrawn areas.
+*/
+void cMyOsd::SetActive(bool on)
+{
+ dsyslog("[play]%s: %d\n", __FUNCTION__, on);
+
+ if (Active() == on) {
+ return; // already active, no action
+ }
+ cOsd::SetActive(on);
+
+ // ignore sub-title, if menu is open
+ if (OsdLevel >= OSD_LEVEL_SUBTITLES && IsOpen()) {
+ return;
+ }
+
+ if (on) {
+ Dirty = 1;
+ // only flush here if there are already bitmaps
+ //if (GetBitmap(0)) {
+ // Flush();
+ //}
+ OsdOpen();
+ } else {
+ OsdClose();
+ }
+}
+
+/**
+** Constructor OSD.
+**
+** Initializes the OSD with the given coordinates.
+**
+** @param left x-coordinate of osd on display
+** @param top y-coordinate of osd on display
+** @param level level of the osd (smallest is shown)
+*/
+cMyOsd::cMyOsd(int left, int top, uint level)
+:cOsd(left, top, level)
+{
+ /* FIXME: OsdWidth/OsdHeight not correct!
+ dsyslog("[play]%s: %dx%d+%d+%d, %d\n", __FUNCTION__, OsdWidth(),
+ OsdHeight(), left, top, level);
+ */
+
+ OsdLevel = level;
+ SetActive(true);
+}
+
+/**
+** OSD Destructor.
+**
+** Shuts down the OSD.
+*/
+cMyOsd::~cMyOsd(void)
+{
+ dsyslog("[play]%s:\n", __FUNCTION__);
+ SetActive(false);
+ // done by SetActive: OsdClose();
+}
+
+/**
+** Actually commits all data to the OSD hardware.
+*/
+void cMyOsd::Flush(void)
+{
+ cPixmapMemory *pm;
+
+ dsyslog("[play]%s: level %d active %d\n", __FUNCTION__, OsdLevel,
+ Active());
+
+ if (!Active()) { // this osd is not active
+ return;
+ }
+ // don't draw sub-title if menu is active
+ if (OsdLevel >= OSD_LEVEL_SUBTITLES && IsOpen()) {
+ return;
+ }
+ //
+ // VDR draws subtitle without clearing the old
+ //
+ if (OsdLevel >= OSD_LEVEL_SUBTITLES) {
+ OsdClear();
+ cMyOsd::Dirty = 1;
+ dsyslog("[softhddev]%s: subtitle clear\n", __FUNCTION__);
+ }
+
+ if (!IsTrueColor()) {
+ cBitmap *bitmap;
+ int i;
+
+ // draw all bitmaps
+ for (i = 0; (bitmap = GetBitmap(i)); ++i) {
+ uint8_t *argb;
+ int x;
+ int y;
+ int w;
+ int h;
+ int x1;
+ int y1;
+ int x2;
+ int y2;
+
+ // get dirty bounding box
+ if (Dirty) { // forced complete update
+ x1 = 0;
+ y1 = 0;
+ x2 = bitmap->Width() - 1;
+ y2 = bitmap->Height() - 1;
+ } else if (!bitmap->Dirty(x1, y1, x2, y2)) {
+ continue; // nothing dirty continue
+ }
+ // convert and upload only dirty areas
+ w = x2 - x1 + 1;
+ h = y2 - y1 + 1;
+ if (1) { // just for the case it makes trouble
+ int width;
+ int height;
+ double video_aspect;
+
+ ::GetOsdSize(&width, &height, &video_aspect);
+ if (w > width) {
+ w = width;
+ x2 = x1 + width - 1;
+ }
+ if (h > height) {
+ h = height;
+ y2 = y1 + height - 1;
+ }
+ }
+#ifdef DEBUG
+ if (w > bitmap->Width() || h > bitmap->Height()) {
+ esyslog(tr("[play]: dirty area too big\n"));
+ abort();
+ }
+#endif
+ argb = (uint8_t *) malloc(w * h * sizeof(uint32_t));
+ for (y = y1; y <= y2; ++y) {
+ for (x = x1; x <= x2; ++x) {
+ ((uint32_t *) argb)[x - x1 + (y - y1) * w] =
+ bitmap->GetColor(x, y);
+ }
+ }
+ dsyslog("[play]%s: draw %dx%d%+d%+d bm\n", __FUNCTION__, w, h,
+ Left() + bitmap->X0() + x1, Top() + bitmap->Y0() + y1);
+ OsdDrawARGB(Left() + bitmap->X0() + x1, Top() + bitmap->Y0() + y1,
+ w, h, argb);
+
+ bitmap->Clean();
+ // FIXME: reuse argb
+ free(argb);
+ }
+ cMyOsd::Dirty = 0;
+ return;
+ }
+
+ LOCK_PIXMAPS;
+ while ((pm = RenderPixmaps())) {
+ int x;
+ int y;
+ int w;
+ int h;
+
+ x = Left() + pm->ViewPort().X();
+ y = Top() + pm->ViewPort().Y();
+ w = pm->ViewPort().Width();
+ h = pm->ViewPort().Height();
+
+ dsyslog("[play]%s: draw %dx%d%+d%+d %p\n", __FUNCTION__, w, h, x, y,
+ pm->Data());
+ OsdDrawARGB(x, y, w, h, pm->Data());
+
+ delete pm;
+ }
+ cMyOsd::Dirty = 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// cOsdProvider
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+** My device plugin OSD provider class.
+*/
+class cMyOsdProvider:public cOsdProvider
+{
+ private:
+ static cOsd *Osd;
+
+ public:
+ virtual cOsd * CreateOsd(int, int, uint);
+ virtual bool ProvidesTrueColor(void);
+ cMyOsdProvider(void);
+};
+
+cOsd *cMyOsdProvider::Osd; ///< single osd
+
+/**
+** Create a new OSD.
+**
+** @param left x-coordinate of OSD
+** @param top y-coordinate of OSD
+** @param level layer level of OSD
+*/
+cOsd *cMyOsdProvider::CreateOsd(int left, int top, uint level)
+{
+ dsyslog("[play]%s: %d, %d, %d\n", __FUNCTION__, left, top, level);
+
+ return Osd = new cMyOsd(left, top, level);
+}
+
+/**
+** Check if this OSD provider is able to handle a true color OSD.
+**
+** @returns true we are able to handle a true color OSD.
+*/
+bool cMyOsdProvider::ProvidesTrueColor(void)
+{
+ return true;
+}
+
+/**
+** Create cOsdProvider class.
+*/
+cMyOsdProvider::cMyOsdProvider(void)
+: cOsdProvider()
+{
+ dsyslog("[play]%s:\n", __FUNCTION__);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// cMenuSetupPage
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+** Play plugin menu setup page class.
+*/
+class cMyMenuSetupPage:public cMenuSetupPage
+{
+ protected:
+ ///
+ /// local copies of global setup variables:
+ /// @{
+ int HideMainMenuEntry;
+
+ /// @}
+ virtual void Store(void);
+
+ public:
+ cMyMenuSetupPage(void);
+ virtual eOSState ProcessKey(eKeys); // handle input
+};
+
+/**
+** Process key for setup menu.
+*/
+eOSState cMyMenuSetupPage::ProcessKey(eKeys key)
+{
+ eOSState state;
+
+ state = cMenuSetupPage::ProcessKey(key);
+
+ return state;
+}
+
+/**
+** Constructor setup menu.
+**
+** Import global config variables into setup.
+*/
+cMyMenuSetupPage::cMyMenuSetupPage(void)
+{
+ HideMainMenuEntry = ConfigHideMainMenuEntry;
+
+ Add(new cMenuEditBoolItem(tr("Hide main menu entry"), &HideMainMenuEntry,
+ trVDR("no"), trVDR("yes")));
+}
+
+/**
+** Store setup.
+*/
+void cMyMenuSetupPage::Store(void)
+{
+ SetupStore("HideMainMenuEntry", ConfigHideMainMenuEntry =
+ HideMainMenuEntry);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// cDevice
+//////////////////////////////////////////////////////////////////////////////
+
+class cMyDevice:public cDevice
+{
+ public:
+ cMyDevice(void);
+ virtual ~ cMyDevice(void);
+
+ virtual void GetOsdSize(int &, int &, double &);
+
+ protected:
+ virtual void MakePrimaryDevice(bool);
+};
+
+/**
+** Device constructor.
+*/
+cMyDevice::cMyDevice(void)
+{
+ dsyslog("[play]%s\n", __FUNCTION__);
+}
+
+/**
+** Device destructor. (never called!)
+*/
+cMyDevice::~cMyDevice(void)
+{
+ dsyslog("[play]%s:\n", __FUNCTION__);
+}
+
+/**
+** Informs a device that it will be the primary device.
+**
+** @param on flag if becoming or loosing primary
+*/
+void cMyDevice::MakePrimaryDevice(bool on)
+{
+ dsyslog("[play]%s: %d\n", __FUNCTION__, on);
+
+ cDevice::MakePrimaryDevice(on);
+ if (on) {
+ new cMyOsdProvider();
+ }
+}
+
+/**
+** Returns the width, height and pixel_aspect ratio the OSD.
+**
+** FIXME: Called every second, for nothing (no OSD displayed)?
+*/
+void cMyDevice::GetOsdSize(int &width, int &height, double &pixel_aspect)
+{
+ if (!&width || !&height || !&pixel_aspect) {
+ esyslog(tr("[play]: GetOsdSize invalid pointer(s)\n"));
+ return;
+ }
+ ::GetOsdSize(&width, &height, &pixel_aspect);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// cPlugin
+//////////////////////////////////////////////////////////////////////////////
+
+static cMyDevice *MyDevice; ///< dummy device needed for osd
+static volatile int DoMakePrimary; ///< switch primary device to this
+
+class cMyPlugin:public cPlugin
+{
+ public:
+ cMyPlugin(void);
+ virtual ~ cMyPlugin(void);
+ virtual const char *Version(void);
+ virtual const char *Description(void);
+ virtual const char *CommandLineHelp(void);
+ virtual bool ProcessArgs(int, char *[]);
+ virtual bool Initialize(void);
+ virtual void MainThreadHook(void);
+ virtual const char *MainMenuEntry(void);
+ virtual cOsdObject *MainMenuAction(void);
+ virtual cMenuSetupPage *SetupMenu(void);
+ virtual bool SetupParse(const char *, const char *);
+ virtual bool Service(const char *, void * = NULL);
+ virtual const char **SVDRPHelpPages(void);
+ virtual cString SVDRPCommand(const char *, const char *, int &);
+};
+
+/**
+** Initialize any member variables here.
+**
+** @note DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
+** VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
+*/
+cMyPlugin::cMyPlugin(void)
+{
+ dsyslog("[play]%s:\n", __FUNCTION__);
+}
+
+/**
+** Clean up after yourself!
+*/
+cMyPlugin::~cMyPlugin(void)
+{
+ dsyslog("[play]%s:\n", __FUNCTION__);
+}
+
+/**
+** Return plugin version number.
+**
+** @returns version number as constant string.
+*/
+const char *cMyPlugin::Version(void)
+{
+ return VERSION;
+}
+
+/**
+** Return plugin short description.
+**
+** @returns short description as constant string.
+*/
+const char *cMyPlugin::Description(void)
+{
+ return tr(DESCRIPTION);
+}
+
+/**
+** Return a string that describes all known command line options.
+**
+** @returns command line help as constant string.
+*/
+const char *cMyPlugin::CommandLineHelp(void)
+{
+ return::CommandLineHelp();
+}
+
+/**
+** Process the command line arguments.
+*/
+bool cMyPlugin::ProcessArgs(int argc, char *argv[])
+{
+ return::ProcessArgs(argc, argv);
+}
+
+/**
+** Start any background activities the plugin shall perform.
+*/
+bool cMyPlugin::Initialize(void)
+{
+ //dsyslog("[play]%s:\n", __FUNCTION__);
+
+ // FIXME: can delay until needed?
+ //Status = new cMyStatus; // start monitoring
+ // FIXME: destructs memory
+
+ MyDevice = new cMyDevice;
+ return true;
+}
+
+/**
+** Create main menu entry.
+*/
+const char *cMyPlugin::MainMenuEntry(void)
+{
+ //dsyslog("[play]%s:\n", __FUNCTION__);
+
+ return ConfigHideMainMenuEntry ? NULL : tr(MAINMENUENTRY);
+}
+
+/**
+** Perform the action when selected from the main VDR menu.
+*/
+cOsdObject *cMyPlugin::MainMenuAction(void)
+{
+ //dsyslog("[play]%s:\n", __FUNCTION__);
+
+#if 0
+ printf("plugin %s %d\n", ShowDiashow, ShowBrowser);
+ if (ShowDiashow) {
+ return new cDiashow(ShowDiashow);
+ }
+#endif
+ if (ShowBrowser) {
+ return new cBrowser("Browse", BrowserStartDir, BrowserFilters);
+ }
+ return new cPlayMenu("Play");
+}
+
+/**
+** Receive requests or messages.
+**
+** @param id unique identification string that identifies the
+** service protocol
+** @param data custom data structure
+*/
+bool cMyPlugin::Service(const char *id, void *data)
+{
+ if (strcmp(id, PLAY_OSD_3DMODE_SERVICE) == 0) {
+ VideoSetOsd3DMode(0);
+ Play_Osd3DModeService_v1_0_t *r =
+ (Play_Osd3DModeService_v1_0_t *) data;
+ VideoSetOsd3DMode(r->Mode);
+ return true;
+ }
+ return false;
+}
+
+/**
+** Return SVDRP commands help pages.
+**
+** return a pointer to a list of help strings for all of the plugin's
+** SVDRP commands.
+*/
+const char **cMyPlugin::SVDRPHelpPages(void)
+{
+ static const char *HelpPages[] = {
+ "3DOF\n" " TURN OFF 3D", "3DTB\n" " TURN ON 3D TB",
+ "3DSB\n" " TURN ON 3D SBS", NULL
+ };
+ return HelpPages;
+}
+
+/**
+** Handle SVDRP commands.
+**
+** @param command SVDRP command
+** @param option all command arguments
+** @param reply_code reply code
+*/
+cString cMyPlugin::SVDRPCommand(const char *command,
+ __attribute__ ((unused)) const char *option,
+ __attribute__ ((unused)) int &reply_code)
+{
+ if (!strcasecmp(command, "3DOF")) {
+ VideoSetOsd3DMode(0);
+ return "3d off";
+ }
+ if (!strcasecmp(command, "3DSB")) {
+ VideoSetOsd3DMode(1);
+ return "3d sbs";
+ }
+ if (!strcasecmp(command, "3DTB")) {
+ VideoSetOsd3DMode(2);
+ return "3d tb";
+ }
+ return NULL;
+}
+
+/**
+** Called for every plugin once during every cycle of VDR's main program
+** loop.
+*/
+void cMyPlugin::MainThreadHook(void)
+{
+ // dsyslog("[play]%s:\n", __FUNCTION__);
+
+ if (DoMakePrimary) {
+ dsyslog("[play]: switching primary device to %d\n", DoMakePrimary);
+ cDevice::SetPrimaryDevice(DoMakePrimary);
+ DoMakePrimary = 0;
+ }
+}
+
+/**
+** Return our setup menu.
+*/
+cMenuSetupPage *cMyPlugin::SetupMenu(void)
+{
+ //dsyslog("[play]%s:\n", __FUNCTION__);
+
+ return new cMyMenuSetupPage;
+}
+
+/**
+** Parse setup parameters
+**
+** @param name paramter name (case sensetive)
+** @param value value as string
+**
+** @returns true if the parameter is supported.
+*/
+bool cMyPlugin::SetupParse(const char *name, const char *value)
+{
+ dsyslog("[play]%s: '%s' = '%s'\n", __FUNCTION__, name, value);
+
+ if (!strcasecmp(name, "HideMainMenuEntry")) {
+ ConfigHideMainMenuEntry = atoi(value);
+ return true;
+ }
+#if 0
+ if (!strncasecmp(name, "Dia.", 4)) {
+ return DiaConfigParse(name + 4, value);
+ }
+#endif
+
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int OldPrimaryDevice; ///< old primary device
+
+/**
+** Enable dummy device.
+*/
+extern "C" void EnableDummyDevice(void)
+{
+ OldPrimaryDevice = cDevice::PrimaryDevice()->DeviceNumber() + 1;
+ DoMakePrimary = MyDevice->DeviceNumber() + 1;
+}
+
+/**
+** Disable dummy device.
+*/
+extern "C" void DisableDummyDevice(void)
+{
+ DoMakePrimary = OldPrimaryDevice;
+ OldPrimaryDevice = 0;
+}
+
+VDRPLUGINCREATOR(cMyPlugin); // Don't touch this!