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
22static constexpr uint8_t MAX_CEC_DEVICES { 10 };
23#define LOC QString("CECAdapter: ")
24
25#if CEC_LIB_VERSION_MAJOR <= 3
26
27int MythCECAdapter::LogMessageCallback(void* /*unused*/, const cec_log_message Message)
28{
29 return MythCECAdapter::LogMessage(Message);
30}
31
32int 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
40int 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
48int MythCECAdapter::AlertCallback(void* /*unused*/, const libcec_alert Alert, const libcec_parameter Data)
49{
50 return MythCECAdapter::HandleAlert(Alert, Data);
51}
52#else
53void MythCECAdapter::LogMessageCallback(void* /*unused*/, const cec_log_message* Message)
54{
56}
57
58void 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
65void 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
72void MythCECAdapter::AlertCallback(void* /*unused*/, const libcec_alert Alert, const libcec_parameter Data)
73{
74 MythCECAdapter::HandleAlert(Alert, Data);
75}
76#endif
77
78void MythCECAdapter::SourceCallback(void* /*unused*/, const cec_logical_address Address, const uint8_t Activated)
79{
80 MythCECAdapter::HandleSource(Address, Activated);
81}
82
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
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;
256 actions |= PowerOnTV;
257 HandleActions(actions);
258}
259
261{
262 if (m_adapter)
263 {
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
277int 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
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
317int 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
652int 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
709void 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
717void 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
749void MythCECAdapter::Action(const QString &Action)
750{
753 else if (ACTION_TVPOWEROFF == Action)
755}
756
758{
759 m_ignoreKeys = Ignore;
760}
761
An action (for this plugin) consists of a description, and a set of key sequences.
Definition: action.h:41
ICECCallbacks m_callbacks
static int KeyPressCallback(void *Adapter, const cec_keypress Keypress)
void IgnoreKeys(bool Ignore)
static QString AddressToString(int Address)
static void HandleSource(cec_logical_address Address, uint8_t Activated)
void Open(MythMainWindow *Window)
ICECAdapter * m_adapter
static int LogMessageCallback(void *, const cec_log_message Message)
bool m_powerOffTVAllowed
bool m_switchInputAllowed
static int HandleAlert(libcec_alert Alert, libcec_parameter Data)
void Action(const QString &Action)
static int AlertCallback(void *, const libcec_alert Alert, const libcec_parameter Data)
static int LogMessage(const cec_log_message &Message)
void HandleActions(MythCECActions Actions)
static void SourceCallback(void *, cec_logical_address Address, uint8_t Activated)
int HandleCommand(const cec_command &Command)
int HandleKeyPress(cec_keypress Key) const
static int CommandCallback(void *Adapter, const cec_command Command)
QString GetSetting(const QString &key, const QString &defaultval="")
void SendSystemEvent(const QString &msg)
int GetNumSetting(const QString &key, int defaultval=0)
bool GetBoolSetting(const QString &key, bool defaultval=false)
MythEDID & GetEDID()
uint16_t PhysicalAddress() const
Definition: mythedid.cpp:59
bool Valid() const
Definition: mythedid.cpp:39
static void ResetScreensaver()
MythDisplay * GetDisplay()
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)
unsigned short uint16_t
Definition: iso6937tables.h:3
#define LOC
static constexpr uint8_t MAX_CEC_DEVICES
#define LIBCEC_ENABLED
Definition: mythcecadapter.h:7
#define POWERONTV_ONSTART
#define LIBCEC_BASE
Definition: mythcecadapter.h:9
#define POWEROFFTV_ALLOWED
#define LIBCEC_DEVICE
Definition: mythcecadapter.h:8
#define POWEROFFTV_ONEXIT
#define LIBCEC_PORT
#define POWERONTV_ALLOWED
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
static constexpr const char * ACTION_TVPOWERON
Definition: mythuiactions.h:25
static constexpr const char * ACTION_TVPOWEROFF
Definition: mythuiactions.h:24