diff --git a/mythtv/libs/libmythui/cecadapter.cpp b/mythtv/libs/libmythui/cecadapter.cpp
index 00a8c73..06a8f3d 100644
--- a/mythtv/libs/libmythui/cecadapter.cpp
+++ b/mythtv/libs/libmythui/cecadapter.cpp
@@ -33,17 +33,31 @@ QWaitCondition* CECAdapter::gActionsReady = new QWaitCondition();
 // libcec1's callback parameters are pass-by-ref
 #define CEC_CALLBACK_PARAM_TYPE &
 #else
-// libcec2's callback parameters are pass-by-value
+#if CEC_LIB_VERSION_MAJOR <= 3
+// libcec2 and 3 callback parameters are pass-by-value
 #define CEC_CALLBACK_PARAM_TYPE
 #endif
+#endif
 
 // The libCEC callback functions
+#if CEC_LIB_VERSION_MAJOR <= 3
 static int CECLogMessageCallback(void *adapter, const cec_log_message CEC_CALLBACK_PARAM_TYPE message);
 static int CECKeyPressCallback(void *adapter, const cec_keypress CEC_CALLBACK_PARAM_TYPE keypress);
 static int CECCommandCallback(void *adapter, const cec_command CEC_CALLBACK_PARAM_TYPE command);
+#endif
+#if CEC_LIB_VERSION_MAJOR >= 4
+static void CECLogMessageCallback(void *adapter, const cec_log_message* message);
+static void CECKeyPressCallback(void *adapter, const cec_keypress* keypress);
+static void CECCommandCallback(void *adapter, const cec_command* command);
+#endif
 
 #if CEC_LIB_VERSION_MAJOR >= 2
+#if CEC_LIB_VERSION_MAJOR <= 3
 static int CECAlertCallback(void *adapter, const libcec_alert alert, const libcec_parameter CEC_CALLBACK_PARAM_TYPE data);
+#endif
+#if CEC_LIB_VERSION_MAJOR >= 4
+static void CECAlertCallback(void *adapter, const libcec_alert alert, const libcec_parameter data);
+#endif
 static void CECSourceActivatedCallback(void *adapter, const cec_logical_address address, const uint8_t activated);
 #endif
 
@@ -107,13 +121,24 @@ class CECAdapterPriv
         }
 
         // Set up the callbacks
+#if CEC_LIB_VERSION_MAJOR <= 3
         callbacks.CBCecLogMessage = &CECLogMessageCallback;
         callbacks.CBCecKeyPress   = &CECKeyPressCallback;
         callbacks.CBCecCommand    = &CECCommandCallback;
-#if CEC_LIB_VERSION_MAJOR >= 2
+#endif
+#if CEC_LIB_VERSION_MAJOR >= 4
+        callbacks.logMessage      = &CECLogMessageCallback;
+        callbacks.keyPress        = &CECKeyPressCallback;
+        callbacks.commandReceived = &CECCommandCallback;
+#endif
+#if CEC_LIB_VERSION_MAJOR >= 2 && CEC_LIB_VERSION_MAJOR <= 3
         callbacks.CBCecAlert      = &CECAlertCallback;
         callbacks.CBCecSourceActivated = &CECSourceActivatedCallback;
 #endif
+#if CEC_LIB_VERSION_MAJOR >= 4
+        callbacks.alert           = &CECAlertCallback;
+        callbacks.sourceActivated = &CECSourceActivatedCallback;
+#endif
         configuration.callbackParam = this;
         configuration.callbacks = &callbacks;
 
@@ -127,8 +152,13 @@ class CECAdapterPriv
         }
 
         // find adapters
+#if CEC_LIB_VERSION_MAJOR >= 4
+        cec_adapter_descriptor *devices = new cec_adapter_descriptor[MAX_CEC_DEVICES];
+        uint8_t num_devices = adapter->DetectAdapters(devices, MAX_CEC_DEVICES, NULL, true);
+#else
         cec_adapter *devices = new cec_adapter[MAX_CEC_DEVICES];
         uint8_t num_devices = adapter->FindAdapters(devices, MAX_CEC_DEVICES, NULL);
