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;
390 Video::getInstance()->signalOn();
391 Led::getInstance()->on();
401 VConnect* vconnect = new VConnect(); // Deleted when VConnect messages Control, and is boxstack->remove()'d
402 boxstack->add(vconnect);
408 messageLoopRun = true;
413 boxstack->removeAllExceptWallpaper();
414 boxstack->remove(wallpaper);
415 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
418 void Control::dispatchMessage(Message* m)
420 logger->debug(TAG, "processing message {}", m->message);
422 if (m->p_to == Message::CONTROL)
426 case Message::SHUTDOWN:
428 messageLoopRun = false;
431 case Message::STOP_PLAYBACK:
433 handleCommand(Input::STOP); // an odd way of doing it, but so simple
436 case Message::VDR_CONNECTED:
438 doJustConnected(static_cast<VConnect*>(m->from));
441 case Message::SCREENSHOT:
443 logger->info(TAG, "Screenshot Message arrived");
444 Osd::getInstance()->screenShot("out.jpg");
447 case Message::CONNECTION_LOST:
452 case Message::INPUT_EVENT:
454 logger->info(TAG, "INPUT_EVENT {}", m->parameter);
456 handleCommand(m->parameter);
459 case Message::CHANGE_LANGUAGE:
461 boxstack->removeAllExceptWallpaper();
462 boxstack->update(wallpaper);
464 if (!VDR::getInstance()->isConnected()) { connectionLost(); break; }
465 VWelcome* vw = new VWelcome();
468 boxstack->update(vw);
471 case Message::LAST_VIEW_CLOSE:
473 // Shouldn't be done like this. Some generic message pass back from vinfo perhaps
480 // VWelcome* vw = new VWelcome();
482 // boxstack->add(vw);
483 // boxstack->update(vw);
489 else if (m->p_to == Message::BOXSTACK)
491 boxstack->processMessage(m);
493 else if (m->p_to == Message::MOUSE_RECEIVER)
495 logger->debug(TAG, "Sending mouse message to boxstack for dispatch");
496 boxstack->processMessage(m);
501 m->to->processMessage(m);
504 logger->debug(TAG, "done processing message {}", m->message);
507 void Control::handleCommand(int button)
509 if (isStandby && (button != Input::POWER)
510 && (button != Input::POWERON)
511 && (button != Input::POWEROFF)) return;
513 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
515 // command was not handled
519 case Input::VOLUMEUP:
520 case Input::VOLUMEDOWN:
522 if (inputMan->handlesVolume()) // CEC volume handler?
524 if (button == Input::VOLUMEDOWN)
525 inputMan->volumeDown();
527 inputMan->volumeUp();
531 VVolume* v = new VVolume();
533 v->handleCommand(button); // this will draw+show
539 if (inputMan->handlesVolume())
541 inputMan->volumeMute();
545 VMute* v = new VMute();
562 case Input::POWEROFF:
570 if (!connLost) return; // if connLost, handle Input::OK
576 logger->debug(TAG, "Handling sleeptimer go");
587 Message* m = new Message(); // break into master mutex
588 m->message = Message::SCREENSHOT;
589 m->p_to = Message::CONTROL;
595 void Control::doStandby()
608 void Control::doPowerOn()
612 Video::getInstance()->signalOn();
613 Led::getInstance()->on();
614 InputMan::getInstance()->changePowerState(true);
617 VConnect* vconnect = new VConnect();
618 boxstack->add(vconnect);
623 void Control::doPowerOff()
627 VDR::getInstance()->shutdownVDR();
628 boxstack->removeAllExceptWallpaper();
629 Video::getInstance()->signalOff();
630 boxstack->update(wallpaper);
632 VDR::getInstance()->configSave("General", "Last Power State", "Off");
633 logger->unsetExternLogger();
634 VDR::getInstance()->disconnect();
635 Led::getInstance()->off();
636 InputMan::getInstance()->changePowerState(false);
638 sleepTimer->shutdown();
642 void Control::doFromTheTop(bool which)
644 if (isStandby) return;
649 logger->info(TAG, "Connection lost dialog already present");
653 logger->info(TAG, "Doing connection lost dialog");
654 connLost = new VInfo();
655 connLost->setSize(360, 200);
656 connLost->createBuffer();
657 if (Video::getInstance()->getFormat() == Video::PAL)
658 connLost->setPosition(190, 170);
660 connLost->setPosition(180, 120);
661 connLost->setOneLiner(tr("Connection lost"));
662 connLost->setDropThrough();
663 connLost->setBorderOn(1);
664 connLost->setTitleBarColour(DrawStyle::DANGER);
665 connLost->okButton();
667 boxstack->add(connLost);
668 boxstack->update(connLost);
670 clearMQInputEvents();
674 logger->unsetExternLogger();
675 VDR::getInstance()->disconnect();
676 boxstack->removeAllExceptWallpaper();
677 boxstack->update(wallpaper);
682 // at this point, everything should be reset to first-go
684 VConnect* vconnect = new VConnect(); // deleted eventually in boxstack
685 boxstack->add(vconnect);
690 void Control::clearMQInputEvents()
692 std::lock_guard<std::mutex> lg(messageQueueMutex); // Get the lock
694 MQueueI i = messages.begin();
695 while(i != messages.end())
698 if (m->message == Message::INPUT_EVENT)
701 i = messages.erase(i);
710 void Control::doReboot()
713 logger->unsetExternLogger();
714 VDR::getInstance()->disconnect();
716 logger->info(TAG, "Reboot");
718 #ifndef VOMP_HAS_EXIT
719 // some plattforms, want a proper deinitialisation of their hardware before reboot
720 Osd::getInstance()->shutdown();
721 Audio::getInstance()->shutdown();
722 Video::getInstance()->shutdown();
723 InputMan::getInstance()->shutdown();
725 reboot(LINUX_REBOOT_CMD_RESTART);
726 // if reboot is not allowed -> stop
738 #endif //Would we support this on windows?
741 void Control::connectionLost()
743 logger->unsetExternLogger();
744 Message* m = new Message(); // break into master mutex
745 m->message = Message::CONNECTION_LOST;
746 m->p_to = Message::CONTROL;
750 void Control::buildCrashedBox()
752 VInfo* crash = new VInfo();
753 crash->setSize(360, 250);
754 crash->createBuffer();
755 if (Video::getInstance()->getFormat() == Video::PAL)
756 crash->setPosition(190, 146);
758 crash->setPosition(180, 96);
759 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.");
760 crash->setBorderOn(1);
761 crash->setTitleBarColour(DrawStyle::DANGER);
763 crash->setExitable();
765 boxstack->add(crash);
766 boxstack->update(crash);
769 int Control::getLangPref(bool subtitle, const char* langcode)
771 std::vector<struct ASLPref>::iterator itty=langcodes.begin();
772 char templangcode[4];
773 templangcode[0] = langcode[0];
774 templangcode[1] = langcode[1];
775 templangcode[2] = langcode[2];
776 templangcode[3] = '\0';
778 while (itty != langcodes.end())
780 size_t pos = (*itty).langcode.find(templangcode);
781 if (pos != std::string::npos)
783 //vector<struct ASLPref>::iterator itty2=langcodes.begin();
784 for (unsigned int i = 0; i < langcodes.size(); i++)
788 pref = langcodes[i].subtitlepref;
790 pref = langcodes[i].audiopref;
795 if (langcodes[i].subtitlepref==langpos) return i;
799 if (langcodes[i].audiopref==langpos) return i;
807 return langcodes.size(); //neutral
810 void Control::doJustConnected(VConnect* vconnect)
813 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
814 logger->info(TAG, "Entering doJustConnected");
816 boxstack->remove(vconnect);
818 VInfo* vi = new VInfo();
819 vi->setSize(400, 200);
821 if (video->getFormat() == Video::PAL)
822 vi->setPosition(170, 200);
824 vi->setPosition(160, 150);
825 vi->setOneLiner(tr("Connected, loading config"));
828 boxstack->update(vi);
830 // FIXME make config system
834 // See if we're supposed to do network logging
835 config = vdr->configLoad("Advanced", "Network logging");
836 if (config && !STRCASECMP(config, "On"))
838 logger->info(TAG, "Turning on network logging");
839 logger->setExternLogger(vdr);
843 logger->unsetExternLogger();
844 logger->info(TAG, "Turned off network logging");
846 if (config) delete[] config;
848 config = vdr->configLoad("Advanced", "Skin Name");
851 const char **skinnames=SkinFactory::getSkinNames();
852 for (int i=0;i<SkinFactory::getNumberofSkins();i++)
854 if (!STRCASECMP(config, skinnames[i]))
856 SkinFactory::InitSkin(i);
862 if (wallpaper && wallpaper_pict)
864 if (DrawStyle::WALLPAPER.alpha)
865 wallpaper_pict->setVisible(true);
867 wallpaper_pict->setVisible(false);
870 boxstack->update(wallpaper);
875 SkinFactory::InitSkin(0);
878 // See if config says to override video format (PAL/NTSC)
879 config = vdr->configLoad("General", "Override Video Format");
882 logger->debug(TAG, "Override Video Format is present");
884 if ( (!strcmp(config, "PAL") && (video->getFormat() != Video::PAL))
885 || (!strcmp(config, "NTSC") && (video->getFormat() != Video::NTSC))
886 || (!strcmp(config, "PAL_M") && (video->getFormat() != Video::PAL_M))
887 || (!strcmp(config, "NTSC_J") && (video->getFormat() != Video::NTSC_J))
890 // Oh sheesh, need to switch format. Bye bye TV...
892 // Take everything down
893 boxstack->removeAllExceptWallpaper();
894 boxstack->remove(wallpaper);
895 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
902 inputMan->shutdown(); // need on raspberry shut not do any harm, hopefully
903 inputMan->init(); // FIXME this breaks badly now
905 // Get video and osd back up with the new mode
906 if (!strcmp(config, "PAL"))
908 logger->debug(TAG, "Switching to PAL");
909 video->init(Video::PAL);
911 else if (!strcmp(config, "NTSC"))
913 logger->debug(TAG, "Switching to NTSC");
914 video->init(Video::NTSC);
915 } else if (!strcmp(config, "PAL_M"))
917 logger->debug(TAG, "Switching to PAL_M");
918 video->init(Video::PAL_M);
919 } else if (!strcmp(config, "NTSC_J"))
921 logger->debug(TAG, "Switching to NTSC_J");
922 video->init(Video::NTSC_J);
927 //we do not init twice
931 // Put the wallpaper back
936 vi->setSize(400, 200);
938 if (video->getFormat() == Video::PAL)
939 vi->setPosition(170, 200);
941 vi->setPosition(160, 150);
943 vi->setOneLiner(tr("Connected, loading config"));
946 boxstack->update(vi);
950 logger->debug(TAG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
955 logger->debug(TAG, "Phew, no dangerous on-the-fly mode switching to do!");
958 // Power off if first boot and config says so
963 logger->debug(TAG, "Load power after boot");
965 config = vdr->configLoad("General", "Power After Boot");
969 if (!STRCASECMP(config, "On"))
971 logger->info(TAG, "Config says Power After Boot = On");
973 else if (!STRCASECMP(config, "Off"))
975 logger->info(TAG, "Config says Power After Boot = Off");
980 else if (!STRCASECMP(config, "Last state"))
982 char* lastPowerState = vdr->configLoad("General", "Last Power State");
985 if (!STRCASECMP(lastPowerState, "On"))
987 logger->info(TAG, "Config says Last Power State = On");
989 else if (!STRCASECMP(lastPowerState, "Off"))
991 logger->info(TAG, "Config says Last Power State = Off");
998 logger->info(TAG, "Config General/Last Power State not understood");
1003 logger->info(TAG, "Config General/Last Power State not found");
1008 logger->info(TAG, "Config/Power After Boot not understood");
1014 logger->info(TAG, "Config General/Power After Boot not found");
1019 // Go S-Video if config says so
1021 config = vdr->configLoad("TV", "Connection");
1025 if (!STRCASECMP(config, "S-Video"))
1027 logger->info(TAG, "Switching to S-Video as Connection={}", config);
1028 video->setConnection(Video::SVIDEO);
1029 } else if (!STRCASECMP(config, "HDMI"))
1031 logger->info(TAG, "Switching to HDMI as Connection={}", config);
1032 video->setConnection(Video::HDMI);
1033 } else if (!STRCASECMP(config, "HDMI3D"))
1035 logger->info(TAG, "Switching to HDMI3D as Connection={}", config);
1036 video->setConnection(Video::HDMI3D);
1040 logger->info(TAG, "Switching to RGB/Composite as Connection={}", config);
1041 video->setConnection(Video::COMPOSITERGB);
1047 logger->info(TAG, "Config TV/S-Video not found");
1050 // Set to shutdown VDR if config says
1052 config = vdr->configLoad("General", "VDR shutdown");
1055 if (!STRCASECMP(config, "On"))
1057 logger->info(TAG, "Shutdown VDR when shutting down vomp");
1058 vdr->setVDRShutdown(true);
1060 else if (!STRCASECMP(config, "Off"))
1062 logger->info(TAG, "Shutdown only vomp");
1063 vdr->setVDRShutdown(false);
1068 logger->info(TAG, "Default shutdown only vomp");
1069 vdr->setVDRShutdown(false); // Default
1072 // Get TV aspect ratio
1074 config = vdr->configLoad("TV", "Aspect");
1077 if (!STRCASECMP(config, "16:9"))
1079 logger->info(TAG, "/// Switching to TV aspect 16:9");
1080 video->setTVsize(Video::ASPECT16X9);
1084 logger->info(TAG, "/// Switching to TV aspect 4:3");
1085 video->setTVsize(Video::ASPECT4X3);
1091 logger->info(TAG, "Config TV/Aspect type not found, going 4:3");
1092 video->setTVsize(Video::ASPECT4X3);
1095 config = vdr->configLoad("TV", "Widemode");
1098 if (!STRCASECMP(config, "Letterbox"))
1100 logger->info(TAG, "Setting letterbox mode");
1101 video->setMode(Video::LETTERBOX);
1105 logger->info(TAG, "Setting chop-sides mode");
1106 video->setMode(Video::NORMAL);
1113 logger->info(TAG, "Config TV/Widemode not found, Setting letterbox mode");
1114 video->setMode(Video::LETTERBOX);
1116 logger->info(TAG, "Config TV/Widemode not found, Setting chop-sides mode");
1117 video->setMode(Video::NORMAL);
1121 config = vdr->configLoad("Advanced", "TCP receive window");
1124 size_t newTCPsize = atoi(config);
1127 logger->info(TAG, "Setting TCP window size %i", newTCPsize);
1128 vdr->setReceiveWindow(newTCPsize);
1132 logger->info(TAG, "TCP window size not found, setting 2048");
1133 if (DEFAULT_TCP_WINDOWSIZE) vdr->setReceiveWindow(2048); // Default
1136 config = vdr->configLoad("Advanced", "Font Name");
1139 Osd::getInstance()->setFont(config);
1140 logger->info(TAG, "Setting Font to %s", config);
1146 // Set recording list type
1148 #ifdef ADVANCED_MENUES
1149 config = vdr->configLoad("Advanced", "Menu type");
1153 if (!STRCASECMP(config, "Advanced"))
1155 logger->info(TAG, "Switching to Advanced menu");
1160 logger->info(TAG, "Switching to Classic menu");
1167 logger->info(TAG, "Config General/menu type not found");
1172 config = vdr->configLoad("Advanced", "Disable WOL");
1175 if (!STRCASECMP(config, "Yes"))
1177 logger->info(TAG, "Config says disable WOL");
1178 Wol::getInstance()->setEnabled(false);
1182 logger->info(TAG, "Config says enable WOL");
1183 Wol::getInstance()->setEnabled(true);
1190 logger->info(TAG, "By default, enable WOL");
1191 Wol::getInstance()->setEnabled(true);
1193 /* device dependend config */
1194 audio->loadOptionsFromServer(vdr);
1195 video->loadOptionsFromServer(vdr);
1196 inputMan->loadOptionsFromServer(vdr);
1198 video->executePendingModeChanges();
1201 // Save power state = on
1203 vdr->configSave("General", "Last Power State", "On");
1205 // Make sure connection didn't die
1206 if (!vdr->isConnected())
1208 Control::getInstance()->connectionLost();
1212 boxstack->remove(vi);
1214 VWelcome* vw = new VWelcome();
1217 boxstack->update(vw);
1219 // Enter pre-keys here
1220 // handleCommand(Input::OK);
1221 // handleCommand(Input::THREE);
1222 // handleCommand(Input::SIX);
1223 // handleCommand(Input::UP);
1224 // handleCommand(Input::OK);
1225 // handleCommand(Input::OK);
1226 // handleCommand(Input::PLAY);
1227 // handleCommand(Input::DOWN);
1228 // handleCommand(Input::DOWN);
1229 // handleCommand(Input::DOWN);
1230 // handleCommand(Input::RIGHT);
1231 // handleCommand(Input::RED);