Stub player.
authorCarl Hetherington <cth@carlh.net>
Wed, 2 Aug 2017 16:25:38 +0000 (17:25 +0100)
committerCarl Hetherington <cth@carlh.net>
Mon, 14 Aug 2017 20:07:48 +0000 (21:07 +0100)
run/dcpomatic_player [new file with mode: 0755]
src/lib/cross.cc
src/lib/cross.h
src/tools/dcpomatic_player.cc [new file with mode: 0644]
src/tools/wscript
src/wx/film_viewer.cc
src/wx/film_viewer.h
src/wx/report_problem_dialog.h

diff --git a/run/dcpomatic_player b/run/dcpomatic_player
new file mode 100755 (executable)
index 0000000..9f28834
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+export LD_LIBRARY_PATH=build/src/lib:build/src/wx:build/src/asdcplib/src:/home/c.hetherington/lib:$LD_LIBRARY_PATH
+export DYLD_LIBRARY_PATH=build/src/lib:build/src/wx:build/src/asdcplib/src:/carl/Environment/64/lib
+if [ "$1" == "--debug" ]; then
+    shift
+    gdb --args build/src/tools/dcpomatic2_player $*
+elif [ "$1" == "--valgrind" ]; then
+    shift
+    valgrind --tool="memcheck" --suppressions=suppressions --track-fds=yes build/src/tools/dcpomatic2_player $*
+elif [ "$1" == "--callgrind" ]; then
+    shift
+    valgrind --tool="callgrind" build/src/tools/dcpomatic2_player $*
+elif [ "$1" == "--massif" ]; then
+    shift
+    valgrind --tool="massif" build/src/tools/dcpomatic2_player $*
+elif [ "$1" == "--i18n" ]; then
+    shift
+    LANGUAGE=fr_FR.UTF8 LANG=fr_FR.UTF8 LC_ALL=fr_FR.UTF8 build/src/tools/dcpomatic2_player "$*"
+elif [ "$1" == "--perf" ]; then
+    shift
+    perf record build/src/tools/dcpomatic2_player $*
+else
+    build/src/tools/dcpomatic2_player $*
+fi
index 831a1a2b2b0c2e1913646834d420c5e53c67b18c..e214019730410a0fbb4627ddf42512fef338926a 100644 (file)
@@ -424,3 +424,25 @@ avio_open_boost (AVIOContext** s, boost::filesystem::path file, int flags)
        return avio_open (s, file.c_str(), flags);
 #endif
 }
+
+#ifdef DCPOMATIC_WINDOWS
+void
+maybe_open_console ()
+{
+       if (Config::instance()->win32_console ()) {
+               AllocConsole();
+
+               HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);
+               int hCrt = _open_osfhandle((intptr_t) handle_out, _O_TEXT);
+               FILE* hf_out = _fdopen(hCrt, "w");
+               setvbuf(hf_out, NULL, _IONBF, 1);
+               *stdout = *hf_out;
+
+               HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE);
+               hCrt = _open_osfhandle((intptr_t) handle_in, _O_TEXT);
+               FILE* hf_in = _fdopen(hCrt, "r");
+               setvbuf(hf_in, NULL, _IONBF, 128);
+               *stdin = *hf_in;
+       }
+}
+#endif
index 02839eb68a5678bfbe2bed0fc98fd432720c6a73..8c1df998b56dec3ccdfd8758fa0262a4a76f1fa4 100644 (file)
@@ -45,6 +45,9 @@ extern boost::filesystem::path openssl_path ();
 #ifdef DCPOMATIC_OSX
 extern boost::filesystem::path app_contents ();
 #endif
+#ifdef DCPOMATIC_WINDOWS
+extern void maybe_open_console ();
+#endif
 extern boost::filesystem::path shared_path ();
 extern FILE * fopen_boost (boost::filesystem::path, std::string);
 extern int dcpomatic_fseek (FILE *, int64_t, int);