+#endif
         if (num_devices < 1)
         {
             LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to find any CEC devices.");
@@ -143,22 +173,37 @@ class CECAdapterPriv
             .arg(num_devices));
         for (uint8_t i = 0; i < num_devices; i++)
         {
+#if CEC_LIB_VERSION_MAJOR >= 4
+            QString comm = QString::fromLatin1(devices[i].strComName);
+            QString path = QString::fromLatin1(devices[i].strComPath);
+#else
             QString comm = QString::fromLatin1(devices[i].comm);
+            QString path = QString::fromLatin1(devices[i].path);
+#endif
             bool match = find ? (comm == defaultDevice) : (i == 0);
             devicenum = match ? i : devicenum;
             LOG(VB_GENERAL, LOG_INFO, LOC +
                 QString("Device %1: path '%2' com port '%3' %4").arg(i + 1)
-                .arg(QString::fromLatin1(devices[i].path)).arg(comm)
+                .arg(path).arg(comm)
                 .arg(match ? "SELECTED" : ""));
         }
 
         // open adapter
-        QString path = QString::fromLatin1(devices[devicenum].path);
+#if CEC_LIB_VERSION_MAJOR >= 4
+        QString comm = QString::fromLatin1(devices[devicenum].strComName);
+        QString path = QString::fromLatin1(devices[devicenum].strComPath);
+#else
         QString comm = QString::fromLatin1(devices[devicenum].comm);
+        QString path = QString::fromLatin1(devices[devicenum].path);
+#endif
         LOG(VB_GENERAL, LOG_INFO, LOC + QString("Trying to open device %1 (%2).")
             .arg(path).arg(comm));
 
+#if CEC_LIB_VERSION_MAJOR >= 4
+        if (!adapter->Open(devices[devicenum].strComName))
+#else
         if (!adapter->Open(devices[devicenum].comm))
+#endif
         {
             LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to open device.");
             return false;
@@ -213,6 +258,20 @@ class CECAdapterPriv
         return 1;
     }
 
+    void LogMessage(const cec_log_message* message)
+    {
+        QString msg(message->message);
+        int lvl = LOG_UNKNOWN;
+        switch (message->level)
+        {
+            case CEC_LOG_ERROR:   lvl = LOG_ERR;     break;
+            case CEC_LOG_WARNING: lvl = LOG_WARNING; break;
+            case CEC_LOG_NOTICE:  lvl = LOG_INFO;    break;
+            case CEC_LOG_DEBUG:   lvl = LOG_DEBUG;   break;
+        }
+        LOG(VB_GENERAL, lvl, LOC + QString("%1").arg(msg));
+    }
+
     // NOTE - libcec2 changes the callbacks
     // to be pass-by-value.
     // For simplicity, this function remains as pass-by-ref
@@ -241,6 +300,29 @@ class CECAdapterPriv
         return 1;
     }
 
+    void HandleCommand(const cec_command* command)
+    {
+        if (!adapter || !valid)
+            return;
+
+        LOG(VB_GENERAL, LOG_DEBUG, LOC +
+            QString("Command %1 from '%2' (%3) - destination '%4' (%5)")
+            .arg(command->opcode)
+            .arg(adapter->ToString(command->initiator))
+            .arg(command->initiator)
+            .arg(adapter->ToString(command->destination))
+            .arg(command->destination));
+
+        switch (command->opcode)
+        {
+            // TODO
+            default:
+                break;
+        }
+        gCoreContext->SendSystemEvent(QString("CEC_COMMAND_RECEIVED COMMAND %1")
+                                      .arg(command->opcode));
+    }
+
     int HandleKeyPress(const cec_keypress &key)
     {
         if (!adapter || !valid)
@@ -572,6 +654,335 @@ class CECAdapterPriv
         return 1;
     }
 
