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, char* tServer)
80 if (initted) return 0;
85 logger = Log::getInstance();
86 boxstack = BoxStack::getInstance();
87 inputMan = InputMan::getInstance();
89 if (!logger || !boxstack || !inputMan)
95 SkinFactory::InitSkin(0);
100 int Command::shutdown()
102 if (!initted) return 0;
109 logger->log("Command", Log::NOTICE, "Request stop");
111 Message* m = new Message(); // break master loop
112 m->message = Message::SHUTDOWN;
117 void Command::doWallpaper()
119 Video* video = Video::getInstance();
122 Boxx* bbg = new Boxx();
123 bbg->setSize(video->getScreenWidth(), video->getScreenHeight());
125 bbg->fillColour(DrawStyle::WALLPAPER);
127 boxstack->update(bbg);
128 boxstack->remove(bbg);
131 wallpaper = new Boxx();
132 wallpaper->setSize(video->getScreenWidth(), video->getScreenHeight());
133 wallpaper->createBuffer();
134 wallpaper->setBackgroundColour(DrawStyle::WALLPAPER);
136 wallpaper_pict = new WJpegTYPE();
137 wallpaper_pict->setSize(video->getScreenWidth(), video->getScreenHeight());
139 if (video->getFormat() == Video::PAL)
141 logger->log("Command", Log::DEBUG, "PAL wallpaper selected");
143 wallpaper_pict->init("/wallpaperPAL.jpg");
145 wallpaper_pict->init("wallpaperPAL.jpg");
150 logger->log("Command", Log::DEBUG, "NTSC wallpaper selected");
151 wallpaper_pict->init("/wallpaperNTSC.jpg");
154 if (DrawStyle::WALLPAPER.alpha)
155 wallpaper_pict->setVisible(true);
157 wallpaper_pict->setVisible(false);
159 wallpaper->add(wallpaper_pict);
162 boxstack->add(wallpaper);
163 boxstack->update(wallpaper);
165 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
166 if (osdv) osdv->updateBackgroundColor(DrawStyle::WALLPAPER);
171 if (!initted) return;
175 Video::getInstance()->signalOn();
176 Led::getInstance()->on();
186 VConnect* vconnect = new VConnect(server);
187 boxstack->add(vconnect);
192 // FIXME Input::NA_SIGNAL is possibly obsolete now
194 std::unique_lock<std::mutex> lockWrapper(messageQueueMutex); // locks. unlocks on out-of-scope
200 messageQueueCond.wait(lockWrapper, [&] { return !irun || !messages.empty(); });
201 logger->log("Command", Log::DEBUG, "woke");
205 while(!messages.empty())
207 Message* m = messages.front();
208 messages.pop_front();
210 lockWrapper.unlock();
221 boxstack->removeAllExceptWallpaper();
222 boxstack->remove(wallpaper);
223 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
226 void Command::processMessage(Message* m)
228 // FIXME - a slight modification - how if messagereceivers were to register
229 // themselves as receivers to avoid the calling-a-deleted-object problem
230 // then only deliver/register/unregister would have to be protected
232 logger->log("Command", Log::DEBUG, "processing message %i", m->message);
239 case Message::SHUTDOWN:
245 case Message::STOP_PLAYBACK:
247 handleCommand(Input::STOP); // an odd way of doing it, but so simple
250 // Also connection_lost comes from player - anywhere else?
254 case Message::VDR_CONNECTED:
256 doJustConnected(static_cast<VConnect*>(m->from));
259 case Message::SCREENSHOT:
261 logger->log("Osd", Log::NOTICE, "Screenshot Message arrived");
262 Osd::getInstance()->screenShot("out.jpg");
265 case Message::CONNECTION_LOST:
270 case Message::INPUT_EVENT:
272 logger->log("Command", Log::NOTICE, "INPUT_EVENT %i", m->parameter);
274 handleCommand(m->parameter);
277 case Message::CHANGE_LANGUAGE:
279 boxstack->removeAllExceptWallpaper();
280 boxstack->update(wallpaper);
282 if (!VDR::getInstance()->isConnected()) { connectionLost(); break; }
283 VWelcome* vw = new VWelcome();
286 boxstack->update(vw);
289 case Message::LAST_VIEW_CLOSE:
291 // Shouldn't be done like this. Some generic message pass back from vinfo perhaps
298 // VWelcome* vw = new VWelcome();
300 // boxstack->add(vw);
301 // boxstack->update(vw);
305 case Message::NEW_PICTURE:
307 //Log::getInstance()->log("Command", Log::DEBUG, "TVMedia NEW_PICTURE");
308 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
309 if (osdv) osdv->informPicture(m->tag, reinterpret_cast<ImageIndex>(m->data));
312 case Message::NEW_PICTURE_STATIC:
314 //Log::getInstance()->log("Command", Log::DEBUG, "TVMedia NEW_PICTURE %x %x",m->tag,m->parameter);
315 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
316 if (osdv) osdv->informPicture(static_cast<unsigned long long>(m->tag) << 32LL, reinterpret_cast<ImageIndex>(m->data));
325 Instead of sending through the boxstack, implement a more generic MessageReceiver interface
326 and have potential receivers register with something
327 When a message needs to be delivered, check if the receiver is still registered, if so, deliver the message
328 This could all be done using the existing big command mutex to keep it simple
331 logger->log("Command", Log::DEBUG, "Sending message to boxstack");
332 boxstack->processMessage(m);
336 void Command::handleCommand(int button)
338 if (isStandby && (button != Input::POWER)
339 && (button != Input::POWERON)
340 && (button != Input::POWEROFF)) return;
342 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
344 // command was not handled
348 case Input::VOLUMEUP:
349 case Input::VOLUMEDOWN:
351 if (inputMan->handlesVolume()) // CEC volume handler?
353 if (button == Input::VOLUMEDOWN)
354 inputMan->volumeDown();
356 inputMan->volumeUp();
360 VVolume* v = new VVolume();
362 v->handleCommand(button); // this will draw+show
368 if (inputMan->handlesVolume())
370 inputMan->volumeMute();
374 VMute* v = new VMute();
391 case Input::POWEROFF:
399 if (!connLost) return; // if connLost, handle Input::OK
405 VSleeptimer* sleep = new VSleeptimer();
406 boxstack->add(sleep);
407 sleep->handleCommand(button); // this will draw+show
416 Message* m = new Message(); // break into master mutex
417 m->message = Message::SCREENSHOT;
423 void Command::doStandby()
436 void Command::doPowerOn()
440 Video::getInstance()->signalOn();
441 Led::getInstance()->on();
442 InputMan::getInstance()->changePowerState(true);
445 VConnect* vconnect = new VConnect(server);
446 boxstack->add(vconnect);
451 void Command::doPowerOff()
455 VDR::getInstance()->shutdownVDR();
456 boxstack->removeAllExceptWallpaper();
457 Video::getInstance()->signalOff();
458 boxstack->update(wallpaper);
460 VDR::getInstance()->configSave("General", "Last Power State", "Off");
461 logger->unsetExternLogger();
462 VDR::getInstance()->disconnect();
463 Led::getInstance()->off();
464 InputMan::getInstance()->changePowerState(false);
466 Sleeptimer::getInstance()->shutdown();
470 void Command::doFromTheTop(bool which)
472 if (isStandby) return;
477 logger->log("Command", Log::NOTICE, "Connection lost dialog already present");
481 logger->log("Command", Log::NOTICE, "Doing connection lost dialog");
482 connLost = new VInfo();
483 connLost->setSize(360, 200);
484 connLost->createBuffer();
485 if (Video::getInstance()->getFormat() == Video::PAL)
486 connLost->setPosition(190, 170);
488 connLost->setPosition(180, 120);
489 connLost->setOneLiner(tr("Connection lost"));
490 connLost->setDropThrough();
491 connLost->setBorderOn(1);
492 connLost->setTitleBarColour(DrawStyle::DANGER);
493 connLost->okButton();
495 boxstack->add(connLost);
496 boxstack->update(connLost);
498 clearMQInputEvents();
502 logger->unsetExternLogger();
503 VDR::getInstance()->disconnect();
504 boxstack->removeAllExceptWallpaper();
505 boxstack->update(wallpaper);
510 // at this point, everything should be reset to first-go
512 VConnect* vconnect = new VConnect(server);
513 boxstack->add(vconnect);
518 void Command::clearMQInputEvents()
520 std::lock_guard<std::mutex> lg(messageQueueMutex); // Get the lock
522 MQueueI i = messages.begin();
523 while(i != messages.end())
526 if (m->message == Message::INPUT_EVENT)
529 i = messages.erase(i);
538 void Command::doReboot()
541 logger->unsetExternLogger();
542 VDR::getInstance()->disconnect();
544 logger->log("Command", Log::NOTICE, "Reboot");
546 #ifndef VOMP_HAS_EXIT
547 // some plattforms, want a proper deinitialisation of their hardware before reboot
548 Osd::getInstance()->shutdown();
549 Audio::getInstance()->shutdown();
550 Video::getInstance()->shutdown();
551 InputMan::getInstance()->shutdown();
553 reboot(LINUX_REBOOT_CMD_RESTART);
554 // if reboot is not allowed -> stop
566 #endif //Would we support this on windows?
569 void Command::connectionLost()
571 logger->unsetExternLogger();
572 Message* m = new Message(); // break into master mutex
573 m->message = Message::CONNECTION_LOST;
578 void Command::buildCrashedBox()
580 VInfo* crash = new VInfo();
581 crash->setSize(360, 250);
582 crash->createBuffer();
583 if (Video::getInstance()->getFormat() == Video::PAL)
584 crash->setPosition(190, 146);
586 crash->setPosition(180, 96);
587 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.");
588 crash->setBorderOn(1);
589 crash->setTitleBarColour(DrawStyle::DANGER);
591 crash->setExitable();
593 boxstack->add(crash);
594 boxstack->update(crash);
597 int Command::getLangPref(bool subtitle, const char* langcode)
599 std::vector<struct ASLPref>::iterator itty=langcodes.begin();
600 char templangcode[4];
601 templangcode[0] = langcode[0];
602 templangcode[1] = langcode[1];
603 templangcode[2] = langcode[2];
604 templangcode[3] = '\0';
606 while (itty != langcodes.end())
608 size_t pos = (*itty).langcode.find(templangcode);
609 if (pos != std::string::npos)
611 //vector<struct ASLPref>::iterator itty2=langcodes.begin();
612 for (unsigned int i = 0; i < langcodes.size(); i++)
616 pref = langcodes[i].subtitlepref;
618 pref = langcodes[i].audiopref;
623 if (langcodes[i].subtitlepref==langpos) return i;
627 if (langcodes[i].audiopref==langpos) return i;
635 return langcodes.size(); //neutral
638 void Command::doJustConnected(VConnect* vconnect)
641 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
642 logger->log("Command", Log::INFO, "Entering doJustConnected");
644 Video* video = Video::getInstance();
645 Audio* audio = Audio::getInstance();
646 boxstack->remove(vconnect);
648 VInfo* vi = new VInfo();
649 vi->setSize(400, 200);
651 if (video->getFormat() == Video::PAL)
652 vi->setPosition(170, 200);
654 vi->setPosition(160, 150);
655 vi->setOneLiner(tr("Connected, loading config"));
658 boxstack->update(vi);
660 VDR* vdr = VDR::getInstance();
663 // See if we're supposed to do network logging
664 config = vdr->configLoad("Advanced", "Network logging");
665 if (config && !STRCASECMP(config, "On"))
667 logger->log("Command", Log::INFO, "Turning on network logging");
668 logger->setExternLogger(vdr);
672 logger->unsetExternLogger();
673 logger->log("Command", Log::INFO, "Turned off network logging");
675 if (config) delete[] config;
677 config = vdr->configLoad("Advanced", "Skin Name");
680 const char **skinnames=SkinFactory::getSkinNames();
681 for (int i=0;i<SkinFactory::getNumberofSkins();i++)
683 if (!STRCASECMP(config, skinnames[i]))
685 SkinFactory::InitSkin(i);
691 if (wallpaper && wallpaper_pict)
693 if (DrawStyle::WALLPAPER.alpha)
694 wallpaper_pict->setVisible(true);
696 wallpaper_pict->setVisible(false);
699 boxstack->update(wallpaper);
704 SkinFactory::InitSkin(0);
707 // See if config says to override video format (PAL/NTSC)
708 config = vdr->configLoad("General", "Override Video Format");
711 logger->log("Command", Log::DEBUG, "Override Video Format is present");
713 if ( (!strcmp(config, "PAL") && (video->getFormat() != Video::PAL))
714 || (!strcmp(config, "NTSC") && (video->getFormat() != Video::NTSC))
715 || (!strcmp(config, "PAL_M") && (video->getFormat() != Video::PAL_M))
716 || (!strcmp(config, "NTSC_J") && (video->getFormat() != Video::NTSC_J))
719 // Oh sheesh, need to switch format. Bye bye TV...
721 // Take everything down
722 boxstack->removeAllExceptWallpaper();
723 boxstack->remove(wallpaper);
724 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
726 Osd* osd = Osd::getInstance();
732 inputMan->shutdown(); // need on raspberry shut not do any harm, hopefully
733 inputMan->init(); // FIXME this breaks badly now
735 // Get video and osd back up with the new mode
736 if (!strcmp(config, "PAL"))
738 logger->log("Command", Log::DEBUG, "Switching to PAL");
739 video->init(Video::PAL);
741 else if (!strcmp(config, "NTSC"))
743 logger->log("Command", Log::DEBUG, "Switching to NTSC");
744 video->init(Video::NTSC);
745 } else if (!strcmp(config, "PAL_M"))
747 logger->log("Command", Log::DEBUG, "Switching to PAL_M");
748 video->init(Video::PAL_M);
749 } else if (!strcmp(config, "NTSC_J"))
751 logger->log("Command", Log::DEBUG, "Switching to NTSC_J");
752 video->init(Video::NTSC_J);
757 //we do not init twice
761 // Put the wallpaper back
766 vi->setSize(400, 200);
768 if (video->getFormat() == Video::PAL)
769 vi->setPosition(170, 200);
771 vi->setPosition(160, 150);
773 vi->setOneLiner(tr("Connected, loading config"));
776 boxstack->update(vi);
780 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
785 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
788 // Power off if first boot and config says so
793 logger->log("Command", Log::DEBUG, "Load power after boot");
795 config = vdr->configLoad("General", "Power After Boot");
799 if (!STRCASECMP(config, "On"))
801 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
803 else if (!STRCASECMP(config, "Off"))
805 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
810 else if (!STRCASECMP(config, "Last state"))
812 char* lastPowerState = vdr->configLoad("General", "Last Power State");
815 if (!STRCASECMP(lastPowerState, "On"))
817 logger->log("Command", Log::INFO, "Config says Last Power State = On");
819 else if (!STRCASECMP(lastPowerState, "Off"))
821 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
828 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
833 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
838 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
844 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
849 // Go S-Video if config says so
851 config = vdr->configLoad("TV", "Connection");
855 if (!STRCASECMP(config, "S-Video"))
857 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
858 video->setConnection(Video::SVIDEO);
859 } else if (!STRCASECMP(config, "HDMI"))
861 logger->log("Command", Log::INFO, "Switching to HDMI as Connection=%s", config);
862 video->setConnection(Video::HDMI);
863 } else if (!STRCASECMP(config, "HDMI3D"))
865 logger->log("Command", Log::INFO, "Switching to HDMI3D as Connection=%s", config);
866 video->setConnection(Video::HDMI3D);
870 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
871 video->setConnection(Video::COMPOSITERGB);
877 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
880 // Set to shutdown VDR if config says
882 config = vdr->configLoad("General", "VDR shutdown");
885 if (!STRCASECMP(config, "On"))
887 logger->log("Command", Log::INFO, "Shutdown VDR when shutting down vomp");
888 vdr->setVDRShutdown(true);
890 else if (!STRCASECMP(config, "Off"))
892 logger->log("Command", Log::INFO, "Shutdown only vomp");
893 vdr->setVDRShutdown(false);
898 logger->log("Command", Log::INFO, "Default shutdown only vomp");
899 vdr->setVDRShutdown(false); // Default
902 // Get TV aspect ratio
904 config = vdr->configLoad("TV", "Aspect");
907 if (!STRCASECMP(config, "16:9"))
909 logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
910 video->setTVsize(Video::ASPECT16X9);
914 logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
915 video->setTVsize(Video::ASPECT4X3);
921 logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
922 video->setTVsize(Video::ASPECT4X3);
925 config = vdr->configLoad("TV", "Widemode");
928 if (!STRCASECMP(config, "Letterbox"))
930 logger->log("Command", Log::INFO, "Setting letterbox mode");
931 video->setMode(Video::LETTERBOX);
935 logger->log("Command", Log::INFO, "Setting chop-sides mode");
936 video->setMode(Video::NORMAL);
943 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting letterbox mode");
944 video->setMode(Video::LETTERBOX);
946 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
947 video->setMode(Video::NORMAL);
951 config = vdr->configLoad("Advanced", "TCP receive window");
954 size_t newTCPsize = atoi(config);
957 logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
958 vdr->setReceiveWindow(newTCPsize);
962 logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
963 if (DEFAULT_TCP_WINDOWSIZE) vdr->setReceiveWindow(2048); // Default
966 config = vdr->configLoad("Advanced", "Font Name");
969 Osd::getInstance()->setFont(config);
970 logger->log("Command", Log::INFO, "Setting Font to %s", config);
976 // Set recording list type
978 #ifdef ADVANCED_MENUES
979 config = vdr->configLoad("Advanced", "Menu type");
983 if (!STRCASECMP(config, "Advanced"))
985 logger->log("Command", Log::INFO, "Switching to Advanced menu");
990 logger->log("Command", Log::INFO, "Switching to Classic menu");
997 logger->log("Command", Log::INFO, "Config General/menu type not found");
1002 config = vdr->configLoad("Advanced", "Disable WOL");
1005 if (!STRCASECMP(config, "Yes"))
1007 logger->log("Command", Log::INFO, "Config says disable WOL");
1008 Wol::getInstance()->setEnabled(false);
1012 logger->log("Command", Log::INFO, "Config says enable WOL");
1013 Wol::getInstance()->setEnabled(true);
1020 logger->log("Command", Log::INFO, "By default, enable WOL");
1021 Wol::getInstance()->setEnabled(true);
1023 /* device dependend config */
1024 audio->loadOptionsFromServer(vdr);
1025 video->loadOptionsFromServer(vdr);
1026 inputMan->loadOptionsFromServer(vdr);
1028 video->executePendingModeChanges();
1031 // Save power state = on
1033 vdr->configSave("General", "Last Power State", "On");
1035 // Make sure connection didn't die
1036 if (!vdr->isConnected())
1038 Command::getInstance()->connectionLost();
1042 boxstack->remove(vi);
1044 VWelcome* vw = new VWelcome();
1047 boxstack->update(vw);
1049 // Enter pre-keys here
1050 // handleCommand(Input::OK);
1051 // handleCommand(Input::THREE);
1052 // handleCommand(Input::SIX);
1053 // handleCommand(Input::OK);
1054 // handleCommand(Input::UP);
1055 // handleCommand(Input::PLAY);
1056 // handleCommand(Input::DOWN);
1057 // handleCommand(Input::DOWN);
1058 // handleCommand(Input::DOWN);
1059 // handleCommand(Input::RIGHT);
1060 // handleCommand(Input::RED);