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);
288 kill(mainPid, SIGURG);
290 // FIXME: Marten - if this actually works on the MVP then you will need
291 // to come up with the Windows equivalent here.
295 void Command::processMessage(Message* m)
297 logger->log("Command", Log::DEBUG, "processing message %i", m->message);
299 // Timer handling is very weird at the mo. Take them out here and convert
300 if (m->message == Message::TIMER)
302 // FIXME - go to one message queue only - then instead of having
303 // objects deriving from messagequeues, make them derive from
304 // messagereceiver - then one messagequeue can deliver any message to anywhere
306 // FIXME - a slight modification - how if messagereceivers were to register
307 // themselves as receivers to avoid the calling-a-deleted-object problem
308 // then only deliver/register/unregister would have to be protected
312 logger->log("Command", Log::DEBUG, "sending timer to %p with parameter %u", m->to, m->parameter);
313 ((TimerReceiver*)m->to)->timercall(m->parameter);
314 // handleCommand(Remote::NA_NONE); // in case any timer has posted messages to viewman,
315 // // run viewman message queue here. FIXME improve this!
318 else if (m->to == this)
322 case Message::STANDBY:
330 case Message::STOP_PLAYBACK:
332 handleCommand(Remote::STOP); // an odd way of doing it, but so simple
335 case Message::STREAM_END:
337 VVideoLive::getInstance()->streamEnd();
341 // Also connection_lost comes from player - anywhere else?
345 case Message::VDR_CONNECTED:
347 doJustConnected((VConnect*)m->from);
350 case Message::SCREENSHOT:
352 Osd::getInstance()->screenShot("/out.jpg");
355 case Message::CONNECTION_LOST:
360 case Message::UDP_BUTTON:
362 handleCommand(m->parameter);
369 logger->log("Command", Log::DEBUG, "Sending message to viewman");
370 viewman->processMessage(m);
374 void Command::handleCommand(int button)
376 if (isStandby && (button != Remote::POWER)) return;
378 if (!connLost && viewman->handleCommand(button)) return; // don't send to viewman if connLost
380 // command was not handled
384 case Remote::DF_LEFT:
385 case Remote::DF_RIGHT:
386 case Remote::VOLUMEUP:
387 case Remote::VOLUMEDOWN:
389 VVolume* v = new VVolume();
391 v->handleCommand(button); // this will draw+show
396 VMute* v = new VMute();
399 viewman->updateView(v);
409 if (!connLost) return; // if connLost, handle Remote::OK
419 Message* m = new Message(); // break into master mutex
420 m->message = Message::SCREENSHOT;
426 void Command::doStandby()
430 Video::getInstance()->signalOn();
431 Led::getInstance()->on();
435 VConnect* vconnect = new VConnect();
436 viewman->add(vconnect);
441 viewman->removeAll();
442 Video::getInstance()->signalOff();
443 viewman->updateView(wallpaper);
445 VDR::getInstance()->configSave("General", "Last Power State", "Off");
446 VDR::getInstance()->disconnect();
447 Led::getInstance()->off();
450 stop(); //different behavoiur on windows, we exit
455 void Command::doFromTheTop(bool which)
459 connLost = new VInfo();
460 connLost->create(360, 200);
461 if (Video::getInstance()->getFormat() == Video::PAL)
462 connLost->setScreenPos(190, 170);
464 connLost->setScreenPos(180, 120);
465 connLost->setOneLiner(tr("Connection lost"));
466 connLost->setDropThrough();
467 connLost->setBorderOn(1);
468 connLost->setTitleBarColour(Colour::DANGER);
469 connLost->okButton();
471 viewman->add(connLost);
472 viewman->updateView(connLost);
473 remote->clearBuffer();
477 VDR::getInstance()->disconnect();
478 viewman->removeAll();
479 viewman->updateView(wallpaper);
481 VConnect* vconnect = new VConnect();
482 viewman->add(vconnect);
487 void Command::doReboot()
489 VDR::getInstance()->disconnect();
491 logger->log("Command", Log::NOTICE, "Reboot");
493 reboot(LINUX_REBOOT_CMD_RESTART);
494 #endif //Would we support this on windows?
497 void Command::connectionLost()
499 Message* m = new Message(); // break into master mutex
500 m->message = Message::CONNECTION_LOST;
502 postMessageNoLock(m);
505 void Command::doJustConnected(VConnect* vconnect)
508 Video* video = Video::getInstance();
509 viewman->removeView(vconnect);
511 VInfo* vi = new VInfo();
512 vi->create(400, 200);
513 if (video->getFormat() == Video::PAL)
514 vi->setScreenPos(170, 200);
516 vi->setScreenPos(160, 150);
518 vi->setOneLiner(tr("Connected, loading config"));
521 viewman->updateView(vi);
523 VDR* vdr = VDR::getInstance();
526 // See if config says to override video format (PAL/NTSC)
527 config = vdr->configLoad("General", "Override Video Format");
530 logger->log("Command", Log::DEBUG, "Override Video Format is present");
532 if ( (!strcmp(config, "PAL") && (video->getFormat() == Video::NTSC))
533 || (!strcmp(config, "NTSC") && (video->getFormat() == Video::PAL)) )
535 // Oh sheesh, need to switch format. Bye bye TV...
537 // Take everything down
538 viewman->removeAll();
539 viewman->removeView(wallpaper);
540 Osd* osd = Osd::getInstance();
544 // Get video and osd back up with the new mode
545 if (!strcmp(config, "PAL"))
547 logger->log("Command", Log::DEBUG, "Switching to PAL");
548 video->init(Video::PAL);
550 else if (!strcmp(config, "NTSC"))
552 logger->log("Command", Log::DEBUG, "Switching to NTSC");
553 video->init(Video::NTSC);
555 osd->init((char*)("/dev/stbgfx"));
557 // Put the wallpaper back
562 vi->create(400, 200);
563 if (video->getFormat() == Video::PAL)
564 vi->setScreenPos(170, 200);
566 vi->setScreenPos(160, 150);
568 vi->setOneLiner(tr("Connected, loading config"));
571 viewman->updateView(vi);
575 logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
580 logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
583 // Power off if first boot and config says so
588 logger->log("Command", Log::DEBUG, "Load power after boot");
590 config = vdr->configLoad("General", "Power After Boot");
594 if (!STRCASECMP(config, "On"))
596 logger->log("Command", Log::INFO, "Config says Power After Boot = On");
598 else if (!STRCASECMP(config, "Off"))
600 logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
605 else if (!STRCASECMP(config, "Last state"))
607 char* lastPowerState = vdr->configLoad("General", "Last Power State");
610 if (!STRCASECMP(lastPowerState, "On"))
612 logger->log("Command", Log::INFO, "Config says Last Power State = On");
614 else if (!STRCASECMP(lastPowerState, "Off"))
616 logger->log("Command", Log::INFO, "Config says Last Power State = Off");
623 logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
628 logger->log("Command", Log::INFO, "Config General/Last Power State not found");
633 logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
639 logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
644 // Go S-Video if config says so
646 config = vdr->configLoad("TV", "Connection");
650 if (!STRCASECMP(config, "S-Video"))
652 logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
653 video->setConnection(Video::SVIDEO);
657 logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
658 video->setConnection(Video::COMPOSITERGB);
664 logger->log("Command", Log::INFO, "Config TV/S-Video not found");
669 config = vdr->configLoad("General", "Remote type");
673 if (!STRCASECMP(config, "New"))
675 logger->log("Command", Log::INFO, "Switching to New remote type");
676 remote->setRemoteType(Remote::NEWREMOTE);
680 logger->log("Command", Log::INFO, "Switching to Old remote type");
681 remote->setRemoteType(Remote::OLDREMOTE);
687 logger->log("Command", Log::INFO, "Config General/Remote type not found");
688 remote->setRemoteType(Remote::OLDREMOTE);
691 // Get TV aspect ratio
693 config = vdr->configLoad("TV", "Aspect");
696 if (!STRCASECMP(config, "16:9"))
698 logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
699 video->setTVsize(Video::ASPECT16X9);
703 logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
704 video->setTVsize(Video::ASPECT4X3);
710 logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
711 video->setTVsize(Video::ASPECT4X3);
714 config = vdr->configLoad("TV", "Widemode");
717 if (!STRCASECMP(config, "Letterbox"))
719 logger->log("Command", Log::INFO, "Setting letterbox mode");
720 video->setMode(Video::LETTERBOX);
724 logger->log("Command", Log::INFO, "Setting chop-sides mode");
725 video->setMode(Video::NORMAL);
731 logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
732 video->setMode(Video::NORMAL);
735 config = vdr->configLoad("Advanced", "TCP receive window");
738 size_t newTCPsize = atoi(config);
741 logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
742 vdr->setReceiveWindow(newTCPsize);
746 logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
747 vdr->setReceiveWindow(2048); // Default
753 // Save power state = on
755 vdr->configSave("General", "Last Power State", "On");
757 // Make sure connection didn't die
758 if (!vdr->isConnected())
760 Command::getInstance()->connectionLost();
764 viewman->removeView(vi);
766 VWelcome* vw = new VWelcome();
769 viewman->updateView(vw);
771 // Enter pre-keys here
772 // handleCommand(Remote::THREE);
773 // handleCommand(Remote::UP);
774 // handleCommand(Remote::OK);
775 // handleCommand(Remote::OK);
776 // handleCommand(Remote::PLAY);
777 // handleCommand(Remote::DOWN);
778 // handleCommand(Remote::DOWN);
779 // handleCommand(Remote::OK);