+    void HandleKeyPress(const cec_keypress* key)
+    {
+        if (!adapter || !valid)
+            return;
+
+        // Ignore key down events and wait for the key 'up'
+        if (key->duration < 1)
+            return;
+
+        QString code;
+        int action = 0;
+        switch (key->keycode)
+        {
+            case CEC_USER_CONTROL_CODE_NUMBER0:
+                action = Qt::Key_0;
+                code   = "0";
+                break;
+            case CEC_USER_CONTROL_CODE_NUMBER1:
+                action = Qt::Key_1;
+                code   = "1";
+                break;
+            case CEC_USER_CONTROL_CODE_NUMBER2:
+                action = Qt::Key_2;
+                code   = "2";
+                break;
+            case CEC_USER_CONTROL_CODE_NUMBER3:
+                action = Qt::Key_3;
+                code   = "3";
+                break;
+            case CEC_USER_CONTROL_CODE_NUMBER4:
+                action = Qt::Key_4;
+                code   = "4";
+                break;
+            case CEC_USER_CONTROL_CODE_NUMBER5:
+                action = Qt::Key_5;
+                code   = "5";
+                break;
+            case CEC_USER_CONTROL_CODE_NUMBER6:
+                action = Qt::Key_6;
+                code   = "6";
+                break;
+            case CEC_USER_CONTROL_CODE_NUMBER7:
+                action = Qt::Key_7;
+                code   = "7";
+                break;
+            case CEC_USER_CONTROL_CODE_NUMBER8:
+                action = Qt::Key_8;
+                code   = "8";
+                break;
+            case CEC_USER_CONTROL_CODE_NUMBER9:
+                action = Qt::Key_9;
+                code   = "9";
+                break;
+            case CEC_USER_CONTROL_CODE_SELECT:
+                action = Qt::Key_Select;
+                code   = "SELECT";
+                break;
+            case CEC_USER_CONTROL_CODE_ENTER:
+                action = Qt::Key_Enter;
+                code   = "ENTER";
+                break;
+            case CEC_USER_CONTROL_CODE_UP:
+                action = Qt::Key_Up;
+                code   = "UP";
+                break;
+            case CEC_USER_CONTROL_CODE_DOWN:
+                action = Qt::Key_Down;
+                code   = "DOWN";
+                break;
+            case CEC_USER_CONTROL_CODE_LEFT:
+                action = Qt::Key_Left;
+                code   = "LEFT";
+                break;
+            case CEC_USER_CONTROL_CODE_LEFT_UP:
+                action = Qt::Key_Left;
+                code   = "LEFT_UP";
+                break;
+            case CEC_USER_CONTROL_CODE_LEFT_DOWN:
+                action = Qt::Key_Left;
+                code   = "LEFT_DOWN";
+                break;
+            case CEC_USER_CONTROL_CODE_RIGHT:
+                action = Qt::Key_Right;
+                code   = "RIGHT";
+                break;
+            case CEC_USER_CONTROL_CODE_RIGHT_UP:
+                action = Qt::Key_Right;
+                code   = "RIGHT_UP";
+                break;
+            case CEC_USER_CONTROL_CODE_RIGHT_DOWN:
+                action = Qt::Key_Right;
+                code   = "RIGHT_DOWN";
+                break;
+            case CEC_USER_CONTROL_CODE_ROOT_MENU:
+                action = Qt::Key_M;
+                code   = "ROOT_MENU";
+                break;
+            case CEC_USER_CONTROL_CODE_EXIT:
+                action = Qt::Key_Escape;
+                code   = "EXIT";
+                break;
+            case CEC_USER_CONTROL_CODE_PREVIOUS_CHANNEL:
+                action = Qt::Key_H;
+                code   = "PREVIOUS_CHANNEL";
+                break;
+            case CEC_USER_CONTROL_CODE_SOUND_SELECT:
+                action = Qt::Key_Plus;
+                code   = "SOUND_SELECT";
+                break;
+            case CEC_USER_CONTROL_CODE_VOLUME_UP:
+                action = Qt::Key_VolumeUp;
+                code   = "VOLUME_UP";
+                break;
+            case CEC_USER_CONTROL_CODE_VOLUME_DOWN:
+                action = Qt::Key_VolumeDown;
+                code   = "VOLUME_DOWN";
+                break;
+            case CEC_USER_CONTROL_CODE_MUTE:
+                action = Qt::Key_VolumeMute;
+                code   = "MUTE";
+                break;
+            case CEC_USER_CONTROL_CODE_PLAY:
+                action = Qt::Key_P;
+                code   = "PLAY";
+                break;
+            case CEC_USER_CONTROL_CODE_PAUSE:
+                action = Qt::Key_P; // same as play
+                code   = "PAUSE";
+                break;
+            case CEC_USER_CONTROL_CODE_STOP:
+                action = Qt::Key_Stop;
+                code   = "STOP";
+                break;
+            case CEC_USER_CONTROL_CODE_RECORD:
+                action = Qt::Key_R;
+                code   = "RECORD";
+                break;
+            case CEC_USER_CONTROL_CODE_CLEAR:
+                action = Qt::Key_Clear;
+                code   = "CLEAR";
+                break;
+            case CEC_USER_CONTROL_CODE_DISPLAY_INFORMATION:
+                action = Qt::Key_I;
+                code   = "DISPLAY_INFORMATION";
+                break;
+            case CEC_USER_CONTROL_CODE_PAGE_UP:
+                action = Qt::Key_PageUp;
+                code   = "PAGE_UP";
+                break;
+            case CEC_USER_CONTROL_CODE_PAGE_DOWN:
+                action = Qt::Key_PageDown;
+                code   = "PAGE_DOWN";
+                break;
+            case CEC_USER_CONTROL_CODE_EJECT:
+                action = Qt::Key_Eject;
+                code   = "EJECT";
+                break;
+            case CEC_USER_CONTROL_CODE_FORWARD:
+                action = Qt::Key_Forward;
+                code   = "FORWARD";
+                break;
+            case CEC_USER_CONTROL_CODE_BACKWARD:
+                action = Qt::Key_Back;
+                code   = "BACKWARD";
+                break;
+            case CEC_USER_CONTROL_CODE_F1_BLUE:
+                action = Qt::Key_F5; // NB F1 is help and we normally map blue to F5
+                code   = "F1_BLUE";
+                break;
+            case CEC_USER_CONTROL_CODE_F2_RED:
+                action = Qt::Key_F2;
+                code   = "F2_RED";
+                break;
+            case CEC_USER_CONTROL_CODE_F3_GREEN:
+                action = Qt::Key_F3;
+                code   = "F3_GREEN";
+                break;
+            case CEC_USER_CONTROL_CODE_F4_YELLOW:
+                action = Qt::Key_F4;
+                code   = "F4_YELLOW";
+                break;
+            case CEC_USER_CONTROL_CODE_SETUP_MENU:
+                action = Qt::Key_M; // Duplicate of Root Menu
+                code   = "SETUP_MENU";
+                break;
+            case CEC_USER_CONTROL_CODE_CONTENTS_MENU:
+                action = Qt::Key_M; // Duplicate of Root Menu
+                code   = "CONTENTS_MENU";
+                break;
+            case CEC_USER_CONTROL_CODE_FAVORITE_MENU:
+                action = Qt::Key_M; // Duplicate of Root Menu
+                code   = "FAVORITE_MENU";
+                break;
+            case CEC_USER_CONTROL_CODE_DOT:
+                action = Qt::Key_Period;
+                code  = "DOT";
+                break;
+            case CEC_USER_CONTROL_CODE_NEXT_FAVORITE:
+                action = Qt::Key_Slash;
+                code   = "NEXT_FAVORITE";
+                break;
+            case CEC_USER_CONTROL_CODE_INPUT_SELECT:
+                action = Qt::Key_C;
+                code = "INPUT_SELECT";
+                break;
+            case CEC_USER_CONTROL_CODE_HELP:
+                action = Qt::Key_F1;
+                code   = "HELP";
+                break;
+            case CEC_USER_CONTROL_CODE_STOP_RECORD:
+                action = Qt::Key_R; // Duplicate of Record
+                code = "STOP_RECORD";
+                break;
+            case CEC_USER_CONTROL_CODE_SUB_PICTURE:
+                action = Qt::Key_V;
+                code   = "SUB_PICTURE";
+                break;
+            case CEC_USER_CONTROL_CODE_ELECTRONIC_PROGRAM_GUIDE:
+                action = Qt::Key_S;
+                code   = "ELECTRONIC_PROGRAM_GUIDE";
+                break;
+            case CEC_USER_CONTROL_CODE_POWER:
+                action = Qt::Key_PowerOff;
+                code = "POWER";
+                break;
+
+             // these codes have 'non-standard' Qt key mappings to ensure
+             // each code has a unique key mapping
+            case CEC_USER_CONTROL_CODE_CHANNEL_DOWN:
+                action = Qt::Key_F20; // to differentiate from Up
+                code   = "CHANNEL_DOWN";
+                break;
+            case CEC_USER_CONTROL_CODE_CHANNEL_UP:
+                action = Qt::Key_F21; // to differentiate from Down
+                code   = "CHANNEL_UP";
+                break;
+            case CEC_USER_CONTROL_CODE_REWIND:
+                action = Qt::Key_F22; // to differentiate from Left
+                code   = "REWIND";
+                break;
+            case CEC_USER_CONTROL_CODE_FAST_FORWARD:
+                action = Qt::Key_F23; // to differentiate from Right
+                code   = "FAST_FORWARD";
+                break;
+            case CEC_USER_CONTROL_CODE_ANGLE:
+                action = Qt::Key_F24;
+                code = "ANGLE";
+                break;
+            case CEC_USER_CONTROL_CODE_F5:
+                action = Qt::Key_F6; // NB!
+                code = "F5";
+                break;
+
+            // codes with no obvious MythTV action
+            case CEC_USER_CONTROL_CODE_INITIAL_CONFIGURATION:
+                code = "INITIAL_CONFIGURATION";
+                break;
+            case CEC_USER_CONTROL_CODE_PAUSE_RECORD:
+                code = "PAUSE_RECORD";
+                break;
+            case CEC_USER_CONTROL_CODE_VIDEO_ON_DEMAND:
+                code = "VIDEO_ON_DEMAND";
+                break;
+            case CEC_USER_CONTROL_CODE_TIMER_PROGRAMMING:
+                code = "TIMER_PROGRAMMING";
+                break;
+            case CEC_USER_CONTROL_CODE_UNKNOWN:
+                code = "UNKNOWN";
+                break;
+            case CEC_USER_CONTROL_CODE_DATA:
+                code = "DATA";
+                break;
+
+            // Functions aren't implemented (similar to macros?)
+            case CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION:
+                code = "POWER_ON_FUNCTION";
+                break;
+            case CEC_USER_CONTROL_CODE_PLAY_FUNCTION:
+                code = "PLAY_FUNCTION";
+                break;
+            case CEC_USER_CONTROL_CODE_PAUSE_PLAY_FUNCTION:
+                code = "PAUSE_PLAY_FUNCTION";
+                break;
+            case CEC_USER_CONTROL_CODE_RECORD_FUNCTION:
+                code = "RECORD_FUNCTION";
+                break;
+            case CEC_USER_CONTROL_CODE_PAUSE_RECORD_FUNCTION:
+                code = "PAUSE_RECORD_FUNCTION";
+                break;
+            case CEC_USER_CONTROL_CODE_STOP_FUNCTION:
+                code = "STOP_FUNCTION";
+                break;
+            case CEC_USER_CONTROL_CODE_MUTE_FUNCTION:
+                code = "MUTE_FUNCTION";
+                break;
+            case CEC_USER_CONTROL_CODE_RESTORE_VOLUME_FUNCTION:
+                code = "RESTORE_VOLUME_FUNCTION";
+                break;
+            case CEC_USER_CONTROL_CODE_TUNE_FUNCTION:
+                code = "TUNE_FUNCTION";
+                break;
+            case CEC_USER_CONTROL_CODE_SELECT_MEDIA_FUNCTION:
+                code = "SELECT_MEDIA_FUNCTION";
+                break;
+            case CEC_USER_CONTROL_CODE_SELECT_AV_INPUT_FUNCTION:
+                code = "SELECT_AV_INPUT_FUNCTION";
+                break;
+            case CEC_USER_CONTROL_CODE_SELECT_AUDIO_INPUT_FUNCTION:
+                code = "SELECT_AUDIO_INPUT_FUNCTION";
+                break;
+            case CEC_USER_CONTROL_CODE_POWER_TOGGLE_FUNCTION:
+                code = "POWER_TOGGLE_FUNCTION";
+                break;
+            case CEC_USER_CONTROL_CODE_POWER_OFF_FUNCTION:
+                code = "POWER_OFF_FUNCTION";
+                break;
+        }
+
+        LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Keypress %1 %2")
+            .arg(code).arg(0 == action ? "(Not actioned)" : ""));
+
+        if (0 == action)
+            return;
+
+        GetMythUI()->ResetScreensaver();
+        QKeyEvent* ke = new QKeyEvent(QEvent::KeyPress, action, Qt::NoModifier);
+        qApp->postEvent(GetMythMainWindow(), (QEvent*)ke);
+    }
+
 #if CEC_LIB_VERSION_MAJOR >= 2
     int HandleAlert(const libcec_alert alert, const libcec_parameter &data)
     {
@@ -764,6 +1175,7 @@ void CECAdapter::Action(const QString &action)
         gActionsReady->wakeAll();
 }
 
