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