MythTV  master
cecadapter.cpp
Go to the documentation of this file.
1 // Qt
2 #include <QApplication>
3 #include <QKeyEvent>
4 #include <QString>
5 
6 // MythTV
7 #include "mythcorecontext.h"
8 #include "mythlogging.h"
9 #include "mythevent.h"
10 #include "mythuihelper.h"
11 #include "mythmainwindow.h"
12 
13 #include "cecadapter.h"
14 #include <vector>
15 
16 #define MAX_CEC_DEVICES 10
17 #define LOC QString("CECAdapter: ")
18 
19 #include <libcec/cec.h>
20 #include <iostream>
21 using namespace CEC;
22 using namespace std;
23 #include <libcec/cecloader.h>
24 
25 QMutex* CECAdapter::gLock = new QMutex(QMutex::Recursive);
26 QMutex* CECAdapter::gHandleActionsLock = new QMutex();
27 QWaitCondition* CECAdapter::gActionsReady = new QWaitCondition();
28 
29 #if CEC_LIB_VERSION_MAJOR < 2
30 // A few defines taken from libcec.h v2
31 #define CEC_MIN_HDMI_PORTNUMBER 1
32 #define CEC_MAX_HDMI_PORTNUMBER 15
33 // libcec1's callback parameters are pass-by-ref
34 #define CEC_CALLBACK_PARAM_TYPE &
35 #else
36 #if CEC_LIB_VERSION_MAJOR <= 3
37 // libcec2 and 3 callback parameters are pass-by-value
38 #define CEC_CALLBACK_PARAM_TYPE
39 #endif
40 #endif
41 
42 // The libCEC callback functions
43 #if CEC_LIB_VERSION_MAJOR <= 3
44 static int CECLogMessageCallback(void *adapter, const cec_log_message CEC_CALLBACK_PARAM_TYPE message);
45 static int CECKeyPressCallback(void *adapter, const cec_keypress CEC_CALLBACK_PARAM_TYPE keypress);
46 static int CECCommandCallback(void *adapter, const cec_command CEC_CALLBACK_PARAM_TYPE command);
47 #endif
48 #if CEC_LIB_VERSION_MAJOR >= 4
49 static void CECLogMessageCallback(void *adapter, const cec_log_message* message);
50 static void CECKeyPressCallback(void *adapter, const cec_keypress* keypress);
51 static void CECCommandCallback(void *adapter, const cec_command* command);
52 #endif
53 
54 #if CEC_LIB_VERSION_MAJOR >= 2
55 #if CEC_LIB_VERSION_MAJOR <= 3
56 static int CECAlertCallback(void *adapter, const libcec_alert alert, const libcec_parameter CEC_CALLBACK_PARAM_TYPE data);
57 #endif
58 #if CEC_LIB_VERSION_MAJOR >= 4
59 static void CECAlertCallback(void *adapter, libcec_alert alert, libcec_parameter data);
60 #endif
61 static void CECSourceActivatedCallback(void *adapter, cec_logical_address address, uint8_t activated);
62 #endif
63 
65 {
66  public:
67 #if CEC_LIB_VERSION_MAJOR < 2
69  {
70  // libcec1 has this as a POD struct, with no
71  // automatic initialisation.
72  // And no .Clear() method...
73  memset(&m_callbacks, 0, sizeof(m_callbacks));
74  }
75 #else
76  CECAdapterPriv() = default;
77 #endif
78 
79  bool Open(void)
80  {
81  // get settings
82  // N.B. these need to be set manually since there is no UI
83  QString defaultDevice = gCoreContext->GetSetting(LIBCEC_DEVICE, "auto").trimmed();
84  // Note - if libcec supports automatic detection via EDID then
85  // these settings are not used
86  // The logical address of the HDMI device Myth is connected to
87  QString base_dev = gCoreContext->GetSetting(LIBCEC_BASE, "auto").trimmed();
88  // The number of the HDMI port Myth is connected to
89  QString hdmi_port = gCoreContext->GetSetting(LIBCEC_PORT, "auto").trimmed();
90 
91  m_powerOffTVAllowed = (bool)gCoreContext->GetNumSetting(POWEROFFTV_ALLOWED, 1);
92  m_powerOffTVOnExit = (bool)gCoreContext->GetNumSetting(POWEROFFTV_ONEXIT, 1);
93  m_powerOnTVAllowed = (bool)gCoreContext->GetNumSetting(POWERONTV_ALLOWED, 1);
94  m_powerOnTVOnStart = (bool)gCoreContext->GetNumSetting(POWERONTV_ONSTART, 1);
95 
96  // create adapter interface
97  libcec_configuration configuration;
98 #if CEC_LIB_VERSION_MAJOR < 2
99  // libcec1 has this as a POD struct, with no
100  // automatic initialisation
101  configuration.Clear();
102 #endif
103  strcpy(configuration.strDeviceName, "MythTV");
104  configuration.deviceTypes.Add(CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
105 
106  if ("auto" != base_dev)
107  {
108  int base = base_dev.toInt();
109  if (base >= 0 && base < CECDEVICE_BROADCAST) {
110  configuration.baseDevice = (cec_logical_address)base;
111  }
112  }
113  if ("auto" != hdmi_port)
114  {
115  int defaultHDMIPort = hdmi_port.toInt();
116  if (defaultHDMIPort >= CEC_MIN_HDMI_PORTNUMBER && defaultHDMIPort <= CEC_MAX_HDMI_PORTNUMBER) {
117  configuration.iHDMIPort = defaultHDMIPort;
118  }
119  }
120 
121  // Set up the callbacks
122 #if CEC_LIB_VERSION_MAJOR <= 3
123  m_callbacks.CBCecLogMessage = &CECLogMessageCallback;
124  m_callbacks.CBCecKeyPress = &CECKeyPressCallback;
125  m_callbacks.CBCecCommand = &CECCommandCallback;
126 #endif
127 #if CEC_LIB_VERSION_MAJOR >= 4
128  m_callbacks.logMessage = &CECLogMessageCallback;
129  m_callbacks.keyPress = &CECKeyPressCallback;
130  m_callbacks.commandReceived = &CECCommandCallback;
131 #endif
132 #if CEC_LIB_VERSION_MAJOR >= 2 && CEC_LIB_VERSION_MAJOR <= 3
133  m_callbacks.CBCecAlert = &CECAlertCallback;
134  m_callbacks.CBCecSourceActivated = &CECSourceActivatedCallback;
135 #endif
136 #if CEC_LIB_VERSION_MAJOR >= 4
137  m_callbacks.alert = &CECAlertCallback;
138  m_callbacks.sourceActivated = &CECSourceActivatedCallback;
139 #endif
140  configuration.callbackParam = this;
141  configuration.callbacks = &m_callbacks;
142 
143  // and initialise
144  m_adapter = LibCecInitialise(&configuration);
145 
146  if (!m_adapter)
147  {
148  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to load libcec.");
149  return false;
150  }
151 
152  // initialise the host on which libCEC is running
153  m_adapter->InitVideoStandalone();
154 
155  // find adapters
156 #if CEC_LIB_VERSION_MAJOR >= 4
157  cec_adapter_descriptor *devices = new cec_adapter_descriptor[MAX_CEC_DEVICES];
158  uint8_t num_devices = m_adapter->DetectAdapters(devices, MAX_CEC_DEVICES, nullptr, true);
159 #else
160  cec_adapter *devices = new cec_adapter[MAX_CEC_DEVICES];
161  uint8_t num_devices = m_adapter->FindAdapters(devices, MAX_CEC_DEVICES, nullptr);
162 #endif
163  if (num_devices < 1)
164  {
165  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to find any CEC devices.");
166  return false;
167  }
168 
169  // find required device and debug
170  int devicenum = 0;
171  bool find = defaultDevice != "auto";
172 
173  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Found %1 CEC devices(s).")
174  .arg(num_devices));
175  for (uint8_t i = 0; i < num_devices; i++)
176  {
177 #if CEC_LIB_VERSION_MAJOR >= 4
178  QString comm = QString::fromLatin1(devices[i].strComName);
179  QString path = QString::fromLatin1(devices[i].strComPath);
180 #else
181  QString comm = QString::fromLatin1(devices[i].comm);
182  QString path = QString::fromLatin1(devices[i].path);
183 #endif
184  bool match = find ? (comm == defaultDevice) : (i == 0);
185  devicenum = match ? i : devicenum;
186  LOG(VB_GENERAL, LOG_INFO, LOC +
187  QString("Device %1: path '%2' com port '%3' %4").arg(i + 1)
188  .arg(path).arg(comm)
189  .arg(match ? "SELECTED" : ""));
190  }
191 
192  // open adapter
193 #if CEC_LIB_VERSION_MAJOR >= 4
194  QString comm = QString::fromLatin1(devices[devicenum].strComName);
195  QString path = QString::fromLatin1(devices[devicenum].strComPath);
196 #else
197  QString comm = QString::fromLatin1(devices[devicenum].comm);
198  QString path = QString::fromLatin1(devices[devicenum].path);
199 #endif
200  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Trying to open device %1 (%2).")
201  .arg(path).arg(comm));
202 
203 #if CEC_LIB_VERSION_MAJOR >= 4
204  if (!m_adapter->Open(devices[devicenum].strComName))
205 #else
206  if (!m_adapter->Open(devices[devicenum].comm))
207 #endif
208  {
209  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to open device.");
210  return false;
211  }
212 
213  LOG(VB_GENERAL, LOG_INFO, LOC + "Opened CEC device.");
214 
215  // turn on tv (if configured)
216  m_powerOnTV = m_powerOnTVOnStart;
217 
218  // switch input (if configured)
219  m_switchInput = true;
220 
221  // all good to go
222  m_valid = true;
223 
224  return true;
225  }
226 
227  void Close(void)
228  {
229  if (m_adapter)
230  {
231  // turn off tv (if configured)
232  m_powerOffTV = m_powerOffTVOnExit;
233  HandleActions();
234 
235  // delete adapter
236  m_adapter->Close();
237  UnloadLibCec(m_adapter);
238 
239  LOG(VB_GENERAL, LOG_INFO, LOC + "Closing down CEC.");
240  }
241  m_valid = false;
242  m_adapter = nullptr;
243  }
244 
245  int LogMessage(const cec_log_message &message)
246  {
247  QString msg(message.message);
248  LogLevel_t lvl = LOG_UNKNOWN;
249  switch (message.level)
250  {
251  case CEC_LOG_ERROR: lvl = LOG_ERR; break;
252  case CEC_LOG_WARNING: lvl = LOG_WARNING; break;
253  case CEC_LOG_NOTICE: lvl = LOG_INFO; break;
254  case CEC_LOG_DEBUG: lvl = LOG_DEBUG; break;
255  default: break;
256  }
257  LOG(VB_GENERAL, lvl, LOC + QString("%1").arg(msg));
258  return 1;
259  }
260 
261  void LogMessage(const cec_log_message* message)
262  {
263  QString msg(message->message);
264  LogLevel_t lvl = LOG_UNKNOWN;
265  switch (message->level)
266  {
267  case CEC_LOG_ERROR: lvl = LOG_ERR; break;
268  case CEC_LOG_WARNING: lvl = LOG_WARNING; break;
269  case CEC_LOG_NOTICE: lvl = LOG_INFO; break;
270  case CEC_LOG_DEBUG: lvl = LOG_DEBUG; break;
271  default: break;
272  }
273  LOG(VB_GENERAL, lvl, LOC + QString("%1").arg(msg));
274  }
275 
276  // NOTE - libcec2 changes the callbacks
277  // to be pass-by-value.
278  // For simplicity, this function remains as pass-by-ref
279  int HandleCommand(const cec_command &command)
280  {
281  if (!m_adapter || !m_valid)
282  return 0;
283 
284  LOG(VB_GENERAL, LOG_DEBUG, LOC +
285  QString("Command %1 from '%2' (%3) - destination '%4' (%5)")
286  .arg(command.opcode)
287  .arg(m_adapter->ToString(command.initiator))
288  .arg(command.initiator)
289  .arg(m_adapter->ToString(command.destination))
290  .arg(command.destination));
291 
292  switch (command.opcode)
293  {
294  case CEC_OPCODE_MENU_REQUEST:
295  cec_keypress key;
296  key.keycode = CEC_USER_CONTROL_CODE_ROOT_MENU;
297  key.duration = 5;
298  HandleKeyPress(key);
299  // SetMenuState could be used to disable the TV menu here
300  // That may be a user-unfriendly thing to do
301  // So they have to see both the TV menu and the MythTV menu
302  break;
303 
304  default:
305  break;
306  }
307  gCoreContext->SendSystemEvent(QString("CEC_COMMAND_RECEIVED COMMAND %1")
308  .arg(command.opcode));
309 
310  return 1;
311  }
312 
313  void HandleCommand(const cec_command* command)
314  {
315  if (!m_adapter || !m_valid)
316  return;
317 
318  LOG(VB_GENERAL, LOG_DEBUG, LOC +
319  QString("Command %1 from '%2' (%3) - destination '%4' (%5)")
320  .arg(command->opcode)
321  .arg(m_adapter->ToString(command->initiator))
322  .arg(command->initiator)
323  .arg(m_adapter->ToString(command->destination))
324  .arg(command->destination));
325 
326  switch (command->opcode)
327  {
328  // TODO
329  default:
330  break;
331  }
332  gCoreContext->SendSystemEvent(QString("CEC_COMMAND_RECEIVED COMMAND %1")
333  .arg(command->opcode));
334  }
335 
336  int HandleKeyPress(const cec_keypress &key)
337  {
338  if (!m_adapter || !m_valid)
339  return 0;
340 
341  // Ignore key down events and wait for the key 'up'
342  if (key.duration < 1)
343  return 1;
344 
345  QString code;
346  int action = 0;
347  Qt::KeyboardModifier modifier = Qt::NoModifier;
348  switch (key.keycode)
349  {
350  case CEC_USER_CONTROL_CODE_NUMBER0:
351  action = Qt::Key_0;
352  code = "0";
353  break;
354  case CEC_USER_CONTROL_CODE_NUMBER1:
355  action = Qt::Key_1;
356  code = "1";
357  break;
358  case CEC_USER_CONTROL_CODE_NUMBER2:
359  action = Qt::Key_2;
360  code = "2";
361  break;
362  case CEC_USER_CONTROL_CODE_NUMBER3:
363  action = Qt::Key_3;
364  code = "3";
365  break;
366  case CEC_USER_CONTROL_CODE_NUMBER4:
367  action = Qt::Key_4;
368  code = "4";
369  break;
370  case CEC_USER_CONTROL_CODE_NUMBER5:
371  action = Qt::Key_5;
372  code = "5";
373  break;
374  case CEC_USER_CONTROL_CODE_NUMBER6:
375  action = Qt::Key_6;
376  code = "6";
377  break;
378  case CEC_USER_CONTROL_CODE_NUMBER7:
379  action = Qt::Key_7;
380  code = "7";
381  break;
382  case CEC_USER_CONTROL_CODE_NUMBER8:
383  action = Qt::Key_8;
384  code = "8";
385  break;
386  case CEC_USER_CONTROL_CODE_NUMBER9:
387  action = Qt::Key_9;
388  code = "9";
389  break;
390  case CEC_USER_CONTROL_CODE_SELECT:
391  action = Qt::Key_Select;
392  code = "SELECT";
393  break;
394  case CEC_USER_CONTROL_CODE_ENTER:
395  action = Qt::Key_Enter;
396  code = "ENTER";
397  break;
398  case CEC_USER_CONTROL_CODE_UP:
399  action = Qt::Key_Up;
400  code = "UP";
401  break;
402  case CEC_USER_CONTROL_CODE_DOWN:
403  action = Qt::Key_Down;
404  code = "DOWN";
405  break;
406  case CEC_USER_CONTROL_CODE_LEFT:
407  action = Qt::Key_Left;
408  code = "LEFT";
409  break;
410  case CEC_USER_CONTROL_CODE_LEFT_UP:
411  action = Qt::Key_Left;
412  code = "LEFT_UP";
413  break;
414  case CEC_USER_CONTROL_CODE_LEFT_DOWN:
415  action = Qt::Key_Left;
416  code = "LEFT_DOWN";
417  break;
418  case CEC_USER_CONTROL_CODE_RIGHT:
419  action = Qt::Key_Right;
420  code = "RIGHT";
421  break;
422  case CEC_USER_CONTROL_CODE_RIGHT_UP:
423  action = Qt::Key_Right;
424  code = "RIGHT_UP";
425  break;
426  case CEC_USER_CONTROL_CODE_RIGHT_DOWN:
427  action = Qt::Key_Right;
428  code = "RIGHT_DOWN";
429  break;
430  case CEC_USER_CONTROL_CODE_ROOT_MENU:
431  action = Qt::Key_M;
432  code = "ROOT_MENU";
433  break;
434  case CEC_USER_CONTROL_CODE_EXIT:
435  action = Qt::Key_Escape;
436  code = "EXIT";
437  break;
438  case CEC_USER_CONTROL_CODE_PREVIOUS_CHANNEL:
439  action = Qt::Key_H;
440  code = "PREVIOUS_CHANNEL";
441  break;
442  case CEC_USER_CONTROL_CODE_SOUND_SELECT:
443  action = Qt::Key_Plus;
444  code = "SOUND_SELECT";
445  break;
446  case CEC_USER_CONTROL_CODE_VOLUME_UP:
447  action = Qt::Key_VolumeUp;
448  code = "VOLUME_UP";
449  break;
450  case CEC_USER_CONTROL_CODE_VOLUME_DOWN:
451  action = Qt::Key_VolumeDown;
452  code = "VOLUME_DOWN";
453  break;
454  case CEC_USER_CONTROL_CODE_MUTE:
455  action = Qt::Key_VolumeMute;
456  code = "MUTE";
457  break;
458  case CEC_USER_CONTROL_CODE_PLAY:
459  action = Qt::Key_P;
460  code = "PLAY";
461  // Play set to control-p to differentiate from pause
462  modifier = Qt::ControlModifier;
463  break;
464  case CEC_USER_CONTROL_CODE_PAUSE:
465  action = Qt::Key_P;
466  code = "PAUSE";
467  break;
468  case CEC_USER_CONTROL_CODE_STOP:
469  action = Qt::Key_Stop;
470  code = "STOP";
471  break;
472  case CEC_USER_CONTROL_CODE_RECORD:
473  action = Qt::Key_R;
474  code = "RECORD";
475  break;
476  case CEC_USER_CONTROL_CODE_CLEAR:
477  action = Qt::Key_Clear;
478  code = "CLEAR";
479  break;
480  case CEC_USER_CONTROL_CODE_DISPLAY_INFORMATION:
481  action = Qt::Key_I;
482  code = "DISPLAY_INFORMATION";
483  break;
484  case CEC_USER_CONTROL_CODE_PAGE_UP:
485  action = Qt::Key_PageUp;
486  code = "PAGE_UP";
487  break;
488  case CEC_USER_CONTROL_CODE_PAGE_DOWN:
489  action = Qt::Key_PageDown;
490  code = "PAGE_DOWN";
491  break;
492  case CEC_USER_CONTROL_CODE_EJECT:
493  action = Qt::Key_Eject;
494  code = "EJECT";
495  break;
496  case CEC_USER_CONTROL_CODE_FORWARD:
497  action = Qt::Key_Forward;
498  code = "FORWARD";
499  break;
500  case CEC_USER_CONTROL_CODE_BACKWARD:
501  action = Qt::Key_Back;
502  code = "BACKWARD";
503  break;
504  case CEC_USER_CONTROL_CODE_F1_BLUE:
505  action = Qt::Key_F5; // NB F1 is help and we normally map blue to F5
506  code = "F1_BLUE";
507  break;
508  case CEC_USER_CONTROL_CODE_F2_RED:
509  action = Qt::Key_F2;
510  code = "F2_RED";
511  break;
512  case CEC_USER_CONTROL_CODE_F3_GREEN:
513  action = Qt::Key_F3;
514  code = "F3_GREEN";
515  break;
516  case CEC_USER_CONTROL_CODE_F4_YELLOW:
517  action = Qt::Key_F4;
518  code = "F4_YELLOW";
519  break;
520  case CEC_USER_CONTROL_CODE_SETUP_MENU:
521  action = Qt::Key_M; // Duplicate of Root Menu
522  code = "SETUP_MENU";
523  break;
524  case CEC_USER_CONTROL_CODE_CONTENTS_MENU:
525  action = Qt::Key_M; // Duplicate of Root Menu
526  code = "CONTENTS_MENU";
527  break;
528  case CEC_USER_CONTROL_CODE_FAVORITE_MENU:
529  action = Qt::Key_M; // Duplicate of Root Menu
530  code = "FAVORITE_MENU";
531  break;
532  case CEC_USER_CONTROL_CODE_DOT:
533  action = Qt::Key_Period;
534  code = "DOT";
535  break;
536  case CEC_USER_CONTROL_CODE_NEXT_FAVORITE:
537  action = Qt::Key_Slash;
538  code = "NEXT_FAVORITE";
539  break;
540  case CEC_USER_CONTROL_CODE_INPUT_SELECT:
541  action = Qt::Key_C;
542  code = "INPUT_SELECT";
543  break;
544  case CEC_USER_CONTROL_CODE_HELP:
545  action = Qt::Key_F1;
546  code = "HELP";
547  break;
548  case CEC_USER_CONTROL_CODE_STOP_RECORD:
549  action = Qt::Key_R; // Duplicate of Record
550  code = "STOP_RECORD";
551  break;
552  case CEC_USER_CONTROL_CODE_SUB_PICTURE:
553  action = Qt::Key_V;
554  code = "SUB_PICTURE";
555  break;
556  case CEC_USER_CONTROL_CODE_ELECTRONIC_PROGRAM_GUIDE:
557  action = Qt::Key_S;
558  code = "ELECTRONIC_PROGRAM_GUIDE";
559  break;
560  case CEC_USER_CONTROL_CODE_POWER:
561  action = Qt::Key_PowerOff;
562  code = "POWER";
563  break;
564 
565  // these codes have 'non-standard' Qt key mappings to ensure
566  // each code has a unique key mapping
567  case CEC_USER_CONTROL_CODE_CHANNEL_DOWN:
568  action = Qt::Key_F20; // to differentiate from Up
569  code = "CHANNEL_DOWN";
570  break;
571  case CEC_USER_CONTROL_CODE_CHANNEL_UP:
572  action = Qt::Key_F21; // to differentiate from Down
573  code = "CHANNEL_UP";
574  break;
575  case CEC_USER_CONTROL_CODE_REWIND:
576  action = Qt::Key_F22; // to differentiate from Left
577  code = "REWIND";
578  break;
579  case CEC_USER_CONTROL_CODE_FAST_FORWARD:
580  action = Qt::Key_F23; // to differentiate from Right
581  code = "FAST_FORWARD";
582  break;
583  case CEC_USER_CONTROL_CODE_ANGLE:
584  action = Qt::Key_F24;
585  code = "ANGLE";
586  break;
587  case CEC_USER_CONTROL_CODE_F5:
588  action = Qt::Key_F6; // NB!
589  code = "F5";
590  break;
591 
592  // codes with no obvious MythTV action
593  case CEC_USER_CONTROL_CODE_INITIAL_CONFIGURATION:
594  code = "INITIAL_CONFIGURATION";
595  break;
596  case CEC_USER_CONTROL_CODE_PAUSE_RECORD:
597  code = "PAUSE_RECORD";
598  break;
599  case CEC_USER_CONTROL_CODE_VIDEO_ON_DEMAND:
600  code = "VIDEO_ON_DEMAND";
601  break;
602  case CEC_USER_CONTROL_CODE_TIMER_PROGRAMMING:
603  code = "TIMER_PROGRAMMING";
604  break;
605  case CEC_USER_CONTROL_CODE_UNKNOWN:
606  code = "UNKNOWN";
607  break;
608  case CEC_USER_CONTROL_CODE_DATA:
609  code = "DATA";
610  break;
611 
612  // Functions aren't implemented (similar to macros?)
613  case CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION:
614  code = "POWER_ON_FUNCTION";
615  break;
616  case CEC_USER_CONTROL_CODE_PLAY_FUNCTION:
617  code = "PLAY_FUNCTION";
618  break;
619  case CEC_USER_CONTROL_CODE_PAUSE_PLAY_FUNCTION:
620  code = "PAUSE_PLAY_FUNCTION";
621  break;
622  case CEC_USER_CONTROL_CODE_RECORD_FUNCTION:
623  code = "RECORD_FUNCTION";
624  break;
625  case CEC_USER_CONTROL_CODE_PAUSE_RECORD_FUNCTION:
626  code = "PAUSE_RECORD_FUNCTION";
627  break;
628  case CEC_USER_CONTROL_CODE_STOP_FUNCTION:
629  code = "STOP_FUNCTION";
630  break;
631  case CEC_USER_CONTROL_CODE_MUTE_FUNCTION:
632  code = "MUTE_FUNCTION";
633  break;
634  case CEC_USER_CONTROL_CODE_RESTORE_VOLUME_FUNCTION:
635  code = "RESTORE_VOLUME_FUNCTION";
636  break;
637  case CEC_USER_CONTROL_CODE_TUNE_FUNCTION:
638  code = "TUNE_FUNCTION";
639  break;
640  case CEC_USER_CONTROL_CODE_SELECT_MEDIA_FUNCTION:
641  code = "SELECT_MEDIA_FUNCTION";
642  break;
643  case CEC_USER_CONTROL_CODE_SELECT_AV_INPUT_FUNCTION:
644  code = "SELECT_AV_INPUT_FUNCTION";
645  break;
646  case CEC_USER_CONTROL_CODE_SELECT_AUDIO_INPUT_FUNCTION:
647  code = "SELECT_AUDIO_INPUT_FUNCTION";
648  break;
649  case CEC_USER_CONTROL_CODE_POWER_TOGGLE_FUNCTION:
650  code = "POWER_TOGGLE_FUNCTION";
651  break;
652  case CEC_USER_CONTROL_CODE_POWER_OFF_FUNCTION:
653  code = "POWER_OFF_FUNCTION";
654  break;
655 
656  default:
657  code = QString("UNKNOWN_FUNCTION_%1").arg(key.keycode);
658  break;
659  }
660 
661  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Keypress %1 %2")
662  .arg(code).arg(0 == action ? "(Not actioned)" : ""));
663 
664  if (0 == action)
665  return 1;
666 
668  QKeyEvent* ke = new QKeyEvent(QEvent::KeyPress, action, modifier);
669  qApp->postEvent(GetMythMainWindow(), (QEvent*)ke);
670 
671  return 1;
672  }
673 
674  void HandleKeyPress(const cec_keypress* key)
675  {
676  if (!m_adapter || !m_valid)
677  return;
678 
679  // Ignore key down events and wait for the key 'up'
680  if (key->duration < 1)
681  return;
682 
683  QString code;
684  int action = 0;
685  switch (key->keycode)
686  {
687  case CEC_USER_CONTROL_CODE_NUMBER0:
688  action = Qt::Key_0;
689  code = "0";
690  break;
691  case CEC_USER_CONTROL_CODE_NUMBER1:
692  action = Qt::Key_1;
693  code = "1";
694  break;
695  case CEC_USER_CONTROL_CODE_NUMBER2:
696  action = Qt::Key_2;
697  code = "2";
698  break;
699  case CEC_USER_CONTROL_CODE_NUMBER3:
700  action = Qt::Key_3;
701  code = "3";
702  break;
703  case CEC_USER_CONTROL_CODE_NUMBER4:
704  action = Qt::Key_4;
705  code = "4";
706  break;
707  case CEC_USER_CONTROL_CODE_NUMBER5:
708  action = Qt::Key_5;
709  code = "5";
710  break;
711  case CEC_USER_CONTROL_CODE_NUMBER6:
712  action = Qt::Key_6;
713  code = "6";
714  break;
715  case CEC_USER_CONTROL_CODE_NUMBER7:
716  action = Qt::Key_7;
717  code = "7";
718  break;
719  case CEC_USER_CONTROL_CODE_NUMBER8:
720  action = Qt::Key_8;
721  code = "8";
722  break;
723  case CEC_USER_CONTROL_CODE_NUMBER9:
724  action = Qt::Key_9;
725  code = "9";
726  break;
727  case CEC_USER_CONTROL_CODE_SELECT:
728  action = Qt::Key_Select;
729  code = "SELECT";
730  break;
731  case CEC_USER_CONTROL_CODE_ENTER:
732  action = Qt::Key_Enter;
733  code = "ENTER";
734  break;
735  case CEC_USER_CONTROL_CODE_UP:
736  action = Qt::Key_Up;
737  code = "UP";
738  break;
739  case CEC_USER_CONTROL_CODE_DOWN:
740  action = Qt::Key_Down;
741  code = "DOWN";
742  break;
743  case CEC_USER_CONTROL_CODE_LEFT:
744  action = Qt::Key_Left;
745  code = "LEFT";
746  break;
747  case CEC_USER_CONTROL_CODE_LEFT_UP:
748  action = Qt::Key_Left;
749  code = "LEFT_UP";
750  break;
751  case CEC_USER_CONTROL_CODE_LEFT_DOWN:
752  action = Qt::Key_Left;
753  code = "LEFT_DOWN";
754  break;
755  case CEC_USER_CONTROL_CODE_RIGHT:
756  action = Qt::Key_Right;
757  code = "RIGHT";
758  break;
759  case CEC_USER_CONTROL_CODE_RIGHT_UP:
760  action = Qt::Key_Right;
761  code = "RIGHT_UP";
762  break;
763  case CEC_USER_CONTROL_CODE_RIGHT_DOWN:
764  action = Qt::Key_Right;
765  code = "RIGHT_DOWN";
766  break;
767  case CEC_USER_CONTROL_CODE_ROOT_MENU:
768  action = Qt::Key_M;
769  code = "ROOT_MENU";
770  break;
771  case CEC_USER_CONTROL_CODE_EXIT:
772  action = Qt::Key_Escape;
773  code = "EXIT";
774  break;
775  case CEC_USER_CONTROL_CODE_PREVIOUS_CHANNEL:
776  action = Qt::Key_H;
777  code = "PREVIOUS_CHANNEL";
778  break;
779  case CEC_USER_CONTROL_CODE_SOUND_SELECT:
780  action = Qt::Key_Plus;
781  code = "SOUND_SELECT";
782  break;
783  case CEC_USER_CONTROL_CODE_VOLUME_UP:
784  action = Qt::Key_VolumeUp;
785  code = "VOLUME_UP";
786  break;
787  case CEC_USER_CONTROL_CODE_VOLUME_DOWN:
788  action = Qt::Key_VolumeDown;
789  code = "VOLUME_DOWN";
790  break;
791  case CEC_USER_CONTROL_CODE_MUTE:
792  action = Qt::Key_VolumeMute;
793  code = "MUTE";
794  break;
795  case CEC_USER_CONTROL_CODE_PLAY:
796  action = Qt::Key_P;
797  code = "PLAY";
798  break;
799  case CEC_USER_CONTROL_CODE_PAUSE:
800  action = Qt::Key_P; // same as play
801  code = "PAUSE";
802  break;
803  case CEC_USER_CONTROL_CODE_STOP:
804  action = Qt::Key_Stop;
805  code = "STOP";
806  break;
807  case CEC_USER_CONTROL_CODE_RECORD:
808  action = Qt::Key_R;
809  code = "RECORD";
810  break;
811  case CEC_USER_CONTROL_CODE_CLEAR:
812  action = Qt::Key_Clear;
813  code = "CLEAR";
814  break;
815  case CEC_USER_CONTROL_CODE_DISPLAY_INFORMATION:
816  action = Qt::Key_I;
817  code = "DISPLAY_INFORMATION";
818  break;
819  case CEC_USER_CONTROL_CODE_PAGE_UP:
820  action = Qt::Key_PageUp;
821  code = "PAGE_UP";
822  break;
823  case CEC_USER_CONTROL_CODE_PAGE_DOWN:
824  action = Qt::Key_PageDown;
825  code = "PAGE_DOWN";
826  break;
827  case CEC_USER_CONTROL_CODE_EJECT:
828  action = Qt::Key_Eject;
829  code = "EJECT";
830  break;
831  case CEC_USER_CONTROL_CODE_FORWARD:
832  action = Qt::Key_Forward;
833  code = "FORWARD";
834  break;
835  case CEC_USER_CONTROL_CODE_BACKWARD:
836  action = Qt::Key_Back;
837  code = "BACKWARD";
838  break;
839  case CEC_USER_CONTROL_CODE_F1_BLUE:
840  action = Qt::Key_F5; // NB F1 is help and we normally map blue to F5
841  code = "F1_BLUE";
842  break;
843  case CEC_USER_CONTROL_CODE_F2_RED:
844  action = Qt::Key_F2;
845  code = "F2_RED";
846  break;
847  case CEC_USER_CONTROL_CODE_F3_GREEN:
848  action = Qt::Key_F3;
849  code = "F3_GREEN";
850  break;
851  case CEC_USER_CONTROL_CODE_F4_YELLOW:
852  action = Qt::Key_F4;
853  code = "F4_YELLOW";
854  break;
855  case CEC_USER_CONTROL_CODE_SETUP_MENU:
856  action = Qt::Key_M; // Duplicate of Root Menu
857  code = "SETUP_MENU";
858  break;
859  case CEC_USER_CONTROL_CODE_CONTENTS_MENU:
860  action = Qt::Key_M; // Duplicate of Root Menu
861  code = "CONTENTS_MENU";
862  break;
863  case CEC_USER_CONTROL_CODE_FAVORITE_MENU:
864  action = Qt::Key_M; // Duplicate of Root Menu
865  code = "FAVORITE_MENU";
866  break;
867  case CEC_USER_CONTROL_CODE_DOT:
868  action = Qt::Key_Period;
869  code = "DOT";
870  break;
871  case CEC_USER_CONTROL_CODE_NEXT_FAVORITE:
872  action = Qt::Key_Slash;
873  code = "NEXT_FAVORITE";
874  break;
875  case CEC_USER_CONTROL_CODE_INPUT_SELECT:
876  action = Qt::Key_C;
877  code = "INPUT_SELECT";
878  break;
879  case CEC_USER_CONTROL_CODE_HELP:
880  action = Qt::Key_F1;
881  code = "HELP";
882  break;
883  case CEC_USER_CONTROL_CODE_STOP_RECORD:
884  action = Qt::Key_R; // Duplicate of Record
885  code = "STOP_RECORD";
886  break;
887  case CEC_USER_CONTROL_CODE_SUB_PICTURE:
888  action = Qt::Key_V;
889  code = "SUB_PICTURE";
890  break;
891  case CEC_USER_CONTROL_CODE_ELECTRONIC_PROGRAM_GUIDE:
892  action = Qt::Key_S;
893  code = "ELECTRONIC_PROGRAM_GUIDE";
894  break;
895  case CEC_USER_CONTROL_CODE_POWER:
896  action = Qt::Key_PowerOff;
897  code = "POWER";
898  break;
899 
900  // these codes have 'non-standard' Qt key mappings to ensure
901  // each code has a unique key mapping
902  case CEC_USER_CONTROL_CODE_CHANNEL_DOWN:
903  action = Qt::Key_F20; // to differentiate from Up
904  code = "CHANNEL_DOWN";
905  break;
906  case CEC_USER_CONTROL_CODE_CHANNEL_UP:
907  action = Qt::Key_F21; // to differentiate from Down
908  code = "CHANNEL_UP";
909  break;
910  case CEC_USER_CONTROL_CODE_REWIND:
911  action = Qt::Key_F22; // to differentiate from Left
912  code = "REWIND";
913  break;
914  case CEC_USER_CONTROL_CODE_FAST_FORWARD:
915  action = Qt::Key_F23; // to differentiate from Right
916  code = "FAST_FORWARD";
917  break;
918  case CEC_USER_CONTROL_CODE_ANGLE:
919  action = Qt::Key_F24;
920  code = "ANGLE";
921  break;
922  case CEC_USER_CONTROL_CODE_F5:
923  action = Qt::Key_F6; // NB!
924  code = "F5";
925  break;
926 
927  // codes with no obvious MythTV action
928  case CEC_USER_CONTROL_CODE_INITIAL_CONFIGURATION:
929  code = "INITIAL_CONFIGURATION";
930  break;
931  case CEC_USER_CONTROL_CODE_PAUSE_RECORD:
932  code = "PAUSE_RECORD";
933  break;
934  case CEC_USER_CONTROL_CODE_VIDEO_ON_DEMAND:
935  code = "VIDEO_ON_DEMAND";
936  break;
937  case CEC_USER_CONTROL_CODE_TIMER_PROGRAMMING:
938  code = "TIMER_PROGRAMMING";
939  break;
940  case CEC_USER_CONTROL_CODE_UNKNOWN:
941  code = "UNKNOWN";
942  break;
943  case CEC_USER_CONTROL_CODE_DATA:
944  code = "DATA";
945  break;
946 
947  // Functions aren't implemented (similar to macros?)
948  case CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION:
949  code = "POWER_ON_FUNCTION";
950  break;
951  case CEC_USER_CONTROL_CODE_PLAY_FUNCTION:
952  code = "PLAY_FUNCTION";
953  break;
954  case CEC_USER_CONTROL_CODE_PAUSE_PLAY_FUNCTION:
955  code = "PAUSE_PLAY_FUNCTION";
956  break;
957  case CEC_USER_CONTROL_CODE_RECORD_FUNCTION:
958  code = "RECORD_FUNCTION";
959  break;
960  case CEC_USER_CONTROL_CODE_PAUSE_RECORD_FUNCTION:
961  code = "PAUSE_RECORD_FUNCTION";
962  break;
963  case CEC_USER_CONTROL_CODE_STOP_FUNCTION:
964  code = "STOP_FUNCTION";
965  break;
966  case CEC_USER_CONTROL_CODE_MUTE_FUNCTION:
967  code = "MUTE_FUNCTION";
968  break;
969  case CEC_USER_CONTROL_CODE_RESTORE_VOLUME_FUNCTION:
970  code = "RESTORE_VOLUME_FUNCTION";
971  break;
972  case CEC_USER_CONTROL_CODE_TUNE_FUNCTION:
973  code = "TUNE_FUNCTION";
974  break;
975  case CEC_USER_CONTROL_CODE_SELECT_MEDIA_FUNCTION:
976  code = "SELECT_MEDIA_FUNCTION";
977  break;
978  case CEC_USER_CONTROL_CODE_SELECT_AV_INPUT_FUNCTION:
979  code = "SELECT_AV_INPUT_FUNCTION";
980  break;
981  case CEC_USER_CONTROL_CODE_SELECT_AUDIO_INPUT_FUNCTION:
982  code = "SELECT_AUDIO_INPUT_FUNCTION";
983  break;
984  case CEC_USER_CONTROL_CODE_POWER_TOGGLE_FUNCTION:
985  code = "POWER_TOGGLE_FUNCTION";
986  break;
987  case CEC_USER_CONTROL_CODE_POWER_OFF_FUNCTION:
988  code = "POWER_OFF_FUNCTION";
989  break;
990 
991  default:
992  code = QString("UNKNOWN_FUNCTION_%1").arg(key->keycode);
993  break;
994  }
995 
996  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Keypress %1 %2")
997  .arg(code).arg(0 == action ? "(Not actioned)" : ""));
998 
999  if (0 == action)
1000  return;
1001 
1003  QKeyEvent* ke = new QKeyEvent(QEvent::KeyPress, action, Qt::NoModifier);
1004  qApp->postEvent(GetMythMainWindow(), (QEvent*)ke);
1005  }
1006 
1007 #if CEC_LIB_VERSION_MAJOR >= 2
1008  int HandleAlert(const libcec_alert alert, const libcec_parameter &data)
1009  {
1010  // These aren't handled yet
1011  // Note that we *DON'T* want to just show these
1012  // to the user in a popup, because some (eg prompting about firmware
1013  // upgrades) aren't appropriate.
1014  // Ideally we'd try to handle this, eg by reopening the adapter
1015  // in a separate thread if it lost the connection....
1016 
1017  QString param;
1018  switch (data.paramType)
1019  {
1020  case CEC_PARAMETER_TYPE_STRING:
1021  param = QString(": %1").arg((char*)data.paramData);
1022  break;
1023  case CEC_PARAMETER_TYPE_UNKOWN: /* libcec typo */
1024  default:
1025  if (data.paramData != nullptr)
1026  {
1027  param = QString(": UNKNOWN param has type %1").arg(data.paramType);
1028  }
1029  break;
1030  }
1031 
1032  // There is no ToString method for libcec_alert...
1033  // Plus libcec adds new values in minor releases (eg 2.1.1)
1034  // but doesn't provide a #define for the last digit...
1035  // Besides, it makes sense to do this, since we could be compiling
1036  // against an older version than we're running against
1037 #if CEC_LIB_VERSION_MAJOR == 2 && CEC_LIB_VERSION_MINOR < 1
1038 // since 2.0.4
1039 #define CEC_ALERT_PHYSICAL_ADDRESS_ERROR 4
1040 #endif
1041 #if CEC_LIB_VERSION_MAJOR == 2 && CEC_LIB_VERSION_MINOR < 2
1042 // since 2.1.1
1043 #define CEC_ALERT_TV_POLL_FAILED 5
1044 #endif
1045  switch (alert)
1046  {
1047  case CEC_ALERT_SERVICE_DEVICE:
1048  LOG(VB_GENERAL, LOG_INFO, LOC + QString("CEC device service message") + param);
1049  break;
1050  case CEC_ALERT_CONNECTION_LOST:
1051  LOG(VB_GENERAL, LOG_ERR, LOC + QString("CEC device connection list") + param);
1052  break;
1053  case CEC_ALERT_PERMISSION_ERROR:
1054  case CEC_ALERT_PORT_BUSY:
1055  /* Don't log due to possible false positives on the initial
1056  * open. libcec will log via the logging callback anyway
1057  */
1058  break;
1059  case CEC_ALERT_PHYSICAL_ADDRESS_ERROR:
1060  LOG(VB_GENERAL, LOG_ERR, LOC + QString("CEC physical address error") + param);
1061  break;
1062  case CEC_ALERT_TV_POLL_FAILED:
1063  LOG(VB_GENERAL, LOG_WARNING, LOC + QString("CEC device can't poll TV") + param);
1064  break;
1065  default:
1066  LOG(VB_GENERAL, LOG_WARNING, LOC + QString("UNKNOWN CEC device alert %1").arg(alert) + param);
1067  break;
1068  }
1069 
1070  return 1;
1071  }
1072 
1073  void HandleSourceActivated(const cec_logical_address address, const uint8_t activated)
1074  {
1075  if (!m_adapter || !m_valid)
1076  return;
1077 
1078  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Source %1 %2")
1079  .arg(m_adapter->ToString(address))
1080  .arg(activated ? "Activated" : "Deactivated"));
1081 
1082  if (activated)
1084  }
1085 #endif
1086 
1087  void HandleActions(void)
1088  {
1089  if (!m_adapter || !m_valid)
1090  return;
1091 
1092  // power state
1093  if (m_powerOffTV && m_powerOffTVAllowed)
1094  {
1095  if (m_adapter->StandbyDevices(CECDEVICE_TV))
1096  LOG(VB_GENERAL, LOG_INFO, LOC + "Asked TV to turn off.");
1097  else
1098  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to turn TV off.");
1099  }
1100 
1101  if (m_powerOnTV && m_powerOnTVAllowed)
1102  {
1103  if (m_adapter->PowerOnDevices(CECDEVICE_TV))
1104  LOG(VB_GENERAL, LOG_INFO, LOC + "Asked TV to turn on.");
1105  else
1106  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to turn TV on.");
1107  }
1108 
1109  // HDMI input
1110  if (m_switchInput && m_switchInputAllowed)
1111  {
1112  if (m_adapter->SetActiveSource())
1113  LOG(VB_GENERAL, LOG_INFO, LOC + "Asked TV to switch to this input.");
1114  else
1115  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to switch to this input.");
1116  }
1117 
1118  m_powerOffTV = false;
1119  m_powerOnTV = false;
1120  m_switchInput = false;
1121  }
1122 
1123  ICECAdapter *m_adapter {nullptr};
1124  ICECCallbacks m_callbacks;
1125  bool m_valid {false};
1126  bool m_powerOffTV {false};
1127  bool m_powerOffTVAllowed {false};
1128  bool m_powerOffTVOnExit {false};
1129  bool m_powerOnTV {false};
1130  bool m_powerOnTVAllowed {false};
1131  bool m_powerOnTVOnStart {false};
1132  bool m_switchInput {false};
1133  bool m_switchInputAllowed {true};
1134 };
1135 
1136 CECAdapter::CECAdapter() : MThread("CECAdapter"), m_priv(new CECAdapterPriv)
1137 {
1138  QMutexLocker lock(gLock);
1139 
1140  // don't try if disabled
1142  {
1143  LOG(VB_GENERAL, LOG_INFO, LOC + "libCEC support is disabled.");
1144  return;
1145  }
1146 
1147  // create the actual adapter
1148  if (!m_priv->Open())
1149  return;
1150 
1151  // start thread
1152  LOG(VB_GENERAL, LOG_DEBUG, LOC + "Starting thread.");
1153  start();
1154 }
1155 
1157 {
1158  RunProlog();
1159  // Handle any actions at startup
1160  // This is done outside the lock to handle initial setup -
1161  // we know that nothing else can be calling us this early.
1162  m_priv->HandleActions();
1163 
1164  while (IsValid()) {
1165  // Note that a lock is used because the QWaitCondition needs it
1166  // None of the other HandleActions callers need the lock because
1167  // they call HandleActions at open/close time, when
1168  // nothing else can be calling it....
1169  gHandleActionsLock->lock();
1171  if (IsValid()) {
1172  m_priv->HandleActions();
1173  }
1174  gHandleActionsLock->unlock();
1175  }
1176  RunEpilog();
1177 }
1178 
1180 {
1181  QMutexLocker lock(gLock);
1182 
1183  // delete the actual adapter
1184  m_priv->Close();
1185  // Free the thread
1186  gActionsReady->wakeAll();
1187  // Wait for it to end
1188  wait();
1189 }
1190 
1192 {
1193  return m_priv->m_valid;
1194 }
1195 
1196 void CECAdapter::Action(const QString &action)
1197 {
1198  QMutexLocker lock(gLock);
1199  if (ACTION_TVPOWERON == action)
1200  m_priv->m_powerOnTV = true;
1201  else if (ACTION_TVPOWEROFF == action)
1202  m_priv->m_powerOffTV = true;
1203  gActionsReady->wakeAll();
1204 }
1205 
1206 #if CEC_LIB_VERSION_MAJOR <= 3
1207 // cppcheck-suppress passedByValue
1208 static int CECLogMessageCallback(void *adapter, const cec_log_message CEC_CALLBACK_PARAM_TYPE message)
1209 {
1210  return ((CECAdapterPriv*)adapter)->LogMessage(message);
1211 }
1212 
1213 // cppcheck-suppress passedByValue
1214 static int CECKeyPressCallback(void *adapter, const cec_keypress CEC_CALLBACK_PARAM_TYPE keypress)
1215 {
1216  return ((CECAdapterPriv*)adapter)->HandleKeyPress(keypress);
1217 }
1218 
1219 // cppcheck-suppress passedByValue
1220 static int CECCommandCallback(void *adapter, const cec_command CEC_CALLBACK_PARAM_TYPE command)
1221 {
1222  return ((CECAdapterPriv*)adapter)->HandleCommand(command);
1223 }
1224 #endif
1225 #if CEC_LIB_VERSION_MAJOR >= 4
1226 static void CECLogMessageCallback(void *adapter, const cec_log_message* message)
1227 {
1228  ((CECAdapterPriv*)adapter)->LogMessage(message);
1229 }
1230 
1231 static void CECKeyPressCallback(void *adapter, const cec_keypress* keypress)
1232 {
1233  ((CECAdapterPriv*)adapter)->HandleKeyPress(keypress);
1234 }
1235 
1236 static void CECCommandCallback(void *adapter, const cec_command* command)
1237 {
1238  ((CECAdapterPriv*)adapter)->HandleCommand(command);
1239 }
1240 #endif
1241 
1242 #if CEC_LIB_VERSION_MAJOR >= 2
1243 #if CEC_LIB_VERSION_MAJOR <= 3
1244 // cppcheck-suppress passedByValue
1245 static int CECAlertCallback(void *adapter, const libcec_alert alert, const libcec_parameter CEC_CALLBACK_PARAM_TYPE data)
1246 {
1247  return ((CECAdapterPriv*)adapter)->HandleAlert(alert, data);
1248 }
1249 #endif
1250 #if CEC_LIB_VERSION_MAJOR >= 4
1251 static void CECAlertCallback(void *adapter, const libcec_alert alert, const libcec_parameter data)
1252 {
1253  ((CECAdapterPriv*)adapter)->HandleAlert(alert, data);
1254 }
1255 #endif
1256 static void CECSourceActivatedCallback(void *adapter, const cec_logical_address address, const uint8_t activated)
1257 {
1258  ((CECAdapterPriv*)adapter)->HandleSourceActivated(address, activated);
1259 }
1260 #endif
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
#define LIBCEC_PORT
Definition: cecadapter.h:11
bool Open(void)
Definition: cecadapter.cpp:79
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
bool IsValid()
#define CEC_MIN_HDMI_PORTNUMBER
Definition: cecadapter.cpp:31
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
static QMutex * gHandleActionsLock
Definition: cecadapter.h:35
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
#define LOC
Definition: cecadapter.cpp:17
#define POWEROFFTV_ONEXIT
Definition: cecadapter.h:13
int LogMessage(const cec_log_message &message)
Definition: cecadapter.cpp:245
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:311
bool
Definition: pxsup2dast.c:30
#define CEC_CALLBACK_PARAM_TYPE
Definition: cecadapter.cpp:34
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
virtual ~CECAdapter()
static int CECCommandCallback(void *adapter, const cec_command CEC_CALLBACK_PARAM_TYPE command)
static int CECKeyPressCallback(void *adapter, const cec_keypress CEC_CALLBACK_PARAM_TYPE keypress)
int HandleKeyPress(const cec_keypress &key)
Definition: cecadapter.cpp:336
#define LIBCEC_DEVICE
Definition: cecadapter.h:9
#define CEC_MAX_HDMI_PORTNUMBER
Definition: cecadapter.cpp:32
static int CECLogMessageCallback(void *adapter, const cec_log_message CEC_CALLBACK_PARAM_TYPE message)
QString GetSetting(const QString &key, const QString &defaultval="")
ICECCallbacks m_callbacks
#define ACTION_TVPOWERON
Definition: mythuiactions.h:25
int HandleCommand(const cec_command &command)
Definition: cecadapter.cpp:279
#define POWERONTV_ALLOWED
Definition: cecadapter.h:14
#define ACTION_TVPOWEROFF
Definition: mythuiactions.h:24
MythUIHelper * GetMythUI()
#define POWERONTV_ONSTART
Definition: cecadapter.h:15
MythMainWindow * GetMythMainWindow(void)
void Action(const QString &action)
int GetNumSetting(const QString &key, int defaultval=0)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
#define LIBCEC_ENABLED
Definition: cecadapter.h:8
void HandleActions(void)
bool GetBoolSetting(const QString &key, bool defaultval=false)
void HandleCommand(const cec_command *command)
Definition: cecadapter.cpp:313
CECAdapterPriv * m_priv
Definition: cecadapter.h:33
static QMutex * gLock
Definition: cecadapter.h:34
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:202
void Close(void)
Definition: cecadapter.cpp:227
void LogMessage(const cec_log_message *message)
Definition: cecadapter.cpp:261
void ResetScreensaver(void)
void HandleKeyPress(const cec_keypress *key)
Definition: cecadapter.cpp:674
static QWaitCondition * gActionsReady
Definition: cecadapter.h:36
#define POWEROFFTV_ALLOWED
Definition: cecadapter.h:12
#define MAX_CEC_DEVICES
Definition: cecadapter.cpp:16
void SendSystemEvent(const QString &msg)
#define LIBCEC_BASE
Definition: cecadapter.h:10