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);
366 case Message::CHANGE_LANGUAGE:
368 viewman->removeAll();
369 viewman->updateView(wallpaper);
371 VWelcome* vw = new VWelcome();
374 viewman->updateView(vw);
377 case Message::LAST_VIEW_CLOSE:
379 // VWelcome* vw = new VWelcome();
382 // viewman->updateView(vw);
390 logger->log("Command", Log::DEBUG, "Sending message to viewman");
391 viewman->processMessage(m);
395 void Command::handleCommand(int button)
397 if (isStandby && (button != Remote::POWER)) return;
399 if (!connLost && viewman->handleCommand(button)) return; // don't send to viewman if connLost
401 // command was not handled
405 case Remote::DF_LEFT:
406 case Remote::DF_RIGHT:
407 case Remote::VOLUMEUP:
408 case Remote::VOLUMEDOWN:
410 VVolume* v = new VVolume();
412 v->handleCommand(button); // this will draw+show
417 VMute* v = new VMute();
420 viewman->updateView(v);
430 if (!connLost) return; // if connLost, handle Remote::OK
440 Message* m = new Message(); // break into master mutex
441 m->message = Message::SCREENSHOT;
447 void Command::doStandby()
451 Video::getInstance()->signalOn();
452 Led::getInstance()->on();
456 VConnect* vconnect = new VConnect();
457 viewman->add(vconnect);
462 viewman->removeAll();
463 Video::getInstance()->signalOff();
464 viewman->updateView(wallpaper);
466 VDR::getInstance()->configSave("General", "Last Power State", "Off");
467 VDR::getInstance()->disconnect();
468 Led::getInstance()->off();
471 stop(); //different behavoiur on windows, we exit
476 void Command::doFromTheTop(bool which)
480 connLost = new VInfo();
481 connLost->create(360, 200);
482 if (Video::getInstance()->getFormat() == Video::PAL)
483 connLost->setScreenPos(190, 170);
485 connLost->setScreenPos(180, 120);
486 connLost->setOneLiner(tr("Connection lost"));
487 connLost->setDropThrough();
488 connLost->setBorderOn(1);
489 connLost->setTitleBarColour(Colour::DANGER);
490 connLost->okButton();
492 viewman->add(connLost);
493 viewman->updateView(connLost);
494 remote->clearBuffer();
498 VDR::getInstance()->disconnect();
499 viewman->removeAll();
500 viewman->updateView(wallpaper);
502 VConnect* vconnect = new VConnect();
503 viewman->add(vconnect);
508 void Command::doReboot()
510 VDR::getInstance()->disconnect();
512 logger->log("Command", Log::NOTICE, "Reboot");
514 reboot(LINUX_REBOOT_CMD_RESTART);
515 #endif //Would we support this on windows?
518 void Command::connectionLost()
520 Message* m = new Message(); // break into master mutex
521 m->message = Message::CONNECTION_LOST;
523 postMessageNoLock(m);
526 void Command::doJustConnected(VConnect* vconnect)
529 Video* video = Video::getInstance();
530 viewman->removeView(vconnect);
532 VInfo* vi = new VInfo();
533 vi->create(400, 200);
534 if (video->getFormat() == Video::PAL)
535 vi->setScreenPos(170, 200);
537 vi->setScreenPos(160, 150);
539 vi->setOneLiner(tr("Connected, loading config"));
542 viewman->updateView(vi);
544 VDR* vdr = VDR::getInstance();
547 // See if config says to override video format (PAL/NTSC)
548 config = vdr->configLoad("General", "Override Video Format");
551 logger->log("Command", Log::DEBUG, "Override Video Format is present");
553 if ( (!strcmp(config, "PAL") && (video->getFormat() == Video::NTSC))
554 || (!strcmp(config, "NTSC") && (video->getFormat() == Video::PAL)) )
556 // Oh sheesh, need to switch format. Bye bye TV...
558 // Take everything down
559 viewman->removeAll();
560 viewman->removeView(wallpaper);
561 Osd* osd = Osd::getInstance();
565 // Get video and osd back up with the new mode
566 if (!strcmp(config, "PAL"))
568 logger->log("Command", Log::DEBUG, "Switching to PAL");
569 video->init(Video::PAL);
571 else if (!strcmp(config, "NTSC"))
573 logger->log("Command", Log::DEBUG, "Switching to NTSC");
574 video->init(Video::NTSC);
576 osd->init((char*)("/dev/stbgfx"));
578 // Put the wallpaper back
583 vi->create(400, 200);
584 if (video->getFormat() == Video::PAL)
585 vi->setScreenPos(170, 200);
587 vi->setScreenPos(160, 150);
589 vi->setOneLiner(tr("Connected, loading config"));
592 viewman->updateView(vi);
596 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
601 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
604 // Power off if first boot and config says so
609 logger->log("Command", Log::DEBUG, "Load power after boot");
611 config = vdr->configLoad("General", "Power After Boot");
615 if (!STRCASECMP(config, "On"))
617 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
619 else if (!STRCASECMP(config, "Off"))
621 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
626 else if (!STRCASECMP(config, "Last state"))
628 char* lastPowerState = vdr->configLoad("General", "Last Power State");
631 if (!STRCASECMP(lastPowerState, "On"))
633 logger->log("Command", Log::INFO, "Config says Last Power State = On");
635 else if (!STRCASECMP(lastPowerState, "Off"))
637 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
644 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
649 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
654 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
660 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
665 // Go S-Video if config says so
667 config = vdr->configLoad("TV", "Connection");
671 if (!STRCASECMP(config, "S-Video"))
673 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
674 video->setConnection(Video::SVIDEO);
678 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
679 video->setConnection(Video::COMPOSITERGB);
685 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
690 config = vdr->configLoad("General", "Remote type");
694 if (!STRCASECMP(config, "New"))
696 logger->log("Command", Log::INFO, "Switching to New remote type");
697 remote->setRemoteType(Remote::NEWREMOTE);
701 logger->log("Command", Log::INFO, "Switching to Old remote type");
702 remote->setRemoteType(Remote::OLDREMOTE);
708 logger->log("Command", Log::INFO, "Config General/Remote type not found");
709 remote->setRemoteType(Remote::OLDREMOTE);
712 // Get TV aspect ratio
714 config = vdr->configLoad("TV", "Aspect");
717 if (!STRCASECMP(config, "16:9"))
719 logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
720 video->setTVsize(Video::ASPECT16X9);
724 logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
725 video->setTVsize(Video::ASPECT4X3);
731 logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
732 video->setTVsize(Video::ASPECT4X3);
735 config = vdr->configLoad("TV", "Widemode");
738 if (!STRCASECMP(config, "Letterbox"))
740 logger->log("Command", Log::INFO, "Setting letterbox mode");
741 video->setMode(Video::LETTERBOX);
745 logger->log("Command", Log::INFO, "Setting chop-sides mode");
746 video->setMode(Video::NORMAL);
752 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
753 video->setMode(Video::NORMAL);
756 config = vdr->configLoad("Advanced", "TCP receive window");
759 size_t newTCPsize = atoi(config);
762 logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
763 vdr->setReceiveWindow(newTCPsize);
767 logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
768 vdr->setReceiveWindow(2048); // Default
774 // Save power state = on
776 vdr->configSave("General", "Last Power State", "On");
778 // Make sure connection didn't die
779 if (!vdr->isConnected())
781 Command::getInstance()->connectionLost();
785 viewman->removeView(vi);
787 VWelcome* vw = new VWelcome();
790 viewman->updateView(vw);
792 // Enter pre-keys here
793 // handleCommand(Remote::THREE);
794 // handleCommand(Remote::UP);
795 // handleCommand(Remote::PLAY);
796 // handleCommand(Remote::DOWN);
797 // handleCommand(Remote::DOWN);
798 // handleCommand(Remote::DOWN);
799 // handleCommand(Remote::OK);
800 // handleCommand(Remote::OK);
801 // handleCommand(Remote::OK);