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 int Control::init(bool tcrashed)
86 if (initted) return 0;
91 SkinFactory::InitSkin(0);
94 logger = Log::getInstance();
98 boxstack = new BoxStack();
100 success = boxstack->init();
103 logger->log("Core", Log::INFO, "BoxStack module initialised");
107 logger->log("Core", Log::EMERG, "BoxStack module failed to initialise");
114 inputMan = InputMan::getInstance();
116 if (!logger || !boxstack || !inputMan)
128 int Control::shutdown()
132 boxstack->shutdown();
135 logger->log("Core", Log::NOTICE, "BoxStack module shut down");
138 if (!initted) return 0;
145 logger->log("Control", Log::NOTICE, "Request stop");
147 Message* m = new Message(); // break master loop
148 m->message = Message::SHUTDOWN;
149 m->p_to = Message::CONTROL;
153 void Control::doWallpaper()
155 Video* video = Video::getInstance();
158 Boxx* bbg = new Boxx();
159 bbg->setSize(video->getScreenWidth(), video->getScreenHeight());
161 bbg->fillColour(DrawStyle::WALLPAPER);
163 boxstack->update(bbg);
164 boxstack->remove(bbg);
167 wallpaper = new Boxx();
168 wallpaper->setSize(video->getScreenWidth(), video->getScreenHeight());
169 wallpaper->createBuffer();
170 wallpaper->setBackgroundColour(DrawStyle::WALLPAPER);
172 wallpaper_pict = new WJpegTYPE();
173 wallpaper_pict->setSize(video->getScreenWidth(), video->getScreenHeight());
175 if (video->getFormat() == Video::PAL)
177 logger->log("Control", Log::DEBUG, "PAL wallpaper selected");
179 wallpaper_pict->init("/wallpaperPAL.jpg");
181 wallpaper_pict->init("wallpaperPAL.jpg");
186 logger->log("Control", Log::DEBUG, "NTSC wallpaper selected");
187 wallpaper_pict->init("/wallpaperNTSC.jpg");
190 if (DrawStyle::WALLPAPER.alpha)
191 wallpaper_pict->setVisible(true);
193 wallpaper_pict->setVisible(false);
195 wallpaper->add(wallpaper_pict);
198 boxstack->add(wallpaper);
199 boxstack->update(wallpaper);
201 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
202 if (osdv) osdv->updateBackgroundColor(DrawStyle::WALLPAPER);
207 if (!initted) return;
211 Video::getInstance()->signalOn();
212 Led::getInstance()->on();
222 VConnect* vconnect = new VConnect();
223 boxstack->add(vconnect);
228 // FIXME Input::NA_SIGNAL is possibly obsolete now
230 std::unique_lock<std::mutex> lockWrapper(messageQueueMutex); // locks. unlocks on out-of-scope
236 messageQueueCond.wait(lockWrapper, [&] { return !irun || !messages.empty(); });
237 logger->log("Control", Log::DEBUG, "woke");
241 while(!messages.empty())
243 Message* m = messages.front();
244 messages.pop_front();
246 lockWrapper.unlock();
257 boxstack->removeAllExceptWallpaper();
258 boxstack->remove(wallpaper);
259 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
262 void Control::processMessage(Message* m)
264 // FIXME - a slight modification - how if messagereceivers were to register
265 // themselves as receivers to avoid the calling-a-deleted-object problem
266 // then only deliver/register/unregister would have to be protected
268 logger->log("Control", Log::DEBUG, "processing message %i", m->message);
271 if ((m->p_to == Message::CONTROL) || (m->to == this)) // Maybe don't check m->to here? Always use predefined?
275 case Message::SHUTDOWN:
281 case Message::STOP_PLAYBACK:
283 handleCommand(Input::STOP); // an odd way of doing it, but so simple
286 // Also connection_lost comes from player - anywhere else?
290 case Message::VDR_CONNECTED:
292 doJustConnected(static_cast<VConnect*>(m->from));
295 case Message::SCREENSHOT:
297 logger->log("Osd", Log::NOTICE, "Screenshot Message arrived");
298 Osd::getInstance()->screenShot("out.jpg");
301 case Message::CONNECTION_LOST:
306 case Message::INPUT_EVENT:
308 logger->log("Control", Log::NOTICE, "INPUT_EVENT %i", m->parameter);
310 handleCommand(m->parameter);
313 case Message::CHANGE_LANGUAGE:
315 boxstack->removeAllExceptWallpaper();
316 boxstack->update(wallpaper);
318 if (!VDR::getInstance()->isConnected()) { connectionLost(); break; }
319 VWelcome* vw = new VWelcome();
322 boxstack->update(vw);
325 case Message::LAST_VIEW_CLOSE:
327 // Shouldn't be done like this. Some generic message pass back from vinfo perhaps
334 // VWelcome* vw = new VWelcome();
336 // boxstack->add(vw);
337 // boxstack->update(vw);
341 case Message::NEW_PICTURE:
343 //Log::getInstance()->log("Control", Log::DEBUG, "TVMedia NEW_PICTURE");
344 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
345 if (osdv) osdv->informPicture(m->tag, reinterpret_cast<ImageIndex>(m->data));
348 case Message::NEW_PICTURE_STATIC:
350 //Log::getInstance()->log("Control", Log::DEBUG, "TVMedia NEW_PICTURE %x %x",m->tag,m->parameter);
351 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
352 if (osdv) osdv->informPicture(static_cast<unsigned long long>(m->tag) << 32LL, reinterpret_cast<ImageIndex>(m->data));
361 Instead of sending through the boxstack, implement a more generic MessageReceiver interface
362 and have potential receivers register with something
363 When a message needs to be delivered, check if the receiver is still registered, if so, deliver the message
364 This could all be done using the existing big control mutex to keep it simple
367 logger->log("Control", Log::DEBUG, "Sending message to boxstack");
368 boxstack->processMessage(m);
372 void Control::handleCommand(int button)
374 if (isStandby && (button != Input::POWER)
375 && (button != Input::POWERON)
376 && (button != Input::POWEROFF)) return;
378 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
380 // command was not handled
384 case Input::VOLUMEUP:
385 case Input::VOLUMEDOWN:
387 if (inputMan->handlesVolume()) // CEC volume handler?
389 if (button == Input::VOLUMEDOWN)
390 inputMan->volumeDown();
392 inputMan->volumeUp();
396 VVolume* v = new VVolume();
398 v->handleCommand(button); // this will draw+show
404 if (inputMan->handlesVolume())
406 inputMan->volumeMute();
410 VMute* v = new VMute();
427 case Input::POWEROFF:
435 if (!connLost) return; // if connLost, handle Input::OK
441 VSleeptimer* sleep = new VSleeptimer();
442 boxstack->add(sleep);
443 sleep->handleCommand(button); // this will draw+show
453 Message* m = new Message(); // break into master mutex
454 m->message = Message::SCREENSHOT;
461 void Control::doStandby()
474 void Control::doPowerOn()
478 Video::getInstance()->signalOn();
479 Led::getInstance()->on();
480 InputMan::getInstance()->changePowerState(true);
483 VConnect* vconnect = new VConnect();
484 boxstack->add(vconnect);
489 void Control::doPowerOff()
493 VDR::getInstance()->shutdownVDR();
494 boxstack->removeAllExceptWallpaper();
495 Video::getInstance()->signalOff();
496 boxstack->update(wallpaper);
498 VDR::getInstance()->configSave("General", "Last Power State", "Off");
499 logger->unsetExternLogger();
500 VDR::getInstance()->disconnect();
501 Led::getInstance()->off();
502 InputMan::getInstance()->changePowerState(false);
504 Sleeptimer::getInstance()->shutdown();
508 void Control::doFromTheTop(bool which)
510 if (isStandby) return;
515 logger->log("Control", Log::NOTICE, "Connection lost dialog already present");
519 logger->log("Control", Log::NOTICE, "Doing connection lost dialog");
520 connLost = new VInfo();
521 connLost->setSize(360, 200);
522 connLost->createBuffer();
523 if (Video::getInstance()->getFormat() == Video::PAL)
524 connLost->setPosition(190, 170);
526 connLost->setPosition(180, 120);
527 connLost->setOneLiner(tr("Connection lost"));
528 connLost->setDropThrough();
529 connLost->setBorderOn(1);
530 connLost->setTitleBarColour(DrawStyle::DANGER);
531 connLost->okButton();
533 boxstack->add(connLost);
534 boxstack->update(connLost);
536 clearMQInputEvents();
540 logger->unsetExternLogger();
541 VDR::getInstance()->disconnect();
542 boxstack->removeAllExceptWallpaper();
543 boxstack->update(wallpaper);
548 // at this point, everything should be reset to first-go
550 VConnect* vconnect = new VConnect();
551 boxstack->add(vconnect);
556 void Control::clearMQInputEvents()
558 std::lock_guard<std::mutex> lg(messageQueueMutex); // Get the lock
560 MQueueI i = messages.begin();
561 while(i != messages.end())
564 if (m->message == Message::INPUT_EVENT)
567 i = messages.erase(i);
576 void Control::doReboot()
579 logger->unsetExternLogger();
580 VDR::getInstance()->disconnect();
582 logger->log("Control", Log::NOTICE, "Reboot");
584 #ifndef VOMP_HAS_EXIT
585 // some plattforms, want a proper deinitialisation of their hardware before reboot
586 Osd::getInstance()->shutdown();
587 Audio::getInstance()->shutdown();
588 Video::getInstance()->shutdown();
589 InputMan::getInstance()->shutdown();
591 reboot(LINUX_REBOOT_CMD_RESTART);
592 // if reboot is not allowed -> stop
604 #endif //Would we support this on windows?
607 void Control::connectionLost()
609 logger->unsetExternLogger();
610 Message* m = new Message(); // break into master mutex
611 m->message = Message::CONNECTION_LOST;
612 m->p_to = Message::CONTROL;
616 void Control::buildCrashedBox()
618 VInfo* crash = new VInfo();
619 crash->setSize(360, 250);
620 crash->createBuffer();
621 if (Video::getInstance()->getFormat() == Video::PAL)
622 crash->setPosition(190, 146);
624 crash->setPosition(180, 96);
625 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.");
626 crash->setBorderOn(1);
627 crash->setTitleBarColour(DrawStyle::DANGER);
629 crash->setExitable();
631 boxstack->add(crash);
632 boxstack->update(crash);
635 int Control::getLangPref(bool subtitle, const char* langcode)
637 std::vector<struct ASLPref>::iterator itty=langcodes.begin();
638 char templangcode[4];
639 templangcode[0] = langcode[0];
640 templangcode[1] = langcode[1];
641 templangcode[2] = langcode[2];
642 templangcode[3] = '\0';
644 while (itty != langcodes.end())
646 size_t pos = (*itty).langcode.find(templangcode);
647 if (pos != std::string::npos)
649 //vector<struct ASLPref>::iterator itty2=langcodes.begin();
650 for (unsigned int i = 0; i < langcodes.size(); i++)
654 pref = langcodes[i].subtitlepref;
656 pref = langcodes[i].audiopref;
661 if (langcodes[i].subtitlepref==langpos) return i;
665 if (langcodes[i].audiopref==langpos) return i;
673 return langcodes.size(); //neutral
676 void Control::doJustConnected(VConnect* vconnect)
679 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
680 logger->log("Control", Log::INFO, "Entering doJustConnected");
682 Video* video = Video::getInstance();
683 Audio* audio = Audio::getInstance();
684 boxstack->remove(vconnect);
686 VInfo* vi = new VInfo();
687 vi->setSize(400, 200);
689 if (video->getFormat() == Video::PAL)
690 vi->setPosition(170, 200);
692 vi->setPosition(160, 150);
693 vi->setOneLiner(tr("Connected, loading config"));
696 boxstack->update(vi);
698 // FIXME make config system
700 VDR* vdr = VDR::getInstance();
703 // See if we're supposed to do network logging
704 config = vdr->configLoad("Advanced", "Network logging");
705 if (config && !STRCASECMP(config, "On"))
707 logger->log("Control", Log::INFO, "Turning on network logging");
708 logger->setExternLogger(vdr);
712 logger->unsetExternLogger();
713 logger->log("Control", Log::INFO, "Turned off network logging");
715 if (config) delete[] config;
717 config = vdr->configLoad("Advanced", "Skin Name");
720 const char **skinnames=SkinFactory::getSkinNames();
721 for (int i=0;i<SkinFactory::getNumberofSkins();i++)
723 if (!STRCASECMP(config, skinnames[i]))
725 SkinFactory::InitSkin(i);
731 if (wallpaper && wallpaper_pict)
733 if (DrawStyle::WALLPAPER.alpha)
734 wallpaper_pict->setVisible(true);
736 wallpaper_pict->setVisible(false);
739 boxstack->update(wallpaper);
744 SkinFactory::InitSkin(0);
747 // See if config says to override video format (PAL/NTSC)
748 config = vdr->configLoad("General", "Override Video Format");
751 logger->log("Control", Log::DEBUG, "Override Video Format is present");
753 if ( (!strcmp(config, "PAL") && (video->getFormat() != Video::PAL))
754 || (!strcmp(config, "NTSC") && (video->getFormat() != Video::NTSC))
755 || (!strcmp(config, "PAL_M") && (video->getFormat() != Video::PAL_M))
756 || (!strcmp(config, "NTSC_J") && (video->getFormat() != Video::NTSC_J))
759 // Oh sheesh, need to switch format. Bye bye TV...
761 // Take everything down
762 boxstack->removeAllExceptWallpaper();
763 boxstack->remove(wallpaper);
764 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
766 Osd* osd = Osd::getInstance();
772 inputMan->shutdown(); // need on raspberry shut not do any harm, hopefully
773 inputMan->init(); // FIXME this breaks badly now
775 // Get video and osd back up with the new mode
776 if (!strcmp(config, "PAL"))
778 logger->log("Control", Log::DEBUG, "Switching to PAL");
779 video->init(Video::PAL);
781 else if (!strcmp(config, "NTSC"))
783 logger->log("Control", Log::DEBUG, "Switching to NTSC");
784 video->init(Video::NTSC);
785 } else if (!strcmp(config, "PAL_M"))
787 logger->log("Control", Log::DEBUG, "Switching to PAL_M");
788 video->init(Video::PAL_M);
789 } else if (!strcmp(config, "NTSC_J"))
791 logger->log("Control", Log::DEBUG, "Switching to NTSC_J");
792 video->init(Video::NTSC_J);
797 //we do not init twice
801 // Put the wallpaper back
806 vi->setSize(400, 200);
808 if (video->getFormat() == Video::PAL)
809 vi->setPosition(170, 200);
811 vi->setPosition(160, 150);
813 vi->setOneLiner(tr("Connected, loading config"));
816 boxstack->update(vi);
820 logger->log("Control", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
825 logger->log("Control", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
828 // Power off if first boot and config says so
833 logger->log("Control", Log::DEBUG, "Load power after boot");
835 config = vdr->configLoad("General", "Power After Boot");
839 if (!STRCASECMP(config, "On"))
841 logger->log("Control", Log::INFO, "Config says Power After Boot = On");
843 else if (!STRCASECMP(config, "Off"))
845 logger->log("Control", Log::INFO, "Config says Power After Boot = Off");
850 else if (!STRCASECMP(config, "Last state"))
852 char* lastPowerState = vdr->configLoad("General", "Last Power State");
855 if (!STRCASECMP(lastPowerState, "On"))
857 logger->log("Control", Log::INFO, "Config says Last Power State = On");
859 else if (!STRCASECMP(lastPowerState, "Off"))
861 logger->log("Control", Log::INFO, "Config says Last Power State = Off");
868 logger->log("Control", Log::INFO, "Config General/Last Power State not understood");
873 logger->log("Control", Log::INFO, "Config General/Last Power State not found");
878 logger->log("Control", Log::INFO, "Config/Power After Boot not understood");
884 logger->log("Control", Log::INFO, "Config General/Power After Boot not found");
889 // Go S-Video if config says so
891 config = vdr->configLoad("TV", "Connection");
895 if (!STRCASECMP(config, "S-Video"))
897 logger->log("Control", Log::INFO, "Switching to S-Video as Connection=%s", config);
898 video->setConnection(Video::SVIDEO);
899 } else if (!STRCASECMP(config, "HDMI"))
901 logger->log("Control", Log::INFO, "Switching to HDMI as Connection=%s", config);
902 video->setConnection(Video::HDMI);
903 } else if (!STRCASECMP(config, "HDMI3D"))
905 logger->log("Control", Log::INFO, "Switching to HDMI3D as Connection=%s", config);
906 video->setConnection(Video::HDMI3D);
910 logger->log("Control", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
911 video->setConnection(Video::COMPOSITERGB);
917 logger->log("Control", Log::INFO, "Config TV/S-Video not found");
920 // Set to shutdown VDR if config says
922 config = vdr->configLoad("General", "VDR shutdown");
925 if (!STRCASECMP(config, "On"))
927 logger->log("Control", Log::INFO, "Shutdown VDR when shutting down vomp");
928 vdr->setVDRShutdown(true);
930 else if (!STRCASECMP(config, "Off"))
932 logger->log("Control", Log::INFO, "Shutdown only vomp");
933 vdr->setVDRShutdown(false);
938 logger->log("Control", Log::INFO, "Default shutdown only vomp");
939 vdr->setVDRShutdown(false); // Default
942 // Get TV aspect ratio
944 config = vdr->configLoad("TV", "Aspect");
947 if (!STRCASECMP(config, "16:9"))
949 logger->log("Control", Log::INFO, "/// Switching to TV aspect 16:9");
950 video->setTVsize(Video::ASPECT16X9);
954 logger->log("Control", Log::INFO, "/// Switching to TV aspect 4:3");
955 video->setTVsize(Video::ASPECT4X3);
961 logger->log("Control", Log::INFO, "Config TV/Aspect type not found, going 4:3");
962 video->setTVsize(Video::ASPECT4X3);
965 config = vdr->configLoad("TV", "Widemode");
968 if (!STRCASECMP(config, "Letterbox"))
970 logger->log("Control", Log::INFO, "Setting letterbox mode");
971 video->setMode(Video::LETTERBOX);
975 logger->log("Control", Log::INFO, "Setting chop-sides mode");
976 video->setMode(Video::NORMAL);
983 logger->log("Control", Log::INFO, "Config TV/Widemode not found, Setting letterbox mode");
984 video->setMode(Video::LETTERBOX);
986 logger->log("Control", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
987 video->setMode(Video::NORMAL);
991 config = vdr->configLoad("Advanced", "TCP receive window");
994 size_t newTCPsize = atoi(config);
997 logger->log("Control", Log::INFO, "Setting TCP window size %i", newTCPsize);
998 vdr->setReceiveWindow(newTCPsize);
1002 logger->log("Control", Log::INFO, "TCP window size not found, setting 2048");
1003 if (DEFAULT_TCP_WINDOWSIZE) vdr->setReceiveWindow(2048); // Default
1006 config = vdr->configLoad("Advanced", "Font Name");
1009 Osd::getInstance()->setFont(config);
1010 logger->log("Control", Log::INFO, "Setting Font to %s", config);
1016 // Set recording list type
1018 #ifdef ADVANCED_MENUES
1019 config = vdr->configLoad("Advanced", "Menu type");
1023 if (!STRCASECMP(config, "Advanced"))
1025 logger->log("Control", Log::INFO, "Switching to Advanced menu");
1030 logger->log("Control", Log::INFO, "Switching to Classic menu");
1037 logger->log("Control", Log::INFO, "Config General/menu type not found");
1042 config = vdr->configLoad("Advanced", "Disable WOL");
1045 if (!STRCASECMP(config, "Yes"))
1047 logger->log("Control", Log::INFO, "Config says disable WOL");
1048 Wol::getInstance()->setEnabled(false);
1052 logger->log("Control", Log::INFO, "Config says enable WOL");
1053 Wol::getInstance()->setEnabled(true);
1060 logger->log("Control", Log::INFO, "By default, enable WOL");
1061 Wol::getInstance()->setEnabled(true);
1063 /* device dependend config */
1064 audio->loadOptionsFromServer(vdr);
1065 video->loadOptionsFromServer(vdr);
1066 inputMan->loadOptionsFromServer(vdr);
1068 video->executePendingModeChanges();
1071 // Save power state = on
1073 vdr->configSave("General", "Last Power State", "On");
1075 // Make sure connection didn't die
1076 if (!vdr->isConnected())
1078 Control::getInstance()->connectionLost();
1082 boxstack->remove(vi);
1084 VWelcome* vw = new VWelcome();
1087 boxstack->update(vw);
1089 // Enter pre-keys here
1090 // handleCommand(Input::OK);
1091 // handleCommand(Input::THREE);
1092 // handleCommand(Input::SIX);
1093 // handleCommand(Input::OK);
1094 // handleCommand(Input::UP);
1095 // handleCommand(Input::PLAY);
1096 // handleCommand(Input::DOWN);
1097 // handleCommand(Input::DOWN);
1098 // handleCommand(Input::DOWN);
1099 // handleCommand(Input::RIGHT);
1100 // handleCommand(Input::RED);