_player_watermark_theatre = f.optional_string_child("PlayerWatermarkTheatre").get_value_or("");
_player_watermark_period = f.optional_number_child<int>("PlayerWatermarkPeriod").get_value_or(1);
_player_watermark_duration = f.optional_number_child<int>("PlayerWatermarkDuration").get_value_or(150);
+ BOOST_FOREACH (cxml::ConstNodePtr i, f.node_children("RequiredMonitor")) {
+ _required_monitors.push_back(Monitor(i));
+ }
#endif
/* Replace any cinemas from config.xml with those from the configured file */
root->add_child("PlayerWatermarkTheatre")->add_child_text(_player_watermark_theatre);
root->add_child("PlayerWatermarkPeriod")->add_child_text(raw_convert<string>(_player_watermark_period));
root->add_child("PlayerWatermarkDuration")->add_child_text(raw_convert<string>(_player_watermark_duration));
+ BOOST_FOREACH (Monitor i, _required_monitors) {
+ i.as_xml(root->add_child("RequiredMonitor"));
+ }
#endif
try {
#include "isdcf_metadata.h"
#include "types.h"
+#include "edid.h"
#include <dcp/name_format.h>
#include <dcp/certificate_chain.h>
#include <dcp/encrypted_kdm.h>
int player_watermark_duration () const {
return _player_watermark_duration;
}
+
+ std::vector<Monitor> required_monitors () const {
+ return _required_monitors;
+ }
#endif
/* SET (mostly) */
void set_player_watermark_duration (int milliseconds) {
maybe_set (_player_watermark_duration, milliseconds);
}
+
+ void set_required_monitors (std::vector<Monitor> monitors) {
+ maybe_set (_required_monitors, monitors);
+ }
#endif
void changed (Property p = OTHER);
int _player_watermark_period;
/** watermark duration in milliseconds */
int _player_watermark_duration;
+ std::vector<Monitor> _required_monitors;
#endif
static int const _current_version;
--- /dev/null
+/*
+ Copyright (C) 2018 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 "compose.hpp"
+#include "edid.h"
+#include <dcp/raw_convert.h>
+#include <libxml++/libxml++.h>
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string.hpp>
+#include <iostream>
+
+#define EDID_SYS_PATH "/sys/class/drm"
+static uint8_t const edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
+
+using std::vector;
+using std::string;
+using std::cout;
+
+vector<Monitor>
+get_monitors()
+{
+ using namespace boost::filesystem;
+ using namespace boost::algorithm;
+
+ vector<Monitor> monitors;
+
+ int card = 0;
+ while (true) {
+ path card_dir = String::compose("%1/card%2", EDID_SYS_PATH, card);
+ if (!is_directory(card_dir)) {
+ break;
+ }
+
+ for (directory_iterator i = directory_iterator(card_dir); i != directory_iterator(); ++i) {
+ if (!starts_with(i->path().filename().string(), String::compose("card%1", card))) {
+ continue;
+ }
+
+ FILE* edid_file = fopen(path(i->path() / "edid").string().c_str(), "r");
+ if (!edid_file) {
+ continue;
+ }
+
+ uint8_t edid[128];
+ int const N = fread(edid, 1, sizeof(edid), edid_file);
+ fclose(edid_file);
+ if (N != 128) {
+ continue;
+ }
+
+ if (memcmp(edid, edid_header, 8) != 0) {
+ continue;
+ }
+
+ Monitor mon;
+
+ uint16_t mid = (edid[8] << 8) | edid[9];
+ mon.manufacturer_id += char(((mid >> 10) & 0x1f) + 'A' - 1);
+ mon.manufacturer_id += char(((mid >> 5) & 0x1f) + 'A' - 1);
+ mon.manufacturer_id += char(((mid >> 0) & 0x1f) + 'A' - 1);
+
+ mon.manufacturer_product_code = (edid[11] << 8) | edid[10];
+
+ mon.serial_number = (edid[15] << 24) | (edid[14] << 16) | (edid[13] << 8) | edid[12];
+ mon.week_of_manufacture = edid[16];
+ mon.year_of_manufacture = edid[17];
+ monitors.push_back (mon);
+ }
+
+ ++card;
+ }
+
+ return monitors;
+}
+
+Monitor::Monitor ()
+ : manufacturer_product_code (0)
+ , serial_number (0)
+ , week_of_manufacture (0)
+ , year_of_manufacture (0)
+{
+
+}
+
+Monitor::Monitor (cxml::ConstNodePtr node)
+ : manufacturer_id(node->string_child("ManufacturerId"))
+ , manufacturer_product_code(node->number_child<uint32_t>("ManufacturerProductCode"))
+ , serial_number(node->number_child<uint32_t>("SerialNumber"))
+ , week_of_manufacture(node->number_child<int>("WeekOfManufacture"))
+ , year_of_manufacture(node->number_child<int>("YearOfManufacture"))
+
+{
+
+}
+
+void
+Monitor::as_xml (xmlpp::Node* parent) const
+{
+ parent->add_child("ManufacturerId")->add_child_text(manufacturer_id);
+ parent->add_child("ManufacturerProductCode")->add_child_text(dcp::raw_convert<string>(manufacturer_product_code));
+ parent->add_child("SerialNumber")->add_child_text(dcp::raw_convert<string>(serial_number));
+ parent->add_child("WeekOfManufacture")->add_child_text(dcp::raw_convert<string>(week_of_manufacture));
+ parent->add_child("YearOfManufacture")->add_child_text(dcp::raw_convert<string>(year_of_manufacture));
+}
+
+bool
+operator== (Monitor const & a, Monitor const & b)
+{
+ return a.manufacturer_id == b.manufacturer_id &&
+ a.manufacturer_product_code == b.manufacturer_product_code &&
+ a.serial_number == b.serial_number &&
+ a.week_of_manufacture == b.week_of_manufacture &&
+ a.year_of_manufacture == b.year_of_manufacture;
+}
--- /dev/null
+/*
+ Copyright (C) 2018 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/>.
+
+*/
+
+#ifndef DCPOMATIC_EDID_H
+#define DCPOMATIC_EDID_H
+
+#include <libcxml/cxml.h>
+#include <vector>
+#include <string>
+
+namespace xmlpp {
+ class Node;
+}
+
+class Monitor
+{
+public:
+ Monitor ();
+ Monitor (cxml::ConstNodePtr node);
+ void as_xml (xmlpp::Node* parent) const;
+
+ std::string manufacturer_id;
+ uint16_t manufacturer_product_code;
+ uint32_t serial_number;
+ uint8_t week_of_manufacture;
+ uint8_t year_of_manufacture;
+};
+
+bool operator== (Monitor const & a, Monitor const & b);
+
+extern std::vector<Monitor> get_monitors();
+
+#endif
--- /dev/null
+/*
+ Copyright (C) 2018 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 "monitor_checker.h"
+#include "config.h"
+#include "cross.h"
+
+using boost::bind;
+using boost::ref;
+
+MonitorChecker* MonitorChecker::_instance = 0;
+
+MonitorChecker::MonitorChecker ()
+ : _thread (0)
+ , _terminate (false)
+ , _ok (true)
+{
+
+}
+
+void
+MonitorChecker::run ()
+{
+ _thread = new boost::thread (boost::bind (&MonitorChecker::thread, this));
+}
+
+MonitorChecker::~MonitorChecker ()
+{
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ _terminate = true;
+ }
+
+ if (_thread) {
+ /* Ideally this would be a DCPOMATIC_ASSERT(_thread->joinable()) but we
+ can't throw exceptions from a destructor.
+ */
+ _thread->interrupt ();
+ try {
+ if (_thread->joinable ()) {
+ _thread->join ();
+ }
+ } catch (boost::thread_interrupted& e) {
+ /* No problem */
+ }
+ }
+ delete _thread;
+}
+
+void
+MonitorChecker::thread ()
+{
+ while (true) {
+ boost::mutex::scoped_lock lm (_mutex);
+ if (_terminate) {
+ break;
+ }
+
+ bool const was_ok = _ok;
+ _ok = Config::instance()->required_monitors().empty() || get_monitors() == Config::instance()->required_monitors();
+ if (was_ok != _ok) {
+ emit (bind(boost::ref(StateChanged)));
+ }
+
+ lm.unlock ();
+ dcpomatic_sleep (60);
+ }
+}
+
+bool
+MonitorChecker::ok () const
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ return _ok;
+}
+
+MonitorChecker *
+MonitorChecker::instance ()
+{
+ if (!_instance) {
+ _instance = new MonitorChecker ();
+ }
+
+ return _instance;
+}
--- /dev/null
+/*
+ Copyright (C) 2018 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 "signaller.h"
+#include <boost/signals2.hpp>
+
+class MonitorChecker : public Signaller, public boost::noncopyable
+{
+public:
+ ~MonitorChecker ();
+
+ void run ();
+
+ bool ok () const;
+ boost::signals2::signal<void (void)> StateChanged;
+
+ static MonitorChecker* instance ();
+private:
+ static MonitorChecker* _instance;
+
+ MonitorChecker ();
+ void thread ();
+
+ boost::thread* _thread;
+ mutable boost::mutex _mutex;
+ bool _terminate;
+ bool _ok;
+};
digester.cc
dkdm_wrapper.cc
dolby_cp750.cc
+ edid.cc
emailer.cc
empty.cc
encoder.cc
log_entry.cc
magick_image_proxy.cc
mid_side_decoder.cc
+ monitor_checker.cc
overlaps.cc
player.cc
player_text.cc
#include "lib/server.h"
#include "lib/dcpomatic_socket.h"
#include "lib/scoped_temporary.h"
+#include "lib/monitor_checker.h"
#include <dcp/dcp.h>
#include <wx/wx.h>
#include <wx/stdpaths.h>
UpdateChecker::instance()->StateChanged.connect (boost::bind (&DOMFrame::update_checker_state_changed, this));
_controls->SPLChanged.connect (boost::bind(&DOMFrame::set_spl, this, _1));
-
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+ MonitorChecker::instance()->StateChanged.connect(boost::bind(&DOMFrame::monitor_checker_state_changed, this));
+ MonitorChecker::instance()->run ();
+#endif
setup_screen ();
}
+ void monitor_checker_state_changed ()
+ {
+ if (!MonitorChecker::instance()->ok()) {
+ error_dialog (this, _("The required display devices are not connected correctly."));
+ _viewer->stop ();
+ }
+ }
+
void setup_main_sizer (Config::PlayerMode mode)
{
wxSizer* main_sizer = new wxBoxSizer (wxVERTICAL);
bool playback_permitted ()
{
+#ifdef DCPOMATIC_VARIANT_SWAROOP
+ if (!MonitorChecker::instance()->ok()) {
+ error_dialog (this, _("The required display devices are not connected correctly."));
+ return false;
+ }
+#endif
if (!_film || !Config::instance()->respect_kdm_validity_periods()) {
return true;
}
boost::function<void (std::vector<T>)> set,
boost::function<std::string (T, int)> column,
bool can_edit = true,
- bool title = true
+ bool title = true,
+ int column_width = 200
)
: wxPanel (parent)
, _get (get)
if (title) {
style |= wxLC_NO_HEADER;
}
- _list = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (columns.size() * 200, 100), style);
+ _list = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (columns.size() * column_width, 100), style);
for (size_t i = 0; i < columns.size(); ++i) {
wxListItem ip;
ip.SetId (i);
ip.SetText (std_to_wx (columns[i]));
- ip.SetWidth (200);
+ ip.SetWidth (column_width);
_list->InsertColumn (i, ip);
}
--- /dev/null
+/*
+ Copyright (C) 2018 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 "monitor_dialog.h"
+#include "wx_util.h"
+#include "lib/encode_server.h"
+#include <dcp/locale_convert.h>
+
+using std::string;
+using dcp::locale_convert;
+using boost::shared_ptr;
+using boost::optional;
+
+MonitorDialog::MonitorDialog (wxWindow* parent)
+ : TableDialog (parent, _("Device"), 2, 1, true)
+{
+ add (_("Manufacturer ID"), true);
+ _manufacturer_id = add (new wxTextCtrl(this, wxID_ANY, wxT("")));
+ add (_("Manufacturer product code"), true);
+ _manufacturer_product_code = add (new wxTextCtrl(this, wxID_ANY, wxT("")));
+ add (_("Serial number"), true);
+ _serial_number = add (new wxTextCtrl(this, wxID_ANY, wxT("")));
+ add (_("Week of manufacture"), true);
+ _week_of_manufacture = add (new wxTextCtrl(this, wxID_ANY, wxT("")));
+ add (_("Year of manufacture"), true);
+ _year_of_manufacture = add (new wxTextCtrl(this, wxID_ANY, wxT("")));
+
+ layout ();
+}
+
+void
+MonitorDialog::set (Monitor monitor)
+{
+ _manufacturer_id->SetValue (std_to_wx(monitor.manufacturer_id));
+ _manufacturer_product_code->SetValue (std_to_wx(locale_convert<string>(monitor.manufacturer_product_code)));
+ _serial_number->SetValue (std_to_wx(locale_convert<string>(monitor.serial_number)));
+ _week_of_manufacture->SetValue (std_to_wx(locale_convert<string>(monitor.week_of_manufacture)));
+ _year_of_manufacture->SetValue (std_to_wx(locale_convert<string>(monitor.year_of_manufacture)));
+}
+
+optional<Monitor>
+MonitorDialog::get () const
+{
+ Monitor m;
+ m.manufacturer_id = wx_to_std (_manufacturer_id->GetValue());
+ m.manufacturer_product_code = locale_convert<uint16_t>(wx_to_std(_manufacturer_product_code->GetValue()));
+ m.serial_number = locale_convert<uint32_t>(wx_to_std(_serial_number->GetValue()));
+ m.week_of_manufacture = locale_convert<uint8_t>(wx_to_std(_week_of_manufacture->GetValue()));
+ m.year_of_manufacture = locale_convert<uint8_t>(wx_to_std (_year_of_manufacture->GetValue()));
+ return m;
+}
--- /dev/null
+/*
+ Copyright (C) 2012 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 "table_dialog.h"
+#include "lib/edid.h"
+#include <boost/optional.hpp>
+
+class MonitorDialog : public TableDialog
+{
+public:
+ explicit MonitorDialog (wxWindow *);
+
+ void set (Monitor);
+ boost::optional<Monitor> get () const;
+
+private:
+ wxTextCtrl* _manufacturer_id;
+ wxTextCtrl* _manufacturer_product_code;
+ wxTextCtrl* _serial_number;
+ wxTextCtrl* _week_of_manufacture;
+ wxTextCtrl* _year_of_manufacture;
+};
#include "email_dialog.h"
#include "name_format_editor.h"
#include "nag_dialog.h"
-#include "config_dialog.h"
+#include "monitor_dialog.h"
#include "lib/config.h"
#include "lib/ratio.h"
#include "lib/filter.h"
wxSpinCtrl* _period;
wxSpinCtrl* _duration;
};
+
+class DevicesPage : public StandardPage
+{
+public:
+ DevicesPage (wxSize panel_size, int border)
+ : StandardPage (panel_size, border)
+ {}
+
+ wxString GetName () const
+ {
+ return _("Devices");
+ }
+
+private:
+ void setup ()
+ {
+ vector<string> columns;
+ columns.push_back(wx_to_std(_("Manufacturer ID")));
+ columns.push_back(wx_to_std(_("Product code")));
+ columns.push_back(wx_to_std(_("Serial")));
+ columns.push_back(wx_to_std(_("Manufacture week")));
+ columns.push_back(wx_to_std(_("Manufacture year")));
+ _monitor_list = new EditableList<Monitor, MonitorDialog> (
+ _panel,
+ columns,
+ boost::bind (&Config::required_monitors, Config::instance()),
+ boost::bind (&Config::set_required_monitors, Config::instance(), _1),
+ boost::bind (&DevicesPage::monitor_column, this, _1, _2),
+ true,
+ true,
+ 100
+ );
+ _panel->GetSizer()->Add(_monitor_list, 1, wxEXPAND | wxALL, _border);
+
+ wxButton* get = new wxButton(_panel, wxID_ANY, _("Read current devices"));
+ _panel->GetSizer()->Add(get, 0, wxEXPAND | wxALL, DCPOMATIC_SIZER_GAP);
+ get->Bind(wxEVT_BUTTON, bind(&DevicesPage::get_clicked, this));
+ }
+
+ void get_clicked ()
+ {
+ Config::instance()->set_required_monitors(get_monitors());
+ _monitor_list->refresh ();
+ }
+
+ string monitor_column (Monitor m, int c)
+ {
+ switch (c) {
+ case 0:
+ return m.manufacturer_id;
+ case 1:
+ return locale_convert<string>(m.manufacturer_product_code);
+ case 2:
+ return locale_convert<string>(m.serial_number);
+ case 3:
+ return locale_convert<string>(m.week_of_manufacture);
+ case 4:
+ return locale_convert<string>(m.year_of_manufacture);
+ default:
+ DCPOMATIC_ASSERT(false);
+ }
+
+ return "";
+ }
+
+ void config_changed ()
+ {
+ _monitor_list->refresh ();
+ }
+
+private:
+ EditableList<Monitor, MonitorDialog>* _monitor_list;
+};
+
#endif
wxPreferencesEditor*
int const border = 8;
#endif
- e->AddPage (new PlayerGeneralPage (ps, border));
- e->AddPage (new KeysPage (ps, border));
+ e->AddPage (new PlayerGeneralPage(ps, border));
+ e->AddPage (new KeysPage(ps, border));
#ifdef DCPOMATIC_VARIANT_SWAROOP
- e->AddPage (new WatermarkPage (ps, border));
+ e->AddPage (new WatermarkPage(ps, border));
+ e->AddPage (new DevicesPage(ps, border));
#endif
return e;
}
key_dialog.cc
make_chain_dialog.cc
message_dialog.cc
+ monitor_dialog.cc
move_to_dialog.cc
nag_dialog.cc
name_format_editor.cc