37fcc34cdadc91603f6feb52e3cb8c2ebfe76490
[dcpomatic.git] / src / tools / dcpomatic_server.cc
1 /*
2     Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include "wx/wx_util.h"
21 #include "wx/wx_signal_manager.h"
22 #include "lib/util.h"
23 #include "lib/server.h"
24 #include "lib/config.h"
25 #include "lib/log.h"
26 #include "lib/signaller.h"
27 #include <wx/taskbar.h>
28 #include <wx/icon.h>
29 #include <boost/thread.hpp>
30 #include <boost/foreach.hpp>
31 #include <iostream>
32
33 using std::cout;
34 using std::string;
35 using std::exception;
36 using std::list;
37 using boost::shared_ptr;
38 using boost::thread;
39 using boost::bind;
40
41 enum {
42         ID_status = 1,
43         ID_quit,
44         ID_timer
45 };
46
47 static int const log_lines = 32;
48
49 class MemoryLog : public Log, public Signaller
50 {
51 public:
52
53         string get () const {
54                 string a;
55                 BOOST_FOREACH (string const & i, _log) {
56                         a += i + "\n";
57                 }
58                 return a;
59         }
60
61         string head_and_tail (int) const {
62                 /* Not necessary */
63                 return "";
64         }
65
66         boost::signals2::signal<void(string)> Appended;
67         boost::signals2::signal<void(int)> Removed;
68
69 private:
70         void do_log (string m)
71         {
72                 _log.push_back (m);
73                 emit (boost::bind (boost::ref (Appended), m));
74                 if (_log.size() > log_lines) {
75                         emit (boost::bind (boost::ref (Removed), _log.front().length()));
76                         _log.pop_front ();
77                 }
78         }
79
80         list<string> _log;
81 };
82
83 static shared_ptr<MemoryLog> memory_log (new MemoryLog);
84
85 class StatusDialog : public wxDialog
86 {
87 public:
88         StatusDialog ()
89                 : wxDialog (
90                         0, wxID_ANY, _("DCP-o-matic encode server"),
91                         wxDefaultPosition, wxDefaultSize,
92                         wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER
93                         )
94         {
95                 _sizer = new wxFlexGridSizer (1, DCPOMATIC_SIZER_GAP, DCPOMATIC_SIZER_GAP);
96                 _sizer->AddGrowableCol (0, 1);
97
98                 wxClientDC dc (this);
99                 wxSize size = dc.GetTextExtent (wxT ("This is the length of the file label it should be quite long"));
100                 int const height = (size.GetHeight() + 2) * log_lines;
101                 SetSize (700, height + DCPOMATIC_SIZER_GAP * 2);
102
103                 _text = new wxTextCtrl (
104                         this, wxID_ANY, std_to_wx (memory_log->get()), wxDefaultPosition, wxSize (-1, height),
105                         wxTE_READONLY | wxTE_MULTILINE
106                         );
107
108                 _sizer->Add (_text, 1, wxEXPAND | wxALL, DCPOMATIC_SIZER_GAP);
109
110                 SetSizer (_sizer);
111                 _sizer->Layout ();
112
113                 memory_log->Appended.connect (bind (&StatusDialog::appended, this, _1));
114                 memory_log->Removed.connect (bind (&StatusDialog::removed, this, _1));
115         }
116
117 private:
118         void appended (string s)
119         {
120                 (*_text) << s << "\n";
121         }
122
123         void removed (int n)
124         {
125                 _text->Remove (0, n + 1);
126         }
127
128         wxFlexGridSizer* _sizer;
129         wxTextCtrl* _text;
130 };
131
132 class TaskBarIcon : public wxTaskBarIcon
133 {
134 public:
135         TaskBarIcon ()
136                 : _status (0)
137         {
138 #ifdef DCPOMATIC_WINDOWS
139                 wxIcon icon (std_to_wx ("taskbar_icon"));
140 #else
141                 wxInitAllImageHandlers();
142                 wxBitmap bitmap (wxString::Format (wxT ("%s/dcpomatic2_server_small.png"), LINUX_SHARE_PREFIX), wxBITMAP_TYPE_PNG);
143                 wxIcon icon;
144                 icon.CopyFromBitmap (bitmap);
145 #endif
146
147                 SetIcon (icon, std_to_wx ("DCP-o-matic encode server"));
148
149                 Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&TaskBarIcon::status, this), ID_status);
150                 Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&TaskBarIcon::quit, this), ID_quit);
151         }
152
153         wxMenu* CreatePopupMenu ()
154         {
155                 wxMenu* menu = new wxMenu;
156                 menu->Append (ID_status, std_to_wx ("Status..."));
157                 menu->Append (ID_quit, std_to_wx ("Quit"));
158                 return menu;
159         }
160
161 private:
162         void status ()
163         {
164                 if (!_status) {
165                         _status = new StatusDialog ();
166                 }
167                 _status->Show ();
168         }
169
170         void quit ()
171         {
172                 wxTheApp->ExitMainLoop ();
173         }
174
175         StatusDialog* _status;
176 };
177
178 class App : public wxApp, public ExceptionStore
179 {
180 public:
181         App ()
182                 : wxApp ()
183                 , _thread (0)
184                 , _icon (0)
185         {}
186
187 private:
188
189         bool OnInit ()
190         {
191                 if (!wxApp::OnInit ()) {
192                         return false;
193                 }
194
195                 dcpomatic_setup_path_encoding ();
196                 dcpomatic_setup_i18n ();
197                 dcpomatic_setup ();
198                 Config::drop ();
199
200                 signal_manager = new wxSignalManager (this);
201                 Bind (wxEVT_IDLE, boost::bind (&App::idle, this));
202
203                 _icon = new TaskBarIcon;
204                 _thread = new thread (bind (&App::main_thread, this));
205
206                 Bind (wxEVT_TIMER, boost::bind (&App::check, this));
207                 _timer.reset (new wxTimer (this));
208                 _timer->Start (1000);
209
210                 return true;
211         }
212
213         int OnExit ()
214         {
215                 delete _icon;
216                 return wxApp::OnExit ();
217         }
218
219         void main_thread ()
220         try {
221                 Server server (memory_log, false);
222                 server.run (Config::instance()->num_local_encoding_threads ());
223         } catch (...) {
224                 store_current ();
225         }
226
227         void check ()
228         {
229                 try {
230                         rethrow ();
231                 } catch (exception& e) {
232                         error_dialog (0, std_to_wx (e.what ()));
233                         wxTheApp->ExitMainLoop ();
234                 } catch (...) {
235                         error_dialog (0, _("An unknown error has occurred with the DCP-o-matic server."));
236                         wxTheApp->ExitMainLoop ();
237                 }
238         }
239
240         void idle ()
241         {
242                 signal_manager->ui_idle ();
243         }
244
245         boost::thread* _thread;
246         TaskBarIcon* _icon;
247         shared_ptr<wxTimer> _timer;
248 };
249
250 IMPLEMENT_APP (App)