2 Copyright 2004-2020 Chris Tallon
4 This file is part of VOMP.
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.
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.
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/>.
20 // FIXME rename to Control and move stuff from main to here
23 #include <unistd.h> // for reboot
24 #include <linux/reboot.h>
25 #include <sys/reboot.h>
35 #include "inputandroid.h"
43 #include "vserverselect.h"
59 #include "sleeptimer.h"
61 #include "osdvector.h"
63 #include "vvideolivetv.h"
67 #ifdef VOMP_PLATFORM_RASPBERRY
68 #include "ledraspberry.h"
69 #include "osdopenvg.h"
75 #include "windowsosd.h"
77 #include "osdwinpixel.h"
79 #include "osdwinvector.h"
89 static const char* TAG = "Control";
91 Control* Control::instance = NULL;
105 Control* Control::getInstance()
110 bool Control::init(bool tcrashed)
112 if (initted) return false;
114 logger = LogNT::getInstance();
116 SkinFactory::InitSkin(0);
120 led = new Led_TYPE(); if (!led) throw 10;
121 if (!led->init(-1)) throw 11; // FIXME init(0) on Win32
123 timers = new Timers(); if (!timers) throw 20;
124 if (!timers->init()) throw 21;
126 video = new Video_TYPE(); if (!video) throw 30;
127 if (!video->init(Video::PAL)) throw 31;
129 audio = new Audio_TYPE(); if (!audio) throw 40;
130 if (!audio->init(Audio::MPEG2_PES)) throw 41;
132 osd = new Osd_TYPE(); if (!osd) throw 50;
133 if (!osd->init()) throw 51;
135 vdr = new VDR(); if (!vdr) throw 60;
136 if (!vdr->init()) throw 61;
138 boxstack = new BoxStack(); if (!boxstack) throw 70;
139 if (!boxstack->init()) throw 71;
141 sleepTimer = new SleepTimer(); if (!sleepTimer) throw 80;
143 wol = new Wol(); if (!wol) throw 90;
145 inputMan = new InputMan(); if (!inputMan) throw 100;
146 if (!inputMan->init()) throw 101;
151 if (e == 10) logger->crit(TAG, "LED module failed to create");
152 else if (e == 11) logger->crit(TAG, "LED module failed to initialise");
153 else if (e == 20) logger->crit(TAG, "Timers module failed to create");
154 else if (e == 21) logger->crit(TAG, "Timers module failed to initialise");
155 else if (e == 30) logger->crit(TAG, "Video module failed to create");
156 else if (e == 31) logger->crit(TAG, "Video module failed to initialise");
157 else if (e == 40) logger->crit(TAG, "Audio module failed to create");
158 else if (e == 41) logger->crit(TAG, "Audio module failed to initialise");
159 else if (e == 50) logger->crit(TAG, "OSD module failed to create");
160 else if (e == 51) logger->crit(TAG, "OSD module failed to initialise");
161 else if (e == 60) logger->crit(TAG, "VDR module failed to create");
162 else if (e == 61) logger->crit(TAG, "VDR module failed to initialise");
163 else if (e == 70) logger->crit(TAG, "BoxStack module failed to create");
164 else if (e == 71) logger->crit(TAG, "BoxStack module failed to initialise");
165 else if (e == 80) logger->crit(TAG, "SleepTimer module failed to create");
166 else if (e == 90) logger->crit(TAG, "WOL module failed to create");
167 else if (e == 100) logger->crit(TAG, "InputMan module failed to create");
168 else if (e == 101) logger->crit(TAG, "InputMan module failed to initialise");
185 boxstack->shutdown();
245 void Control::shutdown()
247 if (!initted) return;
252 inputMan->shutdown();
255 logger->info(TAG, "InputMan module shut down");
261 logger->info(TAG, "WOL module shut down");
268 logger->info(TAG, "SleepTimer module shut down");
273 boxstack->shutdown();
276 logger->info(TAG, "BoxStack module shut down");
284 logger->info(TAG, "VDR module shut down");
292 logger->info(TAG, "OSD module shut down");
300 logger->info(TAG, "Audio module shut down");
308 logger->info(TAG, "Video module shut down");
316 logger->info(TAG, "Timers module shut down");
324 logger->info(TAG, "LED module shut down");
330 logger->info(TAG, "Request stop");
332 Message* m = new Message(); // break master loop
333 m->message = Message::SHUTDOWN;
334 m->p_to = Message::CONTROL;
338 void Control::doWallpaper()
341 Boxx* bbg = new Boxx();
342 bbg->setSize(video->getScreenWidth(), video->getScreenHeight());
344 bbg->fillColour(DrawStyle::WALLPAPER);
346 boxstack->update(bbg);
347 boxstack->remove(bbg);
350 wallpaper = new Boxx();
351 wallpaper->setSize(video->getScreenWidth(), video->getScreenHeight());
352 wallpaper->createBuffer();
353 wallpaper->setBackgroundColour(DrawStyle::WALLPAPER);
355 wallpaper_pict = new WJpegTYPE();
356 wallpaper_pict->setSize(video->getScreenWidth(), video->getScreenHeight());
358 if (video->getFormat() == Video::PAL)
360 logger->debug(TAG, "PAL wallpaper selected");
362 wallpaper_pict->init("/wallpaperPAL.jpg");
364 wallpaper_pict->init("wallpaperPAL.jpg");
369 logger->debug(TAG, "NTSC wallpaper selected");
370 wallpaper_pict->init("/wallpaperNTSC.jpg");
373 if (DrawStyle::WALLPAPER.alpha)
374 wallpaper_pict->setVisible(true);
376 wallpaper_pict->setVisible(false);
378 wallpaper->add(wallpaper_pict);
381 boxstack->add(wallpaper);
382 boxstack->update(wallpaper);
384 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
385 if (osdv) osdv->updateBackgroundColor(DrawStyle::WALLPAPER);
390 if (!initted) return;
393 Video::getInstance()->signalOn();
394 Led::getInstance()->on();
404 VConnect* vconnect = new VConnect(); // Deleted when VConnect messages Control, and is boxstack->remove()'d
405 boxstack->add(vconnect);
411 // logger->debug(TAG, "Setting log trace only mode");
412 // logger->setTraceOnlyMode(true);
414 messageLoopRun = true;
419 boxstack->removeAllExceptWallpaper();
420 boxstack->remove(wallpaper);
421 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
424 void Control::dispatchMessage(Message* m)
426 logger->debug(TAG, "processing message {}", m->message);
428 if (m->p_to == Message::CONTROL)
432 case Message::SHUTDOWN:
434 messageLoopRun = false;
437 case Message::STOP_PLAYBACK:
439 handleCommand(Input::STOP); // an odd way of doing it, but so simple
442 case Message::VDR_CONNECTED:
444 doJustConnected(static_cast<VConnect*>(m->from));
447 case Message::SCREENSHOT:
449 logger->info(TAG, "Screenshot Message arrived");
450 Osd::getInstance()->screenShot("out.jpg");
453 case Message::CONNECTION_LOST:
458 case Message::INPUT_EVENT:
460 logger->info(TAG, "INPUT_EVENT {}", m->parameter);
462 handleCommand(m->parameter);
465 case Message::CHANGE_LANGUAGE:
467 boxstack->removeAllExceptWallpaper();
468 boxstack->update(wallpaper);
470 if (!VDR::getInstance()->isConnected()) { connectionLost(); break; }
471 VWelcome* vw = new VWelcome();
474 boxstack->update(vw);
477 case Message::LAST_VIEW_CLOSE:
479 // Shouldn't be done like this. Some generic message pass back from vinfo perhaps
486 // VWelcome* vw = new VWelcome();
488 // boxstack->add(vw);
489 // boxstack->update(vw);
495 else if (m->p_to == Message::BOXSTACK)
497 boxstack->processMessage(m);
499 else if (m->p_to == Message::MOUSE_RECEIVER)
501 logger->debug(TAG, "Sending mouse message to boxstack for dispatch");
502 boxstack->processMessage(m);
507 m->to->processMessage(m);
510 logger->debug(TAG, "done processing message {}", m->message);
513 void Control::handleCommand(int button)
515 if (isStandby && (button != Input::POWER)
516 && (button != Input::POWERON)
517 && (button != Input::POWEROFF)) return;
519 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
521 // command was not handled
525 case Input::VOLUMEUP:
526 case Input::VOLUMEDOWN:
528 if (inputMan->handlesVolume()) // CEC volume handler?
530 if (button == Input::VOLUMEDOWN)
531 inputMan->volumeDown();
533 inputMan->volumeUp();
537 VVolume* v = new VVolume();
539 v->handleCommand(button); // this will draw+show
545 if (inputMan->handlesVolume())
547 inputMan->volumeMute();
551 VMute* v = new VMute();
568 case Input::POWEROFF:
576 if (!connLost) return; // if connLost, handle Input::OK
582 logger->debug(TAG, "Handling sleeptimer go");
593 Message* m = new Message(); // break into master mutex
594 m->message = Message::SCREENSHOT;
595 m->p_to = Message::CONTROL;
601 void Control::doStandby()
614 void Control::doPowerOn()
618 Video::getInstance()->signalOn();
619 Led::getInstance()->on();
620 InputMan::getInstance()->changePowerState(true);
623 VConnect* vconnect = new VConnect();
624 boxstack->add(vconnect);
629 void Control::doPowerOff()
633 VDR::getInstance()->shutdownVDR();
634 boxstack->removeAllExceptWallpaper();
635 Video::getInstance()->signalOff();
636 boxstack->update(wallpaper);
638 VDR::getInstance()->configSave("General", "Last Power State", "Off");
639 logger->unsetExternLogger();
640 VDR::getInstance()->disconnect();
641 Led::getInstance()->off();
642 InputMan::getInstance()->changePowerState(false);
644 sleepTimer->shutdown();
648 void Control::doFromTheTop(bool which)
650 if (isStandby) return;
655 logger->info(TAG, "Connection lost dialog already present");
659 logger->info(TAG, "Doing connection lost dialog");
660 connLost = new VInfo();
661 connLost->setSize(360, 200);
662 connLost->createBuffer();
663 if (Video::getInstance()->getFormat() == Video::PAL)
664 connLost->setPosition(190, 170);
666 connLost->setPosition(180, 120);
667 connLost->setOneLiner(tr("Connection lost"));
668 connLost->setDropThrough();
669 connLost->setBorderOn(1);
670 connLost->setTitleBarColour(DrawStyle::DANGER);
671 connLost->okButton();
673 boxstack->add(connLost);
674 boxstack->update(connLost);
676 clearMQInputEvents();
680 logger->unsetExternLogger();
681 VDR::getInstance()->disconnect();
682 boxstack->removeAllExceptWallpaper();
683 boxstack->update(wallpaper);
688 // at this point, everything should be reset to first-go
690 VConnect* vconnect = new VConnect(); // deleted eventually in boxstack
691 boxstack->add(vconnect);
696 void Control::clearMQInputEvents()
698 std::lock_guard<std::mutex> lg(messageQueueMutex); // Get the lock
700 MQueueI i = messages.begin();
701 while(i != messages.end())
704 if (m->message == Message::INPUT_EVENT)
707 i = messages.erase(i);
716 void Control::doReboot()
719 logger->unsetExternLogger();
720 VDR::getInstance()->disconnect();
722 logger->info(TAG, "Reboot");
724 #ifndef VOMP_HAS_EXIT
725 // some plattforms, want a proper deinitialisation of their hardware before reboot
726 Osd::getInstance()->shutdown();
727 Audio::getInstance()->shutdown();
728 Video::getInstance()->shutdown();
729 InputMan::getInstance()->shutdown();
731 reboot(LINUX_REBOOT_CMD_RESTART);
732 // if reboot is not allowed -> stop
744 #endif //Would we support this on windows?
747 void Control::connectionLost()
749 logger->unsetExternLogger();
750 Message* m = new Message(); // break into master mutex
751 m->message = Message::CONNECTION_LOST;
752 m->p_to = Message::CONTROL;
756 void Control::buildCrashedBox()
758 VInfo* crash = new VInfo();
759 crash->setSize(360, 250);
760 crash->createBuffer();
761 if (Video::getInstance()->getFormat() == Video::PAL)
762 crash->setPosition(190, 146);
764 crash->setPosition(180, 96);
765 crash->setMainText("Oops, vomp crashed.. :(\nPlease report this crash to the author, with as much detail as possible about what you were doing at the time that might have caused the crash.");
766 crash->setBorderOn(1);
767 crash->setTitleBarColour(DrawStyle::DANGER);
769 crash->setExitable();
771 boxstack->add(crash);
772 boxstack->update(crash);
775 int Control::getLangPref(bool subtitle, const char* langcode)
777 std::vector<struct ASLPref>::iterator itty=langcodes.begin();
778 char templangcode[4];
779 templangcode[0] = langcode[0];
780 templangcode[1] = langcode[1];
781 templangcode[2] = langcode[2];
782 templangcode[3] = '\0';
784 while (itty != langcodes.end())
786 size_t pos = (*itty).langcode.find(templangcode);
787 if (pos != std::string::npos)
789 //vector<struct ASLPref>::iterator itty2=langcodes.begin();
790 for (unsigned int i = 0; i < langcodes.size(); i++)
794 pref = langcodes[i].subtitlepref;
796 pref = langcodes[i].audiopref;
801 if (langcodes[i].subtitlepref==langpos) return i;
805 if (langcodes[i].audiopref==langpos) return i;
813 return langcodes.size(); //neutral
816 void Control::doJustConnected(VConnect* vconnect)
819 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
820 logger->info(TAG, "Entering doJustConnected");
822 boxstack->remove(vconnect);
824 VInfo* vi = new VInfo();
825 vi->setSize(400, 200);
827 if (video->getFormat() == Video::PAL)
828 vi->setPosition(170, 200);
830 vi->setPosition(160, 150);
831 vi->setOneLiner(tr("Connected, loading config"));
834 boxstack->update(vi);
836 // FIXME make config system
840 // See if we're supposed to do network logging
841 config = vdr->configLoad("Advanced", "Network logging");
842 if (config && !STRCASECMP(config, "On"))
844 logger->info(TAG, "Turning on network logging");
845 logger->setExternLogger(vdr);
849 logger->unsetExternLogger();
850 logger->info(TAG, "Turned off network logging");
852 if (config) delete[] config;
854 config = vdr->configLoad("Advanced", "Skin Name");
857 const char **skinnames=SkinFactory::getSkinNames();
858 for (int i=0;i<SkinFactory::getNumberofSkins();i++)
860 if (!STRCASECMP(config, skinnames[i]))
862 SkinFactory::InitSkin(i);
868 if (wallpaper && wallpaper_pict)
870 if (DrawStyle::WALLPAPER.alpha)
871 wallpaper_pict->setVisible(true);
873 wallpaper_pict->setVisible(false);
876 boxstack->update(wallpaper);
881 SkinFactory::InitSkin(0);
884 // See if config says to override video format (PAL/NTSC)
885 config = vdr->configLoad("General", "Override Video Format");
888 logger->debug(TAG, "Override Video Format is present");
890 if ( (!strcmp(config, "PAL") && (video->getFormat() != Video::PAL))
891 || (!strcmp(config, "NTSC") && (video->getFormat() != Video::NTSC))
892 || (!strcmp(config, "PAL_M") && (video->getFormat() != Video::PAL_M))
893 || (!strcmp(config, "NTSC_J") && (video->getFormat() != Video::NTSC_J))
896 // Oh sheesh, need to switch format. Bye bye TV...
898 // Take everything down
899 boxstack->removeAllExceptWallpaper();
900 boxstack->remove(wallpaper);
901 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
908 // FIXME FIXME FIXME why do this?
909 inputMan->shutdown(); // need on raspberry shut not do any harm, hopefully
912 // Get video and osd back up with the new mode
913 if (!strcmp(config, "PAL"))
915 logger->debug(TAG, "Switching to PAL");
916 video->init(Video::PAL);
918 else if (!strcmp(config, "NTSC"))
920 logger->debug(TAG, "Switching to NTSC");
921 video->init(Video::NTSC);
922 } else if (!strcmp(config, "PAL_M"))
924 logger->debug(TAG, "Switching to PAL_M");
925 video->init(Video::PAL_M);
926 } else if (!strcmp(config, "NTSC_J"))
928 logger->debug(TAG, "Switching to NTSC_J");
929 video->init(Video::NTSC_J);
934 //we do not init twice
938 // Put the wallpaper back
943 vi->setSize(400, 200);
945 if (video->getFormat() == Video::PAL)
946 vi->setPosition(170, 200);
948 vi->setPosition(160, 150);
950 vi->setOneLiner(tr("Connected, loading config"));
953 boxstack->update(vi);
957 logger->debug(TAG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
962 logger->debug(TAG, "Phew, no dangerous on-the-fly mode switching to do!");
965 // Power off if first boot and config says so
970 logger->debug(TAG, "Load power after boot");
972 config = vdr->configLoad("General", "Power After Boot");
976 if (!STRCASECMP(config, "On"))
978 logger->info(TAG, "Config says Power After Boot = On");
980 else if (!STRCASECMP(config, "Off"))
982 logger->info(TAG, "Config says Power After Boot = Off");
987 else if (!STRCASECMP(config, "Last state"))
989 char* lastPowerState = vdr->configLoad("General", "Last Power State");
992 if (!STRCASECMP(lastPowerState, "On"))
994 logger->info(TAG, "Config says Last Power State = On");
996 else if (!STRCASECMP(lastPowerState, "Off"))
998 logger->info(TAG, "Config says Last Power State = Off");
1001 return; // quit here
1005 logger->info(TAG, "Config General/Last Power State not understood");
1010 logger->info(TAG, "Config General/Last Power State not found");
1015 logger->info(TAG, "Config/Power After Boot not understood");
1021 logger->info(TAG, "Config General/Power After Boot not found");
1026 // Go S-Video if config says so
1028 config = vdr->configLoad("TV", "Connection");
1032 if (!STRCASECMP(config, "S-Video"))
1034 logger->info(TAG, "Switching to S-Video as Connection={}", config);
1035 video->setConnection(Video::SVIDEO);
1036 } else if (!STRCASECMP(config, "HDMI"))
1038 logger->info(TAG, "Switching to HDMI as Connection={}", config);
1039 video->setConnection(Video::HDMI);
1040 } else if (!STRCASECMP(config, "HDMI3D"))
1042 logger->info(TAG, "Switching to HDMI3D as Connection={}", config);
1043 video->setConnection(Video::HDMI3D);
1047 logger->info(TAG, "Switching to RGB/Composite as Connection={}", config);
1048 video->setConnection(Video::COMPOSITERGB);
1054 logger->info(TAG, "Config TV/S-Video not found");
1057 // Set to shutdown VDR if config says
1059 config = vdr->configLoad("General", "VDR shutdown");
1062 if (!STRCASECMP(config, "On"))
1064 logger->info(TAG, "Shutdown VDR when shutting down vomp");
1065 vdr->setVDRShutdown(true);
1067 else if (!STRCASECMP(config, "Off"))
1069 logger->info(TAG, "Shutdown only vomp");
1070 vdr->setVDRShutdown(false);
1075 logger->info(TAG, "Default shutdown only vomp");
1076 vdr->setVDRShutdown(false); // Default
1079 // Get TV aspect ratio
1081 config = vdr->configLoad("TV", "Aspect");
1084 if (!STRCASECMP(config, "16:9"))
1086 logger->info(TAG, "/// Switching to TV aspect 16:9");
1087 video->setTVsize(Video::ASPECT16X9);
1091 logger->info(TAG, "/// Switching to TV aspect 4:3");
1092 video->setTVsize(Video::ASPECT4X3);
1098 logger->info(TAG, "Config TV/Aspect type not found, going 4:3");
1099 video->setTVsize(Video::ASPECT4X3);
1102 config = vdr->configLoad("TV", "Widemode");
1105 if (!STRCASECMP(config, "Letterbox"))
1107 logger->info(TAG, "Setting letterbox mode");
1108 video->setMode(Video::LETTERBOX);
1112 logger->info(TAG, "Setting chop-sides mode");
1113 video->setMode(Video::NORMAL);
1120 logger->info(TAG, "Config TV/Widemode not found, Setting letterbox mode");
1121 video->setMode(Video::LETTERBOX);
1123 logger->info(TAG, "Config TV/Widemode not found, Setting chop-sides mode");
1124 video->setMode(Video::NORMAL);
1128 config = vdr->configLoad("Advanced", "TCP receive window");
1131 size_t newTCPsize = atoi(config);
1134 logger->info(TAG, "Setting TCP window size %i", newTCPsize);
1135 vdr->setReceiveWindow(newTCPsize);
1139 logger->info(TAG, "TCP window size not found, setting 2048");
1140 if (DEFAULT_TCP_WINDOWSIZE) vdr->setReceiveWindow(2048); // Default
1143 config = vdr->configLoad("Advanced", "Font Name");
1146 Osd::getInstance()->setFont(config);
1147 logger->info(TAG, "Setting Font to %s", config);
1153 // Set recording list type
1155 #ifdef ADVANCED_MENUES
1156 config = vdr->configLoad("Advanced", "Menu type");
1160 if (!STRCASECMP(config, "Advanced"))
1162 logger->info(TAG, "Switching to Advanced menu");
1167 logger->info(TAG, "Switching to Classic menu");
1174 logger->info(TAG, "Config General/menu type not found");
1179 config = vdr->configLoad("Advanced", "Disable WOL");
1182 if (!STRCASECMP(config, "Yes"))
1184 logger->info(TAG, "Config says disable WOL");
1185 Wol::getInstance()->setEnabled(false);
1189 logger->info(TAG, "Config says enable WOL");
1190 Wol::getInstance()->setEnabled(true);
1197 logger->info(TAG, "By default, enable WOL");
1198 Wol::getInstance()->setEnabled(true);
1200 /* device dependend config */
1201 audio->loadOptionsFromServer(vdr);
1202 video->loadOptionsFromServer(vdr);
1203 inputMan->loadOptionsFromServer(vdr);
1205 video->executePendingModeChanges();
1208 // Save power state = on
1210 vdr->configSave("General", "Last Power State", "On");
1212 // Make sure connection didn't die
1213 if (!vdr->isConnected())
1215 Control::getInstance()->connectionLost();
1219 boxstack->remove(vi);
1221 VWelcome* vw = new VWelcome();
1224 // No boxstack->update yet
1226 Config* localConfig = Config::getInstance();
1227 int startToLiveTV{};
1228 localConfig->getInt("main", "start_to_live_tv", startToLiveTV);
1231 std::shared_ptr<ChannelList> chanList = VDR::getInstance()->getChannelsList(VDR::VIDEO);
1232 if (chanList && chanList->size())
1234 Channel* chan = NULL;
1235 for (UINT i = 0; i < chanList->size(); i++)
1237 if ((*chanList)[i]->number == static_cast<ULONG>(startToLiveTV))
1239 chan = (*chanList)[i];
1245 VVideoLiveTV* v = new VVideoLiveTV(chanList, chan->number, NULL);
1251 // Could not find channel, no VVideoLiveTV was made, update vw instead
1252 boxstack->update(vw);
1257 Control::getInstance()->connectionLost();
1260 else // Not starting to live TV
1262 boxstack->update(vw);
1265 // Enter pre-keys here
1266 // handleCommand(Input::OK);
1267 // handleCommand(Input::THREE);
1268 // handleCommand(Input::SIX);
1269 // handleCommand(Input::UP);
1270 // handleCommand(Input::OK);
1271 // handleCommand(Input::OK);
1272 // handleCommand(Input::PLAY);
1273 // handleCommand(Input::DOWN);
1274 // handleCommand(Input::DOWN);
1275 // handleCommand(Input::DOWN);
1276 // handleCommand(Input::RIGHT);
1277 // handleCommand(Input::RED);