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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include <linux/errno.h>
28 #include "remotewin.h"
36 #include "vserverselect.h"
42 #include "timerreceiver.h"
45 #include "vvideolive.h"
55 Command* Command::instance = NULL;
73 Command* Command::getInstance()
78 int Command::init(bool tcrashed)
80 if (initted) return 0;
84 logger = Log::getInstance();
85 boxstack = BoxStack::getInstance();
86 remote = Remote::getInstance();
88 remote->InitHWCListwithDefaults();
90 if (!logger || !boxstack || !remote)
96 pthread_mutex_init(&masterLock, NULL);
98 masterLock=CreateMutex(NULL,FALSE,NULL);
104 int Command::shutdown()
106 if (!initted) return 0;
113 // VDR::getInstance()->cancelFindingServer();
118 void Command::doWallpaper()
120 Video* video = Video::getInstance();
123 Boxx* bbg = new Boxx();
124 bbg->setSize(video->getScreenWidth(), video->getScreenHeight());
126 bbg->fillColour(Colour::VIDEOBLUE);
128 boxstack->update(bbg);
129 boxstack->remove(bbg);
132 WJpeg* wallpaperj = new WJpeg();
133 wallpaperj->setSize(video->getScreenWidth(), video->getScreenHeight());
134 wallpaperj->createBuffer();
136 if (video->getFormat() == Video::PAL)
138 logger->log("Command", Log::DEBUG, "PAL wallpaper selected");
139 wallpaperj->init("/wallpaperPAL.jpg");
143 logger->log("Command", Log::DEBUG, "NTSC wallpaper selected");
144 wallpaperj->init("/wallpaperNTSC.jpg");
148 boxstack->add(wallpaperj);
149 boxstack->update(wallpaperj);
151 wallpaper = wallpaperj;
156 if (!initted) return;
163 Video::getInstance()->signalOn();
164 Led::getInstance()->on();
168 // End of startup. Lock the mutex and put the first view up
169 // logger->log("Command", Log::DEBUG, "WANT LOCK");
171 pthread_mutex_lock(&masterLock);
173 WaitForSingleObject(masterLock, INFINITE );
175 //logger->log("Command", Log::DEBUG, "LOCKED");
183 VConnect* vconnect = new VConnect();
184 boxstack->add(vconnect);
188 // Start method 2 of getting commands in...
195 //logger->log("Command", Log::DEBUG, "UNLOCK");
197 pthread_mutex_unlock(&masterLock);
199 ReleaseMutex(masterLock);
201 button = remote->getButtonPress(2); // FIXME why is this set to 2 and not 0? so it can quit
202 // something happened, lock and process
204 // logger->log("Command", Log::DEBUG, "WANT LOCK");
206 pthread_mutex_lock(&masterLock);
208 WaitForSingleObject(masterLock, INFINITE );
210 // logger->log("Command", Log::DEBUG, "LOCK");
212 if ((button == Remote::NA_NONE) /*|| (button == Remote::NA_UNKNOWN)*/) continue;
214 if (button != Remote::NA_SIGNAL) handleCommand(button);
215 processMessageQueue();
218 //logger->log("Command", Log::DEBUG, "UNLOCK");
220 pthread_mutex_unlock(&masterLock);
222 ReleaseMutex(masterLock);
226 void Command::postMessage(Message* m)
228 // This is locked here in case the main loop is not waiting for an event, but is processing one
229 // it could be killed but then not react to it because the signal wouldn't cause
230 // remote->getButtonPress to break
231 // locking the mutex ensures that the master thread is waiting on getButtonPress
234 //logger->log("Command", Log::DEBUG, "WANT LOCK");
236 pthread_mutex_lock(&masterLock);
238 WaitForSingleObject(masterLock, INFINITE );
240 //logger->log("Command", Log::DEBUG, "LOCK");
241 MessageQueue::postMessage(m);
244 kill(mainPid, SIGURG);
245 pthread_mutex_unlock(&masterLock);
247 ((RemoteWin*)Remote::getInstance())->Signal();
248 ReleaseMutex(masterLock);
250 //logger->log("Command", Log::DEBUG, "UNLOCK");
253 void Command::postMessageNoLock(Message* m)
255 // As above but use this one if this message is being posted because of a button press
256 // the mutex is already locked, locking around postMessage is not needed as the
257 // queue is guaranteed to be run when the button has been processed
258 MessageQueue::postMessage(m);
261 bool Command::postMessageIfNotBusy(Message* m)
263 // Used for Windows mouse events
265 //logger->log("Command", Log::DEBUG, "TRY LOCK");
267 if (pthread_mutex_trylock(&masterLock) != EBUSY)
269 //logger->log("Command", Log::DEBUG, "LOCK");
270 MessageQueue::postMessage(m);
271 kill(mainPid, SIGURG);
272 pthread_mutex_unlock(&masterLock);
273 //logger->log("Command", Log::DEBUG, "UNLOCK");
281 switch (WaitForSingleObject(masterLock, 0 ))
282 { //FIXME this is not "if not busy" check
283 case WAIT_OBJECT_0: //but with proper argument 0 this did not work
284 // case WAIT_ABANDONED:
285 MessageQueue::postMessage(m);
286 ((RemoteWin*)Remote::getInstance())->Signal();
287 ReleaseMutex(masterLock);
290 case WAIT_ABANDONED: return false;
291 case WAIT_TIMEOUT: return false;
297 void Command::postMessageFromOuterSpace(Message* m)
300 Yet another way of getting messages into Command. This one is for events that
301 are not standard button presses (or UDP generated buttons). It is also not for
302 events that are generated as a result of other events (events that can safely
303 call postMessageNoLock and be guaranteed that the message will be processed
304 because it is known that the queue is currently being processed).
305 This is for events that come from outer space and can occur when the master
306 mutex is locked or not, they need to be queued and executed but it doesn't
308 Actually so far it is for events caused by the video stream - aspect ratio
309 changes. These can occur when the master mutex is locked and so postMessage
310 doesn't work. postMessageNoLock doesn't work because if the mutex *isn't*
311 locked at the time then the message could be sat around a while before
313 The whole message system was at first supposed to prevent the problem of
314 calling a function on an object that had just been deleted, by ordering
315 messages such that all calls are done before object deletion. However,
316 because of the new centralised messaging system and the fact that BoxStack
317 locates the destination object before calling it, the messaging system now
318 allows the kind of sloppy calls it was supposed to stop. Weird huh. This
319 is mentioned here because the video stream might generate an event just as
320 the user hits stop. The mutex is locked, and by the time the message
321 is examined the vvideorec/live has been deleted. This doesn't matter because
322 boxstack will drop the message if it can't find the matching object to
324 Finally, all this is fine and dandy, except that I'm not 100% sure that
325 this sloppy postMessage and hope a queued signal will force it to be processed
326 thingy will actually work. Hmmm.
327 Lastly <g>, I will consider making the naming system a little more sane
331 logger->log("Command", Log::DEBUG, "PMFOS called");
332 MessageQueue::postMessage(m);
335 kill(mainPid, SIGURG);
337 ((RemoteWin*)Remote::getInstance())->Signal();
341 void Command::processMessage(Message* m)
343 // FIXME - a slight modification - how if messagereceivers were to register
344 // themselves as receivers to avoid the calling-a-deleted-object problem
345 // then only deliver/register/unregister would have to be protected
347 logger->log("Command", Log::DEBUG, "processing message %i", m->message);
354 case Message::STANDBY:
362 case Message::STOP_PLAYBACK:
364 handleCommand(Remote::STOP); // an odd way of doing it, but so simple
367 case Message::STREAM_END:
369 VVideoLive::getInstance()->streamEnd();
373 // Also connection_lost comes from player - anywhere else?
377 case Message::VDR_CONNECTED:
379 doJustConnected((VConnect*)m->from);
382 case Message::SCREENSHOT:
384 Osd::getInstance()->screenShot("/out.jpg");
387 case Message::CONNECTION_LOST:
392 case Message::UDP_BUTTON:
394 handleCommand(m->parameter);
397 case Message::CHANGE_LANGUAGE:
399 boxstack->removeAll();
400 boxstack->update(wallpaper);
402 VWelcome* vw = new VWelcome();
405 boxstack->update(vw);
408 case Message::LAST_VIEW_CLOSE:
410 // Shouldn't be done like this. Some generic message pass back from vinfo perhaps
417 // VWelcome* vw = new VWelcome();
419 // boxstack->add(vw);
420 // boxstack->update(vw);
428 logger->log("Command", Log::DEBUG, "Sending message to boxstack");
429 boxstack->processMessage(m);
433 void Command::handleCommand(int button)
435 if (isStandby && (button != Remote::POWER)) return;
436 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
438 // command was not handled
442 case Remote::DF_LEFT:
443 case Remote::DF_RIGHT:
444 case Remote::VOLUMEUP:
445 case Remote::VOLUMEDOWN:
447 VVolume* v = new VVolume();
449 v->handleCommand(button); // this will draw+show
454 VMute* v = new VMute();
468 if (!connLost) return; // if connLost, handle Remote::OK
478 Message* m = new Message(); // break into master mutex
479 m->message = Message::SCREENSHOT;
485 void Command::doStandby()
489 Video::getInstance()->signalOn();
490 Led::getInstance()->on();
494 VConnect* vconnect = new VConnect();
495 boxstack->add(vconnect);
500 boxstack->removeAll();
501 Video::getInstance()->signalOff();
502 boxstack->update(wallpaper);
504 VDR::getInstance()->configSave("General", "Last Power State", "Off");
505 VDR::getInstance()->disconnect();
506 Led::getInstance()->off();
509 stop(); //different behavoiur on windows, we exit
514 void Command::doFromTheTop(bool which)
518 connLost = new VInfo();
519 connLost->setSize(360, 200);
520 connLost->createBuffer();
521 if (Video::getInstance()->getFormat() == Video::PAL)
522 connLost->setPosition(190, 170);
524 connLost->setPosition(180, 120);
525 connLost->setOneLiner(tr("Connection lost"));
526 connLost->setDropThrough();
527 connLost->setBorderOn(1);
528 connLost->setTitleBarColour(Colour::DANGER);
529 connLost->okButton();
531 boxstack->add(connLost);
532 boxstack->update(connLost);
533 remote->clearBuffer();
537 VDR::getInstance()->disconnect();
538 boxstack->removeAll();
539 boxstack->update(wallpaper);
541 VConnect* vconnect = new VConnect();
542 boxstack->add(vconnect);
547 void Command::doReboot()
549 VDR::getInstance()->disconnect();
551 logger->log("Command", Log::NOTICE, "Reboot");
553 reboot(LINUX_REBOOT_CMD_RESTART);
554 #endif //Would we support this on windows?
557 void Command::connectionLost()
559 Message* m = new Message(); // break into master mutex
560 m->message = Message::CONNECTION_LOST;
562 postMessageNoLock(m);
565 void Command::buildCrashedBox()
567 VInfo* crash = new VInfo();
568 crash->setSize(360, 250);
569 crash->createBuffer();
570 if (Video::getInstance()->getFormat() == Video::PAL)
571 crash->setPosition(190, 146);
573 crash->setPosition(180, 96);
574 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.");
575 crash->setBorderOn(1);
576 crash->setTitleBarColour(Colour::DANGER);
578 crash->setExitable();
580 boxstack->add(crash);
581 boxstack->update(crash);
584 void Command::doJustConnected(VConnect* vconnect)
587 Video* video = Video::getInstance();
588 Audio* audio = Audio::getInstance();
589 boxstack->remove(vconnect);
591 VInfo* vi = new VInfo();
592 vi->setSize(400, 200);
594 if (video->getFormat() == Video::PAL)
595 vi->setPosition(170, 200);
597 vi->setPosition(160, 150);
598 vi->setOneLiner(tr("Connected, loading config"));
601 boxstack->update(vi);
603 VDR* vdr = VDR::getInstance();
606 // See if config says to override video format (PAL/NTSC)
607 config = vdr->configLoad("General", "Override Video Format");
610 logger->log("Command", Log::DEBUG, "Override Video Format is present");
612 if ( (!strcmp(config, "PAL") && (video->getFormat() == Video::NTSC))
613 || (!strcmp(config, "NTSC") && (video->getFormat() == Video::PAL)) )
615 // Oh sheesh, need to switch format. Bye bye TV...
617 // Take everything down
618 boxstack->removeAll();
619 boxstack->remove(wallpaper);
620 Osd* osd = Osd::getInstance();
624 // Get video and osd back up with the new mode
625 if (!strcmp(config, "PAL"))
627 logger->log("Command", Log::DEBUG, "Switching to PAL");
628 video->init(Video::PAL);
630 else if (!strcmp(config, "NTSC"))
632 logger->log("Command", Log::DEBUG, "Switching to NTSC");
633 video->init(Video::NTSC);
635 osd->init((char*)("/dev/stbgfx"));
637 // Put the wallpaper back
642 vi->setSize(400, 200);
644 if (video->getFormat() == Video::PAL)
645 vi->setPosition(170, 200);
647 vi->setPosition(160, 150);
649 vi->setOneLiner(tr("Connected, loading config"));
652 boxstack->update(vi);
656 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
661 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
664 // Power off if first boot and config says so
669 logger->log("Command", Log::DEBUG, "Load power after boot");
671 config = vdr->configLoad("General", "Power After Boot");
675 if (!STRCASECMP(config, "On"))
677 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
679 else if (!STRCASECMP(config, "Off"))
681 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
686 else if (!STRCASECMP(config, "Last state"))
688 char* lastPowerState = vdr->configLoad("General", "Last Power State");
691 if (!STRCASECMP(lastPowerState, "On"))
693 logger->log("Command", Log::INFO, "Config says Last Power State = On");
695 else if (!STRCASECMP(lastPowerState, "Off"))
697 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
704 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
709 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
714 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
720 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
725 // Go S-Video if config says so
727 config = vdr->configLoad("TV", "Connection");
731 if (!STRCASECMP(config, "S-Video"))
733 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
734 video->setConnection(Video::SVIDEO);
738 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
739 video->setConnection(Video::COMPOSITERGB);
745 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
750 config = vdr->configLoad("General", "Remote type");
754 if (!STRCASECMP(config, "New"))
756 logger->log("Command", Log::INFO, "Switching to New remote type");
757 remote->setRemoteType(Remote::NEWREMOTE);
761 logger->log("Command", Log::INFO, "Switching to Old remote type");
762 remote->setRemoteType(Remote::OLDREMOTE);
768 logger->log("Command", Log::INFO, "Config General/Remote type not found");
769 remote->setRemoteType(Remote::OLDREMOTE);
775 // Get TV aspect ratio
777 config = vdr->configLoad("TV", "Aspect");
780 if (!STRCASECMP(config, "16:9"))
782 logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
783 video->setTVsize(Video::ASPECT16X9);
787 logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
788 video->setTVsize(Video::ASPECT4X3);
794 logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
795 video->setTVsize(Video::ASPECT4X3);
798 config = vdr->configLoad("TV", "Widemode");
801 if (!STRCASECMP(config, "Letterbox"))
803 logger->log("Command", Log::INFO, "Setting letterbox mode");
804 video->setMode(Video::LETTERBOX);
808 logger->log("Command", Log::INFO, "Setting chop-sides mode");
809 video->setMode(Video::NORMAL);
815 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
816 video->setMode(Video::NORMAL);
819 config = vdr->configLoad("Advanced", "TCP receive window");
822 size_t newTCPsize = atoi(config);
825 logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
826 vdr->setReceiveWindow(newTCPsize);
830 logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
831 vdr->setReceiveWindow(2048); // Default
834 config = vdr->configLoad("Advanced", "Disable WOL");
837 if (!STRCASECMP(config, "Yes"))
839 logger->log("Command", Log::INFO, "Config says disable WOL");
840 Wol::getInstance()->setEnabled(false);
844 logger->log("Command", Log::INFO, "Config says enable WOL");
845 Wol::getInstance()->setEnabled(true);
852 logger->log("Command", Log::INFO, "By default, enable WOL");
853 Wol::getInstance()->setEnabled(true);
855 /* device dependend config */
856 audio->loadOptionsfromServer(vdr);
857 video->loadOptionsfromServer(vdr);
858 remote->loadOptionsfromServer(vdr);
861 // Save power state = on
863 vdr->configSave("General", "Last Power State", "On");
865 // Make sure connection didn't die
866 if (!vdr->isConnected())
868 Command::getInstance()->connectionLost();
872 boxstack->remove(vi);
874 VWelcome* vw = new VWelcome();
877 boxstack->update(vw);
879 // Enter pre-keys here
880 // handleCommand(Remote::SIX);
881 // handleCommand(Remote::UP);
882 // handleCommand(Remote::PLAY);
883 // handleCommand(Remote::DOWN);
884 // handleCommand(Remote::DOWN);
885 // handleCommand(Remote::DOWN);
886 // handleCommand(Remote::OK);
887 // handleCommand(Remote::OK);
888 // handleCommand(Remote::RED);