]> git.vomp.tv Git - vompclient.git/blob - inputcec.cc
43 CWFs
[vompclient.git] / inputcec.cc
1  /*
2     Copyright 2004-2020 Chris Tallon; 2012 Marten Richter
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, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include <iostream>
21 #include <libcec/cec.h>
22 #include <libcec/cecloader.h>
23 using namespace CEC;
24
25 #include <bcm_host.h>
26
27 #include "log.h"
28 #include "vdr.h"
29 #include "woptionpane.h"
30 #include "inputcec.h"
31
32 static const char* TAG = "InputCEC";
33
34
35 //#define W_G_HCW(type,code) ((static_cast<ULLONG>(type) << 32) | code)
36
37 //#define W_HCW_KC 1 /* key code as defined by kernel for keyboard and remotes through /dev/input */
38 //#define W_HCW_CEC 2 /* HDMI_CEC */
39 //#define W_HCW_LIRC 3 /* remote control LIRC*/
40
41
42 InputCEC* InputCEC::instance = NULL;
43
44 bool InputCEC::init()
45 {
46   InitHWCListwithDefaults();
47   InitKeymap();
48
49   // bcm_host_init(); //may be move to custom hardware init?
50   // now init cec
51   LogNT::getInstance()->info(TAG, "Init LibCEC");
52   instance = this;
53
54   cec_config.Clear();
55   cec_callbacks.Clear();
56   cec_callbacks.logMessage = cecLogMessage;
57   cec_callbacks.keyPress = cecKeyPress;
58   cec_callbacks.commandReceived = cecCommand;
59   cec_callbacks.configurationChanged = cecConfigurationChanged;
60   cec_callbacks.sourceActivated = cecSourceActivated;
61   cec_config.clientVersion = LIBCEC_VERSION_CURRENT;
62   cec_config.bActivateSource = 1;
63   //cec_config.deviceTypes.Add(CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
64   cec_config.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
65   //cec_config.deviceTypes.Add(CEC_DEVICE_TYPE_TUNER);
66
67   strncpy(cec_config.strDeviceName, "vomp", sizeof(cec_config.strDeviceName));
68
69   cec_config.callbackParam = NULL; // I do not care
70   cec_config.callbacks = &cec_callbacks;
71
72   cec_adap = LibCecInitialise(&cec_config);
73   if (!cec_adap)
74   {
75     LogNT::getInstance()->error(TAG, "Init LibCEC failed");
76     return false;
77   }
78
79   cec_adap->InitVideoStandalone();
80
81   cec_adapter_descriptor cec_adapter_descriptors[10];
82   int adap_num = cec_adap->DetectAdapters(cec_adapter_descriptors, 10);
83   if (adap_num < 0)
84   {
85     LogNT::getInstance()->error(TAG, "CEC:Failed to find adapter");
86     return false;
87   }
88
89   if (adap_num == 0)
90   {
91     LogNT::getInstance()->info(TAG, "CEC: No adapter found");
92     return false;
93   }
94
95   if (!cec_adap->Open(cec_adapter_descriptors[0].strComName))
96   {
97     LogNT::getInstance()->error(TAG, "CEC:Failed to open adapter");
98     return false;
99   }
100
101   if (!cec_adap->SetActiveSource(cec_config.deviceTypes[0]))
102   {
103     LogNT::getInstance()->error(TAG, "CEC:Failed set active source");
104     return false;
105   }
106
107   return true;
108 }
109
110 void InputCEC::shutdown()
111 {
112   if (cec_adap)
113   {
114     LogNT::getInstance()->info(TAG, "Shutdown libcec begin");
115     cec_adap->SetInactiveView();
116     cec_adap->Close();
117     vc_cec_register_callback(NULL, NULL); //deactivate callback!
118     UnloadLibCec(cec_adap);
119     cec_adap = NULL;
120     LogNT::getInstance()->info(TAG, "Shutdown libcec end");
121   }
122
123   instance = NULL;
124 }
125
126 void InputCEC::changePowerState(bool poweron)
127 {
128   if (cec_adap)
129   {
130     if (poweron)
131     {
132       //Log::getInstance()->log("InputCEC", Log::DEBUG, "CEC set active source");
133       cec_adap->SetActiveSource(cec_config.deviceTypes[0]);
134     }
135     else
136     {
137       //Log::getInstance()->log("InputCEC", Log::DEBUG, "CEC set inactive view");
138       cec_adap->SetInactiveView();
139     }
140   }
141 }
142
143 void InputCEC::incomingCECkey(int key)
144 {
145   sendInputKey(TranslateHWC(key));
146 }
147
148 void InputCEC::incomingPowerkey(UCHAR key)
149 {
150   sendInputKey(key);
151 }
152
153 void InputCEC::volumeUp()
154 {
155   cec_adap->VolumeUp();
156 }
157
158 void InputCEC::volumeDown()
159 {
160   cec_adap->VolumeDown();
161 }
162
163 void InputCEC::volumeMute()
164 {
165   cec_adap->AudioToggleMute();
166 }
167
168 bool InputCEC::loadOptionsFromServer(VDR* vdr)
169 {
170    // Set remote keys
171   char* name;
172   name = vdr->configLoad("InputCEC", "HandleVolume");
173
174   if (name != NULL)
175   {
176     if      (STRCASECMP(name, "Vomp") == 0) cechandlesvolume = false;
177     else if (STRCASECMP(name, "Cec") == 0) cechandlesvolume = true;
178     delete[] name;
179   }
180   return Input::loadOptionsFromServer(vdr);
181 }
182
183 bool InputCEC::saveOptionstoServer()
184 {
185   if (cechandlesvolume) VDR::getInstance()->configSave("InputCEC", "HandleVolume","Cec");
186   else VDR::getInstance()->configSave("InputCEC", "HandleVolume","Vomp");
187
188   return Input::saveOptionstoServer();
189 }
190
191 bool InputCEC::addOptionsToPanes(int panenumber,Options *options,WOptionPane* pane)
192 {
193   //if (!Input::addOptionsToPanes(panenumber, options, pane)) return false;
194
195   Option* option;
196   if (panenumber == 2)
197   {
198     static const char* volumeopts[]={"Vomp","Cec"};
199     option = new Option(100,tr("Volume handled by"), "InputCEC","HandleVolume",Option::TYPE_TEXT,/*4,2*/2,0,0,volumeopts,NULL,false,this);
200     options->push_back(option);
201     pane->addOptionLine(option);
202   }
203
204   return true;
205 }
206
207 bool InputCEC::handleOptionChanges(Option* option)
208 {
209     if (Input::handleOptionChanges(option))
210                 return true;
211         switch (option->id) {
212         case 100: {
213                 if (STRCASECMP(option->options[option->userSetChoice], "Vomp") == 0) {
214                         cechandlesvolume=false;
215                 }  else if (STRCASECMP(option->options[option->userSetChoice], "Cec")
216                                 == 0) {
217                         cechandlesvolume=true;
218                 }
219                 LogNT::getInstance()->debug(TAG, "Set volume handling to to {} {}",option->options[option->userSetChoice],cechandlesvolume);
220                 return true;
221         }
222         break;
223         };
224         return false;
225 }
226
227 // STATIC FUNCTIONS FOLLOW
228
229 void InputCEC::cecLogMessage(void* /* param */, const cec_log_message* message)
230 {
231   LogNT::getInstance()->debug(TAG, "CECLOG: {} {} {}", message->time, message->level, message->message);
232 }
233
234 void InputCEC::cecKeyPress(void* /* param */, const cec_keypress* key)
235 {
236   //Log::getInstance()->log("InputCEC", Log::DEBUG, "Incoming cec key %d %d", key->keycode,key->duration);
237   if (key->duration == 0) instance->incomingCECkey(key->keycode);
238 }
239
240 void InputCEC::cecCommand(void* /* param */, const cec_command* command)
241 {
242   LogNT::getInstance()->debug(TAG, "CECCommand: {}", command->opcode);
243   switch (command->opcode)
244   {
245     case CEC_OPCODE_STANDBY:
246     {
247       if (command->initiator==CECDEVICE_TV)
248       {
249         instance->incomingPowerkey(POWEROFF);
250       }
251       break;
252     }
253     case CEC_OPCODE_DECK_CONTROL:
254     {
255       if (command->initiator == CECDEVICE_TV && command->parameters.size == 1
256             && command->parameters[0] == CEC_DECK_CONTROL_MODE_STOP)
257       {
258         instance->incomingCECkey(CEC_USER_CONTROL_CODE_STOP);
259       }
260       break;
261     }
262     case CEC_OPCODE_PLAY:
263     {
264       if (command->initiator == CECDEVICE_TV && command->parameters.size == 1)
265       {
266         if (command->parameters[0] == CEC_PLAY_MODE_PLAY_FORWARD)
267         {
268           instance->incomingCECkey(CEC_USER_CONTROL_CODE_PLAY);
269         }
270         else if (command->parameters[0] == CEC_PLAY_MODE_PLAY_STILL)
271         {
272           instance->incomingCECkey(CEC_USER_CONTROL_CODE_PAUSE);
273         }
274       }
275       break;
276     }
277     default:
278       break;
279   }
280 }
281
282 void InputCEC::cecConfigurationChanged(void* /* param */, const libcec_configuration*)
283 {
284   LogNT::getInstance()->debug(TAG, "CECConfig:"/*,config->string()*/);
285 }
286
287 void InputCEC::cecSourceActivated(void* /* param */, const cec_logical_address address, const uint8_t activated)
288 {
289   LogNT::getInstance()->debug(TAG, "CECSourceActivated: {} {}", address, activated);
290   if (activated == 1)
291   {
292     instance->incomingPowerkey(POWERON);
293   }
294 }
295
296 void InputCEC::InitHWCListwithDefaults()
297 {
298   //Processing CEC_Messages
299   translist[CEC_USER_CONTROL_CODE_NUMBER9] = NINE;
300   translist[CEC_USER_CONTROL_CODE_NUMBER8] = EIGHT;
301   translist[CEC_USER_CONTROL_CODE_NUMBER7] = SEVEN;
302   translist[CEC_USER_CONTROL_CODE_NUMBER6] = SIX;
303   translist[CEC_USER_CONTROL_CODE_NUMBER5] = FIVE;
304   translist[CEC_USER_CONTROL_CODE_NUMBER4] = FOUR;
305   translist[CEC_USER_CONTROL_CODE_NUMBER3] = THREE;
306   translist[CEC_USER_CONTROL_CODE_NUMBER2] = TWO;
307   translist[CEC_USER_CONTROL_CODE_NUMBER1] = ONE;
308   translist[CEC_USER_CONTROL_CODE_NUMBER0] = ZERO;
309   //translist[KEY_KPDOT] = STAR;
310
311   //translist[KEY_J] = GO; //j for JUMP TO instead of go to
312   translist[CEC_USER_CONTROL_CODE_F2_RED] = RED;
313   translist[CEC_USER_CONTROL_CODE_F3_GREEN] = GREEN;
314   translist[CEC_USER_CONTROL_CODE_F4_YELLOW] = YELLOW;
315   translist[CEC_USER_CONTROL_CODE_F1_BLUE] = BLUE;
316   //Processing Remote Style Messages
317   translist[CEC_USER_CONTROL_CODE_FAVORITE_MENU] = MENU;
318
319   translist[CEC_USER_CONTROL_CODE_RECORD] = RECORD;
320   translist[CEC_USER_CONTROL_CODE_PLAY] = PLAY; //Playback Televison
321   translist[CEC_USER_CONTROL_CODE_PAUSE] = PAUSE;
322   translist[CEC_USER_CONTROL_CODE_STOP] = STOP;
323   translist[CEC_USER_CONTROL_CODE_PAUSE_PLAY_FUNCTION] = PLAYPAUSE;
324   translist[CEC_USER_CONTROL_CODE_FORWARD] = SKIPFORWARD;
325   translist[CEC_USER_CONTROL_CODE_BACKWARD] = SKIPBACK;
326   translist[CEC_USER_CONTROL_CODE_FAST_FORWARD ] = FORWARD;
327   translist[CEC_USER_CONTROL_CODE_REWIND] = REVERSE;
328   translist[CEC_USER_CONTROL_CODE_MUTE] = MUTE;
329   translist[CEC_USER_CONTROL_CODE_VOLUME_UP] = VOLUMEUP;
330   translist[CEC_USER_CONTROL_CODE_VOLUME_DOWN] = VOLUMEDOWN;
331   translist[CEC_USER_CONTROL_CODE_CHANNEL_UP ] = CHANNELUP;
332   translist[CEC_USER_CONTROL_CODE_CHANNEL_DOWN] = CHANNELDOWN;
333 }
334
335 UCHAR InputCEC::TranslateHWCFixed(int code)
336 {
337   switch (code)
338   {
339     case CEC_USER_CONTROL_CODE_DOWN:            return DOWN;
340     case CEC_USER_CONTROL_CODE_UP:              return UP;
341     case CEC_USER_CONTROL_CODE_LEFT:            return LEFT;
342     case CEC_USER_CONTROL_CODE_RIGHT:           return RIGHT;
343     case CEC_USER_CONTROL_CODE_ROOT_MENU:
344     case CEC_USER_CONTROL_CODE_CONTENTS_MENU:
345     case CEC_USER_CONTROL_CODE_SETUP_MENU:      return MENU;
346     case CEC_USER_CONTROL_CODE_EXIT:            return BACK;
347     case CEC_USER_CONTROL_CODE_ENTER:
348     case CEC_USER_CONTROL_CODE_SELECT:
349     case CEC_USER_CONTROL_CODE_AN_RETURN:       return OK;
350     case CEC_USER_CONTROL_CODE_POWER:
351     case POWER:                                 return POWER;   //?
352     default:
353       return NA_UNKNOWN;
354   }
355 }
356
357
358 #define NAMETRICK2(pre, code) cec_keymap[pre ## code]=  #code
359 static const char * cec_keymap[CEC_USER_CONTROL_CODE_MAX+1];
360
361 void InputCEC::InitKeymap()
362 {
363   for (int i=0; i < CEC_USER_CONTROL_CODE_MAX + 1; i++)
364   {
365     cec_keymap[i] = NULL;
366   }
367
368   NAMETRICK2(CEC_USER_CONTROL_CODE_,SELECT);
369   NAMETRICK2(CEC_USER_CONTROL_CODE_,UP);
370   NAMETRICK2(CEC_USER_CONTROL_CODE_,DOWN);
371   NAMETRICK2(CEC_USER_CONTROL_CODE_,LEFT);
372   NAMETRICK2(CEC_USER_CONTROL_CODE_,RIGHT);
373   NAMETRICK2(CEC_USER_CONTROL_CODE_,RIGHT_UP);
374   NAMETRICK2(CEC_USER_CONTROL_CODE_,RIGHT_DOWN);
375   NAMETRICK2(CEC_USER_CONTROL_CODE_,LEFT_UP);
376   NAMETRICK2(CEC_USER_CONTROL_CODE_,LEFT_DOWN);
377   NAMETRICK2(CEC_USER_CONTROL_CODE_,ROOT_MENU);
378   NAMETRICK2(CEC_USER_CONTROL_CODE_,SETUP_MENU);
379   NAMETRICK2(CEC_USER_CONTROL_CODE_,CONTENTS_MENU);
380   NAMETRICK2(CEC_USER_CONTROL_CODE_,FAVORITE_MENU);
381   NAMETRICK2(CEC_USER_CONTROL_CODE_,EXIT);
382   NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER0);
383   NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER1);
384   NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER2);
385   NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER3);
386   NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER4);
387   NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER5);
388   NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER6);
389   NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER7);
390   NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER8);
391   NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER9);
392   NAMETRICK2(CEC_USER_CONTROL_CODE_,DOT);
393   NAMETRICK2(CEC_USER_CONTROL_CODE_,ENTER);
394   NAMETRICK2(CEC_USER_CONTROL_CODE_,CLEAR);
395   NAMETRICK2(CEC_USER_CONTROL_CODE_,NEXT_FAVORITE);
396   NAMETRICK2(CEC_USER_CONTROL_CODE_,CHANNEL_UP);
397   NAMETRICK2(CEC_USER_CONTROL_CODE_,CHANNEL_DOWN);
398   NAMETRICK2(CEC_USER_CONTROL_CODE_,PREVIOUS_CHANNEL);
399   NAMETRICK2(CEC_USER_CONTROL_CODE_,SOUND_SELECT);
400   NAMETRICK2(CEC_USER_CONTROL_CODE_,INPUT_SELECT);
401   NAMETRICK2(CEC_USER_CONTROL_CODE_,DISPLAY_INFORMATION);
402   NAMETRICK2(CEC_USER_CONTROL_CODE_,HELP);
403   NAMETRICK2(CEC_USER_CONTROL_CODE_,PAGE_UP);
404   NAMETRICK2(CEC_USER_CONTROL_CODE_,PAGE_DOWN);
405   NAMETRICK2(CEC_USER_CONTROL_CODE_,POWER );
406   NAMETRICK2(CEC_USER_CONTROL_CODE_,VOLUME_UP );
407   NAMETRICK2(CEC_USER_CONTROL_CODE_,VOLUME_DOWN );
408   NAMETRICK2(CEC_USER_CONTROL_CODE_,MUTE );
409   NAMETRICK2(CEC_USER_CONTROL_CODE_,PLAY );
410   NAMETRICK2(CEC_USER_CONTROL_CODE_,STOP );
411   NAMETRICK2(CEC_USER_CONTROL_CODE_,PAUSE );
412   NAMETRICK2(CEC_USER_CONTROL_CODE_,RECORD );
413   NAMETRICK2(CEC_USER_CONTROL_CODE_,REWIND );
414   NAMETRICK2(CEC_USER_CONTROL_CODE_,FAST_FORWARD );
415   NAMETRICK2(CEC_USER_CONTROL_CODE_,EJECT );
416   NAMETRICK2(CEC_USER_CONTROL_CODE_,FORWARD );
417   NAMETRICK2(CEC_USER_CONTROL_CODE_,BACKWARD );
418   NAMETRICK2(CEC_USER_CONTROL_CODE_,STOP_RECORD );
419   NAMETRICK2(CEC_USER_CONTROL_CODE_,PAUSE_RECORD );
420   NAMETRICK2(CEC_USER_CONTROL_CODE_,ANGLE );
421   NAMETRICK2(CEC_USER_CONTROL_CODE_,SUB_PICTURE );
422   NAMETRICK2(CEC_USER_CONTROL_CODE_,VIDEO_ON_DEMAND );
423   NAMETRICK2(CEC_USER_CONTROL_CODE_,ELECTRONIC_PROGRAM_GUIDE );
424   NAMETRICK2(CEC_USER_CONTROL_CODE_,TIMER_PROGRAMMING );
425   NAMETRICK2(CEC_USER_CONTROL_CODE_,INITIAL_CONFIGURATION );
426   NAMETRICK2(CEC_USER_CONTROL_CODE_,PLAY_FUNCTION );
427   NAMETRICK2(CEC_USER_CONTROL_CODE_,PAUSE_PLAY_FUNCTION );
428   NAMETRICK2(CEC_USER_CONTROL_CODE_,RECORD_FUNCTION );
429   NAMETRICK2(CEC_USER_CONTROL_CODE_,PAUSE_RECORD_FUNCTION );
430   NAMETRICK2(CEC_USER_CONTROL_CODE_,STOP_FUNCTION );
431   NAMETRICK2(CEC_USER_CONTROL_CODE_,MUTE_FUNCTION );
432   NAMETRICK2(CEC_USER_CONTROL_CODE_,RESTORE_VOLUME_FUNCTION );
433   NAMETRICK2(CEC_USER_CONTROL_CODE_,TUNE_FUNCTION );
434   NAMETRICK2(CEC_USER_CONTROL_CODE_,SELECT_MEDIA_FUNCTION );
435   NAMETRICK2(CEC_USER_CONTROL_CODE_,SELECT_AV_INPUT_FUNCTION );
436   NAMETRICK2(CEC_USER_CONTROL_CODE_,SELECT_AUDIO_INPUT_FUNCTION );
437   NAMETRICK2(CEC_USER_CONTROL_CODE_,POWER_TOGGLE_FUNCTION );
438   NAMETRICK2(CEC_USER_CONTROL_CODE_,POWER_OFF_FUNCTION );
439   NAMETRICK2(CEC_USER_CONTROL_CODE_,POWER_ON_FUNCTION );
440   NAMETRICK2(CEC_USER_CONTROL_CODE_,F1_BLUE );
441   NAMETRICK2(CEC_USER_CONTROL_CODE_,F2_RED );
442   NAMETRICK2(CEC_USER_CONTROL_CODE_,F3_GREEN );
443   NAMETRICK2(CEC_USER_CONTROL_CODE_,F4_YELLOW );
444   NAMETRICK2(CEC_USER_CONTROL_CODE_,F5 );
445   NAMETRICK2(CEC_USER_CONTROL_CODE_,DATA );
446   NAMETRICK2(CEC_USER_CONTROL_CODE_,AN_RETURN );
447   NAMETRICK2(CEC_USER_CONTROL_CODE_,AN_CHANNELS_LIST );
448   NAMETRICK2(CEC_USER_CONTROL_CODE_,MAX );
449 }
450
451 const char* InputCEC::getHardCodedHardwareKeyNamesForVompKey(UCHAR /* vompKey */)
452 {
453   return ""; // FIXME
454 }
455
456 std::string InputCEC::getHardwareKeyName(int hardwareKey)
457 {
458   const char* desc = cec_keymap[hardwareKey];
459
460   std::string retval;
461
462   if (desc)
463   {
464     retval = desc;
465   }
466   else
467   {
468     char* rt = new char[10];
469     sprintf(rt, "0x%x", hardwareKey);
470     retval = rt;
471   }
472
473   return retval;
474 }
475