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;
72 Command* Command::getInstance()
79 if (initted) return 0;
82 logger = Log::getInstance();
83 boxstack = BoxStack::getInstance();
84 remote = Remote::getInstance();
86 remote->InitHWCListwithDefaults();
88 if (!logger || !boxstack || !remote)
94 pthread_mutex_init(&masterLock, NULL);
96 masterLock=CreateMutex(NULL,FALSE,NULL);
102 int Command::shutdown()
104 if (!initted) return 0;
111 // VDR::getInstance()->cancelFindingServer();
116 void Command::doWallpaper()
118 Video* video = Video::getInstance();
121 Boxx* bbg = new Boxx();
122 bbg->setSize(video->getScreenWidth(), video->getScreenHeight());
124 bbg->fillColour(Colour::VIDEOBLUE);
126 boxstack->update(bbg);
127 boxstack->remove(bbg);
130 WJpeg* wallpaperj = new WJpeg();
131 wallpaperj->setSize(video->getScreenWidth(), video->getScreenHeight());
132 wallpaperj->createBuffer();
134 if (video->getFormat() == Video::PAL)
136 logger->log("Command", Log::DEBUG, "PAL wallpaper selected");
137 wallpaperj->init("/wallpaperPAL.jpg");
141 logger->log("Command", Log::DEBUG, "NTSC wallpaper selected");
142 wallpaperj->init("/wallpaperNTSC.jpg");
146 boxstack->add(wallpaperj);
147 boxstack->update(wallpaperj);
149 wallpaper = wallpaperj;
154 if (!initted) return;
161 Video::getInstance()->signalOn();
162 Led::getInstance()->on();
166 // End of startup. Lock the mutex and put the first view up
167 // logger->log("Command", Log::DEBUG, "WANT LOCK");
169 pthread_mutex_lock(&masterLock);
171 WaitForSingleObject(masterLock, INFINITE );
173 //logger->log("Command", Log::DEBUG, "LOCKED");
175 VConnect* vconnect = new VConnect();
176 boxstack->add(vconnect);
179 // Start method 2 of getting commands in...
186 //logger->log("Command", Log::DEBUG, "UNLOCK");
188 pthread_mutex_unlock(&masterLock);
190 ReleaseMutex(masterLock);
192 button = remote->getButtonPress(2); // FIXME why is this set to 2 and not 0? so it can quit
193 // something happened, lock and process
195 // logger->log("Command", Log::DEBUG, "WANT LOCK");
197 pthread_mutex_lock(&masterLock);
199 WaitForSingleObject(masterLock, INFINITE );
201 // logger->log("Command", Log::DEBUG, "LOCK");
203 if ((button == Remote::NA_NONE) /*|| (button == Remote::NA_UNKNOWN)*/) continue;
205 if (button != Remote::NA_SIGNAL) handleCommand(button);
206 processMessageQueue();
209 //logger->log("Command", Log::DEBUG, "UNLOCK");
211 pthread_mutex_unlock(&masterLock);
213 ReleaseMutex(masterLock);
217 void Command::postMessage(Message* m)
219 // This is locked here in case the main loop is not waiting for an event, but is processing one
220 // it could be killed but then not react to it because the signal wouldn't cause
221 // remote->getButtonPress to break
222 // locking the mutex ensures that the master thread is waiting on getButtonPress
225 //logger->log("Command", Log::DEBUG, "WANT LOCK");
227 pthread_mutex_lock(&masterLock);
229 WaitForSingleObject(masterLock, INFINITE );
231 //logger->log("Command", Log::DEBUG, "LOCK");
232 MessageQueue::postMessage(m);
235 kill(mainPid, SIGURG);
236 pthread_mutex_unlock(&masterLock);
238 ((RemoteWin*)Remote::getInstance())->Signal();
239 ReleaseMutex(masterLock);
241 //logger->log("Command", Log::DEBUG, "UNLOCK");
244 void Command::postMessageNoLock(Message* m)
246 // As above but use this one if this message is being posted because of a button press
247 // the mutex is already locked, locking around postMessage is not needed as the
248 // queue is guaranteed to be run when the button has been processed
249 MessageQueue::postMessage(m);
252 bool Command::postMessageIfNotBusy(Message* m)
254 // Used for Windows mouse events
256 //logger->log("Command", Log::DEBUG, "TRY LOCK");
258 if (pthread_mutex_trylock(&masterLock) != EBUSY)
260 //logger->log("Command", Log::DEBUG, "LOCK");
261 MessageQueue::postMessage(m);
262 kill(mainPid, SIGURG);
263 pthread_mutex_unlock(&masterLock);
264 //logger->log("Command", Log::DEBUG, "UNLOCK");
272 switch (WaitForSingleObject(masterLock, 0 ))
273 { //FIXME this is not "if not busy" check
274 case WAIT_OBJECT_0: //but with proper argument 0 this did not work
275 // case WAIT_ABANDONED:
276 MessageQueue::postMessage(m);
277 ((RemoteWin*)Remote::getInstance())->Signal();
278 ReleaseMutex(masterLock);
281 case WAIT_ABANDONED: return false;
282 case WAIT_TIMEOUT: return false;
288 void Command::postMessageFromOuterSpace(Message* m)
291 Yet another way of getting messages into Command. This one is for events that
292 are not standard button presses (or UDP generated buttons). It is also not for
293 events that are generated as a result of other events (events that can safely
294 call postMessageNoLock and be guaranteed that the message will be processed
295 because it is known that the queue is currently being processed).
296 This is for events that come from outer space and can occur when the master
297 mutex is locked or not, they need to be queued and executed but it doesn't
299 Actually so far it is for events caused by the video stream - aspect ratio
300 changes. These can occur when the master mutex is locked and so postMessage
301 doesn't work. postMessageNoLock doesn't work because if the mutex *isn't*
302 locked at the time then the message could be sat around a while before
304 The whole message system was at first supposed to prevent the problem of
305 calling a function on an object that had just been deleted, by ordering
306 messages such that all calls are done before object deletion. However,
307 because of the new centralised messaging system and the fact that BoxStack
308 locates the destination object before calling it, the messaging system now
309 allows the kind of sloppy calls it was supposed to stop. Weird huh. This
310 is mentioned here because the video stream might generate an event just as
311 the user hits stop. The mutex is locked, and by the time the message
312 is examined the vvideorec/live has been deleted. This doesn't matter because
313 boxstack will drop the message if it can't find the matching object to
315 Finally, all this is fine and dandy, except that I'm not 100% sure that
316 this sloppy postMessage and hope a queued signal will force it to be processed
317 thingy will actually work. Hmmm.
318 Lastly <g>, I will consider making the naming system a little more sane
322 logger->log("Command", Log::DEBUG, "PMFOS called");
323 MessageQueue::postMessage(m);
326 kill(mainPid, SIGURG);
328 ((RemoteWin*)Remote::getInstance())->Signal();
332 void Command::processMessage(Message* m)
334 // FIXME - a slight modification - how if messagereceivers were to register
335 // themselves as receivers to avoid the calling-a-deleted-object problem
336 // then only deliver/register/unregister would have to be protected
338 logger->log("Command", Log::DEBUG, "processing message %i", m->message);
345 case Message::STANDBY:
353 case Message::STOP_PLAYBACK:
355 handleCommand(Remote::STOP); // an odd way of doing it, but so simple
358 case Message::STREAM_END:
360 VVideoLive::getInstance()->streamEnd();
364 // Also connection_lost comes from player - anywhere else?
368 case Message::VDR_CONNECTED:
370 doJustConnected((VConnect*)m->from);
373 case Message::SCREENSHOT:
375 Osd::getInstance()->screenShot("/out.jpg");
378 case Message::CONNECTION_LOST:
383 case Message::UDP_BUTTON:
385 handleCommand(m->parameter);
388 case Message::CHANGE_LANGUAGE:
390 boxstack->removeAll();
391 boxstack->update(wallpaper);
393 VWelcome* vw = new VWelcome();
396 boxstack->update(vw);
399 case Message::LAST_VIEW_CLOSE:
401 // not currently used
402 // VWelcome* vw = new VWelcome();
404 // boxstack->add(vw);
405 // boxstack->update(vw);
413 logger->log("Command", Log::DEBUG, "Sending message to boxstack");
414 boxstack->processMessage(m);
418 void Command::handleCommand(int button)
420 if (isStandby && (button != Remote::POWER)) return;
421 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
423 // command was not handled
427 case Remote::DF_LEFT:
428 case Remote::DF_RIGHT:
429 case Remote::VOLUMEUP:
430 case Remote::VOLUMEDOWN:
432 VVolume* v = new VVolume();
434 v->handleCommand(button); // this will draw+show
439 VMute* v = new VMute();
452 if (!connLost) return; // if connLost, handle Remote::OK
462 Message* m = new Message(); // break into master mutex
463 m->message = Message::SCREENSHOT;
469 void Command::doStandby()
473 Video::getInstance()->signalOn();
474 Led::getInstance()->on();
478 VConnect* vconnect = new VConnect();
479 boxstack->add(vconnect);
484 boxstack->removeAll();
485 Video::getInstance()->signalOff();
486 boxstack->update(wallpaper);
488 VDR::getInstance()->configSave("General", "Last Power State", "Off");
489 VDR::getInstance()->disconnect();
490 Led::getInstance()->off();
493 stop(); //different behavoiur on windows, we exit
498 void Command::doFromTheTop(bool which)
502 connLost = new VInfo();
503 connLost->setSize(360, 200);
504 connLost->createBuffer();
505 if (Video::getInstance()->getFormat() == Video::PAL)
506 connLost->setPosition(190, 170);
508 connLost->setPosition(180, 120);
509 connLost->setOneLiner(tr("Connection lost"));
510 connLost->setDropThrough();
511 connLost->setBorderOn(1);
512 connLost->setTitleBarColour(Colour::DANGER);
513 connLost->okButton();
515 boxstack->add(connLost);
516 boxstack->update(connLost);
517 remote->clearBuffer();
521 VDR::getInstance()->disconnect();
522 boxstack->removeAll();
523 boxstack->update(wallpaper);
525 VConnect* vconnect = new VConnect();
526 boxstack->add(vconnect);
531 void Command::doReboot()
533 VDR::getInstance()->disconnect();
535 logger->log("Command", Log::NOTICE, "Reboot");
537 reboot(LINUX_REBOOT_CMD_RESTART);
538 #endif //Would we support this on windows?
541 void Command::connectionLost()
543 Message* m = new Message(); // break into master mutex
544 m->message = Message::CONNECTION_LOST;
546 postMessageNoLock(m);
549 void Command::doJustConnected(VConnect* vconnect)
552 Video* video = Video::getInstance();
553 Audio* audio = Audio::getInstance();
554 boxstack->remove(vconnect);
556 VInfo* vi = new VInfo();
557 vi->setSize(400, 200);
559 if (video->getFormat() == Video::PAL)
560 vi->setPosition(170, 200);
562 vi->setPosition(160, 150);
563 vi->setOneLiner(tr("Connected, loading config"));
566 boxstack->update(vi);
568 VDR* vdr = VDR::getInstance();
571 // See if config says to override video format (PAL/NTSC)
572 config = vdr->configLoad("General", "Override Video Format");
575 logger->log("Command", Log::DEBUG, "Override Video Format is present");
577 if ( (!strcmp(config, "PAL") && (video->getFormat() == Video::NTSC))
578 || (!strcmp(config, "NTSC") && (video->getFormat() == Video::PAL)) )
580 // Oh sheesh, need to switch format. Bye bye TV...
582 // Take everything down
583 boxstack->removeAll();
584 boxstack->remove(wallpaper);
585 Osd* osd = Osd::getInstance();
589 // Get video and osd back up with the new mode
590 if (!strcmp(config, "PAL"))
592 logger->log("Command", Log::DEBUG, "Switching to PAL");
593 video->init(Video::PAL);
595 else if (!strcmp(config, "NTSC"))
597 logger->log("Command", Log::DEBUG, "Switching to NTSC");
598 video->init(Video::NTSC);
600 osd->init((char*)("/dev/stbgfx"));
602 // Put the wallpaper back
607 vi->setSize(400, 200);
609 if (video->getFormat() == Video::PAL)
610 vi->setPosition(170, 200);
612 vi->setPosition(160, 150);
614 vi->setOneLiner(tr("Connected, loading config"));
617 boxstack->update(vi);
621 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
626 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
629 // Power off if first boot and config says so
634 logger->log("Command", Log::DEBUG, "Load power after boot");
636 config = vdr->configLoad("General", "Power After Boot");
640 if (!STRCASECMP(config, "On"))
642 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
644 else if (!STRCASECMP(config, "Off"))
646 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
651 else if (!STRCASECMP(config, "Last state"))
653 char* lastPowerState = vdr->configLoad("General", "Last Power State");
656 if (!STRCASECMP(lastPowerState, "On"))
658 logger->log("Command", Log::INFO, "Config says Last Power State = On");
660 else if (!STRCASECMP(lastPowerState, "Off"))
662 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
669 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
674 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
679 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
685 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
690 // Go S-Video if config says so
692 config = vdr->configLoad("TV", "Connection");
696 if (!STRCASECMP(config, "S-Video"))
698 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
699 video->setConnection(Video::SVIDEO);
703 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
704 video->setConnection(Video::COMPOSITERGB);
710 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
715 config = vdr->configLoad("General", "Remote type");
719 if (!STRCASECMP(config, "New"))
721 logger->log("Command", Log::INFO, "Switching to New remote type");
722 remote->setRemoteType(Remote::NEWREMOTE);
726 logger->log("Command", Log::INFO, "Switching to Old remote type");
727 remote->setRemoteType(Remote::OLDREMOTE);
733 logger->log("Command", Log::INFO, "Config General/Remote type not found");
734 remote->setRemoteType(Remote::OLDREMOTE);
740 // Get TV aspect ratio
742 config = vdr->configLoad("TV", "Aspect");
745 if (!STRCASECMP(config, "16:9"))
747 logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
748 video->setTVsize(Video::ASPECT16X9);
752 logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
753 video->setTVsize(Video::ASPECT4X3);
759 logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
760 video->setTVsize(Video::ASPECT4X3);
763 config = vdr->configLoad("TV", "Widemode");
766 if (!STRCASECMP(config, "Letterbox"))
768 logger->log("Command", Log::INFO, "Setting letterbox mode");
769 video->setMode(Video::LETTERBOX);
773 logger->log("Command", Log::INFO, "Setting chop-sides mode");
774 video->setMode(Video::NORMAL);
780 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
781 video->setMode(Video::NORMAL);
784 config = vdr->configLoad("Advanced", "TCP receive window");
787 size_t newTCPsize = atoi(config);
790 logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
791 vdr->setReceiveWindow(newTCPsize);
795 logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
796 vdr->setReceiveWindow(2048); // Default
799 config = vdr->configLoad("Advanced", "Disable WOL");
802 if (!STRCASECMP(config, "Yes"))
804 logger->log("Command", Log::INFO, "Config says disable WOL");
805 Wol::getInstance()->setEnabled(false);
809 logger->log("Command", Log::INFO, "Config says enable WOL");
810 Wol::getInstance()->setEnabled(true);
817 logger->log("Command", Log::INFO, "By default, enable WOL");
818 Wol::getInstance()->setEnabled(true);
820 /* device dependend config */
821 audio->loadOptionsfromServer(vdr);
822 video->loadOptionsfromServer(vdr);
823 remote->loadOptionsfromServer(vdr);
826 // Save power state = on
828 vdr->configSave("General", "Last Power State", "On");
830 // Make sure connection didn't die
831 if (!vdr->isConnected())
833 Command::getInstance()->connectionLost();
837 boxstack->remove(vi);
839 VWelcome* vw = new VWelcome();
842 boxstack->update(vw);
844 // Enter pre-keys here
845 // handleCommand(Remote::SIX);
846 // handleCommand(Remote::UP);
847 // handleCommand(Remote::PLAY);
848 // handleCommand(Remote::DOWN);
849 // handleCommand(Remote::DOWN);
850 // handleCommand(Remote::DOWN);
851 // handleCommand(Remote::OK);
852 // handleCommand(Remote::OK);
853 // handleCommand(Remote::RED);