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
21 #include <linux/errno.h>
26 #include "remotewin.h"
33 #include "vserverselect.h"
39 #include "timerreceiver.h"
42 #include "vvideolive.h"
52 Command* Command::instance = NULL;
69 Command* Command::getInstance()
76 if (initted) return 0;
79 logger = Log::getInstance();
80 boxstack = BoxStack::getInstance();
81 remote = Remote::getInstance();
83 if (!logger || !boxstack || !remote)
89 pthread_mutex_init(&masterLock, NULL);
91 masterLock=CreateMutex(NULL,FALSE,NULL);
97 int Command::shutdown()
99 if (!initted) return 0;
106 // VDR::getInstance()->cancelFindingServer();
111 void Command::doWallpaper()
113 Video* video = Video::getInstance();
116 Boxx* bbg = new Boxx();
117 bbg->setSize(video->getScreenWidth(), video->getScreenHeight());
119 bbg->fillColour(Colour::VIDEOBLUE);
121 boxstack->update(bbg);
122 boxstack->remove(bbg);
125 WJpeg* wallpaperj = new WJpeg();
126 wallpaperj->setSize(video->getScreenWidth(), video->getScreenHeight());
127 wallpaperj->createBuffer();
129 if (video->getFormat() == Video::PAL)
131 logger->log("Command", Log::DEBUG, "PAL wallpaper selected");
132 wallpaperj->init("/wallpaperPAL.jpg");
136 logger->log("Command", Log::DEBUG, "NTSC wallpaper selected");
137 wallpaperj->init("/wallpaperNTSC.jpg");
141 boxstack->add(wallpaperj);
142 boxstack->update(wallpaperj);
144 wallpaper = wallpaperj;
149 if (!initted) return;
156 Video::getInstance()->signalOn();
157 Led::getInstance()->on();
161 // End of startup. Lock the mutex and put the first view up
162 // logger->log("Command", Log::DEBUG, "WANT LOCK");
164 pthread_mutex_lock(&masterLock);
166 WaitForSingleObject(masterLock, INFINITE );
168 //logger->log("Command", Log::DEBUG, "LOCKED");
170 VConnect* vconnect = new VConnect();
171 boxstack->add(vconnect);
174 // Start method 2 of getting commands in...
181 //logger->log("Command", Log::DEBUG, "UNLOCK");
183 pthread_mutex_unlock(&masterLock);
185 ReleaseMutex(masterLock);
187 button = remote->getButtonPress(2); // FIXME why is this set to 2 and not 0? so it can quit
188 // something happened, lock and process
190 // logger->log("Command", Log::DEBUG, "WANT LOCK");
192 pthread_mutex_lock(&masterLock);
194 WaitForSingleObject(masterLock, INFINITE );
196 // logger->log("Command", Log::DEBUG, "LOCK");
198 if ((button == Remote::NA_NONE) /*|| (button == Remote::NA_UNKNOWN)*/) continue;
200 if (button != Remote::NA_SIGNAL) handleCommand(button);
201 processMessageQueue();
204 //logger->log("Command", Log::DEBUG, "UNLOCK");
206 pthread_mutex_unlock(&masterLock);
208 ReleaseMutex(masterLock);
212 void Command::postMessage(Message* m)
214 // This is locked here in case the main loop is not waiting for an event, but is processing one
215 // it could be killed but then not react to it because the signal wouldn't cause
216 // remote->getButtonPress to break
217 // locking the mutex ensures that the master thread is waiting on getButtonPress
220 //logger->log("Command", Log::DEBUG, "WANT LOCK");
222 pthread_mutex_lock(&masterLock);
224 WaitForSingleObject(masterLock, INFINITE );
226 //logger->log("Command", Log::DEBUG, "LOCK");
227 MessageQueue::postMessage(m);
230 kill(mainPid, SIGURG);
231 pthread_mutex_unlock(&masterLock);
233 ((RemoteWin*)Remote::getInstance())->Signal();
234 ReleaseMutex(masterLock);
236 //logger->log("Command", Log::DEBUG, "UNLOCK");
239 void Command::postMessageNoLock(Message* m)
241 // As above but use this one if this message is being posted because of a button press
242 // the mutex is already locked, locking around postMessage is not needed as the
243 // queue is guaranteed to be run when the button has been processed
244 MessageQueue::postMessage(m);
247 bool Command::postMessageIfNotBusy(Message* m)
249 // Used for Windows mouse events
251 //logger->log("Command", Log::DEBUG, "TRY LOCK");
253 if (pthread_mutex_trylock(&masterLock) != EBUSY)
255 //logger->log("Command", Log::DEBUG, "LOCK");
256 MessageQueue::postMessage(m);
257 kill(mainPid, SIGURG);
258 pthread_mutex_unlock(&masterLock);
259 //logger->log("Command", Log::DEBUG, "UNLOCK");
267 switch (WaitForSingleObject(masterLock, 0 ))
268 { //FIXME this is not "if not busy" check
269 case WAIT_OBJECT_0: //but with proper argument 0 this did not work
270 // case WAIT_ABANDONED:
271 MessageQueue::postMessage(m);
272 ((RemoteWin*)Remote::getInstance())->Signal();
273 ReleaseMutex(masterLock);
276 case WAIT_ABANDONED: return false;
277 case WAIT_TIMEOUT: return false;
283 void Command::postMessageFromOuterSpace(Message* m)
286 Yet another way of getting messages into Command. This one is for events that
287 are not standard button presses (or UDP generated buttons). It is also not for
288 events that are generated as a result of other events (events that can safely
289 call postMessageNoLock and be guaranteed that the message will be processed
290 because it is known that the queue is currently being processed).
291 This is for events that come from outer space and can occur when the master
292 mutex is locked or not, they need to be queued and executed but it doesn't
294 Actually so far it is for events caused by the video stream - aspect ratio
295 changes. These can occur when the master mutex is locked and so postMessage
296 doesn't work. postMessageNoLock doesn't work because if the mutex *isn't*
297 locked at the time then the message could be sat around a while before
299 The whole message system was at first supposed to prevent the problem of
300 calling a function on an object that had just been deleted, by ordering
301 messages such that all calls are done before object deletion. However,
302 because of the new centralised messaging system and the fact that BoxStack
303 locates the destination object before calling it, the messaging system now
304 allows the kind of sloppy calls it was supposed to stop. Weird huh. This
305 is mentioned here because the video stream might generate an event just as
306 the user hits stop. The mutex is locked, and by the time the message
307 is examined the vvideorec/live has been deleted. This doesn't matter because
308 boxstack will drop the message if it can't find the matching object to
310 Finally, all this is fine and dandy, except that I'm not 100% sure that
311 this sloppy postMessage and hope a queued signal will force it to be processed
312 thingy will actually work. Hmmm.
313 Lastly <g>, I will consider making the naming system a little more sane
317 logger->log("Command", Log::DEBUG, "PMFOS called");
318 MessageQueue::postMessage(m);
321 kill(mainPid, SIGURG);
323 ((RemoteWin*)Remote::getInstance())->Signal();
327 void Command::processMessage(Message* m)
329 // FIXME - a slight modification - how if messagereceivers were to register
330 // themselves as receivers to avoid the calling-a-deleted-object problem
331 // then only deliver/register/unregister would have to be protected
333 logger->log("Command", Log::DEBUG, "processing message %i", m->message);
340 case Message::STANDBY:
348 case Message::STOP_PLAYBACK:
350 handleCommand(Remote::STOP); // an odd way of doing it, but so simple
353 case Message::STREAM_END:
355 VVideoLive::getInstance()->streamEnd();
359 // Also connection_lost comes from player - anywhere else?
363 case Message::VDR_CONNECTED:
365 doJustConnected((VConnect*)m->from);
368 case Message::SCREENSHOT:
370 Osd::getInstance()->screenShot("/out.jpg");
373 case Message::CONNECTION_LOST:
378 case Message::UDP_BUTTON:
380 handleCommand(m->parameter);
383 case Message::CHANGE_LANGUAGE:
385 boxstack->removeAll();
386 boxstack->update(wallpaper);
388 VWelcome* vw = new VWelcome();
391 boxstack->update(vw);
394 case Message::LAST_VIEW_CLOSE:
396 // not currently used
397 // VWelcome* vw = new VWelcome();
399 // boxstack->add(vw);
400 // boxstack->update(vw);
408 logger->log("Command", Log::DEBUG, "Sending message to boxstack");
409 boxstack->processMessage(m);
413 void Command::handleCommand(int button)
415 if (isStandby && (button != Remote::POWER)) return;
416 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
418 // command was not handled
422 case Remote::DF_LEFT:
423 case Remote::DF_RIGHT:
424 case Remote::VOLUMEUP:
425 case Remote::VOLUMEDOWN:
427 VVolume* v = new VVolume();
429 v->handleCommand(button); // this will draw+show
434 VMute* v = new VMute();
447 if (!connLost) return; // if connLost, handle Remote::OK
457 Message* m = new Message(); // break into master mutex
458 m->message = Message::SCREENSHOT;
464 void Command::doStandby()
468 Video::getInstance()->signalOn();
469 Led::getInstance()->on();
473 VConnect* vconnect = new VConnect();
474 boxstack->add(vconnect);
479 boxstack->removeAll();
480 Video::getInstance()->signalOff();
481 boxstack->update(wallpaper);
483 VDR::getInstance()->configSave("General", "Last Power State", "Off");
484 VDR::getInstance()->disconnect();
485 Led::getInstance()->off();
488 stop(); //different behavoiur on windows, we exit
493 void Command::doFromTheTop(bool which)
497 connLost = new VInfo();
498 connLost->setSize(360, 200);
499 connLost->createBuffer();
500 if (Video::getInstance()->getFormat() == Video::PAL)
501 connLost->setPosition(190, 170);
503 connLost->setPosition(180, 120);
504 connLost->setOneLiner(tr("Connection lost"));
505 connLost->setDropThrough();
506 connLost->setBorderOn(1);
507 connLost->setTitleBarColour(Colour::DANGER);
508 connLost->okButton();
510 boxstack->add(connLost);
511 boxstack->update(connLost);
512 remote->clearBuffer();
516 VDR::getInstance()->disconnect();
517 boxstack->removeAll();
518 boxstack->update(wallpaper);
520 VConnect* vconnect = new VConnect();
521 boxstack->add(vconnect);
526 void Command::doReboot()
528 VDR::getInstance()->disconnect();
530 logger->log("Command", Log::NOTICE, "Reboot");
532 reboot(LINUX_REBOOT_CMD_RESTART);
533 #endif //Would we support this on windows?
536 void Command::connectionLost()
538 Message* m = new Message(); // break into master mutex
539 m->message = Message::CONNECTION_LOST;
541 postMessageNoLock(m);
544 void Command::doJustConnected(VConnect* vconnect)
547 Video* video = Video::getInstance();
548 boxstack->remove(vconnect);
550 VInfo* vi = new VInfo();
551 vi->setSize(400, 200);
553 if (video->getFormat() == Video::PAL)
554 vi->setPosition(170, 200);
556 vi->setPosition(160, 150);
557 vi->setOneLiner(tr("Connected, loading config"));
560 boxstack->update(vi);
562 VDR* vdr = VDR::getInstance();
565 // See if config says to override video format (PAL/NTSC)
566 config = vdr->configLoad("General", "Override Video Format");
569 logger->log("Command", Log::DEBUG, "Override Video Format is present");
571 if ( (!strcmp(config, "PAL") && (video->getFormat() == Video::NTSC))
572 || (!strcmp(config, "NTSC") && (video->getFormat() == Video::PAL)) )
574 // Oh sheesh, need to switch format. Bye bye TV...
576 // Take everything down
577 boxstack->removeAll();
578 boxstack->remove(wallpaper);
579 Osd* osd = Osd::getInstance();
583 // Get video and osd back up with the new mode
584 if (!strcmp(config, "PAL"))
586 logger->log("Command", Log::DEBUG, "Switching to PAL");
587 video->init(Video::PAL);
589 else if (!strcmp(config, "NTSC"))
591 logger->log("Command", Log::DEBUG, "Switching to NTSC");
592 video->init(Video::NTSC);
594 osd->init((char*)("/dev/stbgfx"));
596 // Put the wallpaper back
601 vi->setSize(400, 200);
603 if (video->getFormat() == Video::PAL)
604 vi->setPosition(170, 200);
606 vi->setPosition(160, 150);
608 vi->setOneLiner(tr("Connected, loading config"));
611 boxstack->update(vi);
615 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
620 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
623 // Power off if first boot and config says so
628 logger->log("Command", Log::DEBUG, "Load power after boot");
630 config = vdr->configLoad("General", "Power After Boot");
634 if (!STRCASECMP(config, "On"))
636 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
638 else if (!STRCASECMP(config, "Off"))
640 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
645 else if (!STRCASECMP(config, "Last state"))
647 char* lastPowerState = vdr->configLoad("General", "Last Power State");
650 if (!STRCASECMP(lastPowerState, "On"))
652 logger->log("Command", Log::INFO, "Config says Last Power State = On");
654 else if (!STRCASECMP(lastPowerState, "Off"))
656 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
663 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
668 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
673 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
679 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
684 // Go S-Video if config says so
686 config = vdr->configLoad("TV", "Connection");
690 if (!STRCASECMP(config, "S-Video"))
692 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
693 video->setConnection(Video::SVIDEO);
697 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
698 video->setConnection(Video::COMPOSITERGB);
704 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
709 config = vdr->configLoad("General", "Remote type");
713 if (!STRCASECMP(config, "New"))
715 logger->log("Command", Log::INFO, "Switching to New remote type");
716 remote->setRemoteType(Remote::NEWREMOTE);
720 logger->log("Command", Log::INFO, "Switching to Old remote type");
721 remote->setRemoteType(Remote::OLDREMOTE);
727 logger->log("Command", Log::INFO, "Config General/Remote type not found");
728 remote->setRemoteType(Remote::OLDREMOTE);
733 config = vdr->configLoad("General", "Remote keys");
737 logger->log("Command", Log::INFO, "Config General/Remote keys load");
738 remote->LoadKeysConfig(config);
743 logger->log("Command", Log::INFO, "Config General/Remote keys not found");
744 remote->InitHWCListwithDefaults();
747 // Get TV aspect ratio
749 config = vdr->configLoad("TV", "Aspect");
752 if (!STRCASECMP(config, "16:9"))
754 logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
755 video->setTVsize(Video::ASPECT16X9);
759 logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
760 video->setTVsize(Video::ASPECT4X3);
766 logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
767 video->setTVsize(Video::ASPECT4X3);
770 config = vdr->configLoad("TV", "Widemode");
773 if (!STRCASECMP(config, "Letterbox"))
775 logger->log("Command", Log::INFO, "Setting letterbox mode");
776 video->setMode(Video::LETTERBOX);
780 logger->log("Command", Log::INFO, "Setting chop-sides mode");
781 video->setMode(Video::NORMAL);
787 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
788 video->setMode(Video::NORMAL);
791 config = vdr->configLoad("Advanced", "TCP receive window");
794 size_t newTCPsize = atoi(config);
797 logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
798 vdr->setReceiveWindow(newTCPsize);
802 logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
803 vdr->setReceiveWindow(2048); // Default
806 config = vdr->configLoad("Advanced", "Disable WOL");
809 if (!STRCASECMP(config, "Yes"))
811 logger->log("Command", Log::INFO, "Config says disable WOL");
812 Wol::getInstance()->setEnabled(false);
816 logger->log("Command", Log::INFO, "Config says enable WOL");
817 Wol::getInstance()->setEnabled(true);
824 logger->log("Command", Log::INFO, "By default, enable WOL");
825 Wol::getInstance()->setEnabled(true);
830 // Save power state = on
832 vdr->configSave("General", "Last Power State", "On");
834 // Make sure connection didn't die
835 if (!vdr->isConnected())
837 Command::getInstance()->connectionLost();
841 boxstack->remove(vi);
843 VWelcome* vw = new VWelcome();
846 boxstack->update(vw);
848 // Enter pre-keys here
849 // handleCommand(Remote::SIX);
850 // handleCommand(Remote::UP);
851 // handleCommand(Remote::PLAY);
852 // handleCommand(Remote::DOWN);
853 // handleCommand(Remote::DOWN);
854 // handleCommand(Remote::DOWN);
855 // handleCommand(Remote::OK);
856 // handleCommand(Remote::OK);
857 // handleCommand(Remote::RED);