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/>.
23 #include "remotewin.h"
27 #include "remoteandroid.h"
35 #include "vserverselect.h"
41 #include "timerreceiver.h"
51 #include "vsleeptimer.h"
53 #include "osdvector.h"
56 Command* Command::instance = NULL;
69 Command* Command::getInstance()
74 int Command::init(bool tcrashed, char* tServer)
76 if (initted) return 0;
81 logger = Log::getInstance();
82 boxstack = BoxStack::getInstance();
83 remote = Input::getInstance();
85 remote->InitHWCListwithDefaults();
87 if (!logger || !boxstack || !remote)
93 SkinFactory::InitSkin(0);
98 int Command::shutdown()
100 if (!initted) return 0;
107 logger->log("Command", Log::NOTICE, "Request stop");
109 Message* m = new Message(); // break master loop
110 m->message = Message::SHUTDOWN;
115 void Command::doWallpaper()
117 Video* video = Video::getInstance();
120 Boxx* bbg = new Boxx();
121 bbg->setSize(video->getScreenWidth(), video->getScreenHeight());
123 bbg->fillColour(DrawStyle::WALLPAPER);
125 boxstack->update(bbg);
126 boxstack->remove(bbg);
129 wallpaper = new Boxx();
130 wallpaper->setSize(video->getScreenWidth(), video->getScreenHeight());
131 wallpaper->createBuffer();
132 wallpaper->setBackgroundColour(DrawStyle::WALLPAPER);
134 wallpaper_pict = new WJpegTYPE();
135 wallpaper_pict->setSize(video->getScreenWidth(), video->getScreenHeight());
137 if (video->getFormat() == Video::PAL)
139 logger->log("Command", Log::DEBUG, "PAL wallpaper selected");
141 wallpaper_pict->init("/wallpaperPAL.jpg");
143 wallpaper_pict->init("wallpaperPAL.jpg");
148 logger->log("Command", Log::DEBUG, "NTSC wallpaper selected");
149 wallpaper_pict->init("/wallpaperNTSC.jpg");
152 if (DrawStyle::WALLPAPER.alpha)
153 wallpaper_pict->setVisible(true);
155 wallpaper_pict->setVisible(false);
157 wallpaper->add(wallpaper_pict);
160 boxstack->add(wallpaper);
161 boxstack->update(wallpaper);
163 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
164 if (osdv) osdv->updateBackgroundColor(DrawStyle::WALLPAPER);
169 if (!initted) return;
173 Video::getInstance()->signalOn();
174 Led::getInstance()->on();
184 VConnect* vconnect = new VConnect(server);
185 boxstack->add(vconnect);
189 // Start remote thread loop
192 // Start method 2 of getting commands in...
195 // FIXME Input::NA_SIGNAL is possibly obsolete now
197 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:
247 case Message::STOP_PLAYBACK:
249 handleCommand(Input::STOP); // an odd way of doing it, but so simple
252 // Also connection_lost comes from player - anywhere else?
256 case Message::VDR_CONNECTED:
258 doJustConnected(static_cast<VConnect*>(m->from));
261 case Message::SCREENSHOT:
263 logger->log("Osd", Log::NOTICE, "Screenshot Message arrived");
264 Osd::getInstance()->screenShot("out.jpg");
267 case Message::CONNECTION_LOST:
272 case Message::INPUT_EVENT:
274 logger->log("Command", Log::NOTICE, "INPUT_EVENT %i", m->parameter);
276 handleCommand(m->parameter);
279 case Message::UDP_BUTTON:
281 handleCommand(m->parameter);
284 case Message::CHANGE_LANGUAGE:
286 boxstack->removeAllExceptWallpaper();
287 boxstack->update(wallpaper);
289 if (!VDR::getInstance()->isConnected()) { connectionLost(); break; }
290 VWelcome* vw = new VWelcome();
293 boxstack->update(vw);
296 case Message::LAST_VIEW_CLOSE:
298 // Shouldn't be done like this. Some generic message pass back from vinfo perhaps
305 // VWelcome* vw = new VWelcome();
307 // boxstack->add(vw);
308 // boxstack->update(vw);
312 case Message::NEW_PICTURE:
314 //Log::getInstance()->log("Command", Log::DEBUG, "TVMedia NEW_PICTURE");
315 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
316 if (osdv) osdv->informPicture(m->tag, reinterpret_cast<ImageIndex>(m->data));
319 case Message::NEW_PICTURE_STATIC:
321 //Log::getInstance()->log("Command", Log::DEBUG, "TVMedia NEW_PICTURE %x %x",m->tag,m->parameter);
322 OsdVector* osdv = dynamic_cast<OsdVector*>(Osd::getInstance());
323 if (osdv) osdv->informPicture(static_cast<unsigned long long>(m->tag) << 32LL, reinterpret_cast<ImageIndex>(m->data));
332 Instead of sending through the boxstack, implement a more generic MessageReceiver interface
333 and have potential receivers register with something
334 When a message needs to be delivered, check if the receiver is still registered, if so, deliver the message
335 This could all be done using the existing big command mutex to keep it simple
338 logger->log("Command", Log::DEBUG, "Sending message to boxstack");
339 boxstack->processMessage(m);
343 void Command::handleCommand(int button)
345 if (isStandby && (button != Input::POWER)
346 && (button != Input::POWERON)
347 && (button != Input::POWEROFF)) return;
349 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
351 // command was not handled
356 case Input::DF_RIGHT:
357 case Input::VOLUMEUP:
358 case Input::VOLUMEDOWN:
360 if (remote->handlesVolume())
362 if (button==Input::DF_LEFT || button==Input::VOLUMEDOWN)
363 remote->volumeDown();
369 VVolume* v = new VVolume();
371 v->handleCommand(button); // this will draw+show
377 if (remote->handlesVolume())
379 remote->volumeMute();
383 VMute* v = new VMute();
400 case Input::POWEROFF:
408 if (!connLost) return; // if connLost, handle Input::OK
414 VSleeptimer* sleep = new VSleeptimer();
415 boxstack->add(sleep);
416 sleep->handleCommand(button); // this will draw+show
425 Message* m = new Message(); // break into master mutex
426 m->message = Message::SCREENSHOT;
432 void Command::doStandby()
445 void Command::doPowerOn()
449 Video::getInstance()->signalOn();
450 Led::getInstance()->on();
451 Input::getInstance()->changePowerState(true);
454 VConnect* vconnect = new VConnect(server);
455 boxstack->add(vconnect);
460 void Command::doPowerOff()
464 VDR::getInstance()->shutdownVDR();
465 boxstack->removeAllExceptWallpaper();
466 Video::getInstance()->signalOff();
467 boxstack->update(wallpaper);
469 VDR::getInstance()->configSave("General", "Last Power State", "Off");
470 logger->unsetExternLogger();
471 VDR::getInstance()->disconnect();
472 Led::getInstance()->off();
473 Input::getInstance()->changePowerState(false);
475 Sleeptimer::getInstance()->shutdown();
477 stop(); //different behavoiur on windows, we exit
482 void Command::doFromTheTop(bool which)
484 if (isStandby) return;
489 logger->log("Command", Log::NOTICE, "Connection lost dialog already present");
493 logger->log("Command", Log::NOTICE, "Doing connection lost dialog");
494 connLost = new VInfo();
495 connLost->setSize(360, 200);
496 connLost->createBuffer();
497 if (Video::getInstance()->getFormat() == Video::PAL)
498 connLost->setPosition(190, 170);
500 connLost->setPosition(180, 120);
501 connLost->setOneLiner(tr("Connection lost"));
502 connLost->setDropThrough();
503 connLost->setBorderOn(1);
504 connLost->setTitleBarColour(DrawStyle::DANGER);
505 connLost->okButton();
507 boxstack->add(connLost);
508 boxstack->update(connLost);
510 clearMQInputEvents();
514 logger->unsetExternLogger();
515 VDR::getInstance()->disconnect();
516 boxstack->removeAllExceptWallpaper();
517 boxstack->update(wallpaper);
522 // at this point, everything should be reset to first-go
524 VConnect* vconnect = new VConnect(server);
525 boxstack->add(vconnect);
530 void Command::clearMQInputEvents()
532 // FIXME implement this
535 void Command::doReboot()
538 logger->unsetExternLogger();
539 VDR::getInstance()->disconnect();
541 logger->log("Command", Log::NOTICE, "Reboot");
543 #ifndef VOMP_HAS_EXIT
544 // some plattforms, want a proper deinitialisation of their hardware before reboot
545 Osd::getInstance()->shutdown();
546 Audio::getInstance()->shutdown();
547 Video::getInstance()->shutdown();
548 Input::getInstance()->shutdown();
550 reboot(LINUX_REBOOT_CMD_RESTART);
551 // if reboot is not allowed -> stop
563 #endif //Would we support this on windows?
566 void Command::connectionLost()
568 logger->unsetExternLogger();
569 Message* m = new Message(); // break into master mutex
570 m->message = Message::CONNECTION_LOST;
575 void Command::buildCrashedBox()
577 VInfo* crash = new VInfo();
578 crash->setSize(360, 250);
579 crash->createBuffer();
580 if (Video::getInstance()->getFormat() == Video::PAL)
581 crash->setPosition(190, 146);
583 crash->setPosition(180, 96);
584 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.");
585 crash->setBorderOn(1);
586 crash->setTitleBarColour(DrawStyle::DANGER);
588 crash->setExitable();
590 boxstack->add(crash);
591 boxstack->update(crash);
594 int Command::getLangPref(bool subtitle, const char* langcode)
596 std::vector<struct ASLPref>::iterator itty=langcodes.begin();
597 char templangcode[4];
598 templangcode[0] = langcode[0];
599 templangcode[1] = langcode[1];
600 templangcode[2] = langcode[2];
601 templangcode[3] = '\0';
603 while (itty != langcodes.end())
605 size_t pos = (*itty).langcode.find(templangcode);
606 if (pos != std::string::npos)
608 //vector<struct ASLPref>::iterator itty2=langcodes.begin();
609 for (unsigned int i = 0; i < langcodes.size(); i++)
613 pref = langcodes[i].subtitlepref;
615 pref = langcodes[i].audiopref;
620 if (langcodes[i].subtitlepref==langpos) return i;
624 if (langcodes[i].audiopref==langpos) return i;
632 return langcodes.size(); //neutral
635 void Command::doJustConnected(VConnect* vconnect)
638 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
639 logger->log("Command", Log::INFO, "Entering doJustConnected");
641 Video* video = Video::getInstance();
642 Audio* audio = Audio::getInstance();
643 boxstack->remove(vconnect);
645 VInfo* vi = new VInfo();
646 vi->setSize(400, 200);
648 if (video->getFormat() == Video::PAL)
649 vi->setPosition(170, 200);
651 vi->setPosition(160, 150);
652 vi->setOneLiner(tr("Connected, loading config"));
655 boxstack->update(vi);
657 VDR* vdr = VDR::getInstance();
660 // See if we're supposed to do network logging
661 config = vdr->configLoad("Advanced", "Network logging");
662 if (config && !STRCASECMP(config, "On"))
664 logger->log("Command", Log::INFO, "Turning on network logging");
665 logger->setExternLogger(vdr);
669 logger->unsetExternLogger();
670 logger->log("Command", Log::INFO, "Turned off network logging");
672 if (config) delete[] config;
674 config = vdr->configLoad("Advanced", "Skin Name");
677 const char **skinnames=SkinFactory::getSkinNames();
678 for (int i=0;i<SkinFactory::getNumberofSkins();i++)
680 if (!STRCASECMP(config, skinnames[i]))
682 SkinFactory::InitSkin(i);
688 if (wallpaper && wallpaper_pict)
690 if (DrawStyle::WALLPAPER.alpha)
691 wallpaper_pict->setVisible(true);
693 wallpaper_pict->setVisible(false);
696 boxstack->update(wallpaper);
701 SkinFactory::InitSkin(0);
704 // See if config says to override video format (PAL/NTSC)
705 config = vdr->configLoad("General", "Override Video Format");
708 logger->log("Command", Log::DEBUG, "Override Video Format is present");
710 if ( (!strcmp(config, "PAL") && (video->getFormat() != Video::PAL))
711 || (!strcmp(config, "NTSC") && (video->getFormat() != Video::NTSC))
712 || (!strcmp(config, "PAL_M") && (video->getFormat() != Video::PAL_M))
713 || (!strcmp(config, "NTSC_J") && (video->getFormat() != Video::NTSC_J))
716 // Oh sheesh, need to switch format. Bye bye TV...
718 // Take everything down
719 boxstack->removeAllExceptWallpaper();
720 boxstack->remove(wallpaper);
721 delete wallpaper_pict; wallpaper_pict = NULL; wallpaper = NULL;
723 Osd* osd = Osd::getInstance();
729 remote->shutdown(); // need on raspberry shut not do any harm, hopefully
730 remote->init(RemoteStartDev);
732 // Get video and osd back up with the new mode
733 if (!strcmp(config, "PAL"))
735 logger->log("Command", Log::DEBUG, "Switching to PAL");
736 video->init(Video::PAL);
738 else if (!strcmp(config, "NTSC"))
740 logger->log("Command", Log::DEBUG, "Switching to NTSC");
741 video->init(Video::NTSC);
742 } else if (!strcmp(config, "PAL_M"))
744 logger->log("Command", Log::DEBUG, "Switching to PAL_M");
745 video->init(Video::PAL_M);
746 } else if (!strcmp(config, "NTSC_J"))
748 logger->log("Command", Log::DEBUG, "Switching to NTSC_J");
749 video->init(Video::NTSC_J);
754 //we do not init twice
758 // Put the wallpaper back
763 vi->setSize(400, 200);
765 if (video->getFormat() == Video::PAL)
766 vi->setPosition(170, 200);
768 vi->setPosition(160, 150);
770 vi->setOneLiner(tr("Connected, loading config"));
773 boxstack->update(vi);
777 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
782 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
785 // Power off if first boot and config says so
790 logger->log("Command", Log::DEBUG, "Load power after boot");
792 config = vdr->configLoad("General", "Power After Boot");
796 if (!STRCASECMP(config, "On"))
798 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
800 else if (!STRCASECMP(config, "Off"))
802 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
807 else if (!STRCASECMP(config, "Last state"))
809 char* lastPowerState = vdr->configLoad("General", "Last Power State");
812 if (!STRCASECMP(lastPowerState, "On"))
814 logger->log("Command", Log::INFO, "Config says Last Power State = On");
816 else if (!STRCASECMP(lastPowerState, "Off"))
818 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
825 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
830 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
835 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
841 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
846 // Go S-Video if config says so
848 config = vdr->configLoad("TV", "Connection");
852 if (!STRCASECMP(config, "S-Video"))
854 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
855 video->setConnection(Video::SVIDEO);
856 } else if (!STRCASECMP(config, "HDMI"))
858 logger->log("Command", Log::INFO, "Switching to HDMI as Connection=%s", config);
859 video->setConnection(Video::HDMI);
860 } else if (!STRCASECMP(config, "HDMI3D"))
862 logger->log("Command", Log::INFO, "Switching to HDMI3D as Connection=%s", config);
863 video->setConnection(Video::HDMI3D);
867 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
868 video->setConnection(Video::COMPOSITERGB);
874 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
877 // Set to shutdown VDR if config says
879 config = vdr->configLoad("General", "VDR shutdown");
882 if (!STRCASECMP(config, "On"))
884 logger->log("Command", Log::INFO, "Shutdown VDR when shutting down vomp");
885 vdr->setVDRShutdown(true);
887 else if (!STRCASECMP(config, "Off"))
889 logger->log("Command", Log::INFO, "Shutdown only vomp");
890 vdr->setVDRShutdown(false);
895 logger->log("Command", Log::INFO, "Default shutdown only vomp");
896 vdr->setVDRShutdown(false); // Default
901 config = vdr->configLoad("General", "Remote type");
905 if (!STRCASECMP(config, "New"))
907 logger->log("Command", Log::INFO, "Switching to New remote type");
908 remote->setRemoteType(Input::NEWREMOTE);
912 logger->log("Command", Log::INFO, "Switching to Old remote type");
913 remote->setRemoteType(Input::OLDREMOTE);
919 logger->log("Command", Log::INFO, "Config General/Remote type not found");
920 remote->setRemoteType(Input::OLDREMOTE);
925 // Get TV aspect ratio
927 config = vdr->configLoad("TV", "Aspect");
930 if (!STRCASECMP(config, "16:9"))
932 logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
933 video->setTVsize(Video::ASPECT16X9);
937 logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
938 video->setTVsize(Video::ASPECT4X3);
944 logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
945 video->setTVsize(Video::ASPECT4X3);
948 config = vdr->configLoad("TV", "Widemode");
951 if (!STRCASECMP(config, "Letterbox"))
953 logger->log("Command", Log::INFO, "Setting letterbox mode");
954 video->setMode(Video::LETTERBOX);
958 logger->log("Command", Log::INFO, "Setting chop-sides mode");
959 video->setMode(Video::NORMAL);
966 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting letterbox mode");
967 video->setMode(Video::LETTERBOX);
969 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
970 video->setMode(Video::NORMAL);
974 config = vdr->configLoad("Advanced", "TCP receive window");
977 size_t newTCPsize = atoi(config);
980 logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
981 vdr->setReceiveWindow(newTCPsize);
985 logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
986 if (DEFAULT_TCP_WINDOWSIZE) vdr->setReceiveWindow(2048); // Default
989 config = vdr->configLoad("Advanced", "Font Name");
992 Osd::getInstance()->setFont(config);
993 logger->log("Command", Log::INFO, "Setting Font to %s", config);
999 // Set recording list type
1001 #ifdef ADVANCED_MENUES
1002 config = vdr->configLoad("Advanced", "Menu type");
1006 if (!STRCASECMP(config, "Advanced"))
1008 logger->log("Command", Log::INFO, "Switching to Advanced menu");
1013 logger->log("Command", Log::INFO, "Switching to Classic menu");
1020 logger->log("Command", Log::INFO, "Config General/menu type not found");
1025 config = vdr->configLoad("Advanced", "Disable WOL");
1028 if (!STRCASECMP(config, "Yes"))
1030 logger->log("Command", Log::INFO, "Config says disable WOL");
1031 Wol::getInstance()->setEnabled(false);
1035 logger->log("Command", Log::INFO, "Config says enable WOL");
1036 Wol::getInstance()->setEnabled(true);
1043 logger->log("Command", Log::INFO, "By default, enable WOL");
1044 Wol::getInstance()->setEnabled(true);
1046 /* device dependend config */
1047 audio->loadOptionsfromServer(vdr);
1048 video->loadOptionsfromServer(vdr);
1049 remote->loadOptionsfromServer(vdr);
1051 video->executePendingModeChanges();
1054 // Save power state = on
1056 vdr->configSave("General", "Last Power State", "On");
1058 // Make sure connection didn't die
1059 if (!vdr->isConnected())
1061 Command::getInstance()->connectionLost();
1065 boxstack->remove(vi);
1067 VWelcome* vw = new VWelcome();
1070 boxstack->update(vw);
1072 // Enter pre-keys here
1073 // handleCommand(Input::OK);
1074 // handleCommand(Input::THREE);
1075 // handleCommand(Input::SIX);
1076 // handleCommand(Input::OK);
1077 // handleCommand(Input::UP);
1078 // handleCommand(Input::PLAY);
1079 // handleCommand(Input::DOWN);
1080 // handleCommand(Input::DOWN);
1081 // handleCommand(Input::DOWN);
1082 // handleCommand(Input::OK);
1083 // handleCommand(Input::RED);