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