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();
469 stop(); //different behavoiur on windows, we exit // FIXME - stop() now called directly from winmain
474 void Command::doFromTheTop(bool which)
476 if (isStandby) return;
481 logger->log("Command", Log::NOTICE, "Connection lost dialog already present");
485 logger->log("Command", Log::NOTICE, "Doing connection lost dialog");
486 connLost = new VInfo();
487 connLost->setSize(360, 200);
488 connLost->createBuffer();
489 if (Video::getInstance()->getFormat() == Video::PAL)
490 connLost->setPosition(190, 170);
492 connLost->setPosition(180, 120);
493 connLost->setOneLiner(tr("Connection lost"));
494 connLost->setDropThrough();
495 connLost->setBorderOn(1);
496 connLost->setTitleBarColour(DrawStyle::DANGER);
497 connLost->okButton();
499 boxstack->add(connLost);
500 boxstack->update(connLost);
502 clearMQInputEvents();
506 logger->unsetExternLogger();
507 VDR::getInstance()->disconnect();
508 boxstack->removeAllExceptWallpaper();
509 boxstack->update(wallpaper);
514 // at this point, everything should be reset to first-go
516 VConnect* vconnect = new VConnect(server);
517 boxstack->add(vconnect);
522 void Command::clearMQInputEvents()
524 // FIXME implement this
527 void Command::doReboot()
530 logger->unsetExternLogger();
531 VDR::getInstance()->disconnect();
533 logger->log("Command", Log::NOTICE, "Reboot");
535 #ifndef VOMP_HAS_EXIT
536 // some plattforms, want a proper deinitialisation of their hardware before reboot
537 Osd::getInstance()->shutdown();
538 Audio::getInstance()->shutdown();
539 Video::getInstance()->shutdown();
540 InputMan::getInstance()->shutdown();
542 reboot(LINUX_REBOOT_CMD_RESTART);
543 // if reboot is not allowed -> stop
555 #endif //Would we support this on windows?
558 void Command::connectionLost()
560 logger->unsetExternLogger();
561 Message* m = new Message(); // break into master mutex
562 m->message = Message::CONNECTION_LOST;
567 void Command::buildCrashedBox()
569 VInfo* crash = new VInfo();
570 crash->setSize(360, 250);
571 crash->createBuffer();
572 if (Video::getInstance()->getFormat() == Video::PAL)
573 crash->setPosition(190, 146);
575 crash->setPosition(180, 96);
576 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.");
577 crash->setBorderOn(1);
578 crash->setTitleBarColour(DrawStyle::DANGER);
580 crash->setExitable();
582 boxstack->add(crash);
583 boxstack->update(crash);
586 int Command::getLangPref(bool subtitle, const char* langcode)
588 std::vector<struct ASLPref>::iterator itty=langcodes.begin();
589 char templangcode[4];
590 templangcode[0] = langcode[0];
591 templangcode[1] = langcode[1];
592 templangcode[2] = langcode[2];
593 templangcode[3] = '\0';
595 while (itty != langcodes.end())
597 size_t pos = (*itty).langcode.find(templangcode);
598 if (pos != std::string::npos)
600 //vector<struct ASLPref>::iterator itty2=langcodes.begin();
601 for (unsigned int i = 0; i < langcodes.size(); i++)
605 pref = langcodes[i].subtitlepref;
607 pref = langcodes[i].audiopref;
612 if (langcodes[i].subtitlepref==langpos) return i;
616 if (langcodes[i].audiopref==langpos) return i;
624 return langcodes.size(); //neutral
627 void Command::doJustConnected(VConnect* vconnect)
630 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
631 logger->log("Command", Log::INFO, "Entering doJustConnected");
633 Video* video = Video::getInstance();
634 Audio* audio = Audio::getInstance();
635 boxstack->remove(vconnect);
637 VInfo* vi = new VInfo();
638 vi->setSize(400, 200);
640 if (video->getFormat() == Video::PAL)
641 vi->setPosition(170, 200);
643 vi->setPosition(160, 150);
644 vi->setOneLiner(tr("Connected, loading config"));
647 boxstack->update(vi);
649 VDR* vdr = VDR::getInstance();
652 // See if we're supposed to do network logging
653 config = vdr->configLoad("Advanced", "Network logging");
654 if (config && !STRCASECMP(config, "On"))
656 logger->log("Command", Log::INFO, "Turning on network logging");
657 logger->setExternLogger(vdr);
661 logger->unsetExternLogger();
662 logger->log("Command", Log::INFO, "Turned off network logging");
664 if (config) delete[] config;
666 config = vdr->configLoad("Advanced", "Skin Name");
669 const char **skinnames=SkinFactory::getSkinNames();
670 for (int i=0;i<SkinFactory::getNumberofSkins();i++)
672 if (!STRCASECMP(config, skinnames[i]))
674 SkinFactory::InitSkin(i);
680 if (wallpaper && wallpaper_pict)
682 if (DrawStyle::WALLPAPER.alpha)
683 wallpaper_pict->setVisible(true);
685 wallpaper_pict->setVisible(false);
688 boxstack->update(wallpaper);
693 SkinFactory::InitSkin(0);
696 // See if config says to override video format (PAL/NTSC)
697 config = vdr->configLoad("General", "Override Video Format");
700 logger->log("Command", Log::DEBUG, "Override Video Format is present");
702 if ( (!strcmp(config, "PAL") && (video->getFormat() != Video::PAL))
703 || (!strcmp(config, "NTSC") && (video->getFormat() != Video::NTSC))
704 || (!strcmp(config, "PAL_M") && (video->getFormat() != Video::PAL_M))
705 || (!strcmp(config, "NTSC_J") && (video->getFormat() != Video::NTSC_J))
708 // Oh sheesh, need to switch format. Bye bye TV...
710 // Take everything down
711 boxstack->removeAllExceptWallpaper();
712 boxstack->remove(wallpaper);
713 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
715 Osd* osd = Osd::getInstance();
721 inputMan->shutdown(); // need on raspberry shut not do any harm, hopefully
722 inputMan->init(); // FIXME this breaks badly now
724 // Get video and osd back up with the new mode
725 if (!strcmp(config, "PAL"))
727 logger->log("Command", Log::DEBUG, "Switching to PAL");
728 video->init(Video::PAL);
730 else if (!strcmp(config, "NTSC"))
732 logger->log("Command", Log::DEBUG, "Switching to NTSC");
733 video->init(Video::NTSC);
734 } else if (!strcmp(config, "PAL_M"))
736 logger->log("Command", Log::DEBUG, "Switching to PAL_M");
737 video->init(Video::PAL_M);
738 } else if (!strcmp(config, "NTSC_J"))
740 logger->log("Command", Log::DEBUG, "Switching to NTSC_J");
741 video->init(Video::NTSC_J);
746 //we do not init twice
750 // Put the wallpaper back
755 vi->setSize(400, 200);
757 if (video->getFormat() == Video::PAL)
758 vi->setPosition(170, 200);
760 vi->setPosition(160, 150);
762 vi->setOneLiner(tr("Connected, loading config"));
765 boxstack->update(vi);
769 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
774 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
777 // Power off if first boot and config says so
782 logger->log("Command", Log::DEBUG, "Load power after boot");
784 config = vdr->configLoad("General", "Power After Boot");
788 if (!STRCASECMP(config, "On"))
790 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
792 else if (!STRCASECMP(config, "Off"))
794 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
799 else if (!STRCASECMP(config, "Last state"))
801 char* lastPowerState = vdr->configLoad("General", "Last Power State");
804 if (!STRCASECMP(lastPowerState, "On"))
806 logger->log("Command", Log::INFO, "Config says Last Power State = On");
808 else if (!STRCASECMP(lastPowerState, "Off"))
810 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
817 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
822 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
827 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
833 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
838 // Go S-Video if config says so
840 config = vdr->configLoad("TV", "Connection");
844 if (!STRCASECMP(config, "S-Video"))
846 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
847 video->setConnection(Video::SVIDEO);
848 } else if (!STRCASECMP(config, "HDMI"))
850 logger->log("Command", Log::INFO, "Switching to HDMI as Connection=%s", config);
851 video->setConnection(Video::HDMI);
852 } else if (!STRCASECMP(config, "HDMI3D"))
854 logger->log("Command", Log::INFO, "Switching to HDMI3D as Connection=%s", config);
855 video->setConnection(Video::HDMI3D);
859 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
860 video->setConnection(Video::COMPOSITERGB);
866 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
869 // Set to shutdown VDR if config says
871 config = vdr->configLoad("General", "VDR shutdown");
874 if (!STRCASECMP(config, "On"))
876 logger->log("Command", Log::INFO, "Shutdown VDR when shutting down vomp");
877 vdr->setVDRShutdown(true);
879 else if (!STRCASECMP(config, "Off"))
881 logger->log("Command", Log::INFO, "Shutdown only vomp");
882 vdr->setVDRShutdown(false);
887 logger->log("Command", Log::INFO, "Default shutdown only vomp");
888 vdr->setVDRShutdown(false); // Default
891 // Get TV aspect ratio
893 config = vdr->configLoad("TV", "Aspect");
896 if (!STRCASECMP(config, "16:9"))
898 logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
899 video->setTVsize(Video::ASPECT16X9);
903 logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
904 video->setTVsize(Video::ASPECT4X3);
910 logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
911 video->setTVsize(Video::ASPECT4X3);
914 config = vdr->configLoad("TV", "Widemode");
917 if (!STRCASECMP(config, "Letterbox"))
919 logger->log("Command", Log::INFO, "Setting letterbox mode");
920 video->setMode(Video::LETTERBOX);
924 logger->log("Command", Log::INFO, "Setting chop-sides mode");
925 video->setMode(Video::NORMAL);
932 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting letterbox mode");
933 video->setMode(Video::LETTERBOX);
935 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
936 video->setMode(Video::NORMAL);
940 config = vdr->configLoad("Advanced", "TCP receive window");
943 size_t newTCPsize = atoi(config);
946 logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
947 vdr->setReceiveWindow(newTCPsize);
951 logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
952 if (DEFAULT_TCP_WINDOWSIZE) vdr->setReceiveWindow(2048); // Default
955 config = vdr->configLoad("Advanced", "Font Name");
958 Osd::getInstance()->setFont(config);
959 logger->log("Command", Log::INFO, "Setting Font to %s", config);
965 // Set recording list type
967 #ifdef ADVANCED_MENUES
968 config = vdr->configLoad("Advanced", "Menu type");
972 if (!STRCASECMP(config, "Advanced"))
974 logger->log("Command", Log::INFO, "Switching to Advanced menu");
979 logger->log("Command", Log::INFO, "Switching to Classic menu");
986 logger->log("Command", Log::INFO, "Config General/menu type not found");
991 config = vdr->configLoad("Advanced", "Disable WOL");
994 if (!STRCASECMP(config, "Yes"))
996 logger->log("Command", Log::INFO, "Config says disable WOL");
997 Wol::getInstance()->setEnabled(false);
1001 logger->log("Command", Log::INFO, "Config says enable WOL");
1002 Wol::getInstance()->setEnabled(true);
1009 logger->log("Command", Log::INFO, "By default, enable WOL");
1010 Wol::getInstance()->setEnabled(true);
1012 /* device dependend config */
1013 audio->loadOptionsFromServer(vdr);
1014 video->loadOptionsFromServer(vdr);
1015 inputMan->loadOptionsFromServer(vdr);
1017 video->executePendingModeChanges();
1020 // Save power state = on
1022 vdr->configSave("General", "Last Power State", "On");
1024 // Make sure connection didn't die
1025 if (!vdr->isConnected())
1027 Command::getInstance()->connectionLost();
1031 boxstack->remove(vi);
1033 VWelcome* vw = new VWelcome();
1036 boxstack->update(vw);
1038 // Enter pre-keys here
1039 // handleCommand(Input::OK);
1040 // handleCommand(Input::THREE);
1041 // handleCommand(Input::SIX);
1042 // handleCommand(Input::OK);
1043 // handleCommand(Input::UP);
1044 // handleCommand(Input::PLAY);
1045 // handleCommand(Input::DOWN);
1046 // handleCommand(Input::DOWN);
1047 // handleCommand(Input::DOWN);
1048 // handleCommand(Input::RIGHT);
1049 // handleCommand(Input::RED);