Ticket #11317: cecadapter.cpp

File cecadapter.cpp, 42.0 KB (added by scox_nz@…, 11 years ago)

rough and ready update to support the latest firmware/libccec - note currently has hard coded logical/physical addresses

Line 
1// Qt
2#include <QApplication>
3#include <QTimer>
4#include <QKeyEvent>
5#include <QString>
6
7// MythTV
8#include "mythcorecontext.h"
9#include "mythlogging.h"
10#include "mythevent.h"
11#include "mythuihelper.h"
12#include "mythmainwindow.h"
13
14#include "cecadapter.h"
15#include <vector>
16
17#define CEC_CONFIG_VERSION CEC_CLIENT_VERSION_CURRENT;
18#define MIN_LIBCEC_VERSION 1
19#define MAX_CEC_DEVICES 10
20#define LOC QString("CECAdapter: ")
21#define OSDNAME "MythTv"
22
23#include <libcec/cec.h>
24#include <iostream>
25using namespace CEC;
26using namespace std;
27#include <libcec/cecloader.h>
28
29QMutex* CECAdapter::gLock = new QMutex(QMutex::Recursive);
30
31class CECAdapterPriv
32{
33  public:
34    CECAdapterPriv()
35      : adapter(NULL), defaultDevice("auto"), defaultHDMIPort(1),
36        defaultDeviceID(CECDEVICE_PLAYBACKDEVICE1), timer(NULL), valid(false),
37        powerOffTV(false),  powerOffTVAllowed(false), powerOffTVOnExit(false),
38        powerOnTV(false),   powerOnTVAllowed(false),  powerOnTVOnStart(false),
39        switchInput(false), switchInputAllowed(true)
40    {
41        m_callbacks.Clear();
42// (CEC::CBCecLogMessageType)
43        m_callbacks.CBCecLogMessage = &LogMessageCallback;
44//(CEC::CBCecKeyPressType)
45        m_callbacks.CBCecKeyPress = &KeyPressCallback;
46//(CEC::CBCecConfigurationChangedType)
47        m_callbacks.CBCecConfigurationChanged =  &ConfigCallback;
48//        m_callbacks.CBCecCommand = (CEC::CBCecCommandType) &CommandCallback;
49//        m_callbacks.CBCecAlert = (CEC::CBCecAlertType) &AlertCallback;
50//        m_callbacks.CBCecSourceActivated = &SourceActivatedCallback;
51        m_callbacks.CBCecMenuStateChanged = &MenuStateCallback;
52    }
53
54    static int CEC_CDECL LogMessageCallback(void *Object, const cec_log_message message)
55    {
56           QString msg(message.message);
57            LOG(VB_GENERAL, CEC_LOG_DEBUG, LOC + QString("level %2 message %1").arg(msg).arg(message.level));
58
59//            int lvl = LOG_UNKNOWN;
60//            switch (message.level)
61//            {
62//            case CEC_LOG_ERROR:   lvl = LOG_ERR;     break;
63//            case CEC_LOG_WARNING: lvl = LOG_WARNING; break;
64//            case CEC_LOG_NOTICE:  lvl = LOG_INFO;    break;
65//            case CEC_LOG_DEBUG:   lvl = LOG_DEBUG;   break;
66//          case CEC_LOG_TRAFFIC:  lvl =  LOG_DEBUG; break;
67//            }
68//            LOG(VB_GENERAL, lvl, LOC + QString("%1").arg(msg));
69        return 1;
70     }
71
72    static int CEC_CDECL KeyPressCallback(void *Object, const cec_keypress Key)
73    {
74        LOG(VB_GENERAL, LOG_INFO, LOC + QString("KeyPressCallBack %1").arg(Key.keycode));
75
76        QString code;
77        int action = 0;
78        bool print = false;
79        switch (Key.keycode)
80        {
81            case CEC_USER_CONTROL_CODE_NUMBER0:
82                action = Qt::Key_0;
83                code = "0";
84                print = true;
85                break;
86            case CEC_USER_CONTROL_CODE_NUMBER1:
87                action = Qt::Key_1;
88                code = "1";
89                print = true;
90                break;
91            case CEC_USER_CONTROL_CODE_NUMBER2:
92                action = Qt::Key_2;
93                code = "2";
94                print = true;
95                break;
96            case CEC_USER_CONTROL_CODE_NUMBER3:
97                action = Qt::Key_3;
98                code = "3";
99                print = true;
100                break;
101            case CEC_USER_CONTROL_CODE_NUMBER4:
102                action = Qt::Key_4;
103                code = "4";
104                print = true;
105                break;
106            case CEC_USER_CONTROL_CODE_NUMBER5:
107                action = Qt::Key_5;
108                code = "5";
109                print = true;
110                break;
111            case CEC_USER_CONTROL_CODE_NUMBER6:
112                action = Qt::Key_6;
113                code = "6";
114                print = true;
115                break;
116            case CEC_USER_CONTROL_CODE_NUMBER7:
117                action = Qt::Key_7;
118                code = "7";
119                print = true;
120                break;
121            case CEC_USER_CONTROL_CODE_NUMBER8:
122                action = Qt::Key_8;
123                code = "8";
124                print = true;
125                break;
126            case CEC_USER_CONTROL_CODE_NUMBER9:
127                action = Qt::Key_9;
128                code = "9";
129                print = true;
130                break;
131            case CEC_USER_CONTROL_CODE_SELECT:
132                action = Qt::Key_Select;
133                code = "SELECT";
134                break;
135            case CEC_USER_CONTROL_CODE_ENTER:
136                action = Qt::Key_Enter;
137                code = "ENTER";
138                break;
139            case CEC_USER_CONTROL_CODE_UP:
140                action = Qt::Key_Up;
141                code = "UP";
142                break;
143            case CEC_USER_CONTROL_CODE_DOWN:
144                action = Qt::Key_Down;
145                code = "DOWN";
146                break;
147            case CEC_USER_CONTROL_CODE_LEFT:
148                action = Qt::Key_Left;
149                code = "LEFT";
150                break;
151            case CEC_USER_CONTROL_CODE_LEFT_UP:
152                action = Qt::Key_Left;
153                code = "LEFT_UP";
154                break;
155            case CEC_USER_CONTROL_CODE_LEFT_DOWN:
156                action = Qt::Key_Left;
157                code = "LEFT_DOWN";
158                break;
159            case CEC_USER_CONTROL_CODE_RIGHT:
160                action = Qt::Key_Right;
161                code = "RIGHT";
162                break;
163            case CEC_USER_CONTROL_CODE_RIGHT_UP:
164                action = Qt::Key_Right;
165                code = "RIGHT_UP";
166                break;
167            case CEC_USER_CONTROL_CODE_RIGHT_DOWN:
168                action = Qt::Key_Right;
169                code = "RIGHT_DOWN";
170                break;
171            case CEC_USER_CONTROL_CODE_ROOT_MENU:
172                action = Qt::Key_M;
173                code = "ROOT_MENU";
174                break;
175            case CEC_USER_CONTROL_CODE_EXIT:
176                action = Qt::Key_Escape;
177                code = "EXIT";
178                break;
179            case CEC_USER_CONTROL_CODE_PREVIOUS_CHANNEL:
180                action = Qt::Key_H;
181                code = "PREVIOUS_CHANNEL";
182                break;
183            case CEC_USER_CONTROL_CODE_SOUND_SELECT:
184                action = Qt::Key_Plus;
185                code = "SOUND_SELECT";
186                break;
187            case CEC_USER_CONTROL_CODE_VOLUME_UP:
188                action = Qt::Key_VolumeUp;
189                code = "VOLUME_UP";
190                break;
191            case CEC_USER_CONTROL_CODE_VOLUME_DOWN:
192                action = Qt::Key_VolumeDown;
193                code = "VOLUME_DOWN";
194                break;
195            case CEC_USER_CONTROL_CODE_MUTE:
196                action = Qt::Key_VolumeMute;
197                code = "MUTE";
198                break;
199            case CEC_USER_CONTROL_CODE_PLAY:
200                action = Qt::Key_P;
201                code = "PLAY";
202                break;
203            case CEC_USER_CONTROL_CODE_PAUSE:
204                action = Qt::Key_P; // same as play
205                code = "PAUSE";
206                break;
207            case CEC_USER_CONTROL_CODE_STOP:
208                action = Qt::Key_Stop;
209                code = "STOP";
210                break;
211            case CEC_USER_CONTROL_CODE_RECORD:
212                action = Qt::Key_R;
213                code = "RECORD";
214                break;
215            case CEC_USER_CONTROL_CODE_CLEAR:
216                action = Qt::Key_Clear;
217                code = "CLEAR";
218                break;
219            case CEC_USER_CONTROL_CODE_DISPLAY_INFORMATION:
220                action = Qt::Key_I;
221                code = "DISPLAY_INFORMATION";
222                break;
223            case CEC_USER_CONTROL_CODE_PAGE_UP:
224                action = Qt::Key_PageUp;
225                code = "PAGE_UP";
226                break;
227            case CEC_USER_CONTROL_CODE_PAGE_DOWN:
228                action = Qt::Key_PageDown;
229                code = "PAGE_DOWN";
230                break;
231            case CEC_USER_CONTROL_CODE_EJECT:
232                action = Qt::Key_Eject;
233                code = "EJECT";
234                break;
235            case CEC_USER_CONTROL_CODE_FORWARD:
236                action = Qt::Key_Forward;
237                code = "FORWARD";
238                break;
239            case CEC_USER_CONTROL_CODE_BACKWARD:
240                action = Qt::Key_Back;
241                code = "BACKWARD";
242                break;
243            case CEC_USER_CONTROL_CODE_F1_BLUE:
244                action = Qt::Key_F5; // NB F1 is help and we normally map blue to F5
245                code = "F1_BLUE";
246                break;
247            case CEC_USER_CONTROL_CODE_F2_RED:
248                action = Qt::Key_F2;
249                code = "F2_RED";
250                break;
251            case CEC_USER_CONTROL_CODE_F3_GREEN:
252                action = Qt::Key_F3;
253                code = "F3_GREEN";
254                break;
255            case CEC_USER_CONTROL_CODE_F4_YELLOW:
256                action = Qt::Key_F4;
257                code = "F4_YELLOW";
258                break;
259            case CEC_USER_CONTROL_CODE_SETUP_MENU:
260                action = Qt::Key_M; // Duplicate of Root Menu
261                code = "SETUP_MENU";
262                break;
263            case CEC_USER_CONTROL_CODE_CONTENTS_MENU:
264                action = Qt::Key_M; // Duplicate of Root Menu
265                code = "CONTENTS_MENU";
266                break;
267            case CEC_USER_CONTROL_CODE_FAVORITE_MENU:
268                action = Qt::Key_M; // Duplicate of Root Menu
269                code = "FAVORITE_MENU";
270                break;
271            case CEC_USER_CONTROL_CODE_DOT:
272                action = Qt::Key_Period;
273                print = true;
274                code = "DOT";
275                break;
276            case CEC_USER_CONTROL_CODE_NEXT_FAVORITE:
277                action = Qt::Key_Slash;
278                code = "NEXT_FAVORITE";
279                break;
280            case CEC_USER_CONTROL_CODE_INPUT_SELECT:
281                action = Qt::Key_C;
282                code = "INPUT_SELECT";
283                break;
284            case CEC_USER_CONTROL_CODE_HELP:
285                action = Qt::Key_F1;
286                code = "HELP";
287                break;
288            case CEC_USER_CONTROL_CODE_STOP_RECORD:
289                action = Qt::Key_R; // Duplicate of Record
290                code = "STOP_RECORD";
291                break;
292            case CEC_USER_CONTROL_CODE_SUB_PICTURE:
293                action = Qt::Key_V;
294                code = "SUB_PICTURE";
295                break;
296            case CEC_USER_CONTROL_CODE_ELECTRONIC_PROGRAM_GUIDE:
297                action = Qt::Key_S;
298                code = "ELECTRONIC_PROGRAM_GUIDE";
299                break;
300            case CEC_USER_CONTROL_CODE_POWER:
301                action = Qt::Key_PowerOff;
302                code = "POWER";
303                break;
304
305             // these codes have 'non-standard' Qt key mappings to ensure
306             // each code has a unique key mapping
307            case CEC_USER_CONTROL_CODE_CHANNEL_DOWN:
308                action = Qt::Key_F20; // to differentiate from Up
309                code = "CHANNEL_DOWN";
310                break;
311            case CEC_USER_CONTROL_CODE_CHANNEL_UP:
312                action = Qt::Key_F21; // to differentiate from Down
313                code = "CHANNEL_UP";
314                break;
315            case CEC_USER_CONTROL_CODE_REWIND:
316                action = Qt::Key_F22; // to differentiate from Left
317                code = "REWIND";
318                break;
319            case CEC_USER_CONTROL_CODE_FAST_FORWARD:
320                action = Qt::Key_F23; // to differentiate from Right
321                code = "FAST_FORWARD";
322                break;
323            case CEC_USER_CONTROL_CODE_ANGLE:
324                action = Qt::Key_F24;
325                code = "ANGLE";
326                break;
327            case CEC_USER_CONTROL_CODE_F5:
328                action = Qt::Key_F6; // NB!
329                code = "F5";
330                break;
331
332            // codes with no obvious MythTV action
333            case CEC_USER_CONTROL_CODE_INITIAL_CONFIGURATION:
334                code = "INITIAL_CONFIGURATION";
335                break;
336            case CEC_USER_CONTROL_CODE_PAUSE_RECORD:
337                code = "PAUSE_RECORD";
338                break;
339            case CEC_USER_CONTROL_CODE_VIDEO_ON_DEMAND:
340                code = "VIDEO_ON_DEMAND";
341                break;
342            case CEC_USER_CONTROL_CODE_TIMER_PROGRAMMING:
343                code = "TIMER_PROGRAMMING";
344                break;
345            case CEC_USER_CONTROL_CODE_UNKNOWN:
346                code = "UNKOWN";
347                break;
348            case CEC_USER_CONTROL_CODE_DATA:
349                code = "DATA";
350                break;
351
352            // Functions aren't implemented (similar to macros?)
353            case CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION:
354                code = "POWER_ON_FUNCTION";
355                break;
356            case CEC_USER_CONTROL_CODE_PLAY_FUNCTION:
357                code = "PLAY_FUNCTION";
358                break;
359            case CEC_USER_CONTROL_CODE_PAUSE_PLAY_FUNCTION:
360                code = "PAUSE_PLAY_FUNCTION";
361                break;
362            case CEC_USER_CONTROL_CODE_RECORD_FUNCTION:
363                code = "RECORD_FUNCTION";
364                break;
365            case CEC_USER_CONTROL_CODE_PAUSE_RECORD_FUNCTION:
366                code = "PAUSE_RECORD_FUNCTION";
367                break;
368            case CEC_USER_CONTROL_CODE_STOP_FUNCTION:
369                code = "STOP_FUNCTION";
370                break;
371            case CEC_USER_CONTROL_CODE_MUTE_FUNCTION:
372                code = "MUTE_FUNCTION";
373                break;
374            case CEC_USER_CONTROL_CODE_RESTORE_VOLUME_FUNCTION:
375                code = "RESTORE_VOLUME_FUNCTION";
376                break;
377            case CEC_USER_CONTROL_CODE_TUNE_FUNCTION:
378                code = "TUNE_FUNCTION";
379                break;
380            case CEC_USER_CONTROL_CODE_SELECT_MEDIA_FUNCTION:
381                code = "SELECT_MEDIA_FUNCTION";
382                break;
383            case CEC_USER_CONTROL_CODE_SELECT_AV_INPUT_FUNCTION:
384                code = "SELECT_AV_INPUT_FUNCTION";
385                break;
386            case CEC_USER_CONTROL_CODE_SELECT_AUDIO_INPUT_FUNCTION:
387                code = "SELECT_AUDIO_INPUT_FUNCTION";
388                break;
389            case CEC_USER_CONTROL_CODE_POWER_TOGGLE_FUNCTION:
390                code = "POWER_TOGGLE_FUNCTION";
391                break;
392            case CEC_USER_CONTROL_CODE_POWER_OFF_FUNCTION:
393                code = "POWER_OFF_FUNCTION";
394                break;
395            case CEC_USER_CONTROL_CODE_AN_RETURN:
396                code = "AN_RETURN";
397                break;
398            case CEC_USER_CONTROL_CODE_AN_CHANNELS_LIST:
399                code = "AN_CHANNELS_LIST";
400                break;
401        }
402
403        LOG(VB_GENERAL, LOG_INFO, LOC + QString("Keypress %1 %2")
404            .arg(code).arg(0 == action ? "(Not actioned)" : ""));
405
406        if (0 == action)
407            return 1;
408
409        QKeyEvent *keyevent = new QKeyEvent(
410                            Key.duration > 0 ? QEvent::KeyRelease : QEvent::KeyPress,
411                            action, Qt::NoModifier); //print ? TORC_KEYEVENT_PRINTABLE_MODIFIERS : TORC_KEYEVENT_MODIFIERS,
412                            //CEC_KEYPRESS_CONTEXT);
413        qApp->postEvent(GetMythMainWindow(), (QEvent*)keyevent);
414
415        //if (gLocalContext->GetUIObject())
416        //    QApplication::postEvent(gLocalContext->GetUIObject(), keyevent);
417
418        return 1;
419    }
420
421    static int CEC_CDECL CommandCallback(void *Object, const cec_command &command)
422    {
423        LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Command opcode 0x%1").arg(command.opcode,0,16));
424//            LOG(VB_GENERAL, LOG_DEBUG, LOC +
425//                QString("Command %1 from '%2' (%3) - destination '%4' (%5)")
426//                .arg(command.opcode)
427//                .arg(addressToString(command.initiator, true))
428//                .arg(command.initiator)
429//                .arg(addressToString(command.destination, false))
430//                .arg(command.destination));
431
432        switch (command.opcode)
433        {
434            // TODO
435            default:
436                break;
437        }
438
439        return 1;
440    }
441
442    static int CEC_CDECL ConfigCallback(void *Object, const libcec_configuration Config)
443    {
444        LOG(VB_GENERAL, LOG_INFO, LOC + "Adapter configuration changed.");
445        return 1;
446    }
447
448    static int CEC_CDECL AlertCallback(void *Object, const libcec_alert Alert, const libcec_parameter &CECParam)
449    {
450        LOG(VB_GENERAL, LOG_INFO, LOC + "AlertCallBack");
451        return 1;
452
453        switch (Alert)
454        {
455            case CEC_ALERT_CONNECTION_LOST:
456            case CEC_ALERT_PERMISSION_ERROR:
457            case CEC_ALERT_PORT_BUSY:
458                LOG(VB_GENERAL, LOG_WARNING, (const char*)CECParam.paramData);
459                break;
460            case CEC_ALERT_SERVICE_DEVICE: // already logged
461                break;
462            default:
463                break;
464        }
465
466        return 1;
467    }
468
469    static int CEC_CDECL MenuStateCallback(void *Object, const cec_menu_state State)
470    {
471        LOG(VB_GENERAL, LOG_INFO, LOC + QString("CEC menu state %1")
472            .arg(State == CEC_MENU_STATE_ACTIVATED ? "Activated" : "Deactivated"));
473        return 1;
474    }
475
476    static void CEC_CDECL SourceActivatedCallback(void *Object, const cec_logical_address Address, const uint8_t Activated)
477    {
478        LOG(VB_GENERAL, LOG_INFO, LOC + QString("Source %1 %2")
479            .arg(Address).arg(Activated ? "Activated" : "Deactivated"));
480    }
481
482    static QString addressToString(enum cec_logical_address addr, bool source)
483    {
484        switch (addr)
485        {
486            case CECDEVICE_UNKNOWN:          return QString("Unknown");
487            case CECDEVICE_TV:               return QString("TV");
488            case CECDEVICE_RECORDINGDEVICE1: return QString("RecordingDevice1");
489            case CECDEVICE_RECORDINGDEVICE2: return QString("RecordingDevice2");
490            case CECDEVICE_RECORDINGDEVICE3: return QString("RecordingDevice3");
491            case CECDEVICE_TUNER1:           return QString("Tuner1");
492            case CECDEVICE_TUNER2:           return QString("Tuner2");
493            case CECDEVICE_TUNER3:           return QString("Tuner3");
494            case CECDEVICE_TUNER4:           return QString("Tuner4");
495            case CECDEVICE_PLAYBACKDEVICE1:  return QString("PlaybackDevice1");
496            case CECDEVICE_PLAYBACKDEVICE2:  return QString("PlaybackDevice2");
497            case CECDEVICE_PLAYBACKDEVICE3:  return QString("PlaybackDevice3");
498            case CECDEVICE_AUDIOSYSTEM:      return QString("Audiosystem");
499            case CECDEVICE_RESERVED1:        return QString("Reserved1");
500            case CECDEVICE_RESERVED2:        return QString("Reserved2");
501            case CECDEVICE_FREEUSE:          return QString("FreeUse");
502            case CECDEVICE_UNREGISTERED:
503            //case CECDEVICE_BROADCAST:
504                return source ? QString("Unregistered") : QString("Broadcast");
505        }
506        return QString("Invalid");
507    }
508
509    // N.B. This may need revisiting when the UI is added
510    static QStringList GetDeviceList(void)
511    {
512        QStringList results;
513        libcec_configuration config;
514        config.Clear();
515        //cec_device_type_list list;
516        //list.Clear();
517        //list.Add(CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
518        //ICECAdapter *adapter = LibCecInit("MythTV", list);
519        ICECAdapter *adapter = LibCecInitialise(&config);
520        if (!adapter)
521            return results;
522        cec_adapter *devices = new cec_adapter[MAX_CEC_DEVICES];
523        uint8_t num_devices = adapter->FindAdapters(devices, MAX_CEC_DEVICES, NULL);
524        if (num_devices < 1)
525            return results;
526        for (uint8_t i = 0; i < num_devices; i++)
527            results << QString::fromAscii(devices[i].comm);
528        UnloadLibCec(adapter);
529        return results;
530    }
531
532    bool Open(void)
533    {
534        // if already opened return...
535        if (adapter)
536            return true;
537
538        // get settings
539        // N.B. these do not currently work as there is no UI
540        defaultDevice     = gCoreContext->GetSetting(LIBCEC_DEVICE, "auto").trimmed();
541        QString hdmi_port = gCoreContext->GetSetting(LIBCEC_PORT, "auto");
542        QString device_id = gCoreContext->GetSetting(LIBCEC_DEVICEID, "auto");
543        powerOffTVAllowed = (bool)gCoreContext->GetNumSetting(POWEROFFTV_ALLOWED, 1);
544        powerOffTVOnExit  = (bool)gCoreContext->GetNumSetting(POWEROFFTV_ONEXIT, 1);
545        powerOnTVAllowed  = (bool)gCoreContext->GetNumSetting(POWERONTV_ALLOWED, 1);
546        powerOnTVOnStart  = (bool)gCoreContext->GetNumSetting(POWERONTV_ONSTART, 1);
547
548        defaultHDMIPort = 1;
549        if ("auto" != hdmi_port)
550        {
551            defaultHDMIPort = hdmi_port.toInt();
552            if (defaultHDMIPort < 1 || defaultHDMIPort > 3)
553                defaultHDMIPort = 1;
554        }
555        defaultHDMIPort = defaultHDMIPort << 12;
556
557        defaultDeviceID = CECDEVICE_PLAYBACKDEVICE1;
558        if ("auto" != device_id)
559        {
560            int id = device_id.toInt();
561            if (id < 1 || id > 3)
562                id = 1;
563            defaultDeviceID = (id == 1) ? CECDEVICE_PLAYBACKDEVICE1 :
564                             ((id == 2) ? CECDEVICE_PLAYBACKDEVICE2 :
565                                          CECDEVICE_PLAYBACKDEVICE3);
566        }
567
568        // create adapter interface
569        libcec_configuration config;
570        config.Clear();
571        config.clientVersion = CEC_CONFIG_VERSION;
572
573        // Set the On Screen Display Name  - TODO - use Hostname?
574        //int length = sizeof(OSDNAME);
575        //if (length > 13)
576        //    length = 13;
577        //memcpy(config.strDeviceName, OSDNAME, length);
578        snprintf(config.strDeviceName, 13, "MythTv");
579
580        // Set the Config Device Type
581        config.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
582
583        // CURRENTLY HARDCODED TO PORT 2 OF THE AV RECEIVER
584        config.iPhysicalAddress = (quint16) 0x1100; //detectedphysicaladdress ? detectedphysicaladdress : m_defaultPhysicalAddress;
585        config.baseDevice = (cec_logical_address) 5;//m_defaultBaseDevice;
586        config.iHDMIPort = (quint8) 1;//m_defaultHDMIPort;
587
588        //config.tvVendor
589        config.wakeDevices.Set(CECDEVICE_TV);//m_powerOnTVOnStart ? CECDEVICE_TV : CECDEVICE_UNKNOWN);
590        config.powerOffDevices.Set(CECDEVICE_TV);//m_powerOffTVOnExit ? CECDEVICE_TV : CECDEVICE_UNKNOWN);
591        //config.serverVersion
592
593        //config.bGetSettingsFromROM
594        //config.bUseTVMenuLanguage
595        config.bActivateSource = (int)true;//m_makeActiveSource;
596        //config.bPowerOffScreensaver
597        //config.bPowerOffOnStandby
598        config.bSendInactiveSource = (int)true;//m_sendInactiveSource;
599
600        config.callbackParam = (void*)this;
601        config.callbacks = &m_callbacks;
602
603//      config.Clear();
604//      config.bGetSettingsFromROM = 1;
605//        config.bGetSettingsFromROM = 0;
606        adapter = LibCecInitialise(&config);
607
608//      adapter->EnableCallbacks(NULL, NULL);
609
610        //cec_device_type_list list;
611        //list.Clear();
612        //list.Add(CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
613        //adapter = LibCecInit("MythTV", list);
614
615        if (!adapter)
616        {
617            LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to load libcec.");
618            return false;
619        }
620
621        // find adapters
622        cec_adapter *devices = new cec_adapter[MAX_CEC_DEVICES];
623        uint8_t num_devices = adapter->FindAdapters(devices, MAX_CEC_DEVICES, NULL);
624        if (num_devices < 1)
625        {
626            LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to find any CEC devices.");
627            return false;
628        }
629
630        // find required device and debug
631        int devicenum = 0;
632        bool find = defaultDevice != "auto";
633
634        LOG(VB_GENERAL, LOG_INFO, LOC + QString("Found %1 CEC devices(s).")
635            .arg(num_devices));
636        for (uint8_t i = 0; i < num_devices; i++)
637        {
638            QString comm = QString::fromAscii(devices[i].comm);
639            bool match = find ? (comm == defaultDevice) : (i == 0);
640            devicenum = match ? i : devicenum;
641            LOG(VB_GENERAL, LOG_INFO, LOC +
642                QString("Device %1: path '%2' com port '%3' %4").arg(i + 1)
643                .arg(QString::fromAscii(devices[i].path)).arg(comm)
644                .arg(match ? "SELECTED" : ""));
645        }
646
647        // open adapter
648        QString path = QString::fromAscii(devices[devicenum].path);
649        QString comm = QString::fromAscii(devices[devicenum].comm);
650        LOG(VB_GENERAL, LOG_INFO, LOC + QString("Trying to open device %1 (%2).")
651            .arg(path).arg(comm));
652
653        adapter->InitVideoStandalone();
654
655        if (!adapter->Open(devices[devicenum].comm))
656        {
657            LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to open device.");
658            return false;
659        }
660
661        LOG(VB_GENERAL, LOG_INFO, LOC + "Opened CEC device.");
662        cec_deck_info deckinfo;
663        adapter->SetDeckInfo(deckinfo, true);
664
665        // turn on tv (if configured)
666        powerOnTV = powerOnTVOnStart;
667        HandleActions();
668
669        // get the vendor ID (for non-standard implementations)
670        adapter->GetDeviceVendorId(CECDEVICE_TV);
671
672        // set the physical address
673//        adapter->SetPhysicalAddress(defaultHDMIPort);
674
675        // set the logical address
676//        adapter->SetLogicalAddress(defaultDeviceID);
677
678        // switch input (if configured)
679        switchInput = true;
680        HandleActions();
681
682        delete [] devices;
683        valid = true;
684        return true;
685    }
686
687
688    void Close(void)
689    {
690        if (adapter)
691        {
692            // turn off tv (if configured)
693            powerOffTV = powerOffTVOnExit;
694            HandleActions();
695
696            // delete adapter
697            adapter->Close();
698            UnloadLibCec(adapter);
699
700            LOG(VB_GENERAL, LOG_INFO, LOC + "Closing down CEC.");
701        }
702        valid = false;
703        adapter = NULL;
704    }
705
706//    void LogMessages(void)
707//    {
708//        if (!adapter || !valid)
709//            return;
710//
711//        cec_log_message message;
712//        while (adapter->GetNextLogMessage(&message))
713//        {
714//            QString msg(message.message);
715//            int lvl = LOG_UNKNOWN;
716//            switch (message.level)
717//            {
718//            case CEC_LOG_ERROR:   lvl = LOG_ERR;     break;
719//            case CEC_LOG_WARNING: lvl = LOG_WARNING; break;
720//            case CEC_LOG_NOTICE:  lvl = LOG_INFO;    break;
721//            case CEC_LOG_DEBUG:   lvl = LOG_DEBUG;   break;
722//            }
723//            LOG(VB_GENERAL, lvl, LOC + QString("%1").arg(msg));
724//        }
725//    }
726
727    void HandleCommands(void)
728    {
729        if (!adapter || !valid)
730            return;
731
732//        LogMessages();
733
734//        cec_command command;
735//        while (adapter->GetNextCommand(&command))
736//        {
737//            LOG(VB_GENERAL, LOG_DEBUG, LOC +
738//                QString("Command %1 from '%2' (%3) - destination '%4' (%5)")
739//                .arg(command.opcode)
740//                .arg(addressToString(command.initiator, true))
741//                .arg(command.initiator)
742//                .arg(addressToString(command.destination, false))
743//                .arg(command.destination));
744//
745//            switch (command.opcode)
746//            {
747//                // TODO
748//                default:
749//                    break;
750//            }
751//        }
752//
753//        LogMessages();
754    }
755
756    void HandleKeyPresses(void)
757    {
758        if (!adapter || !valid)
759            return;
760
761        cec_keypress key;
762        //if (!adapter->GetNextKeypress(&key))
763        //    return;
764
765        // Ignore key down events and wait for the key 'up'
766        if (key.duration < 1)
767            return;
768
769        QString code;
770        int action = 0;
771        switch (key.keycode)
772        {
773            case CEC_USER_CONTROL_CODE_NUMBER0:
774                action = Qt::Key_0;
775                code   = "0";
776                break;
777            case CEC_USER_CONTROL_CODE_NUMBER1:
778                action = Qt::Key_1;
779                code   = "1";
780                break;
781            case CEC_USER_CONTROL_CODE_NUMBER2:
782                action = Qt::Key_2;
783                code   = "2";
784                break;
785            case CEC_USER_CONTROL_CODE_NUMBER3:
786                action = Qt::Key_3;
787                code   = "3";
788                break;
789            case CEC_USER_CONTROL_CODE_NUMBER4:
790                action = Qt::Key_4;
791                code   = "4";
792                break;
793            case CEC_USER_CONTROL_CODE_NUMBER5:
794                action = Qt::Key_5;
795                code   = "5";
796                break;
797            case CEC_USER_CONTROL_CODE_NUMBER6:
798                action = Qt::Key_6;
799                code   = "6";
800                break;
801            case CEC_USER_CONTROL_CODE_NUMBER7:
802                action = Qt::Key_7;
803                code   = "7";
804                break;
805            case CEC_USER_CONTROL_CODE_NUMBER8:
806                action = Qt::Key_8;
807                code   = "8";
808                break;
809            case CEC_USER_CONTROL_CODE_NUMBER9:
810                action = Qt::Key_9;
811                code   = "9";
812                break;
813            case CEC_USER_CONTROL_CODE_SELECT:
814                action = Qt::Key_Select;
815                code   = "SELECT";
816                break;
817            case CEC_USER_CONTROL_CODE_ENTER:
818                action = Qt::Key_Enter;
819                code   = "ENTER";
820                break;
821            case CEC_USER_CONTROL_CODE_UP:
822                action = Qt::Key_Up;
823                code   = "UP";
824                break;
825            case CEC_USER_CONTROL_CODE_DOWN:
826                action = Qt::Key_Down;
827                code   = "DOWN";
828                break;
829            case CEC_USER_CONTROL_CODE_LEFT:
830                action = Qt::Key_Left;
831                code   = "LEFT";
832                break;
833            case CEC_USER_CONTROL_CODE_LEFT_UP:
834                action = Qt::Key_Left;
835                code   = "LEFT_UP";
836                break;
837            case CEC_USER_CONTROL_CODE_LEFT_DOWN:
838                action = Qt::Key_Left;
839                code   = "LEFT_DOWN";
840                break;
841            case CEC_USER_CONTROL_CODE_RIGHT:
842                action = Qt::Key_Right;
843                code   = "RIGHT";
844                break;
845            case CEC_USER_CONTROL_CODE_RIGHT_UP:
846                action = Qt::Key_Right;
847                code   = "RIGHT_UP";
848                break;
849            case CEC_USER_CONTROL_CODE_RIGHT_DOWN:
850                action = Qt::Key_Right;
851                code   = "RIGHT_DOWN";
852                break;
853            case CEC_USER_CONTROL_CODE_ROOT_MENU:
854                action = Qt::Key_M;
855                code   = "ROOT_MENU";
856                break;
857            case CEC_USER_CONTROL_CODE_EXIT:
858                action = Qt::Key_Escape;
859                code   = "EXIT";
860                break;
861            case CEC_USER_CONTROL_CODE_PREVIOUS_CHANNEL:
862                action = Qt::Key_H;
863                code   = "PREVIOUS_CHANNEL";
864                break;
865            case CEC_USER_CONTROL_CODE_SOUND_SELECT:
866                action = Qt::Key_Plus;
867                code   = "SOUND_SELECT";
868                break;
869            case CEC_USER_CONTROL_CODE_VOLUME_UP:
870                action = Qt::Key_VolumeUp;
871                code   = "VOLUME_UP";
872                break;
873            case CEC_USER_CONTROL_CODE_VOLUME_DOWN:
874                action = Qt::Key_VolumeDown;
875                code   = "VOLUME_DOWN";
876                break;
877            case CEC_USER_CONTROL_CODE_MUTE:
878                action = Qt::Key_VolumeMute;
879                code   = "MUTE";
880                break;
881            case CEC_USER_CONTROL_CODE_PLAY:
882                action = Qt::Key_P;
883                code   = "PLAY";
884                break;
885            case CEC_USER_CONTROL_CODE_PAUSE:
886                action = Qt::Key_P; // same as play
887                code   = "PAUSE";
888                break;
889            case CEC_USER_CONTROL_CODE_STOP:
890                action = Qt::Key_Stop;
891                code   = "STOP";
892                break;
893            case CEC_USER_CONTROL_CODE_RECORD:
894                action = Qt::Key_R;
895                code   = "RECORD";
896                break;
897            case CEC_USER_CONTROL_CODE_CLEAR:
898                action = Qt::Key_Clear;
899                code   = "CLEAR";
900                break;
901            case CEC_USER_CONTROL_CODE_DISPLAY_INFORMATION:
902                action = Qt::Key_I;
903                code   = "DISPLAY_INFORMATION";
904                break;
905            case CEC_USER_CONTROL_CODE_PAGE_UP:
906                action = Qt::Key_PageUp;
907                code   = "PAGE_UP";
908                break;
909            case CEC_USER_CONTROL_CODE_PAGE_DOWN:
910                action = Qt::Key_PageDown;
911                code   = "PAGE_DOWN";
912                break;
913            case CEC_USER_CONTROL_CODE_EJECT:
914                action = Qt::Key_Eject;
915                code   = "EJECT";
916                break;
917            case CEC_USER_CONTROL_CODE_FORWARD:
918                action = Qt::Key_Forward;
919                code   = "FORWARD";
920                break;
921            case CEC_USER_CONTROL_CODE_BACKWARD:
922                action = Qt::Key_Back;
923                code   = "BACKWARD";
924                break;
925            case CEC_USER_CONTROL_CODE_F1_BLUE:
926                action = Qt::Key_F5; // NB F1 is help and we normally map blue to F5
927                code   = "F1_BLUE";
928                break;
929            case CEC_USER_CONTROL_CODE_F2_RED:
930                action = Qt::Key_F2;
931                code   = "F2_RED";
932                break;
933            case CEC_USER_CONTROL_CODE_F3_GREEN:
934                action = Qt::Key_F3;
935                code   = "F3_GREEN";
936                break;
937            case CEC_USER_CONTROL_CODE_F4_YELLOW:
938                action = Qt::Key_F4;
939                code   = "F4_YELLOW";
940                break;
941            case CEC_USER_CONTROL_CODE_SETUP_MENU:
942                action = Qt::Key_M; // Duplicate of Root Menu
943                code   = "SETUP_MENU";
944                break;
945            case CEC_USER_CONTROL_CODE_CONTENTS_MENU:
946                action = Qt::Key_M; // Duplicate of Root Menu
947                code   = "CONTENTS_MENU";
948                break;
949            case CEC_USER_CONTROL_CODE_FAVORITE_MENU:
950                action = Qt::Key_M; // Duplicate of Root Menu
951                code   = "FAVORITE_MENU";
952                break;
953            case CEC_USER_CONTROL_CODE_DOT:
954                action = Qt::Key_Period;
955                code  = "DOT";
956                break;
957            case CEC_USER_CONTROL_CODE_NEXT_FAVORITE:
958                action = Qt::Key_Slash;
959                code   = "NEXT_FAVORITE";
960                break;
961            case CEC_USER_CONTROL_CODE_INPUT_SELECT:
962                action = Qt::Key_C;
963                code = "INPUT_SELECT";
964                break;
965            case CEC_USER_CONTROL_CODE_HELP:
966                action = Qt::Key_F1;
967                code   = "HELP";
968                break;
969            case CEC_USER_CONTROL_CODE_STOP_RECORD:
970                action = Qt::Key_R; // Duplicate of Record
971                code = "STOP_RECORD";
972                break;
973            case CEC_USER_CONTROL_CODE_SUB_PICTURE:
974                action = Qt::Key_V;
975                code   = "SUB_PICTURE";
976                break;
977            case CEC_USER_CONTROL_CODE_ELECTRONIC_PROGRAM_GUIDE:
978                action = Qt::Key_S;
979                code   = "ELECTRONIC_PROGRAM_GUIDE";
980                break;
981            case CEC_USER_CONTROL_CODE_POWER:
982                action = Qt::Key_PowerOff;
983                code = "POWER";
984                break;
985
986             // these codes have 'non-standard' Qt key mappings to ensure
987             // each code has a unique key mapping
988            case CEC_USER_CONTROL_CODE_CHANNEL_DOWN:
989                action = Qt::Key_F20; // to differentiate from Up
990                code   = "CHANNEL_DOWN";
991                break;
992            case CEC_USER_CONTROL_CODE_CHANNEL_UP:
993                action = Qt::Key_F21; // to differentiate from Down
994                code   = "CHANNEL_UP";
995                break;
996            case CEC_USER_CONTROL_CODE_REWIND:
997                action = Qt::Key_F22; // to differentiate from Left
998                code   = "REWIND";
999                break;
1000            case CEC_USER_CONTROL_CODE_FAST_FORWARD:
1001                action = Qt::Key_F23; // to differentiate from Right
1002                code   = "FAST_FORWARD";
1003                break;
1004            case CEC_USER_CONTROL_CODE_ANGLE:
1005                action = Qt::Key_F24;
1006                code = "ANGLE";
1007                break;
1008            case CEC_USER_CONTROL_CODE_F5:
1009                action = Qt::Key_F6; // NB!
1010                code = "F5";
1011                break;
1012
1013            // codes with no obvious MythTV action
1014            case CEC_USER_CONTROL_CODE_INITIAL_CONFIGURATION:
1015                code = "INITIAL_CONFIGURATION";
1016                break;
1017            case CEC_USER_CONTROL_CODE_PAUSE_RECORD:
1018                code = "PAUSE_RECORD";
1019                break;
1020            case CEC_USER_CONTROL_CODE_VIDEO_ON_DEMAND:
1021                code = "VIDEO_ON_DEMAND";
1022                break;
1023            case CEC_USER_CONTROL_CODE_TIMER_PROGRAMMING:
1024                code = "TIMER_PROGRAMMING";
1025                break;
1026            case CEC_USER_CONTROL_CODE_UNKNOWN:
1027                code = "UNKOWN";
1028                break;
1029            case CEC_USER_CONTROL_CODE_DATA:
1030                code = "DATA";
1031                break;
1032
1033            // Functions aren't implemented (similar to macros?)
1034            case CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION:
1035                code = "POWER_ON_FUNCTION";
1036                break;
1037            case CEC_USER_CONTROL_CODE_PLAY_FUNCTION:
1038                code = "PLAY_FUNCTION";
1039                break;
1040            case CEC_USER_CONTROL_CODE_PAUSE_PLAY_FUNCTION:
1041                code = "PAUSE_PLAY_FUNCTION";
1042                break;
1043            case CEC_USER_CONTROL_CODE_RECORD_FUNCTION:
1044                code = "RECORD_FUNCTION";
1045                break;
1046            case CEC_USER_CONTROL_CODE_PAUSE_RECORD_FUNCTION:
1047                code = "PAUSE_RECORD_FUNCTION";
1048                break;
1049            case CEC_USER_CONTROL_CODE_STOP_FUNCTION:
1050                code = "STOP_FUNCTION";
1051                break;
1052            case CEC_USER_CONTROL_CODE_MUTE_FUNCTION:
1053                code = "MUTE_FUNCTION";
1054                break;
1055            case CEC_USER_CONTROL_CODE_RESTORE_VOLUME_FUNCTION:
1056                code = "RESTORE_VOLUME_FUNCTION";
1057                break;
1058            case CEC_USER_CONTROL_CODE_TUNE_FUNCTION:
1059                code = "TUNE_FUNCTION";
1060                break;
1061            case CEC_USER_CONTROL_CODE_SELECT_MEDIA_FUNCTION:
1062                code = "SELECT_MEDIA_FUNCTION";
1063                break;
1064            case CEC_USER_CONTROL_CODE_SELECT_AV_INPUT_FUNCTION:
1065                code = "SELECT_AV_INPUT_FUNCTION";
1066                break;
1067            case CEC_USER_CONTROL_CODE_SELECT_AUDIO_INPUT_FUNCTION:
1068                code = "SELECT_AUDIO_INPUT_FUNCTION";
1069                break;
1070            case CEC_USER_CONTROL_CODE_POWER_TOGGLE_FUNCTION:
1071                code = "POWER_TOGGLE_FUNCTION";
1072                break;
1073            case CEC_USER_CONTROL_CODE_POWER_OFF_FUNCTION:
1074                code = "POWER_OFF_FUNCTION";
1075                break;
1076        }
1077
1078        LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Keypress %1 %2")
1079            .arg(code).arg(0 == action ? "(Not actioned)" : ""));
1080
1081        if (0 == action)
1082            return;
1083
1084        GetMythUI()->ResetScreensaver();
1085        QKeyEvent* ke = new QKeyEvent(QEvent::KeyPress, action, Qt::NoModifier);
1086        qApp->postEvent(GetMythMainWindow(), (QEvent*)ke);
1087
1088    }
1089
1090    void HandleActions(void)
1091    {
1092        if (!adapter || !valid)
1093            return;
1094
1095        // power state
1096        if (powerOffTV && powerOffTVAllowed)
1097        {
1098            if (adapter->StandbyDevices(CECDEVICE_TV))
1099                LOG(VB_GENERAL, LOG_INFO, LOC + "Asked TV to turn off.");
1100            else
1101                LOG(VB_GENERAL, LOG_ERR,  LOC + "Failed to turn TV off.");
1102        }
1103
1104        if (powerOnTV && powerOnTVAllowed)
1105        {
1106            if (adapter->PowerOnDevices(CECDEVICE_TV))
1107                LOG(VB_GENERAL, LOG_INFO, LOC + "Asked TV to turn on.");
1108            else
1109                LOG(VB_GENERAL, LOG_ERR,  LOC + "Failed to turn TV on.");
1110        }
1111
1112        // HDMI input
1113        if (switchInput && switchInputAllowed)
1114        {
1115            if (adapter->SetActiveSource())
1116                LOG(VB_GENERAL, LOG_INFO, LOC + "Asked TV to switch to this source.");
1117            else
1118                LOG(VB_GENERAL, LOG_ERR,  LOC + "Failed to switch to this source.");
1119        }
1120
1121        powerOffTV  = false;
1122        powerOnTV   = false;
1123        switchInput = false;
1124
1125//        LogMessages();
1126    }
1127
1128    ICECAdapter *adapter;
1129    ICECCallbacks m_callbacks;
1130    QString      defaultDevice;
1131    int          defaultHDMIPort;
1132    cec_logical_address defaultDeviceID;
1133    QTimer      *timer;
1134    bool         valid;
1135    bool         powerOffTV;
1136    bool         powerOffTVAllowed;
1137    bool         powerOffTVOnExit;
1138    bool         powerOnTV;
1139    bool         powerOnTVAllowed;
1140    bool         powerOnTVOnStart;
1141    bool         switchInput;
1142    bool         switchInputAllowed;
1143};
1144
1145QStringList CECAdapter::GetDeviceList(void)
1146{
1147    QMutexLocker lock(gLock);
1148    return CECAdapterPriv::GetDeviceList();
1149}
1150
1151CECAdapter::CECAdapter() : MThread("CECAdapter"), m_priv(new CECAdapterPriv)
1152{
1153    QMutexLocker lock(gLock);
1154
1155    // don't try if disabled
1156    if (!gCoreContext->GetNumSetting(LIBCEC_ENABLED, 1))
1157    {
1158        LOG(VB_GENERAL, LOG_INFO, LOC + "libCEC support is disabled.");
1159        return;
1160    }
1161
1162    // create the actual adapter
1163    if (!m_priv->Open())
1164        return;
1165
1166    // create process timer
1167    m_priv->timer = new QTimer(this);
1168    QObject::connect(m_priv->timer, SIGNAL(timeout()), this, SLOT(Process()));
1169    m_priv->timer->start(10);
1170
1171    // start thread
1172    LOG(VB_GENERAL, LOG_DEBUG, LOC + "Starting thread.");
1173    start();
1174}
1175
1176CECAdapter::~CECAdapter()
1177{
1178    QMutexLocker lock(gLock);
1179
1180    // delete process timer
1181    if (m_priv->timer)
1182        m_priv->timer->stop();
1183    delete m_priv->timer;
1184    m_priv->timer = NULL;
1185
1186    // stop thread
1187    if (isRunning())
1188    {
1189        LOG(VB_GENERAL, LOG_DEBUG, LOC + "Stopping thread.");
1190        exit();
1191    }
1192
1193    // delete the actual adapter
1194    m_priv->Close();
1195}
1196
1197bool CECAdapter::IsValid(void)
1198{
1199    QMutexLocker lock(gLock);
1200    return m_priv->valid;
1201}
1202
1203void CECAdapter::Action(const QString &action)
1204{
1205    QMutexLocker lock(gLock);
1206    if (ACTION_TVPOWERON == action)
1207        m_priv->powerOnTV = true;
1208    else if (ACTION_TVPOWEROFF == action)
1209        m_priv->powerOffTV = true;
1210}
1211
1212void CECAdapter::Process(void)
1213{
1214    gLock->lock();
1215//    m_priv->HandleCommands();
1216//    m_priv->HandleKeyPresses();
1217//    m_priv->HandleActions();
1218    gLock->unlock();
1219}