2 Copyright 2004-2005 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, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 #include <linux/errno.h>
28 #include "remotewin.h"
32 #include "remoteandroid.h"
41 #include "vserverselect.h"
47 #include "timerreceiver.h"
57 #include "vsleeptimer.h"
60 Command* Command::instance = NULL;
79 Command* Command::getInstance()
84 int Command::init(bool tcrashed, char* tServer)
86 if (initted) return 0;
91 logger = Log::getInstance();
92 boxstack = BoxStack::getInstance();
93 remote = Remote::getInstance();
95 remote->InitHWCListwithDefaults();
97 if (!logger || !boxstack || !remote)
103 pthread_mutex_init(&masterLock, NULL);
105 masterLock=CreateMutex(NULL,FALSE,NULL);
111 int Command::shutdown()
113 if (!initted) return 0;
120 // VDR::getInstance()->cancelFindingServer();
121 logger->log("Command", Log::NOTICE, "Command stop1...");
124 logger->log("Command", Log::NOTICE, "Command stop2...");
128 void Command::doWallpaper()
130 Video* video = Video::getInstance();
133 Boxx* bbg = new Boxx();
134 bbg->setSize(video->getScreenWidth(), video->getScreenHeight());
136 bbg->fillColour(DrawStyle::VIDEOBLUE);
138 boxstack->update(bbg);
139 boxstack->remove(bbg);
142 WJpeg* wallpaperj = new WJpegTYPE();
143 wallpaperj->setSize(video->getScreenWidth(), video->getScreenHeight());
144 wallpaperj->createBuffer();
146 if (video->getFormat() == Video::PAL)
148 logger->log("Command", Log::DEBUG, "PAL wallpaper selected");
150 wallpaperj->init("/wallpaperPAL.jpg");
152 wallpaperj->init("wallpaperPAL.jpg");
157 logger->log("Command", Log::DEBUG, "NTSC wallpaper selected");
158 wallpaperj->init("/wallpaperNTSC.jpg");
162 boxstack->add(wallpaperj);
163 boxstack->update(wallpaperj);
165 wallpaper = wallpaperj;
170 if (!initted) return;
177 Video::getInstance()->signalOn();
178 Led::getInstance()->on();
182 // End of startup. Lock the mutex and put the first view up
183 // logger->log("Command", Log::DEBUG, "WANT LOCK");
185 pthread_mutex_lock(&masterLock);
187 WaitForSingleObject(masterLock, INFINITE );
189 //logger->log("Command", Log::DEBUG, "LOCKED");
197 VConnect* vconnect = new VConnect(server);
198 boxstack->add(vconnect);
202 // Start method 2 of getting commands in...
209 //logger->log("Command", Log::DEBUG, "UNLOCK");
211 pthread_mutex_unlock(&masterLock);
213 ReleaseMutex(masterLock);
216 button = remote->getButtonPress(2); // FIXME why is this set to 2 and not 0? so it can quit
217 // something happened, lock and process
219 // logger->log("Command", Log::DEBUG, "WANT LOCK");
221 pthread_mutex_lock(&masterLock);
223 WaitForSingleObject(masterLock, INFINITE );
225 // logger->log("Command", Log::DEBUG, "LOCK");
227 if ((button == Remote::NA_NONE) /*|| (button == Remote::NA_UNKNOWN)*/) continue;
229 if (button != Remote::NA_SIGNAL) handleCommand(button);
230 processMessageQueue();
234 //logger->log("Command", Log::DEBUG, "UNLOCK");
236 pthread_mutex_unlock(&masterLock);
238 ReleaseMutex(masterLock);
244 void Command::postMessage(Message* m)
246 // This is locked here in case the main loop is not waiting for an event, but is processing one
247 // it could be killed but then not react to it because the signal wouldn't cause
248 // remote->getButtonPress to break
249 // locking the mutex ensures that the master thread is waiting on getButtonPress
252 //logger->log("Command", Log::DEBUG, "WANT LOCK");
254 pthread_mutex_lock(&masterLock);
256 WaitForSingleObject(masterLock, INFINITE );
258 //logger->log("Command", Log::DEBUG, "LOCK");
259 MessageQueue::postMessage(m);
263 kill(mainPid, SIGURG);
265 ((RemoteAndroid*)Remote::getInstance())->Signal();
267 pthread_mutex_unlock(&masterLock);
269 ((RemoteWin*)Remote::getInstance())->Signal();
270 ReleaseMutex(masterLock);
272 //logger->log("Command", Log::DEBUG, "UNLOCK");
275 void Command::postMessageNoLock(Message* m)
277 // As above but use this one if this message is being posted because of a button press
278 // the mutex is already locked, locking around postMessage is not needed as the
279 // queue is guaranteed to be run when the button has been processed
280 MessageQueue::postMessage(m);
283 bool Command::postMessageIfNotBusy(Message* m)
285 // Used for Windows mouse events
287 //logger->log("Command", Log::DEBUG, "TRY LOCK");
289 if (pthread_mutex_trylock(&masterLock) != EBUSY)
291 //logger->log("Command", Log::DEBUG, "LOCK");
292 MessageQueue::postMessage(m);
294 kill(mainPid, SIGURG);
296 ((RemoteAndroid*)Remote::getInstance())->Signal();
298 pthread_mutex_unlock(&masterLock);
299 //logger->log("Command", Log::DEBUG, "UNLOCK");
307 switch (WaitForSingleObject(masterLock, 0 ))
308 { //FIXME this is not "if not busy" check
309 case WAIT_OBJECT_0: //but with proper argument 0 this did not work
310 // case WAIT_ABANDONED:
311 MessageQueue::postMessage(m);
312 ((RemoteWin*)Remote::getInstance())->Signal();
313 ReleaseMutex(masterLock);
316 case WAIT_ABANDONED: return false;
317 case WAIT_TIMEOUT: return false;
323 void Command::postMessageFromOuterSpace(Message* m)
326 Yet another way of getting messages into Command. This one is for events that
327 are not standard button presses (or UDP generated buttons). It is also not for
328 events that are generated as a result of other events (events that can safely
329 call postMessageNoLock and be guaranteed that the message will be processed
330 because it is known that the queue is currently being processed).
331 This is for events that come from outer space and can occur when the master
332 mutex is locked or not, they need to be queued and executed but it doesn't
334 Actually so far it is for events caused by the video stream - aspect ratio
335 changes. These can occur when the master mutex is locked and so postMessage
336 doesn't work. postMessageNoLock doesn't work because if the mutex *isn't*
337 locked at the time then the message could be sat around a while before
339 The whole message system was at first supposed to prevent the problem of
340 calling a function on an object that had just been deleted, by ordering
341 messages such that all calls are done before object deletion. However,
342 because of the new centralised messaging system and the fact that BoxStack
343 locates the destination object before calling it, the messaging system now
344 allows the kind of sloppy calls it was supposed to stop. Weird huh. This
345 is mentioned here because the video stream might generate an event just as
346 the user hits stop. The mutex is locked, and by the time the message
347 is examined the vvideorec/live has been deleted. This doesn't matter because
348 boxstack will drop the message if it can't find the matching object to
350 Finally, all this is fine and dandy, except that I'm not 100% sure that
351 this sloppy postMessage and hope a queued signal will force it to be processed
352 thingy will actually work. Hmmm.
353 Lastly <g>, I will consider making the naming system a little more sane
357 logger->log("Command", Log::DEBUG, "PMFOS called");
358 MessageQueue::postMessage(m);
362 kill(mainPid, SIGURG);
364 ((RemoteAndroid*)Remote::getInstance())->Signal();
367 ((RemoteWin*)Remote::getInstance())->Signal();
371 void Command::processMessage(Message* m)
373 // FIXME - a slight modification - how if messagereceivers were to register
374 // themselves as receivers to avoid the calling-a-deleted-object problem
375 // then only deliver/register/unregister would have to be protected
377 logger->log("Command", Log::DEBUG, "processing message %i", m->message);
385 case Message::STOP_PLAYBACK:
387 handleCommand(Remote::STOP); // an odd way of doing it, but so simple
390 // Also connection_lost comes from player - anywhere else?
394 case Message::VDR_CONNECTED:
396 doJustConnected((VConnect*)m->from);
399 case Message::SCREENSHOT:
401 Osd::getInstance()->screenShot("/out.jpg");
404 case Message::CONNECTION_LOST:
409 case Message::UDP_BUTTON:
411 handleCommand(m->parameter);
414 case Message::CHANGE_LANGUAGE:
416 boxstack->removeAll();
417 boxstack->update(wallpaper);
419 if (!VDR::getInstance()->isConnected()) { connectionLost(); break; }
420 VWelcome* vw = new VWelcome();
423 boxstack->update(vw);
426 case Message::LAST_VIEW_CLOSE:
428 // Shouldn't be done like this. Some generic message pass back from vinfo perhaps
435 // VWelcome* vw = new VWelcome();
437 // boxstack->add(vw);
438 // boxstack->update(vw);
448 Instead of sending through the boxstack, implement a more generic MessageReceiver interface
449 and have potential receivers register with something
450 When a message needs to be delivered, check if the receiver is still registered, if so, deliver the message
451 This could all be done using the existing big command mutex to keep it simple
454 logger->log("Command", Log::DEBUG, "Sending message to boxstack");
455 boxstack->processMessage(m);
459 void Command::handleCommand(int button)
461 if (isStandby && (button != Remote::POWER)) return;
462 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
464 // command was not handled
468 case Remote::DF_LEFT:
469 case Remote::DF_RIGHT:
470 case Remote::VOLUMEUP:
471 case Remote::VOLUMEDOWN:
473 if (remote->handlesVolume()) {
474 if (button==Remote::DF_LEFT || button==Remote::VOLUMEDOWN)
475 remote->volumeDown();
476 else remote->volumeUp();
478 VVolume* v = new VVolume();
480 v->handleCommand(button); // this will draw+show
486 if (remote->handlesVolume()) {
487 remote->volumeMute();
489 VMute* v = new VMute();
504 if (!connLost) return; // if connLost, handle Remote::OK
510 VSleeptimer* sleep = new VSleeptimer();
511 boxstack->add(sleep);
512 sleep->handleCommand(button); // this will draw+show
521 Message* m = new Message(); // break into master mutex
522 m->message = Message::SCREENSHOT;
528 void Command::doStandby()
532 Video::getInstance()->signalOn();
533 Led::getInstance()->on();
537 VConnect* vconnect = new VConnect(server);
538 boxstack->add(vconnect);
543 VDR::getInstance()->shutdownVDR();
544 boxstack->removeAll();
545 Video::getInstance()->signalOff();
546 boxstack->update(wallpaper);
548 VDR::getInstance()->configSave("General", "Last Power State", "Off");
549 logger->unsetExternLogger();
550 VDR::getInstance()->disconnect();
551 Led::getInstance()->off();
553 Sleeptimer::getInstance()->shutdown();
555 stop(); //different behavoiur on windows, we exit
560 void Command::doFromTheTop(bool which)
566 logger->log("Command", Log::NOTICE, "Connection lost dialog already present");
570 logger->log("Command", Log::NOTICE, "Doing connection lost dialog");
571 connLost = new VInfo();
572 connLost->setSize(360, 200);
573 connLost->createBuffer();
574 if (Video::getInstance()->getFormat() == Video::PAL)
575 connLost->setPosition(190, 170);
577 connLost->setPosition(180, 120);
578 connLost->setOneLiner(tr("Connection lost"));
579 connLost->setDropThrough();
580 connLost->setBorderOn(1);
581 connLost->setTitleBarColour(DrawStyle::DANGER);
582 connLost->okButton();
584 boxstack->add(connLost);
585 boxstack->update(connLost);
586 remote->clearBuffer();
590 logger->unsetExternLogger();
591 VDR::getInstance()->disconnect();
592 boxstack->removeAll();
593 boxstack->update(wallpaper);
597 remote->clearBuffer();
599 // at this point, everything should be reset to first-go
601 VConnect* vconnect = new VConnect(server);
602 boxstack->add(vconnect);
607 void Command::doReboot()
610 logger->unsetExternLogger();
611 VDR::getInstance()->disconnect();
613 logger->log("Command", Log::NOTICE, "Reboot");
615 #ifndef VOMP_HAS_EXIT
616 // some plattforms, want a proper deinitialisation of their hardware before reboot
617 Osd::getInstance()->shutdown();
618 Audio::getInstance()->shutdown();
619 Video::getInstance()->shutdown();
620 Remote::getInstance()->shutdown();
622 reboot(LINUX_REBOOT_CMD_RESTART);
623 // if reboot is not allowed -> stop
635 #endif //Would we support this on windows?
638 void Command::connectionLost()
640 logger->unsetExternLogger();
641 Message* m = new Message(); // break into master mutex
642 m->message = Message::CONNECTION_LOST;
644 postMessageFromOuterSpace(m);
647 void Command::buildCrashedBox()
649 VInfo* crash = new VInfo();
650 crash->setSize(360, 250);
651 crash->createBuffer();
652 if (Video::getInstance()->getFormat() == Video::PAL)
653 crash->setPosition(190, 146);
655 crash->setPosition(180, 96);
656 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.");
657 crash->setBorderOn(1);
658 crash->setTitleBarColour(DrawStyle::DANGER);
660 crash->setExitable();
662 boxstack->add(crash);
663 boxstack->update(crash);
666 void Command::doJustConnected(VConnect* vconnect)
669 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
670 logger->log("Command", Log::INFO, "Entering doJustConnected");
672 Video* video = Video::getInstance();
673 Audio* audio = Audio::getInstance();
674 boxstack->remove(vconnect);
676 VInfo* vi = new VInfo();
677 vi->setSize(400, 200);
679 if (video->getFormat() == Video::PAL)
680 vi->setPosition(170, 200);
682 vi->setPosition(160, 150);
683 vi->setOneLiner(tr("Connected, loading config"));
686 boxstack->update(vi);
688 VDR* vdr = VDR::getInstance();
691 // See if we're supposed to do network logging
692 config = vdr->configLoad("Advanced", "Network logging");
693 if (config && !STRCASECMP(config, "On"))
695 logger->log("Command", Log::INFO, "Turning on network logging");
696 logger->setExternLogger(vdr);
700 logger->unsetExternLogger();
701 logger->log("Command", Log::INFO, "Turned off network logging");
703 if (config) delete[] config;
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->removeAll();
721 boxstack->remove(wallpaper);
722 Osd* osd = Osd::getInstance();
728 remote->shutdown(); // need on raspberry shut not do any harm, hopefully
729 remote->init(RemoteStartDev);
731 // Get video and osd back up with the new mode
732 if (!strcmp(config, "PAL"))
734 logger->log("Command", Log::DEBUG, "Switching to PAL");
735 video->init(Video::PAL);
737 else if (!strcmp(config, "NTSC"))
739 logger->log("Command", Log::DEBUG, "Switching to NTSC");
740 video->init(Video::NTSC);
741 } else if (!strcmp(config, "PAL_M"))
743 logger->log("Command", Log::DEBUG, "Switching to PAL_M");
744 video->init(Video::PAL_M);
745 } else if (!strcmp(config, "NTSC_J"))
747 logger->log("Command", Log::DEBUG, "Switching to NTSC_J");
748 video->init(Video::NTSC_J);
751 //we do not init twice
752 osd->init((char*)("/dev/stbgfx"));
755 // Put the wallpaper back
760 vi->setSize(400, 200);
762 if (video->getFormat() == Video::PAL)
763 vi->setPosition(170, 200);
765 vi->setPosition(160, 150);
767 vi->setOneLiner(tr("Connected, loading config"));
770 boxstack->update(vi);
774 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
779 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
782 // Power off if first boot and config says so
787 logger->log("Command", Log::DEBUG, "Load power after boot");
789 config = vdr->configLoad("General", "Power After Boot");
793 if (!STRCASECMP(config, "On"))
795 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
797 else if (!STRCASECMP(config, "Off"))
799 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
804 else if (!STRCASECMP(config, "Last state"))
806 char* lastPowerState = vdr->configLoad("General", "Last Power State");
809 if (!STRCASECMP(lastPowerState, "On"))
811 logger->log("Command", Log::INFO, "Config says Last Power State = On");
813 else if (!STRCASECMP(lastPowerState, "Off"))
815 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
822 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
827 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
832 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
838 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
843 // Go S-Video if config says so
845 config = vdr->configLoad("TV", "Connection");
849 if (!STRCASECMP(config, "S-Video"))
851 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
852 video->setConnection(Video::SVIDEO);
853 } else if (!STRCASECMP(config, "HDMI"))
855 logger->log("Command", Log::INFO, "Switching to HDMI as Connection=%s", config);
856 video->setConnection(Video::HDMI);
857 } else if (!STRCASECMP(config, "HDMI3D"))
859 logger->log("Command", Log::INFO, "Switching to HDMI3D as Connection=%s", config);
860 video->setConnection(Video::HDMI3D);
864 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
865 video->setConnection(Video::COMPOSITERGB);
871 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
874 // Set to shutdown VDR if config says
876 config = vdr->configLoad("General", "VDR shutdown");
879 if (!STRCASECMP(config, "On"))
881 logger->log("Command", Log::INFO, "Shutdown VDR when shutting down vomp");
882 vdr->setVDRShutdown(true);
884 else if (!STRCASECMP(config, "Off"))
886 logger->log("Command", Log::INFO, "Shutdown only vomp");
887 vdr->setVDRShutdown(false);
892 logger->log("Command", Log::INFO, "Default shutdown only vomp");
893 vdr->setVDRShutdown(false); // Default
898 config = vdr->configLoad("General", "Remote type");
902 if (!STRCASECMP(config, "New"))
904 logger->log("Command", Log::INFO, "Switching to New remote type");
905 remote->setRemoteType(Remote::NEWREMOTE);
909 logger->log("Command", Log::INFO, "Switching to Old remote type");
910 remote->setRemoteType(Remote::OLDREMOTE);
916 logger->log("Command", Log::INFO, "Config General/Remote type not found");
917 remote->setRemoteType(Remote::OLDREMOTE);
923 // Get TV aspect ratio
925 config = vdr->configLoad("TV", "Aspect");
928 if (!STRCASECMP(config, "16:9"))
930 logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
931 video->setTVsize(Video::ASPECT16X9);
935 logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
936 video->setTVsize(Video::ASPECT4X3);
942 logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
943 video->setTVsize(Video::ASPECT4X3);
946 config = vdr->configLoad("TV", "Widemode");
949 if (!STRCASECMP(config, "Letterbox"))
951 logger->log("Command", Log::INFO, "Setting letterbox mode");
952 video->setMode(Video::LETTERBOX);
956 logger->log("Command", Log::INFO, "Setting chop-sides mode");
957 video->setMode(Video::NORMAL);
964 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting letterbox mode");
965 video->setMode(Video::LETTERBOX);
967 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
968 video->setMode(Video::NORMAL);
972 config = vdr->configLoad("Advanced", "TCP receive window");
975 size_t newTCPsize = atoi(config);
978 logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
979 vdr->setReceiveWindow(newTCPsize);
983 logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
984 if (DEFAULT_TCP_WINDOWSIZE) vdr->setReceiveWindow(2048); // Default
987 config = vdr->configLoad("Advanced", "Font Name");
990 Osd::getInstance()->setFont(config);
991 logger->log("Command", Log::INFO, "Setting Font to %s", config);
997 config = vdr->configLoad("Advanced", "Disable WOL");
1000 if (!STRCASECMP(config, "Yes"))
1002 logger->log("Command", Log::INFO, "Config says disable WOL");
1003 Wol::getInstance()->setEnabled(false);
1007 logger->log("Command", Log::INFO, "Config says enable WOL");
1008 Wol::getInstance()->setEnabled(true);
1015 logger->log("Command", Log::INFO, "By default, enable WOL");
1016 Wol::getInstance()->setEnabled(true);
1018 /* device dependend config */
1019 audio->loadOptionsfromServer(vdr);
1020 video->loadOptionsfromServer(vdr);
1021 remote->loadOptionsfromServer(vdr);
1023 video->executePendingModeChanges();
1026 // Save power state = on
1028 vdr->configSave("General", "Last Power State", "On");
1030 // Make sure connection didn't die
1031 if (!vdr->isConnected())
1033 Command::getInstance()->connectionLost();
1037 boxstack->remove(vi);
1039 VWelcome* vw = new VWelcome();
1042 boxstack->update(vw);
1044 // Enter pre-keys here
1045 // handleCommand(Remote::OK);
1046 // handleCommand(Remote::THREE);
1047 // handleCommand(Remote::SIX);
1048 // handleCommand(Remote::OK);
1049 // handleCommand(Remote::UP);
1050 // handleCommand(Remote::PLAY);
1051 // handleCommand(Remote::DOWN);
1052 // handleCommand(Remote::DOWN);
1053 // handleCommand(Remote::DOWN);
1054 // handleCommand(Remote::OK);
1055 // handleCommand(Remote::RED);