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"
64 #ifdef VOMP_PLATFORM_RASPBERRY
65 #include "ledraspberry.h"
66 #include "osdopenvg.h"
72 #include "windowsosd.h"
74 #include "osdwinpixel.h"
76 #include "osdwinvector.h"
86 static const char* TAG = "Control";
88 Control* Control::instance = NULL;
102 Control* Control::getInstance()
107 bool Control::init(bool tcrashed)
109 if (initted) return false;
111 logger = LogNT::getInstance();
113 SkinFactory::InitSkin(0);
117 led = new Led_TYPE(); if (!led) throw 10;
118 if (!led->init(-1)) throw 11; // FIXME init(0) on Win32
120 timers = new Timers(); if (!timers) throw 20;
121 if (!timers->init()) throw 21;
123 video = new Video_TYPE(); if (!video) throw 30;
124 if (!video->init(Video::PAL)) throw 31;
126 audio = new Audio_TYPE(); if (!audio) throw 40;
127 if (!audio->init(Audio::MPEG2_PES)) throw 41;
129 osd = new Osd_TYPE(); if (!osd) throw 50;
130 if (!osd->init()) throw 51;
132 vdr = new VDR(); if (!vdr) throw 60;
133 if (!vdr->init()) throw 61;
135 boxstack = new BoxStack(); if (!boxstack) throw 70;
136 if (!boxstack->init()) throw 71;
138 sleepTimer = new SleepTimer(); if (!sleepTimer) throw 80;
140 wol = new Wol(); if (!wol) throw 90;
142 inputMan = new InputMan(); if (!inputMan) throw 100;
143 if (!inputMan->init()) throw 101;
148 if (e == 10) logger->crit(TAG, "LED module failed to create");
149 else if (e == 11) logger->crit(TAG, "LED module failed to initialise");
150 else if (e == 20) logger->crit(TAG, "Timers module failed to create");
151 else if (e == 21) logger->crit(TAG, "Timers module failed to initialise");
152 else if (e == 30) logger->crit(TAG, "Video module failed to create");
153 else if (e == 31) logger->crit(TAG, "Video module failed to initialise");
154 else if (e == 40) logger->crit(TAG, "Audio module failed to create");
155 else if (e == 41) logger->crit(TAG, "Audio module failed to initialise");
156 else if (e == 50) logger->crit(TAG, "OSD module failed to create");
157 else if (e == 51) logger->crit(TAG, "OSD module failed to initialise");
158 else if (e == 60) logger->crit(TAG, "VDR module failed to create");
159 else if (e == 61) logger->crit(TAG, "VDR module failed to initialise");
160 else if (e == 70) logger->crit(TAG, "BoxStack module failed to create");
161 else if (e == 71) logger->crit(TAG, "BoxStack module failed to initialise");
162 else if (e == 80) logger->crit(TAG, "SleepTimer module failed to create");
163 else if (e == 90) logger->crit(TAG, "WOL module failed to create");
164 else if (e == 100) logger->crit(TAG, "InputMan module failed to create");
165 else if (e == 101) logger->crit(TAG, "InputMan module failed to initialise");
182 boxstack->shutdown();
242 void Control::shutdown()
244 if (!initted) return;
249 inputMan->shutdown();
252 logger->info(TAG, "InputMan module shut down");
258 logger->info(TAG, "WOL module shut down");
265 logger->info(TAG, "SleepTimer module shut down");
270 boxstack->shutdown();
273 logger->info(TAG, "BoxStack module shut down");
281 logger->info(TAG, "VDR module shut down");
289 logger->info(TAG, "OSD module shut down");
297 logger->info(TAG, "Audio module shut down");
305 logger->info(TAG, "Video module shut down");
313 logger->info(TAG, "Timers module shut down");
321 logger->info(TAG, "LED module shut down");
327 logger->info(TAG, "Request stop");
329 Message* m = new Message(); // break master loop
330 m->message = Message::SHUTDOWN;
331 m->p_to = Message::CONTROL;
335 void Control::doWallpaper()
338 Boxx* bbg = new Boxx();
339 bbg->setSize(video->getScreenWidth(), video->getScreenHeight());
341 bbg->fillColour(DrawStyle::WALLPAPER);
343 boxstack->update(bbg);
344 boxstack->remove(bbg);
347 wallpaper = new Boxx();
348 wallpaper->setSize(video->getScreenWidth(), video->getScreenHeight());
349 wallpaper->createBuffer();
350 wallpaper->setBackgroundColour(DrawStyle::WALLPAPER);
352 wallpaper_pict = new WJpegTYPE();
353 wallpaper_pict->setSize(video->getScreenWidth(), video->getScreenHeight());
355 if (video->getFormat() == Video::PAL)
357 logger->debug(TAG, "PAL wallpaper selected");
359 wallpaper_pict->init("/wallpaperPAL.jpg");
361 wallpaper_pict->init("wallpaperPAL.jpg");
366 logger->debug(TAG, "NTSC wallpaper selected");
367 wallpaper_pict->init("/wallpaperNTSC.jpg");
370 if (DrawStyle::WALLPAPER.alpha)
371 wallpaper_pict->setVisible(true);
373 wallpaper_pict->setVisible(false);
375 wallpaper->add(wallpaper_pict);
378 boxstack->add(wallpaper);
379 boxstack->update(wallpaper);
381 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
382 if (osdv) osdv->updateBackgroundColor(DrawStyle::WALLPAPER);
387 if (!initted) return;
391 Video::getInstance()->signalOn();
392 Led::getInstance()->on();
402 VConnect* vconnect = new VConnect(); // Deleted when VConnect messages Control, and is boxstack->remove()'d
403 boxstack->add(vconnect);
407 std::unique_lock<std::mutex> lockWrapper(messageQueueMutex); // locks. unlocks on out-of-scope
413 messageQueueCond.wait(lockWrapper, [&] { return !irun || !messages.empty(); });
414 logger->debug(TAG, "woke");
418 while(!messages.empty())
420 Message* m = messages.front();
421 messages.pop_front();
423 lockWrapper.unlock();
434 boxstack->removeAllExceptWallpaper();
435 boxstack->remove(wallpaper);
436 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
439 void Control::processMessage(Message* m)
441 logger->debug(TAG, "processing message {}", m->message);
443 if (m->p_to == Message::CONTROL)
447 case Message::SHUTDOWN:
452 case Message::STOP_PLAYBACK:
454 handleCommand(Input::STOP); // an odd way of doing it, but so simple
457 case Message::VDR_CONNECTED:
459 doJustConnected(static_cast<VConnect*>(m->from));
462 case Message::SCREENSHOT:
464 logger->info(TAG, "Screenshot Message arrived");
465 Osd::getInstance()->screenShot("out.jpg");
468 case Message::CONNECTION_LOST:
473 case Message::INPUT_EVENT:
475 logger->info(TAG, "INPUT_EVENT {}", m->parameter);
477 handleCommand(m->parameter);
480 case Message::CHANGE_LANGUAGE:
482 boxstack->removeAllExceptWallpaper();
483 boxstack->update(wallpaper);
485 if (!VDR::getInstance()->isConnected()) { connectionLost(); break; }
486 VWelcome* vw = new VWelcome();
489 boxstack->update(vw);
492 case Message::LAST_VIEW_CLOSE:
494 // Shouldn't be done like this. Some generic message pass back from vinfo perhaps
501 // VWelcome* vw = new VWelcome();
503 // boxstack->add(vw);
504 // boxstack->update(vw);
508 case Message::NEW_PICTURE:
512 //Log::getInstance()->log("Control", Log::DEBUG, "TVMedia NEW_PICTURE");
513 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
514 if (osdv) osdv->informPicture(m->tag, reinterpret_cast<ImageIndex>(m->data));
517 case Message::NEW_PICTURE_STATIC:
519 //Log::getInstance()->log("Control", Log::DEBUG, "TVMedia NEW_PICTURE %x %x",m->tag,m->parameter);
520 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
521 if (osdv) osdv->informPicture(static_cast<unsigned long long>(m->tag) << 32LL, reinterpret_cast<ImageIndex>(m->data));
530 Instead of sending through the boxstack, implement a more generic MessageReceiver interface
531 and have potential receivers register with something
532 When a message needs to be delivered, check if the receiver is still registered, if so, deliver the message
533 This could all be done using the existing big control mutex to keep it simple
536 logger->debug(TAG, "Sending message to boxstack");
537 boxstack->processMessage(m);
541 void Control::handleCommand(int button)
543 if (isStandby && (button != Input::POWER)
544 && (button != Input::POWERON)
545 && (button != Input::POWEROFF)) return;
547 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
549 // command was not handled
553 case Input::VOLUMEUP:
554 case Input::VOLUMEDOWN:
556 if (inputMan->handlesVolume()) // CEC volume handler?
558 if (button == Input::VOLUMEDOWN)
559 inputMan->volumeDown();
561 inputMan->volumeUp();
565 VVolume* v = new VVolume();
567 v->handleCommand(button); // this will draw+show
573 if (inputMan->handlesVolume())
575 inputMan->volumeMute();
579 VMute* v = new VMute();
596 case Input::POWEROFF:
604 if (!connLost) return; // if connLost, handle Input::OK
610 logger->debug(TAG, "Handling sleeptimer go");
621 Message* m = new Message(); // break into master mutex
622 m->message = Message::SCREENSHOT;
623 m->p_to = Message::CONTROL;
629 void Control::doStandby()
642 void Control::doPowerOn()
646 Video::getInstance()->signalOn();
647 Led::getInstance()->on();
648 InputMan::getInstance()->changePowerState(true);
651 VConnect* vconnect = new VConnect();
652 boxstack->add(vconnect);
657 void Control::doPowerOff()
661 VDR::getInstance()->shutdownVDR();
662 boxstack->removeAllExceptWallpaper();
663 Video::getInstance()->signalOff();
664 boxstack->update(wallpaper);
666 VDR::getInstance()->configSave("General", "Last Power State", "Off");
667 logger->unsetExternLogger();
668 VDR::getInstance()->disconnect();
669 Led::getInstance()->off();
670 InputMan::getInstance()->changePowerState(false);
672 sleepTimer->shutdown();
676 void Control::doFromTheTop(bool which)
678 if (isStandby) return;
683 logger->info(TAG, "Connection lost dialog already present");
687 logger->info(TAG, "Doing connection lost dialog");
688 connLost = new VInfo();
689 connLost->setSize(360, 200);
690 connLost->createBuffer();
691 if (Video::getInstance()->getFormat() == Video::PAL)
692 connLost->setPosition(190, 170);
694 connLost->setPosition(180, 120);
695 connLost->setOneLiner(tr("Connection lost"));
696 connLost->setDropThrough();
697 connLost->setBorderOn(1);
698 connLost->setTitleBarColour(DrawStyle::DANGER);
699 connLost->okButton();
701 boxstack->add(connLost);
702 boxstack->update(connLost);
704 clearMQInputEvents();
708 logger->unsetExternLogger();
709 VDR::getInstance()->disconnect();
710 boxstack->removeAllExceptWallpaper();
711 boxstack->update(wallpaper);
716 // at this point, everything should be reset to first-go
718 VConnect* vconnect = new VConnect(); // deleted eventually in boxstack
719 boxstack->add(vconnect);
724 void Control::clearMQInputEvents()
726 std::lock_guard<std::mutex> lg(messageQueueMutex); // Get the lock
728 MQueueI i = messages.begin();
729 while(i != messages.end())
732 if (m->message == Message::INPUT_EVENT)
735 i = messages.erase(i);
744 void Control::doReboot()
747 logger->unsetExternLogger();
748 VDR::getInstance()->disconnect();
750 logger->info(TAG, "Reboot");
752 #ifndef VOMP_HAS_EXIT
753 // some plattforms, want a proper deinitialisation of their hardware before reboot
754 Osd::getInstance()->shutdown();
755 Audio::getInstance()->shutdown();
756 Video::getInstance()->shutdown();
757 InputMan::getInstance()->shutdown();
759 reboot(LINUX_REBOOT_CMD_RESTART);
760 // if reboot is not allowed -> stop
772 #endif //Would we support this on windows?
775 void Control::connectionLost()
777 logger->unsetExternLogger();
778 Message* m = new Message(); // break into master mutex
779 m->message = Message::CONNECTION_LOST;
780 m->p_to = Message::CONTROL;
784 void Control::buildCrashedBox()
786 VInfo* crash = new VInfo();
787 crash->setSize(360, 250);
788 crash->createBuffer();
789 if (Video::getInstance()->getFormat() == Video::PAL)
790 crash->setPosition(190, 146);
792 crash->setPosition(180, 96);
793 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.");
794 crash->setBorderOn(1);
795 crash->setTitleBarColour(DrawStyle::DANGER);
797 crash->setExitable();
799 boxstack->add(crash);
800 boxstack->update(crash);
803 int Control::getLangPref(bool subtitle, const char* langcode)
805 std::vector<struct ASLPref>::iterator itty=langcodes.begin();
806 char templangcode[4];
807 templangcode[0] = langcode[0];
808 templangcode[1] = langcode[1];
809 templangcode[2] = langcode[2];
810 templangcode[3] = '\0';
812 while (itty != langcodes.end())
814 size_t pos = (*itty).langcode.find(templangcode);
815 if (pos != std::string::npos)
817 //vector<struct ASLPref>::iterator itty2=langcodes.begin();
818 for (unsigned int i = 0; i < langcodes.size(); i++)
822 pref = langcodes[i].subtitlepref;
824 pref = langcodes[i].audiopref;
829 if (langcodes[i].subtitlepref==langpos) return i;
833 if (langcodes[i].audiopref==langpos) return i;
841 return langcodes.size(); //neutral
844 void Control::doJustConnected(VConnect* vconnect)
847 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
848 logger->info(TAG, "Entering doJustConnected");
850 boxstack->remove(vconnect);
852 VInfo* vi = new VInfo();
853 vi->setSize(400, 200);
855 if (video->getFormat() == Video::PAL)
856 vi->setPosition(170, 200);
858 vi->setPosition(160, 150);
859 vi->setOneLiner(tr("Connected, loading config"));
862 boxstack->update(vi);
864 // FIXME make config system
868 // See if we're supposed to do network logging
869 config = vdr->configLoad("Advanced", "Network logging");
870 if (config && !STRCASECMP(config, "On"))
872 logger->info(TAG, "Turning on network logging");
873 logger->setExternLogger(vdr);
877 logger->unsetExternLogger();
878 logger->info(TAG, "Turned off network logging");
880 if (config) delete[] config;
882 config = vdr->configLoad("Advanced", "Skin Name");
885 const char **skinnames=SkinFactory::getSkinNames();
886 for (int i=0;i<SkinFactory::getNumberofSkins();i++)
888 if (!STRCASECMP(config, skinnames[i]))
890 SkinFactory::InitSkin(i);
896 if (wallpaper && wallpaper_pict)
898 if (DrawStyle::WALLPAPER.alpha)
899 wallpaper_pict->setVisible(true);
901 wallpaper_pict->setVisible(false);
904 boxstack->update(wallpaper);
909 SkinFactory::InitSkin(0);
912 // See if config says to override video format (PAL/NTSC)
913 config = vdr->configLoad("General", "Override Video Format");
916 logger->debug(TAG, "Override Video Format is present");
918 if ( (!strcmp(config, "PAL") && (video->getFormat() != Video::PAL))
919 || (!strcmp(config, "NTSC") && (video->getFormat() != Video::NTSC))
920 || (!strcmp(config, "PAL_M") && (video->getFormat() != Video::PAL_M))
921 || (!strcmp(config, "NTSC_J") && (video->getFormat() != Video::NTSC_J))
924 // Oh sheesh, need to switch format. Bye bye TV...
926 // Take everything down
927 boxstack->removeAllExceptWallpaper();
928 boxstack->remove(wallpaper);
929 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
936 inputMan->shutdown(); // need on raspberry shut not do any harm, hopefully
937 inputMan->init(); // FIXME this breaks badly now
939 // Get video and osd back up with the new mode
940 if (!strcmp(config, "PAL"))
942 logger->debug(TAG, "Switching to PAL");
943 video->init(Video::PAL);
945 else if (!strcmp(config, "NTSC"))
947 logger->debug(TAG, "Switching to NTSC");
948 video->init(Video::NTSC);
949 } else if (!strcmp(config, "PAL_M"))
951 logger->debug(TAG, "Switching to PAL_M");
952 video->init(Video::PAL_M);
953 } else if (!strcmp(config, "NTSC_J"))
955 logger->debug(TAG, "Switching to NTSC_J");
956 video->init(Video::NTSC_J);
961 //we do not init twice
965 // Put the wallpaper back
970 vi->setSize(400, 200);
972 if (video->getFormat() == Video::PAL)
973 vi->setPosition(170, 200);
975 vi->setPosition(160, 150);
977 vi->setOneLiner(tr("Connected, loading config"));
980 boxstack->update(vi);
984 logger->debug(TAG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
989 logger->debug(TAG, "Phew, no dangerous on-the-fly mode switching to do!");
992 // Power off if first boot and config says so
997 logger->debug(TAG, "Load power after boot");
999 config = vdr->configLoad("General", "Power After Boot");
1003 if (!STRCASECMP(config, "On"))
1005 logger->info(TAG, "Config says Power After Boot = On");
1007 else if (!STRCASECMP(config, "Off"))
1009 logger->info(TAG, "Config says Power After Boot = Off");
1012 return; // quit here
1014 else if (!STRCASECMP(config, "Last state"))
1016 char* lastPowerState = vdr->configLoad("General", "Last Power State");
1019 if (!STRCASECMP(lastPowerState, "On"))
1021 logger->info(TAG, "Config says Last Power State = On");
1023 else if (!STRCASECMP(lastPowerState, "Off"))
1025 logger->info(TAG, "Config says Last Power State = Off");
1028 return; // quit here
1032 logger->info(TAG, "Config General/Last Power State not understood");
1037 logger->info(TAG, "Config General/Last Power State not found");
1042 logger->info(TAG, "Config/Power After Boot not understood");
1048 logger->info(TAG, "Config General/Power After Boot not found");
1053 // Go S-Video if config says so
1055 config = vdr->configLoad("TV", "Connection");
1059 if (!STRCASECMP(config, "S-Video"))
1061 logger->info(TAG, "Switching to S-Video as Connection={}", config);
1062 video->setConnection(Video::SVIDEO);
1063 } else if (!STRCASECMP(config, "HDMI"))
1065 logger->info(TAG, "Switching to HDMI as Connection={}", config);
1066 video->setConnection(Video::HDMI);
1067 } else if (!STRCASECMP(config, "HDMI3D"))
1069 logger->info(TAG, "Switching to HDMI3D as Connection={}", config);
1070 video->setConnection(Video::HDMI3D);
1074 logger->info(TAG, "Switching to RGB/Composite as Connection={}", config);
1075 video->setConnection(Video::COMPOSITERGB);
1081 logger->info(TAG, "Config TV/S-Video not found");
1084 // Set to shutdown VDR if config says
1086 config = vdr->configLoad("General", "VDR shutdown");
1089 if (!STRCASECMP(config, "On"))
1091 logger->info(TAG, "Shutdown VDR when shutting down vomp");
1092 vdr->setVDRShutdown(true);
1094 else if (!STRCASECMP(config, "Off"))
1096 logger->info(TAG, "Shutdown only vomp");
1097 vdr->setVDRShutdown(false);
1102 logger->info(TAG, "Default shutdown only vomp");
1103 vdr->setVDRShutdown(false); // Default
1106 // Get TV aspect ratio
1108 config = vdr->configLoad("TV", "Aspect");
1111 if (!STRCASECMP(config, "16:9"))
1113 logger->info(TAG, "/// Switching to TV aspect 16:9");
1114 video->setTVsize(Video::ASPECT16X9);
1118 logger->info(TAG, "/// Switching to TV aspect 4:3");
1119 video->setTVsize(Video::ASPECT4X3);
1125 logger->info(TAG, "Config TV/Aspect type not found, going 4:3");
1126 video->setTVsize(Video::ASPECT4X3);
1129 config = vdr->configLoad("TV", "Widemode");
1132 if (!STRCASECMP(config, "Letterbox"))
1134 logger->info(TAG, "Setting letterbox mode");
1135 video->setMode(Video::LETTERBOX);
1139 logger->info(TAG, "Setting chop-sides mode");
1140 video->setMode(Video::NORMAL);
1147 logger->info(TAG, "Config TV/Widemode not found, Setting letterbox mode");
1148 video->setMode(Video::LETTERBOX);
1150 logger->info(TAG, "Config TV/Widemode not found, Setting chop-sides mode");
1151 video->setMode(Video::NORMAL);
1155 config = vdr->configLoad("Advanced", "TCP receive window");
1158 size_t newTCPsize = atoi(config);
1161 logger->info(TAG, "Setting TCP window size %i", newTCPsize);
1162 vdr->setReceiveWindow(newTCPsize);
1166 logger->info(TAG, "TCP window size not found, setting 2048");
1167 if (DEFAULT_TCP_WINDOWSIZE) vdr->setReceiveWindow(2048); // Default
1170 config = vdr->configLoad("Advanced", "Font Name");
1173 Osd::getInstance()->setFont(config);
1174 logger->info(TAG, "Setting Font to %s", config);
1180 // Set recording list type
1182 #ifdef ADVANCED_MENUES
1183 config = vdr->configLoad("Advanced", "Menu type");
1187 if (!STRCASECMP(config, "Advanced"))
1189 logger->info(TAG, "Switching to Advanced menu");
1194 logger->info(TAG, "Switching to Classic menu");
1201 logger->info(TAG, "Config General/menu type not found");
1206 config = vdr->configLoad("Advanced", "Disable WOL");
1209 if (!STRCASECMP(config, "Yes"))
1211 logger->info(TAG, "Config says disable WOL");
1212 Wol::getInstance()->setEnabled(false);
1216 logger->info(TAG, "Config says enable WOL");
1217 Wol::getInstance()->setEnabled(true);
1224 logger->info(TAG, "By default, enable WOL");
1225 Wol::getInstance()->setEnabled(true);
1227 /* device dependend config */
1228 audio->loadOptionsFromServer(vdr);
1229 video->loadOptionsFromServer(vdr);
1230 inputMan->loadOptionsFromServer(vdr);
1232 video->executePendingModeChanges();
1235 // Save power state = on
1237 vdr->configSave("General", "Last Power State", "On");
1239 // Make sure connection didn't die
1240 if (!vdr->isConnected())
1242 Control::getInstance()->connectionLost();
1246 boxstack->remove(vi);
1248 VWelcome* vw = new VWelcome();
1251 boxstack->update(vw);
1253 // Enter pre-keys here
1254 // handleCommand(Input::OK);
1255 // handleCommand(Input::THREE);
1256 // handleCommand(Input::SIX);
1257 // handleCommand(Input::UP);
1258 // handleCommand(Input::OK);
1259 // handleCommand(Input::OK);
1260 // handleCommand(Input::PLAY);
1261 // handleCommand(Input::DOWN);
1262 // handleCommand(Input::DOWN);
1263 // handleCommand(Input::DOWN);
1264 // handleCommand(Input::RIGHT);
1265 // handleCommand(Input::RED);