Don't create status dialog until it's needed, otherwise we don't get an icon on Linux.
[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                 _status = new StatusDialog ();
165                 _status->Show ();
166         }
167
168         void quit ()
169         {
170                 wxTheApp->ExitMainLoop ();
171         }
172
173         StatusDialog* _status;
174 };
175
176 class App : public wxApp, public ExceptionStore
177 {
178 public:
179         App ()
180                 : wxApp ()
181                 , _thread (0)
182                 , _icon (0)
183         {}
184
185 private:
186
187         bool OnInit ()
188         {
189                 if (!wxApp::OnInit ()) {
190                         return false;
191                 }
192
193                 dcpomatic_setup_path_encoding ();
194                 dcpomatic_setup_i18n ();
195                 dcpomatic_setup ();
196                 Config::drop ();
197
198                 signal_manager = new wxSignalManager (this);
199                 Bind (wxEVT_IDLE, boost::bind (&App::idle, this));
200
201                 _icon = new TaskBarIcon;
202                 _thread = new thread (bind (&App::main_thread, this));
203
204                 Bind (wxEVT_TIMER, boost::bind (&App::check, this));
205                 _timer.reset (new wxTimer (this));
206                 _timer->Start (1000);
207
208                 return true;
209         }
210
211         int OnExit ()
212         {
213                 delete _icon;
214                 return wxApp::OnExit ();
215         }
216
217         void main_thread ()
218         try {
219                 Server server (memory_log, false);
220                 server.run (Config::instance()->num_local_encoding_threads ());
221         } catch (...) {
222                 store_current ();
223         }
224
225         void check ()
226         {
227                 try {
228                         rethrow ();
229                 } catch (exception& e) {
230                         error_dialog (0, std_to_wx (e.what ()));
231                         wxTheApp->ExitMainLoop ();
232                 } catch (...) {
233                         error_dialog (0, _("An unknown error has occurred with the DCP-o-matic server."));
234                         wxTheApp->ExitMainLoop ();
235                 }
236         }
237
238         void idle ()
239         {
240                 signal_manager->ui_idle ();
241         }
242
243         boost::thread* _thread;
244         TaskBarIcon* _icon;
245         shared_ptr<wxTimer> _timer;
246 };
247
248 IMPLEMENT_APP (App)