]> 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 //  logger->log("Command", Log::DEBUG, "WANT LOCK");
131 #ifndef WIN32
132   pthread_mutex_lock(&masterLock);
133 #else
134   WaitForSingleObject(masterLock, INFINITE );
135 #endif
136   //logger->log("Command", Log::DEBUG, "LOCKED");
137
138   VConnect* vconnect = new VConnect();
139   viewman->add(vconnect);
140   vconnect->run();
141
142   // Start method 2 of getting commands in...
143   udp.run(this);
144
145   UCHAR button = 0;
146   while(irun)
147   {
148     // unlock and wait
149     //logger->log("Command", Log::DEBUG, "UNLOCK");
150 #ifndef WIN32
151     pthread_mutex_unlock(&masterLock);
152 #else
153     ReleaseMutex(masterLock);
154 #endif
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
157
158     //logger->log("Command", Log::DEBUG, "WANT LOCK");
159 #ifndef WIN32
160     pthread_mutex_lock(&masterLock);
161 #else
162     WaitForSingleObject(masterLock, INFINITE );
163 #endif
164     //logger->log("Command", Log::DEBUG, "LOCK");
165
166     if ((button == Remote::NA_NONE) || (button == Remote::NA_UNKNOWN)) continue;
167
168     if (button != Remote::NA_SIGNAL) handleCommand(button);
169     processMessageQueue();
170   }
171
172   //logger->log("Command", Log::DEBUG, "UNLOCK");
173 #ifndef WIN32
174   pthread_mutex_unlock(&masterLock);
175 #else
176   ReleaseMutex(masterLock);
177 #endif
178 }
179
180 void Command::postMessage(Message* m)
181 {
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
186
187
188   //logger->log("Command", Log::DEBUG, "WANT LOCK");
189 #ifndef WIN32
190   pthread_mutex_lock(&masterLock);
191 #else
192   WaitForSingleObject(masterLock, INFINITE );
193 #endif
194   //logger->log("Command", Log::DEBUG, "LOCK");
195   MessageQueue::postMessage(m);
196
197 #ifndef WIN32
198   kill(mainPid, SIGURG);
199   pthread_mutex_unlock(&masterLock);
200 #else
201   ((RemoteWin*)Remote::getInstance())->Signal();
202   ReleaseMutex(masterLock);
203 #endif
204   //logger->log("Command", Log::DEBUG, "UNLOCK");
205 }
206
207 void Command::postMessageNoLock(Message* m)
208 {
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);
213 }
214
215 bool Command::postMessageIfNotBusy(Message* m)
216 {
217   // This is for the timers module
218   // If the masterlock is locked then the timers module wants to
219   // cancel delivery
220   //logger->log("Command", Log::DEBUG, "TRY LOCK");
221 #ifndef WIN32
222   if (pthread_mutex_trylock(&masterLock) != EBUSY)
223   {
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");
229     return true;
230   }
231   else
232   {
233     return false;
234   }
235 #else
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);
243     return true;
244
245     case WAIT_ABANDONED: return false;
246     case WAIT_TIMEOUT: return false;
247   }
248     return false;
249 #endif
250 }
251
252 void Command::postMessageFromOuterSpace(Message* m)
253 {
254   /*
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
262   matter when.
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
267   being noticed.
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
278   deliver it 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
283   if this works.
284   */
285
286   logger->log("Command", Log::DEBUG, "PMFOS called");
287   MessageQueue::postMessage(m);
288   kill(mainPid, SIGURG);
289
290   // FIXME: Marten - if this actually works on the MVP then you will need
291   // to come up with the Windows equivalent here.
292
293 }
294
295 void Command::processMessage(Message* m)
296 {
297   logger->log("Command", Log::DEBUG, "processing message %i", m->message);
298
299   // Timer handling is very weird at the mo. Take them out here and convert
300   if (m->message == Message::TIMER)
301   {
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
305
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
309
310     // deliver timer
311
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!
316 //    break;
317   }
318   else if (m->to == this)
319   {
320     switch(m->message)
321     {
322       case Message::STANDBY:
323       {
324         doStandby();
325         break;
326       }
327
328
329       // << FIXME OBSELETE
330       case Message::STOP_PLAYBACK:
331       {
332         handleCommand(Remote::STOP); // an odd way of doing it, but so simple
333         break;
334       }
335       case Message::STREAM_END:
336       {
337         VVideoLive::getInstance()->streamEnd();
338         break;
339       }
340
341       // Also connection_lost comes from player - anywhere else?
342       // FIXME OBSELETE >>
343
344
345       case Message::VDR_CONNECTED:
346       {
347         doJustConnected((VConnect*)m->from);
348         break;
349       }
350       case Message::SCREENSHOT:
351       {
352         Osd::getInstance()->screenShot("/out.jpg");
353         break;
354       }
355       case Message::CONNECTION_LOST:
356       {
357         doFromTheTop(true);
358         break;
359       }
360       case Message::UDP_BUTTON:
361       {
362         handleCommand(m->parameter);
363         break;
364       }
365     }
366   }
367   else
368   {
369     logger->log("Command", Log::DEBUG, "Sending message to viewman");
370     viewman->processMessage(m);
371   }
372 }
373
374 void Command::handleCommand(int button)
375 {
376   if (isStandby && (button != Remote::POWER)) return;
377
378   if (!connLost && viewman->handleCommand(button)) return; // don't send to viewman if connLost
379
380   // command was not handled
381
382   switch(button)
383   {
384     case Remote::DF_LEFT:
385     case Remote::DF_RIGHT:
386     case Remote::VOLUMEUP:
387     case Remote::VOLUMEDOWN:
388     {
389       VVolume* v = new VVolume();
390       viewman->add(v);
391       v->handleCommand(button); // this will draw+show
392       return;
393     }
394     case Remote::MUTE:
395     {
396       VMute* v = new VMute();
397       v->draw();
398       viewman->add(v);
399       viewman->updateView(v);
400       return;
401     }
402     case Remote::POWER:
403     {
404       doStandby();
405       return;
406     }
407     case Remote::OK:
408     {
409       if (!connLost) return; // if connLost, handle Remote::OK
410       doFromTheTop(false);
411       return;
412     }
413   }
414 }
415
416 void Command::sig1()
417 {
418 #ifdef DEV
419   Message* m = new Message(); // break into master mutex
420   m->message = Message::SCREENSHOT;
421   m->to = this;
422   postMessage(m);
423 #endif
424 }
425
426 void Command::doStandby()
427 {
428   if (isStandby)
429   {
430     Video::getInstance()->signalOn();
431     Led::getInstance()->on();
432     isStandby = 0;
433
434
435     VConnect* vconnect = new VConnect();
436     viewman->add(vconnect);
437     vconnect->run();
438   }
439   else
440   {
441     viewman->removeAll();
442     Video::getInstance()->signalOff();
443     viewman->updateView(wallpaper);
444
445     VDR::getInstance()->configSave("General", "Last Power State", "Off");
446     VDR::getInstance()->disconnect();
447     Led::getInstance()->off();
448     isStandby = 1;
449 #ifdef WIN32
450     stop(); //different behavoiur on windows, we exit
451 #endif
452   }
453 }
454
455 void Command::doFromTheTop(bool which)
456 {
457   if (which)
458   {
459     connLost = new VInfo();
460     connLost->create(360, 200);
461     if (Video::getInstance()->getFormat() == Video::PAL)
462       connLost->setScreenPos(190, 170);
463     else
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();
470     connLost->draw();
471     viewman->add(connLost);
472     viewman->updateView(connLost);
473     remote->clearBuffer();
474   }
475   else
476   {
477     VDR::getInstance()->disconnect();
478     viewman->removeAll();
479     viewman->updateView(wallpaper);
480     connLost = NULL;
481     VConnect* vconnect = new VConnect();
482     viewman->add(vconnect);
483     vconnect->run();
484   }
485 }
486
487 void Command::doReboot()
488 {
489   VDR::getInstance()->disconnect();
490   // just kill it...
491   logger->log("Command", Log::NOTICE, "Reboot");
492 #ifndef WIN32
493   reboot(LINUX_REBOOT_CMD_RESTART);
494 #endif //Would we support this on windows?
495 }
496
497 void Command::connectionLost()
498 {
499   Message* m = new Message(); // break into master mutex
500   m->message = Message::CONNECTION_LOST;
501   m->to = this;
502   postMessageNoLock(m);
503 }
504
505 void Command::doJustConnected(VConnect* vconnect)
506 {
507   I18n::initialize();
508   Video* video = Video::getInstance();
509   viewman->removeView(vconnect);
510
511   VInfo* vi = new VInfo();
512   vi->create(400, 200);
513   if (video->getFormat() == Video::PAL)
514     vi->setScreenPos(170, 200);
515   else
516     vi->setScreenPos(160, 150);
517
518   vi->setOneLiner(tr("Connected, loading config"));
519   vi->draw();
520   viewman->add(vi);
521   viewman->updateView(vi);
522
523   VDR* vdr = VDR::getInstance();
524   char* config;
525
526   // See if config says to override video format (PAL/NTSC)
527   config = vdr->configLoad("General", "Override Video Format");
528   if (config)
529   {
530     logger->log("Command", Log::DEBUG, "Override Video Format is present");
531
532     if (   (!strcmp(config, "PAL") && (video->getFormat() == Video::NTSC))
533         || (!strcmp(config, "NTSC") && (video->getFormat() == Video::PAL))  )
534     {
535       // Oh sheesh, need to switch format. Bye bye TV...
536
537       // Take everything down
538       viewman->removeAll();
539       viewman->removeView(wallpaper);
540       Osd* osd = Osd::getInstance();
541       osd->shutdown();
542       video->shutdown();
543
544       // Get video and osd back up with the new mode
545       if (!strcmp(config, "PAL"))
546       {
547         logger->log("Command", Log::DEBUG, "Switching to PAL");
548         video->init(Video::PAL);
549       }
550       else if (!strcmp(config, "NTSC"))
551       {
552         logger->log("Command", Log::DEBUG, "Switching to NTSC");
553         video->init(Video::NTSC);
554       }
555       osd->init((char*)("/dev/stbgfx"));
556
557       // Put the wallpaper back
558       doWallpaper();
559
560       // Re add the vinfo
561       vi = new VInfo();
562       vi->create(400, 200);
563       if (video->getFormat() == Video::PAL)
564         vi->setScreenPos(170, 200);
565       else
566         vi->setScreenPos(160, 150);
567
568       vi->setOneLiner(tr("Connected, loading config"));
569       vi->draw();
570       viewman->add(vi);
571       viewman->updateView(vi);
572     }
573     else
574     {
575       logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
576     }
577   }
578   else
579   {
580     logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
581   }
582
583   // Power off if first boot and config says so
584   if (firstBoot)
585   {
586     firstBoot = 0;
587
588     logger->log("Command", Log::DEBUG, "Load power after boot");
589
590     config = vdr->configLoad("General", "Power After Boot");
591
592     if (config)
593     {
594       if (!STRCASECMP(config, "On"))
595       {
596         logger->log("Command", Log::INFO, "Config says Power After Boot = On");
597       }
598       else if (!STRCASECMP(config, "Off"))
599       {
600         logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
601         doStandby();
602         delete[] config;
603         return; // quit here
604       }
605       else if (!STRCASECMP(config, "Last state"))
606       {
607         char* lastPowerState = vdr->configLoad("General", "Last Power State");
608         if (lastPowerState)
609         {
610           if (!STRCASECMP(lastPowerState, "On"))
611           {
612             logger->log("Command", Log::INFO, "Config says Last Power State = On");
613           }
614           else if (!STRCASECMP(lastPowerState, "Off"))
615           {
616             logger->log("Command", Log::INFO, "Config says Last Power State = Off");
617             doStandby();
618             delete[] config;
619             return; // quit here
620           }
621           else
622           {
623             logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
624           }
625         }
626         else
627         {
628           logger->log("Command", Log::INFO, "Config General/Last Power State not found");
629         }
630       }
631       else
632       {
633         logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
634       }
635       delete[] config;
636     }
637     else
638     {
639       logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
640     }
641   }
642
643
644   // Go S-Video if config says so
645
646   config = vdr->configLoad("TV", "Connection");
647
648   if (config)
649   {
650     if (!STRCASECMP(config, "S-Video"))
651     {
652       logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
653       video->setConnection(Video::SVIDEO);
654     }
655     else
656     {
657       logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
658       video->setConnection(Video::COMPOSITERGB);
659     }
660     delete[] config;
661   }
662   else
663   {
664     logger->log("Command", Log::INFO, "Config TV/S-Video not found");
665   }
666
667   // Set remote type
668
669   config = vdr->configLoad("General", "Remote type");
670
671   if (config)
672   {
673     if (!STRCASECMP(config, "New"))
674     {
675       logger->log("Command", Log::INFO, "Switching to New remote type");
676       remote->setRemoteType(Remote::NEWREMOTE);
677     }
678     else
679     {
680       logger->log("Command", Log::INFO, "Switching to Old remote type");
681       remote->setRemoteType(Remote::OLDREMOTE);
682     }
683     delete[] config;
684   }
685   else
686   {
687     logger->log("Command", Log::INFO, "Config General/Remote type not found");
688     remote->setRemoteType(Remote::OLDREMOTE);
689   }
690
691   // Get TV aspect ratio
692
693   config = vdr->configLoad("TV", "Aspect");
694   if (config)
695   {
696     if (!STRCASECMP(config, "16:9"))
697     {
698       logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
699       video->setTVsize(Video::ASPECT16X9);
700     }
701     else
702     {
703       logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
704       video->setTVsize(Video::ASPECT4X3);
705     }
706     delete[] config;
707   }
708   else
709   {
710     logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
711     video->setTVsize(Video::ASPECT4X3);
712   }
713
714   config = vdr->configLoad("TV", "Widemode");
715   if (config)
716   {
717     if (!STRCASECMP(config, "Letterbox"))
718     {
719       logger->log("Command", Log::INFO, "Setting letterbox mode");
720       video->setMode(Video::LETTERBOX);
721     }
722     else
723     {
724       logger->log("Command", Log::INFO, "Setting chop-sides mode");
725       video->setMode(Video::NORMAL);
726     }
727     delete[] config;
728   }
729   else
730   {
731     logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
732     video->setMode(Video::NORMAL);
733   }
734
735   config = vdr->configLoad("Advanced", "TCP receive window");
736   if (config)
737   {
738     size_t newTCPsize = atoi(config);
739     delete[] config;
740
741     logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
742     vdr->setReceiveWindow(newTCPsize);
743   }
744   else
745   {
746     logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
747     vdr->setReceiveWindow(2048); // Default
748   }
749
750
751   // config done
752
753   // Save power state = on
754
755   vdr->configSave("General", "Last Power State", "On");
756
757   // Make sure connection didn't die
758   if (!vdr->isConnected())
759   {
760     Command::getInstance()->connectionLost();
761   }
762   else
763   {
764     viewman->removeView(vi);
765
766     VWelcome* vw = new VWelcome();
767     vw->draw();
768     viewman->add(vw);
769     viewman->updateView(vw);
770
771     // Enter pre-keys here
772 //    handleCommand(Remote::THREE);
773 //    handleCommand(Remote::SKIPFORWARD);
774 //    handleCommand(Remote::OK);
775 //    handleCommand(Remote::PLAY);
776 //    handleCommand(Remote::OK);
777 //    handleCommand(Remote::DOWN);
778 //    handleCommand(Remote::DOWN);
779 //    handleCommand(Remote::DOWN);
780 //    handleCommand(Remote::OK);
781   }
782 }