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