2 Copyright 2004-2020 Chris Tallon; 2012 Marten Richter
4 This file is part of VOMP.
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.
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.
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/>.
21 #include <libcec/cec.h>
22 #include <libcec/cecloader.h>
29 #include "messagequeue.h"
31 #include "woptionpane.h"
35 //#define W_G_HCW(type,code) ((static_cast<ULLONG>(type) << 32) | code)
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*/
42 InputCEC* InputCEC::instance = NULL;
46 InitHWCListwithDefaults();
49 // bcm_host_init(); //may be move to custom hardware init?
51 Log::getInstance()->log("InputCEC", Log::NOTICE, "Init LibCEC");
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);
67 strncpy(cec_config.strDeviceName, "vomp", sizeof(cec_config.strDeviceName));
69 cec_config.callbackParam = NULL; // I do not care
70 cec_config.callbacks = &cec_callbacks;
72 cec_adap = LibCecInitialise(&cec_config);
75 Log::getInstance()->log("InputCEC", Log::ERR, "Init LibCEC failed");
79 cec_adap->InitVideoStandalone();
81 cec_adapter_descriptor cec_adapter_descriptors[10];
82 int adap_num = cec_adap->DetectAdapters(cec_adapter_descriptors, 10);
85 Log::getInstance()->log("InputCEC", Log::ERR, "CEC:Failed to find adapter");
91 Log::getInstance()->log("InputCEC", Log::NOTICE, "CEC: No adapter found");
95 if (!cec_adap->Open(cec_adapter_descriptors[0].strComName))
97 Log::getInstance()->log("InputCEC", Log::ERR, "CEC:Failed to open adapter");
101 if (!cec_adap->SetActiveSource(cec_config.deviceTypes[0]))
103 Log::getInstance()->log("InputCEC", Log::ERR, "CEC:Failed set active source");
110 void InputCEC::shutdown()
114 Log::getInstance()->log("InputCEC", Log::NOTICE, "Shutdown libcec begin");
115 cec_adap->SetInactiveView();
117 vc_cec_register_callback(NULL, NULL); //deactivate callback!
118 UnloadLibCec(cec_adap);
120 Log::getInstance()->log("InputCEC", Log::NOTICE, "Shutdown libcec end");
126 void InputCEC::changePowerState(bool poweron)
132 //Log::getInstance()->log("InputCEC", Log::DEBUG, "CEC set active source");
133 cec_adap->SetActiveSource(cec_config.deviceTypes[0]);
137 //Log::getInstance()->log("InputCEC", Log::DEBUG, "CEC set inactive view");
138 cec_adap->SetInactiveView();
143 void InputCEC::incomingCECkey(int keys)
145 // Send INPUT message
146 Message* m = new Message();
147 m->message = Message::INPUT_EVENT;
148 m->to = Command::getInstance();
150 m->parameter = TranslateHWC(keys);
151 MessageQueue::getInstance()->postMessage(m);
154 void InputCEC::incomingPowerkey(UCHAR key)
156 // Send INPUT message
157 Message* m = new Message();
158 m->message = Message::INPUT_EVENT;
159 m->to = Command::getInstance();
162 MessageQueue::getInstance()->postMessage(m);
165 void InputCEC::volumeUp()
167 cec_adap->VolumeUp();
170 void InputCEC::volumeDown()
172 cec_adap->VolumeDown();
175 void InputCEC::volumeMute()
177 cec_adap->AudioToggleMute();
180 bool InputCEC::loadOptionsfromServer(VDR* vdr)
184 name = vdr->configLoad("InputCEC", "HandleVolume");
188 if (STRCASECMP(name, "Vomp") == 0) cechandlesvolume = false;
189 else if (STRCASECMP(name, "Cec") == 0) cechandlesvolume = true;
192 return Input::loadOptionsfromServer(vdr);
195 bool InputCEC::saveOptionstoServer()
197 if (cechandlesvolume) VDR::getInstance()->configSave("InputCEC", "HandleVolume","Cec");
198 else VDR::getInstance()->configSave("InputCEC", "HandleVolume","Vomp");
200 return Input::saveOptionstoServer();
203 bool InputCEC::addOptionsToPanes(int panenumber,Options *options,WOptionPane* pane)
205 //if (!Input::addOptionsToPanes(panenumber, options, pane)) return false;
210 static const char* volumeopts[]={"Vomp","Cec"};
211 option = new Option(100,tr("Volume handled by"), "InputCEC","HandleVolume",Option::TYPE_TEXT,/*4,2*/2,0,0,volumeopts,NULL,false,this);
212 options->push_back(option);
213 pane->addOptionLine(option);
219 bool InputCEC::handleOptionChanges(Option* option)
221 if (Input::handleOptionChanges(option))
223 switch (option->id) {
225 if (STRCASECMP(option->options[option->userSetChoice], "Vomp") == 0) {
226 cechandlesvolume=false;
227 } else if (STRCASECMP(option->options[option->userSetChoice], "Cec")
229 cechandlesvolume=true;
231 Log::getInstance()->log("InputCEC", Log::DEBUG, "Set volume handling to to %s %d",option->options[option->userSetChoice],cechandlesvolume);
239 // STATIC FUNCTIONS FOLLOW
241 void InputCEC::cecLogMessage(void* /* param */, const cec_log_message* message)
243 Log::getInstance()->log("InputCEC", Log::DEBUG, "CECLOG: %lld %d %s", message->time, message->level, message->message);
246 void InputCEC::cecKeyPress(void* /* param */, const cec_keypress* key)
248 //Log::getInstance()->log("InputCEC", Log::DEBUG, "Incoming cec key %d %d", key->keycode,key->duration);
249 if (key->duration == 0) instance->incomingCECkey(key->keycode);
252 void InputCEC::cecCommand(void* /* param */, const cec_command* command)
254 Log::getInstance()->log("InputCEC", Log::DEBUG, "CECCommand: %d", command->opcode);
255 switch (command->opcode)
257 case CEC_OPCODE_STANDBY:
259 if (command->initiator==CECDEVICE_TV)
261 instance->incomingPowerkey(POWEROFF);
265 case CEC_OPCODE_DECK_CONTROL:
267 if (command->initiator == CECDEVICE_TV && command->parameters.size == 1
268 && command->parameters[0] == CEC_DECK_CONTROL_MODE_STOP)
270 instance->incomingCECkey(CEC_USER_CONTROL_CODE_STOP);
274 case CEC_OPCODE_PLAY:
276 if (command->initiator == CECDEVICE_TV && command->parameters.size == 1)
278 if (command->parameters[0] == CEC_PLAY_MODE_PLAY_FORWARD)
280 instance->incomingCECkey(CEC_USER_CONTROL_CODE_PLAY);
282 else if (command->parameters[0] == CEC_PLAY_MODE_PLAY_STILL)
284 instance->incomingCECkey(CEC_USER_CONTROL_CODE_PAUSE);
294 void InputCEC::cecConfigurationChanged(void* /* param */, const libcec_configuration*)
296 Log::getInstance()->log("InputCEC", Log::DEBUG, "CECConfig:"/*,config->string()*/);
299 void InputCEC::cecSourceActivated(void* /* param */, const cec_logical_address address, const uint8_t activated)
301 Log::getInstance()->log("InputCEC", Log::DEBUG, "CECSourceActivated: %d %d", address, activated);
304 instance->incomingPowerkey(POWERON);
308 void InputCEC::InitHWCListwithDefaults()
310 //Processing CEC_Messages
311 translist[CEC_USER_CONTROL_CODE_NUMBER9] = NINE;
312 translist[CEC_USER_CONTROL_CODE_NUMBER8] = EIGHT;
313 translist[CEC_USER_CONTROL_CODE_NUMBER7] = SEVEN;
314 translist[CEC_USER_CONTROL_CODE_NUMBER6] = SIX;
315 translist[CEC_USER_CONTROL_CODE_NUMBER5] = FIVE;
316 translist[CEC_USER_CONTROL_CODE_NUMBER4] = FOUR;
317 translist[CEC_USER_CONTROL_CODE_NUMBER3] = THREE;
318 translist[CEC_USER_CONTROL_CODE_NUMBER2] = TWO;
319 translist[CEC_USER_CONTROL_CODE_NUMBER1] = ONE;
320 translist[CEC_USER_CONTROL_CODE_NUMBER0] = ZERO;
321 //translist[KEY_KPDOT] = STAR;
323 //translist[KEY_J] = GO; //j for JUMP TO instead of go to
324 translist[CEC_USER_CONTROL_CODE_F2_RED] = RED;
325 translist[CEC_USER_CONTROL_CODE_F3_GREEN] = GREEN;
326 translist[CEC_USER_CONTROL_CODE_F4_YELLOW] = YELLOW;
327 translist[CEC_USER_CONTROL_CODE_F1_BLUE] = BLUE;
328 //Processing Remote Style Messages
329 translist[CEC_USER_CONTROL_CODE_FAVORITE_MENU] = MENU;
331 translist[CEC_USER_CONTROL_CODE_RECORD] = RECORD;
332 translist[CEC_USER_CONTROL_CODE_PLAY] = PLAY; //Playback Televison
333 translist[CEC_USER_CONTROL_CODE_PAUSE] = PAUSE;
334 translist[CEC_USER_CONTROL_CODE_STOP] = STOP;
335 translist[CEC_USER_CONTROL_CODE_PAUSE_PLAY_FUNCTION] = PLAYPAUSE;
336 translist[CEC_USER_CONTROL_CODE_FORWARD] = SKIPFORWARD;
337 translist[CEC_USER_CONTROL_CODE_BACKWARD] = SKIPBACK;
338 translist[CEC_USER_CONTROL_CODE_FAST_FORWARD ] = FORWARD;
339 translist[CEC_USER_CONTROL_CODE_REWIND] = REVERSE;
340 translist[CEC_USER_CONTROL_CODE_MUTE] = MUTE;
341 translist[CEC_USER_CONTROL_CODE_VOLUME_UP] = VOLUMEUP;
342 translist[CEC_USER_CONTROL_CODE_VOLUME_DOWN] = VOLUMEDOWN;
343 translist[CEC_USER_CONTROL_CODE_CHANNEL_UP ] = CHANNELUP;
344 translist[CEC_USER_CONTROL_CODE_CHANNEL_DOWN] = CHANNELDOWN;
347 UCHAR InputCEC::TranslateHWCFixed(int code)
351 case CEC_USER_CONTROL_CODE_DOWN: return DOWN;
352 case CEC_USER_CONTROL_CODE_UP: return UP;
353 case CEC_USER_CONTROL_CODE_LEFT: return LEFT;
354 case CEC_USER_CONTROL_CODE_RIGHT: return RIGHT;
355 case CEC_USER_CONTROL_CODE_ROOT_MENU:
356 case CEC_USER_CONTROL_CODE_CONTENTS_MENU:
357 case CEC_USER_CONTROL_CODE_SETUP_MENU: return MENU;
358 case CEC_USER_CONTROL_CODE_EXIT: return BACK;
359 case CEC_USER_CONTROL_CODE_ENTER:
360 case CEC_USER_CONTROL_CODE_SELECT:
361 case CEC_USER_CONTROL_CODE_AN_RETURN: return OK;
362 case CEC_USER_CONTROL_CODE_POWER:
363 case POWER: return POWER; //?
370 #define NAMETRICK2(pre, code) cec_keymap[pre ## code]= #code
371 static const char * cec_keymap[CEC_USER_CONTROL_CODE_MAX+1];
373 void InputCEC::InitKeymap()
375 for (int i=0; i < CEC_USER_CONTROL_CODE_MAX + 1; i++)
377 cec_keymap[i] = NULL;
380 NAMETRICK2(CEC_USER_CONTROL_CODE_,SELECT);
381 NAMETRICK2(CEC_USER_CONTROL_CODE_,UP);
382 NAMETRICK2(CEC_USER_CONTROL_CODE_,DOWN);
383 NAMETRICK2(CEC_USER_CONTROL_CODE_,LEFT);
384 NAMETRICK2(CEC_USER_CONTROL_CODE_,RIGHT);
385 NAMETRICK2(CEC_USER_CONTROL_CODE_,RIGHT_UP);
386 NAMETRICK2(CEC_USER_CONTROL_CODE_,RIGHT_DOWN);
387 NAMETRICK2(CEC_USER_CONTROL_CODE_,LEFT_UP);
388 NAMETRICK2(CEC_USER_CONTROL_CODE_,LEFT_DOWN);
389 NAMETRICK2(CEC_USER_CONTROL_CODE_,ROOT_MENU);
390 NAMETRICK2(CEC_USER_CONTROL_CODE_,SETUP_MENU);
391 NAMETRICK2(CEC_USER_CONTROL_CODE_,CONTENTS_MENU);
392 NAMETRICK2(CEC_USER_CONTROL_CODE_,FAVORITE_MENU);
393 NAMETRICK2(CEC_USER_CONTROL_CODE_,EXIT);
394 NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER0);
395 NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER1);
396 NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER2);
397 NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER3);
398 NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER4);
399 NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER5);
400 NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER6);
401 NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER7);
402 NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER8);
403 NAMETRICK2(CEC_USER_CONTROL_CODE_,NUMBER9);
404 NAMETRICK2(CEC_USER_CONTROL_CODE_,DOT);
405 NAMETRICK2(CEC_USER_CONTROL_CODE_,ENTER);
406 NAMETRICK2(CEC_USER_CONTROL_CODE_,CLEAR);
407 NAMETRICK2(CEC_USER_CONTROL_CODE_,NEXT_FAVORITE);
408 NAMETRICK2(CEC_USER_CONTROL_CODE_,CHANNEL_UP);
409 NAMETRICK2(CEC_USER_CONTROL_CODE_,CHANNEL_DOWN);
410 NAMETRICK2(CEC_USER_CONTROL_CODE_,PREVIOUS_CHANNEL);
411 NAMETRICK2(CEC_USER_CONTROL_CODE_,SOUND_SELECT);
412 NAMETRICK2(CEC_USER_CONTROL_CODE_,INPUT_SELECT);
413 NAMETRICK2(CEC_USER_CONTROL_CODE_,DISPLAY_INFORMATION);
414 NAMETRICK2(CEC_USER_CONTROL_CODE_,HELP);
415 NAMETRICK2(CEC_USER_CONTROL_CODE_,PAGE_UP);
416 NAMETRICK2(CEC_USER_CONTROL_CODE_,PAGE_DOWN);
417 NAMETRICK2(CEC_USER_CONTROL_CODE_,POWER );
418 NAMETRICK2(CEC_USER_CONTROL_CODE_,VOLUME_UP );
419 NAMETRICK2(CEC_USER_CONTROL_CODE_,VOLUME_DOWN );
420 NAMETRICK2(CEC_USER_CONTROL_CODE_,MUTE );
421 NAMETRICK2(CEC_USER_CONTROL_CODE_,PLAY );
422 NAMETRICK2(CEC_USER_CONTROL_CODE_,STOP );
423 NAMETRICK2(CEC_USER_CONTROL_CODE_,PAUSE );
424 NAMETRICK2(CEC_USER_CONTROL_CODE_,RECORD );
425 NAMETRICK2(CEC_USER_CONTROL_CODE_,REWIND );
426 NAMETRICK2(CEC_USER_CONTROL_CODE_,FAST_FORWARD );
427 NAMETRICK2(CEC_USER_CONTROL_CODE_,EJECT );
428 NAMETRICK2(CEC_USER_CONTROL_CODE_,FORWARD );
429 NAMETRICK2(CEC_USER_CONTROL_CODE_,BACKWARD );
430 NAMETRICK2(CEC_USER_CONTROL_CODE_,STOP_RECORD );
431 NAMETRICK2(CEC_USER_CONTROL_CODE_,PAUSE_RECORD );
432 NAMETRICK2(CEC_USER_CONTROL_CODE_,ANGLE );
433 NAMETRICK2(CEC_USER_CONTROL_CODE_,SUB_PICTURE );
434 NAMETRICK2(CEC_USER_CONTROL_CODE_,VIDEO_ON_DEMAND );
435 NAMETRICK2(CEC_USER_CONTROL_CODE_,ELECTRONIC_PROGRAM_GUIDE );
436 NAMETRICK2(CEC_USER_CONTROL_CODE_,TIMER_PROGRAMMING );
437 NAMETRICK2(CEC_USER_CONTROL_CODE_,INITIAL_CONFIGURATION );
438 NAMETRICK2(CEC_USER_CONTROL_CODE_,PLAY_FUNCTION );
439 NAMETRICK2(CEC_USER_CONTROL_CODE_,PAUSE_PLAY_FUNCTION );
440 NAMETRICK2(CEC_USER_CONTROL_CODE_,RECORD_FUNCTION );
441 NAMETRICK2(CEC_USER_CONTROL_CODE_,PAUSE_RECORD_FUNCTION );
442 NAMETRICK2(CEC_USER_CONTROL_CODE_,STOP_FUNCTION );
443 NAMETRICK2(CEC_USER_CONTROL_CODE_,MUTE_FUNCTION );
444 NAMETRICK2(CEC_USER_CONTROL_CODE_,RESTORE_VOLUME_FUNCTION );
445 NAMETRICK2(CEC_USER_CONTROL_CODE_,TUNE_FUNCTION );
446 NAMETRICK2(CEC_USER_CONTROL_CODE_,SELECT_MEDIA_FUNCTION );
447 NAMETRICK2(CEC_USER_CONTROL_CODE_,SELECT_AV_INPUT_FUNCTION );
448 NAMETRICK2(CEC_USER_CONTROL_CODE_,SELECT_AUDIO_INPUT_FUNCTION );
449 NAMETRICK2(CEC_USER_CONTROL_CODE_,POWER_TOGGLE_FUNCTION );
450 NAMETRICK2(CEC_USER_CONTROL_CODE_,POWER_OFF_FUNCTION );
451 NAMETRICK2(CEC_USER_CONTROL_CODE_,POWER_ON_FUNCTION );
452 NAMETRICK2(CEC_USER_CONTROL_CODE_,F1_BLUE );
453 NAMETRICK2(CEC_USER_CONTROL_CODE_,F2_RED );
454 NAMETRICK2(CEC_USER_CONTROL_CODE_,F3_GREEN );
455 NAMETRICK2(CEC_USER_CONTROL_CODE_,F4_YELLOW );
456 NAMETRICK2(CEC_USER_CONTROL_CODE_,F5 );
457 NAMETRICK2(CEC_USER_CONTROL_CODE_,DATA );
458 NAMETRICK2(CEC_USER_CONTROL_CODE_,AN_RETURN );
459 NAMETRICK2(CEC_USER_CONTROL_CODE_,AN_CHANNELS_LIST );
460 NAMETRICK2(CEC_USER_CONTROL_CODE_,MAX );