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 Command* Command::instance = NULL;
73 Command* Command::getInstance()
78 int Command::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 Command::shutdown()
101 if (!initted) return 0;
108 logger->log("Command", Log::NOTICE, "Request stop");
110 Message* m = new Message(); // break master loop
111 m->message = Message::SHUTDOWN;
116 void Command::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("Command", Log::DEBUG, "PAL wallpaper selected");
142 wallpaper_pict->init("/wallpaperPAL.jpg");
144 wallpaper_pict->init("wallpaperPAL.jpg");
149 logger->log("Command", 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("Command", 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 Command::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("Command", Log::DEBUG, "processing message %i", m->message);
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("Command", 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("Command", 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("Command", 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 command mutex to keep it simple
330 logger->log("Command", Log::DEBUG, "Sending message to boxstack");
331 boxstack->processMessage(m);
335 void Command::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
415 Message* m = new Message(); // break into master mutex
416 m->message = Message::SCREENSHOT;
422 void Command::doStandby()
435 void Command::doPowerOn()
439 Video::getInstance()->signalOn();
440 Led::getInstance()->on();
441 InputMan::getInstance()->changePowerState(true);
444 VConnect* vconnect = new VConnect();
445 boxstack->add(vconnect);
450 void Command::doPowerOff()
454 VDR::getInstance()->shutdownVDR();
455 boxstack->removeAllExceptWallpaper();
456 Video::getInstance()->signalOff();
457 boxstack->update(wallpaper);
459 VDR::getInstance()->configSave("General", "Last Power State", "Off");
460 logger->unsetExternLogger();
461 VDR::getInstance()->disconnect();
462 Led::getInstance()->off();
463 InputMan::getInstance()->changePowerState(false);
465 Sleeptimer::getInstance()->shutdown();
469 void Command::doFromTheTop(bool which)
471 if (isStandby) return;
476 logger->log("Command", Log::NOTICE, "Connection lost dialog already present");
480 logger->log("Command", Log::NOTICE, "Doing connection lost dialog");
481 connLost = new VInfo();
482 connLost->setSize(360, 200);
483 connLost->createBuffer();
484 if (Video::getInstance()->getFormat() == Video::PAL)
485 connLost->setPosition(190, 170);
487 connLost->setPosition(180, 120);
488 connLost->setOneLiner(tr("Connection lost"));
489 connLost->setDropThrough();
490 connLost->setBorderOn(1);
491 connLost->setTitleBarColour(DrawStyle::DANGER);
492 connLost->okButton();
494 boxstack->add(connLost);
495 boxstack->update(connLost);
497 clearMQInputEvents();
501 logger->unsetExternLogger();
502 VDR::getInstance()->disconnect();
503 boxstack->removeAllExceptWallpaper();
504 boxstack->update(wallpaper);
509 // at this point, everything should be reset to first-go
511 VConnect* vconnect = new VConnect();
512 boxstack->add(vconnect);
517 void Command::clearMQInputEvents()
519 std::lock_guard<std::mutex> lg(messageQueueMutex); // Get the lock
521 MQueueI i = messages.begin();
522 while(i != messages.end())
525 if (m->message == Message::INPUT_EVENT)
528 i = messages.erase(i);
537 void Command::doReboot()
540 logger->unsetExternLogger();
541 VDR::getInstance()->disconnect();
543 logger->log("Command", Log::NOTICE, "Reboot");
545 #ifndef VOMP_HAS_EXIT
546 // some plattforms, want a proper deinitialisation of their hardware before reboot
547 Osd::getInstance()->shutdown();
548 Audio::getInstance()->shutdown();
549 Video::getInstance()->shutdown();
550 InputMan::getInstance()->shutdown();
552 reboot(LINUX_REBOOT_CMD_RESTART);
553 // if reboot is not allowed -> stop
565 #endif //Would we support this on windows?
568 void Command::connectionLost()
570 logger->unsetExternLogger();
571 Message* m = new Message(); // break into master mutex
572 m->message = Message::CONNECTION_LOST;
577 void Command::buildCrashedBox()
579 VInfo* crash = new VInfo();
580 crash->setSize(360, 250);
581 crash->createBuffer();
582 if (Video::getInstance()->getFormat() == Video::PAL)
583 crash->setPosition(190, 146);
585 crash->setPosition(180, 96);
586 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.");
587 crash->setBorderOn(1);
588 crash->setTitleBarColour(DrawStyle::DANGER);
590 crash->setExitable();
592 boxstack->add(crash);
593 boxstack->update(crash);
596 int Command::getLangPref(bool subtitle, const char* langcode)
598 std::vector<struct ASLPref>::iterator itty=langcodes.begin();
599 char templangcode[4];
600 templangcode[0] = langcode[0];
601 templangcode[1] = langcode[1];
602 templangcode[2] = langcode[2];
603 templangcode[3] = '\0';
605 while (itty != langcodes.end())
607 size_t pos = (*itty).langcode.find(templangcode);
608 if (pos != std::string::npos)
610 //vector<struct ASLPref>::iterator itty2=langcodes.begin();
611 for (unsigned int i = 0; i < langcodes.size(); i++)
615 pref = langcodes[i].subtitlepref;
617 pref = langcodes[i].audiopref;
622 if (langcodes[i].subtitlepref==langpos) return i;
626 if (langcodes[i].audiopref==langpos) return i;
634 return langcodes.size(); //neutral
637 void Command::doJustConnected(VConnect* vconnect)
640 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
641 logger->log("Command", Log::INFO, "Entering doJustConnected");
643 Video* video = Video::getInstance();
644 Audio* audio = Audio::getInstance();
645 boxstack->remove(vconnect);
647 VInfo* vi = new VInfo();
648 vi->setSize(400, 200);
650 if (video->getFormat() == Video::PAL)
651 vi->setPosition(170, 200);
653 vi->setPosition(160, 150);
654 vi->setOneLiner(tr("Connected, loading config"));
657 boxstack->update(vi);
659 // FIXME make config system
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);