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");
141 inputMan = InputMan::getInstance();
155 void Control::shutdown()
157 if (!initted) return;
163 logger->log("Control", Log::NOTICE, "WOL module shut down");
170 logger->log("Control", Log::NOTICE, "Sleeptimer module shut down");
175 boxstack->shutdown();
178 logger->log("Control", Log::NOTICE, "BoxStack module shut down");
186 logger->log("Control", Log::NOTICE, "VDR module shut down");
192 logger->log("Control", Log::NOTICE, "Request stop");
194 Message* m = new Message(); // break master loop
195 m->message = Message::SHUTDOWN;
196 m->p_to = Message::CONTROL;
200 void Control::doWallpaper()
202 Video* video = Video::getInstance();
205 Boxx* bbg = new Boxx();
206 bbg->setSize(video->getScreenWidth(), video->getScreenHeight());
208 bbg->fillColour(DrawStyle::WALLPAPER);
210 boxstack->update(bbg);
211 boxstack->remove(bbg);
214 wallpaper = new Boxx();
215 wallpaper->setSize(video->getScreenWidth(), video->getScreenHeight());
216 wallpaper->createBuffer();
217 wallpaper->setBackgroundColour(DrawStyle::WALLPAPER);
219 wallpaper_pict = new WJpegTYPE();
220 wallpaper_pict->setSize(video->getScreenWidth(), video->getScreenHeight());
222 if (video->getFormat() == Video::PAL)
224 logger->log("Control", Log::DEBUG, "PAL wallpaper selected");
226 wallpaper_pict->init("/wallpaperPAL.jpg");
228 wallpaper_pict->init("wallpaperPAL.jpg");
233 logger->log("Control", Log::DEBUG, "NTSC wallpaper selected");
234 wallpaper_pict->init("/wallpaperNTSC.jpg");
237 if (DrawStyle::WALLPAPER.alpha)
238 wallpaper_pict->setVisible(true);
240 wallpaper_pict->setVisible(false);
242 wallpaper->add(wallpaper_pict);
245 boxstack->add(wallpaper);
246 boxstack->update(wallpaper);
248 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
249 if (osdv) osdv->updateBackgroundColor(DrawStyle::WALLPAPER);
254 if (!initted) return;
258 Video::getInstance()->signalOn();
259 Led::getInstance()->on();
269 VConnect* vconnect = new VConnect();
270 boxstack->add(vconnect);
275 // FIXME Input::NA_SIGNAL is possibly obsolete now
277 std::unique_lock<std::mutex> lockWrapper(messageQueueMutex); // locks. unlocks on out-of-scope
283 messageQueueCond.wait(lockWrapper, [&] { return !irun || !messages.empty(); });
284 logger->log("Control", Log::DEBUG, "woke");
288 while(!messages.empty())
290 Message* m = messages.front();
291 messages.pop_front();
293 lockWrapper.unlock();
304 boxstack->removeAllExceptWallpaper();
305 boxstack->remove(wallpaper);
306 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
309 void Control::processMessage(Message* m)
311 // FIXME - a slight modification - how if messagereceivers were to register
312 // themselves as receivers to avoid the calling-a-deleted-object problem
313 // then only deliver/register/unregister would have to be protected
315 logger->log("Control", Log::DEBUG, "processing message %i", m->message);
318 if ((m->p_to == Message::CONTROL) || (m->to == this)) // Maybe don't check m->to here? Always use predefined?
322 case Message::SHUTDOWN:
328 case Message::STOP_PLAYBACK:
330 handleCommand(Input::STOP); // an odd way of doing it, but so simple
333 // Also connection_lost comes from player - anywhere else?
337 case Message::VDR_CONNECTED:
339 doJustConnected(static_cast<VConnect*>(m->from));
342 case Message::SCREENSHOT:
344 logger->log("Osd", Log::NOTICE, "Screenshot Message arrived");
345 Osd::getInstance()->screenShot("out.jpg");
348 case Message::CONNECTION_LOST:
353 case Message::INPUT_EVENT:
355 logger->log("Control", Log::NOTICE, "INPUT_EVENT %i", m->parameter);
357 handleCommand(m->parameter);
360 case Message::CHANGE_LANGUAGE:
362 boxstack->removeAllExceptWallpaper();
363 boxstack->update(wallpaper);
365 if (!VDR::getInstance()->isConnected()) { connectionLost(); break; }
366 VWelcome* vw = new VWelcome();
369 boxstack->update(vw);
372 case Message::LAST_VIEW_CLOSE:
374 // Shouldn't be done like this. Some generic message pass back from vinfo perhaps
381 // VWelcome* vw = new VWelcome();
383 // boxstack->add(vw);
384 // boxstack->update(vw);
388 case Message::NEW_PICTURE:
390 //Log::getInstance()->log("Control", Log::DEBUG, "TVMedia NEW_PICTURE");
391 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
392 if (osdv) osdv->informPicture(m->tag, reinterpret_cast<ImageIndex>(m->data));
395 case Message::NEW_PICTURE_STATIC:
397 //Log::getInstance()->log("Control", Log::DEBUG, "TVMedia NEW_PICTURE %x %x",m->tag,m->parameter);
398 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
399 if (osdv) osdv->informPicture(static_cast<unsigned long long>(m->tag) << 32LL, reinterpret_cast<ImageIndex>(m->data));
408 Instead of sending through the boxstack, implement a more generic MessageReceiver interface
409 and have potential receivers register with something
410 When a message needs to be delivered, check if the receiver is still registered, if so, deliver the message
411 This could all be done using the existing big control mutex to keep it simple
414 logger->log("Control", Log::DEBUG, "Sending message to boxstack");
415 boxstack->processMessage(m);
419 void Control::handleCommand(int button)
421 if (isStandby && (button != Input::POWER)
422 && (button != Input::POWERON)
423 && (button != Input::POWEROFF)) return;
425 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
427 // command was not handled
431 case Input::VOLUMEUP:
432 case Input::VOLUMEDOWN:
434 if (inputMan->handlesVolume()) // CEC volume handler?
436 if (button == Input::VOLUMEDOWN)
437 inputMan->volumeDown();
439 inputMan->volumeUp();
443 VVolume* v = new VVolume();
445 v->handleCommand(button); // this will draw+show
451 if (inputMan->handlesVolume())
453 inputMan->volumeMute();
457 VMute* v = new VMute();
474 case Input::POWEROFF:
482 if (!connLost) return; // if connLost, handle Input::OK
488 VSleeptimer* sleep = new VSleeptimer();
489 boxstack->add(sleep);
490 sleep->handleCommand(button); // this will draw+show
500 Message* m = new Message(); // break into master mutex
501 m->message = Message::SCREENSHOT;
508 void Control::doStandby()
521 void Control::doPowerOn()
525 Video::getInstance()->signalOn();
526 Led::getInstance()->on();
527 InputMan::getInstance()->changePowerState(true);
530 VConnect* vconnect = new VConnect();
531 boxstack->add(vconnect);
536 void Control::doPowerOff()
540 VDR::getInstance()->shutdownVDR();
541 boxstack->removeAllExceptWallpaper();
542 Video::getInstance()->signalOff();
543 boxstack->update(wallpaper);
545 VDR::getInstance()->configSave("General", "Last Power State", "Off");
546 logger->unsetExternLogger();
547 VDR::getInstance()->disconnect();
548 Led::getInstance()->off();
549 InputMan::getInstance()->changePowerState(false);
551 Sleeptimer::getInstance()->shutdown();
555 void Control::doFromTheTop(bool which)
557 if (isStandby) return;
562 logger->log("Control", Log::NOTICE, "Connection lost dialog already present");
566 logger->log("Control", Log::NOTICE, "Doing connection lost dialog");
567 connLost = new VInfo();
568 connLost->setSize(360, 200);
569 connLost->createBuffer();
570 if (Video::getInstance()->getFormat() == Video::PAL)
571 connLost->setPosition(190, 170);
573 connLost->setPosition(180, 120);
574 connLost->setOneLiner(tr("Connection lost"));
575 connLost->setDropThrough();
576 connLost->setBorderOn(1);
577 connLost->setTitleBarColour(DrawStyle::DANGER);
578 connLost->okButton();
580 boxstack->add(connLost);
581 boxstack->update(connLost);
583 clearMQInputEvents();
587 logger->unsetExternLogger();
588 VDR::getInstance()->disconnect();
589 boxstack->removeAllExceptWallpaper();
590 boxstack->update(wallpaper);
595 // at this point, everything should be reset to first-go
597 VConnect* vconnect = new VConnect();
598 boxstack->add(vconnect);
603 void Control::clearMQInputEvents()
605 std::lock_guard<std::mutex> lg(messageQueueMutex); // Get the lock
607 MQueueI i = messages.begin();
608 while(i != messages.end())
611 if (m->message == Message::INPUT_EVENT)
614 i = messages.erase(i);
623 void Control::doReboot()
626 logger->unsetExternLogger();
627 VDR::getInstance()->disconnect();
629 logger->log("Control", Log::NOTICE, "Reboot");
631 #ifndef VOMP_HAS_EXIT
632 // some plattforms, want a proper deinitialisation of their hardware before reboot
633 Osd::getInstance()->shutdown();
634 Audio::getInstance()->shutdown();
635 Video::getInstance()->shutdown();
636 InputMan::getInstance()->shutdown();
638 reboot(LINUX_REBOOT_CMD_RESTART);
639 // if reboot is not allowed -> stop
651 #endif //Would we support this on windows?
654 void Control::connectionLost()
656 logger->unsetExternLogger();
657 Message* m = new Message(); // break into master mutex
658 m->message = Message::CONNECTION_LOST;
659 m->p_to = Message::CONTROL;
663 void Control::buildCrashedBox()
665 VInfo* crash = new VInfo();
666 crash->setSize(360, 250);
667 crash->createBuffer();
668 if (Video::getInstance()->getFormat() == Video::PAL)
669 crash->setPosition(190, 146);
671 crash->setPosition(180, 96);
672 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.");
673 crash->setBorderOn(1);
674 crash->setTitleBarColour(DrawStyle::DANGER);
676 crash->setExitable();
678 boxstack->add(crash);
679 boxstack->update(crash);
682 int Control::getLangPref(bool subtitle, const char* langcode)
684 std::vector<struct ASLPref>::iterator itty=langcodes.begin();
685 char templangcode[4];
686 templangcode[0] = langcode[0];
687 templangcode[1] = langcode[1];
688 templangcode[2] = langcode[2];
689 templangcode[3] = '\0';
691 while (itty != langcodes.end())
693 size_t pos = (*itty).langcode.find(templangcode);
694 if (pos != std::string::npos)
696 //vector<struct ASLPref>::iterator itty2=langcodes.begin();
697 for (unsigned int i = 0; i < langcodes.size(); i++)
701 pref = langcodes[i].subtitlepref;
703 pref = langcodes[i].audiopref;
708 if (langcodes[i].subtitlepref==langpos) return i;
712 if (langcodes[i].audiopref==langpos) return i;
720 return langcodes.size(); //neutral
723 void Control::doJustConnected(VConnect* vconnect)
726 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
727 logger->log("Control", Log::INFO, "Entering doJustConnected");
729 Video* video = Video::getInstance();
730 Audio* audio = Audio::getInstance();
731 boxstack->remove(vconnect);
733 VInfo* vi = new VInfo();
734 vi->setSize(400, 200);
736 if (video->getFormat() == Video::PAL)
737 vi->setPosition(170, 200);
739 vi->setPosition(160, 150);
740 vi->setOneLiner(tr("Connected, loading config"));
743 boxstack->update(vi);
745 // FIXME make config system
749 // See if we're supposed to do network logging
750 config = vdr->configLoad("Advanced", "Network logging");
751 if (config && !STRCASECMP(config, "On"))
753 logger->log("Control", Log::INFO, "Turning on network logging");
754 logger->setExternLogger(vdr);
758 logger->unsetExternLogger();
759 logger->log("Control", Log::INFO, "Turned off network logging");
761 if (config) delete[] config;
763 config = vdr->configLoad("Advanced", "Skin Name");
766 const char **skinnames=SkinFactory::getSkinNames();
767 for (int i=0;i<SkinFactory::getNumberofSkins();i++)
769 if (!STRCASECMP(config, skinnames[i]))
771 SkinFactory::InitSkin(i);
777 if (wallpaper && wallpaper_pict)
779 if (DrawStyle::WALLPAPER.alpha)
780 wallpaper_pict->setVisible(true);
782 wallpaper_pict->setVisible(false);
785 boxstack->update(wallpaper);
790 SkinFactory::InitSkin(0);
793 // See if config says to override video format (PAL/NTSC)
794 config = vdr->configLoad("General", "Override Video Format");
797 logger->log("Control", Log::DEBUG, "Override Video Format is present");
799 if ( (!strcmp(config, "PAL") && (video->getFormat() != Video::PAL))
800 || (!strcmp(config, "NTSC") && (video->getFormat() != Video::NTSC))
801 || (!strcmp(config, "PAL_M") && (video->getFormat() != Video::PAL_M))
802 || (!strcmp(config, "NTSC_J") && (video->getFormat() != Video::NTSC_J))
805 // Oh sheesh, need to switch format. Bye bye TV...
807 // Take everything down
808 boxstack->removeAllExceptWallpaper();
809 boxstack->remove(wallpaper);
810 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
812 Osd* osd = Osd::getInstance();
818 inputMan->shutdown(); // need on raspberry shut not do any harm, hopefully
819 inputMan->init(); // FIXME this breaks badly now
821 // Get video and osd back up with the new mode
822 if (!strcmp(config, "PAL"))
824 logger->log("Control", Log::DEBUG, "Switching to PAL");
825 video->init(Video::PAL);
827 else if (!strcmp(config, "NTSC"))
829 logger->log("Control", Log::DEBUG, "Switching to NTSC");
830 video->init(Video::NTSC);
831 } else if (!strcmp(config, "PAL_M"))
833 logger->log("Control", Log::DEBUG, "Switching to PAL_M");
834 video->init(Video::PAL_M);
835 } else if (!strcmp(config, "NTSC_J"))
837 logger->log("Control", Log::DEBUG, "Switching to NTSC_J");
838 video->init(Video::NTSC_J);
843 //we do not init twice
847 // Put the wallpaper back
852 vi->setSize(400, 200);
854 if (video->getFormat() == Video::PAL)
855 vi->setPosition(170, 200);
857 vi->setPosition(160, 150);
859 vi->setOneLiner(tr("Connected, loading config"));
862 boxstack->update(vi);
866 logger->log("Control", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
871 logger->log("Control", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
874 // Power off if first boot and config says so
879 logger->log("Control", Log::DEBUG, "Load power after boot");
881 config = vdr->configLoad("General", "Power After Boot");
885 if (!STRCASECMP(config, "On"))
887 logger->log("Control", Log::INFO, "Config says Power After Boot = On");
889 else if (!STRCASECMP(config, "Off"))
891 logger->log("Control", Log::INFO, "Config says Power After Boot = Off");
896 else if (!STRCASECMP(config, "Last state"))
898 char* lastPowerState = vdr->configLoad("General", "Last Power State");
901 if (!STRCASECMP(lastPowerState, "On"))
903 logger->log("Control", Log::INFO, "Config says Last Power State = On");
905 else if (!STRCASECMP(lastPowerState, "Off"))
907 logger->log("Control", Log::INFO, "Config says Last Power State = Off");
914 logger->log("Control", Log::INFO, "Config General/Last Power State not understood");
919 logger->log("Control", Log::INFO, "Config General/Last Power State not found");
924 logger->log("Control", Log::INFO, "Config/Power After Boot not understood");
930 logger->log("Control", Log::INFO, "Config General/Power After Boot not found");
935 // Go S-Video if config says so
937 config = vdr->configLoad("TV", "Connection");
941 if (!STRCASECMP(config, "S-Video"))
943 logger->log("Control", Log::INFO, "Switching to S-Video as Connection=%s", config);
944 video->setConnection(Video::SVIDEO);
945 } else if (!STRCASECMP(config, "HDMI"))
947 logger->log("Control", Log::INFO, "Switching to HDMI as Connection=%s", config);
948 video->setConnection(Video::HDMI);
949 } else if (!STRCASECMP(config, "HDMI3D"))
951 logger->log("Control", Log::INFO, "Switching to HDMI3D as Connection=%s", config);
952 video->setConnection(Video::HDMI3D);
956 logger->log("Control", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
957 video->setConnection(Video::COMPOSITERGB);
963 logger->log("Control", Log::INFO, "Config TV/S-Video not found");
966 // Set to shutdown VDR if config says
968 config = vdr->configLoad("General", "VDR shutdown");
971 if (!STRCASECMP(config, "On"))
973 logger->log("Control", Log::INFO, "Shutdown VDR when shutting down vomp");
974 vdr->setVDRShutdown(true);
976 else if (!STRCASECMP(config, "Off"))
978 logger->log("Control", Log::INFO, "Shutdown only vomp");
979 vdr->setVDRShutdown(false);
984 logger->log("Control", Log::INFO, "Default shutdown only vomp");
985 vdr->setVDRShutdown(false); // Default
988 // Get TV aspect ratio
990 config = vdr->configLoad("TV", "Aspect");
993 if (!STRCASECMP(config, "16:9"))
995 logger->log("Control", Log::INFO, "/// Switching to TV aspect 16:9");
996 video->setTVsize(Video::ASPECT16X9);
1000 logger->log("Control", Log::INFO, "/// Switching to TV aspect 4:3");
1001 video->setTVsize(Video::ASPECT4X3);
1007 logger->log("Control", Log::INFO, "Config TV/Aspect type not found, going 4:3");
1008 video->setTVsize(Video::ASPECT4X3);
1011 config = vdr->configLoad("TV", "Widemode");
1014 if (!STRCASECMP(config, "Letterbox"))
1016 logger->log("Control", Log::INFO, "Setting letterbox mode");
1017 video->setMode(Video::LETTERBOX);
1021 logger->log("Control", Log::INFO, "Setting chop-sides mode");
1022 video->setMode(Video::NORMAL);
1029 logger->log("Control", Log::INFO, "Config TV/Widemode not found, Setting letterbox mode");
1030 video->setMode(Video::LETTERBOX);
1032 logger->log("Control", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
1033 video->setMode(Video::NORMAL);
1037 config = vdr->configLoad("Advanced", "TCP receive window");
1040 size_t newTCPsize = atoi(config);
1043 logger->log("Control", Log::INFO, "Setting TCP window size %i", newTCPsize);
1044 vdr->setReceiveWindow(newTCPsize);
1048 logger->log("Control", Log::INFO, "TCP window size not found, setting 2048");
1049 if (DEFAULT_TCP_WINDOWSIZE) vdr->setReceiveWindow(2048); // Default
1052 config = vdr->configLoad("Advanced", "Font Name");
1055 Osd::getInstance()->setFont(config);
1056 logger->log("Control", Log::INFO, "Setting Font to %s", config);
1062 // Set recording list type
1064 #ifdef ADVANCED_MENUES
1065 config = vdr->configLoad("Advanced", "Menu type");
1069 if (!STRCASECMP(config, "Advanced"))
1071 logger->log("Control", Log::INFO, "Switching to Advanced menu");
1076 logger->log("Control", Log::INFO, "Switching to Classic menu");
1083 logger->log("Control", Log::INFO, "Config General/menu type not found");
1088 config = vdr->configLoad("Advanced", "Disable WOL");
1091 if (!STRCASECMP(config, "Yes"))
1093 logger->log("Control", Log::INFO, "Config says disable WOL");
1094 Wol::getInstance()->setEnabled(false);
1098 logger->log("Control", Log::INFO, "Config says enable WOL");
1099 Wol::getInstance()->setEnabled(true);
1106 logger->log("Control", Log::INFO, "By default, enable WOL");
1107 Wol::getInstance()->setEnabled(true);
1109 /* device dependend config */
1110 audio->loadOptionsFromServer(vdr);
1111 video->loadOptionsFromServer(vdr);
1112 inputMan->loadOptionsFromServer(vdr);
1114 video->executePendingModeChanges();
1117 // Save power state = on
1119 vdr->configSave("General", "Last Power State", "On");
1121 // Make sure connection didn't die
1122 if (!vdr->isConnected())
1124 Control::getInstance()->connectionLost();
1128 boxstack->remove(vi);
1130 VWelcome* vw = new VWelcome();
1133 boxstack->update(vw);
1135 // Enter pre-keys here
1136 // handleCommand(Input::OK);
1137 // handleCommand(Input::THREE);
1138 // handleCommand(Input::SIX);
1139 // handleCommand(Input::OK);
1140 // handleCommand(Input::UP);
1141 // handleCommand(Input::PLAY);
1142 // handleCommand(Input::DOWN);
1143 // handleCommand(Input::DOWN);
1144 // handleCommand(Input::DOWN);
1145 // handleCommand(Input::RIGHT);
1146 // handleCommand(Input::RED);