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
130 // logger->log("Command", Log::DEBUG, "WANT LOCK");
132 pthread_mutex_lock(&masterLock);
134 WaitForSingleObject(masterLock, INFINITE );
136 //logger->log("Command", Log::DEBUG, "LOCKED");
138 VConnect* vconnect = new VConnect();
139 viewman->add(vconnect);
142 // Start method 2 of getting commands in...
149 //logger->log("Command", Log::DEBUG, "UNLOCK");
151 pthread_mutex_unlock(&masterLock);
153 ReleaseMutex(masterLock);
155 button = remote->getButtonPress(2); // FIXME why is this set to 2 and not 0? so it can quit
156 // something happened, lock and process
158 //logger->log("Command", Log::DEBUG, "WANT LOCK");
160 pthread_mutex_lock(&masterLock);
162 WaitForSingleObject(masterLock, INFINITE );
164 //logger->log("Command", Log::DEBUG, "LOCK");
166 if ((button == Remote::NA_NONE) || (button == Remote::NA_UNKNOWN)) continue;
168 if (button != Remote::NA_SIGNAL) handleCommand(button);
169 processMessageQueue();
172 //logger->log("Command", Log::DEBUG, "UNLOCK");
174 pthread_mutex_unlock(&masterLock);
176 ReleaseMutex(masterLock);
180 void Command::postMessage(Message* m)
182 // This is locked here in case the main loop is not waiting for an event, but is processing one
183 // it could be killed but then not react to it because the signal wouldn't cause
184 // remote->getButtonPress to break
185 // locking the mutex ensures that the master thread is waiting on getButtonPress
188 //logger->log("Command", Log::DEBUG, "WANT LOCK");
190 pthread_mutex_lock(&masterLock);
192 WaitForSingleObject(masterLock, INFINITE );
194 //logger->log("Command", Log::DEBUG, "LOCK");
195 MessageQueue::postMessage(m);
198 kill(mainPid, SIGURG);
199 pthread_mutex_unlock(&masterLock);
201 ((RemoteWin*)Remote::getInstance())->Signal();
202 ReleaseMutex(masterLock);
204 //logger->log("Command", Log::DEBUG, "UNLOCK");
207 void Command::postMessageNoLock(Message* m)
209 // As above but use this one if this message is being posted because of a button press
210 // the mutex is already locked, locking around postMessage is not needed as the
211 // queue is guaranteed to be run when the button has been processed
212 MessageQueue::postMessage(m);
215 bool Command::postMessageIfNotBusy(Message* m)
217 // This is for the timers module
218 // If the masterlock is locked then the timers module wants to
220 //logger->log("Command", Log::DEBUG, "TRY LOCK");
222 if (pthread_mutex_trylock(&masterLock) != EBUSY)
224 //logger->log("Command", Log::DEBUG, "LOCK");
225 MessageQueue::postMessage(m);
226 kill(mainPid, SIGURG);
227 pthread_mutex_unlock(&masterLock);
228 //logger->log("Command", Log::DEBUG, "UNLOCK");
236 switch (WaitForSingleObject(masterLock, 0 ))
237 { //FIXME this is not "if not busy" check
238 case WAIT_OBJECT_0: //but with proper argument 0 this did not work
239 // case WAIT_ABANDONED:
240 MessageQueue::postMessage(m);
241 ((RemoteWin*)Remote::getInstance())->Signal();
242 ReleaseMutex(masterLock);
245 case WAIT_ABANDONED: return false;
246 case WAIT_TIMEOUT: return false;
252 void Command::postMessageFromOuterSpace(Message* m)
255 Yet another way of getting messages into Command. This one is for events that
256 are not standard button presses (or UDP generated buttons). It is also not for
257 events that are generated as a result of other events (events that can safely
258 call postMessageNoLock and be guaranteed that the message will be processed
259 because it is known that the queue is currently being processed).
260 This is for events that come from outer space and can occur when the master
261 mutex is locked or not, they need to be queued and executed but it doesn't
263 Actually so far it is for events caused by the video stream - aspect ratio
264 changes. These can occur when the master mutex is locked and so postMessage
265 doesn't work. postMessageNoLock doesn't work because if the mutex *isn't*
266 locked at the time then the message could be sat around a while before
268 The whole message system was at first supposed to prevent the problem of
269 calling a function on an object that had just been deleted, by ordering
270 messages such that all calls are done before object deletion. However,
271 because of the new centralised messaging system and the fact that ViewMan
272 locates the destination object before calling it, the messaging system now
273 allows the kind of sloppy calls it was supposed to stop. Weird huh. This
274 is mentioned here because the video stream might generate an event just as
275 the user hits stop. The mutex is locked, and by the time the message
276 is examined the vvideorec/live has been deleted. This doesn't matter because
277 viewman will drop the message if it can't find the matching object to
279 Finally, all this is fine and dandy, except that I'm not 100% sure that
280 this sloppy postMessage and hope a queued signal will force it to be processed
281 thingy will actually work. Hmmm.
282 Lastly <g>, I will consider making the naming system a little more sane
286 logger->log("Command", Log::DEBUG, "PMFOS called");
287 MessageQueue::postMessage(m);
290 kill(mainPid, SIGURG);
292 ((RemoteWin*)Remote::getInstance())->Signal();
296 void Command::processMessage(Message* m)
298 logger->log("Command", Log::DEBUG, "processing message %i", m->message);
300 // Timer handling is very weird at the mo. Take them out here and convert
301 if (m->message == Message::TIMER)
303 // FIXME - go to one message queue only - then instead of having
304 // objects deriving from messagequeues, make them derive from
305 // messagereceiver - then one messagequeue can deliver any message to anywhere
307 // FIXME - a slight modification - how if messagereceivers were to register
308 // themselves as receivers to avoid the calling-a-deleted-object problem
309 // then only deliver/register/unregister would have to be protected
313 logger->log("Command", Log::DEBUG, "sending timer to %p with parameter %u", m->to, m->parameter);
314 ((TimerReceiver*)m->to)->timercall(m->parameter);
315 // handleCommand(Remote::NA_NONE); // in case any timer has posted messages to viewman,
316 // // run viewman message queue here. FIXME improve this!
319 else if (m->to == this)
323 case Message::STANDBY:
331 case Message::STOP_PLAYBACK:
333 handleCommand(Remote::STOP); // an odd way of doing it, but so simple
336 case Message::STREAM_END:
338 VVideoLive::getInstance()->streamEnd();
342 // Also connection_lost comes from player - anywhere else?
346 case Message::VDR_CONNECTED:
348 doJustConnected((VConnect*)m->from);
351 case Message::SCREENSHOT:
353 Osd::getInstance()->screenShot("/out.jpg");
356 case Message::CONNECTION_LOST:
361 case Message::UDP_BUTTON:
363 handleCommand(m->parameter);
370 logger->log("Command", Log::DEBUG, "Sending message to viewman");
371 viewman->processMessage(m);
375 void Command::handleCommand(int button)
377 if (isStandby && (button != Remote::POWER)) return;
379 if (!connLost && viewman->handleCommand(button)) return; // don't send to viewman if connLost
381 // command was not handled
385 case Remote::DF_LEFT:
386 case Remote::DF_RIGHT:
387 case Remote::VOLUMEUP:
388 case Remote::VOLUMEDOWN:
390 VVolume* v = new VVolume();
392 v->handleCommand(button); // this will draw+show
397 VMute* v = new VMute();
400 viewman->updateView(v);
410 if (!connLost) return; // if connLost, handle Remote::OK
420 Message* m = new Message(); // break into master mutex
421 m->message = Message::SCREENSHOT;
427 void Command::doStandby()
431 Video::getInstance()->signalOn();
432 Led::getInstance()->on();
436 VConnect* vconnect = new VConnect();
437 viewman->add(vconnect);
442 viewman->removeAll();
443 Video::getInstance()->signalOff();
444 viewman->updateView(wallpaper);
446 VDR::getInstance()->configSave("General", "Last Power State", "Off");
447 VDR::getInstance()->disconnect();
448 Led::getInstance()->off();
451 stop(); //different behavoiur on windows, we exit
456 void Command::doFromTheTop(bool which)
460 connLost = new VInfo();
461 connLost->create(360, 200);
462 if (Video::getInstance()->getFormat() == Video::PAL)
463 connLost->setScreenPos(190, 170);
465 connLost->setScreenPos(180, 120);
466 connLost->setOneLiner(tr("Connection lost"));
467 connLost->setDropThrough();
468 connLost->setBorderOn(1);
469 connLost->setTitleBarColour(Colour::DANGER);
470 connLost->okButton();
472 viewman->add(connLost);
473 viewman->updateView(connLost);
474 remote->clearBuffer();
478 VDR::getInstance()->disconnect();
479 viewman->removeAll();
480 viewman->updateView(wallpaper);
482 VConnect* vconnect = new VConnect();
483 viewman->add(vconnect);
488 void Command::doReboot()
490 VDR::getInstance()->disconnect();
492 logger->log("Command", Log::NOTICE, "Reboot");
494 reboot(LINUX_REBOOT_CMD_RESTART);
495 #endif //Would we support this on windows?
498 void Command::connectionLost()
500 Message* m = new Message(); // break into master mutex
501 m->message = Message::CONNECTION_LOST;
503 postMessageNoLock(m);
506 void Command::doJustConnected(VConnect* vconnect)
509 Video* video = Video::getInstance();
510 viewman->removeView(vconnect);
512 VInfo* vi = new VInfo();
513 vi->create(400, 200);
514 if (video->getFormat() == Video::PAL)
515 vi->setScreenPos(170, 200);
517 vi->setScreenPos(160, 150);
519 vi->setOneLiner(tr("Connected, loading config"));
522 viewman->updateView(vi);
524 VDR* vdr = VDR::getInstance();
527 // See if config says to override video format (PAL/NTSC)
528 config = vdr->configLoad("General", "Override Video Format");
531 logger->log("Command", Log::DEBUG, "Override Video Format is present");
533 if ( (!strcmp(config, "PAL") && (video->getFormat() == Video::NTSC))
534 || (!strcmp(config, "NTSC") && (video->getFormat() == Video::PAL)) )
536 // Oh sheesh, need to switch format. Bye bye TV...
538 // Take everything down
539 viewman->removeAll();
540 viewman->removeView(wallpaper);
541 Osd* osd = Osd::getInstance();
545 // Get video and osd back up with the new mode
546 if (!strcmp(config, "PAL"))
548 logger->log("Command", Log::DEBUG, "Switching to PAL");
549 video->init(Video::PAL);
551 else if (!strcmp(config, "NTSC"))
553 logger->log("Command", Log::DEBUG, "Switching to NTSC");
554 video->init(Video::NTSC);
556 osd->init((char*)("/dev/stbgfx"));
558 // Put the wallpaper back
563 vi->create(400, 200);
564 if (video->getFormat() == Video::PAL)
565 vi->setScreenPos(170, 200);
567 vi->setScreenPos(160, 150);
569 vi->setOneLiner(tr("Connected, loading config"));
572 viewman->updateView(vi);
576 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
581 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
584 // Power off if first boot and config says so
589 logger->log("Command", Log::DEBUG, "Load power after boot");
591 config = vdr->configLoad("General", "Power After Boot");
595 if (!STRCASECMP(config, "On"))
597 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
599 else if (!STRCASECMP(config, "Off"))
601 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
606 else if (!STRCASECMP(config, "Last state"))
608 char* lastPowerState = vdr->configLoad("General", "Last Power State");
611 if (!STRCASECMP(lastPowerState, "On"))
613 logger->log("Command", Log::INFO, "Config says Last Power State = On");
615 else if (!STRCASECMP(lastPowerState, "Off"))
617 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
624 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
629 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
634 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
640 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
645 // Go S-Video if config says so
647 config = vdr->configLoad("TV", "Connection");
651 if (!STRCASECMP(config, "S-Video"))
653 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
654 video->setConnection(Video::SVIDEO);
658 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
659 video->setConnection(Video::COMPOSITERGB);
665 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
670 config = vdr->configLoad("General", "Remote type");
674 if (!STRCASECMP(config, "New"))
676 logger->log("Command", Log::INFO, "Switching to New remote type");
677 remote->setRemoteType(Remote::NEWREMOTE);
681 logger->log("Command", Log::INFO, "Switching to Old remote type");
682 remote->setRemoteType(Remote::OLDREMOTE);
688 logger->log("Command", Log::INFO, "Config General/Remote type not found");
689 remote->setRemoteType(Remote::OLDREMOTE);
692 // Get TV aspect ratio
694 config = vdr->configLoad("TV", "Aspect");
697 if (!STRCASECMP(config, "16:9"))
699 logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
700 video->setTVsize(Video::ASPECT16X9);
704 logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
705 video->setTVsize(Video::ASPECT4X3);
711 logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
712 video->setTVsize(Video::ASPECT4X3);
715 config = vdr->configLoad("TV", "Widemode");
718 if (!STRCASECMP(config, "Letterbox"))
720 logger->log("Command", Log::INFO, "Setting letterbox mode");
721 video->setMode(Video::LETTERBOX);
725 logger->log("Command", Log::INFO, "Setting chop-sides mode");
726 video->setMode(Video::NORMAL);
732 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
733 video->setMode(Video::NORMAL);
736 config = vdr->configLoad("Advanced", "TCP receive window");
739 size_t newTCPsize = atoi(config);
742 logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
743 vdr->setReceiveWindow(newTCPsize);
747 logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
748 vdr->setReceiveWindow(2048); // Default
754 // Save power state = on
756 vdr->configSave("General", "Last Power State", "On");
758 // Make sure connection didn't die
759 if (!vdr->isConnected())
761 Command::getInstance()->connectionLost();
765 viewman->removeView(vi);
767 VWelcome* vw = new VWelcome();
770 viewman->updateView(vw);
772 // Enter pre-keys here
773 // handleCommand(Remote::THREE);
774 // handleCommand(Remote::UP);
775 // handleCommand(Remote::OK);
776 // handleCommand(Remote::OK);
777 // handleCommand(Remote::PLAY);
778 // handleCommand(Remote::DOWN);
779 // handleCommand(Remote::DOWN);
780 // handleCommand(Remote::OK);