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"
54 Command* Command::instance = NULL;
73 Command* Command::getInstance()
78 int Command::init(bool tcrashed, char* tServer)
80 if (initted) return 0;
85 logger = Log::getInstance();
86 boxstack = BoxStack::getInstance();
87 remote = Remote::getInstance();
89 remote->InitHWCListwithDefaults();
91 if (!logger || !boxstack || !remote)
97 pthread_mutex_init(&masterLock, NULL);
99 masterLock=CreateMutex(NULL,FALSE,NULL);
105 int Command::shutdown()
107 if (!initted) return 0;
114 // VDR::getInstance()->cancelFindingServer();
119 void Command::doWallpaper()
121 Video* video = Video::getInstance();
124 Boxx* bbg = new Boxx();
125 bbg->setSize(video->getScreenWidth(), video->getScreenHeight());
127 bbg->fillColour(Colour::VIDEOBLUE);
129 boxstack->update(bbg);
130 boxstack->remove(bbg);
133 WJpeg* wallpaperj = new WJpeg();
134 wallpaperj->setSize(video->getScreenWidth(), video->getScreenHeight());
135 wallpaperj->createBuffer();
137 if (video->getFormat() == Video::PAL)
139 logger->log("Command", Log::DEBUG, "PAL wallpaper selected");
140 wallpaperj->init("/wallpaperPAL.jpg");
144 logger->log("Command", Log::DEBUG, "NTSC wallpaper selected");
145 wallpaperj->init("/wallpaperNTSC.jpg");
149 boxstack->add(wallpaperj);
150 boxstack->update(wallpaperj);
152 wallpaper = wallpaperj;
157 if (!initted) return;
164 Video::getInstance()->signalOn();
165 Led::getInstance()->on();
169 // End of startup. Lock the mutex and put the first view up
170 // logger->log("Command", Log::DEBUG, "WANT LOCK");
172 pthread_mutex_lock(&masterLock);
174 WaitForSingleObject(masterLock, INFINITE );
176 //logger->log("Command", Log::DEBUG, "LOCKED");
184 VConnect* vconnect = new VConnect(server);
185 boxstack->add(vconnect);
189 // Start method 2 of getting commands in...
196 //logger->log("Command", Log::DEBUG, "UNLOCK");
198 pthread_mutex_unlock(&masterLock);
200 ReleaseMutex(masterLock);
202 button = remote->getButtonPress(2); // FIXME why is this set to 2 and not 0? so it can quit
203 // something happened, lock and process
205 // logger->log("Command", Log::DEBUG, "WANT LOCK");
207 pthread_mutex_lock(&masterLock);
209 WaitForSingleObject(masterLock, INFINITE );
211 // logger->log("Command", Log::DEBUG, "LOCK");
213 if ((button == Remote::NA_NONE) /*|| (button == Remote::NA_UNKNOWN)*/) continue;
215 if (button != Remote::NA_SIGNAL) handleCommand(button);
216 processMessageQueue();
219 //logger->log("Command", Log::DEBUG, "UNLOCK");
221 pthread_mutex_unlock(&masterLock);
223 ReleaseMutex(masterLock);
227 void Command::postMessage(Message* m)
229 // This is locked here in case the main loop is not waiting for an event, but is processing one
230 // it could be killed but then not react to it because the signal wouldn't cause
231 // remote->getButtonPress to break
232 // locking the mutex ensures that the master thread is waiting on getButtonPress
235 //logger->log("Command", Log::DEBUG, "WANT LOCK");
237 pthread_mutex_lock(&masterLock);
239 WaitForSingleObject(masterLock, INFINITE );
241 //logger->log("Command", Log::DEBUG, "LOCK");
242 MessageQueue::postMessage(m);
245 kill(mainPid, SIGURG);
246 pthread_mutex_unlock(&masterLock);
248 ((RemoteWin*)Remote::getInstance())->Signal();
249 ReleaseMutex(masterLock);
251 //logger->log("Command", Log::DEBUG, "UNLOCK");
254 void Command::postMessageNoLock(Message* m)
256 // As above but use this one if this message is being posted because of a button press
257 // the mutex is already locked, locking around postMessage is not needed as the
258 // queue is guaranteed to be run when the button has been processed
259 MessageQueue::postMessage(m);
262 bool Command::postMessageIfNotBusy(Message* m)
264 // Used for Windows mouse events
266 //logger->log("Command", Log::DEBUG, "TRY LOCK");
268 if (pthread_mutex_trylock(&masterLock) != EBUSY)
270 //logger->log("Command", Log::DEBUG, "LOCK");
271 MessageQueue::postMessage(m);
272 kill(mainPid, SIGURG);
273 pthread_mutex_unlock(&masterLock);
274 //logger->log("Command", Log::DEBUG, "UNLOCK");
282 switch (WaitForSingleObject(masterLock, 0 ))
283 { //FIXME this is not "if not busy" check
284 case WAIT_OBJECT_0: //but with proper argument 0 this did not work
285 // case WAIT_ABANDONED:
286 MessageQueue::postMessage(m);
287 ((RemoteWin*)Remote::getInstance())->Signal();
288 ReleaseMutex(masterLock);
291 case WAIT_ABANDONED: return false;
292 case WAIT_TIMEOUT: return false;
298 void Command::postMessageFromOuterSpace(Message* m)
301 Yet another way of getting messages into Command. This one is for events that
302 are not standard button presses (or UDP generated buttons). It is also not for
303 events that are generated as a result of other events (events that can safely
304 call postMessageNoLock and be guaranteed that the message will be processed
305 because it is known that the queue is currently being processed).
306 This is for events that come from outer space and can occur when the master
307 mutex is locked or not, they need to be queued and executed but it doesn't
309 Actually so far it is for events caused by the video stream - aspect ratio
310 changes. These can occur when the master mutex is locked and so postMessage
311 doesn't work. postMessageNoLock doesn't work because if the mutex *isn't*
312 locked at the time then the message could be sat around a while before
314 The whole message system was at first supposed to prevent the problem of
315 calling a function on an object that had just been deleted, by ordering
316 messages such that all calls are done before object deletion. However,
317 because of the new centralised messaging system and the fact that BoxStack
318 locates the destination object before calling it, the messaging system now
319 allows the kind of sloppy calls it was supposed to stop. Weird huh. This
320 is mentioned here because the video stream might generate an event just as
321 the user hits stop. The mutex is locked, and by the time the message
322 is examined the vvideorec/live has been deleted. This doesn't matter because
323 boxstack will drop the message if it can't find the matching object to
325 Finally, all this is fine and dandy, except that I'm not 100% sure that
326 this sloppy postMessage and hope a queued signal will force it to be processed
327 thingy will actually work. Hmmm.
328 Lastly <g>, I will consider making the naming system a little more sane
332 logger->log("Command", Log::DEBUG, "PMFOS called");
333 MessageQueue::postMessage(m);
336 kill(mainPid, SIGURG);
338 ((RemoteWin*)Remote::getInstance())->Signal();
342 void Command::processMessage(Message* m)
344 // FIXME - a slight modification - how if messagereceivers were to register
345 // themselves as receivers to avoid the calling-a-deleted-object problem
346 // then only deliver/register/unregister would have to be protected
348 logger->log("Command", Log::DEBUG, "processing message %i", m->message);
356 case Message::STOP_PLAYBACK:
358 handleCommand(Remote::STOP); // an odd way of doing it, but so simple
361 // Also connection_lost comes from player - anywhere else?
365 case Message::VDR_CONNECTED:
367 doJustConnected((VConnect*)m->from);
370 case Message::SCREENSHOT:
372 Osd::getInstance()->screenShot("/out.jpg");
375 case Message::CONNECTION_LOST:
380 case Message::UDP_BUTTON:
382 handleCommand(m->parameter);
385 case Message::CHANGE_LANGUAGE:
387 boxstack->removeAll();
388 boxstack->update(wallpaper);
390 if (!VDR::getInstance()->isConnected()) { connectionLost(); break; }
391 VWelcome* vw = new VWelcome();
394 boxstack->update(vw);
397 case Message::LAST_VIEW_CLOSE:
399 // Shouldn't be done like this. Some generic message pass back from vinfo perhaps
406 // VWelcome* vw = new VWelcome();
408 // boxstack->add(vw);
409 // boxstack->update(vw);
419 Instead of sending through the boxstack, implement a more generic MessageReceiver interface
420 and have potential receivers register with something
421 When a message needs to be delivered, check if the receiver is still registered, if so, deliver the message
422 This could all be done using the existing big command mutex to keep it simple
425 logger->log("Command", Log::DEBUG, "Sending message to boxstack");
426 boxstack->processMessage(m);
430 void Command::handleCommand(int button)
432 if (isStandby && (button != Remote::POWER)) return;
433 if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost
435 // command was not handled
439 case Remote::DF_LEFT:
440 case Remote::DF_RIGHT:
441 case Remote::VOLUMEUP:
442 case Remote::VOLUMEDOWN:
444 VVolume* v = new VVolume();
446 v->handleCommand(button); // this will draw+show
451 VMute* v = new VMute();
465 if (!connLost) return; // if connLost, handle Remote::OK
475 Message* m = new Message(); // break into master mutex
476 m->message = Message::SCREENSHOT;
482 void Command::doStandby()
486 Video::getInstance()->signalOn();
487 Led::getInstance()->on();
491 VConnect* vconnect = new VConnect(server);
492 boxstack->add(vconnect);
497 boxstack->removeAll();
498 Video::getInstance()->signalOff();
499 boxstack->update(wallpaper);
501 VDR::getInstance()->configSave("General", "Last Power State", "Off");
502 VDR::getInstance()->disconnect();
503 Led::getInstance()->off();
506 stop(); //different behavoiur on windows, we exit
511 void Command::doFromTheTop(bool which)
517 logger->log("Command", Log::NOTICE, "Connection lost dialog already present");
521 logger->log("Command", Log::NOTICE, "Doing connection lost dialog");
522 connLost = new VInfo();
523 connLost->setSize(360, 200);
524 connLost->createBuffer();
525 if (Video::getInstance()->getFormat() == Video::PAL)
526 connLost->setPosition(190, 170);
528 connLost->setPosition(180, 120);
529 connLost->setOneLiner(tr("Connection lost"));
530 connLost->setDropThrough();
531 connLost->setBorderOn(1);
532 connLost->setTitleBarColour(Colour::DANGER);
533 connLost->okButton();
535 boxstack->add(connLost);
536 boxstack->update(connLost);
537 remote->clearBuffer();
541 VDR::getInstance()->disconnect();
542 boxstack->removeAll();
543 boxstack->update(wallpaper);
547 remote->clearBuffer();
549 // at this point, everything should be reset to first-go
551 VConnect* vconnect = new VConnect(server);
552 boxstack->add(vconnect);
557 void Command::doReboot()
559 VDR::getInstance()->disconnect();
561 logger->log("Command", Log::NOTICE, "Reboot");
563 reboot(LINUX_REBOOT_CMD_RESTART);
564 #endif //Would we support this on windows?
567 void Command::connectionLost()
569 Message* m = new Message(); // break into master mutex
570 m->message = Message::CONNECTION_LOST;
572 postMessageFromOuterSpace(m);
575 void Command::buildCrashedBox()
577 VInfo* crash = new VInfo();
578 crash->setSize(360, 250);
579 crash->createBuffer();
580 if (Video::getInstance()->getFormat() == Video::PAL)
581 crash->setPosition(190, 146);
583 crash->setPosition(180, 96);
584 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.");
585 crash->setBorderOn(1);
586 crash->setTitleBarColour(Colour::DANGER);
588 crash->setExitable();
590 boxstack->add(crash);
591 boxstack->update(crash);
594 void Command::doJustConnected(VConnect* vconnect)
597 if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }
599 Video* video = Video::getInstance();
600 Audio* audio = Audio::getInstance();
601 boxstack->remove(vconnect);
603 VInfo* vi = new VInfo();
604 vi->setSize(400, 200);
606 if (video->getFormat() == Video::PAL)
607 vi->setPosition(170, 200);
609 vi->setPosition(160, 150);
610 vi->setOneLiner(tr("Connected, loading config"));
613 boxstack->update(vi);
615 VDR* vdr = VDR::getInstance();
618 // See if config says to override video format (PAL/NTSC)
619 config = vdr->configLoad("General", "Override Video Format");
622 logger->log("Command", Log::DEBUG, "Override Video Format is present");
624 if ( (!strcmp(config, "PAL") && (video->getFormat() == Video::NTSC))
625 || (!strcmp(config, "NTSC") && (video->getFormat() == Video::PAL)) )
627 // Oh sheesh, need to switch format. Bye bye TV...
629 // Take everything down
630 boxstack->removeAll();
631 boxstack->remove(wallpaper);
632 Osd* osd = Osd::getInstance();
636 // Get video and osd back up with the new mode
637 if (!strcmp(config, "PAL"))
639 logger->log("Command", Log::DEBUG, "Switching to PAL");
640 video->init(Video::PAL);
642 else if (!strcmp(config, "NTSC"))
644 logger->log("Command", Log::DEBUG, "Switching to NTSC");
645 video->init(Video::NTSC);
647 osd->init((char*)("/dev/stbgfx"));
649 // Put the wallpaper back
654 vi->setSize(400, 200);
656 if (video->getFormat() == Video::PAL)
657 vi->setPosition(170, 200);
659 vi->setPosition(160, 150);
661 vi->setOneLiner(tr("Connected, loading config"));
664 boxstack->update(vi);
668 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
673 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
676 // Power off if first boot and config says so
681 logger->log("Command", Log::DEBUG, "Load power after boot");
683 config = vdr->configLoad("General", "Power After Boot");
687 if (!STRCASECMP(config, "On"))
689 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
691 else if (!STRCASECMP(config, "Off"))
693 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
698 else if (!STRCASECMP(config, "Last state"))
700 char* lastPowerState = vdr->configLoad("General", "Last Power State");
703 if (!STRCASECMP(lastPowerState, "On"))
705 logger->log("Command", Log::INFO, "Config says Last Power State = On");
707 else if (!STRCASECMP(lastPowerState, "Off"))
709 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
716 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
721 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
726 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
732 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
737 // Go S-Video if config says so
739 config = vdr->configLoad("TV", "Connection");
743 if (!STRCASECMP(config, "S-Video"))
745 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
746 video->setConnection(Video::SVIDEO);
750 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
751 video->setConnection(Video::COMPOSITERGB);
757 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
762 config = vdr->configLoad("General", "Remote type");
766 if (!STRCASECMP(config, "New"))
768 logger->log("Command", Log::INFO, "Switching to New remote type");
769 remote->setRemoteType(Remote::NEWREMOTE);
773 logger->log("Command", Log::INFO, "Switching to Old remote type");
774 remote->setRemoteType(Remote::OLDREMOTE);
780 logger->log("Command", Log::INFO, "Config General/Remote type not found");
781 remote->setRemoteType(Remote::OLDREMOTE);
787 // Get TV aspect ratio
789 config = vdr->configLoad("TV", "Aspect");
792 if (!STRCASECMP(config, "16:9"))
794 logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
795 video->setTVsize(Video::ASPECT16X9);
799 logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
800 video->setTVsize(Video::ASPECT4X3);
806 logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
807 video->setTVsize(Video::ASPECT4X3);
810 config = vdr->configLoad("TV", "Widemode");
813 if (!STRCASECMP(config, "Letterbox"))
815 logger->log("Command", Log::INFO, "Setting letterbox mode");
816 video->setMode(Video::LETTERBOX);
820 logger->log("Command", Log::INFO, "Setting chop-sides mode");
821 video->setMode(Video::NORMAL);
827 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
828 video->setMode(Video::NORMAL);
831 config = vdr->configLoad("Advanced", "TCP receive window");
834 size_t newTCPsize = atoi(config);
837 logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
838 vdr->setReceiveWindow(newTCPsize);
842 logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
843 vdr->setReceiveWindow(2048); // Default
846 config = vdr->configLoad("Advanced", "Disable WOL");
849 if (!STRCASECMP(config, "Yes"))
851 logger->log("Command", Log::INFO, "Config says disable WOL");
852 Wol::getInstance()->setEnabled(false);
856 logger->log("Command", Log::INFO, "Config says enable WOL");
857 Wol::getInstance()->setEnabled(true);
864 logger->log("Command", Log::INFO, "By default, enable WOL");
865 Wol::getInstance()->setEnabled(true);
867 /* device dependend config */
868 audio->loadOptionsfromServer(vdr);
869 video->loadOptionsfromServer(vdr);
870 remote->loadOptionsfromServer(vdr);
873 // Save power state = on
875 vdr->configSave("General", "Last Power State", "On");
877 // Make sure connection didn't die
878 if (!vdr->isConnected())
880 Command::getInstance()->connectionLost();
884 boxstack->remove(vi);
886 VWelcome* vw = new VWelcome();
889 boxstack->update(vw);
891 // Enter pre-keys here
892 // handleCommand(Remote::OK);
893 // handleCommand(Remote::ONE);
894 // handleCommand(Remote::SIX);
895 // handleCommand(Remote::OK);
896 // handleCommand(Remote::UP);
897 // handleCommand(Remote::PLAY);
898 // handleCommand(Remote::DOWN);
899 // handleCommand(Remote::DOWN);
900 // handleCommand(Remote::DOWN);
901 // handleCommand(Remote::OK);
902 // handleCommand(Remote::RED);