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 messageLoopRun = true;
416 boxstack->removeAllExceptWallpaper();
417 boxstack->remove(wallpaper);
418 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
421 void Control::dispatchMessage(Message* m)
423 logger->debug(TAG, "processing message {}", m->message);
425 if (m->p_to == Message::CONTROL)
429 case Message::SHUTDOWN:
431 messageLoopRun = false;
434 case Message::STOP_PLAYBACK:
436 handleCommand(Input::STOP); // an odd way of doing it, but so simple
439 case Message::VDR_CONNECTED:
441 doJustConnected(static_cast<VConnect*>(m->from));
444 case Message::SCREENSHOT:
446 logger->info(TAG, "Screenshot Message arrived");
447 Osd::getInstance()->screenShot("out.jpg");
450 case Message::CONNECTION_LOST:
455 case Message::INPUT_EVENT:
457 logger->info(TAG, "INPUT_EVENT {}", m->parameter);
459 handleCommand(m->parameter);
462 case Message::CHANGE_LANGUAGE:
464 boxstack->removeAllExceptWallpaper();
465 boxstack->update(wallpaper);
467 if (!VDR::getInstance()->isConnected()) { connectionLost(); break; }
468 VWelcome* vw = new VWelcome();
471 boxstack->update(vw);
474 case Message::LAST_VIEW_CLOSE:
476 // Shouldn't be done like this. Some generic message pass back from vinfo perhaps
483 // VWelcome* vw = new VWelcome();
485 // boxstack->add(vw);
486 // boxstack->update(vw);
492 else if (m->p_to == Message::BOXSTACK)
494 boxstack->processMessage(m);
496 else if (m->p_to == Message::MOUSE_RECEIVER)
498 logger->debug(TAG, "Sending mouse message to boxstack for dispatch");
499 boxstack->processMessage(m);
504 m->to->processMessage(m);
507 logger->debug(TAG, "done processing message {}", m->message);
510 void Control::handleCommand(int button)
512 if (isStandby && (button != Input::POWER)
513 && (button != Input::POWERON)
514 && (button != Input::POWEROFF)) return;
516 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
518 // command was not handled
522 case Input::VOLUMEUP:
523 case Input::VOLUMEDOWN:
525 if (inputMan->handlesVolume()) // CEC volume handler?
527 if (button == Input::VOLUMEDOWN)
528 inputMan->volumeDown();
530 inputMan->volumeUp();
534 VVolume* v = new VVolume();
536 v->handleCommand(button); // this will draw+show
542 if (inputMan->handlesVolume())
544 inputMan->volumeMute();
548 VMute* v = new VMute();
565 case Input::POWEROFF:
573 if (!connLost) return; // if connLost, handle Input::OK
579 logger->debug(TAG, "Handling sleeptimer go");
590 Message* m = new Message(); // break into master mutex
591 m->message = Message::SCREENSHOT;
592 m->p_to = Message::CONTROL;
598 void Control::doStandby()
611 void Control::doPowerOn()
615 Video::getInstance()->signalOn();
616 Led::getInstance()->on();
617 InputMan::getInstance()->changePowerState(true);
620 VConnect* vconnect = new VConnect();
621 boxstack->add(vconnect);
626 void Control::doPowerOff()
630 VDR::getInstance()->shutdownVDR();
631 boxstack->removeAllExceptWallpaper();
632 Video::getInstance()->signalOff();
633 boxstack->update(wallpaper);
635 VDR::getInstance()->configSave("General", "Last Power State", "Off");
636 logger->unsetExternLogger();
637 VDR::getInstance()->disconnect();
638 Led::getInstance()->off();
639 InputMan::getInstance()->changePowerState(false);
641 sleepTimer->shutdown();
645 void Control::doFromTheTop(bool which)
647 if (isStandby) return;
652 logger->info(TAG, "Connection lost dialog already present");
656 logger->info(TAG, "Doing connection lost dialog");
657 connLost = new VInfo();
658 connLost->setSize(360, 200);
659 connLost->createBuffer();
660 if (Video::getInstance()->getFormat() == Video::PAL)
661 connLost->setPosition(190, 170);
663 connLost->setPosition(180, 120);
664 connLost->setOneLiner(tr("Connection lost"));
665 connLost->setDropThrough();
666 connLost->setBorderOn(1);
667 connLost->setTitleBarColour(DrawStyle::DANGER);
668 connLost->okButton();
670 boxstack->add(connLost);
671 boxstack->update(connLost);
673 clearMQInputEvents();
677 logger->unsetExternLogger();
678 VDR::getInstance()->disconnect();
679 boxstack->removeAllExceptWallpaper();
680 boxstack->update(wallpaper);
685 // at this point, everything should be reset to first-go
687 VConnect* vconnect = new VConnect(); // deleted eventually in boxstack
688 boxstack->add(vconnect);
693 void Control::clearMQInputEvents()
695 std::lock_guard<std::mutex> lg(messageQueueMutex); // Get the lock
697 MQueueI i = messages.begin();
698 while(i != messages.end())
701 if (m->message == Message::INPUT_EVENT)
704 i = messages.erase(i);
713 void Control::doReboot()
716 logger->unsetExternLogger();
717 VDR::getInstance()->disconnect();
719 logger->info(TAG, "Reboot");
721 #ifndef VOMP_HAS_EXIT
722 // some plattforms, want a proper deinitialisation of their hardware before reboot
723 Osd::getInstance()->shutdown();
724 Audio::getInstance()->shutdown();
725 Video::getInstance()->shutdown();
726 InputMan::getInstance()->shutdown();
728 reboot(LINUX_REBOOT_CMD_RESTART);
729 // if reboot is not allowed -> stop
741 #endif //Would we support this on windows?
744 void Control::connectionLost()
746 logger->unsetExternLogger();
747 Message* m = new Message(); // break into master mutex
748 m->message = Message::CONNECTION_LOST;
749 m->p_to = Message::CONTROL;
753 void Control::buildCrashedBox()
755 VInfo* crash = new VInfo();
756 crash->setSize(360, 250);
757 crash->createBuffer();
758 if (Video::getInstance()->getFormat() == Video::PAL)
759 crash->setPosition(190, 146);
761 crash->setPosition(180, 96);
762 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.");
763 crash->setBorderOn(1);
764 crash->setTitleBarColour(DrawStyle::DANGER);
766 crash->setExitable();
768 boxstack->add(crash);
769 boxstack->update(crash);
772 int Control::getLangPref(bool subtitle, const char* langcode)
774 std::vector<struct ASLPref>::iterator itty=langcodes.begin();
775 char templangcode[4];
776 templangcode[0] = langcode[0];
777 templangcode[1] = langcode[1];
778 templangcode[2] = langcode[2];
779 templangcode[3] = '\0';
781 while (itty != langcodes.end())
783 size_t pos = (*itty).langcode.find(templangcode);
784 if (pos != std::string::npos)
786 //vector<struct ASLPref>::iterator itty2=langcodes.begin();
787 for (unsigned int i = 0; i < langcodes.size(); i++)
791 pref = langcodes[i].subtitlepref;
793 pref = langcodes[i].audiopref;
798 if (langcodes[i].subtitlepref==langpos) return i;
802 if (langcodes[i].audiopref==langpos) return i;
810 return langcodes.size(); //neutral
813 void Control::doJustConnected(VConnect* vconnect)
816 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
817 logger->info(TAG, "Entering doJustConnected");
819 boxstack->remove(vconnect);
821 VInfo* vi = new VInfo();
822 vi->setSize(400, 200);
824 if (video->getFormat() == Video::PAL)
825 vi->setPosition(170, 200);
827 vi->setPosition(160, 150);
828 vi->setOneLiner(tr("Connected, loading config"));
831 boxstack->update(vi);
833 // FIXME make config system
837 // See if we're supposed to do network logging
838 config = vdr->configLoad("Advanced", "Network logging");
839 if (config && !STRCASECMP(config, "On"))
841 logger->info(TAG, "Turning on network logging");
842 logger->setExternLogger(vdr);
846 logger->unsetExternLogger();
847 logger->info(TAG, "Turned off network logging");
849 if (config) delete[] config;
851 config = vdr->configLoad("Advanced", "Skin Name");
854 const char **skinnames=SkinFactory::getSkinNames();
855 for (int i=0;i<SkinFactory::getNumberofSkins();i++)
857 if (!STRCASECMP(config, skinnames[i]))
859 SkinFactory::InitSkin(i);
865 if (wallpaper && wallpaper_pict)
867 if (DrawStyle::WALLPAPER.alpha)
868 wallpaper_pict->setVisible(true);
870 wallpaper_pict->setVisible(false);
873 boxstack->update(wallpaper);
878 SkinFactory::InitSkin(0);
881 // See if config says to override video format (PAL/NTSC)
882 config = vdr->configLoad("General", "Override Video Format");
885 logger->debug(TAG, "Override Video Format is present");
887 if ( (!strcmp(config, "PAL") && (video->getFormat() != Video::PAL))
888 || (!strcmp(config, "NTSC") && (video->getFormat() != Video::NTSC))
889 || (!strcmp(config, "PAL_M") && (video->getFormat() != Video::PAL_M))
890 || (!strcmp(config, "NTSC_J") && (video->getFormat() != Video::NTSC_J))
893 // Oh sheesh, need to switch format. Bye bye TV...
895 // Take everything down
896 boxstack->removeAllExceptWallpaper();
897 boxstack->remove(wallpaper);
898 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
905 // FIXME FIXME FIXME why do this?
906 inputMan->shutdown(); // need on raspberry shut not do any harm, hopefully
909 // Get video and osd back up with the new mode
910 if (!strcmp(config, "PAL"))
912 logger->debug(TAG, "Switching to PAL");
913 video->init(Video::PAL);
915 else if (!strcmp(config, "NTSC"))
917 logger->debug(TAG, "Switching to NTSC");
918 video->init(Video::NTSC);
919 } else if (!strcmp(config, "PAL_M"))
921 logger->debug(TAG, "Switching to PAL_M");
922 video->init(Video::PAL_M);
923 } else if (!strcmp(config, "NTSC_J"))
925 logger->debug(TAG, "Switching to NTSC_J");
926 video->init(Video::NTSC_J);
931 //we do not init twice
935 // Put the wallpaper back
940 vi->setSize(400, 200);
942 if (video->getFormat() == Video::PAL)
943 vi->setPosition(170, 200);
945 vi->setPosition(160, 150);
947 vi->setOneLiner(tr("Connected, loading config"));
950 boxstack->update(vi);
954 logger->debug(TAG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
959 logger->debug(TAG, "Phew, no dangerous on-the-fly mode switching to do!");
962 // Power off if first boot and config says so
967 logger->debug(TAG, "Load power after boot");
969 config = vdr->configLoad("General", "Power After Boot");
973 if (!STRCASECMP(config, "On"))
975 logger->info(TAG, "Config says Power After Boot = On");
977 else if (!STRCASECMP(config, "Off"))
979 logger->info(TAG, "Config says Power After Boot = Off");
984 else if (!STRCASECMP(config, "Last state"))
986 char* lastPowerState = vdr->configLoad("General", "Last Power State");
989 if (!STRCASECMP(lastPowerState, "On"))
991 logger->info(TAG, "Config says Last Power State = On");
993 else if (!STRCASECMP(lastPowerState, "Off"))
995 logger->info(TAG, "Config says Last Power State = Off");
1002 logger->info(TAG, "Config General/Last Power State not understood");
1007 logger->info(TAG, "Config General/Last Power State not found");
1012 logger->info(TAG, "Config/Power After Boot not understood");
1018 logger->info(TAG, "Config General/Power After Boot not found");
1023 // Go S-Video if config says so
1025 config = vdr->configLoad("TV", "Connection");
1029 if (!STRCASECMP(config, "S-Video"))
1031 logger->info(TAG, "Switching to S-Video as Connection={}", config);
1032 video->setConnection(Video::SVIDEO);
1033 } else if (!STRCASECMP(config, "HDMI"))
1035 logger->info(TAG, "Switching to HDMI as Connection={}", config);
1036 video->setConnection(Video::HDMI);
1037 } else if (!STRCASECMP(config, "HDMI3D"))
1039 logger->info(TAG, "Switching to HDMI3D as Connection={}", config);
1040 video->setConnection(Video::HDMI3D);
1044 logger->info(TAG, "Switching to RGB/Composite as Connection={}", config);
1045 video->setConnection(Video::COMPOSITERGB);
1051 logger->info(TAG, "Config TV/S-Video not found");
1054 // Set to shutdown VDR if config says
1056 config = vdr->configLoad("General", "VDR shutdown");
1059 if (!STRCASECMP(config, "On"))
1061 logger->info(TAG, "Shutdown VDR when shutting down vomp");
1062 vdr->setVDRShutdown(true);
1064 else if (!STRCASECMP(config, "Off"))
1066 logger->info(TAG, "Shutdown only vomp");
1067 vdr->setVDRShutdown(false);
1072 logger->info(TAG, "Default shutdown only vomp");
1073 vdr->setVDRShutdown(false); // Default
1076 // Get TV aspect ratio
1078 config = vdr->configLoad("TV", "Aspect");
1081 if (!STRCASECMP(config, "16:9"))
1083 logger->info(TAG, "/// Switching to TV aspect 16:9");
1084 video->setTVsize(Video::ASPECT16X9);
1088 logger->info(TAG, "/// Switching to TV aspect 4:3");
1089 video->setTVsize(Video::ASPECT4X3);
1095 logger->info(TAG, "Config TV/Aspect type not found, going 4:3");
1096 video->setTVsize(Video::ASPECT4X3);
1099 config = vdr->configLoad("TV", "Widemode");
1102 if (!STRCASECMP(config, "Letterbox"))
1104 logger->info(TAG, "Setting letterbox mode");
1105 video->setMode(Video::LETTERBOX);
1109 logger->info(TAG, "Setting chop-sides mode");
1110 video->setMode(Video::NORMAL);
1117 logger->info(TAG, "Config TV/Widemode not found, Setting letterbox mode");
1118 video->setMode(Video::LETTERBOX);
1120 logger->info(TAG, "Config TV/Widemode not found, Setting chop-sides mode");
1121 video->setMode(Video::NORMAL);
1125 config = vdr->configLoad("Advanced", "TCP receive window");
1128 size_t newTCPsize = atoi(config);
1131 logger->info(TAG, "Setting TCP window size %i", newTCPsize);
1132 vdr->setReceiveWindow(newTCPsize);
1136 logger->info(TAG, "TCP window size not found, setting 2048");
1137 if (DEFAULT_TCP_WINDOWSIZE) vdr->setReceiveWindow(2048); // Default
1140 config = vdr->configLoad("Advanced", "Font Name");
1143 Osd::getInstance()->setFont(config);
1144 logger->info(TAG, "Setting Font to %s", config);
1150 // Set recording list type
1152 #ifdef ADVANCED_MENUES
1153 config = vdr->configLoad("Advanced", "Menu type");
1157 if (!STRCASECMP(config, "Advanced"))
1159 logger->info(TAG, "Switching to Advanced menu");
1164 logger->info(TAG, "Switching to Classic menu");
1171 logger->info(TAG, "Config General/menu type not found");
1176 config = vdr->configLoad("Advanced", "Disable WOL");
1179 if (!STRCASECMP(config, "Yes"))
1181 logger->info(TAG, "Config says disable WOL");
1182 Wol::getInstance()->setEnabled(false);
1186 logger->info(TAG, "Config says enable WOL");
1187 Wol::getInstance()->setEnabled(true);
1194 logger->info(TAG, "By default, enable WOL");
1195 Wol::getInstance()->setEnabled(true);
1197 /* device dependend config */
1198 audio->loadOptionsFromServer(vdr);
1199 video->loadOptionsFromServer(vdr);
1200 inputMan->loadOptionsFromServer(vdr);
1202 video->executePendingModeChanges();
1205 // Save power state = on
1207 vdr->configSave("General", "Last Power State", "On");
1209 // Make sure connection didn't die
1210 if (!vdr->isConnected())
1212 Control::getInstance()->connectionLost();
1216 boxstack->remove(vi);
1218 VWelcome* vw = new VWelcome();
1221 // No boxstack->update yet
1223 Config* localConfig = Config::getInstance();
1224 int startToLiveTV{};
1225 localConfig->getInt("main", "start_to_live_tv", startToLiveTV);
1228 std::shared_ptr<ChannelList> chanList = VDR::getInstance()->getChannelsList(VDR::VIDEO);
1229 if (chanList && chanList->size())
1231 Channel* chan = NULL;
1232 for (UINT i = 0; i < chanList->size(); i++)
1234 if ((*chanList)[i]->number == static_cast<ULONG>(startToLiveTV))
1236 chan = (*chanList)[i];
1242 VVideoLiveTV* v = new VVideoLiveTV(chanList, chan->number, NULL);
1248 // Could not find channel, no VVideoLiveTV was made, update vw instead
1249 boxstack->update(vw);
1254 Control::getInstance()->connectionLost();
1257 else // Not starting to live TV
1259 boxstack->update(vw);
1262 // Enter pre-keys here
1263 // handleCommand(Input::OK);
1264 // handleCommand(Input::THREE);
1265 // handleCommand(Input::SIX);
1266 // handleCommand(Input::UP);
1267 // handleCommand(Input::OK);
1268 // handleCommand(Input::OK);
1269 // handleCommand(Input::PLAY);
1270 // handleCommand(Input::DOWN);
1271 // handleCommand(Input::DOWN);
1272 // handleCommand(Input::DOWN);
1273 // handleCommand(Input::RIGHT);
1274 // handleCommand(Input::RED);