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