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();
120 void Command::doWallpaper()
122 Video* video = Video::getInstance();
125 Boxx* bbg = new Boxx();
126 bbg->setSize(video->getScreenWidth(), video->getScreenHeight());
128 bbg->fillColour(Colour::VIDEOBLUE);
130 boxstack->update(bbg);
131 boxstack->remove(bbg);
134 WJpeg* wallpaperj = new WJpeg();
135 wallpaperj->setSize(video->getScreenWidth(), video->getScreenHeight());
136 wallpaperj->createBuffer();
138 if (video->getFormat() == Video::PAL)
140 logger->log("Command", Log::DEBUG, "PAL wallpaper selected");
141 wallpaperj->init("/wallpaperPAL.jpg");
145 logger->log("Command", Log::DEBUG, "NTSC wallpaper selected");
146 wallpaperj->init("/wallpaperNTSC.jpg");
150 boxstack->add(wallpaperj);
151 boxstack->update(wallpaperj);
153 wallpaper = wallpaperj;
158 if (!initted) return;
165 Video::getInstance()->signalOn();
166 Led::getInstance()->on();
170 // End of startup. Lock the mutex and put the first view up
171 // logger->log("Command", Log::DEBUG, "WANT LOCK");
173 pthread_mutex_lock(&masterLock);
175 WaitForSingleObject(masterLock, INFINITE );
177 //logger->log("Command", Log::DEBUG, "LOCKED");
185 VConnect* vconnect = new VConnect(server);
186 boxstack->add(vconnect);
190 // Start method 2 of getting commands in...
197 //logger->log("Command", Log::DEBUG, "UNLOCK");
199 pthread_mutex_unlock(&masterLock);
201 ReleaseMutex(masterLock);
203 button = remote->getButtonPress(2); // FIXME why is this set to 2 and not 0? so it can quit
204 // something happened, lock and process
206 // logger->log("Command", Log::DEBUG, "WANT LOCK");
208 pthread_mutex_lock(&masterLock);
210 WaitForSingleObject(masterLock, INFINITE );
212 // logger->log("Command", Log::DEBUG, "LOCK");
214 if ((button == Remote::NA_NONE) /*|| (button == Remote::NA_UNKNOWN)*/) continue;
216 if (button != Remote::NA_SIGNAL) handleCommand(button);
217 processMessageQueue();
220 //logger->log("Command", Log::DEBUG, "UNLOCK");
222 pthread_mutex_unlock(&masterLock);
224 ReleaseMutex(masterLock);
228 void Command::postMessage(Message* m)
230 // This is locked here in case the main loop is not waiting for an event, but is processing one
231 // it could be killed but then not react to it because the signal wouldn't cause
232 // remote->getButtonPress to break
233 // locking the mutex ensures that the master thread is waiting on getButtonPress
236 //logger->log("Command", Log::DEBUG, "WANT LOCK");
238 pthread_mutex_lock(&masterLock);
240 WaitForSingleObject(masterLock, INFINITE );
242 //logger->log("Command", Log::DEBUG, "LOCK");
243 MessageQueue::postMessage(m);
246 kill(mainPid, SIGURG);
247 pthread_mutex_unlock(&masterLock);
249 ((RemoteWin*)Remote::getInstance())->Signal();
250 ReleaseMutex(masterLock);
252 //logger->log("Command", Log::DEBUG, "UNLOCK");
255 void Command::postMessageNoLock(Message* m)
257 // As above but use this one if this message is being posted because of a button press
258 // the mutex is already locked, locking around postMessage is not needed as the
259 // queue is guaranteed to be run when the button has been processed
260 MessageQueue::postMessage(m);
263 bool Command::postMessageIfNotBusy(Message* m)
265 // Used for Windows mouse events
267 //logger->log("Command", Log::DEBUG, "TRY LOCK");
269 if (pthread_mutex_trylock(&masterLock) != EBUSY)
271 //logger->log("Command", Log::DEBUG, "LOCK");
272 MessageQueue::postMessage(m);
273 kill(mainPid, SIGURG);
274 pthread_mutex_unlock(&masterLock);
275 //logger->log("Command", Log::DEBUG, "UNLOCK");
283 switch (WaitForSingleObject(masterLock, 0 ))
284 { //FIXME this is not "if not busy" check
285 case WAIT_OBJECT_0: //but with proper argument 0 this did not work
286 // case WAIT_ABANDONED:
287 MessageQueue::postMessage(m);
288 ((RemoteWin*)Remote::getInstance())->Signal();
289 ReleaseMutex(masterLock);
292 case WAIT_ABANDONED: return false;
293 case WAIT_TIMEOUT: return false;
299 void Command::postMessageFromOuterSpace(Message* m)
302 Yet another way of getting messages into Command. This one is for events that
303 are not standard button presses (or UDP generated buttons). It is also not for
304 events that are generated as a result of other events (events that can safely
305 call postMessageNoLock and be guaranteed that the message will be processed
306 because it is known that the queue is currently being processed).
307 This is for events that come from outer space and can occur when the master
308 mutex is locked or not, they need to be queued and executed but it doesn't
310 Actually so far it is for events caused by the video stream - aspect ratio
311 changes. These can occur when the master mutex is locked and so postMessage
312 doesn't work. postMessageNoLock doesn't work because if the mutex *isn't*
313 locked at the time then the message could be sat around a while before
315 The whole message system was at first supposed to prevent the problem of
316 calling a function on an object that had just been deleted, by ordering
317 messages such that all calls are done before object deletion. However,
318 because of the new centralised messaging system and the fact that BoxStack
319 locates the destination object before calling it, the messaging system now
320 allows the kind of sloppy calls it was supposed to stop. Weird huh. This
321 is mentioned here because the video stream might generate an event just as
322 the user hits stop. The mutex is locked, and by the time the message
323 is examined the vvideorec/live has been deleted. This doesn't matter because
324 boxstack will drop the message if it can't find the matching object to
326 Finally, all this is fine and dandy, except that I'm not 100% sure that
327 this sloppy postMessage and hope a queued signal will force it to be processed
328 thingy will actually work. Hmmm.
329 Lastly <g>, I will consider making the naming system a little more sane
333 logger->log("Command", Log::DEBUG, "PMFOS called");
334 MessageQueue::postMessage(m);
337 kill(mainPid, SIGURG);
339 ((RemoteWin*)Remote::getInstance())->Signal();
343 void Command::processMessage(Message* m)
345 // FIXME - a slight modification - how if messagereceivers were to register
346 // themselves as receivers to avoid the calling-a-deleted-object problem
347 // then only deliver/register/unregister would have to be protected
349 logger->log("Command", Log::DEBUG, "processing message %i", m->message);
357 case Message::STOP_PLAYBACK:
359 handleCommand(Remote::STOP); // an odd way of doing it, but so simple
362 // Also connection_lost comes from player - anywhere else?
366 case Message::VDR_CONNECTED:
368 doJustConnected((VConnect*)m->from);
371 case Message::SCREENSHOT:
373 Osd::getInstance()->screenShot("/out.jpg");
376 case Message::CONNECTION_LOST:
381 case Message::UDP_BUTTON:
383 handleCommand(m->parameter);
386 case Message::CHANGE_LANGUAGE:
388 boxstack->removeAll();
389 boxstack->update(wallpaper);
391 if (!VDR::getInstance()->isConnected()) { connectionLost(); break; }
392 VWelcome* vw = new VWelcome();
395 boxstack->update(vw);
398 case Message::LAST_VIEW_CLOSE:
400 // Shouldn't be done like this. Some generic message pass back from vinfo perhaps
407 // VWelcome* vw = new VWelcome();
409 // boxstack->add(vw);
410 // boxstack->update(vw);
420 Instead of sending through the boxstack, implement a more generic MessageReceiver interface
421 and have potential receivers register with something
422 When a message needs to be delivered, check if the receiver is still registered, if so, deliver the message
423 This could all be done using the existing big command mutex to keep it simple
426 logger->log("Command", Log::DEBUG, "Sending message to boxstack");
427 boxstack->processMessage(m);
431 void Command::handleCommand(int button)
433 if (isStandby && (button != Remote::POWER)) return;
434 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
436 // command was not handled
440 case Remote::DF_LEFT:
441 case Remote::DF_RIGHT:
442 case Remote::VOLUMEUP:
443 case Remote::VOLUMEDOWN:
445 VVolume* v = new VVolume();
447 v->handleCommand(button); // this will draw+show
452 VMute* v = new VMute();
466 if (!connLost) return; // if connLost, handle Remote::OK
472 VSleeptimer* sleep = new VSleeptimer();
473 boxstack->add(sleep);
474 sleep->handleCommand(button); // this will draw+show
483 Message* m = new Message(); // break into master mutex
484 m->message = Message::SCREENSHOT;
490 void Command::doStandby()
494 Video::getInstance()->signalOn();
495 Led::getInstance()->on();
499 VConnect* vconnect = new VConnect(server);
500 boxstack->add(vconnect);
505 boxstack->removeAll();
506 Video::getInstance()->signalOff();
507 boxstack->update(wallpaper);
509 VDR::getInstance()->configSave("General", "Last Power State", "Off");
511 VDR::getInstance()->disconnect();
512 Led::getInstance()->off();
514 Sleeptimer::getInstance()->shutdown();
516 stop(); //different behavoiur on windows, we exit
521 void Command::doFromTheTop(bool which)
527 logger->log("Command", Log::NOTICE, "Connection lost dialog already present");
531 logger->log("Command", Log::NOTICE, "Doing connection lost dialog");
532 connLost = new VInfo();
533 connLost->setSize(360, 200);
534 connLost->createBuffer();
535 if (Video::getInstance()->getFormat() == Video::PAL)
536 connLost->setPosition(190, 170);
538 connLost->setPosition(180, 120);
539 connLost->setOneLiner(tr("Connection lost"));
540 connLost->setDropThrough();
541 connLost->setBorderOn(1);
542 connLost->setTitleBarColour(Colour::DANGER);
543 connLost->okButton();
545 boxstack->add(connLost);
546 boxstack->update(connLost);
547 remote->clearBuffer();
552 VDR::getInstance()->disconnect();
553 boxstack->removeAll();
554 boxstack->update(wallpaper);
558 remote->clearBuffer();
560 // at this point, everything should be reset to first-go
562 VConnect* vconnect = new VConnect(server);
563 boxstack->add(vconnect);
568 void Command::doReboot()
571 VDR::getInstance()->disconnect();
573 logger->log("Command", Log::NOTICE, "Reboot");
575 reboot(LINUX_REBOOT_CMD_RESTART);
576 #endif //Would we support this on windows?
579 void Command::connectionLost()
582 Message* m = new Message(); // break into master mutex
583 m->message = Message::CONNECTION_LOST;
585 postMessageFromOuterSpace(m);
588 void Command::buildCrashedBox()
590 VInfo* crash = new VInfo();
591 crash->setSize(360, 250);
592 crash->createBuffer();
593 if (Video::getInstance()->getFormat() == Video::PAL)
594 crash->setPosition(190, 146);
596 crash->setPosition(180, 96);
597 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.");
598 crash->setBorderOn(1);
599 crash->setTitleBarColour(Colour::DANGER);
601 crash->setExitable();
603 boxstack->add(crash);
604 boxstack->update(crash);
607 void Command::doJustConnected(VConnect* vconnect)
610 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
612 Video* video = Video::getInstance();
613 Audio* audio = Audio::getInstance();
614 boxstack->remove(vconnect);
616 VInfo* vi = new VInfo();
617 vi->setSize(400, 200);
619 if (video->getFormat() == Video::PAL)
620 vi->setPosition(170, 200);
622 vi->setPosition(160, 150);
623 vi->setOneLiner(tr("Connected, loading config"));
626 boxstack->update(vi);
628 VDR* vdr = VDR::getInstance();
631 // See if we're supposed to do network logging
632 config = vdr->configLoad("Advanced", "Network logging");
633 if (config && !STRCASECMP(config, "On"))
635 logger->log("Command", Log::INFO, "Turning on network logging");
641 logger->log("Command", Log::INFO, "Turned off network logging");
643 if (config) delete[] config;
645 // See if config says to override video format (PAL/NTSC)
646 config = vdr->configLoad("General", "Override Video Format");
649 logger->log("Command", Log::DEBUG, "Override Video Format is present");
651 if ( (!strcmp(config, "PAL") && (video->getFormat() == Video::NTSC))
652 || (!strcmp(config, "NTSC") && (video->getFormat() == Video::PAL)) )
654 // Oh sheesh, need to switch format. Bye bye TV...
656 // Take everything down
657 boxstack->removeAll();
658 boxstack->remove(wallpaper);
659 Osd* osd = Osd::getInstance();
663 // Get video and osd back up with the new mode
664 if (!strcmp(config, "PAL"))
666 logger->log("Command", Log::DEBUG, "Switching to PAL");
667 video->init(Video::PAL);
669 else if (!strcmp(config, "NTSC"))
671 logger->log("Command", Log::DEBUG, "Switching to NTSC");
672 video->init(Video::NTSC);
674 osd->init((char*)("/dev/stbgfx"));
676 // Put the wallpaper back
681 vi->setSize(400, 200);
683 if (video->getFormat() == Video::PAL)
684 vi->setPosition(170, 200);
686 vi->setPosition(160, 150);
688 vi->setOneLiner(tr("Connected, loading config"));
691 boxstack->update(vi);
695 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
700 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
703 // Power off if first boot and config says so
708 logger->log("Command", Log::DEBUG, "Load power after boot");
710 config = vdr->configLoad("General", "Power After Boot");
714 if (!STRCASECMP(config, "On"))
716 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
718 else if (!STRCASECMP(config, "Off"))
720 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
725 else if (!STRCASECMP(config, "Last state"))
727 char* lastPowerState = vdr->configLoad("General", "Last Power State");
730 if (!STRCASECMP(lastPowerState, "On"))
732 logger->log("Command", Log::INFO, "Config says Last Power State = On");
734 else if (!STRCASECMP(lastPowerState, "Off"))
736 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
743 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
748 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
753 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
759 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
764 // Go S-Video if config says so
766 config = vdr->configLoad("TV", "Connection");
770 if (!STRCASECMP(config, "S-Video"))
772 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
773 video->setConnection(Video::SVIDEO);
777 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
778 video->setConnection(Video::COMPOSITERGB);
784 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
789 config = vdr->configLoad("General", "Remote type");
793 if (!STRCASECMP(config, "New"))
795 logger->log("Command", Log::INFO, "Switching to New remote type");
796 remote->setRemoteType(Remote::NEWREMOTE);
800 logger->log("Command", Log::INFO, "Switching to Old remote type");
801 remote->setRemoteType(Remote::OLDREMOTE);
807 logger->log("Command", Log::INFO, "Config General/Remote type not found");
808 remote->setRemoteType(Remote::OLDREMOTE);
814 // Get TV aspect ratio
816 config = vdr->configLoad("TV", "Aspect");
819 if (!STRCASECMP(config, "16:9"))
821 logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
822 video->setTVsize(Video::ASPECT16X9);
826 logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
827 video->setTVsize(Video::ASPECT4X3);
833 logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
834 video->setTVsize(Video::ASPECT4X3);
837 config = vdr->configLoad("TV", "Widemode");
840 if (!STRCASECMP(config, "Letterbox"))
842 logger->log("Command", Log::INFO, "Setting letterbox mode");
843 video->setMode(Video::LETTERBOX);
847 logger->log("Command", Log::INFO, "Setting chop-sides mode");
848 video->setMode(Video::NORMAL);
854 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
855 video->setMode(Video::NORMAL);
858 config = vdr->configLoad("Advanced", "TCP receive window");
861 size_t newTCPsize = atoi(config);
864 logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
865 vdr->setReceiveWindow(newTCPsize);
869 logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
870 vdr->setReceiveWindow(2048); // Default
873 config = vdr->configLoad("Advanced", "Disable WOL");
876 if (!STRCASECMP(config, "Yes"))
878 logger->log("Command", Log::INFO, "Config says disable WOL");
879 Wol::getInstance()->setEnabled(false);
883 logger->log("Command", Log::INFO, "Config says enable WOL");
884 Wol::getInstance()->setEnabled(true);
891 logger->log("Command", Log::INFO, "By default, enable WOL");
892 Wol::getInstance()->setEnabled(true);
894 /* device dependend config */
895 audio->loadOptionsfromServer(vdr);
896 video->loadOptionsfromServer(vdr);
897 remote->loadOptionsfromServer(vdr);
900 // Save power state = on
902 vdr->configSave("General", "Last Power State", "On");
904 // Make sure connection didn't die
905 if (!vdr->isConnected())
907 Command::getInstance()->connectionLost();
911 boxstack->remove(vi);
913 VWelcome* vw = new VWelcome();
916 boxstack->update(vw);
918 // Enter pre-keys here
919 // handleCommand(Remote::OK);
920 // handleCommand(Remote::THREE);
921 // handleCommand(Remote::SIX);
922 // handleCommand(Remote::OK);
923 // handleCommand(Remote::UP);
924 // handleCommand(Remote::PLAY);
925 // handleCommand(Remote::DOWN);
926 // handleCommand(Remote::DOWN);
927 // handleCommand(Remote::DOWN);
928 // handleCommand(Remote::OK);
929 // handleCommand(Remote::RED);