diff --git a/src/tools/dcpomatic_player.cc b/src/tools/dcpomatic_player.cc
new file mode 100644 (file)
index 0000000..6dede00
--- /dev/null
@@ -0,0 +1,424 @@
+/*
+    Copyright (C) 2017 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic 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 2 of the License, or
+    (at your option) any later version.
+
+    DCP-o-matic 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 DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "lib/cross.h"
+#include "lib/config.h"
+#include "lib/util.h"
+#include "lib/update_checker.h"
+#include "lib/compose.hpp"
+#include "lib/encode_server_finder.h"
+#include "lib/dcp_content.h"
+#include "lib/job_manager.h"
+#include "wx/wx_signal_manager.h"
+#include "wx/wx_util.h"
+#include "wx/about_dialog.h"
+#include "wx/report_problem_dialog.h"
+#include "wx/film_viewer.h"
+#include "wx/update_dialog.h"
+#include <wx/wx.h>
+#include <wx/stdpaths.h>
+#include <wx/splash.h>
+#include <wx/cmdline.h>
+#include <boost/bind.hpp>
+
+using std::string;
+using std::exception;
+using boost::shared_ptr;
+using boost::optional;
+
+enum {
+       ID_file_open = 1,
+       ID_help_report_a_problem,
+       ID_tools_check_for_updates,
+};
+
+class DOMFrame : public wxFrame
+{
+public:
+       DOMFrame ()
+               : wxFrame (0, -1, _("DCP-o-matic Player"))
+               , _update_news_requested (false)
+       {
+
+#if defined(DCPOMATIC_WINDOWS)
+               maybe_open_console ();
+               cout << "DCP-o-matic Player is starting." << "\n";
+#endif
+
+               wxMenuBar* bar = new wxMenuBar;
+               setup_menu (bar);
+               SetMenuBar (bar);
+
+#ifdef DCPOMATIC_WINDOWS
+               SetIcon (wxIcon (std_to_wx ("id")));
+#endif
+
+               Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_open, this), ID_file_open);
+               Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_exit, this), wxID_EXIT);
+               Bind (wxEVT_MENU, boost::bind (&DOMFrame::help_about, this), wxID_ABOUT);
+               Bind (wxEVT_MENU, boost::bind (&DOMFrame::help_report_a_problem, this), ID_help_report_a_problem);
+               Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_check_for_updates, this), ID_tools_check_for_updates);
+
+               /* Use a panel as the only child of the Frame so that we avoid
+                  the dark-grey background on Windows.
+               */
+               wxPanel* overall_panel = new wxPanel (this, wxID_ANY);
+
+               _viewer = new FilmViewer (overall_panel, false, false);
+               wxBoxSizer* main_sizer = new wxBoxSizer (wxHORIZONTAL);
+               main_sizer->Add (_viewer, 1, wxEXPAND | wxALL, 6);
+               overall_panel->SetSizer (main_sizer);
+
+               UpdateChecker::instance()->StateChanged.connect (boost::bind (&DOMFrame::update_checker_state_changed, this));
+       }
+
+       void load_dcp (boost::filesystem::path dir)
+       {
+               _film.reset (new Film (optional<boost::filesystem::path>()));
+               shared_ptr<DCPContent> dcp (new DCPContent (_film, dir));
+               _film->examine_and_add_content (dcp);
+
+               JobManager* jm = JobManager::instance ();
+               while (jm->work_to_do ()) {
+                       /* XXX: progress dialog */
+                       while (signal_manager->ui_idle ()) {}
+                       dcpomatic_sleep (1);
+               }
+
+               /* XXX: report errors */
+
+               _viewer->set_film (_film);
+       }
+
+private:
+
+       void setup_menu (wxMenuBar* m)
+       {
+               wxMenu* file = new wxMenu;
+               file->Append (ID_file_open, _("&Open...\tCtrl-O"));
+
+#ifndef __WXOSX__
+               file->AppendSeparator ();
+#endif
+
+#ifdef __WXOSX__
+               file->Append (wxID_EXIT, _("&Exit"));
+#else
+               file->Append (wxID_EXIT, _("&Quit"));
+#endif
+
+#ifdef __WXOSX__
+               file->Append (wxID_PREFERENCES, _("&Preferences...\tCtrl-P"));
+#else
+               wxMenu* edit = new wxMenu;
+               edit->Append (wxID_PREFERENCES, _("&Preferences...\tCtrl-P"));
+#endif
+
+               wxMenu* tools = new wxMenu;
+               tools->Append (ID_tools_check_for_updates, _("Check for updates"));
+
+               wxMenu* help = new wxMenu;
+#ifdef __WXOSX__
+               help->Append (wxID_ABOUT, _("About DCP-o-matic"));
+#else
+               help->Append (wxID_ABOUT, _("About"));
+#endif
+               help->Append (ID_help_report_a_problem, _("Report a problem..."));
+
+               m->Append (file, _("&File"));
+               m->Append (tools, _("&Tools"));
+               m->Append (help, _("&Help"));
+       }
+
+       void file_open ()
+       {
+               wxDirDialog* c = new wxDirDialog (
+                       this,
+                       _("Select DCP to open"),
+                       wxStandardPaths::Get().GetDocumentsDir(),
+                       wxDEFAULT_DIALOG_STYLE | wxDD_DIR_MUST_EXIST
+                       );
+
+               int r;
+               while (true) {
+                       r = c->ShowModal ();
+                       if (r == wxID_OK && c->GetPath() == wxStandardPaths::Get().GetDocumentsDir()) {
+                               error_dialog (this, _("You did not select a folder.  Make sure that you select a folder before clicking Open."));
+                       } else {
+                               break;
+                       }
+               }
+
+               if (r == wxID_OK) {
+                       load_dcp (wx_to_std (c->GetPath ()));
+               }
+
+               c->Destroy ();
+       }
+
+       void file_exit ()
+       {
+               Close ();
+       }
+
+       void tools_check_for_updates ()
+       {
+               UpdateChecker::instance()->run ();
+               _update_news_requested = true;
+       }
+
+       void help_about ()
+       {
+               AboutDialog* d = new AboutDialog (this);
+               d->ShowModal ();
+               d->Destroy ();
+       }
+
+       void help_report_a_problem ()
+       {
+               ReportProblemDialog* d = new ReportProblemDialog (this);
+               if (d->ShowModal () == wxID_OK) {
+                       d->report ();
+               }
+               d->Destroy ();
+       }
+
+       void update_checker_state_changed ()
+       {
+               UpdateChecker* uc = UpdateChecker::instance ();
+
+               bool const announce =
+                       _update_news_requested ||
+                       (uc->stable() && Config::instance()->check_for_updates()) ||
+                       (uc->test() && Config::instance()->check_for_updates() && Config::instance()->check_for_test_updates());
+
+               _update_news_requested = false;
+
+               if (!announce) {
+                       return;
+               }
+
+               if (uc->state() == UpdateChecker::YES) {
+                       UpdateDialog* dialog = new UpdateDialog (this, uc->stable (), uc->test ());
+                       dialog->ShowModal ();
+                       dialog->Destroy ();
+               } else if (uc->state() == UpdateChecker::FAILED) {
+                       error_dialog (this, _("The DCP-o-matic download server could not be contacted."));
+               } else {
+                       error_dialog (this, _("There are no new versions of DCP-o-matic available."));
+               }
+
+               _update_news_requested = false;
+       }
+
+       bool _update_news_requested;
+       FilmViewer* _viewer;
+       boost::shared_ptr<Film> _film;
+};
+
+static const wxCmdLineEntryDesc command_line_description[] = {
+       { wxCMD_LINE_PARAM, 0, 0, "DCP to load or create", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
+       { wxCMD_LINE_NONE, "", "", "", wxCmdLineParamType (0), 0 }
+};
+
+/** @class App
+ *  @brief The magic App class for wxWidgets.
+ */
+class App : public wxApp
+{
+public:
+       App ()
+               : wxApp ()
+               , _frame (0)
+       {}
+
+private:
+
+       bool OnInit ()
+       try
+       {
+               wxInitAllImageHandlers ();
+
+               Config::FailedToLoad.connect (boost::bind (&App::config_failed_to_load, this));
+
+               wxSplashScreen* splash = 0;
+               try {
+                       if (!Config::have_existing ("config.xml")) {
+                               wxBitmap bitmap;
+                               boost::filesystem::path p = shared_path () / "splash.png";
+                               if (bitmap.LoadFile (std_to_wx (p.string ()), wxBITMAP_TYPE_PNG)) {
+                                       splash = new wxSplashScreen (bitmap, wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_NO_TIMEOUT, 0, 0, -1);
+                                       wxYield ();
+                               }
+                       }
+               } catch (boost::filesystem::filesystem_error& e) {
+                       /* Maybe we couldn't find the splash image; never mind */
+               }
+
+               SetAppName (_("DCP-o-matic Player"));
+
+               if (!wxApp::OnInit()) {
+                       return false;
+               }
+
+#ifdef DCPOMATIC_LINUX
+               unsetenv ("UBUNTU_MENUPROXY");
+#endif
+
+#ifdef __WXOSX__
+               ProcessSerialNumber serial;
+               GetCurrentProcess (&serial);
+               TransformProcessType (&serial, kProcessTransformToForegroundApplication);
+#endif
+
+               dcpomatic_setup_path_encoding ();
+
+               /* Enable i18n; this will create a Config object
+                  to look for a force-configured language.  This Config
+                  object will be wrong, however, because dcpomatic_setup
+                  hasn't yet been called and there aren't any filters etc.
+                  set up yet.
+               */
+               dcpomatic_setup_i18n ();
+
+               /* Set things up, including filters etc.
+                  which will now be internationalised correctly.
+               */
+               dcpomatic_setup ();
+
+               /* Force the configuration to be re-loaded correctly next
+                  time it is needed.
+               */
+               Config::drop ();
+
+               _frame = new DOMFrame ();
+               SetTopWindow (_frame);
+               _frame->Maximize ();
+               if (splash) {
+                       splash->Destroy ();
+               }
+               _frame->Show ();
+
+               if (!_dcp_to_load.empty() && boost::filesystem::is_directory (_dcp_to_load)) {
+                       try {
+                               _frame->load_dcp (_dcp_to_load);
+                       } catch (exception& e) {
+                               error_dialog (0, std_to_wx (String::compose (wx_to_std (_("Could not load DCP %1 (%2)")), _dcp_to_load, e.what())));
+                       }
+               }
+
+               signal_manager = new wxSignalManager (this);
+               Bind (wxEVT_IDLE, boost::bind (&App::idle, this));
+
+               Bind (wxEVT_TIMER, boost::bind (&App::check, this));
+               _timer.reset (new wxTimer (this));
+               _timer->Start (1000);
+
+               if (Config::instance()->check_for_updates ()) {
+                       UpdateChecker::instance()->run ();
+               }
+
+               return true;
+       }
+       catch (exception& e)
+       {
+               error_dialog (0, wxString::Format ("DCP-o-matic Player could not start: %s", e.what ()));
+               return true;
+       }
+
+       void OnInitCmdLine (wxCmdLineParser& parser)
+       {
+               parser.SetDesc (command_line_description);
+               parser.SetSwitchChars (wxT ("-"));
+       }
+
+       bool OnCmdLineParsed (wxCmdLineParser& parser)
+       {
+               if (parser.GetParamCount() > 0) {
+                       _dcp_to_load = wx_to_std (parser.GetParam (0));
+               }
+
+               return true;
+       }
+
+       void report_exception ()
+       {
+               try {
+                       throw;
+               } catch (FileError& e) {
+                       error_dialog (
+                               0,
+                               wxString::Format (
+                                       _("An exception occurred: %s (%s)\n\n") + REPORT_PROBLEM,
+                                       std_to_wx (e.what()),
+                                       std_to_wx (e.file().string().c_str ())
+                                       )
+                               );
+               } catch (exception& e) {
+                       error_dialog (
+                               0,
+                               wxString::Format (
+                                       _("An exception occurred: %s.\n\n") + REPORT_PROBLEM,
+                                       std_to_wx (e.what ())
+                                       )
+                               );
+               } catch (...) {
+                       error_dialog (0, _("An unknown exception occurred.") + "  " + REPORT_PROBLEM);
+               }
+       }
+
+       /* An unhandled exception has occurred inside the main event loop */
+       bool OnExceptionInMainLoop ()
+       {
+               report_exception ();
+               /* This will terminate the program */
+               return false;
+       }
+
+       void OnUnhandledException ()
+       {
+               report_exception ();
+       }
+
+       void idle ()
+       {
+               signal_manager->ui_idle ();
+       }
+
+       void check ()
+       {
+               try {
+                       EncodeServerFinder::instance()->rethrow ();
+               } catch (exception& e) {
+                       error_dialog (0, std_to_wx (e.what ()));
+               }
+       }
+
+       void config_failed_to_load ()
+       {
+               message_dialog (_frame, _("The existing configuration failed to load.  Default values will be used instead.  These may take a short time to create."));
+       }
+
+       DOMFrame* _frame;
+       shared_ptr<wxTimer> _timer;
+       string _dcp_to_load;
+};
+
+IMPLEMENT_APP (App)
index 00f3123c66115bfec4dbb1c77eb3b3b567d84b6a..8b24bb0432201181d67af46a6c9a2e9c279b5d03 100644 (file)
@@ -46,7 +46,7 @@ def build(bld):
             obj.install_path = None
 
     if not bld.env.DISABLE_GUI:
-        for t in ['dcpomatic', 'dcpomatic_batch', 'dcpomatic_server', 'dcpomatic_kdm']:
+        for t in ['dcpomatic', 'dcpomatic_batch', 'dcpomatic_server', 'dcpomatic_kdm', 'dcpomatic_player']:
             obj = bld(features='cxx cxxprogram')
             obj.uselib = uselib
             if bld.env.BUILD_STATIC or bld.env.TARGET_LINUX:
index 0299b24626fd803c505b567b2d8e0338c2bb4d79..776926fbefed8b3682e1e8f0a1e738f1cd1b173c 100644 (file)
@@ -72,7 +72,7 @@ rtaudio_callback (void* out, void *, unsigned int frames, double, RtAudioStreamS
        return reinterpret_cast<FilmViewer*>(data)->audio_callback (out, frames);
 }
 
-FilmViewer::FilmViewer (wxWindow* p)
+FilmViewer::FilmViewer (wxWindow* p, bool outline_content, bool jump_to_selected)
        : wxPanel (p)
        , _panel (new wxPanel (this))
        , _outline_content (new wxCheckBox (this, wxID_ANY, _("Outline content")))
@@ -106,10 +106,14 @@ FilmViewer::FilmViewer (wxWindow* p)
        _v_sizer->Add (_panel, 1, wxEXPAND);
 
        wxBoxSizer* view_options = new wxBoxSizer (wxHORIZONTAL);
-       view_options->Add (_outline_content, 0, wxRIGHT, DCPOMATIC_SIZER_GAP);
+       if (outline_content) {
+               view_options->Add (_outline_content, 0, wxRIGHT, DCPOMATIC_SIZER_GAP);
+       }
        view_options->Add (_left_eye, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_GAP);
        view_options->Add (_right_eye, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_GAP);
-       view_options->Add (_jump_to_selected, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_GAP);
+       if (jump_to_selected) {
+               view_options->Add (_jump_to_selected, 0, wxLEFT | wxRIGHT, DCPOMATIC_SIZER_GAP);
+       }
        _v_sizer->Add (view_options, 0, wxALL, DCPOMATIC_SIZER_GAP);
 
        wxBoxSizer* h_sizer = new wxBoxSizer (wxHORIZONTAL);
index 4a704ae6c26c217d5aa035c94596bdb9fc355833..a411be5ec5f14a86d040df633262075f7d4cfe58 100644 (file)
@@ -41,7 +41,7 @@ class Butler;
 class FilmViewer : public wxPanel
 {
 public:
-       FilmViewer (wxWindow *);
+       FilmViewer (wxWindow *, bool outline_content = true, bool jump_to_selected = true);
        ~FilmViewer ();
 
        void set_film (boost::shared_ptr<Film>);
index a13c6a671a6fd36fce8d47a778933fb54edb78e3..9bd70a50e7c73226f44dc644b53814de63c9dcec 100644 (file)
@@ -29,7 +29,7 @@ class Film;
 class ReportProblemDialog : public wxDialog
 {
 public:
-       ReportProblemDialog (wxWindow* parent, boost::shared_ptr<Film> film);
+       ReportProblemDialog (wxWindow* parent, boost::shared_ptr<Film> film = boost::shared_ptr<Film>());
 
        void report ();