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 // Used for Windows mouse events
219 //logger->log("Command", Log::DEBUG, "TRY LOCK");
221 if (pthread_mutex_trylock(&masterLock) != EBUSY)
223 //logger->log("Command", Log::DEBUG, "LOCK");
224 MessageQueue::postMessage(m);
225 kill(mainPid, SIGURG);
226 pthread_mutex_unlock(&masterLock);
227 //logger->log("Command", Log::DEBUG, "UNLOCK");
235 switch (WaitForSingleObject(masterLock, 0 ))
236 { //FIXME this is not "if not busy" check
237 case WAIT_OBJECT_0: //but with proper argument 0 this did not work
238 // case WAIT_ABANDONED:
239 MessageQueue::postMessage(m);
240 ((RemoteWin*)Remote::getInstance())->Signal();
241 ReleaseMutex(masterLock);
244 case WAIT_ABANDONED: return false;
245 case WAIT_TIMEOUT: return false;
251 void Command::postMessageFromOuterSpace(Message* m)
254 Yet another way of getting messages into Command. This one is for events that
255 are not standard button presses (or UDP generated buttons). It is also not for
256 events that are generated as a result of other events (events that can safely
257 call postMessageNoLock and be guaranteed that the message will be processed
258 because it is known that the queue is currently being processed).
259 This is for events that come from outer space and can occur when the master
260 mutex is locked or not, they need to be queued and executed but it doesn't
262 Actually so far it is for events caused by the video stream - aspect ratio
263 changes. These can occur when the master mutex is locked and so postMessage
264 doesn't work. postMessageNoLock doesn't work because if the mutex *isn't*
265 locked at the time then the message could be sat around a while before
267 The whole message system was at first supposed to prevent the problem of
268 calling a function on an object that had just been deleted, by ordering
269 messages such that all calls are done before object deletion. However,
270 because of the new centralised messaging system and the fact that ViewMan
271 locates the destination object before calling it, the messaging system now
272 allows the kind of sloppy calls it was supposed to stop. Weird huh. This
273 is mentioned here because the video stream might generate an event just as
274 the user hits stop. The mutex is locked, and by the time the message
275 is examined the vvideorec/live has been deleted. This doesn't matter because
276 viewman will drop the message if it can't find the matching object to
278 Finally, all this is fine and dandy, except that I'm not 100% sure that
279 this sloppy postMessage and hope a queued signal will force it to be processed
280 thingy will actually work. Hmmm.
281 Lastly <g>, I will consider making the naming system a little more sane
285 logger->log("Command", Log::DEBUG, "PMFOS called");
286 MessageQueue::postMessage(m);
289 kill(mainPid, SIGURG);
291 ((RemoteWin*)Remote::getInstance())->Signal();
295 void Command::processMessage(Message* m)
297 // FIXME - a slight modification - how if messagereceivers were to register
298 // themselves as receivers to avoid the calling-a-deleted-object problem
299 // then only deliver/register/unregister would have to be protected
301 logger->log("Command", Log::DEBUG, "processing message %i", m->message);
308 case Message::STANDBY:
316 case Message::STOP_PLAYBACK:
318 handleCommand(Remote::STOP); // an odd way of doing it, but so simple
321 case Message::STREAM_END:
323 VVideoLive::getInstance()->streamEnd();
327 // Also connection_lost comes from player - anywhere else?
331 case Message::VDR_CONNECTED:
333 doJustConnected((VConnect*)m->from);
336 case Message::SCREENSHOT:
338 Osd::getInstance()->screenShot("/out.jpg");
341 case Message::CONNECTION_LOST:
346 case Message::UDP_BUTTON:
348 handleCommand(m->parameter);
351 case Message::CHANGE_LANGUAGE:
353 viewman->removeAll();
354 viewman->updateView(wallpaper);
356 VWelcome* vw = new VWelcome();
359 viewman->updateView(vw);
362 case Message::LAST_VIEW_CLOSE:
364 // VWelcome* vw = new VWelcome();
367 // viewman->updateView(vw);
375 logger->log("Command", Log::DEBUG, "Sending message to viewman");
376 viewman->processMessage(m);
380 void Command::handleCommand(int button)
382 if (isStandby && (button != Remote::POWER)) return;
384 if (!connLost && viewman->handleCommand(button)) return; // don't send to viewman if connLost
386 // command was not handled
390 case Remote::DF_LEFT:
391 case Remote::DF_RIGHT:
392 case Remote::VOLUMEUP:
393 case Remote::VOLUMEDOWN:
395 VVolume* v = new VVolume();
397 v->handleCommand(button); // this will draw+show
402 VMute* v = new VMute();
405 viewman->updateView(v);
415 if (!connLost) return; // if connLost, handle Remote::OK
425 Message* m = new Message(); // break into master mutex
426 m->message = Message::SCREENSHOT;
432 void Command::doStandby()
436 Video::getInstance()->signalOn();
437 Led::getInstance()->on();
441 VConnect* vconnect = new VConnect();
442 viewman->add(vconnect);
447 viewman->removeAll();
448 Video::getInstance()->signalOff();
449 viewman->updateView(wallpaper);
451 VDR::getInstance()->configSave("General", "Last Power State", "Off");
452 VDR::getInstance()->disconnect();
453 Led::getInstance()->off();
456 stop(); //different behavoiur on windows, we exit
461 void Command::doFromTheTop(bool which)
465 connLost = new VInfo();
466 connLost->create(360, 200);
467 if (Video::getInstance()->getFormat() == Video::PAL)
468 connLost->setScreenPos(190, 170);
470 connLost->setScreenPos(180, 120);
471 connLost->setOneLiner(tr("Connection lost"));
472 connLost->setDropThrough();
473 connLost->setBorderOn(1);
474 connLost->setTitleBarColour(Colour::DANGER);
475 connLost->okButton();
477 viewman->add(connLost);
478 viewman->updateView(connLost);
479 remote->clearBuffer();
483 VDR::getInstance()->disconnect();
484 viewman->removeAll();
485 viewman->updateView(wallpaper);
487 VConnect* vconnect = new VConnect();
488 viewman->add(vconnect);
493 void Command::doReboot()
495 VDR::getInstance()->disconnect();
497 logger->log("Command", Log::NOTICE, "Reboot");
499 reboot(LINUX_REBOOT_CMD_RESTART);
500 #endif //Would we support this on windows?
503 void Command::connectionLost()
505 Message* m = new Message(); // break into master mutex
506 m->message = Message::CONNECTION_LOST;
508 postMessageNoLock(m);
511 void Command::doJustConnected(VConnect* vconnect)
514 Video* video = Video::getInstance();
515 viewman->removeView(vconnect);
517 VInfo* vi = new VInfo();
518 vi->create(400, 200);
519 if (video->getFormat() == Video::PAL)
520 vi->setScreenPos(170, 200);
522 vi->setScreenPos(160, 150);
524 vi->setOneLiner(tr("Connected, loading config"));
527 viewman->updateView(vi);
529 VDR* vdr = VDR::getInstance();
532 // See if config says to override video format (PAL/NTSC)
533 config = vdr->configLoad("General", "Override Video Format");
536 logger->log("Command", Log::DEBUG, "Override Video Format is present");
538 if ( (!strcmp(config, "PAL") && (video->getFormat() == Video::NTSC))
539 || (!strcmp(config, "NTSC") && (video->getFormat() == Video::PAL)) )
541 // Oh sheesh, need to switch format. Bye bye TV...
543 // Take everything down
544 viewman->removeAll();
545 viewman->removeView(wallpaper);
546 Osd* osd = Osd::getInstance();
550 // Get video and osd back up with the new mode
551 if (!strcmp(config, "PAL"))
553 logger->log("Command", Log::DEBUG, "Switching to PAL");
554 video->init(Video::PAL);
556 else if (!strcmp(config, "NTSC"))
558 logger->log("Command", Log::DEBUG, "Switching to NTSC");
559 video->init(Video::NTSC);
561 osd->init((char*)("/dev/stbgfx"));
563 // Put the wallpaper back
568 vi->create(400, 200);
569 if (video->getFormat() == Video::PAL)
570 vi->setScreenPos(170, 200);
572 vi->setScreenPos(160, 150);
574 vi->setOneLiner(tr("Connected, loading config"));
577 viewman->updateView(vi);
581 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
586 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
589 // Power off if first boot and config says so
594 logger->log("Command", Log::DEBUG, "Load power after boot");
596 config = vdr->configLoad("General", "Power After Boot");
600 if (!STRCASECMP(config, "On"))
602 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
604 else if (!STRCASECMP(config, "Off"))
606 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
611 else if (!STRCASECMP(config, "Last state"))
613 char* lastPowerState = vdr->configLoad("General", "Last Power State");
616 if (!STRCASECMP(lastPowerState, "On"))
618 logger->log("Command", Log::INFO, "Config says Last Power State = On");
620 else if (!STRCASECMP(lastPowerState, "Off"))
622 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
629 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
634 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
639 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
645 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
650 // Go S-Video if config says so
652 config = vdr->configLoad("TV", "Connection");
656 if (!STRCASECMP(config, "S-Video"))
658 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
659 video->setConnection(Video::SVIDEO);
663 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
664 video->setConnection(Video::COMPOSITERGB);
670 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
675 config = vdr->configLoad("General", "Remote type");
679 if (!STRCASECMP(config, "New"))
681 logger->log("Command", Log::INFO, "Switching to New remote type");
682 remote->setRemoteType(Remote::NEWREMOTE);
686 logger->log("Command", Log::INFO, "Switching to Old remote type");
687 remote->setRemoteType(Remote::OLDREMOTE);
693 logger->log("Command", Log::INFO, "Config General/Remote type not found");
694 remote->setRemoteType(Remote::OLDREMOTE);
699 config = vdr->configLoad("General", "Remote keys");
703 logger->log("Command", Log::INFO, "Config General/Remote keys load");
704 remote->LoadKeysConfig(config);
709 logger->log("Command", Log::INFO, "Config General/Remote keys not found");
710 remote->InitHWCListwithDefaults();
713 // Get TV aspect ratio
715 config = vdr->configLoad("TV", "Aspect");
718 if (!STRCASECMP(config, "16:9"))
720 logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
721 video->setTVsize(Video::ASPECT16X9);
725 logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
726 video->setTVsize(Video::ASPECT4X3);
732 logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
733 video->setTVsize(Video::ASPECT4X3);
736 config = vdr->configLoad("TV", "Widemode");
739 if (!STRCASECMP(config, "Letterbox"))
741 logger->log("Command", Log::INFO, "Setting letterbox mode");
742 video->setMode(Video::LETTERBOX);
746 logger->log("Command", Log::INFO, "Setting chop-sides mode");
747 video->setMode(Video::NORMAL);
753 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
754 video->setMode(Video::NORMAL);
757 config = vdr->configLoad("Advanced", "TCP receive window");
760 size_t newTCPsize = atoi(config);
763 logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
764 vdr->setReceiveWindow(newTCPsize);
768 logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
769 vdr->setReceiveWindow(2048); // Default
775 // Save power state = on
777 vdr->configSave("General", "Last Power State", "On");
779 // Make sure connection didn't die
780 if (!vdr->isConnected())
782 Command::getInstance()->connectionLost();
786 viewman->removeView(vi);
788 VWelcome* vw = new VWelcome();
791 viewman->updateView(vw);
793 // Enter pre-keys here
794 // handleCommand(Remote::THREE);
795 // handleCommand(Remote::UP);
796 // handleCommand(Remote::PLAY);
797 // handleCommand(Remote::DOWN);
798 // handleCommand(Remote::DOWN);
799 // handleCommand(Remote::DOWN);
800 // handleCommand(Remote::OK);
801 // handleCommand(Remote::OK);
802 // handleCommand(Remote::OK);