MythTV  master
mythcecadapter.cpp
Go to the documentation of this file.
1 // Qt
2 #include <QApplication>
3 #include <QKeyEvent>
4 #include <QString>
5 
6 // MythTV
10 
11 #include "mythmainwindow.h"
12 #include "mythdisplay.h"
13 #include "mythcecadapter.h"
14 
15 // Std
16 #include <memory>
17 #include <vector>
18 
19 // libcec
20 #include <libcec/cecloader.h>
21 
22 static constexpr uint8_t MAX_CEC_DEVICES { 10 };
23 #define LOC QString("CECAdapter: ")
24 
25 #if CEC_LIB_VERSION_MAJOR <= 3
26 
27 int MythCECAdapter::LogMessageCallback(void* /*unused*/, const cec_log_message Message)
28 {
29  return MythCECAdapter::LogMessage(Message);
30 }
31 
32 int MythCECAdapter::KeyPressCallback(void* Adapter, const cec_keypress Keypress)
33 {
34  MythCECAdapter* adapter = reinterpret_cast<MythCECAdapter*>(Adapter);
35  if (adapter)
36  return adapter->HandleKeyPress(Keypress);
37  return 1;
38 }
39 
40 int MythCECAdapter::CommandCallback(void* Adapter, const cec_command Command)
41 {
42  MythCECAdapter* adapter = reinterpret_cast<MythCECAdapter*>(Adapter);
43  if (adapter)
44  return adapter->HandleCommand(Command);
45  return 1;
46 }
47 
48 int MythCECAdapter::AlertCallback(void* /*unused*/, const libcec_alert Alert, const libcec_parameter Data)
49 {
50  return MythCECAdapter::HandleAlert(Alert, Data);
51 }
52 #else
53 void MythCECAdapter::LogMessageCallback(void* /*unused*/, const cec_log_message* Message)
54 {
56 }
57 
58 void MythCECAdapter::KeyPressCallback(void* Adapter, const cec_keypress* Keypress)
59 {
60  auto * adapter = reinterpret_cast<MythCECAdapter*>(Adapter);
61  if (adapter)
62  adapter->HandleKeyPress(*Keypress);
63 }
64 
65 void MythCECAdapter::CommandCallback(void* Adapter, const cec_command* Command)
66 {
67  auto * adapter = reinterpret_cast<MythCECAdapter*>(Adapter);
68  if (adapter)
69  adapter->HandleCommand(*Command);
70 }
71 
72 void MythCECAdapter::AlertCallback(void* /*unused*/, const libcec_alert Alert, const libcec_parameter Data)
73 {
74  MythCECAdapter::HandleAlert(Alert, Data);
75 }
76 #endif
77 
78 void MythCECAdapter::SourceCallback(void* /*unused*/, const cec_logical_address Address, const uint8_t Activated)
79 {
80  MythCECAdapter::HandleSource(Address, Activated);
81 }
82 
83 QString MythCECAdapter::AddressToString(int Address)
84 {
85  return QString("%1.%2.%3.%4").arg((Address >> 12) & 0xF).arg((Address >> 8) & 0xF)
86  .arg((Address >> 4) & 0xF).arg(Address & 0xF);
87 }
88 
90 {
91  Close();
92 }
93 
95 {
96  Close();
97 
98  // don't try if disabled
100  {
101  LOG(VB_GENERAL, LOG_INFO, LOC + "libCEC support is disabled.");
102  return;
103  }
104 
105  // get settings
106  // N.B. these need to be set manually since there is no UI
107  QString defaultDevice = gCoreContext->GetSetting(LIBCEC_DEVICE, "auto").trimmed();
108  // Note - if libcec supports automatic detection via EDID then
109  // these settings are not used
110  // The logical address of the HDMI device Myth is connected to
111  QString base_dev = gCoreContext->GetSetting(LIBCEC_BASE, "auto").trimmed();
112  // The number of the HDMI port Myth is connected to
113  QString hdmi_port = gCoreContext->GetSetting(LIBCEC_PORT, "auto").trimmed();
114 
119 
120  // create adapter interface
121  libcec_configuration configuration;
122  strcpy(configuration.strDeviceName, "MythTV");
123  configuration.deviceTypes.Add(CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
124 
125  if ("auto" != base_dev)
126  {
127  int base = base_dev.toInt();
128  if (base >= 0 && base < CECDEVICE_BROADCAST)
129  configuration.baseDevice = static_cast<cec_logical_address>(base);
130  }
131  if ("auto" != hdmi_port)
132  {
133  int defaultHDMIPort = hdmi_port.toInt();
134  if (defaultHDMIPort >= CEC_MIN_HDMI_PORTNUMBER && defaultHDMIPort <= CEC_MAX_HDMI_PORTNUMBER)
135  configuration.iHDMIPort = static_cast<uint8_t>(defaultHDMIPort);
136  }
137 
138  // MythDisplay has more accurate physical address detection than libcec - so
139  // use any physical address detected there.
140  // NOTE There is no guarantee that the adapter is actually connected via the
141  // same HDMI device (e.g. a USB CEC adapter could be connected to a different
142  // display).
143  // NOTE We could listen for display changes here but the adapter will be recreated
144  // following a screen change and realistically any setup with more than 1 display
145  // and/or CEC adapter is going to be very hit and miss.
146  MythDisplay* display = Window->GetDisplay();
147  if (display->GetEDID().Valid())
148  {
149  uint16_t address = display->GetEDID().PhysicalAddress();
150  if (address != 0)
151  {
152  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Using physical address %1 from EDID")
153  .arg(AddressToString(address)));
154  configuration.iPhysicalAddress = address;
155  }
156  }
157 
158  // Set up the callbacks
159 #if CEC_LIB_VERSION_MAJOR <= 3
164  m_callbacks.CBCecSourceActivated = &MythCECAdapter::SourceCallback;
165 #else
168  m_callbacks.commandReceived = &MythCECAdapter::CommandCallback;
170  m_callbacks.sourceActivated = &MythCECAdapter::SourceCallback;
171 #endif
172  configuration.callbackParam = this;
173  configuration.callbacks = &m_callbacks;
174 
175  // and initialise
176  m_adapter = LibCecInitialise(&configuration);
177 
178  if (!m_adapter)
179  {
180  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to load libcec.");
181  Close();
182  return;
183  }
184 
185  // initialise the host on which libCEC is running
186  m_adapter->InitVideoStandalone();
187 
188  // find adapters
189 #if CEC_LIB_VERSION_MAJOR >= 4
190  auto *devices = new cec_adapter_descriptor[MAX_CEC_DEVICES];
191  int8_t num_devices = m_adapter->DetectAdapters(devices, MAX_CEC_DEVICES, nullptr, true);
192  // NOLINTNEXTLINE(modernize-avoid-c-arrays)
193  std::unique_ptr<cec_adapter_descriptor[]> cec_devices(devices);
194 #else
195  cec_adapter *devices = new cec_adapter[MAX_CEC_DEVICES];
196  uint8_t num_devices = m_adapter->FindAdapters(devices, MAX_CEC_DEVICES, nullptr);
197 #endif
198  if (num_devices < 1)
199  {
200  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to find any CEC devices.");
201  Close();
202  return;
203  }
204 
205  // find required device and debug
206  int devicenum = 0;
207  bool find = defaultDevice != "auto";
208 
209  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Found %1 CEC devices(s).")
210  .arg(num_devices));
211  for (uint8_t i = 0; i < num_devices; i++)
212  {
213 #if CEC_LIB_VERSION_MAJOR >= 4
214  QString comm = QString::fromLatin1(devices[i].strComName);
215  QString path = QString::fromLatin1(devices[i].strComPath);
216 #else
217  QString comm = QString::fromLatin1(devices[i].comm);
218  QString path = QString::fromLatin1(devices[i].path);
219 #endif
220  bool match = find ? (comm == defaultDevice) : (i == 0);
221  devicenum = match ? i : devicenum;
222  LOG(VB_GENERAL, LOG_INFO, LOC +
223  QString("Device %1: path '%2' com port '%3' %4")
224  .arg(QString::number(i + 1),
225  path, comm, match ? "SELECTED" : ""));
226  }
227 
228  // open adapter
229 #if CEC_LIB_VERSION_MAJOR >= 4
230  QString comm = QString::fromLatin1(devices[devicenum].strComName);
231  QString path = QString::fromLatin1(devices[devicenum].strComPath);
232 #else
233  QString comm = QString::fromLatin1(devices[devicenum].comm);
234  QString path = QString::fromLatin1(devices[devicenum].path);
235 #endif
236  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Trying to open device %1 (%2).")
237  .arg(path, comm));
238 
239 #if CEC_LIB_VERSION_MAJOR >= 4
240  if (!m_adapter->Open(devices[devicenum].strComName))
241 #else
242  if (!m_adapter->Open(devices[devicenum].comm))
243 #endif
244  {
245  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to open device.");
246  Close();
247  return;
248  }
249 
250  LOG(VB_GENERAL, LOG_INFO, LOC + "Opened CEC device.");
251  m_valid = true;
252 
253  // Power on TV if enabled and switch to our input
254  MythCECActions actions = SwitchInput;
255  if (m_powerOnTVOnStart)
256  actions |= PowerOnTV;
257  HandleActions(actions);
258 }
259 
261 {
262  if (m_adapter)
263  {
264  if (m_powerOffTVOnExit)
266  m_adapter->Close();
267  UnloadLibCec(m_adapter);
268  // Workaround for bug in libcec/cecloader.h
269  // MythTV issue #299, libcec issue #555
270  g_libCEC = nullptr;
271  LOG(VB_GENERAL, LOG_INFO, LOC + "Closing down CEC.");
272  }
273  m_valid = false;
274  m_adapter = nullptr;
275 }
276 
277 int MythCECAdapter::LogMessage(const cec_log_message &Message)
278 {
279  QString msg(Message.message);
280  LogLevel_t level = LOG_UNKNOWN;
281  switch (Message.level)
282  {
283  case CEC_LOG_ERROR: level = LOG_ERR; break;
284  case CEC_LOG_WARNING: level = LOG_WARNING; break;
285  case CEC_LOG_NOTICE: level = LOG_INFO; break;
286  case CEC_LOG_DEBUG: level = LOG_DEBUG; break;
287  default: break;
288  }
289  LOG(VB_GENERAL, level, LOC + QString("%1").arg(msg));
290  return 1;
291 }
292 
293 int MythCECAdapter::HandleCommand(const cec_command &Command)
294 {
295  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Command %1 from '%2' (%3) - destination '%4' (%5)")
296  .arg(Command.opcode).arg(Command.initiator).arg(Command.initiator)
297  .arg(Command.destination).arg(Command.destination));
298 
299  switch (Command.opcode)
300  {
301  case CEC_OPCODE_MENU_REQUEST:
302  cec_keypress key;
303  key.keycode = CEC_USER_CONTROL_CODE_ROOT_MENU;
304  key.duration = 5;
305  HandleKeyPress(key);
306  // SetMenuState could be used to disable the TV menu here
307  // That may be a user-unfriendly thing to do
308  // So they have to see both the TV menu and the MythTV menu
309  break;
310  default:
311  break;
312  }
313  gCoreContext->SendSystemEvent(QString("CEC_COMMAND_RECEIVED COMMAND %1").arg(Command.opcode));
314  return 1;
315 }
316 
317 int MythCECAdapter::HandleKeyPress(const cec_keypress Key) const
318 {
319  // Ignore key down events and wait for the key 'up'
320  if (Key.duration < 1 || m_ignoreKeys)
321  return 1;
322 
323  QString code;
324  int action = 0;
325  Qt::KeyboardModifier modifier = Qt::NoModifier;
326  switch (Key.keycode)
327  {
328  case CEC_USER_CONTROL_CODE_NUMBER0:
329  action = Qt::Key_0;
330  code = "0";
331  break;
332  case CEC_USER_CONTROL_CODE_NUMBER1:
333  action = Qt::Key_1;
334  code = "1";
335  break;
336  case CEC_USER_CONTROL_CODE_NUMBER2:
337  action = Qt::Key_2;
338  code = "2";
339  break;
340  case CEC_USER_CONTROL_CODE_NUMBER3:
341  action = Qt::Key_3;
342  code = "3";
343  break;
344  case CEC_USER_CONTROL_CODE_NUMBER4:
345  action = Qt::Key_4;
346  code = "4";
347  break;
348  case CEC_USER_CONTROL_CODE_NUMBER5:
349  action = Qt::Key_5;
350  code = "5";
351  break;
352  case CEC_USER_CONTROL_CODE_NUMBER6:
353  action = Qt::Key_6;
354  code = "6";
355  break;
356  case CEC_USER_CONTROL_CODE_NUMBER7:
357  action = Qt::Key_7;
358  code = "7";
359  break;
360  case CEC_USER_CONTROL_CODE_NUMBER8:
361  action = Qt::Key_8;
362  code = "8";
363  break;
364  case CEC_USER_CONTROL_CODE_NUMBER9:
365  action = Qt::Key_9;
366  code = "9";
367  break;
368  case CEC_USER_CONTROL_CODE_SELECT:
369  action = Qt::Key_Select;
370  code = "SELECT";
371  break;
372  case CEC_USER_CONTROL_CODE_ENTER:
373  action = Qt::Key_Enter;
374  code = "ENTER";
375  break;
376  case CEC_USER_CONTROL_CODE_UP:
377  action = Qt::Key_Up;
378  code = "UP";
379  break;
380  case CEC_USER_CONTROL_CODE_DOWN:
381  action = Qt::Key_Down;
382  code = "DOWN";
383  break;
384  case CEC_USER_CONTROL_CODE_LEFT:
385  action = Qt::Key_Left;
386  code = "LEFT";
387  break;
388  case CEC_USER_CONTROL_CODE_LEFT_UP:
389  action = Qt::Key_Left;
390  code = "LEFT_UP";
391  break;
392  case CEC_USER_CONTROL_CODE_LEFT_DOWN:
393  action = Qt::Key_Left;
394  code = "LEFT_DOWN";
395  break;
396  case CEC_USER_CONTROL_CODE_RIGHT:
397  action = Qt::Key_Right;
398  code = "RIGHT";
399  break;
400  case CEC_USER_CONTROL_CODE_RIGHT_UP:
401  action = Qt::Key_Right;
402  code = "RIGHT_UP";
403  break;
404  case CEC_USER_CONTROL_CODE_RIGHT_DOWN:
405  action = Qt::Key_Right;
406  code = "RIGHT_DOWN";
407  break;
408  case CEC_USER_CONTROL_CODE_ROOT_MENU:
409  action = Qt::Key_M;
410  code = "ROOT_MENU";
411  break;
412  case CEC_USER_CONTROL_CODE_AN_RETURN: //return (Samsung) (0x91)
413  case CEC_USER_CONTROL_CODE_EXIT:
414  action = Qt::Key_Escape;
415  code = "EXIT";
416  break;
417  case CEC_USER_CONTROL_CODE_PREVIOUS_CHANNEL:
418  action = Qt::Key_H;
419  code = "PREVIOUS_CHANNEL";
420  break;
421  case CEC_USER_CONTROL_CODE_SOUND_SELECT:
422  action = Qt::Key_Plus;
423  code = "SOUND_SELECT";
424  break;
425  case CEC_USER_CONTROL_CODE_VOLUME_UP:
426  action = Qt::Key_VolumeUp;
427  code = "VOLUME_UP";
428  break;
429  case CEC_USER_CONTROL_CODE_VOLUME_DOWN:
430  action = Qt::Key_VolumeDown;
431  code = "VOLUME_DOWN";
432  break;
433  case CEC_USER_CONTROL_CODE_MUTE:
434  action = Qt::Key_VolumeMute;
435  code = "MUTE";
436  break;
437  case CEC_USER_CONTROL_CODE_PLAY:
438  action = Qt::Key_P;
439  code = "PLAY";
440  // Play set to control-p to differentiate from pause
441  modifier = Qt::ControlModifier;
442  break;
443  case CEC_USER_CONTROL_CODE_PAUSE:
444  action = Qt::Key_P;
445  code = "PAUSE";
446  break;
447  case CEC_USER_CONTROL_CODE_STOP:
448  action = Qt::Key_Stop;
449  code = "STOP";
450  break;
451  case CEC_USER_CONTROL_CODE_RECORD:
452  action = Qt::Key_R;
453  code = "RECORD";
454  break;
455  case CEC_USER_CONTROL_CODE_CLEAR:
456  action = Qt::Key_Clear;
457  code = "CLEAR";
458  break;
459  case CEC_USER_CONTROL_CODE_DISPLAY_INFORMATION:
460  action = Qt::Key_I;
461  code = "DISPLAY_INFORMATION";
462  break;
463  case CEC_USER_CONTROL_CODE_PAGE_UP:
464  action = Qt::Key_PageUp;
465  code = "PAGE_UP";
466  break;
467  case CEC_USER_CONTROL_CODE_PAGE_DOWN:
468  action = Qt::Key_PageDown;
469  code = "PAGE_DOWN";
470  break;
471  case CEC_USER_CONTROL_CODE_EJECT:
472  action = Qt::Key_Eject;
473  code = "EJECT";
474  break;
475  case CEC_USER_CONTROL_CODE_FORWARD:
476  action = Qt::Key_Forward;
477  code = "FORWARD";
478  break;
479  case CEC_USER_CONTROL_CODE_BACKWARD:
480  action = Qt::Key_Back;
481  code = "BACKWARD";
482  break;
483  case CEC_USER_CONTROL_CODE_F1_BLUE:
484  action = Qt::Key_F5; // NB F1 is help and we normally map blue to F5
485  code = "F1_BLUE";
486  break;
487  case CEC_USER_CONTROL_CODE_F2_RED:
488  action = Qt::Key_F2;
489  code = "F2_RED";
490  break;
491  case CEC_USER_CONTROL_CODE_F3_GREEN:
492  action = Qt::Key_F3;
493  code = "F3_GREEN";
494  break;
495  case CEC_USER_CONTROL_CODE_F4_YELLOW:
496  action = Qt::Key_F4;
497  code = "F4_YELLOW";
498  break;
499  case CEC_USER_CONTROL_CODE_SETUP_MENU:
500  action = Qt::Key_M; // Duplicate of Root Menu
501  code = "SETUP_MENU";
502  break;
503  case CEC_USER_CONTROL_CODE_CONTENTS_MENU:
504  action = Qt::Key_M; // Duplicate of Root Menu
505  code = "CONTENTS_MENU";
506  break;
507  case CEC_USER_CONTROL_CODE_FAVORITE_MENU:
508  action = Qt::Key_M; // Duplicate of Root Menu
509  code = "FAVORITE_MENU";
510  break;
511  case CEC_USER_CONTROL_CODE_DOT:
512  action = Qt::Key_Period;
513  code = "DOT";
514  break;
515  case CEC_USER_CONTROL_CODE_NEXT_FAVORITE:
516  action = Qt::Key_Slash;
517  code = "NEXT_FAVORITE";
518  break;
519  case CEC_USER_CONTROL_CODE_INPUT_SELECT:
520  action = Qt::Key_C;
521  code = "INPUT_SELECT";
522  break;
523  case CEC_USER_CONTROL_CODE_HELP:
524  action = Qt::Key_F1;
525  code = "HELP";
526  break;
527  case CEC_USER_CONTROL_CODE_STOP_RECORD:
528  action = Qt::Key_R; // Duplicate of Record
529  code = "STOP_RECORD";
530  break;
531  case CEC_USER_CONTROL_CODE_SUB_PICTURE:
532  action = Qt::Key_V;
533  code = "SUB_PICTURE";
534  break;
535  case CEC_USER_CONTROL_CODE_ELECTRONIC_PROGRAM_GUIDE:
536  action = Qt::Key_S;
537  code = "ELECTRONIC_PROGRAM_GUIDE";
538  break;
539  case CEC_USER_CONTROL_CODE_POWER:
540  action = Qt::Key_PowerOff;
541  code = "POWER";
542  break;
543 
544  // these codes have 'non-standard' Qt key mappings to ensure
545  // each code has a unique key mapping
546  case CEC_USER_CONTROL_CODE_CHANNEL_DOWN:
547  action = Qt::Key_F20; // to differentiate from Up
548  code = "CHANNEL_DOWN";
549  break;
550  case CEC_USER_CONTROL_CODE_CHANNEL_UP:
551  action = Qt::Key_F21; // to differentiate from Down
552  code = "CHANNEL_UP";
553  break;
554  case CEC_USER_CONTROL_CODE_REWIND:
555  action = Qt::Key_F22; // to differentiate from Left
556  code = "REWIND";
557  break;
558  case CEC_USER_CONTROL_CODE_FAST_FORWARD:
559  action = Qt::Key_F23; // to differentiate from Right
560  code = "FAST_FORWARD";
561  break;
562  case CEC_USER_CONTROL_CODE_ANGLE:
563  action = Qt::Key_F24;
564  code = "ANGLE";
565  break;
566  case CEC_USER_CONTROL_CODE_F5:
567  action = Qt::Key_F6; // NB!
568  code = "F5";
569  break;
570 
571  // codes with no obvious MythTV action
572  case CEC_USER_CONTROL_CODE_INITIAL_CONFIGURATION:
573  code = "INITIAL_CONFIGURATION";
574  break;
575  case CEC_USER_CONTROL_CODE_PAUSE_RECORD:
576  code = "PAUSE_RECORD";
577  break;
578  case CEC_USER_CONTROL_CODE_VIDEO_ON_DEMAND:
579  code = "VIDEO_ON_DEMAND";
580  break;
581  case CEC_USER_CONTROL_CODE_TIMER_PROGRAMMING:
582  code = "TIMER_PROGRAMMING";
583  break;
584  case CEC_USER_CONTROL_CODE_UNKNOWN:
585  code = "UNKNOWN";
586  break;
587  case CEC_USER_CONTROL_CODE_DATA:
588  code = "DATA";
589  break;
590 
591  // Functions aren't implemented (similar to macros?)
592  case CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION:
593  code = "POWER_ON_FUNCTION";
594  break;
595  case CEC_USER_CONTROL_CODE_PLAY_FUNCTION:
596  code = "PLAY_FUNCTION";
597  break;
598  case CEC_USER_CONTROL_CODE_PAUSE_PLAY_FUNCTION:
599  code = "PAUSE_PLAY_FUNCTION";
600  break;
601  case CEC_USER_CONTROL_CODE_RECORD_FUNCTION:
602  code = "RECORD_FUNCTION";
603  break;
604  case CEC_USER_CONTROL_CODE_PAUSE_RECORD_FUNCTION:
605  code = "PAUSE_RECORD_FUNCTION";
606  break;
607  case CEC_USER_CONTROL_CODE_STOP_FUNCTION:
608  code = "STOP_FUNCTION";
609  break;
610  case CEC_USER_CONTROL_CODE_MUTE_FUNCTION:
611  code = "MUTE_FUNCTION";
612  break;
613  case CEC_USER_CONTROL_CODE_RESTORE_VOLUME_FUNCTION:
614  code = "RESTORE_VOLUME_FUNCTION";
615  break;
616  case CEC_USER_CONTROL_CODE_TUNE_FUNCTION:
617  code = "TUNE_FUNCTION";
618  break;
619  case CEC_USER_CONTROL_CODE_SELECT_MEDIA_FUNCTION:
620  code = "SELECT_MEDIA_FUNCTION";
621  break;
622  case CEC_USER_CONTROL_CODE_SELECT_AV_INPUT_FUNCTION:
623  code = "SELECT_AV_INPUT_FUNCTION";
624  break;
625  case CEC_USER_CONTROL_CODE_SELECT_AUDIO_INPUT_FUNCTION:
626  code = "SELECT_AUDIO_INPUT_FUNCTION";
627  break;
628  case CEC_USER_CONTROL_CODE_POWER_TOGGLE_FUNCTION:
629  code = "POWER_TOGGLE_FUNCTION";
630  break;
631  case CEC_USER_CONTROL_CODE_POWER_OFF_FUNCTION:
632  code = "POWER_OFF_FUNCTION";
633  break;
634 
635  default:
636  code = QString("UNKNOWN_FUNCTION_%1").arg(Key.keycode);
637  break;
638  }
639 
640  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Keypress %1 %2")
641  .arg(code, 0 == action ? "(Not actioned)" : ""));
642 
643  if (0 == action)
644  return 1;
645 
647  auto* ke = new QKeyEvent(QEvent::KeyPress, action, modifier);
648  QCoreApplication::postEvent(GetMythMainWindow(), ke);
649  return 1;
650 }
651 
652 int MythCECAdapter::HandleAlert(const libcec_alert Alert, const libcec_parameter Data)
653 {
654  // These aren't handled yet
655  // Note that we *DON'T* want to just show these
656  // to the user in a popup, because some (eg prompting about firmware
657  // upgrades) aren't appropriate.
658  // Ideally we'd try to handle this, eg by reopening the adapter
659  // in a separate thread if it lost the connection....
660 
661  QString param;
662  switch (Data.paramType)
663  {
664  case CEC_PARAMETER_TYPE_STRING:
665  param = QString(": %1").arg(static_cast<char*>(Data.paramData));
666  break;
667  case CEC_PARAMETER_TYPE_UNKOWN: /* libcec typo */
668  if (Data.paramData != nullptr)
669  param = QString(": UNKNOWN param has type %1").arg(Data.paramType);
670  break;
671  }
672 
673  // There is no ToString method for libcec_alert...
674  // Plus libcec adds new values in minor releases (eg 2.1.1)
675  // but doesn't provide a #define for the last digit...
676  // Besides, it makes sense to do this, since we could be compiling
677  // against an older version than we're running against
678 #if CEC_LIB_VERSION_MAJOR == 2 && CEC_LIB_VERSION_MINOR < 1
679 // since 2.0.4
680 #define CEC_ALERT_PHYSICAL_ADDRESS_ERROR 4
681 #endif
682 #if CEC_LIB_VERSION_MAJOR == 2 && CEC_LIB_VERSION_MINOR < 2
683 // since 2.1.1
684 #define CEC_ALERT_TV_POLL_FAILED 5
685 #endif
686  switch (Alert)
687  {
688  case CEC_ALERT_SERVICE_DEVICE:
689  LOG(VB_GENERAL, LOG_INFO, LOC + QString("CEC device service message") + param);
690  break;
691  case CEC_ALERT_CONNECTION_LOST:
692  LOG(VB_GENERAL, LOG_ERR, LOC + QString("CEC device connection list") + param);
693  break;
694  case CEC_ALERT_PERMISSION_ERROR:
695  case CEC_ALERT_PORT_BUSY:
696  // Don't log due to possible false positives on the initial
697  // open. libcec will log via the logging callback anyway
698  break;
699  case CEC_ALERT_PHYSICAL_ADDRESS_ERROR:
700  LOG(VB_GENERAL, LOG_ERR, LOC + QString("CEC physical address error") + param);
701  break;
702  case CEC_ALERT_TV_POLL_FAILED:
703  LOG(VB_GENERAL, LOG_WARNING, LOC + QString("CEC device can't poll TV") + param);
704  break;
705  }
706  return 1;
707 }
708 
709 void MythCECAdapter::HandleSource(const cec_logical_address Address, const uint8_t Activated)
710 {
711  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Source %1 %2")
712  .arg(Address).arg(Activated ? "Activated" : "Deactivated"));
713  if (Activated)
715 }
716 
717 void MythCECAdapter::HandleActions(MythCECActions Actions)
718 {
719  if (!m_adapter || !m_valid)
720  return;
721 
722  // power state
723  if (((Actions & PowerOffTV) != 0U) && m_powerOffTVAllowed)
724  {
725  if (m_adapter->StandbyDevices(CECDEVICE_TV))
726  LOG(VB_GENERAL, LOG_INFO, LOC + "Asked TV to turn off.");
727  else
728  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to turn TV off.");
729  }
730 
731  if (((Actions & PowerOnTV) != 0U) && m_powerOnTVAllowed)
732  {
733  if (m_adapter->PowerOnDevices(CECDEVICE_TV))
734  LOG(VB_GENERAL, LOG_INFO, LOC + "Asked TV to turn on.");
735  else
736  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to turn TV on.");
737  }
738 
739  // HDMI input
740  if (((Actions & SwitchInput) != 0U) && m_switchInputAllowed)
741  {
742  if (m_adapter->SetActiveSource())
743  LOG(VB_GENERAL, LOG_INFO, LOC + "Asked TV to switch to this input.");
744  else
745  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to switch to this input.");
746  }
747 }
748 
749 void MythCECAdapter::Action(const QString &Action)
750 {
751  if (ACTION_TVPOWERON == Action)
753  else if (ACTION_TVPOWEROFF == Action)
755 }
756 
757 void MythCECAdapter::IgnoreKeys(bool Ignore)
758 {
759  m_ignoreKeys = Ignore;
760 }
761 
mythevent.h
MythCECAdapter::SwitchInput
@ SwitchInput
Definition: mythcecadapter.h:31
MythCECAdapter::m_adapter
ICECAdapter * m_adapter
Definition: mythcecadapter.h:66
MythDisplay::GetEDID
MythEDID & GetEDID()
Definition: mythdisplay.cpp:934
POWEROFFTV_ALLOWED
#define POWEROFFTV_ALLOWED
Definition: mythcecadapter.h:11
MythEDID::PhysicalAddress
uint16_t PhysicalAddress() const
Definition: mythedid.cpp:59
mythcecadapter.h
MythCECAdapter::m_callbacks
ICECCallbacks m_callbacks
Definition: mythcecadapter.h:67
MythCECAdapter::m_powerOffTVOnExit
bool m_powerOffTVOnExit
Definition: mythcecadapter.h:71
MythCECAdapter::HandleKeyPress
int HandleKeyPress(cec_keypress Key) const
Definition: mythcecadapter.cpp:317
MythCECAdapter::m_powerOnTVAllowed
bool m_powerOnTVAllowed
Definition: mythcecadapter.h:72
MythCECAdapter::Open
void Open(MythMainWindow *Window)
Definition: mythcecadapter.cpp:94
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythCECAdapter::IgnoreKeys
void IgnoreKeys(bool Ignore)
Definition: mythcecadapter.cpp:757
MythCECAdapter::LogMessage
static int LogMessage(const cec_log_message &Message)
Definition: mythcecadapter.cpp:277
Action
An action (for this plugin) consists of a description, and a set of key sequences.
Definition: action.h:40
MythCECAdapter::m_powerOnTVOnStart
bool m_powerOnTVOnStart
Definition: mythcecadapter.h:73
MAX_CEC_DEVICES
static constexpr uint8_t MAX_CEC_DEVICES
Definition: mythcecadapter.cpp:22
LIBCEC_ENABLED
#define LIBCEC_ENABLED
Definition: mythcecadapter.h:7
MythMainWindow::GetDisplay
MythDisplay * GetDisplay()
Definition: mythmainwindow.cpp:253
MythCECAdapter::m_valid
bool m_valid
Definition: mythcecadapter.h:68
MythCECAdapter::KeyPressCallback
static int KeyPressCallback(void *Adapter, const cec_keypress Keypress)
Definition: mythcecadapter.cpp:32
mythdisplay.h
LIBCEC_PORT
#define LIBCEC_PORT
Definition: mythcecadapter.h:10
Command
Definition: gamesettings.cpp:233
mythlogging.h
MythCECAdapter::PowerOffTV
@ PowerOffTV
Definition: mythcecadapter.h:29
ACTION_TVPOWERON
static constexpr const char * ACTION_TVPOWERON
Definition: mythuiactions.h:25
MythCoreContext::SendSystemEvent
void SendSystemEvent(const QString &msg)
Definition: mythcorecontext.cpp:1544
POWERONTV_ALLOWED
#define POWERONTV_ALLOWED
Definition: mythcecadapter.h:13
MythCECAdapter::m_powerOffTVAllowed
bool m_powerOffTVAllowed
Definition: mythcecadapter.h:70
MythEDID::Valid
bool Valid() const
Definition: mythedid.cpp:39
MythCECAdapter::m_switchInputAllowed
bool m_switchInputAllowed
Definition: mythcecadapter.h:74
MythCECAdapter::m_ignoreKeys
bool m_ignoreKeys
Definition: mythcecadapter.h:69
MythCECAdapter::SourceCallback
static void SourceCallback(void *, cec_logical_address Address, uint8_t Activated)
Definition: mythcecadapter.cpp:78
MythCECAdapter::CommandCallback
static int CommandCallback(void *Adapter, const cec_command Command)
Definition: mythcecadapter.cpp:40
LIBCEC_BASE
#define LIBCEC_BASE
Definition: mythcecadapter.h:9
ACTION_TVPOWEROFF
static constexpr const char * ACTION_TVPOWEROFF
Definition: mythuiactions.h:24
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
MythDisplay
Definition: mythdisplay.h:22
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:912
MythCECAdapter::LogMessageCallback
static int LogMessageCallback(void *, const cec_log_message Message)
Definition: mythcecadapter.cpp:27
MythCECAdapter::HandleSource
static void HandleSource(cec_logical_address Address, uint8_t Activated)
Definition: mythcecadapter.cpp:709
POWERONTV_ONSTART
#define POWERONTV_ONSTART
Definition: mythcecadapter.h:14
MythCoreContext::GetBoolSetting
bool GetBoolSetting(const QString &key, bool defaultval=false)
Definition: mythcorecontext.cpp:906
LOC
#define LOC
Definition: mythcecadapter.cpp:23
MythCECAdapter::AlertCallback
static int AlertCallback(void *, const libcec_alert Alert, const libcec_parameter Data)
Definition: mythcecadapter.cpp:48
MythCECAdapter::AddressToString
static QString AddressToString(int Address)
Definition: mythcecadapter.cpp:83
mythcorecontext.h
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:104
MythCECAdapter::PowerOnTV
@ PowerOnTV
Definition: mythcecadapter.h:30
build_compdb.action
action
Definition: build_compdb.py:9
LIBCEC_DEVICE
#define LIBCEC_DEVICE
Definition: mythcecadapter.h:8
MythCECAdapter::HandleCommand
int HandleCommand(const cec_command &Command)
Definition: mythcecadapter.cpp:293
uint16_t
unsigned short uint16_t
Definition: iso6937tables.h:3
MythCECAdapter::Close
void Close(void)
Definition: mythcecadapter.cpp:260
MythCECAdapter::~MythCECAdapter
~MythCECAdapter()
Definition: mythcecadapter.cpp:89
MythMainWindow::ResetScreensaver
static void ResetScreensaver()
Definition: mythmainwindow.cpp:589
MythCECAdapter::HandleActions
void HandleActions(MythCECActions Actions)
Definition: mythcecadapter.cpp:717
POWEROFFTV_ONEXIT
#define POWEROFFTV_ONEXIT
Definition: mythcecadapter.h:12
mythmainwindow.h
MythCECAdapter::HandleAlert
static int HandleAlert(libcec_alert Alert, libcec_parameter Data)
Definition: mythcecadapter.cpp:652
MythCECAdapter
Definition: mythcecadapter.h:23
MythMainWindow
Definition: mythmainwindow.h:28
find
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)
Definition: dvbstreamhandler.cpp:363
MythCECAdapter::Action
void Action(const QString &Action)
Definition: mythcecadapter.cpp:749
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:898