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 boxstack->removeAll();
544 Video::getInstance()->signalOff();
545 boxstack->update(wallpaper);
547 VDR::getInstance()->configSave("General", "Last Power State", "Off");
548 logger->unsetExternLogger();
549 VDR::getInstance()->disconnect();
550 Led::getInstance()->off();
552 Sleeptimer::getInstance()->shutdown();
554 stop(); //different behavoiur on windows, we exit
559 void Command::doFromTheTop(bool which)
565 logger->log("Command", Log::NOTICE, "Connection lost dialog already present");
569 logger->log("Command", Log::NOTICE, "Doing connection lost dialog");
570 connLost = new VInfo();
571 connLost->setSize(360, 200);
572 connLost->createBuffer();
573 if (Video::getInstance()->getFormat() == Video::PAL)
574 connLost->setPosition(190, 170);
576 connLost->setPosition(180, 120);
577 connLost->setOneLiner(tr("Connection lost"));
578 connLost->setDropThrough();
579 connLost->setBorderOn(1);
580 connLost->setTitleBarColour(DrawStyle::DANGER);
581 connLost->okButton();
583 boxstack->add(connLost);
584 boxstack->update(connLost);
585 remote->clearBuffer();
589 logger->unsetExternLogger();
590 VDR::getInstance()->disconnect();
591 boxstack->removeAll();
592 boxstack->update(wallpaper);
596 remote->clearBuffer();
598 // at this point, everything should be reset to first-go
600 VConnect* vconnect = new VConnect(server);
601 boxstack->add(vconnect);
606 void Command::doReboot()
609 logger->unsetExternLogger();
610 VDR::getInstance()->disconnect();
612 logger->log("Command", Log::NOTICE, "Reboot");
614 #ifndef VOMP_HAS_EXIT
615 // some plattforms, want a proper deinitialisation of their hardware before reboot
616 Osd::getInstance()->shutdown();
617 Audio::getInstance()->shutdown();
618 Video::getInstance()->shutdown();
619 Remote::getInstance()->shutdown();
621 reboot(LINUX_REBOOT_CMD_RESTART);
622 // if reboot is not allowed -> stop
634 #endif //Would we support this on windows?
637 void Command::connectionLost()
639 logger->unsetExternLogger();
640 Message* m = new Message(); // break into master mutex
641 m->message = Message::CONNECTION_LOST;
643 postMessageFromOuterSpace(m);
646 void Command::buildCrashedBox()
648 VInfo* crash = new VInfo();
649 crash->setSize(360, 250);
650 crash->createBuffer();
651 if (Video::getInstance()->getFormat() == Video::PAL)
652 crash->setPosition(190, 146);
654 crash->setPosition(180, 96);
655 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.");
656 crash->setBorderOn(1);
657 crash->setTitleBarColour(DrawStyle::DANGER);
659 crash->setExitable();
661 boxstack->add(crash);
662 boxstack->update(crash);
665 void Command::doJustConnected(VConnect* vconnect)
668 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
669 logger->log("Command", Log::INFO, "Entering doJustConnected");
671 Video* video = Video::getInstance();
672 Audio* audio = Audio::getInstance();
673 boxstack->remove(vconnect);
675 VInfo* vi = new VInfo();
676 vi->setSize(400, 200);
678 if (video->getFormat() == Video::PAL)
679 vi->setPosition(170, 200);
681 vi->setPosition(160, 150);
682 vi->setOneLiner(tr("Connected, loading config"));
685 boxstack->update(vi);
687 VDR* vdr = VDR::getInstance();
690 // See if we're supposed to do network logging
691 config = vdr->configLoad("Advanced", "Network logging");
692 if (config && !STRCASECMP(config, "On"))
694 logger->log("Command", Log::INFO, "Turning on network logging");
695 logger->setExternLogger(vdr);
699 logger->unsetExternLogger();
700 logger->log("Command", Log::INFO, "Turned off network logging");
702 if (config) delete[] config;
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->removeAll();
720 boxstack->remove(wallpaper);
721 Osd* osd = Osd::getInstance();
727 remote->shutdown(); // need on raspberry shut not do any harm, hopefully
728 remote->init(RemoteStartDev);
730 // Get video and osd back up with the new mode
731 if (!strcmp(config, "PAL"))
733 logger->log("Command", Log::DEBUG, "Switching to PAL");
734 video->init(Video::PAL);
736 else if (!strcmp(config, "NTSC"))
738 logger->log("Command", Log::DEBUG, "Switching to NTSC");
739 video->init(Video::NTSC);
740 } else if (!strcmp(config, "PAL_M"))
742 logger->log("Command", Log::DEBUG, "Switching to PAL_M");
743 video->init(Video::PAL_M);
744 } else if (!strcmp(config, "NTSC_J"))
746 logger->log("Command", Log::DEBUG, "Switching to NTSC_J");
747 video->init(Video::NTSC_J);
750 //we do not init twice
751 osd->init((char*)("/dev/stbgfx"));
754 // Put the wallpaper back
759 vi->setSize(400, 200);
761 if (video->getFormat() == Video::PAL)
762 vi->setPosition(170, 200);
764 vi->setPosition(160, 150);
766 vi->setOneLiner(tr("Connected, loading config"));
769 boxstack->update(vi);
773 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
778 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
781 // Power off if first boot and config says so
786 logger->log("Command", Log::DEBUG, "Load power after boot");
788 config = vdr->configLoad("General", "Power After Boot");
792 if (!STRCASECMP(config, "On"))
794 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
796 else if (!STRCASECMP(config, "Off"))
798 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
803 else if (!STRCASECMP(config, "Last state"))
805 char* lastPowerState = vdr->configLoad("General", "Last Power State");
808 if (!STRCASECMP(lastPowerState, "On"))
810 logger->log("Command", Log::INFO, "Config says Last Power State = On");
812 else if (!STRCASECMP(lastPowerState, "Off"))
814 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
821 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
826 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
831 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
837 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
842 // Go S-Video if config says so
844 config = vdr->configLoad("TV", "Connection");
848 if (!STRCASECMP(config, "S-Video"))
850 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
851 video->setConnection(Video::SVIDEO);
852 } else if (!STRCASECMP(config, "HDMI"))
854 logger->log("Command", Log::INFO, "Switching to HDMI as Connection=%s", config);
855 video->setConnection(Video::HDMI);
856 } else if (!STRCASECMP(config, "HDMI3D"))
858 logger->log("Command", Log::INFO, "Switching to HDMI3D as Connection=%s", config);
859 video->setConnection(Video::HDMI3D);
863 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
864 video->setConnection(Video::COMPOSITERGB);
870 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
875 config = vdr->configLoad("General", "Remote type");
879 if (!STRCASECMP(config, "New"))
881 logger->log("Command", Log::INFO, "Switching to New remote type");
882 remote->setRemoteType(Remote::NEWREMOTE);
886 logger->log("Command", Log::INFO, "Switching to Old remote type");
887 remote->setRemoteType(Remote::OLDREMOTE);
893 logger->log("Command", Log::INFO, "Config General/Remote type not found");
894 remote->setRemoteType(Remote::OLDREMOTE);
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 vdr->setReceiveWindow(2048); // Default
964 config = vdr->configLoad("Advanced", "Disable WOL");
967 if (!STRCASECMP(config, "Yes"))
969 logger->log("Command", Log::INFO, "Config says disable WOL");
970 Wol::getInstance()->setEnabled(false);
974 logger->log("Command", Log::INFO, "Config says enable WOL");
975 Wol::getInstance()->setEnabled(true);
982 logger->log("Command", Log::INFO, "By default, enable WOL");
983 Wol::getInstance()->setEnabled(true);
985 /* device dependend config */
986 audio->loadOptionsfromServer(vdr);
987 video->loadOptionsfromServer(vdr);
988 remote->loadOptionsfromServer(vdr);
990 video->executePendingModeChanges();
993 // Save power state = on
995 vdr->configSave("General", "Last Power State", "On");
997 // Make sure connection didn't die
998 if (!vdr->isConnected())
1000 Command::getInstance()->connectionLost();
1004 boxstack->remove(vi);
1006 VWelcome* vw = new VWelcome();
1009 boxstack->update(vw);
1011 // Enter pre-keys here
1012 // handleCommand(Remote::OK);
1013 // handleCommand(Remote::THREE);
1014 // handleCommand(Remote::SIX);
1015 // handleCommand(Remote::OK);
1016 // handleCommand(Remote::UP);
1017 // handleCommand(Remote::PLAY);
1018 // handleCommand(Remote::DOWN);
1019 // handleCommand(Remote::DOWN);
1020 // handleCommand(Remote::DOWN);
1021 // handleCommand(Remote::OK);
1022 // handleCommand(Remote::RED);