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
30 #include "inputandroid.h"
38 #include "vserverselect.h"
54 #include "vsleeptimer.h"
56 #include "osdvector.h"
59 Control* Control::instance = NULL;
73 Control* Control::getInstance()
78 int Control::init(bool tcrashed)
80 if (initted) return 0;
84 logger = Log::getInstance();
85 boxstack = BoxStack::getInstance();
86 inputMan = InputMan::getInstance();
88 if (!logger || !boxstack || !inputMan)
94 SkinFactory::InitSkin(0);
99 int Control::shutdown()
101 if (!initted) return 0;
108 logger->log("Control", Log::NOTICE, "Request stop");
110 Message* m = new Message(); // break master loop
111 m->message = Message::SHUTDOWN;
112 m->p_to = Message::CONTROL;
116 void Control::doWallpaper()
118 Video* video = Video::getInstance();
121 Boxx* bbg = new Boxx();
122 bbg->setSize(video->getScreenWidth(), video->getScreenHeight());
124 bbg->fillColour(DrawStyle::WALLPAPER);
126 boxstack->update(bbg);
127 boxstack->remove(bbg);
130 wallpaper = new Boxx();
131 wallpaper->setSize(video->getScreenWidth(), video->getScreenHeight());
132 wallpaper->createBuffer();
133 wallpaper->setBackgroundColour(DrawStyle::WALLPAPER);
135 wallpaper_pict = new WJpegTYPE();
136 wallpaper_pict->setSize(video->getScreenWidth(), video->getScreenHeight());
138 if (video->getFormat() == Video::PAL)
140 logger->log("Control", Log::DEBUG, "PAL wallpaper selected");
142 wallpaper_pict->init("/wallpaperPAL.jpg");
144 wallpaper_pict->init("wallpaperPAL.jpg");
149 logger->log("Control", Log::DEBUG, "NTSC wallpaper selected");
150 wallpaper_pict->init("/wallpaperNTSC.jpg");
153 if (DrawStyle::WALLPAPER.alpha)
154 wallpaper_pict->setVisible(true);
156 wallpaper_pict->setVisible(false);
158 wallpaper->add(wallpaper_pict);
161 boxstack->add(wallpaper);
162 boxstack->update(wallpaper);
164 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
165 if (osdv) osdv->updateBackgroundColor(DrawStyle::WALLPAPER);
170 if (!initted) return;
174 Video::getInstance()->signalOn();
175 Led::getInstance()->on();
185 VConnect* vconnect = new VConnect();
186 boxstack->add(vconnect);
191 // FIXME Input::NA_SIGNAL is possibly obsolete now
193 std::unique_lock<std::mutex> lockWrapper(messageQueueMutex); // locks. unlocks on out-of-scope
199 messageQueueCond.wait(lockWrapper, [&] { return !irun || !messages.empty(); });
200 logger->log("Control", Log::DEBUG, "woke");
204 while(!messages.empty())
206 Message* m = messages.front();
207 messages.pop_front();
209 lockWrapper.unlock();
220 boxstack->removeAllExceptWallpaper();
221 boxstack->remove(wallpaper);
222 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
225 void Control::processMessage(Message* m)
227 // FIXME - a slight modification - how if messagereceivers were to register
228 // themselves as receivers to avoid the calling-a-deleted-object problem
229 // then only deliver/register/unregister would have to be protected
231 logger->log("Control", Log::DEBUG, "processing message %i", m->message);
234 if ((m->p_to == Message::CONTROL) || (m->to == this)) // Maybe don't check m->to here? Always use predefined?
238 case Message::SHUTDOWN:
244 case Message::STOP_PLAYBACK:
246 handleCommand(Input::STOP); // an odd way of doing it, but so simple
249 // Also connection_lost comes from player - anywhere else?
253 case Message::VDR_CONNECTED:
255 doJustConnected(static_cast<VConnect*>(m->from));
258 case Message::SCREENSHOT:
260 logger->log("Osd", Log::NOTICE, "Screenshot Message arrived");
261 Osd::getInstance()->screenShot("out.jpg");
264 case Message::CONNECTION_LOST:
269 case Message::INPUT_EVENT:
271 logger->log("Control", Log::NOTICE, "INPUT_EVENT %i", m->parameter);
273 handleCommand(m->parameter);
276 case Message::CHANGE_LANGUAGE:
278 boxstack->removeAllExceptWallpaper();
279 boxstack->update(wallpaper);
281 if (!VDR::getInstance()->isConnected()) { connectionLost(); break; }
282 VWelcome* vw = new VWelcome();
285 boxstack->update(vw);
288 case Message::LAST_VIEW_CLOSE:
290 // Shouldn't be done like this. Some generic message pass back from vinfo perhaps
297 // VWelcome* vw = new VWelcome();
299 // boxstack->add(vw);
300 // boxstack->update(vw);
304 case Message::NEW_PICTURE:
306 //Log::getInstance()->log("Control", Log::DEBUG, "TVMedia NEW_PICTURE");
307 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
308 if (osdv) osdv->informPicture(m->tag, reinterpret_cast<ImageIndex>(m->data));
311 case Message::NEW_PICTURE_STATIC:
313 //Log::getInstance()->log("Control", Log::DEBUG, "TVMedia NEW_PICTURE %x %x",m->tag,m->parameter);
314 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
315 if (osdv) osdv->informPicture(static_cast<unsigned long long>(m->tag) << 32LL, reinterpret_cast<ImageIndex>(m->data));
324 Instead of sending through the boxstack, implement a more generic MessageReceiver interface
325 and have potential receivers register with something
326 When a message needs to be delivered, check if the receiver is still registered, if so, deliver the message
327 This could all be done using the existing big control mutex to keep it simple
330 logger->log("Control", Log::DEBUG, "Sending message to boxstack");
331 boxstack->processMessage(m);
335 void Control::handleCommand(int button)
337 if (isStandby && (button != Input::POWER)
338 && (button != Input::POWERON)
339 && (button != Input::POWEROFF)) return;
341 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
343 // command was not handled
347 case Input::VOLUMEUP:
348 case Input::VOLUMEDOWN:
350 if (inputMan->handlesVolume()) // CEC volume handler?
352 if (button == Input::VOLUMEDOWN)
353 inputMan->volumeDown();
355 inputMan->volumeUp();
359 VVolume* v = new VVolume();
361 v->handleCommand(button); // this will draw+show
367 if (inputMan->handlesVolume())
369 inputMan->volumeMute();
373 VMute* v = new VMute();
390 case Input::POWEROFF:
398 if (!connLost) return; // if connLost, handle Input::OK
404 VSleeptimer* sleep = new VSleeptimer();
405 boxstack->add(sleep);
406 sleep->handleCommand(button); // this will draw+show
416 Message* m = new Message(); // break into master mutex
417 m->message = Message::SCREENSHOT;
424 void Control::doStandby()
437 void Control::doPowerOn()
441 Video::getInstance()->signalOn();
442 Led::getInstance()->on();
443 InputMan::getInstance()->changePowerState(true);
446 VConnect* vconnect = new VConnect();
447 boxstack->add(vconnect);
452 void Control::doPowerOff()
456 VDR::getInstance()->shutdownVDR();
457 boxstack->removeAllExceptWallpaper();
458 Video::getInstance()->signalOff();
459 boxstack->update(wallpaper);
461 VDR::getInstance()->configSave("General", "Last Power State", "Off");
462 logger->unsetExternLogger();
463 VDR::getInstance()->disconnect();
464 Led::getInstance()->off();
465 InputMan::getInstance()->changePowerState(false);
467 Sleeptimer::getInstance()->shutdown();
471 void Control::doFromTheTop(bool which)
473 if (isStandby) return;
478 logger->log("Control", Log::NOTICE, "Connection lost dialog already present");
482 logger->log("Control", Log::NOTICE, "Doing connection lost dialog");
483 connLost = new VInfo();
484 connLost->setSize(360, 200);
485 connLost->createBuffer();
486 if (Video::getInstance()->getFormat() == Video::PAL)
487 connLost->setPosition(190, 170);
489 connLost->setPosition(180, 120);
490 connLost->setOneLiner(tr("Connection lost"));
491 connLost->setDropThrough();
492 connLost->setBorderOn(1);
493 connLost->setTitleBarColour(DrawStyle::DANGER);
494 connLost->okButton();
496 boxstack->add(connLost);
497 boxstack->update(connLost);
499 clearMQInputEvents();
503 logger->unsetExternLogger();
504 VDR::getInstance()->disconnect();
505 boxstack->removeAllExceptWallpaper();
506 boxstack->update(wallpaper);
511 // at this point, everything should be reset to first-go
513 VConnect* vconnect = new VConnect();
514 boxstack->add(vconnect);
519 void Control::clearMQInputEvents()
521 std::lock_guard<std::mutex> lg(messageQueueMutex); // Get the lock
523 MQueueI i = messages.begin();
524 while(i != messages.end())
527 if (m->message == Message::INPUT_EVENT)
530 i = messages.erase(i);
539 void Control::doReboot()
542 logger->unsetExternLogger();
543 VDR::getInstance()->disconnect();
545 logger->log("Control", Log::NOTICE, "Reboot");
547 #ifndef VOMP_HAS_EXIT
548 // some plattforms, want a proper deinitialisation of their hardware before reboot
549 Osd::getInstance()->shutdown();
550 Audio::getInstance()->shutdown();
551 Video::getInstance()->shutdown();
552 InputMan::getInstance()->shutdown();
554 reboot(LINUX_REBOOT_CMD_RESTART);
555 // if reboot is not allowed -> stop
567 #endif //Would we support this on windows?
570 void Control::connectionLost()
572 logger->unsetExternLogger();
573 Message* m = new Message(); // break into master mutex
574 m->message = Message::CONNECTION_LOST;
575 m->p_to = Message::CONTROL;
579 void Control::buildCrashedBox()
581 VInfo* crash = new VInfo();
582 crash->setSize(360, 250);
583 crash->createBuffer();
584 if (Video::getInstance()->getFormat() == Video::PAL)
585 crash->setPosition(190, 146);
587 crash->setPosition(180, 96);
588 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.");
589 crash->setBorderOn(1);
590 crash->setTitleBarColour(DrawStyle::DANGER);
592 crash->setExitable();
594 boxstack->add(crash);
595 boxstack->update(crash);
598 int Control::getLangPref(bool subtitle, const char* langcode)
600 std::vector<struct ASLPref>::iterator itty=langcodes.begin();
601 char templangcode[4];
602 templangcode[0] = langcode[0];
603 templangcode[1] = langcode[1];
604 templangcode[2] = langcode[2];
605 templangcode[3] = '\0';
607 while (itty != langcodes.end())
609 size_t pos = (*itty).langcode.find(templangcode);
610 if (pos != std::string::npos)
612 //vector<struct ASLPref>::iterator itty2=langcodes.begin();
613 for (unsigned int i = 0; i < langcodes.size(); i++)
617 pref = langcodes[i].subtitlepref;
619 pref = langcodes[i].audiopref;
624 if (langcodes[i].subtitlepref==langpos) return i;
628 if (langcodes[i].audiopref==langpos) return i;
636 return langcodes.size(); //neutral
639 void Control::doJustConnected(VConnect* vconnect)
642 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
643 logger->log("Control", Log::INFO, "Entering doJustConnected");
645 Video* video = Video::getInstance();
646 Audio* audio = Audio::getInstance();
647 boxstack->remove(vconnect);
649 VInfo* vi = new VInfo();
650 vi->setSize(400, 200);
652 if (video->getFormat() == Video::PAL)
653 vi->setPosition(170, 200);
655 vi->setPosition(160, 150);
656 vi->setOneLiner(tr("Connected, loading config"));
659 boxstack->update(vi);
661 // FIXME make config system
663 VDR* vdr = VDR::getInstance();
666 // See if we're supposed to do network logging
667 config = vdr->configLoad("Advanced", "Network logging");
668 if (config && !STRCASECMP(config, "On"))
670 logger->log("Control", Log::INFO, "Turning on network logging");
671 logger->setExternLogger(vdr);
675 logger->unsetExternLogger();
676 logger->log("Control", Log::INFO, "Turned off network logging");
678 if (config) delete[] config;
680 config = vdr->configLoad("Advanced", "Skin Name");
683 const char **skinnames=SkinFactory::getSkinNames();
684 for (int i=0;i<SkinFactory::getNumberofSkins();i++)
686 if (!STRCASECMP(config, skinnames[i]))
688 SkinFactory::InitSkin(i);
694 if (wallpaper && wallpaper_pict)
696 if (DrawStyle::WALLPAPER.alpha)
697 wallpaper_pict->setVisible(true);
699 wallpaper_pict->setVisible(false);
702 boxstack->update(wallpaper);
707 SkinFactory::InitSkin(0);
710 // See if config says to override video format (PAL/NTSC)
711 config = vdr->configLoad("General", "Override Video Format");
714 logger->log("Control", Log::DEBUG, "Override Video Format is present");
716 if ( (!strcmp(config, "PAL") && (video->getFormat() != Video::PAL))
717 || (!strcmp(config, "NTSC") && (video->getFormat() != Video::NTSC))
718 || (!strcmp(config, "PAL_M") && (video->getFormat() != Video::PAL_M))
719 || (!strcmp(config, "NTSC_J") && (video->getFormat() != Video::NTSC_J))
722 // Oh sheesh, need to switch format. Bye bye TV...
724 // Take everything down
725 boxstack->removeAllExceptWallpaper();
726 boxstack->remove(wallpaper);
727 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
729 Osd* osd = Osd::getInstance();
735 inputMan->shutdown(); // need on raspberry shut not do any harm, hopefully
736 inputMan->init(); // FIXME this breaks badly now
738 // Get video and osd back up with the new mode
739 if (!strcmp(config, "PAL"))
741 logger->log("Control", Log::DEBUG, "Switching to PAL");
742 video->init(Video::PAL);
744 else if (!strcmp(config, "NTSC"))
746 logger->log("Control", Log::DEBUG, "Switching to NTSC");
747 video->init(Video::NTSC);
748 } else if (!strcmp(config, "PAL_M"))
750 logger->log("Control", Log::DEBUG, "Switching to PAL_M");
751 video->init(Video::PAL_M);
752 } else if (!strcmp(config, "NTSC_J"))
754 logger->log("Control", Log::DEBUG, "Switching to NTSC_J");
755 video->init(Video::NTSC_J);
760 //we do not init twice
764 // Put the wallpaper back
769 vi->setSize(400, 200);
771 if (video->getFormat() == Video::PAL)
772 vi->setPosition(170, 200);
774 vi->setPosition(160, 150);
776 vi->setOneLiner(tr("Connected, loading config"));
779 boxstack->update(vi);
783 logger->log("Control", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
788 logger->log("Control", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
791 // Power off if first boot and config says so
796 logger->log("Control", Log::DEBUG, "Load power after boot");
798 config = vdr->configLoad("General", "Power After Boot");
802 if (!STRCASECMP(config, "On"))
804 logger->log("Control", Log::INFO, "Config says Power After Boot = On");
806 else if (!STRCASECMP(config, "Off"))
808 logger->log("Control", Log::INFO, "Config says Power After Boot = Off");
813 else if (!STRCASECMP(config, "Last state"))
815 char* lastPowerState = vdr->configLoad("General", "Last Power State");
818 if (!STRCASECMP(lastPowerState, "On"))
820 logger->log("Control", Log::INFO, "Config says Last Power State = On");
822 else if (!STRCASECMP(lastPowerState, "Off"))
824 logger->log("Control", Log::INFO, "Config says Last Power State = Off");
831 logger->log("Control", Log::INFO, "Config General/Last Power State not understood");
836 logger->log("Control", Log::INFO, "Config General/Last Power State not found");
841 logger->log("Control", Log::INFO, "Config/Power After Boot not understood");
847 logger->log("Control", Log::INFO, "Config General/Power After Boot not found");
852 // Go S-Video if config says so
854 config = vdr->configLoad("TV", "Connection");
858 if (!STRCASECMP(config, "S-Video"))
860 logger->log("Control", Log::INFO, "Switching to S-Video as Connection=%s", config);
861 video->setConnection(Video::SVIDEO);
862 } else if (!STRCASECMP(config, "HDMI"))
864 logger->log("Control", Log::INFO, "Switching to HDMI as Connection=%s", config);
865 video->setConnection(Video::HDMI);
866 } else if (!STRCASECMP(config, "HDMI3D"))
868 logger->log("Control", Log::INFO, "Switching to HDMI3D as Connection=%s", config);
869 video->setConnection(Video::HDMI3D);
873 logger->log("Control", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
874 video->setConnection(Video::COMPOSITERGB);
880 logger->log("Control", Log::INFO, "Config TV/S-Video not found");
883 // Set to shutdown VDR if config says
885 config = vdr->configLoad("General", "VDR shutdown");
888 if (!STRCASECMP(config, "On"))
890 logger->log("Control", Log::INFO, "Shutdown VDR when shutting down vomp");
891 vdr->setVDRShutdown(true);
893 else if (!STRCASECMP(config, "Off"))
895 logger->log("Control", Log::INFO, "Shutdown only vomp");
896 vdr->setVDRShutdown(false);
901 logger->log("Control", Log::INFO, "Default shutdown only vomp");
902 vdr->setVDRShutdown(false); // Default
905 // Get TV aspect ratio
907 config = vdr->configLoad("TV", "Aspect");
910 if (!STRCASECMP(config, "16:9"))
912 logger->log("Control", Log::INFO, "/// Switching to TV aspect 16:9");
913 video->setTVsize(Video::ASPECT16X9);
917 logger->log("Control", Log::INFO, "/// Switching to TV aspect 4:3");
918 video->setTVsize(Video::ASPECT4X3);
924 logger->log("Control", Log::INFO, "Config TV/Aspect type not found, going 4:3");
925 video->setTVsize(Video::ASPECT4X3);
928 config = vdr->configLoad("TV", "Widemode");
931 if (!STRCASECMP(config, "Letterbox"))
933 logger->log("Control", Log::INFO, "Setting letterbox mode");
934 video->setMode(Video::LETTERBOX);
938 logger->log("Control", Log::INFO, "Setting chop-sides mode");
939 video->setMode(Video::NORMAL);
946 logger->log("Control", Log::INFO, "Config TV/Widemode not found, Setting letterbox mode");
947 video->setMode(Video::LETTERBOX);
949 logger->log("Control", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
950 video->setMode(Video::NORMAL);
954 config = vdr->configLoad("Advanced", "TCP receive window");
957 size_t newTCPsize = atoi(config);
960 logger->log("Control", Log::INFO, "Setting TCP window size %i", newTCPsize);
961 vdr->setReceiveWindow(newTCPsize);
965 logger->log("Control", Log::INFO, "TCP window size not found, setting 2048");
966 if (DEFAULT_TCP_WINDOWSIZE) vdr->setReceiveWindow(2048); // Default
969 config = vdr->configLoad("Advanced", "Font Name");
972 Osd::getInstance()->setFont(config);
973 logger->log("Control", Log::INFO, "Setting Font to %s", config);
979 // Set recording list type
981 #ifdef ADVANCED_MENUES
982 config = vdr->configLoad("Advanced", "Menu type");
986 if (!STRCASECMP(config, "Advanced"))
988 logger->log("Control", Log::INFO, "Switching to Advanced menu");
993 logger->log("Control", Log::INFO, "Switching to Classic menu");
1000 logger->log("Control", Log::INFO, "Config General/menu type not found");
1005 config = vdr->configLoad("Advanced", "Disable WOL");
1008 if (!STRCASECMP(config, "Yes"))
1010 logger->log("Control", Log::INFO, "Config says disable WOL");
1011 Wol::getInstance()->setEnabled(false);
1015 logger->log("Control", Log::INFO, "Config says enable WOL");
1016 Wol::getInstance()->setEnabled(true);
1023 logger->log("Control", Log::INFO, "By default, enable WOL");
1024 Wol::getInstance()->setEnabled(true);
1026 /* device dependend config */
1027 audio->loadOptionsFromServer(vdr);
1028 video->loadOptionsFromServer(vdr);
1029 inputMan->loadOptionsFromServer(vdr);
1031 video->executePendingModeChanges();
1034 // Save power state = on
1036 vdr->configSave("General", "Last Power State", "On");
1038 // Make sure connection didn't die
1039 if (!vdr->isConnected())
1041 Control::getInstance()->connectionLost();
1045 boxstack->remove(vi);
1047 VWelcome* vw = new VWelcome();
1050 boxstack->update(vw);
1052 // Enter pre-keys here
1053 // handleCommand(Input::OK);
1054 // handleCommand(Input::THREE);
1055 // handleCommand(Input::SIX);
1056 // handleCommand(Input::OK);
1057 // handleCommand(Input::UP);
1058 // handleCommand(Input::PLAY);
1059 // handleCommand(Input::DOWN);
1060 // handleCommand(Input::DOWN);
1061 // handleCommand(Input::DOWN);
1062 // handleCommand(Input::RIGHT);
1063 // handleCommand(Input::RED);