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"
36 #include "vserverselect.h"
42 #include "timerreceiver.h"
52 #include "vsleeptimer.h"
55 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 remote = Remote::getInstance();
90 remote->InitHWCListwithDefaults();
92 if (!logger || !boxstack || !remote)
98 pthread_mutex_init(&masterLock, NULL);
100 masterLock=CreateMutex(NULL,FALSE,NULL);
106 int Command::shutdown()
108 if (!initted) return 0;
115 // VDR::getInstance()->cancelFindingServer();
116 logger->log("Command", Log::NOTICE, "Command stop1...");
119 logger->log("Command", Log::NOTICE, "Command stop2...");
123 void Command::doWallpaper()
125 Video* video = Video::getInstance();
128 Boxx* bbg = new Boxx();
129 bbg->setSize(video->getScreenWidth(), video->getScreenHeight());
131 bbg->fillColour(Colour::VIDEOBLUE);
133 boxstack->update(bbg);
134 boxstack->remove(bbg);
137 WJpeg* wallpaperj = new WJpeg();
138 wallpaperj->setSize(video->getScreenWidth(), video->getScreenHeight());
139 wallpaperj->createBuffer();
141 if (video->getFormat() == Video::PAL)
143 logger->log("Command", Log::DEBUG, "PAL wallpaper selected");
145 wallpaperj->init("/wallpaperPAL.jpg");
147 wallpaperj->init("wallpaperPAL.jpg");
152 logger->log("Command", Log::DEBUG, "NTSC wallpaper selected");
153 wallpaperj->init("/wallpaperNTSC.jpg");
157 boxstack->add(wallpaperj);
158 boxstack->update(wallpaperj);
160 wallpaper = wallpaperj;
165 if (!initted) return;
172 Video::getInstance()->signalOn();
173 Led::getInstance()->on();
177 // End of startup. Lock the mutex and put the first view up
178 // logger->log("Command", Log::DEBUG, "WANT LOCK");
180 pthread_mutex_lock(&masterLock);
182 WaitForSingleObject(masterLock, INFINITE );
184 //logger->log("Command", Log::DEBUG, "LOCKED");
192 VConnect* vconnect = new VConnect(server);
193 boxstack->add(vconnect);
197 // Start method 2 of getting commands in...
204 //logger->log("Command", Log::DEBUG, "UNLOCK");
206 pthread_mutex_unlock(&masterLock);
208 ReleaseMutex(masterLock);
211 button = remote->getButtonPress(2); // FIXME why is this set to 2 and not 0? so it can quit
212 // something happened, lock and process
214 // logger->log("Command", Log::DEBUG, "WANT LOCK");
216 pthread_mutex_lock(&masterLock);
218 WaitForSingleObject(masterLock, INFINITE );
220 // logger->log("Command", Log::DEBUG, "LOCK");
222 if ((button == Remote::NA_NONE) /*|| (button == Remote::NA_UNKNOWN)*/) continue;
224 if (button != Remote::NA_SIGNAL) handleCommand(button);
225 processMessageQueue();
228 //logger->log("Command", Log::DEBUG, "UNLOCK");
230 pthread_mutex_unlock(&masterLock);
232 ReleaseMutex(masterLock);
237 void Command::postMessage(Message* m)
239 // This is locked here in case the main loop is not waiting for an event, but is processing one
240 // it could be killed but then not react to it because the signal wouldn't cause
241 // remote->getButtonPress to break
242 // locking the mutex ensures that the master thread is waiting on getButtonPress
245 //logger->log("Command", Log::DEBUG, "WANT LOCK");
247 pthread_mutex_lock(&masterLock);
249 WaitForSingleObject(masterLock, INFINITE );
251 //logger->log("Command", Log::DEBUG, "LOCK");
252 MessageQueue::postMessage(m);
255 kill(mainPid, SIGURG);
256 pthread_mutex_unlock(&masterLock);
258 ((RemoteWin*)Remote::getInstance())->Signal();
259 ReleaseMutex(masterLock);
261 //logger->log("Command", Log::DEBUG, "UNLOCK");
264 void Command::postMessageNoLock(Message* m)
266 // As above but use this one if this message is being posted because of a button press
267 // the mutex is already locked, locking around postMessage is not needed as the
268 // queue is guaranteed to be run when the button has been processed
269 MessageQueue::postMessage(m);
272 bool Command::postMessageIfNotBusy(Message* m)
274 // Used for Windows mouse events
276 //logger->log("Command", Log::DEBUG, "TRY LOCK");
278 if (pthread_mutex_trylock(&masterLock) != EBUSY)
280 //logger->log("Command", Log::DEBUG, "LOCK");
281 MessageQueue::postMessage(m);
282 kill(mainPid, SIGURG);
283 pthread_mutex_unlock(&masterLock);
284 //logger->log("Command", Log::DEBUG, "UNLOCK");
292 switch (WaitForSingleObject(masterLock, 0 ))
293 { //FIXME this is not "if not busy" check
294 case WAIT_OBJECT_0: //but with proper argument 0 this did not work
295 // case WAIT_ABANDONED:
296 MessageQueue::postMessage(m);
297 ((RemoteWin*)Remote::getInstance())->Signal();
298 ReleaseMutex(masterLock);
301 case WAIT_ABANDONED: return false;
302 case WAIT_TIMEOUT: return false;
308 void Command::postMessageFromOuterSpace(Message* m)
311 Yet another way of getting messages into Command. This one is for events that
312 are not standard button presses (or UDP generated buttons). It is also not for
313 events that are generated as a result of other events (events that can safely
314 call postMessageNoLock and be guaranteed that the message will be processed
315 because it is known that the queue is currently being processed).
316 This is for events that come from outer space and can occur when the master
317 mutex is locked or not, they need to be queued and executed but it doesn't
319 Actually so far it is for events caused by the video stream - aspect ratio
320 changes. These can occur when the master mutex is locked and so postMessage
321 doesn't work. postMessageNoLock doesn't work because if the mutex *isn't*
322 locked at the time then the message could be sat around a while before
324 The whole message system was at first supposed to prevent the problem of
325 calling a function on an object that had just been deleted, by ordering
326 messages such that all calls are done before object deletion. However,
327 because of the new centralised messaging system and the fact that BoxStack
328 locates the destination object before calling it, the messaging system now
329 allows the kind of sloppy calls it was supposed to stop. Weird huh. This
330 is mentioned here because the video stream might generate an event just as
331 the user hits stop. The mutex is locked, and by the time the message
332 is examined the vvideorec/live has been deleted. This doesn't matter because
333 boxstack will drop the message if it can't find the matching object to
335 Finally, all this is fine and dandy, except that I'm not 100% sure that
336 this sloppy postMessage and hope a queued signal will force it to be processed
337 thingy will actually work. Hmmm.
338 Lastly <g>, I will consider making the naming system a little more sane
342 logger->log("Command", Log::DEBUG, "PMFOS called");
343 MessageQueue::postMessage(m);
346 kill(mainPid, SIGURG);
348 ((RemoteWin*)Remote::getInstance())->Signal();
352 void Command::processMessage(Message* m)
354 // FIXME - a slight modification - how if messagereceivers were to register
355 // themselves as receivers to avoid the calling-a-deleted-object problem
356 // then only deliver/register/unregister would have to be protected
358 logger->log("Command", Log::DEBUG, "processing message %i", m->message);
366 case Message::STOP_PLAYBACK:
368 handleCommand(Remote::STOP); // an odd way of doing it, but so simple
371 // Also connection_lost comes from player - anywhere else?
375 case Message::VDR_CONNECTED:
377 doJustConnected((VConnect*)m->from);
380 case Message::SCREENSHOT:
382 Osd::getInstance()->screenShot("/out.jpg");
385 case Message::CONNECTION_LOST:
390 case Message::UDP_BUTTON:
392 handleCommand(m->parameter);
395 case Message::CHANGE_LANGUAGE:
397 boxstack->removeAll();
398 boxstack->update(wallpaper);
400 if (!VDR::getInstance()->isConnected()) { connectionLost(); break; }
401 VWelcome* vw = new VWelcome();
404 boxstack->update(vw);
407 case Message::LAST_VIEW_CLOSE:
409 // Shouldn't be done like this. Some generic message pass back from vinfo perhaps
416 // VWelcome* vw = new VWelcome();
418 // boxstack->add(vw);
419 // boxstack->update(vw);
429 Instead of sending through the boxstack, implement a more generic MessageReceiver interface
430 and have potential receivers register with something
431 When a message needs to be delivered, check if the receiver is still registered, if so, deliver the message
432 This could all be done using the existing big command mutex to keep it simple
435 logger->log("Command", Log::DEBUG, "Sending message to boxstack");
436 boxstack->processMessage(m);
440 void Command::handleCommand(int button)
442 if (isStandby && (button != Remote::POWER)) return;
443 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
445 // command was not handled
449 case Remote::DF_LEFT:
450 case Remote::DF_RIGHT:
451 case Remote::VOLUMEUP:
452 case Remote::VOLUMEDOWN:
454 VVolume* v = new VVolume();
456 v->handleCommand(button); // this will draw+show
461 VMute* v = new VMute();
475 if (!connLost) return; // if connLost, handle Remote::OK
481 VSleeptimer* sleep = new VSleeptimer();
482 boxstack->add(sleep);
483 sleep->handleCommand(button); // this will draw+show
492 Message* m = new Message(); // break into master mutex
493 m->message = Message::SCREENSHOT;
499 void Command::doStandby()
503 Video::getInstance()->signalOn();
504 Led::getInstance()->on();
508 VConnect* vconnect = new VConnect(server);
509 boxstack->add(vconnect);
514 boxstack->removeAll();
515 Video::getInstance()->signalOff();
516 boxstack->update(wallpaper);
518 VDR::getInstance()->configSave("General", "Last Power State", "Off");
520 VDR::getInstance()->disconnect();
521 Led::getInstance()->off();
523 Sleeptimer::getInstance()->shutdown();
525 stop(); //different behavoiur on windows, we exit
530 void Command::doFromTheTop(bool which)
536 logger->log("Command", Log::NOTICE, "Connection lost dialog already present");
540 logger->log("Command", Log::NOTICE, "Doing connection lost dialog");
541 connLost = new VInfo();
542 connLost->setSize(360, 200);
543 connLost->createBuffer();
544 if (Video::getInstance()->getFormat() == Video::PAL)
545 connLost->setPosition(190, 170);
547 connLost->setPosition(180, 120);
548 connLost->setOneLiner(tr("Connection lost"));
549 connLost->setDropThrough();
550 connLost->setBorderOn(1);
551 connLost->setTitleBarColour(Colour::DANGER);
552 connLost->okButton();
554 boxstack->add(connLost);
555 boxstack->update(connLost);
556 remote->clearBuffer();
561 VDR::getInstance()->disconnect();
562 boxstack->removeAll();
563 boxstack->update(wallpaper);
567 remote->clearBuffer();
569 // at this point, everything should be reset to first-go
571 VConnect* vconnect = new VConnect(server);
572 boxstack->add(vconnect);
577 void Command::doReboot()
580 VDR::getInstance()->disconnect();
582 logger->log("Command", Log::NOTICE, "Reboot");
585 reboot(LINUX_REBOOT_CMD_RESTART);
589 #endif //Would we support this on windows?
592 void Command::connectionLost()
595 Message* m = new Message(); // break into master mutex
596 m->message = Message::CONNECTION_LOST;
598 postMessageFromOuterSpace(m);
601 void Command::buildCrashedBox()
603 VInfo* crash = new VInfo();
604 crash->setSize(360, 250);
605 crash->createBuffer();
606 if (Video::getInstance()->getFormat() == Video::PAL)
607 crash->setPosition(190, 146);
609 crash->setPosition(180, 96);
610 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.");
611 crash->setBorderOn(1);
612 crash->setTitleBarColour(Colour::DANGER);
614 crash->setExitable();
616 boxstack->add(crash);
617 boxstack->update(crash);
620 void Command::doJustConnected(VConnect* vconnect)
623 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
625 Video* video = Video::getInstance();
626 Audio* audio = Audio::getInstance();
627 boxstack->remove(vconnect);
629 VInfo* vi = new VInfo();
630 vi->setSize(400, 200);
632 if (video->getFormat() == Video::PAL)
633 vi->setPosition(170, 200);
635 vi->setPosition(160, 150);
636 vi->setOneLiner(tr("Connected, loading config"));
639 boxstack->update(vi);
641 VDR* vdr = VDR::getInstance();
644 // See if we're supposed to do network logging
645 config = vdr->configLoad("Advanced", "Network logging");
646 if (config && !STRCASECMP(config, "On"))
648 logger->log("Command", Log::INFO, "Turning on network logging");
654 logger->log("Command", Log::INFO, "Turned off network logging");
656 if (config) delete[] config;
658 // See if config says to override video format (PAL/NTSC)
659 config = vdr->configLoad("General", "Override Video Format");
662 logger->log("Command", Log::DEBUG, "Override Video Format is present");
664 if ( (!strcmp(config, "PAL") && (video->getFormat() == Video::NTSC))
665 || (!strcmp(config, "NTSC") && (video->getFormat() == Video::PAL)) )
667 // Oh sheesh, need to switch format. Bye bye TV...
669 // Take everything down
670 boxstack->removeAll();
671 boxstack->remove(wallpaper);
672 Osd* osd = Osd::getInstance();
676 // Get video and osd back up with the new mode
677 if (!strcmp(config, "PAL"))
679 logger->log("Command", Log::DEBUG, "Switching to PAL");
680 video->init(Video::PAL);
682 else if (!strcmp(config, "NTSC"))
684 logger->log("Command", Log::DEBUG, "Switching to NTSC");
685 video->init(Video::NTSC);
687 osd->init((char*)("/dev/stbgfx"));
689 // Put the wallpaper back
694 vi->setSize(400, 200);
696 if (video->getFormat() == Video::PAL)
697 vi->setPosition(170, 200);
699 vi->setPosition(160, 150);
701 vi->setOneLiner(tr("Connected, loading config"));
704 boxstack->update(vi);
708 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
713 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
716 // Power off if first boot and config says so
721 logger->log("Command", Log::DEBUG, "Load power after boot");
723 config = vdr->configLoad("General", "Power After Boot");
727 if (!STRCASECMP(config, "On"))
729 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
731 else if (!STRCASECMP(config, "Off"))
733 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
738 else if (!STRCASECMP(config, "Last state"))
740 char* lastPowerState = vdr->configLoad("General", "Last Power State");
743 if (!STRCASECMP(lastPowerState, "On"))
745 logger->log("Command", Log::INFO, "Config says Last Power State = On");
747 else if (!STRCASECMP(lastPowerState, "Off"))
749 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
756 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
761 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
766 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
772 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
777 // Go S-Video if config says so
779 config = vdr->configLoad("TV", "Connection");
783 if (!STRCASECMP(config, "S-Video"))
785 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
786 video->setConnection(Video::SVIDEO);
790 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
791 video->setConnection(Video::COMPOSITERGB);
797 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
802 config = vdr->configLoad("General", "Remote type");
806 if (!STRCASECMP(config, "New"))
808 logger->log("Command", Log::INFO, "Switching to New remote type");
809 remote->setRemoteType(Remote::NEWREMOTE);
813 logger->log("Command", Log::INFO, "Switching to Old remote type");
814 remote->setRemoteType(Remote::OLDREMOTE);
820 logger->log("Command", Log::INFO, "Config General/Remote type not found");
821 remote->setRemoteType(Remote::OLDREMOTE);
827 // Get TV aspect ratio
829 config = vdr->configLoad("TV", "Aspect");
832 if (!STRCASECMP(config, "16:9"))
834 logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
835 video->setTVsize(Video::ASPECT16X9);
839 logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
840 video->setTVsize(Video::ASPECT4X3);
846 logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
847 video->setTVsize(Video::ASPECT4X3);
850 config = vdr->configLoad("TV", "Widemode");
853 if (!STRCASECMP(config, "Letterbox"))
855 logger->log("Command", Log::INFO, "Setting letterbox mode");
856 video->setMode(Video::LETTERBOX);
860 logger->log("Command", Log::INFO, "Setting chop-sides mode");
861 video->setMode(Video::NORMAL);
867 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
868 video->setMode(Video::NORMAL);
871 config = vdr->configLoad("Advanced", "TCP receive window");
874 size_t newTCPsize = atoi(config);
877 logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
878 vdr->setReceiveWindow(newTCPsize);
882 logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
883 vdr->setReceiveWindow(2048); // Default
886 config = vdr->configLoad("Advanced", "Disable WOL");
889 if (!STRCASECMP(config, "Yes"))
891 logger->log("Command", Log::INFO, "Config says disable WOL");
892 Wol::getInstance()->setEnabled(false);
896 logger->log("Command", Log::INFO, "Config says enable WOL");
897 Wol::getInstance()->setEnabled(true);
904 logger->log("Command", Log::INFO, "By default, enable WOL");
905 Wol::getInstance()->setEnabled(true);
907 /* device dependend config */
908 audio->loadOptionsfromServer(vdr);
909 video->loadOptionsfromServer(vdr);
910 remote->loadOptionsfromServer(vdr);
913 // Save power state = on
915 vdr->configSave("General", "Last Power State", "On");
917 // Make sure connection didn't die
918 if (!vdr->isConnected())
920 Command::getInstance()->connectionLost();
924 boxstack->remove(vi);
926 VWelcome* vw = new VWelcome();
929 boxstack->update(vw);
931 // Enter pre-keys here
932 // handleCommand(Remote::OK);
933 // handleCommand(Remote::THREE);
934 // handleCommand(Remote::SIX);
935 // handleCommand(Remote::OK);
936 // handleCommand(Remote::UP);
937 // handleCommand(Remote::PLAY);
938 // handleCommand(Remote::DOWN);
939 // handleCommand(Remote::DOWN);
940 // handleCommand(Remote::DOWN);
941 // handleCommand(Remote::OK);
942 // handleCommand(Remote::RED);