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"
44 #include "timerreceiver.h"
55 #include "vsleeptimer.h"
57 #include "osdvector.h"
60 Command* Command::instance = NULL;
74 Command* Command::getInstance()
79 int Command::init(bool tcrashed, char* tServer)
81 if (initted) return 0;
86 logger = Log::getInstance();
87 boxstack = BoxStack::getInstance();
88 inputMan = InputMan::getInstance();
90 if (!logger || !boxstack || !inputMan)
96 SkinFactory::InitSkin(0);
101 int Command::shutdown()
103 if (!initted) return 0;
110 logger->log("Command", Log::NOTICE, "Request stop");
112 Message* m = new Message(); // break master loop
113 m->message = Message::SHUTDOWN;
118 void Command::doWallpaper()
120 Video* video = Video::getInstance();
123 Boxx* bbg = new Boxx();
124 bbg->setSize(video->getScreenWidth(), video->getScreenHeight());
126 bbg->fillColour(DrawStyle::WALLPAPER);
128 boxstack->update(bbg);
129 boxstack->remove(bbg);
132 wallpaper = new Boxx();
133 wallpaper->setSize(video->getScreenWidth(), video->getScreenHeight());
134 wallpaper->createBuffer();
135 wallpaper->setBackgroundColour(DrawStyle::WALLPAPER);
137 wallpaper_pict = new WJpegTYPE();
138 wallpaper_pict->setSize(video->getScreenWidth(), video->getScreenHeight());
140 if (video->getFormat() == Video::PAL)
142 logger->log("Command", Log::DEBUG, "PAL wallpaper selected");
144 wallpaper_pict->init("/wallpaperPAL.jpg");
146 wallpaper_pict->init("wallpaperPAL.jpg");
151 logger->log("Command", Log::DEBUG, "NTSC wallpaper selected");
152 wallpaper_pict->init("/wallpaperNTSC.jpg");
155 if (DrawStyle::WALLPAPER.alpha)
156 wallpaper_pict->setVisible(true);
158 wallpaper_pict->setVisible(false);
160 wallpaper->add(wallpaper_pict);
163 boxstack->add(wallpaper);
164 boxstack->update(wallpaper);
166 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
167 if (osdv) osdv->updateBackgroundColor(DrawStyle::WALLPAPER);
172 if (!initted) return;
176 Video::getInstance()->signalOn();
177 Led::getInstance()->on();
187 VConnect* vconnect = new VConnect(server);
188 boxstack->add(vconnect);
193 // FIXME Input::NA_SIGNAL is possibly obsolete now
195 std::unique_lock<std::mutex> lockWrapper(messageQueueMutex); // locks. unlocks on out-of-scope
201 messageQueueCond.wait(lockWrapper, [&] { return !irun || !messages.empty(); });
202 logger->log("Command", Log::DEBUG, "woke");
206 while(!messages.empty())
208 Message* m = messages.front();
209 messages.pop_front();
211 lockWrapper.unlock();
222 boxstack->removeAllExceptWallpaper();
223 boxstack->remove(wallpaper);
224 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
227 void Command::processMessage(Message* m)
229 // FIXME - a slight modification - how if messagereceivers were to register
230 // themselves as receivers to avoid the calling-a-deleted-object problem
231 // then only deliver/register/unregister would have to be protected
233 logger->log("Command", Log::DEBUG, "processing message %i", m->message);
240 case Message::SHUTDOWN:
246 case Message::STOP_PLAYBACK:
248 handleCommand(Input::STOP); // an odd way of doing it, but so simple
251 // Also connection_lost comes from player - anywhere else?
255 case Message::VDR_CONNECTED:
257 doJustConnected(static_cast<VConnect*>(m->from));
260 case Message::SCREENSHOT:
262 logger->log("Osd", Log::NOTICE, "Screenshot Message arrived");
263 Osd::getInstance()->screenShot("out.jpg");
266 case Message::CONNECTION_LOST:
271 case Message::INPUT_EVENT:
273 logger->log("Command", Log::NOTICE, "INPUT_EVENT %i", m->parameter);
275 handleCommand(m->parameter);
278 case Message::CHANGE_LANGUAGE:
280 boxstack->removeAllExceptWallpaper();
281 boxstack->update(wallpaper);
283 if (!VDR::getInstance()->isConnected()) { connectionLost(); break; }
284 VWelcome* vw = new VWelcome();
287 boxstack->update(vw);
290 case Message::LAST_VIEW_CLOSE:
292 // Shouldn't be done like this. Some generic message pass back from vinfo perhaps
299 // VWelcome* vw = new VWelcome();
301 // boxstack->add(vw);
302 // boxstack->update(vw);
306 case Message::NEW_PICTURE:
308 //Log::getInstance()->log("Command", Log::DEBUG, "TVMedia NEW_PICTURE");
309 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
310 if (osdv) osdv->informPicture(m->tag, reinterpret_cast<ImageIndex>(m->data));
313 case Message::NEW_PICTURE_STATIC:
315 //Log::getInstance()->log("Command", Log::DEBUG, "TVMedia NEW_PICTURE %x %x",m->tag,m->parameter);
316 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
317 if (osdv) osdv->informPicture(static_cast<unsigned long long>(m->tag) << 32LL, reinterpret_cast<ImageIndex>(m->data));
326 Instead of sending through the boxstack, implement a more generic MessageReceiver interface
327 and have potential receivers register with something
328 When a message needs to be delivered, check if the receiver is still registered, if so, deliver the message
329 This could all be done using the existing big command mutex to keep it simple
332 logger->log("Command", Log::DEBUG, "Sending message to boxstack");
333 boxstack->processMessage(m);
337 void Command::handleCommand(int button)
339 if (isStandby && (button != Input::POWER)
340 && (button != Input::POWERON)
341 && (button != Input::POWEROFF)) return;
343 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
345 // command was not handled
349 case Input::VOLUMEUP:
350 case Input::VOLUMEDOWN:
352 if (inputMan->handlesVolume()) // CEC volume handler?
354 if (button == Input::VOLUMEDOWN)
355 inputMan->volumeDown();
357 inputMan->volumeUp();
361 VVolume* v = new VVolume();
363 v->handleCommand(button); // this will draw+show
369 if (inputMan->handlesVolume())
371 inputMan->volumeMute();
375 VMute* v = new VMute();
392 case Input::POWEROFF:
400 if (!connLost) return; // if connLost, handle Input::OK
406 VSleeptimer* sleep = new VSleeptimer();
407 boxstack->add(sleep);
408 sleep->handleCommand(button); // this will draw+show
417 Message* m = new Message(); // break into master mutex
418 m->message = Message::SCREENSHOT;
424 void Command::doStandby()
437 void Command::doPowerOn()
441 Video::getInstance()->signalOn();
442 Led::getInstance()->on();
443 InputMan::getInstance()->changePowerState(true);
446 VConnect* vconnect = new VConnect(server);
447 boxstack->add(vconnect);
452 void Command::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 Command::doFromTheTop(bool which)
473 if (isStandby) return;
478 logger->log("Command", Log::NOTICE, "Connection lost dialog already present");
482 logger->log("Command", 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(server);
514 boxstack->add(vconnect);
519 void Command::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 Command::doReboot()
542 logger->unsetExternLogger();
543 VDR::getInstance()->disconnect();
545 logger->log("Command", 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 Command::connectionLost()
572 logger->unsetExternLogger();
573 Message* m = new Message(); // break into master mutex
574 m->message = Message::CONNECTION_LOST;
579 void Command::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 Command::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 Command::doJustConnected(VConnect* vconnect)
642 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
643 logger->log("Command", 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 VDR* vdr = VDR::getInstance();
664 // See if we're supposed to do network logging
665 config = vdr->configLoad("Advanced", "Network logging");
666 if (config && !STRCASECMP(config, "On"))
668 logger->log("Command", Log::INFO, "Turning on network logging");
669 logger->setExternLogger(vdr);
673 logger->unsetExternLogger();
674 logger->log("Command", Log::INFO, "Turned off network logging");
676 if (config) delete[] config;
678 config = vdr->configLoad("Advanced", "Skin Name");
681 const char **skinnames=SkinFactory::getSkinNames();
682 for (int i=0;i<SkinFactory::getNumberofSkins();i++)
684 if (!STRCASECMP(config, skinnames[i]))
686 SkinFactory::InitSkin(i);
692 if (wallpaper && wallpaper_pict)
694 if (DrawStyle::WALLPAPER.alpha)
695 wallpaper_pict->setVisible(true);
697 wallpaper_pict->setVisible(false);
700 boxstack->update(wallpaper);
705 SkinFactory::InitSkin(0);
708 // See if config says to override video format (PAL/NTSC)
709 config = vdr->configLoad("General", "Override Video Format");
712 logger->log("Command", Log::DEBUG, "Override Video Format is present");
714 if ( (!strcmp(config, "PAL") && (video->getFormat() != Video::PAL))
715 || (!strcmp(config, "NTSC") && (video->getFormat() != Video::NTSC))
716 || (!strcmp(config, "PAL_M") && (video->getFormat() != Video::PAL_M))
717 || (!strcmp(config, "NTSC_J") && (video->getFormat() != Video::NTSC_J))
720 // Oh sheesh, need to switch format. Bye bye TV...
722 // Take everything down
723 boxstack->removeAllExceptWallpaper();
724 boxstack->remove(wallpaper);
725 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
727 Osd* osd = Osd::getInstance();
733 inputMan->shutdown(); // need on raspberry shut not do any harm, hopefully
734 inputMan->init(); // FIXME this breaks badly now
736 // Get video and osd back up with the new mode
737 if (!strcmp(config, "PAL"))
739 logger->log("Command", Log::DEBUG, "Switching to PAL");
740 video->init(Video::PAL);
742 else if (!strcmp(config, "NTSC"))
744 logger->log("Command", Log::DEBUG, "Switching to NTSC");
745 video->init(Video::NTSC);
746 } else if (!strcmp(config, "PAL_M"))
748 logger->log("Command", Log::DEBUG, "Switching to PAL_M");
749 video->init(Video::PAL_M);
750 } else if (!strcmp(config, "NTSC_J"))
752 logger->log("Command", Log::DEBUG, "Switching to NTSC_J");
753 video->init(Video::NTSC_J);
758 //we do not init twice
762 // Put the wallpaper back
767 vi->setSize(400, 200);
769 if (video->getFormat() == Video::PAL)
770 vi->setPosition(170, 200);
772 vi->setPosition(160, 150);
774 vi->setOneLiner(tr("Connected, loading config"));
777 boxstack->update(vi);
781 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
786 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
789 // Power off if first boot and config says so
794 logger->log("Command", Log::DEBUG, "Load power after boot");
796 config = vdr->configLoad("General", "Power After Boot");
800 if (!STRCASECMP(config, "On"))
802 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
804 else if (!STRCASECMP(config, "Off"))
806 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
811 else if (!STRCASECMP(config, "Last state"))
813 char* lastPowerState = vdr->configLoad("General", "Last Power State");
816 if (!STRCASECMP(lastPowerState, "On"))
818 logger->log("Command", Log::INFO, "Config says Last Power State = On");
820 else if (!STRCASECMP(lastPowerState, "Off"))
822 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
829 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
834 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
839 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
845 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
850 // Go S-Video if config says so
852 config = vdr->configLoad("TV", "Connection");
856 if (!STRCASECMP(config, "S-Video"))
858 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
859 video->setConnection(Video::SVIDEO);
860 } else if (!STRCASECMP(config, "HDMI"))
862 logger->log("Command", Log::INFO, "Switching to HDMI as Connection=%s", config);
863 video->setConnection(Video::HDMI);
864 } else if (!STRCASECMP(config, "HDMI3D"))
866 logger->log("Command", Log::INFO, "Switching to HDMI3D as Connection=%s", config);
867 video->setConnection(Video::HDMI3D);
871 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
872 video->setConnection(Video::COMPOSITERGB);
878 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
881 // Set to shutdown VDR if config says
883 config = vdr->configLoad("General", "VDR shutdown");
886 if (!STRCASECMP(config, "On"))
888 logger->log("Command", Log::INFO, "Shutdown VDR when shutting down vomp");
889 vdr->setVDRShutdown(true);
891 else if (!STRCASECMP(config, "Off"))
893 logger->log("Command", Log::INFO, "Shutdown only vomp");
894 vdr->setVDRShutdown(false);
899 logger->log("Command", Log::INFO, "Default shutdown only vomp");
900 vdr->setVDRShutdown(false); // Default
903 // Get TV aspect ratio
905 config = vdr->configLoad("TV", "Aspect");
908 if (!STRCASECMP(config, "16:9"))
910 logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
911 video->setTVsize(Video::ASPECT16X9);
915 logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
916 video->setTVsize(Video::ASPECT4X3);
922 logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
923 video->setTVsize(Video::ASPECT4X3);
926 config = vdr->configLoad("TV", "Widemode");
929 if (!STRCASECMP(config, "Letterbox"))
931 logger->log("Command", Log::INFO, "Setting letterbox mode");
932 video->setMode(Video::LETTERBOX);
936 logger->log("Command", Log::INFO, "Setting chop-sides mode");
937 video->setMode(Video::NORMAL);
944 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting letterbox mode");
945 video->setMode(Video::LETTERBOX);
947 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
948 video->setMode(Video::NORMAL);
952 config = vdr->configLoad("Advanced", "TCP receive window");
955 size_t newTCPsize = atoi(config);
958 logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
959 vdr->setReceiveWindow(newTCPsize);
963 logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
964 if (DEFAULT_TCP_WINDOWSIZE) vdr->setReceiveWindow(2048); // Default
967 config = vdr->configLoad("Advanced", "Font Name");
970 Osd::getInstance()->setFont(config);
971 logger->log("Command", Log::INFO, "Setting Font to %s", config);
977 // Set recording list type
979 #ifdef ADVANCED_MENUES
980 config = vdr->configLoad("Advanced", "Menu type");
984 if (!STRCASECMP(config, "Advanced"))
986 logger->log("Command", Log::INFO, "Switching to Advanced menu");
991 logger->log("Command", Log::INFO, "Switching to Classic menu");
998 logger->log("Command", Log::INFO, "Config General/menu type not found");
1003 config = vdr->configLoad("Advanced", "Disable WOL");
1006 if (!STRCASECMP(config, "Yes"))
1008 logger->log("Command", Log::INFO, "Config says disable WOL");
1009 Wol::getInstance()->setEnabled(false);
1013 logger->log("Command", Log::INFO, "Config says enable WOL");
1014 Wol::getInstance()->setEnabled(true);
1021 logger->log("Command", Log::INFO, "By default, enable WOL");
1022 Wol::getInstance()->setEnabled(true);
1024 /* device dependend config */
1025 audio->loadOptionsFromServer(vdr);
1026 video->loadOptionsFromServer(vdr);
1027 inputMan->loadOptionsFromServer(vdr);
1029 video->executePendingModeChanges();
1032 // Save power state = on
1034 vdr->configSave("General", "Last Power State", "On");
1036 // Make sure connection didn't die
1037 if (!vdr->isConnected())
1039 Command::getInstance()->connectionLost();
1043 boxstack->remove(vi);
1045 VWelcome* vw = new VWelcome();
1048 boxstack->update(vw);
1050 // Enter pre-keys here
1051 // handleCommand(Input::OK);
1052 // handleCommand(Input::THREE);
1053 // handleCommand(Input::SIX);
1054 // handleCommand(Input::OK);
1055 // handleCommand(Input::UP);
1056 // handleCommand(Input::PLAY);
1057 // handleCommand(Input::DOWN);
1058 // handleCommand(Input::DOWN);
1059 // handleCommand(Input::DOWN);
1060 // handleCommand(Input::RIGHT);
1061 // handleCommand(Input::RED);