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 vdr = new VDR(); if (!boxstack) throw 1;
98 if (!vdr->init()) throw 2;
100 boxstack = new BoxStack(); if (!boxstack) throw 10;
101 if (!boxstack->init()) throw 20;
103 sleeptimer = new Sleeptimer(); if (!sleeptimer) throw 30;
105 wol = new Wol(); if (!wol) throw 40;
110 if (e == 1 ) logger->log("Control", Log::EMERG, "VDR module failed to create");
111 else if (e == 2 ) logger->log("Control", Log::EMERG, "VDR module failed to initialise");
112 else if (e == 10) logger->log("Control", Log::EMERG, "BoxStack module failed to create");
113 else if (e == 20) logger->log("Control", Log::EMERG, "BoxStack module failed to initialise");
114 else if (e == 30) logger->log("Control", Log::EMERG, "SleepTimer module failed to create");
115 else if (e == 40) logger->log("Control", Log::EMERG, "WOL module failed to create");
142 inputMan = InputMan::getInstance();
156 void Control::shutdown()
158 if (!initted) return;
164 logger->log("Control", Log::NOTICE, "WOL module shut down");
171 logger->log("Control", Log::NOTICE, "Sleeptimer module shut down");
176 boxstack->shutdown();
179 logger->log("Control", Log::NOTICE, "BoxStack module shut down");
187 logger->log("Control", Log::NOTICE, "VDR module shut down");
193 logger->log("Control", Log::NOTICE, "Request stop");
195 Message* m = new Message(); // break master loop
196 m->message = Message::SHUTDOWN;
197 m->p_to = Message::CONTROL;
201 void Control::doWallpaper()
203 Video* video = Video::getInstance();
206 Boxx* bbg = new Boxx();
207 bbg->setSize(video->getScreenWidth(), video->getScreenHeight());
209 bbg->fillColour(DrawStyle::WALLPAPER);
211 boxstack->update(bbg);
212 boxstack->remove(bbg);
215 wallpaper = new Boxx();
216 wallpaper->setSize(video->getScreenWidth(), video->getScreenHeight());
217 wallpaper->createBuffer();
218 wallpaper->setBackgroundColour(DrawStyle::WALLPAPER);
220 wallpaper_pict = new WJpegTYPE();
221 wallpaper_pict->setSize(video->getScreenWidth(), video->getScreenHeight());
223 if (video->getFormat() == Video::PAL)
225 logger->log("Control", Log::DEBUG, "PAL wallpaper selected");
227 wallpaper_pict->init("/wallpaperPAL.jpg");
229 wallpaper_pict->init("wallpaperPAL.jpg");
234 logger->log("Control", Log::DEBUG, "NTSC wallpaper selected");
235 wallpaper_pict->init("/wallpaperNTSC.jpg");
238 if (DrawStyle::WALLPAPER.alpha)
239 wallpaper_pict->setVisible(true);
241 wallpaper_pict->setVisible(false);
243 wallpaper->add(wallpaper_pict);
246 boxstack->add(wallpaper);
247 boxstack->update(wallpaper);
249 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
250 if (osdv) osdv->updateBackgroundColor(DrawStyle::WALLPAPER);
255 if (!initted) return;
259 Video::getInstance()->signalOn();
260 Led::getInstance()->on();
270 VConnect* vconnect = new VConnect();
271 boxstack->add(vconnect);
276 // FIXME Input::NA_SIGNAL is possibly obsolete now
278 std::unique_lock<std::mutex> lockWrapper(messageQueueMutex); // locks. unlocks on out-of-scope
284 messageQueueCond.wait(lockWrapper, [&] { return !irun || !messages.empty(); });
285 logger->log("Control", Log::DEBUG, "woke");
289 while(!messages.empty())
291 Message* m = messages.front();
292 messages.pop_front();
294 lockWrapper.unlock();
305 boxstack->removeAllExceptWallpaper();
306 boxstack->remove(wallpaper);
307 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
310 void Control::processMessage(Message* m)
312 // FIXME - a slight modification - how if messagereceivers were to register
313 // themselves as receivers to avoid the calling-a-deleted-object problem
314 // then only deliver/register/unregister would have to be protected
316 logger->log("Control", Log::DEBUG, "processing message %i", m->message);
319 if ((m->p_to == Message::CONTROL) || (m->to == this)) // Maybe don't check m->to here? Always use predefined?
323 case Message::SHUTDOWN:
329 case Message::STOP_PLAYBACK:
331 handleCommand(Input::STOP); // an odd way of doing it, but so simple
334 // Also connection_lost comes from player - anywhere else?
338 case Message::VDR_CONNECTED:
340 doJustConnected(static_cast<VConnect*>(m->from));
343 case Message::SCREENSHOT:
345 logger->log("Osd", Log::NOTICE, "Screenshot Message arrived");
346 Osd::getInstance()->screenShot("out.jpg");
349 case Message::CONNECTION_LOST:
354 case Message::INPUT_EVENT:
356 logger->log("Control", Log::NOTICE, "INPUT_EVENT %i", m->parameter);
358 handleCommand(m->parameter);
361 case Message::CHANGE_LANGUAGE:
363 boxstack->removeAllExceptWallpaper();
364 boxstack->update(wallpaper);
366 if (!VDR::getInstance()->isConnected()) { connectionLost(); break; }
367 VWelcome* vw = new VWelcome();
370 boxstack->update(vw);
373 case Message::LAST_VIEW_CLOSE:
375 // Shouldn't be done like this. Some generic message pass back from vinfo perhaps
382 // VWelcome* vw = new VWelcome();
384 // boxstack->add(vw);
385 // boxstack->update(vw);
389 case Message::NEW_PICTURE:
391 //Log::getInstance()->log("Control", Log::DEBUG, "TVMedia NEW_PICTURE");
392 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
393 if (osdv) osdv->informPicture(m->tag, reinterpret_cast<ImageIndex>(m->data));
396 case Message::NEW_PICTURE_STATIC:
398 //Log::getInstance()->log("Control", Log::DEBUG, "TVMedia NEW_PICTURE %x %x",m->tag,m->parameter);
399 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
400 if (osdv) osdv->informPicture(static_cast<unsigned long long>(m->tag) << 32LL, reinterpret_cast<ImageIndex>(m->data));
409 Instead of sending through the boxstack, implement a more generic MessageReceiver interface
410 and have potential receivers register with something
411 When a message needs to be delivered, check if the receiver is still registered, if so, deliver the message
412 This could all be done using the existing big control mutex to keep it simple
415 logger->log("Control", Log::DEBUG, "Sending message to boxstack");
416 boxstack->processMessage(m);
420 void Control::handleCommand(int button)
422 if (isStandby && (button != Input::POWER)
423 && (button != Input::POWERON)
424 && (button != Input::POWEROFF)) return;
426 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
428 // command was not handled
432 case Input::VOLUMEUP:
433 case Input::VOLUMEDOWN:
435 if (inputMan->handlesVolume()) // CEC volume handler?
437 if (button == Input::VOLUMEDOWN)
438 inputMan->volumeDown();
440 inputMan->volumeUp();
444 VVolume* v = new VVolume();
446 v->handleCommand(button); // this will draw+show
452 if (inputMan->handlesVolume())
454 inputMan->volumeMute();
458 VMute* v = new VMute();
475 case Input::POWEROFF:
483 if (!connLost) return; // if connLost, handle Input::OK
489 VSleeptimer* sleep = new VSleeptimer();
490 boxstack->add(sleep);
491 sleep->handleCommand(button); // this will draw+show
501 Message* m = new Message(); // break into master mutex
502 m->message = Message::SCREENSHOT;
509 void Control::doStandby()
522 void Control::doPowerOn()
526 Video::getInstance()->signalOn();
527 Led::getInstance()->on();
528 InputMan::getInstance()->changePowerState(true);
531 VConnect* vconnect = new VConnect();
532 boxstack->add(vconnect);
537 void Control::doPowerOff()
541 VDR::getInstance()->shutdownVDR();
542 boxstack->removeAllExceptWallpaper();
543 Video::getInstance()->signalOff();
544 boxstack->update(wallpaper);
546 VDR::getInstance()->configSave("General", "Last Power State", "Off");
547 logger->unsetExternLogger();
548 VDR::getInstance()->disconnect();
549 Led::getInstance()->off();
550 InputMan::getInstance()->changePowerState(false);
552 Sleeptimer::getInstance()->shutdown();
556 void Control::doFromTheTop(bool which)
558 if (isStandby) return;
563 logger->log("Control", Log::NOTICE, "Connection lost dialog already present");
567 logger->log("Control", Log::NOTICE, "Doing connection lost dialog");
568 connLost = new VInfo();
569 connLost->setSize(360, 200);
570 connLost->createBuffer();
571 if (Video::getInstance()->getFormat() == Video::PAL)
572 connLost->setPosition(190, 170);
574 connLost->setPosition(180, 120);
575 connLost->setOneLiner(tr("Connection lost"));
576 connLost->setDropThrough();
577 connLost->setBorderOn(1);
578 connLost->setTitleBarColour(DrawStyle::DANGER);
579 connLost->okButton();
581 boxstack->add(connLost);
582 boxstack->update(connLost);
584 clearMQInputEvents();
588 logger->unsetExternLogger();
589 VDR::getInstance()->disconnect();
590 boxstack->removeAllExceptWallpaper();
591 boxstack->update(wallpaper);
596 // at this point, everything should be reset to first-go
598 VConnect* vconnect = new VConnect();
599 boxstack->add(vconnect);
604 void Control::clearMQInputEvents()
606 std::lock_guard<std::mutex> lg(messageQueueMutex); // Get the lock
608 MQueueI i = messages.begin();
609 while(i != messages.end())
612 if (m->message == Message::INPUT_EVENT)
615 i = messages.erase(i);
624 void Control::doReboot()
627 logger->unsetExternLogger();
628 VDR::getInstance()->disconnect();
630 logger->log("Control", Log::NOTICE, "Reboot");
632 #ifndef VOMP_HAS_EXIT
633 // some plattforms, want a proper deinitialisation of their hardware before reboot
634 Osd::getInstance()->shutdown();
635 Audio::getInstance()->shutdown();
636 Video::getInstance()->shutdown();
637 InputMan::getInstance()->shutdown();
639 reboot(LINUX_REBOOT_CMD_RESTART);
640 // if reboot is not allowed -> stop
652 #endif //Would we support this on windows?
655 void Control::connectionLost()
657 logger->unsetExternLogger();
658 Message* m = new Message(); // break into master mutex
659 m->message = Message::CONNECTION_LOST;
660 m->p_to = Message::CONTROL;
664 void Control::buildCrashedBox()
666 VInfo* crash = new VInfo();
667 crash->setSize(360, 250);
668 crash->createBuffer();
669 if (Video::getInstance()->getFormat() == Video::PAL)
670 crash->setPosition(190, 146);
672 crash->setPosition(180, 96);
673 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.");
674 crash->setBorderOn(1);
675 crash->setTitleBarColour(DrawStyle::DANGER);
677 crash->setExitable();
679 boxstack->add(crash);
680 boxstack->update(crash);
683 int Control::getLangPref(bool subtitle, const char* langcode)
685 std::vector<struct ASLPref>::iterator itty=langcodes.begin();
686 char templangcode[4];
687 templangcode[0] = langcode[0];
688 templangcode[1] = langcode[1];
689 templangcode[2] = langcode[2];
690 templangcode[3] = '\0';
692 while (itty != langcodes.end())
694 size_t pos = (*itty).langcode.find(templangcode);
695 if (pos != std::string::npos)
697 //vector<struct ASLPref>::iterator itty2=langcodes.begin();
698 for (unsigned int i = 0; i < langcodes.size(); i++)
702 pref = langcodes[i].subtitlepref;
704 pref = langcodes[i].audiopref;
709 if (langcodes[i].subtitlepref==langpos) return i;
713 if (langcodes[i].audiopref==langpos) return i;
721 return langcodes.size(); //neutral
724 void Control::doJustConnected(VConnect* vconnect)
727 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
728 logger->log("Control", Log::INFO, "Entering doJustConnected");
730 Video* video = Video::getInstance();
731 Audio* audio = Audio::getInstance();
732 boxstack->remove(vconnect);
734 VInfo* vi = new VInfo();
735 vi->setSize(400, 200);
737 if (video->getFormat() == Video::PAL)
738 vi->setPosition(170, 200);
740 vi->setPosition(160, 150);
741 vi->setOneLiner(tr("Connected, loading config"));
744 boxstack->update(vi);
746 // FIXME make config system
750 // See if we're supposed to do network logging
751 config = vdr->configLoad("Advanced", "Network logging");
752 if (config && !STRCASECMP(config, "On"))
754 logger->log("Control", Log::INFO, "Turning on network logging");
755 logger->setExternLogger(vdr);
759 logger->unsetExternLogger();
760 logger->log("Control", Log::INFO, "Turned off network logging");
762 if (config) delete[] config;
764 config = vdr->configLoad("Advanced", "Skin Name");
767 const char **skinnames=SkinFactory::getSkinNames();
768 for (int i=0;i<SkinFactory::getNumberofSkins();i++)
770 if (!STRCASECMP(config, skinnames[i]))
772 SkinFactory::InitSkin(i);
778 if (wallpaper && wallpaper_pict)
780 if (DrawStyle::WALLPAPER.alpha)
781 wallpaper_pict->setVisible(true);
783 wallpaper_pict->setVisible(false);
786 boxstack->update(wallpaper);
791 SkinFactory::InitSkin(0);
794 // See if config says to override video format (PAL/NTSC)
795 config = vdr->configLoad("General", "Override Video Format");
798 logger->log("Control", Log::DEBUG, "Override Video Format is present");
800 if ( (!strcmp(config, "PAL") && (video->getFormat() != Video::PAL))
801 || (!strcmp(config, "NTSC") && (video->getFormat() != Video::NTSC))
802 || (!strcmp(config, "PAL_M") && (video->getFormat() != Video::PAL_M))
803 || (!strcmp(config, "NTSC_J") && (video->getFormat() != Video::NTSC_J))
806 // Oh sheesh, need to switch format. Bye bye TV...
808 // Take everything down
809 boxstack->removeAllExceptWallpaper();
810 boxstack->remove(wallpaper);
811 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
813 Osd* osd = Osd::getInstance();
819 inputMan->shutdown(); // need on raspberry shut not do any harm, hopefully
820 inputMan->init(); // FIXME this breaks badly now
822 // Get video and osd back up with the new mode
823 if (!strcmp(config, "PAL"))
825 logger->log("Control", Log::DEBUG, "Switching to PAL");
826 video->init(Video::PAL);
828 else if (!strcmp(config, "NTSC"))
830 logger->log("Control", Log::DEBUG, "Switching to NTSC");
831 video->init(Video::NTSC);
832 } else if (!strcmp(config, "PAL_M"))
834 logger->log("Control", Log::DEBUG, "Switching to PAL_M");
835 video->init(Video::PAL_M);
836 } else if (!strcmp(config, "NTSC_J"))
838 logger->log("Control", Log::DEBUG, "Switching to NTSC_J");
839 video->init(Video::NTSC_J);
844 //we do not init twice
848 // Put the wallpaper back
853 vi->setSize(400, 200);
855 if (video->getFormat() == Video::PAL)
856 vi->setPosition(170, 200);
858 vi->setPosition(160, 150);
860 vi->setOneLiner(tr("Connected, loading config"));
863 boxstack->update(vi);
867 logger->log("Control", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
872 logger->log("Control", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
875 // Power off if first boot and config says so
880 logger->log("Control", Log::DEBUG, "Load power after boot");
882 config = vdr->configLoad("General", "Power After Boot");
886 if (!STRCASECMP(config, "On"))
888 logger->log("Control", Log::INFO, "Config says Power After Boot = On");
890 else if (!STRCASECMP(config, "Off"))
892 logger->log("Control", Log::INFO, "Config says Power After Boot = Off");
897 else if (!STRCASECMP(config, "Last state"))
899 char* lastPowerState = vdr->configLoad("General", "Last Power State");
902 if (!STRCASECMP(lastPowerState, "On"))
904 logger->log("Control", Log::INFO, "Config says Last Power State = On");
906 else if (!STRCASECMP(lastPowerState, "Off"))
908 logger->log("Control", Log::INFO, "Config says Last Power State = Off");
915 logger->log("Control", Log::INFO, "Config General/Last Power State not understood");
920 logger->log("Control", Log::INFO, "Config General/Last Power State not found");
925 logger->log("Control", Log::INFO, "Config/Power After Boot not understood");
931 logger->log("Control", Log::INFO, "Config General/Power After Boot not found");
936 // Go S-Video if config says so
938 config = vdr->configLoad("TV", "Connection");
942 if (!STRCASECMP(config, "S-Video"))
944 logger->log("Control", Log::INFO, "Switching to S-Video as Connection=%s", config);
945 video->setConnection(Video::SVIDEO);
946 } else if (!STRCASECMP(config, "HDMI"))
948 logger->log("Control", Log::INFO, "Switching to HDMI as Connection=%s", config);
949 video->setConnection(Video::HDMI);
950 } else if (!STRCASECMP(config, "HDMI3D"))
952 logger->log("Control", Log::INFO, "Switching to HDMI3D as Connection=%s", config);
953 video->setConnection(Video::HDMI3D);
957 logger->log("Control", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
958 video->setConnection(Video::COMPOSITERGB);
964 logger->log("Control", Log::INFO, "Config TV/S-Video not found");
967 // Set to shutdown VDR if config says
969 config = vdr->configLoad("General", "VDR shutdown");
972 if (!STRCASECMP(config, "On"))
974 logger->log("Control", Log::INFO, "Shutdown VDR when shutting down vomp");
975 vdr->setVDRShutdown(true);
977 else if (!STRCASECMP(config, "Off"))
979 logger->log("Control", Log::INFO, "Shutdown only vomp");
980 vdr->setVDRShutdown(false);
985 logger->log("Control", Log::INFO, "Default shutdown only vomp");
986 vdr->setVDRShutdown(false); // Default
989 // Get TV aspect ratio
991 config = vdr->configLoad("TV", "Aspect");
994 if (!STRCASECMP(config, "16:9"))
996 logger->log("Control", Log::INFO, "/// Switching to TV aspect 16:9");
997 video->setTVsize(Video::ASPECT16X9);
1001 logger->log("Control", Log::INFO, "/// Switching to TV aspect 4:3");
1002 video->setTVsize(Video::ASPECT4X3);
1008 logger->log("Control", Log::INFO, "Config TV/Aspect type not found, going 4:3");
1009 video->setTVsize(Video::ASPECT4X3);
1012 config = vdr->configLoad("TV", "Widemode");
1015 if (!STRCASECMP(config, "Letterbox"))
1017 logger->log("Control", Log::INFO, "Setting letterbox mode");
1018 video->setMode(Video::LETTERBOX);
1022 logger->log("Control", Log::INFO, "Setting chop-sides mode");
1023 video->setMode(Video::NORMAL);
1030 logger->log("Control", Log::INFO, "Config TV/Widemode not found, Setting letterbox mode");
1031 video->setMode(Video::LETTERBOX);
1033 logger->log("Control", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
1034 video->setMode(Video::NORMAL);
1038 config = vdr->configLoad("Advanced", "TCP receive window");
1041 size_t newTCPsize = atoi(config);
1044 logger->log("Control", Log::INFO, "Setting TCP window size %i", newTCPsize);
1045 vdr->setReceiveWindow(newTCPsize);
1049 logger->log("Control", Log::INFO, "TCP window size not found, setting 2048");
1050 if (DEFAULT_TCP_WINDOWSIZE) vdr->setReceiveWindow(2048); // Default
1053 config = vdr->configLoad("Advanced", "Font Name");
1056 Osd::getInstance()->setFont(config);
1057 logger->log("Control", Log::INFO, "Setting Font to %s", config);
1063 // Set recording list type
1065 #ifdef ADVANCED_MENUES
1066 config = vdr->configLoad("Advanced", "Menu type");
1070 if (!STRCASECMP(config, "Advanced"))
1072 logger->log("Control", Log::INFO, "Switching to Advanced menu");
1077 logger->log("Control", Log::INFO, "Switching to Classic menu");
1084 logger->log("Control", Log::INFO, "Config General/menu type not found");
1089 config = vdr->configLoad("Advanced", "Disable WOL");
1092 if (!STRCASECMP(config, "Yes"))
1094 logger->log("Control", Log::INFO, "Config says disable WOL");
1095 Wol::getInstance()->setEnabled(false);
1099 logger->log("Control", Log::INFO, "Config says enable WOL");
1100 Wol::getInstance()->setEnabled(true);
1107 logger->log("Control", Log::INFO, "By default, enable WOL");
1108 Wol::getInstance()->setEnabled(true);
1110 /* device dependend config */
1111 audio->loadOptionsFromServer(vdr);
1112 video->loadOptionsFromServer(vdr);
1113 inputMan->loadOptionsFromServer(vdr);
1115 video->executePendingModeChanges();
1118 // Save power state = on
1120 vdr->configSave("General", "Last Power State", "On");
1122 // Make sure connection didn't die
1123 if (!vdr->isConnected())
1125 Control::getInstance()->connectionLost();
1129 boxstack->remove(vi);
1131 VWelcome* vw = new VWelcome();
1134 boxstack->update(vw);
1136 // Enter pre-keys here
1137 // handleCommand(Input::OK);
1138 // handleCommand(Input::THREE);
1139 // handleCommand(Input::SIX);
1140 // handleCommand(Input::OK);
1141 // handleCommand(Input::UP);
1142 // handleCommand(Input::PLAY);
1143 // handleCommand(Input::DOWN);
1144 // handleCommand(Input::DOWN);
1145 // handleCommand(Input::DOWN);
1146 // handleCommand(Input::RIGHT);
1147 // handleCommand(Input::RED);