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
26 #include "inputManwin.h"
30 #include "inputManandroid.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
199 // Start method 2 of getting commands in... // FIXME move this inside input
204 messageQueueCond.wait(lockWrapper, [&] { return !irun || !messages.empty(); });
205 logger->log("Command", Log::DEBUG, "woke");
209 while(!messages.empty())
211 Message* m = messages.front();
212 messages.pop_front();
214 lockWrapper.unlock();
225 boxstack->removeAllExceptWallpaper();
226 boxstack->remove(wallpaper);
227 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
230 void Command::processMessage(Message* m)
232 // FIXME - a slight modification - how if messagereceivers were to register
233 // themselves as receivers to avoid the calling-a-deleted-object problem
234 // then only deliver/register/unregister would have to be protected
236 logger->log("Command", Log::DEBUG, "processing message %i", m->message);
243 case Message::SHUTDOWN:
250 case Message::STOP_PLAYBACK:
252 handleCommand(Input::STOP); // an odd way of doing it, but so simple
255 // Also connection_lost comes from player - anywhere else?
259 case Message::VDR_CONNECTED:
261 doJustConnected(static_cast<VConnect*>(m->from));
264 case Message::SCREENSHOT:
266 logger->log("Osd", Log::NOTICE, "Screenshot Message arrived");
267 Osd::getInstance()->screenShot("out.jpg");
270 case Message::CONNECTION_LOST:
275 case Message::INPUT_EVENT:
277 logger->log("Command", Log::NOTICE, "INPUT_EVENT %i", m->parameter);
279 handleCommand(m->parameter);
282 case Message::UDP_BUTTON:
284 handleCommand(m->parameter);
287 case Message::CHANGE_LANGUAGE:
289 boxstack->removeAllExceptWallpaper();
290 boxstack->update(wallpaper);
292 if (!VDR::getInstance()->isConnected()) { connectionLost(); break; }
293 VWelcome* vw = new VWelcome();
296 boxstack->update(vw);
299 case Message::LAST_VIEW_CLOSE:
301 // Shouldn't be done like this. Some generic message pass back from vinfo perhaps
308 // VWelcome* vw = new VWelcome();
310 // boxstack->add(vw);
311 // boxstack->update(vw);
315 case Message::NEW_PICTURE:
317 //Log::getInstance()->log("Command", Log::DEBUG, "TVMedia NEW_PICTURE");
318 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
319 if (osdv) osdv->informPicture(m->tag, reinterpret_cast<ImageIndex>(m->data));
322 case Message::NEW_PICTURE_STATIC:
324 //Log::getInstance()->log("Command", Log::DEBUG, "TVMedia NEW_PICTURE %x %x",m->tag,m->parameter);
325 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
326 if (osdv) osdv->informPicture(static_cast<unsigned long long>(m->tag) << 32LL, reinterpret_cast<ImageIndex>(m->data));
335 Instead of sending through the boxstack, implement a more generic MessageReceiver interface
336 and have potential receivers register with something
337 When a message needs to be delivered, check if the receiver is still registered, if so, deliver the message
338 This could all be done using the existing big command mutex to keep it simple
341 logger->log("Command", Log::DEBUG, "Sending message to boxstack");
342 boxstack->processMessage(m);
346 void Command::handleCommand(int button)
348 if (isStandby && (button != Input::POWER)
349 && (button != Input::POWERON)
350 && (button != Input::POWEROFF)) return;
352 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
354 // command was not handled
358 case Input::VOLUMEUP:
359 case Input::VOLUMEDOWN:
361 if (inputMan->handlesVolume()) // CEC volume handler?
363 if (button == Input::VOLUMEDOWN)
364 inputMan->volumeDown();
366 inputMan->volumeUp();
370 VVolume* v = new VVolume();
372 v->handleCommand(button); // this will draw+show
378 if (inputMan->handlesVolume())
380 inputMan->volumeMute();
384 VMute* v = new VMute();
401 case Input::POWEROFF:
409 if (!connLost) return; // if connLost, handle Input::OK
415 VSleeptimer* sleep = new VSleeptimer();
416 boxstack->add(sleep);
417 sleep->handleCommand(button); // this will draw+show
426 Message* m = new Message(); // break into master mutex
427 m->message = Message::SCREENSHOT;
433 void Command::doStandby()
446 void Command::doPowerOn()
450 Video::getInstance()->signalOn();
451 Led::getInstance()->on();
452 InputMan::getInstance()->changePowerState(true);
455 VConnect* vconnect = new VConnect(server);
456 boxstack->add(vconnect);
461 void Command::doPowerOff()
465 VDR::getInstance()->shutdownVDR();
466 boxstack->removeAllExceptWallpaper();
467 Video::getInstance()->signalOff();
468 boxstack->update(wallpaper);
470 VDR::getInstance()->configSave("General", "Last Power State", "Off");
471 logger->unsetExternLogger();
472 VDR::getInstance()->disconnect();
473 Led::getInstance()->off();
474 InputMan::getInstance()->changePowerState(false);
476 Sleeptimer::getInstance()->shutdown();
478 stop(); //different behavoiur on windows, we exit
483 void Command::doFromTheTop(bool which)
485 if (isStandby) return;
490 logger->log("Command", Log::NOTICE, "Connection lost dialog already present");
494 logger->log("Command", Log::NOTICE, "Doing connection lost dialog");
495 connLost = new VInfo();
496 connLost->setSize(360, 200);
497 connLost->createBuffer();
498 if (Video::getInstance()->getFormat() == Video::PAL)
499 connLost->setPosition(190, 170);
501 connLost->setPosition(180, 120);
502 connLost->setOneLiner(tr("Connection lost"));
503 connLost->setDropThrough();
504 connLost->setBorderOn(1);
505 connLost->setTitleBarColour(DrawStyle::DANGER);
506 connLost->okButton();
508 boxstack->add(connLost);
509 boxstack->update(connLost);
511 clearMQInputEvents();
515 logger->unsetExternLogger();
516 VDR::getInstance()->disconnect();
517 boxstack->removeAllExceptWallpaper();
518 boxstack->update(wallpaper);
523 // at this point, everything should be reset to first-go
525 VConnect* vconnect = new VConnect(server);
526 boxstack->add(vconnect);
531 void Command::clearMQInputEvents()
533 // FIXME implement this
536 void Command::doReboot()
539 logger->unsetExternLogger();
540 VDR::getInstance()->disconnect();
542 logger->log("Command", Log::NOTICE, "Reboot");
544 #ifndef VOMP_HAS_EXIT
545 // some plattforms, want a proper deinitialisation of their hardware before reboot
546 Osd::getInstance()->shutdown();
547 Audio::getInstance()->shutdown();
548 Video::getInstance()->shutdown();
549 InputMan::getInstance()->shutdown();
551 reboot(LINUX_REBOOT_CMD_RESTART);
552 // if reboot is not allowed -> stop
564 #endif //Would we support this on windows?
567 void Command::connectionLost()
569 logger->unsetExternLogger();
570 Message* m = new Message(); // break into master mutex
571 m->message = Message::CONNECTION_LOST;
576 void Command::buildCrashedBox()
578 VInfo* crash = new VInfo();
579 crash->setSize(360, 250);
580 crash->createBuffer();
581 if (Video::getInstance()->getFormat() == Video::PAL)
582 crash->setPosition(190, 146);
584 crash->setPosition(180, 96);
585 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.");
586 crash->setBorderOn(1);
587 crash->setTitleBarColour(DrawStyle::DANGER);
589 crash->setExitable();
591 boxstack->add(crash);
592 boxstack->update(crash);
595 int Command::getLangPref(bool subtitle, const char* langcode)
597 std::vector<struct ASLPref>::iterator itty=langcodes.begin();
598 char templangcode[4];
599 templangcode[0] = langcode[0];
600 templangcode[1] = langcode[1];
601 templangcode[2] = langcode[2];
602 templangcode[3] = '\0';
604 while (itty != langcodes.end())
606 size_t pos = (*itty).langcode.find(templangcode);
607 if (pos != std::string::npos)
609 //vector<struct ASLPref>::iterator itty2=langcodes.begin();
610 for (unsigned int i = 0; i < langcodes.size(); i++)
614 pref = langcodes[i].subtitlepref;
616 pref = langcodes[i].audiopref;
621 if (langcodes[i].subtitlepref==langpos) return i;
625 if (langcodes[i].audiopref==langpos) return i;
633 return langcodes.size(); //neutral
636 void Command::doJustConnected(VConnect* vconnect)
639 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
640 logger->log("Command", Log::INFO, "Entering doJustConnected");
642 Video* video = Video::getInstance();
643 Audio* audio = Audio::getInstance();
644 boxstack->remove(vconnect);
646 VInfo* vi = new VInfo();
647 vi->setSize(400, 200);
649 if (video->getFormat() == Video::PAL)
650 vi->setPosition(170, 200);
652 vi->setPosition(160, 150);
653 vi->setOneLiner(tr("Connected, loading config"));
656 boxstack->update(vi);
658 VDR* vdr = VDR::getInstance();
661 // See if we're supposed to do network logging
662 config = vdr->configLoad("Advanced", "Network logging");
663 if (config && !STRCASECMP(config, "On"))
665 logger->log("Command", Log::INFO, "Turning on network logging");
666 logger->setExternLogger(vdr);
670 logger->unsetExternLogger();
671 logger->log("Command", Log::INFO, "Turned off network logging");
673 if (config) delete[] config;
675 config = vdr->configLoad("Advanced", "Skin Name");
678 const char **skinnames=SkinFactory::getSkinNames();
679 for (int i=0;i<SkinFactory::getNumberofSkins();i++)
681 if (!STRCASECMP(config, skinnames[i]))
683 SkinFactory::InitSkin(i);
689 if (wallpaper && wallpaper_pict)
691 if (DrawStyle::WALLPAPER.alpha)
692 wallpaper_pict->setVisible(true);
694 wallpaper_pict->setVisible(false);
697 boxstack->update(wallpaper);
702 SkinFactory::InitSkin(0);
705 // See if config says to override video format (PAL/NTSC)
706 config = vdr->configLoad("General", "Override Video Format");
709 logger->log("Command", Log::DEBUG, "Override Video Format is present");
711 if ( (!strcmp(config, "PAL") && (video->getFormat() != Video::PAL))
712 || (!strcmp(config, "NTSC") && (video->getFormat() != Video::NTSC))
713 || (!strcmp(config, "PAL_M") && (video->getFormat() != Video::PAL_M))
714 || (!strcmp(config, "NTSC_J") && (video->getFormat() != Video::NTSC_J))
717 // Oh sheesh, need to switch format. Bye bye TV...
719 // Take everything down
720 boxstack->removeAllExceptWallpaper();
721 boxstack->remove(wallpaper);
722 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
724 Osd* osd = Osd::getInstance();
730 inputMan->shutdown(); // need on raspberry shut not do any harm, hopefully
731 inputMan->init(); // FIXME this breaks badly now
733 // Get video and osd back up with the new mode
734 if (!strcmp(config, "PAL"))
736 logger->log("Command", Log::DEBUG, "Switching to PAL");
737 video->init(Video::PAL);
739 else if (!strcmp(config, "NTSC"))
741 logger->log("Command", Log::DEBUG, "Switching to NTSC");
742 video->init(Video::NTSC);
743 } else if (!strcmp(config, "PAL_M"))
745 logger->log("Command", Log::DEBUG, "Switching to PAL_M");
746 video->init(Video::PAL_M);
747 } else if (!strcmp(config, "NTSC_J"))
749 logger->log("Command", Log::DEBUG, "Switching to NTSC_J");
750 video->init(Video::NTSC_J);
755 //we do not init twice
759 // Put the wallpaper back
764 vi->setSize(400, 200);
766 if (video->getFormat() == Video::PAL)
767 vi->setPosition(170, 200);
769 vi->setPosition(160, 150);
771 vi->setOneLiner(tr("Connected, loading config"));
774 boxstack->update(vi);
778 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
783 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
786 // Power off if first boot and config says so
791 logger->log("Command", Log::DEBUG, "Load power after boot");
793 config = vdr->configLoad("General", "Power After Boot");
797 if (!STRCASECMP(config, "On"))
799 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
801 else if (!STRCASECMP(config, "Off"))
803 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
808 else if (!STRCASECMP(config, "Last state"))
810 char* lastPowerState = vdr->configLoad("General", "Last Power State");
813 if (!STRCASECMP(lastPowerState, "On"))
815 logger->log("Command", Log::INFO, "Config says Last Power State = On");
817 else if (!STRCASECMP(lastPowerState, "Off"))
819 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
826 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
831 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
836 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
842 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
847 // Go S-Video if config says so
849 config = vdr->configLoad("TV", "Connection");
853 if (!STRCASECMP(config, "S-Video"))
855 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
856 video->setConnection(Video::SVIDEO);
857 } else if (!STRCASECMP(config, "HDMI"))
859 logger->log("Command", Log::INFO, "Switching to HDMI as Connection=%s", config);
860 video->setConnection(Video::HDMI);
861 } else if (!STRCASECMP(config, "HDMI3D"))
863 logger->log("Command", Log::INFO, "Switching to HDMI3D as Connection=%s", config);
864 video->setConnection(Video::HDMI3D);
868 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
869 video->setConnection(Video::COMPOSITERGB);
875 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
878 // Set to shutdown VDR if config says
880 config = vdr->configLoad("General", "VDR shutdown");
883 if (!STRCASECMP(config, "On"))
885 logger->log("Command", Log::INFO, "Shutdown VDR when shutting down vomp");
886 vdr->setVDRShutdown(true);
888 else if (!STRCASECMP(config, "Off"))
890 logger->log("Command", Log::INFO, "Shutdown only vomp");
891 vdr->setVDRShutdown(false);
896 logger->log("Command", Log::INFO, "Default shutdown only vomp");
897 vdr->setVDRShutdown(false); // Default
900 // Get TV aspect ratio
902 config = vdr->configLoad("TV", "Aspect");
905 if (!STRCASECMP(config, "16:9"))
907 logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
908 video->setTVsize(Video::ASPECT16X9);
912 logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
913 video->setTVsize(Video::ASPECT4X3);
919 logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
920 video->setTVsize(Video::ASPECT4X3);
923 config = vdr->configLoad("TV", "Widemode");
926 if (!STRCASECMP(config, "Letterbox"))
928 logger->log("Command", Log::INFO, "Setting letterbox mode");
929 video->setMode(Video::LETTERBOX);
933 logger->log("Command", Log::INFO, "Setting chop-sides mode");
934 video->setMode(Video::NORMAL);
941 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting letterbox mode");
942 video->setMode(Video::LETTERBOX);
944 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
945 video->setMode(Video::NORMAL);
949 config = vdr->configLoad("Advanced", "TCP receive window");
952 size_t newTCPsize = atoi(config);
955 logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
956 vdr->setReceiveWindow(newTCPsize);
960 logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
961 if (DEFAULT_TCP_WINDOWSIZE) vdr->setReceiveWindow(2048); // Default
964 config = vdr->configLoad("Advanced", "Font Name");
967 Osd::getInstance()->setFont(config);
968 logger->log("Command", Log::INFO, "Setting Font to %s", config);
974 // Set recording list type
976 #ifdef ADVANCED_MENUES
977 config = vdr->configLoad("Advanced", "Menu type");
981 if (!STRCASECMP(config, "Advanced"))
983 logger->log("Command", Log::INFO, "Switching to Advanced menu");
988 logger->log("Command", Log::INFO, "Switching to Classic menu");
995 logger->log("Command", Log::INFO, "Config General/menu type not found");
1000 config = vdr->configLoad("Advanced", "Disable WOL");
1003 if (!STRCASECMP(config, "Yes"))
1005 logger->log("Command", Log::INFO, "Config says disable WOL");
1006 Wol::getInstance()->setEnabled(false);
1010 logger->log("Command", Log::INFO, "Config says enable WOL");
1011 Wol::getInstance()->setEnabled(true);
1018 logger->log("Command", Log::INFO, "By default, enable WOL");
1019 Wol::getInstance()->setEnabled(true);
1021 /* device dependend config */
1022 audio->loadOptionsfromServer(vdr);
1023 video->loadOptionsfromServer(vdr);
1024 inputMan->loadOptionsfromServer(vdr);
1026 video->executePendingModeChanges();
1029 // Save power state = on
1031 vdr->configSave("General", "Last Power State", "On");
1033 // Make sure connection didn't die
1034 if (!vdr->isConnected())
1036 Command::getInstance()->connectionLost();
1040 boxstack->remove(vi);
1042 VWelcome* vw = new VWelcome();
1045 boxstack->update(vw);
1047 // Enter pre-keys here
1048 // handleCommand(Input::OK);
1049 // handleCommand(Input::THREE);
1050 // handleCommand(Input::SIX);
1051 // handleCommand(Input::OK);
1052 // handleCommand(Input::UP);
1053 // handleCommand(Input::PLAY);
1054 // handleCommand(Input::DOWN);
1055 // handleCommand(Input::DOWN);
1056 // handleCommand(Input::DOWN);
1057 // handleCommand(Input::OK);
1058 // handleCommand(Input::RED);