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
23 #include "remotewin.h"
26 Command* Command::instance = NULL;
43 Command* Command::getInstance()
50 if (initted) return 0;
53 logger = Log::getInstance();
54 viewman = ViewMan::getInstance();
55 remote = Remote::getInstance();
57 if (!logger || !viewman || !remote)
63 pthread_mutex_init(&masterLock, NULL);
65 masterLock=CreateMutex(NULL,FALSE,NULL);
71 int Command::shutdown()
73 if (!initted) return 0;
80 // VDR::getInstance()->cancelFindingServer();
85 void Command::doWallpaper()
87 Video* video = Video::getInstance();
91 v->create(video->getScreenWidth(), video->getScreenHeight());
92 v->setBackgroundColour(Colour::VIDEOBLUE);
95 viewman->updateView(v);
96 viewman->removeView(v);
99 wallpaper = new VWallpaper();
100 if (video->getFormat() == Video::PAL)
102 logger->log("Command", Log::DEBUG, "PAL wallpaper selected");
103 wallpaper->init("/wallpaperPAL.jpg");
107 logger->log("Command", Log::DEBUG, "NTSC wallpaper selected");
108 wallpaper->init("/wallpaperNTSC.jpg");
111 viewman->add(wallpaper);
112 viewman->updateView(wallpaper);
117 if (!initted) return;
124 Video::getInstance()->signalOn();
125 Led::getInstance()->on();
129 // End of startup. Lock the mutex and put the first view up
131 pthread_mutex_lock(&masterLock);
133 WaitForSingleObject(masterLock, INFINITE );
135 VConnect* vconnect = new VConnect();
136 viewman->add(vconnect);
139 // Start method 2 of getting commands in...
147 pthread_mutex_unlock(&masterLock);
149 ReleaseMutex(masterLock);
151 button = remote->getButtonPress(2); // FIXME why is this set to 2 and not 0? so it can quit
152 // something happened, lock and process
155 pthread_mutex_lock(&masterLock);
157 WaitForSingleObject(masterLock, INFINITE );
160 if ((button == Remote::NA_NONE) || (button == Remote::NA_UNKNOWN)) continue;
162 if (button != Remote::NA_SIGNAL) handleCommand(button);
163 processMessageQueue();
167 pthread_mutex_unlock(&masterLock);
169 ReleaseMutex(masterLock);
173 void Command::postMessage(Message* m)
175 // This is locked here in case the main loop is not waiting for an event, but is processing one
176 // it could be killed but then not react to it because the signal wouldn't cause
177 // remote->getButtonPress to break
178 // locking the mutex ensures that the master thread is waiting on getButtonPress
182 pthread_mutex_lock(&masterLock);
184 WaitForSingleObject(masterLock, INFINITE );
186 MessageQueue::postMessage(m);
189 kill(mainPid, SIGURG);
190 pthread_mutex_unlock(&masterLock);
192 ((RemoteWin*)Remote::getInstance())->Signal();
193 ReleaseMutex(masterLock);
197 void Command::postMessageNoLock(Message* m)
199 // As above but use this one if this message is being posted because of a button press
200 // the mutex is already locked, locking around postMessage is not needed as the
201 // queue is guaranteed to be run when the button has been processed
202 MessageQueue::postMessage(m);
205 bool Command::postMessageIfNotBusy(Message* m)
207 // This is for the timers module
208 // If the masterlock is locked then the timers module wants to
211 if (pthread_mutex_trylock(&masterLock) != EBUSY)
213 MessageQueue::postMessage(m);
214 kill(mainPid, SIGURG);
215 pthread_mutex_unlock(&masterLock);
223 switch (WaitForSingleObject(masterLock, 0 ))
224 { //FIXME this is not "if not busy" check
225 case WAIT_OBJECT_0: //but with proper argument 0 this did not work
226 // case WAIT_ABANDONED:
227 MessageQueue::postMessage(m);
228 ((RemoteWin*)Remote::getInstance())->Signal();
229 ReleaseMutex(masterLock);
232 case WAIT_ABANDONED: return false;
233 case WAIT_TIMEOUT: return false;
239 void Command::postMessageFromOuterSpace(Message* m)
242 Yet another way of getting messages into Command. This one is for events that
243 are not standard button presses (or UDP generated buttons). It is also not for
244 events that are generated as a result of other events (events that can safely
245 call postMessageNoLock and be guaranteed that the message will be processed
246 because it is known that the queue is currently being processed).
247 This is for events that come from outer space and can occur when the master
248 mutex is locked or not, they need to be queued and executed but it doesn't
250 Actually so far it is for events caused by the video stream - aspect ratio
251 changes. These can occur when the master mutex is locked and so postMessage
252 doesn't work. postMessageNoLock doesn't work because if the mutex *isn't*
253 locked at the time then the message could be sat around a while before
255 The whole message system was at first supposed to prevent the problem of
256 calling a function on an object that had just been deleted, by ordering
257 messages such that all calls are done before object deletion. However,
258 because of the new centralised messaging system and the fact that ViewMan
259 locates the destination object before calling it, the messaging system now
260 allows the kind of sloppy calls it was supposed to stop. Weird huh. This
261 is mentioned here because the video stream might generate an event just as
262 the user hits stop. The mutex is locked, and by the time the message
263 is examined the vvideorec/live has been deleted. This doesn't matter because
264 viewman will drop the message if it can't find the matching object to
266 Finally, all this is fine and dandy, except that I'm not 100% sure that
267 this sloppy postMessage and hope a queued signal will force it to be processed
268 thingy will actually work. Hmmm.
269 Lastly <g>, I will consider making the naming system a little more sane
273 logger->log("Command", Log::DEBUG, "PMFOS called");
274 MessageQueue::postMessage(m);
275 kill(mainPid, SIGURG);
277 // FIXME: Marten - if this actually works on the MVP then you will need
278 // to come up with the Windows equivalent here.
282 void Command::processMessage(Message* m)
284 logger->log("Command", Log::DEBUG, "processing message %i", m->message);
286 // Timer handling is very weird at the mo. Take them out here and convert
287 if (m->message == Message::TIMER)
289 // FIXME - go to one message queue only - then instead of having
290 // objects deriving from messagequeues, make them derive from
291 // messagereceiver - then one messagequeue can deliver any message to anywhere
293 // FIXME - a slight modification - how if messagereceivers were to register
294 // themselves as receivers to avoid the calling-a-deleted-object problem
295 // then only deliver/register/unregister would have to be protected
299 logger->log("Command", Log::DEBUG, "sending timer");
300 ((TimerReceiver*)m->to)->timercall(m->parameter);
301 // handleCommand(Remote::NA_NONE); // in case any timer has posted messages to viewman,
302 // // run viewman message queue here. FIXME improve this!
305 else if (m->to == this)
309 case Message::STANDBY:
317 case Message::STOP_PLAYBACK:
319 handleCommand(Remote::STOP); // an odd way of doing it, but so simple
322 case Message::STREAM_END:
324 VVideoLive::getInstance()->streamEnd();
328 // Also connection_lost comes from player - anywhere else?
332 case Message::VDR_CONNECTED:
334 doJustConnected((VConnect*)m->from);
337 case Message::SCREENSHOT:
339 Osd::getInstance()->screenShot("/out.jpg");
342 case Message::CONNECTION_LOST:
347 case Message::UDP_BUTTON:
349 handleCommand(m->parameter);
356 logger->log("Command", Log::DEBUG, "Sending message to viewman");
357 viewman->processMessage(m);
361 void Command::handleCommand(int button)
363 if (isStandby && (button != Remote::POWER)) return;
365 if (!connLost && viewman->handleCommand(button)) return; // don't send to viewman if connLost
367 // command was not handled
371 case Remote::DF_LEFT:
372 case Remote::DF_RIGHT:
373 case Remote::VOLUMEUP:
374 case Remote::VOLUMEDOWN:
376 VVolume* v = new VVolume();
378 v->handleCommand(button); // this will draw+show
383 VMute* v = new VMute();
386 viewman->updateView(v);
396 if (!connLost) return; // if connLost, handle Remote::OK
406 Message* m = new Message(); // break into master mutex
407 m->message = Message::SCREENSHOT;
413 void Command::doStandby()
417 Video::getInstance()->signalOn();
418 Led::getInstance()->on();
422 VConnect* vconnect = new VConnect();
423 viewman->add(vconnect);
428 viewman->removeAll();
429 Video::getInstance()->signalOff();
430 viewman->updateView(wallpaper);
432 VDR::getInstance()->configSave("General", "Last Power State", "Off");
433 VDR::getInstance()->disconnect();
434 Led::getInstance()->off();
437 stop(); //different behavoiur on windows, we exit
442 void Command::doFromTheTop(bool which)
446 connLost = new VInfo();
447 connLost->create(360, 200);
448 if (Video::getInstance()->getFormat() == Video::PAL)
449 connLost->setScreenPos(190, 170);
451 connLost->setScreenPos(180, 120);
452 connLost->setOneLiner(tr("Connection lost"));
453 connLost->setDropThrough();
454 connLost->setBorderOn(1);
455 connLost->setTitleBarColour(Colour::DANGER);
456 connLost->okButton();
458 viewman->add(connLost);
459 viewman->updateView(connLost);
460 remote->clearBuffer();
464 VDR::getInstance()->disconnect();
465 viewman->removeAll();
466 viewman->updateView(wallpaper);
468 VConnect* vconnect = new VConnect();
469 viewman->add(vconnect);
474 void Command::doReboot()
476 VDR::getInstance()->disconnect();
478 logger->log("Command", Log::NOTICE, "Reboot");
480 reboot(LINUX_REBOOT_CMD_RESTART);
481 #endif //Would we support this on windows?
484 void Command::connectionLost()
486 Message* m = new Message(); // break into master mutex
487 m->message = Message::CONNECTION_LOST;
489 postMessageNoLock(m);
492 void Command::doJustConnected(VConnect* vconnect)
495 Video* video = Video::getInstance();
496 viewman->removeView(vconnect);
498 VInfo* vi = new VInfo();
499 vi->create(400, 200);
500 if (video->getFormat() == Video::PAL)
501 vi->setScreenPos(170, 200);
503 vi->setScreenPos(160, 150);
505 vi->setOneLiner(tr("Connected, loading config"));
508 viewman->updateView(vi);
510 VDR* vdr = VDR::getInstance();
513 // See if config says to override video format (PAL/NTSC)
514 config = vdr->configLoad("General", "Override Video Format");
517 logger->log("Command", Log::DEBUG, "Override Video Format is present");
519 if ( (!strcmp(config, "PAL") && (video->getFormat() == Video::NTSC))
520 || (!strcmp(config, "NTSC") && (video->getFormat() == Video::PAL)) )
522 // Oh sheesh, need to switch format. Bye bye TV...
524 // Take everything down
525 viewman->removeAll();
526 viewman->removeView(wallpaper);
527 Osd* osd = Osd::getInstance();
531 // Get video and osd back up with the new mode
532 if (!strcmp(config, "PAL"))
534 logger->log("Command", Log::DEBUG, "Switching to PAL");
535 video->init(Video::PAL);
537 else if (!strcmp(config, "NTSC"))
539 logger->log("Command", Log::DEBUG, "Switching to NTSC");
540 video->init(Video::NTSC);
542 osd->init((char*)("/dev/stbgfx"));
544 // Put the wallpaper back
549 vi->create(400, 200);
550 if (video->getFormat() == Video::PAL)
551 vi->setScreenPos(170, 200);
553 vi->setScreenPos(160, 150);
555 vi->setOneLiner(tr("Connected, loading config"));
558 viewman->updateView(vi);
562 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
567 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
570 // Power off if first boot and config says so
575 logger->log("Command", Log::DEBUG, "Load power after boot");
577 config = vdr->configLoad("General", "Power After Boot");
581 if (!STRCASECMP(config, "On"))
583 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
585 else if (!STRCASECMP(config, "Off"))
587 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
592 else if (!STRCASECMP(config, "Last state"))
594 char* lastPowerState = vdr->configLoad("General", "Last Power State");
597 if (!STRCASECMP(lastPowerState, "On"))
599 logger->log("Command", Log::INFO, "Config says Last Power State = On");
601 else if (!STRCASECMP(lastPowerState, "Off"))
603 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
610 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
615 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
620 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
626 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
631 // Go S-Video if config says so
633 config = vdr->configLoad("TV", "Connection");
637 if (!STRCASECMP(config, "S-Video"))
639 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
640 video->setConnection(Video::SVIDEO);
644 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
645 video->setConnection(Video::COMPOSITERGB);
651 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
656 config = vdr->configLoad("General", "Remote type");
660 if (!STRCASECMP(config, "New"))
662 logger->log("Command", Log::INFO, "Switching to New remote type");
663 remote->setRemoteType(Remote::NEWREMOTE);
667 logger->log("Command", Log::INFO, "Switching to Old remote type");
668 remote->setRemoteType(Remote::OLDREMOTE);
674 logger->log("Command", Log::INFO, "Config General/Remote type not found");
675 remote->setRemoteType(Remote::OLDREMOTE);
678 // Get TV aspect ratio
680 config = vdr->configLoad("TV", "Aspect");
683 if (!STRCASECMP(config, "16:9"))
685 logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
686 video->setTVsize(Video::ASPECT16X9);
690 logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
691 video->setTVsize(Video::ASPECT4X3);
697 logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
698 video->setTVsize(Video::ASPECT4X3);
701 config = vdr->configLoad("TV", "Widemode");
704 if (!STRCASECMP(config, "Letterbox"))
706 logger->log("Command", Log::INFO, "Setting letterbox mode");
707 video->setMode(Video::LETTERBOX);
711 logger->log("Command", Log::INFO, "Setting chop-sides mode");
712 video->setMode(Video::NORMAL);
718 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
719 video->setMode(Video::NORMAL);
722 config = vdr->configLoad("Advanced", "TCP receive window");
725 size_t newTCPsize = atoi(config);
728 logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
729 vdr->setReceiveWindow(newTCPsize);
733 logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
734 vdr->setReceiveWindow(2048); // Default
740 // Save power state = on
742 vdr->configSave("General", "Last Power State", "On");
744 // Make sure connection didn't die
745 if (!vdr->isConnected())
747 Command::getInstance()->connectionLost();
751 viewman->removeView(vi);
753 VWelcome* vw = new VWelcome();
756 viewman->updateView(vw);
758 // Enter pre-keys here
759 // handleCommand(Remote::THREE);
760 // handleCommand(Remote::SKIPFORWARD);
761 // handleCommand(Remote::OK);
762 // handleCommand(Remote::PLAY);
763 // handleCommand(Remote::OK);
764 // handleCommand(Remote::DOWN);
765 // handleCommand(Remote::DOWN);
766 // handleCommand(Remote::DOWN);
767 // handleCommand(Remote::OK);