From 86765a617035e0283c20c9f2696909743e618156 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 2 Aug 2017 17:25:38 +0100 Subject: [PATCH] Stub player. --- run/dcpomatic_player | 25 ++ src/lib/cross.cc | 22 ++ src/lib/cross.h | 3 + src/tools/dcpomatic_player.cc | 424 +++++++++++++++++++++++++++++++++ src/tools/wscript | 2 +- src/wx/film_viewer.cc | 10 +- src/wx/film_viewer.h | 2 +- src/wx/report_problem_dialog.h | 2 +- 8 files changed, 484 insertions(+), 6 deletions(-) create mode 100755 run/dcpomatic_player create mode 100644 src/tools/dcpomatic_player.cc diff --git a/run/dcpomatic_player b/run/dcpomatic_player new file mode 100755 index 000000000..9f2883403 --- /dev/null +++ b/run/dcpomatic_player @@ -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 diff --git a/src/lib/cross.cc b/src/lib/cross.cc index 831a1a2b2..e21401973 100644 --- a/src/lib/cross.cc +++ b/src/lib/cross.cc @@ -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 diff --git a/src/lib/cross.h b/src/lib/cross.h index 02839eb68..8c1df998b 100644 --- a/src/lib/cross.h +++ b/src/lib/cross.h @@ -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 index 000000000..6dede00ca --- /dev/null +++ b/src/tools/dcpomatic_player.cc @@ -0,0 +1,424 @@ +/* + Copyright (C) 2017 Carl Hetherington + + 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 . + +*/ + +#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 +#include +#include +#include +#include + +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())); + shared_ptr 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; +}; + +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 _timer; + string _dcp_to_load; +}; + +IMPLEMENT_APP (App) diff --git a/src/tools/wscript b/src/tools/wscript index 00f3123c6..8b24bb043 100644 --- a/src/tools/wscript +++ b/src/tools/wscript @@ -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: diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 0299b2462..776926fbe 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -72,7 +72,7 @@ rtaudio_callback (void* out, void *, unsigned int frames, double, RtAudioStreamS return reinterpret_cast(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); diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index 4a704ae6c..a411be5ec 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -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); diff --git a/src/wx/report_problem_dialog.h b/src/wx/report_problem_dialog.h index a13c6a671..9bd70a50e 100644 --- a/src/wx/report_problem_dialog.h +++ b/src/wx/report_problem_dialog.h @@ -29,7 +29,7 @@ class Film; class ReportProblemDialog : public wxDialog { public: - ReportProblemDialog (wxWindow* parent, boost::shared_ptr film); + ReportProblemDialog (wxWindow* parent, boost::shared_ptr film = boost::shared_ptr()); void report (); -- 2.30.2