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 if (!logger || !boxstack || !remote)
92 pthread_mutex_init(&masterLock, NULL);
94 masterLock=CreateMutex(NULL,FALSE,NULL);
100 int Command::shutdown()
102 if (!initted) return 0;
109 // VDR::getInstance()->cancelFindingServer();
114 void Command::doWallpaper()
116 Video* video = Video::getInstance();
119 Boxx* bbg = new Boxx();
120 bbg->setSize(video->getScreenWidth(), video->getScreenHeight());
122 bbg->fillColour(Colour::VIDEOBLUE);
124 boxstack->update(bbg);
125 boxstack->remove(bbg);
128 WJpeg* wallpaperj = new WJpeg();
129 wallpaperj->setSize(video->getScreenWidth(), video->getScreenHeight());
130 wallpaperj->createBuffer();
132 if (video->getFormat() == Video::PAL)
134 logger->log("Command", Log::DEBUG, "PAL wallpaper selected");
135 wallpaperj->init("/wallpaperPAL.jpg");
139 logger->log("Command", Log::DEBUG, "NTSC wallpaper selected");
140 wallpaperj->init("/wallpaperNTSC.jpg");
144 boxstack->add(wallpaperj);
145 boxstack->update(wallpaperj);
147 wallpaper = wallpaperj;
152 if (!initted) return;
159 Video::getInstance()->signalOn();
160 Led::getInstance()->on();
164 // End of startup. Lock the mutex and put the first view up
165 // logger->log("Command", Log::DEBUG, "WANT LOCK");
167 pthread_mutex_lock(&masterLock);
169 WaitForSingleObject(masterLock, INFINITE );
171 //logger->log("Command", Log::DEBUG, "LOCKED");
173 VConnect* vconnect = new VConnect();
174 boxstack->add(vconnect);
177 // Start method 2 of getting commands in...
184 //logger->log("Command", Log::DEBUG, "UNLOCK");
186 pthread_mutex_unlock(&masterLock);
188 ReleaseMutex(masterLock);
190 button = remote->getButtonPress(2); // FIXME why is this set to 2 and not 0? so it can quit
191 // something happened, lock and process
193 // logger->log("Command", Log::DEBUG, "WANT LOCK");
195 pthread_mutex_lock(&masterLock);
197 WaitForSingleObject(masterLock, INFINITE );
199 // logger->log("Command", Log::DEBUG, "LOCK");
201 if ((button == Remote::NA_NONE) /*|| (button == Remote::NA_UNKNOWN)*/) continue;
203 if (button != Remote::NA_SIGNAL) handleCommand(button);
204 processMessageQueue();
207 //logger->log("Command", Log::DEBUG, "UNLOCK");
209 pthread_mutex_unlock(&masterLock);
211 ReleaseMutex(masterLock);
215 void Command::postMessage(Message* m)
217 // This is locked here in case the main loop is not waiting for an event, but is processing one
218 // it could be killed but then not react to it because the signal wouldn't cause
219 // remote->getButtonPress to break
220 // locking the mutex ensures that the master thread is waiting on getButtonPress
223 //logger->log("Command", Log::DEBUG, "WANT LOCK");
225 pthread_mutex_lock(&masterLock);
227 WaitForSingleObject(masterLock, INFINITE );
229 //logger->log("Command", Log::DEBUG, "LOCK");
230 MessageQueue::postMessage(m);
233 kill(mainPid, SIGURG);
234 pthread_mutex_unlock(&masterLock);
236 ((RemoteWin*)Remote::getInstance())->Signal();
237 ReleaseMutex(masterLock);
239 //logger->log("Command", Log::DEBUG, "UNLOCK");
242 void Command::postMessageNoLock(Message* m)
244 // As above but use this one if this message is being posted because of a button press
245 // the mutex is already locked, locking around postMessage is not needed as the
246 // queue is guaranteed to be run when the button has been processed
247 MessageQueue::postMessage(m);
250 bool Command::postMessageIfNotBusy(Message* m)
252 // Used for Windows mouse events
254 //logger->log("Command", Log::DEBUG, "TRY LOCK");
256 if (pthread_mutex_trylock(&masterLock) != EBUSY)
258 //logger->log("Command", Log::DEBUG, "LOCK");
259 MessageQueue::postMessage(m);
260 kill(mainPid, SIGURG);
261 pthread_mutex_unlock(&masterLock);
262 //logger->log("Command", Log::DEBUG, "UNLOCK");
270 switch (WaitForSingleObject(masterLock, 0 ))
271 { //FIXME this is not "if not busy" check
272 case WAIT_OBJECT_0: //but with proper argument 0 this did not work
273 // case WAIT_ABANDONED:
274 MessageQueue::postMessage(m);
275 ((RemoteWin*)Remote::getInstance())->Signal();
276 ReleaseMutex(masterLock);
279 case WAIT_ABANDONED: return false;
280 case WAIT_TIMEOUT: return false;
286 void Command::postMessageFromOuterSpace(Message* m)
289 Yet another way of getting messages into Command. This one is for events that
290 are not standard button presses (or UDP generated buttons). It is also not for
291 events that are generated as a result of other events (events that can safely
292 call postMessageNoLock and be guaranteed that the message will be processed
293 because it is known that the queue is currently being processed).
294 This is for events that come from outer space and can occur when the master
295 mutex is locked or not, they need to be queued and executed but it doesn't
297 Actually so far it is for events caused by the video stream - aspect ratio
298 changes. These can occur when the master mutex is locked and so postMessage
299 doesn't work. postMessageNoLock doesn't work because if the mutex *isn't*
300 locked at the time then the message could be sat around a while before
302 The whole message system was at first supposed to prevent the problem of
303 calling a function on an object that had just been deleted, by ordering
304 messages such that all calls are done before object deletion. However,
305 because of the new centralised messaging system and the fact that BoxStack
306 locates the destination object before calling it, the messaging system now
307 allows the kind of sloppy calls it was supposed to stop. Weird huh. This
308 is mentioned here because the video stream might generate an event just as
309 the user hits stop. The mutex is locked, and by the time the message
310 is examined the vvideorec/live has been deleted. This doesn't matter because
311 boxstack will drop the message if it can't find the matching object to
313 Finally, all this is fine and dandy, except that I'm not 100% sure that
314 this sloppy postMessage and hope a queued signal will force it to be processed
315 thingy will actually work. Hmmm.
316 Lastly <g>, I will consider making the naming system a little more sane
320 logger->log("Command", Log::DEBUG, "PMFOS called");
321 MessageQueue::postMessage(m);
324 kill(mainPid, SIGURG);
326 ((RemoteWin*)Remote::getInstance())->Signal();
330 void Command::processMessage(Message* m)
332 // FIXME - a slight modification - how if messagereceivers were to register
333 // themselves as receivers to avoid the calling-a-deleted-object problem
334 // then only deliver/register/unregister would have to be protected
336 logger->log("Command", Log::DEBUG, "processing message %i", m->message);
343 case Message::STANDBY:
351 case Message::STOP_PLAYBACK:
353 handleCommand(Remote::STOP); // an odd way of doing it, but so simple
356 case Message::STREAM_END:
358 VVideoLive::getInstance()->streamEnd();
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 VWelcome* vw = new VWelcome();
394 boxstack->update(vw);
397 case Message::LAST_VIEW_CLOSE:
399 // not currently used
400 // VWelcome* vw = new VWelcome();
402 // boxstack->add(vw);
403 // boxstack->update(vw);
411 logger->log("Command", Log::DEBUG, "Sending message to boxstack");
412 boxstack->processMessage(m);
416 void Command::handleCommand(int button)
418 if (isStandby && (button != Remote::POWER)) return;
419 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
421 // command was not handled
425 case Remote::DF_LEFT:
426 case Remote::DF_RIGHT:
427 case Remote::VOLUMEUP:
428 case Remote::VOLUMEDOWN:
430 VVolume* v = new VVolume();
432 v->handleCommand(button); // this will draw+show
437 VMute* v = new VMute();
450 if (!connLost) return; // if connLost, handle Remote::OK
460 Message* m = new Message(); // break into master mutex
461 m->message = Message::SCREENSHOT;
467 void Command::doStandby()
471 Video::getInstance()->signalOn();
472 Led::getInstance()->on();
476 VConnect* vconnect = new VConnect();
477 boxstack->add(vconnect);
482 boxstack->removeAll();
483 Video::getInstance()->signalOff();
484 boxstack->update(wallpaper);
486 VDR::getInstance()->configSave("General", "Last Power State", "Off");
487 VDR::getInstance()->disconnect();
488 Led::getInstance()->off();
491 stop(); //different behavoiur on windows, we exit
496 void Command::doFromTheTop(bool which)
500 connLost = new VInfo();
501 connLost->setSize(360, 200);
502 connLost->createBuffer();
503 if (Video::getInstance()->getFormat() == Video::PAL)
504 connLost->setPosition(190, 170);
506 connLost->setPosition(180, 120);
507 connLost->setOneLiner(tr("Connection lost"));
508 connLost->setDropThrough();
509 connLost->setBorderOn(1);
510 connLost->setTitleBarColour(Colour::DANGER);
511 connLost->okButton();
513 boxstack->add(connLost);
514 boxstack->update(connLost);
515 remote->clearBuffer();
519 VDR::getInstance()->disconnect();
520 boxstack->removeAll();
521 boxstack->update(wallpaper);
523 VConnect* vconnect = new VConnect();
524 boxstack->add(vconnect);
529 void Command::doReboot()
531 VDR::getInstance()->disconnect();
533 logger->log("Command", Log::NOTICE, "Reboot");
535 reboot(LINUX_REBOOT_CMD_RESTART);
536 #endif //Would we support this on windows?
539 void Command::connectionLost()
541 Message* m = new Message(); // break into master mutex
542 m->message = Message::CONNECTION_LOST;
544 postMessageNoLock(m);
547 void Command::doJustConnected(VConnect* vconnect)
550 Video* video = Video::getInstance();
551 Audio* audio = Audio::getInstance();
552 boxstack->remove(vconnect);
554 VInfo* vi = new VInfo();
555 vi->setSize(400, 200);
557 if (video->getFormat() == Video::PAL)
558 vi->setPosition(170, 200);
560 vi->setPosition(160, 150);
561 vi->setOneLiner(tr("Connected, loading config"));
564 boxstack->update(vi);
566 VDR* vdr = VDR::getInstance();
569 // See if config says to override video format (PAL/NTSC)
570 config = vdr->configLoad("General", "Override Video Format");
573 logger->log("Command", Log::DEBUG, "Override Video Format is present");
575 if ( (!strcmp(config, "PAL") && (video->getFormat() == Video::NTSC))
576 || (!strcmp(config, "NTSC") && (video->getFormat() == Video::PAL)) )
578 // Oh sheesh, need to switch format. Bye bye TV...
580 // Take everything down
581 boxstack->removeAll();
582 boxstack->remove(wallpaper);
583 Osd* osd = Osd::getInstance();
587 // Get video and osd back up with the new mode
588 if (!strcmp(config, "PAL"))
590 logger->log("Command", Log::DEBUG, "Switching to PAL");
591 video->init(Video::PAL);
593 else if (!strcmp(config, "NTSC"))
595 logger->log("Command", Log::DEBUG, "Switching to NTSC");
596 video->init(Video::NTSC);
598 osd->init((char*)("/dev/stbgfx"));
600 // Put the wallpaper back
605 vi->setSize(400, 200);
607 if (video->getFormat() == Video::PAL)
608 vi->setPosition(170, 200);
610 vi->setPosition(160, 150);
612 vi->setOneLiner(tr("Connected, loading config"));
615 boxstack->update(vi);
619 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
624 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
627 // Power off if first boot and config says so
632 logger->log("Command", Log::DEBUG, "Load power after boot");
634 config = vdr->configLoad("General", "Power After Boot");
638 if (!STRCASECMP(config, "On"))
640 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
642 else if (!STRCASECMP(config, "Off"))
644 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
649 else if (!STRCASECMP(config, "Last state"))
651 char* lastPowerState = vdr->configLoad("General", "Last Power State");
654 if (!STRCASECMP(lastPowerState, "On"))
656 logger->log("Command", Log::INFO, "Config says Last Power State = On");
658 else if (!STRCASECMP(lastPowerState, "Off"))
660 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
667 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
672 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
677 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
683 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
688 // Go S-Video if config says so
690 config = vdr->configLoad("TV", "Connection");
694 if (!STRCASECMP(config, "S-Video"))
696 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
697 video->setConnection(Video::SVIDEO);
701 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
702 video->setConnection(Video::COMPOSITERGB);
708 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
713 config = vdr->configLoad("General", "Remote type");
717 if (!STRCASECMP(config, "New"))
719 logger->log("Command", Log::INFO, "Switching to New remote type");
720 remote->setRemoteType(Remote::NEWREMOTE);
724 logger->log("Command", Log::INFO, "Switching to Old remote type");
725 remote->setRemoteType(Remote::OLDREMOTE);
731 logger->log("Command", Log::INFO, "Config General/Remote type not found");
732 remote->setRemoteType(Remote::OLDREMOTE);
738 // Get TV aspect ratio
740 config = vdr->configLoad("TV", "Aspect");
743 if (!STRCASECMP(config, "16:9"))
745 logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
746 video->setTVsize(Video::ASPECT16X9);
750 logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
751 video->setTVsize(Video::ASPECT4X3);
757 logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
758 video->setTVsize(Video::ASPECT4X3);
761 config = vdr->configLoad("TV", "Widemode");
764 if (!STRCASECMP(config, "Letterbox"))
766 logger->log("Command", Log::INFO, "Setting letterbox mode");
767 video->setMode(Video::LETTERBOX);
771 logger->log("Command", Log::INFO, "Setting chop-sides mode");
772 video->setMode(Video::NORMAL);
778 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
779 video->setMode(Video::NORMAL);
782 config = vdr->configLoad("Advanced", "TCP receive window");
785 size_t newTCPsize = atoi(config);
788 logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
789 vdr->setReceiveWindow(newTCPsize);
793 logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
794 vdr->setReceiveWindow(2048); // Default
797 config = vdr->configLoad("Advanced", "Disable WOL");
800 if (!STRCASECMP(config, "Yes"))
802 logger->log("Command", Log::INFO, "Config says disable WOL");
803 Wol::getInstance()->setEnabled(false);
807 logger->log("Command", Log::INFO, "Config says enable WOL");
808 Wol::getInstance()->setEnabled(true);
815 logger->log("Command", Log::INFO, "By default, enable WOL");
816 Wol::getInstance()->setEnabled(true);
818 /* device dependend config */
819 audio->loadOptionsfromServer(vdr);
820 video->loadOptionsfromServer(vdr);
821 remote->loadOptionsfromServer(vdr);
824 // Save power state = on
826 vdr->configSave("General", "Last Power State", "On");
828 // Make sure connection didn't die
829 if (!vdr->isConnected())
831 Command::getInstance()->connectionLost();
835 boxstack->remove(vi);
837 VWelcome* vw = new VWelcome();
840 boxstack->update(vw);
842 // Enter pre-keys here
843 // handleCommand(Remote::SIX);
844 // handleCommand(Remote::UP);
845 // handleCommand(Remote::PLAY);
846 // handleCommand(Remote::DOWN);
847 // handleCommand(Remote::DOWN);
848 // handleCommand(Remote::DOWN);
849 // handleCommand(Remote::OK);
850 // handleCommand(Remote::OK);
851 // handleCommand(Remote::RED);