]> git.vomp.tv Git - vompclient.git/blob - main.cc
Start main / winmain / control reorganisation
[vompclient.git] / main.cc
1 /*
2     Copyright 2004-2020 Chris Tallon
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 // FIXME
21 // Work towards main and winmain only containing what they need for their own platforms
22 // Move anything common to utils or control. Move any module startup / shutdown that can
23 // be done in control to there.
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <signal.h>
29
30 #include <thread>
31 #include <string>
32 #include <chrono>
33 #include <iomanip>
34
35 #ifndef WIN32
36   #include <unistd.h>
37   #include <endian.h>
38 #endif
39
40 #include "defines.h"
41
42 #ifdef HANDLE_VT_SWITCHING
43   #include <sys/ioctl.h>
44   #include <linux/vt.h>
45 #endif
46
47 #include "log.h"
48 #include "timers.h"
49 #include "vdr.h"
50 #include "control.h"
51 #include "inputman.h"
52 #include "wol.h"
53 #include "vsleeptimer.h"
54
55 #ifdef VOMP_PLATTFORM_NMT
56   #include "lednmt.h"
57   #include "osddirectfb.h"
58   #include "audionmt.h"
59   #include "videonmt.h"
60 #endif
61
62 #ifdef VOMP_PLATFORM_RASPBERRY
63   #include "ledraspberry.h"
64   #include "osdopenvg.h"
65   #include "audioomx.h"
66   #include "videoomx.h"
67 #endif
68
69
70
71 #ifndef WIN32 // FIXME do we need any if WIN32 stuff in here? windows has own winmain file
72 void threadSignalReceiverFunction();
73 #endif
74
75 ULLONG htonll(ULLONG);
76 ULLONG ntohll(ULLONG);
77 void shutdown(int code);
78 const std::string& getCommandLineServer();
79
80 // Global variables --------------------------------------------------------------------------------------------------
81 Log* logger;
82 InputMan* inputMan;
83 Led* led;
84 Osd* osd;
85 Timers* timers;
86 Control* control;
87 VDR* vdr;
88 Video* video;
89 Audio* audio;
90 Wol* wol;
91 Sleeptimer* sleeptimer;
92
93 // Temporary, will move to Config system
94 std::string argvServer;
95
96 #ifdef HANDLE_VT_SWITCHING
97 int fdtty;
98 struct vt_mode old_vtmode;
99 #endif
100
101 // Linux main function and sighandler
102 #ifndef WIN32
103 int main(int argc, char** argv)
104 {
105   bool daemonize = true;
106   bool debugEnabled = false;
107   bool crashed = false;
108   int c;
109
110   while ((c = getopt(argc, argv, "cdns:")) != -1)
111   {
112     switch (c)
113     {
114       case 'c':
115         crashed = true;
116         break;
117       case 'd':
118         debugEnabled = true; // and...
119         [[fallthrough]];
120       case 'n':
121         daemonize = false;
122         break;
123       case 's':
124         argvServer = optarg;
125         break;
126       case '?':
127         printf("Unknown option\n");
128         return 1;
129       default:
130         printf("Option error\n");
131         return 1;
132     }
133   }
134
135   // Init global vars ------------------------------------------------------------------------------------------------
136   logger     = new Log();
137   timers     = new Timers();
138   vdr        = new VDR();
139   inputMan   = new InputMan();
140
141   led        = new Led_TYPE();
142   osd        = new Osd_TYPE();
143   audio      = new Audio_TYPE();
144   video      = new Video_TYPE();
145
146   control    = new Control();
147   wol        = new Wol();
148   sleeptimer = new Sleeptimer();
149
150   if (!logger || !timers || !inputMan || !led || !osd || !video || !audio || !control || !wol || !sleeptimer)
151   {
152     printf("Could not create objects. Memory problems?\n");
153     shutdown(1);
154   }
155
156   // Get logging module started --------------------------------------------------------------------------------------
157
158   if (!logger->init(Log::DEBUG, "dummy", debugEnabled ? 1 : 0))
159   {
160     printf("Could not initialise log object. Aborting.\n");
161     shutdown(1);
162   }
163
164   logger->log("Core", Log::INFO, "Starting up...");
165
166   // Daemonize if not -d
167
168   if (daemonize)
169   {
170     // Fork away
171     pid_t forkTest = fork();
172     if (forkTest == -1)
173     { printf("Cannot fork (1).\n"); exit(1); }
174     if (forkTest != 0) _exit(0); // PID returned, I am the parent
175     // otherwise, I am the child
176     setsid();
177     forkTest = fork();
178     if (forkTest == -1)
179     { printf("Cannot fork (2).\n"); exit(1); }
180     if (forkTest != 0) _exit(0); // PID returned, I am the parent
181     // otherwise, I am the child
182     close(0);
183     close(1);
184     close(2);
185   }
186
187   // Set up signal handling ------------------------------------------------------------------------------------------
188
189   // Block all signals
190   sigset_t set;
191   sigfillset(&set);
192   int sigBlockResult = pthread_sigmask(SIG_SETMASK, &set, NULL);
193   if (sigBlockResult)
194   {
195     logger->log("Core", Log::EMERG, "Could not block signals: %i", sigBlockResult);
196     shutdown(1);
197   }
198
199   // Start signal receiver thread
200   std::thread threadSignalReceiver(threadSignalReceiverFunction);
201   threadSignalReceiver.detach();
202
203   logger->log("Core", Log::INFO, "Signal handlers set up successfully");
204
205   // VT Switching -----------------------------------------------------------------------------------------------
206
207 #ifdef HANDLE_VT_SWITCHING
208   if (
209
210
211     (fdtty = open("/dev/tty", O_WRONLY),0) == -1   // FIXME. Why the ,0 ? Surely this kills the log line below
212
213
214   )
215   {
216     logger->log("Core", Log::EMERG, "Could not open /dev/tty. Please change permissions");
217   }
218   else
219   {
220     int free_vt;
221     if (ioctl(fdtty, VT_OPENQRY, &free_vt) == -1 || free_vt == -1)
222     {
223       logger->log("Core", Log::EMERG, "Could not retrieve free virtual console, please change permissions");
224     }
225     else
226     {
227       ioctl(fdtty, VT_ACTIVATE, free_vt);
228       ioctl(fdtty, VT_WAITACTIVE, free_vt);
229       ioctl(fdtty, VT_LOCKSWITCH, 1);
230     }
231   }
232 #endif
233
234   // Init modules ----------------------------------------------------------------------------------------------------
235   int success;
236
237   success = inputMan->init();
238
239   if (success)
240   {
241     logger->log("Core", Log::INFO, "InputMan module initialised");
242   }
243   else
244   {
245     logger->log("Core", Log::EMERG, "InputMan module failed to initialise");
246     shutdown(1);
247   }
248
249   success = led->init(-1);
250   if (success)
251   {
252     logger->log("Core", Log::INFO, "LED module initialised");
253   }
254   else
255   {
256     logger->log("Core", Log::EMERG, "LED module failed to initialise");
257     shutdown(1);
258   }
259
260   success = timers->init();
261   if (success)
262   {
263     logger->log("Core", Log::INFO, "Timers module initialised");
264   }
265   else
266   {
267     logger->log("Core", Log::EMERG, "Timers module failed to initialise");
268     shutdown(1);
269   }
270
271   UCHAR videoFormat = Video::PAL; // PALNTSC FIXME
272
273   success = video->init(videoFormat);
274   if (success)
275   {
276     logger->log("Core", Log::INFO, "Video module initialised");
277   }
278   else
279   {
280     logger->log("Core", Log::EMERG, "Video module failed to initialise");
281     shutdown(1);
282   }
283
284   success = osd->init();
285   if (success)
286   {
287     logger->log("Core", Log::INFO, "OSD module initialised");
288   }
289   else
290   {
291     logger->log("Core", Log::EMERG, "OSD module failed to initialise");
292     shutdown(1);
293   }
294
295   success = audio->init(Audio::MPEG2_PES);
296   if (success)
297   {
298     logger->log("Core", Log::INFO, "Audio module initialised");
299   }
300   else
301   {
302     logger->log("Core", Log::EMERG, "Audio module failed to initialise");
303     shutdown(1);
304   }
305
306   success = vdr->init();
307   if (success)
308   {
309     logger->log("Core", Log::INFO, "VDR module initialised");
310   }
311   else
312   {
313     logger->log("Core", Log::EMERG, "VDR module failed to initialise");
314     shutdown(1);
315   }
316
317
318
319
320
321   success = control->init(crashed);
322   if (success)
323   {
324     logger->log("Core", Log::INFO, "Control module initialised");
325   }
326   else
327   {
328     logger->log("Core", Log::EMERG, "Control module failed to initialise");
329     shutdown(1);
330   }
331
332   // Other init ------------------------------------------------------------------------------------------------------
333
334   logger->log("Core", Log::NOTICE, "Startup successful");
335
336   // Run main loop ---------------------------------------------------------------------------------------------------
337
338   // Ok, all major device components and other bits are loaded and ready
339   control->run();
340
341   // When that returns quit ------------------------------------------------------------------------------------------
342
343   control->shutdown();
344
345   shutdown(0);
346   return 0;
347 }
348
349 // -------------------------------------------------------------------------------------------------------------------
350
351 void threadSignalReceiverFunction()
352 {
353   int sig;
354   sigset_t set;
355   sigfillset(&set);
356   while(1)
357   {
358     if(sigwait(&set, &sig))
359     {
360       logger->log("Core", Log::CRIT, "Sigwait returned fail - signal handler exiting");
361       return;
362     }
363
364     logger->log("Core", Log::NOTICE, "Signal received: %i", sig);
365
366     if ((sig == SIGINT) || (sig == SIGTERM)) control->stop();
367   }
368 }
369
370 #endif
371
372 // -------------------------------------------------------------------------------------------------------------------
373
374 void shutdown(int code)
375 {
376   // FIXME, send a del all to boxstack first, then get rid of it after control?
377   if (control) // shut down control here in case views have posted messages
378   {
379     delete control;
380     control = NULL;
381     logger->log("Core", Log::NOTICE, "Control module shut down");
382   }
383
384   if (vdr)
385   {
386     vdr->shutdown();
387     delete vdr;
388     logger->log("Core", Log::NOTICE, "VDR module shut down");
389   }
390
391   if (osd)
392   {
393     osd->shutdown();
394     delete osd;
395     logger->log("Core", Log::NOTICE, "OSD module shut down");
396   }
397
398   if (audio)
399   {
400     audio->shutdown();
401     delete audio;
402     logger->log("Core", Log::NOTICE, "Audio module shut down");
403   }
404
405   if (video)
406   {
407     video->shutdown();
408     delete video;
409     logger->log("Core", Log::NOTICE, "Video module shut down");
410   }
411
412   if (timers)
413   {
414     timers->shutdown();
415     delete timers;
416     logger->log("Core", Log::NOTICE, "Timers module shut down");
417   }
418
419   if (led)
420   {
421     led->shutdown();
422     delete led;
423     logger->log("Core", Log::NOTICE, "LED module shut down");
424   }
425
426   if (inputMan)
427   {
428     inputMan->shutdown();
429     delete inputMan;
430     logger->log("Core", Log::NOTICE, "InputMan module shut down");
431   }
432
433   if (wol)
434   {
435     delete wol;
436     logger->log("Core", Log::NOTICE, "WOL module shut down");
437   }
438
439   if (sleeptimer)
440   {
441     delete sleeptimer;
442     logger->log("Core", Log::NOTICE, "Sleeptimer module shut down");
443   }
444
445 #ifdef HANDLE_VT_SWITCHING
446   ioctl(fdtty, VT_UNLOCKSWITCH, 1);
447   close(fdtty);
448 #endif
449
450   if (logger)
451   {
452     logger->log("Core", Log::NOTICE, "Log module shutting down... bye!\n\n");
453     logger->shutdown();
454     delete logger;
455   }
456
457   exit(code);
458 }
459
460 // -------------------------------------------------------------------------------------------------------------------
461
462 ULLONG htonll(ULLONG a)
463 {
464   #if BYTE_ORDER == BIG_ENDIAN
465     return a;
466   #else
467     ULLONG b = 0;
468
469     b = ((a << 56) & 0xFF00000000000000ULL)
470       | ((a << 40) & 0x00FF000000000000ULL)
471       | ((a << 24) & 0x0000FF0000000000ULL)
472       | ((a <<  8) & 0x000000FF00000000ULL)
473       | ((a >>  8) & 0x00000000FF000000ULL)
474       | ((a >> 24) & 0x0000000000FF0000ULL)
475       | ((a >> 40) & 0x000000000000FF00ULL)
476       | ((a >> 56) & 0x00000000000000FFULL) ;
477
478     return b;
479   #endif
480 }
481
482 ULLONG ntohll(ULLONG a)
483 {
484   return htonll(a);
485 }
486
487 void MILLISLEEP(ULONG a)
488 {
489 #ifndef WIN32
490   struct timespec delayTime;
491   delayTime.tv_sec = a / 1000;
492   delayTime.tv_nsec = (a % 1000) * 1000000;
493   nanosleep(&delayTime, NULL);
494 #else
495   Sleep(a);
496 #endif
497 }
498
499 long long getTimeMS()
500 {
501   struct timespec ts;
502   clock_gettime(VOMP_LINUX_CLOCK, &ts);
503   return ts.tv_sec * 1000 + ts.tv_nsec / 1000000LL;
504 }
505
506 int getClockRealTime(struct timespec *tp) // FIXME - del if all goes chrono
507 {
508   return clock_gettime(CLOCK_REALTIME, tp);
509 }
510
511 int min(UINT a, int b)
512 {
513   if (a > b) return b;
514   else return a;
515 }
516
517 int max(int a, int b)
518 {
519   if (a > b) return a;
520   else return b;
521 }
522
523 std::string tp2str(const std::chrono::time_point<std::chrono::system_clock>& tp)
524 {
525   auto tms = std::chrono::time_point_cast<std::chrono::milliseconds>(tp);
526   std::chrono::milliseconds e = tms.time_since_epoch();
527   long long c = e.count();
528   time_t tt = c / 1000;
529   int ttm = c % 1000;
530   auto stm = std::localtime(&tt);
531   std::stringstream ss;
532   ss << std::put_time(stm, "%T") << "." << std::setfill('0') << std::setw(3) << ttm;
533   return ss.str();
534 }
535
536 const std::string& getCommandLineServer()
537 {
538   return argvServer;
539 }