]> git.vomp.tv Git - vompclient.git/blob - command.cc
*** empty log message ***
[vompclient.git] / command.cc
1 /*
2     Copyright 2004-2005 Chris Tallon
3
4     This file is part of VOMP.
5
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.
10
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.
15
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
19 */
20
21 #include "command.h"
22 #ifdef WIN32
23 #include "remotewin.h"
24 #endif
25
26 Command* Command::instance = NULL;
27
28 Command::Command()
29 {
30   if (instance) return;
31   instance = this;
32   initted = 0;
33   isStandby = 0;
34   firstBoot = 1;
35   connLost = NULL;
36 }
37
38 Command::~Command()
39 {
40   instance = NULL;
41 }
42
43 Command* Command::getInstance()
44 {
45   return instance;
46 }
47
48 int Command::init()
49 {
50   if (initted) return 0;
51   initted = 1;
52
53   logger = Log::getInstance();
54   viewman = ViewMan::getInstance();
55   remote = Remote::getInstance();
56
57   if (!logger || !viewman || !remote)
58   {
59     initted = 0;
60     return 0;
61   }
62 #ifndef WIN32
63   pthread_mutex_init(&masterLock, NULL);
64 #else
65   masterLock=CreateMutex(NULL,FALSE,NULL);
66 #endif
67
68   return 1;
69 }
70
71 int Command::shutdown()
72 {
73   if (!initted) return 0;
74   initted = 0;
75   return 1;
76 }
77
78 void Command::stop()
79 {
80 //  VDR::getInstance()->cancelFindingServer();
81   udp.shutdown();
82   irun = 0;
83 }
84
85 void Command::doWallpaper()
86 {
87   Video* video = Video::getInstance();
88
89   // Blue background
90   View* v = new View();
91   v->create(video->getScreenWidth(), video->getScreenHeight());
92   v->setBackgroundColour(Colour::VIDEOBLUE);
93   v->draw();
94   viewman->add(v);
95   viewman->updateView(v);
96   viewman->removeView(v);
97
98   // Wallpaper
99   wallpaper = new VWallpaper();
100   if (video->getFormat() == Video::PAL)
101   {
102     logger->log("Command", Log::DEBUG, "PAL wallpaper selected");
103     wallpaper->init("/wallpaperPAL.jpg");
104   }
105   else
106   {
107     logger->log("Command", Log::DEBUG, "NTSC wallpaper selected");
108     wallpaper->init("/wallpaperNTSC.jpg");
109   }
110   wallpaper->draw();
111   viewman->add(wallpaper);
112   viewman->updateView(wallpaper);
113 }
114
115 void Command::run()
116 {
117   if (!initted) return;
118   irun = 1;
119 #ifndef WIN32
120   mainPid = getpid();
121 #endif
122
123   // just in case
124   Video::getInstance()->signalOn();
125   Led::getInstance()->on();
126
127   doWallpaper();
128
129   // End of startup. Lock the mutex and put the first view up
130 #ifndef WIN32
131   pthread_mutex_lock(&masterLock);
132 #else
133   WaitForSingleObject(masterLock, INFINITE );
134 #endif
135   VConnect* vconnect = new VConnect();
136   viewman->add(vconnect);
137   vconnect->run();
138
139   // Start method 2 of getting commands in...
140   udp.run(this);
141
142   UCHAR button = 0;
143   while(irun)
144   {
145     // unlock and wait
146 #ifndef WIN32
147     pthread_mutex_unlock(&masterLock);
148 #else
149     ReleaseMutex(masterLock);
150 #endif
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
153
154 #ifndef WIN32
155     pthread_mutex_lock(&masterLock);
156 #else
157     WaitForSingleObject(masterLock, INFINITE );
158 #endif
159
160     if ((button == Remote::NA_NONE) || (button == Remote::NA_UNKNOWN)) continue;
161
162     if (button != Remote::NA_SIGNAL) handleCommand(button);
163     processMessageQueue();
164   }
165
166 #ifndef WIN32
167   pthread_mutex_unlock(&masterLock);
168 #else
169   ReleaseMutex(masterLock);
170 #endif
171 }
172
173 void Command::postMessage(Message* m)
174 {
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
179
180
181 #ifndef WIN32
182   pthread_mutex_lock(&masterLock);
183 #else
184   WaitForSingleObject(masterLock, INFINITE );
185 #endif
186   MessageQueue::postMessage(m);
187
188 #ifndef WIN32
189   kill(mainPid, SIGURG);
190   pthread_mutex_unlock(&masterLock);
191 #else
192   ((RemoteWin*)Remote::getInstance())->Signal();
193   ReleaseMutex(masterLock);
194 #endif
195 }
196
197 void Command::postMessageNoLock(Message* m)
198 {
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);
203 }
204
205 bool Command::postMessageIfNotBusy(Message* m)
206 {
207   // This is for the timers module
208   // If the masterlock is locked then the timers module wants to
209   // cancel delivery
210 #ifndef WIN32
211   if (pthread_mutex_trylock(&masterLock) != EBUSY)
212   {
213     MessageQueue::postMessage(m);
214     kill(mainPid, SIGURG);
215     pthread_mutex_unlock(&masterLock);
216     return true;
217   }
218   else
219   {
220     return false;
221   }
222 #else
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);
230     return true;
231
232     case WAIT_ABANDONED: return false;
233     case WAIT_TIMEOUT: return false;
234   }
235     return false;
236 #endif
237 }
238
239 void Command::postMessageFromOuterSpace(Message* m)
240 {
241   /*
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
249   matter when.
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
254   being noticed.
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
265   deliver it 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
270   if this works.
271   */
272
273   logger->log("Command", Log::DEBUG, "PMFOS called");
274   MessageQueue::postMessage(m);
275   kill(mainPid, SIGURG);
276
277   // FIXME: Marten - if this actually works on the MVP then you will need
278   // to come up with the Windows equivalent here.
279
280 }
281
282 void Command::processMessage(Message* m)
283 {
284   logger->log("Command", Log::DEBUG, "processing message %i", m->message);
285
286   // Timer handling is very weird at the mo. Take them out here and convert
287   if (m->message == Message::TIMER)
288   {
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
292
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
296
297     // deliver timer
298
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!
303 //    break;
304   }
305   else if (m->to == this)
306   {
307     switch(m->message)
308     {
309       case Message::STANDBY:
310       {
311         doStandby();
312         break;
313       }
314
315
316       // << FIXME OBSELETE
317       case Message::STOP_PLAYBACK:
318       {
319         handleCommand(Remote::STOP); // an odd way of doing it, but so simple
320         break;
321       }
322       case Message::STREAM_END:
323       {
324         VVideoLive::getInstance()->streamEnd();
325         break;
326       }
327
328       // Also connection_lost comes from player - anywhere else?
329       // FIXME OBSELETE >>
330
331
332       case Message::VDR_CONNECTED:
333       {
334         doJustConnected((VConnect*)m->from);
335         break;
336       }
337       case Message::SCREENSHOT:
338       {
339         Osd::getInstance()->screenShot("/out.jpg");
340         break;
341       }
342       case Message::CONNECTION_LOST:
343       {
344         doFromTheTop(true);
345         break;
346       }
347       case Message::UDP_BUTTON:
348       {
349         handleCommand(m->parameter);
350         break;
351       }
352     }
353   }
354   else
355   {
356     logger->log("Command", Log::DEBUG, "Sending message to viewman");
357     viewman->processMessage(m);
358   }
359 }
360
361 void Command::handleCommand(int button)
362 {
363   if (isStandby && (button != Remote::POWER)) return;
364
365   if (!connLost && viewman->handleCommand(button)) return; // don't send to viewman if connLost
366
367   // command was not handled
368
369   switch(button)
370   {
371     case Remote::DF_LEFT:
372     case Remote::DF_RIGHT:
373     case Remote::VOLUMEUP:
374     case Remote::VOLUMEDOWN:
375     {
376       VVolume* v = new VVolume();
377       viewman->add(v);
378       v->handleCommand(button); // this will draw+show
379       return;
380     }
381     case Remote::MUTE:
382     {
383       VMute* v = new VMute();
384       v->draw();
385       viewman->add(v);
386       viewman->updateView(v);
387       return;
388     }
389     case Remote::POWER:
390     {
391       doStandby();
392       return;
393     }
394     case Remote::OK:
395     {
396       if (!connLost) return; // if connLost, handle Remote::OK
397       doFromTheTop(false);
398       return;
399     }
400   }
401 }
402
403 void Command::sig1()
404 {
405 #ifdef DEV
406   Message* m = new Message(); // break into master mutex
407   m->message = Message::SCREENSHOT;
408   m->to = this;
409   postMessage(m);
410 #endif
411 }
412
413 void Command::doStandby()
414 {
415   if (isStandby)
416   {
417     Video::getInstance()->signalOn();
418     Led::getInstance()->on();
419     isStandby = 0;
420
421
422     VConnect* vconnect = new VConnect();
423     viewman->add(vconnect);
424     vconnect->run();
425   }
426   else
427   {
428     viewman->removeAll();
429     Video::getInstance()->signalOff();
430     viewman->updateView(wallpaper);
431
432     VDR::getInstance()->configSave("General", "Last Power State", "Off");
433     VDR::getInstance()->disconnect();
434     Led::getInstance()->off();
435     isStandby = 1;
436 #ifdef WIN32
437     stop(); //different behavoiur on windows, we exit
438 #endif
439   }
440 }
441
442 void Command::doFromTheTop(bool which)
443 {
444   if (which)
445   {
446     connLost = new VInfo();
447     connLost->create(360, 200);
448     if (Video::getInstance()->getFormat() == Video::PAL)
449       connLost->setScreenPos(190, 170);
450     else
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();
457     connLost->draw();
458     viewman->add(connLost);
459     viewman->updateView(connLost);
460     remote->clearBuffer();
461   }
462   else
463   {
464     VDR::getInstance()->disconnect();
465     viewman->removeAll();
466     viewman->updateView(wallpaper);
467     connLost = NULL;
468     VConnect* vconnect = new VConnect();
469     viewman->add(vconnect);
470     vconnect->run();
471   }
472 }
473
474 void Command::doReboot()
475 {
476   VDR::getInstance()->disconnect();
477   // just kill it...
478   logger->log("Command", Log::NOTICE, "Reboot");
479 #ifndef WIN32
480   reboot(LINUX_REBOOT_CMD_RESTART);
481 #endif //Would we support this on windows?
482 }
483
484 void Command::connectionLost()
485 {
486   Message* m = new Message(); // break into master mutex
487   m->message = Message::CONNECTION_LOST;
488   m->to = this;
489   postMessageNoLock(m);
490 }
491
492 void Command::doJustConnected(VConnect* vconnect)
493 {
494   I18n::initialize();
495   Video* video = Video::getInstance();
496   viewman->removeView(vconnect);
497
498   VInfo* vi = new VInfo();
499   vi->create(400, 200);
500   if (video->getFormat() == Video::PAL)
501     vi->setScreenPos(170, 200);
502   else
503     vi->setScreenPos(160, 150);
504
505   vi->setOneLiner(tr("Connected, loading config"));
506   vi->draw();
507   viewman->add(vi);
508   viewman->updateView(vi);
509
510   VDR* vdr = VDR::getInstance();
511   char* config;
512
513   // See if config says to override video format (PAL/NTSC)
514   config = vdr->configLoad("General", "Override Video Format");
515   if (config)
516   {
517     logger->log("Command", Log::DEBUG, "Override Video Format is present");
518
519     if (   (!strcmp(config, "PAL") && (video->getFormat() == Video::NTSC))
520         || (!strcmp(config, "NTSC") && (video->getFormat() == Video::PAL))  )
521     {
522       // Oh sheesh, need to switch format. Bye bye TV...
523
524       // Take everything down
525       viewman->removeAll();
526       viewman->removeView(wallpaper);
527       Osd* osd = Osd::getInstance();
528       osd->shutdown();
529       video->shutdown();
530
531       // Get video and osd back up with the new mode
532       if (!strcmp(config, "PAL"))
533       {
534         logger->log("Command", Log::DEBUG, "Switching to PAL");
535         video->init(Video::PAL);
536       }
537       else if (!strcmp(config, "NTSC"))
538       {
539         logger->log("Command", Log::DEBUG, "Switching to NTSC");
540         video->init(Video::NTSC);
541       }
542       osd->init((char*)("/dev/stbgfx"));
543
544       // Put the wallpaper back
545       doWallpaper();
546
547       // Re add the vinfo
548       vi = new VInfo();
549       vi->create(400, 200);
550       if (video->getFormat() == Video::PAL)
551         vi->setScreenPos(170, 200);
552       else
553         vi->setScreenPos(160, 150);
554
555       vi->setOneLiner(tr("Connected, loading config"));
556       vi->draw();
557       viewman->add(vi);
558       viewman->updateView(vi);
559     }
560     else
561     {
562       logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
563     }
564   }
565   else
566   {
567     logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
568   }
569
570   // Power off if first boot and config says so
571   if (firstBoot)
572   {
573     firstBoot = 0;
574
575     logger->log("Command", Log::DEBUG, "Load power after boot");
576
577     config = vdr->configLoad("General", "Power After Boot");
578
579     if (config)
580     {
581       if (!STRCASECMP(config, "On"))
582       {
583         logger->log("Command", Log::INFO, "Config says Power After Boot = On");
584       }
585       else if (!STRCASECMP(config, "Off"))
586       {
587         logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
588         doStandby();
589         delete[] config;
590         return; // quit here
591       }
592       else if (!STRCASECMP(config, "Last state"))
593       {
594         char* lastPowerState = vdr->configLoad("General", "Last Power State");
595         if (lastPowerState)
596         {
597           if (!STRCASECMP(lastPowerState, "On"))
598           {
599             logger->log("Command", Log::INFO, "Config says Last Power State = On");
600           }
601           else if (!STRCASECMP(lastPowerState, "Off"))
602           {
603             logger->log("Command", Log::INFO, "Config says Last Power State = Off");
604             doStandby();
605             delete[] config;
606             return; // quit here
607           }
608           else
609           {
610             logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
611           }
612         }
613         else
614         {
615           logger->log("Command", Log::INFO, "Config General/Last Power State not found");
616         }
617       }
618       else
619       {
620         logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
621       }
622       delete[] config;
623     }
624     else
625     {
626       logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
627     }
628   }
629
630
631   // Go S-Video if config says so
632
633   config = vdr->configLoad("TV", "Connection");
634
635   if (config)
636   {
637     if (!STRCASECMP(config, "S-Video"))
638     {
639       logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
640       video->setConnection(Video::SVIDEO);
641     }
642     else
643     {
644       logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
645       video->setConnection(Video::COMPOSITERGB);
646     }
647     delete[] config;
648   }
649   else
650   {
651     logger->log("Command", Log::INFO, "Config TV/S-Video not found");
652   }
653
654   // Set remote type
655
656   config = vdr->configLoad("General", "Remote type");
657
658   if (config)
659   {
660     if (!STRCASECMP(config, "New"))
661     {
662       logger->log("Command", Log::INFO, "Switching to New remote type");
663       remote->setRemoteType(Remote::NEWREMOTE);
664     }
665     else
666     {
667       logger->log("Command", Log::INFO, "Switching to Old remote type");
668       remote->setRemoteType(Remote::OLDREMOTE);
669     }
670     delete[] config;
671   }
672   else
673   {
674     logger->log("Command", Log::INFO, "Config General/Remote type not found");
675     remote->setRemoteType(Remote::OLDREMOTE);
676   }
677
678   // Get TV aspect ratio
679
680   config = vdr->configLoad("TV", "Aspect");
681   if (config)
682   {
683     if (!STRCASECMP(config, "16:9"))
684     {
685       logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
686       video->setTVsize(Video::ASPECT16X9);
687     }
688     else
689     {
690       logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
691       video->setTVsize(Video::ASPECT4X3);
692     }
693     delete[] config;
694   }
695   else
696   {
697     logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
698     video->setTVsize(Video::ASPECT4X3);
699   }
700
701   config = vdr->configLoad("TV", "Widemode");
702   if (config)
703   {
704     if (!STRCASECMP(config, "Letterbox"))
705     {
706       logger->log("Command", Log::INFO, "Setting letterbox mode");
707       video->setMode(Video::LETTERBOX);
708     }
709     else
710     {
711       logger->log("Command", Log::INFO, "Setting chop-sides mode");
712       video->setMode(Video::NORMAL);
713     }
714     delete[] config;
715   }
716   else
717   {
718     logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
719     video->setMode(Video::NORMAL);
720   }
721
722   config = vdr->configLoad("Advanced", "TCP receive window");
723   if (config)
724   {
725     size_t newTCPsize = atoi(config);
726     delete[] config;
727
728     logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
729     vdr->setReceiveWindow(newTCPsize);
730   }
731   else
732   {
733     logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
734     vdr->setReceiveWindow(2048); // Default
735   }
736
737
738   // config done
739
740   // Save power state = on
741
742   vdr->configSave("General", "Last Power State", "On");
743
744   // Make sure connection didn't die
745   if (!vdr->isConnected())
746   {
747     Command::getInstance()->connectionLost();
748   }
749   else
750   {
751     viewman->removeView(vi);
752
753     VWelcome* vw = new VWelcome();
754     vw->draw();
755     viewman->add(vw);
756     viewman->updateView(vw);
757
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);
768   }
769 }