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