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 "vsleeptimer.h"
61 #include "osdvector.h"
65 Control* Control::instance = NULL;
79 Control* Control::getInstance()
84 bool Control::init(bool tcrashed)
86 if (initted) return false;
90 SkinFactory::InitSkin(0);
92 logger = Log::getInstance();
97 boxstack = new BoxStack(); if (!boxstack) throw 10;
98 if (!boxstack->init()) throw 20;
100 sleeptimer = new Sleeptimer(); if (!sleeptimer) throw 30;
102 wol = new Wol(); if (!wol) throw 40;
107 if (e == 10) logger->log("Control", Log::EMERG, "BoxStack module failed to create");
108 else if (e == 20) logger->log("Control", Log::EMERG, "BoxStack module failed to initialise");
109 else if (e == 30) logger->log("Control", Log::EMERG, "SleepTimer module failed to create");
110 else if (e == 40) logger->log("Control", Log::EMERG, "WOL module failed to create");
130 inputMan = InputMan::getInstance();
144 void Control::shutdown()
146 if (!initted) return;
152 logger->log("Control", Log::NOTICE, "WOL module shut down");
159 logger->log("Control", Log::NOTICE, "Sleeptimer module shut down");
164 boxstack->shutdown();
167 logger->log("Control", Log::NOTICE, "BoxStack module shut down");
174 logger->log("Control", Log::NOTICE, "Request stop");
176 Message* m = new Message(); // break master loop
177 m->message = Message::SHUTDOWN;
178 m->p_to = Message::CONTROL;
182 void Control::doWallpaper()
184 Video* video = Video::getInstance();
187 Boxx* bbg = new Boxx();
188 bbg->setSize(video->getScreenWidth(), video->getScreenHeight());
190 bbg->fillColour(DrawStyle::WALLPAPER);
192 boxstack->update(bbg);
193 boxstack->remove(bbg);
196 wallpaper = new Boxx();
197 wallpaper->setSize(video->getScreenWidth(), video->getScreenHeight());
198 wallpaper->createBuffer();
199 wallpaper->setBackgroundColour(DrawStyle::WALLPAPER);
201 wallpaper_pict = new WJpegTYPE();
202 wallpaper_pict->setSize(video->getScreenWidth(), video->getScreenHeight());
204 if (video->getFormat() == Video::PAL)
206 logger->log("Control", Log::DEBUG, "PAL wallpaper selected");
208 wallpaper_pict->init("/wallpaperPAL.jpg");
210 wallpaper_pict->init("wallpaperPAL.jpg");
215 logger->log("Control", Log::DEBUG, "NTSC wallpaper selected");
216 wallpaper_pict->init("/wallpaperNTSC.jpg");
219 if (DrawStyle::WALLPAPER.alpha)
220 wallpaper_pict->setVisible(true);
222 wallpaper_pict->setVisible(false);
224 wallpaper->add(wallpaper_pict);
227 boxstack->add(wallpaper);
228 boxstack->update(wallpaper);
230 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
231 if (osdv) osdv->updateBackgroundColor(DrawStyle::WALLPAPER);
236 if (!initted) return;
240 Video::getInstance()->signalOn();
241 Led::getInstance()->on();
251 VConnect* vconnect = new VConnect();
252 boxstack->add(vconnect);
257 // FIXME Input::NA_SIGNAL is possibly obsolete now
259 std::unique_lock<std::mutex> lockWrapper(messageQueueMutex); // locks. unlocks on out-of-scope
265 messageQueueCond.wait(lockWrapper, [&] { return !irun || !messages.empty(); });
266 logger->log("Control", Log::DEBUG, "woke");
270 while(!messages.empty())
272 Message* m = messages.front();
273 messages.pop_front();
275 lockWrapper.unlock();
286 boxstack->removeAllExceptWallpaper();
287 boxstack->remove(wallpaper);
288 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
291 void Control::processMessage(Message* m)
293 // FIXME - a slight modification - how if messagereceivers were to register
294 // themselves as receivers to avoid the calling-a-deleted-object problem
295 // then only deliver/register/unregister would have to be protected
297 logger->log("Control", Log::DEBUG, "processing message %i", m->message);
300 if ((m->p_to == Message::CONTROL) || (m->to == this)) // Maybe don't check m->to here? Always use predefined?
304 case Message::SHUTDOWN:
310 case Message::STOP_PLAYBACK:
312 handleCommand(Input::STOP); // an odd way of doing it, but so simple
315 // Also connection_lost comes from player - anywhere else?
319 case Message::VDR_CONNECTED:
321 doJustConnected(static_cast<VConnect*>(m->from));
324 case Message::SCREENSHOT:
326 logger->log("Osd", Log::NOTICE, "Screenshot Message arrived");
327 Osd::getInstance()->screenShot("out.jpg");
330 case Message::CONNECTION_LOST:
335 case Message::INPUT_EVENT:
337 logger->log("Control", Log::NOTICE, "INPUT_EVENT %i", m->parameter);
339 handleCommand(m->parameter);
342 case Message::CHANGE_LANGUAGE:
344 boxstack->removeAllExceptWallpaper();
345 boxstack->update(wallpaper);
347 if (!VDR::getInstance()->isConnected()) { connectionLost(); break; }
348 VWelcome* vw = new VWelcome();
351 boxstack->update(vw);
354 case Message::LAST_VIEW_CLOSE:
356 // Shouldn't be done like this. Some generic message pass back from vinfo perhaps
363 // VWelcome* vw = new VWelcome();
365 // boxstack->add(vw);
366 // boxstack->update(vw);
370 case Message::NEW_PICTURE:
372 //Log::getInstance()->log("Control", Log::DEBUG, "TVMedia NEW_PICTURE");
373 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
374 if (osdv) osdv->informPicture(m->tag, reinterpret_cast<ImageIndex>(m->data));
377 case Message::NEW_PICTURE_STATIC:
379 //Log::getInstance()->log("Control", Log::DEBUG, "TVMedia NEW_PICTURE %x %x",m->tag,m->parameter);
380 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
381 if (osdv) osdv->informPicture(static_cast<unsigned long long>(m->tag) << 32LL, reinterpret_cast<ImageIndex>(m->data));
390 Instead of sending through the boxstack, implement a more generic MessageReceiver interface
391 and have potential receivers register with something
392 When a message needs to be delivered, check if the receiver is still registered, if so, deliver the message
393 This could all be done using the existing big control mutex to keep it simple
396 logger->log("Control", Log::DEBUG, "Sending message to boxstack");
397 boxstack->processMessage(m);
401 void Control::handleCommand(int button)
403 if (isStandby && (button != Input::POWER)
404 && (button != Input::POWERON)
405 && (button != Input::POWEROFF)) return;
407 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
409 // command was not handled
413 case Input::VOLUMEUP:
414 case Input::VOLUMEDOWN:
416 if (inputMan->handlesVolume()) // CEC volume handler?
418 if (button == Input::VOLUMEDOWN)
419 inputMan->volumeDown();
421 inputMan->volumeUp();
425 VVolume* v = new VVolume();
427 v->handleCommand(button); // this will draw+show
433 if (inputMan->handlesVolume())
435 inputMan->volumeMute();
439 VMute* v = new VMute();
456 case Input::POWEROFF:
464 if (!connLost) return; // if connLost, handle Input::OK
470 VSleeptimer* sleep = new VSleeptimer();
471 boxstack->add(sleep);
472 sleep->handleCommand(button); // this will draw+show
482 Message* m = new Message(); // break into master mutex
483 m->message = Message::SCREENSHOT;
490 void Control::doStandby()
503 void Control::doPowerOn()
507 Video::getInstance()->signalOn();
508 Led::getInstance()->on();
509 InputMan::getInstance()->changePowerState(true);
512 VConnect* vconnect = new VConnect();
513 boxstack->add(vconnect);
518 void Control::doPowerOff()
522 VDR::getInstance()->shutdownVDR();
523 boxstack->removeAllExceptWallpaper();
524 Video::getInstance()->signalOff();
525 boxstack->update(wallpaper);
527 VDR::getInstance()->configSave("General", "Last Power State", "Off");
528 logger->unsetExternLogger();
529 VDR::getInstance()->disconnect();
530 Led::getInstance()->off();
531 InputMan::getInstance()->changePowerState(false);
533 Sleeptimer::getInstance()->shutdown();
537 void Control::doFromTheTop(bool which)
539 if (isStandby) return;
544 logger->log("Control", Log::NOTICE, "Connection lost dialog already present");
548 logger->log("Control", Log::NOTICE, "Doing connection lost dialog");
549 connLost = new VInfo();
550 connLost->setSize(360, 200);
551 connLost->createBuffer();
552 if (Video::getInstance()->getFormat() == Video::PAL)
553 connLost->setPosition(190, 170);
555 connLost->setPosition(180, 120);
556 connLost->setOneLiner(tr("Connection lost"));
557 connLost->setDropThrough();
558 connLost->setBorderOn(1);
559 connLost->setTitleBarColour(DrawStyle::DANGER);
560 connLost->okButton();
562 boxstack->add(connLost);
563 boxstack->update(connLost);
565 clearMQInputEvents();
569 logger->unsetExternLogger();
570 VDR::getInstance()->disconnect();
571 boxstack->removeAllExceptWallpaper();
572 boxstack->update(wallpaper);
577 // at this point, everything should be reset to first-go
579 VConnect* vconnect = new VConnect();
580 boxstack->add(vconnect);
585 void Control::clearMQInputEvents()
587 std::lock_guard<std::mutex> lg(messageQueueMutex); // Get the lock
589 MQueueI i = messages.begin();
590 while(i != messages.end())
593 if (m->message == Message::INPUT_EVENT)
596 i = messages.erase(i);
605 void Control::doReboot()
608 logger->unsetExternLogger();
609 VDR::getInstance()->disconnect();
611 logger->log("Control", Log::NOTICE, "Reboot");
613 #ifndef VOMP_HAS_EXIT
614 // some plattforms, want a proper deinitialisation of their hardware before reboot
615 Osd::getInstance()->shutdown();
616 Audio::getInstance()->shutdown();
617 Video::getInstance()->shutdown();
618 InputMan::getInstance()->shutdown();
620 reboot(LINUX_REBOOT_CMD_RESTART);
621 // if reboot is not allowed -> stop
633 #endif //Would we support this on windows?
636 void Control::connectionLost()
638 logger->unsetExternLogger();
639 Message* m = new Message(); // break into master mutex
640 m->message = Message::CONNECTION_LOST;
641 m->p_to = Message::CONTROL;
645 void Control::buildCrashedBox()
647 VInfo* crash = new VInfo();
648 crash->setSize(360, 250);
649 crash->createBuffer();
650 if (Video::getInstance()->getFormat() == Video::PAL)
651 crash->setPosition(190, 146);
653 crash->setPosition(180, 96);
654 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.");
655 crash->setBorderOn(1);
656 crash->setTitleBarColour(DrawStyle::DANGER);
658 crash->setExitable();
660 boxstack->add(crash);
661 boxstack->update(crash);
664 int Control::getLangPref(bool subtitle, const char* langcode)
666 std::vector<struct ASLPref>::iterator itty=langcodes.begin();
667 char templangcode[4];
668 templangcode[0] = langcode[0];
669 templangcode[1] = langcode[1];
670 templangcode[2] = langcode[2];
671 templangcode[3] = '\0';
673 while (itty != langcodes.end())
675 size_t pos = (*itty).langcode.find(templangcode);
676 if (pos != std::string::npos)
678 //vector<struct ASLPref>::iterator itty2=langcodes.begin();
679 for (unsigned int i = 0; i < langcodes.size(); i++)
683 pref = langcodes[i].subtitlepref;
685 pref = langcodes[i].audiopref;
690 if (langcodes[i].subtitlepref==langpos) return i;
694 if (langcodes[i].audiopref==langpos) return i;
702 return langcodes.size(); //neutral
705 void Control::doJustConnected(VConnect* vconnect)
708 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
709 logger->log("Control", Log::INFO, "Entering doJustConnected");
711 Video* video = Video::getInstance();
712 Audio* audio = Audio::getInstance();
713 boxstack->remove(vconnect);
715 VInfo* vi = new VInfo();
716 vi->setSize(400, 200);
718 if (video->getFormat() == Video::PAL)
719 vi->setPosition(170, 200);
721 vi->setPosition(160, 150);
722 vi->setOneLiner(tr("Connected, loading config"));
725 boxstack->update(vi);
727 // FIXME make config system
729 VDR* vdr = VDR::getInstance();
732 // See if we're supposed to do network logging
733 config = vdr->configLoad("Advanced", "Network logging");
734 if (config && !STRCASECMP(config, "On"))
736 logger->log("Control", Log::INFO, "Turning on network logging");
737 logger->setExternLogger(vdr);
741 logger->unsetExternLogger();
742 logger->log("Control", Log::INFO, "Turned off network logging");
744 if (config) delete[] config;
746 config = vdr->configLoad("Advanced", "Skin Name");
749 const char **skinnames=SkinFactory::getSkinNames();
750 for (int i=0;i<SkinFactory::getNumberofSkins();i++)
752 if (!STRCASECMP(config, skinnames[i]))
754 SkinFactory::InitSkin(i);
760 if (wallpaper && wallpaper_pict)
762 if (DrawStyle::WALLPAPER.alpha)
763 wallpaper_pict->setVisible(true);
765 wallpaper_pict->setVisible(false);
768 boxstack->update(wallpaper);
773 SkinFactory::InitSkin(0);
776 // See if config says to override video format (PAL/NTSC)
777 config = vdr->configLoad("General", "Override Video Format");
780 logger->log("Control", Log::DEBUG, "Override Video Format is present");
782 if ( (!strcmp(config, "PAL") && (video->getFormat() != Video::PAL))
783 || (!strcmp(config, "NTSC") && (video->getFormat() != Video::NTSC))
784 || (!strcmp(config, "PAL_M") && (video->getFormat() != Video::PAL_M))
785 || (!strcmp(config, "NTSC_J") && (video->getFormat() != Video::NTSC_J))
788 // Oh sheesh, need to switch format. Bye bye TV...
790 // Take everything down
791 boxstack->removeAllExceptWallpaper();
792 boxstack->remove(wallpaper);
793 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
795 Osd* osd = Osd::getInstance();
801 inputMan->shutdown(); // need on raspberry shut not do any harm, hopefully
802 inputMan->init(); // FIXME this breaks badly now
804 // Get video and osd back up with the new mode
805 if (!strcmp(config, "PAL"))
807 logger->log("Control", Log::DEBUG, "Switching to PAL");
808 video->init(Video::PAL);
810 else if (!strcmp(config, "NTSC"))
812 logger->log("Control", Log::DEBUG, "Switching to NTSC");
813 video->init(Video::NTSC);
814 } else if (!strcmp(config, "PAL_M"))
816 logger->log("Control", Log::DEBUG, "Switching to PAL_M");
817 video->init(Video::PAL_M);
818 } else if (!strcmp(config, "NTSC_J"))
820 logger->log("Control", Log::DEBUG, "Switching to NTSC_J");
821 video->init(Video::NTSC_J);
826 //we do not init twice
830 // Put the wallpaper back
835 vi->setSize(400, 200);
837 if (video->getFormat() == Video::PAL)
838 vi->setPosition(170, 200);
840 vi->setPosition(160, 150);
842 vi->setOneLiner(tr("Connected, loading config"));
845 boxstack->update(vi);
849 logger->log("Control", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
854 logger->log("Control", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
857 // Power off if first boot and config says so
862 logger->log("Control", Log::DEBUG, "Load power after boot");
864 config = vdr->configLoad("General", "Power After Boot");
868 if (!STRCASECMP(config, "On"))
870 logger->log("Control", Log::INFO, "Config says Power After Boot = On");
872 else if (!STRCASECMP(config, "Off"))
874 logger->log("Control", Log::INFO, "Config says Power After Boot = Off");
879 else if (!STRCASECMP(config, "Last state"))
881 char* lastPowerState = vdr->configLoad("General", "Last Power State");
884 if (!STRCASECMP(lastPowerState, "On"))
886 logger->log("Control", Log::INFO, "Config says Last Power State = On");
888 else if (!STRCASECMP(lastPowerState, "Off"))
890 logger->log("Control", Log::INFO, "Config says Last Power State = Off");
897 logger->log("Control", Log::INFO, "Config General/Last Power State not understood");
902 logger->log("Control", Log::INFO, "Config General/Last Power State not found");
907 logger->log("Control", Log::INFO, "Config/Power After Boot not understood");
913 logger->log("Control", Log::INFO, "Config General/Power After Boot not found");
918 // Go S-Video if config says so
920 config = vdr->configLoad("TV", "Connection");
924 if (!STRCASECMP(config, "S-Video"))
926 logger->log("Control", Log::INFO, "Switching to S-Video as Connection=%s", config);
927 video->setConnection(Video::SVIDEO);
928 } else if (!STRCASECMP(config, "HDMI"))
930 logger->log("Control", Log::INFO, "Switching to HDMI as Connection=%s", config);
931 video->setConnection(Video::HDMI);
932 } else if (!STRCASECMP(config, "HDMI3D"))
934 logger->log("Control", Log::INFO, "Switching to HDMI3D as Connection=%s", config);
935 video->setConnection(Video::HDMI3D);
939 logger->log("Control", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
940 video->setConnection(Video::COMPOSITERGB);
946 logger->log("Control", Log::INFO, "Config TV/S-Video not found");
949 // Set to shutdown VDR if config says
951 config = vdr->configLoad("General", "VDR shutdown");
954 if (!STRCASECMP(config, "On"))
956 logger->log("Control", Log::INFO, "Shutdown VDR when shutting down vomp");
957 vdr->setVDRShutdown(true);
959 else if (!STRCASECMP(config, "Off"))
961 logger->log("Control", Log::INFO, "Shutdown only vomp");
962 vdr->setVDRShutdown(false);
967 logger->log("Control", Log::INFO, "Default shutdown only vomp");
968 vdr->setVDRShutdown(false); // Default
971 // Get TV aspect ratio
973 config = vdr->configLoad("TV", "Aspect");
976 if (!STRCASECMP(config, "16:9"))
978 logger->log("Control", Log::INFO, "/// Switching to TV aspect 16:9");
979 video->setTVsize(Video::ASPECT16X9);
983 logger->log("Control", Log::INFO, "/// Switching to TV aspect 4:3");
984 video->setTVsize(Video::ASPECT4X3);
990 logger->log("Control", Log::INFO, "Config TV/Aspect type not found, going 4:3");
991 video->setTVsize(Video::ASPECT4X3);
994 config = vdr->configLoad("TV", "Widemode");
997 if (!STRCASECMP(config, "Letterbox"))
999 logger->log("Control", Log::INFO, "Setting letterbox mode");
1000 video->setMode(Video::LETTERBOX);
1004 logger->log("Control", Log::INFO, "Setting chop-sides mode");
1005 video->setMode(Video::NORMAL);
1012 logger->log("Control", Log::INFO, "Config TV/Widemode not found, Setting letterbox mode");
1013 video->setMode(Video::LETTERBOX);
1015 logger->log("Control", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
1016 video->setMode(Video::NORMAL);
1020 config = vdr->configLoad("Advanced", "TCP receive window");
1023 size_t newTCPsize = atoi(config);
1026 logger->log("Control", Log::INFO, "Setting TCP window size %i", newTCPsize);
1027 vdr->setReceiveWindow(newTCPsize);
1031 logger->log("Control", Log::INFO, "TCP window size not found, setting 2048");
1032 if (DEFAULT_TCP_WINDOWSIZE) vdr->setReceiveWindow(2048); // Default
1035 config = vdr->configLoad("Advanced", "Font Name");
1038 Osd::getInstance()->setFont(config);
1039 logger->log("Control", Log::INFO, "Setting Font to %s", config);
1045 // Set recording list type
1047 #ifdef ADVANCED_MENUES
1048 config = vdr->configLoad("Advanced", "Menu type");
1052 if (!STRCASECMP(config, "Advanced"))
1054 logger->log("Control", Log::INFO, "Switching to Advanced menu");
1059 logger->log("Control", Log::INFO, "Switching to Classic menu");
1066 logger->log("Control", Log::INFO, "Config General/menu type not found");
1071 config = vdr->configLoad("Advanced", "Disable WOL");
1074 if (!STRCASECMP(config, "Yes"))
1076 logger->log("Control", Log::INFO, "Config says disable WOL");
1077 Wol::getInstance()->setEnabled(false);
1081 logger->log("Control", Log::INFO, "Config says enable WOL");
1082 Wol::getInstance()->setEnabled(true);
1089 logger->log("Control", Log::INFO, "By default, enable WOL");
1090 Wol::getInstance()->setEnabled(true);
1092 /* device dependend config */
1093 audio->loadOptionsFromServer(vdr);
1094 video->loadOptionsFromServer(vdr);
1095 inputMan->loadOptionsFromServer(vdr);
1097 video->executePendingModeChanges();
1100 // Save power state = on
1102 vdr->configSave("General", "Last Power State", "On");
1104 // Make sure connection didn't die
1105 if (!vdr->isConnected())
1107 Control::getInstance()->connectionLost();
1111 boxstack->remove(vi);
1113 VWelcome* vw = new VWelcome();
1116 boxstack->update(vw);
1118 // Enter pre-keys here
1119 // handleCommand(Input::OK);
1120 // handleCommand(Input::THREE);
1121 // handleCommand(Input::SIX);
1122 // handleCommand(Input::OK);
1123 // handleCommand(Input::UP);
1124 // handleCommand(Input::PLAY);
1125 // handleCommand(Input::DOWN);
1126 // handleCommand(Input::DOWN);
1127 // handleCommand(Input::DOWN);
1128 // handleCommand(Input::RIGHT);
1129 // handleCommand(Input::RED);