]> git.vomp.tv Git - vompclient.git/blob - command.cc
Portability
[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
23 Command* Command::instance = NULL;
24
25 Command::Command()
26 {
27   if (instance) return;
28   instance = this;
29   initted = 0;
30   isStandby = 0;
31   firstBoot = 1;
32   connLost = NULL;
33 }
34
35 Command::~Command()
36 {
37   instance = NULL;
38 }
39
40 Command* Command::getInstance()
41 {
42   return instance;
43 }
44
45 int Command::init()
46 {
47   if (initted) return 0;
48   initted = 1;
49
50   logger = Log::getInstance();
51   viewman = ViewMan::getInstance();
52   remote = Remote::getInstance();
53
54   if (!logger || !viewman || !remote)
55   {
56     initted = 0;
57     return 0;
58   }
59 #ifndef WIN32
60   pthread_mutex_init(&masterLock, NULL);
61 #else
62   masterLock=CreateMutex(NULL,FALSE,NULL);
63 #endif
64
65   return 1;
66 }
67
68 int Command::shutdown()
69 {
70   if (!initted) return 0;
71   initted = 0;
72   return 1;
73 }
74
75 void Command::stop()
76 {
77 //  VDR::getInstance()->cancelFindingServer();
78   irun = 0;
79 }
80
81 void Command::doWallpaper()
82 {
83   Video* video = Video::getInstance();
84
85   // Blue background
86   View* v = new View();
87   v->create(video->getScreenWidth(), video->getScreenHeight());
88   v->setBackgroundColour(Colour::VIDEOBLUE);
89   v->draw();
90   viewman->add(v);
91   viewman->updateView(v);
92   viewman->removeView(v);
93
94   // Wallpaper
95   wallpaper = new VWallpaper();
96   if (video->getFormat() == Video::PAL)
97   {
98     logger->log("Command", Log::DEBUG, "PAL wallpaper selected");
99     wallpaper->init("/wallpaperPAL.jpg");
100   }
101   else
102   {
103     logger->log("Command", Log::DEBUG, "NTSC wallpaper selected");
104     wallpaper->init("/wallpaperNTSC.jpg");
105   }
106   wallpaper->draw();
107   viewman->add(wallpaper);
108   viewman->updateView(wallpaper);
109 }
110
111 void Command::run()
112 {
113   if (!initted) return;
114   irun = 1;
115 #ifndef WIN32
116   mainPid = getpid();
117 #endif
118
119   // just in case
120   Video::getInstance()->signalOn();
121   Led::getInstance()->on();
122
123   doWallpaper();
124
125   // End of startup. Lock the mutex and put the first view up
126 #ifndef WIN32
127   pthread_mutex_lock(&masterLock);
128 #else
129   WaitForSingleObject(masterLock, INFINITE );
130 #endif
131   VConnect* vconnect = new VConnect();
132   viewman->add(vconnect);
133   vconnect->run();
134
135
136   UCHAR button = 0;
137   while(irun)
138   {
139     // unlock and wait
140 #ifndef WIN32
141     pthread_mutex_unlock(&masterLock);
142 #else
143     ReleaseMutex(masterLock);
144 #endif
145
146     button = remote->getButtonPress(2);  // FIXME why is this set to 2 and not 0? so it can quit
147     // something happened, lock and process
148
149 #ifndef WIN32
150   pthread_mutex_lock(&masterLock);
151 #else
152   WaitForSingleObject(masterLock, INFINITE );
153 #endif
154
155     if ((button == Remote::NA_NONE) || (button == Remote::NA_UNKNOWN)) continue;
156
157     if (button != Remote::NA_SIGNAL) handleCommand(button);
158     processMessageQueue();
159   }
160
161 #ifndef WIN32
162     pthread_mutex_unlock(&masterLock);
163 #else
164     ReleaseMutex(masterLock);
165 #endif
166 }
167
168 void Command::postMessage(Message* m)
169 {
170   // This is locked here in case the main loop is not waiting for an event, but is processing one
171   // it could be killed but then not react to it because the signal wouldn't cause
172   // remote->getButtonPress to break
173   // locking the mutex ensures that the master thread is waiting on getButtonPress
174
175
176 #ifndef WIN32
177     pthread_mutex_lock(&masterLock);
178 #else
179     WaitForSingleObject(masterLock, INFINITE );
180 #endif
181     MessageQueue::postMessage(m);
182
183 #ifndef WIN32
184     kill(mainPid, SIGURG);
185     pthread_mutex_unlock(&masterLock);
186 #else
187     //TODO: send Window Message
188     ReleaseMutex(masterLock);
189 #endif
190 }
191
192 void Command::postMessageNoLock(Message* m)
193 {
194   // As above but use this one if this message is being posted because of a button press
195   // the mutex is already locked, locking around postMessage is not needed as the
196   // queue is guaranteed to be run when the button has been processed
197   MessageQueue::postMessage(m);
198 }
199
200 bool Command::postMessageIfNotBusy(Message* m)
201 {
202   // This is for the timers module
203   // If the masterlock is locked then the timers module wants to
204   // cancel delivery
205 #ifndef WIN32
206   if (pthread_mutex_trylock(&masterLock) != EBUSY)
207   {
208     MessageQueue::postMessage(m);
209     kill(mainPid, SIGURG);
210     pthread_mutex_unlock(&masterLock);
211     return true;
212   }
213   else
214   {
215     return false;
216   }
217 #else
218   if (WaitForSingleObject(masterLock, INFINITE ) == WAIT_OBJECT_0)
219   {
220     MessageQueue::postMessage(m);
221     //TODO: send Window Message
222     ReleaseMutex(masterLock);
223     return true;
224   }
225   else
226   {
227     return false;
228   }
229 #endif
230 }
231
232 void Command::processMessage(Message* m)
233 {
234   logger->log("Command", Log::DEBUG, "processing message %i", m->message);
235
236   switch(m->message)
237   {
238     case Message::STANDBY:
239     {
240       doStandby();
241       break;
242     }
243     case Message::STOP_PLAYBACK:
244     {
245       handleCommand(Remote::STOP); // an odd way of doing it, but so simple
246       break;
247     }
248     case Message::STREAM_END:
249     {
250       VVideoLive::getInstance()->streamEnd();
251       break;
252     }
253     case Message::VDR_CONNECTED:
254     {
255       doJustConnected((VConnect*)m->from);
256       break;
257     }
258     case Message::TIMER:
259     {
260       // FIXME - go to one message queue only - then instead of having
261       // objects deriving from messagequeues, make them derive from
262       // messagereceiver - then one messagequeue can deliver any message to anywhere
263
264       // deliver timer
265
266       ((TimerReceiver*)m->to)->timercall(m->parameter);
267       handleCommand(Remote::NA_NONE); // in case any timer has posted messages to viewman,
268                                       // run viewman message queue here. FIXME improve this!
269       break;
270     }
271     case Message::SCREENSHOT:
272     {
273       Osd::getInstance()->screenShot("/out.jpg");
274       break;
275     }
276     case Message::CONNECTION_LOST:
277     {
278       doFromTheTop(true);
279       break;
280     }
281   }
282 }
283
284 void Command::handleCommand(int button)
285 {
286   if (isStandby && (button != Remote::POWER)) return;
287
288   if (!connLost && viewman->handleCommand(button)) return; // don't send to viewman if connLost
289
290   // command was not handled
291
292   switch(button)
293   {
294     case Remote::DF_LEFT:
295     case Remote::DF_RIGHT:
296     case Remote::VOLUMEUP:
297     case Remote::VOLUMEDOWN:
298     {
299       VVolume* v = new VVolume();
300       viewman->add(v);
301       v->handleCommand(button); // this will draw+show
302       return;
303     }
304     case Remote::MUTE:
305     {
306       VMute* v = new VMute();
307       v->draw();
308       viewman->add(v);
309       viewman->updateView(v);
310       return;
311     }
312     case Remote::POWER:
313     {
314       doStandby();
315       return;
316     }
317     case Remote::OK:
318     {
319       if (!connLost) return; // if connLost, handle Remote::OK
320       doFromTheTop(false);
321       return;
322     }
323   }
324 }
325
326 void Command::sig1()
327 {
328 #ifdef DEV
329   Message* m = new Message(); // break into master mutex
330   m->message = Message::SCREENSHOT;
331   m->to = this;
332   postMessage(m);
333 #endif
334 }
335
336 void Command::doStandby()
337 {
338   if (isStandby)
339   {
340     Video::getInstance()->signalOn();
341     Led::getInstance()->on();
342     isStandby = 0;
343
344
345     VConnect* vconnect = new VConnect();
346     viewman->add(vconnect);
347     vconnect->run();
348   }
349   else
350   {
351     viewman->removeAll();
352     Video::getInstance()->signalOff();
353     viewman->updateView(wallpaper);
354
355     VDR::getInstance()->configSave("General", "Last Power State", "Off");
356     VDR::getInstance()->disconnect();
357     Led::getInstance()->off();
358     isStandby = 1;
359   }
360 }
361
362 void Command::doFromTheTop(bool which)
363 {
364   if (which)
365   {
366     connLost = new VInfo();
367     connLost->create(360, 200);
368     if (Video::getInstance()->getFormat() == Video::PAL)
369       connLost->setScreenPos(190, 170);
370     else
371       connLost->setScreenPos(180, 120);
372     connLost->setOneLiner(tr("Connection lost"));
373     connLost->setDropThrough();
374     connLost->setBorderOn(1);
375     connLost->setTitleBarColour(Colour::DANGER);
376     connLost->okButton();
377     connLost->draw();
378     viewman->add(connLost);
379     viewman->updateView(connLost);
380     remote->clearBuffer();
381   }
382   else
383   {
384     VDR::getInstance()->disconnect();
385     viewman->removeAll();
386     viewman->updateView(wallpaper);
387     connLost = NULL;
388     VConnect* vconnect = new VConnect();
389     viewman->add(vconnect);
390     vconnect->run();
391   }
392 }
393
394 void Command::doReboot()
395 {
396   VDR::getInstance()->disconnect();
397   // just kill it...
398   logger->log("Command", Log::NOTICE, "Reboot");
399 #ifndef WIN32
400   reboot(LINUX_REBOOT_CMD_RESTART);
401 #endif //Would we support this on windows?
402 }
403
404 void Command::connectionLost()
405 {
406   Message* m = new Message(); // break into master mutex
407   m->message = Message::CONNECTION_LOST;
408   m->to = this;
409   postMessageNoLock(m);
410 }
411
412 void Command::doJustConnected(VConnect* vconnect)
413 {
414   I18n::initialize();
415   Video* video = Video::getInstance();
416   viewman->removeView(vconnect);
417
418   VInfo* vi = new VInfo();
419   vi->create(400, 200);
420   if (video->getFormat() == Video::PAL)
421     vi->setScreenPos(170, 200);
422   else
423     vi->setScreenPos(160, 150);
424
425   vi->setOneLiner(tr("Connected, loading config"));
426   vi->draw();
427   viewman->add(vi);
428   viewman->updateView(vi);
429
430   VDR* vdr = VDR::getInstance();
431   char* config;
432
433   // See if config says to override video format (PAL/NTSC)
434   config = vdr->configLoad("General", "Override Video Format");
435   if (config)
436   {
437     logger->log("Command", Log::DEBUG, "Override Video Format is present");
438
439     if (   (!strcmp(config, "PAL") && (video->getFormat() == Video::NTSC))
440         || (!strcmp(config, "NTSC") && (video->getFormat() == Video::PAL))  )
441     {
442       // Oh sheesh, need to switch format. Bye bye TV...
443
444       // Take everything down
445       viewman->removeAll();
446       viewman->removeView(wallpaper);
447       Osd* osd = Osd::getInstance();
448       osd->shutdown();
449       video->shutdown();
450
451       // Get video and osd back up with the new mode
452       if (!strcmp(config, "PAL"))
453       {
454         logger->log("Command", Log::DEBUG, "Switching to PAL");
455         video->init(Video::PAL);
456       }
457       else if (!strcmp(config, "NTSC"))
458       {
459         logger->log("Command", Log::DEBUG, "Switching to NTSC");
460         video->init(Video::NTSC);
461       }
462       osd->init("/dev/stbgfx");
463
464       // Put the wallpaper back
465       doWallpaper();
466
467       // Re add the vinfo
468       vi = new VInfo();
469       vi->create(400, 200);
470       if (video->getFormat() == Video::PAL)
471         vi->setScreenPos(170, 200);
472       else
473         vi->setScreenPos(160, 150);
474
475       vi->setOneLiner(tr("Connected, loading config"));
476       vi->draw();
477       viewman->add(vi);
478       viewman->updateView(vi);
479     }
480     else
481     {
482       logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");
483     }
484   }
485   else
486   {
487     logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");
488   }
489
490   // Power off if first boot and config says so
491   if (firstBoot)
492   {
493     firstBoot = 0;
494
495     logger->log("Command", Log::DEBUG, "Load power after boot");
496
497     config = vdr->configLoad("General", "Power After Boot");
498
499     if (config)
500     {
501       if (!STRCASECMP(config, "On"))
502       {
503         logger->log("Command", Log::INFO, "Config says Power After Boot = On");
504       }
505       else if (!STRCASECMP(config, "Off"))
506       {
507         logger->log("Command", Log::INFO, "Config says Power After Boot = Off");
508         doStandby();
509         delete[] config;
510         return; // quit here
511       }
512       else if (!STRCASECMP(config, "Last state"))
513       {
514         char* lastPowerState = vdr->configLoad("General", "Last Power State");
515         if (lastPowerState)
516         {
517           if (!STRCASECMP(lastPowerState, "On"))
518           {
519             logger->log("Command", Log::INFO, "Config says Last Power State = On");
520           }
521           else if (!STRCASECMP(lastPowerState, "Off"))
522           {
523             logger->log("Command", Log::INFO, "Config says Last Power State = Off");
524             doStandby();
525             delete[] config;
526             return; // quit here
527           }
528           else
529           {
530             logger->log("Command", Log::INFO, "Config General/Last Power State not understood");
531           }
532         }
533         else
534         {
535           logger->log("Command", Log::INFO, "Config General/Last Power State not found");
536         }
537       }
538       else
539       {
540         logger->log("Command", Log::INFO, "Config/Power After Boot not understood");
541       }
542       delete[] config;
543     }
544     else
545     {
546       logger->log("Command", Log::INFO, "Config General/Power After Boot not found");
547     }
548   }
549
550
551   // Go S-Video if config says so
552
553   config = vdr->configLoad("TV", "Connection");
554
555   if (config)
556   {
557     if (!STRCASECMP(config, "S-Video"))
558     {
559       logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);
560       video->setConnection(Video::SVIDEO);
561     }
562     else
563     {
564       logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);
565       video->setConnection(Video::COMPOSITERGB);
566     }
567     delete[] config;
568   }
569   else
570   {
571     logger->log("Command", Log::INFO, "Config TV/S-Video not found");
572   }
573
574   // Set remote type
575
576   config = vdr->configLoad("General", "Remote type");
577
578   if (config)
579   {
580     if (!STRCASECMP(config, "New"))
581     {
582       logger->log("Command", Log::INFO, "Switching to New remote type");
583       remote->setRemoteType(Remote::NEWREMOTE);
584     }
585     else
586     {
587       logger->log("Command", Log::INFO, "Switching to Old remote type");
588       remote->setRemoteType(Remote::OLDREMOTE);
589     }
590     delete[] config;
591   }
592   else
593   {
594     logger->log("Command", Log::INFO, "Config General/Remote type not found");
595     remote->setRemoteType(Remote::OLDREMOTE);
596   }
597
598   // Get TV aspect ratio
599
600   config = vdr->configLoad("TV", "Aspect");
601   if (config)
602   {
603     if (!STRCASECMP(config, "16:9"))
604     {
605       logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");
606       video->setTVsize(Video::ASPECT16X9);
607     }
608     else
609     {
610       logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");
611       video->setTVsize(Video::ASPECT4X3);
612     }
613     delete[] config;
614   }
615   else
616   {
617     logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");
618     video->setTVsize(Video::ASPECT4X3);
619   }
620
621   config = vdr->configLoad("TV", "Widemode");
622   if (config)
623   {
624     if (!STRCASECMP(config, "Letterbox"))
625     {
626       logger->log("Command", Log::INFO, "Setting letterbox mode");
627       video->setMode(Video::LETTERBOX);
628     }
629     else
630     {
631       logger->log("Command", Log::INFO, "Setting chop-sides mode");
632       video->setMode(Video::NORMAL);
633     }
634     delete[] config;
635   }
636   else
637   {
638     logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");
639     video->setMode(Video::NORMAL);
640   }
641
642   config = vdr->configLoad("Advanced", "TCP receive window");
643   if (config)
644   {
645     size_t newTCPsize = atoi(config);
646     delete[] config;
647
648     logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);
649     vdr->setReceiveWindow(newTCPsize);
650   }
651   else
652   {
653     logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");
654     vdr->setReceiveWindow(2048); // Default
655   }
656
657
658   // config done
659
660   // Save power state = on
661
662   vdr->configSave("General", "Last Power State", "On");
663
664   // Make sure connection didn't die
665   if (!vdr->isConnected())
666   {
667     Command::getInstance()->connectionLost();
668   }
669   else
670   {
671     viewman->removeView(vi);
672
673     VWelcome* vw = new VWelcome();
674     vw->draw();
675     viewman->add(vw);
676     viewman->updateView(vw);
677
678   // Enter pre-keys here
679 //  handleCommand(Remote::THREE);
680 //  handleCommand(Remote::DOWN);
681 //  handleCommand(Remote::OK);
682 //  handleCommand(Remote::PLAY);
683   }
684 }