+#if CEC_LIB_VERSION_MAJOR <= 3
 static int CECLogMessageCallback(void *adapter, const cec_log_message CEC_CALLBACK_PARAM_TYPE message)
 {
     return ((CECAdapterPriv*)adapter)->LogMessage(message);
@@ -778,13 +1190,37 @@ static int CECCommandCallback(void *adapter, const cec_command CEC_CALLBACK_PARA
 {
     return ((CECAdapterPriv*)adapter)->HandleCommand(command);
 }
+#endif
+#if CEC_LIB_VERSION_MAJOR >= 4
+static void CECLogMessageCallback(void *adapter, const cec_log_message* message)
+{
+    ((CECAdapterPriv*)adapter)->LogMessage(message);
+}
+
+static void CECKeyPressCallback(void *adapter, const cec_keypress* keypress)
+{
+    ((CECAdapterPriv*)adapter)->HandleKeyPress(keypress);
+}
+
+static void CECCommandCallback(void *adapter, const cec_command* command)
+{
+    ((CECAdapterPriv*)adapter)->HandleCommand(command);
+}
+#endif
 
 #if CEC_LIB_VERSION_MAJOR >= 2
+#if CEC_LIB_VERSION_MAJOR <= 3
 static int CECAlertCallback(void *adapter, const libcec_alert alert, const libcec_parameter CEC_CALLBACK_PARAM_TYPE data)
 {
     return ((CECAdapterPriv*)adapter)->HandleAlert(alert, data);
 }
-
+#endif
+#if CEC_LIB_VERSION_MAJOR >= 4
+static void CECAlertCallback(void *adapter, const libcec_alert alert, const libcec_parameter data)
+{
+    ((CECAdapterPriv*)adapter)->HandleAlert(alert, data);
+}
+#endif
 static void CECSourceActivatedCallback(void *adapter, const cec_logical_address address, const uint8_t activated)
 {
     ((CECAdapterPriv*)adapter)->HandleSourceActivated(address, activated);