1 #include "CECInput.h"
2 
3 #ifdef HAVE_LIBCEC
4 #include "Log.h"
5 #include <libcec/cec.h>
6 #include <iostream> // bad bad cecloader
7 #include <libcec/cecloader.h>
8 #include <SDL_events.h>
9 #ifdef _RPI_
10 extern "C" {
11 #include <interface/vmcs_host/vc_cecservice.h>
12 #include <interface/vmcs_host/vc_tvservice.h>
13 #include <interface/vmcs_host/vchost.h>
14 }
15 #endif // _RPI_
16 #endif // HAVE_LIBCEC
17 
18 // hack for cec support
19 extern int SDL_USER_CECBUTTONDOWN;
20 extern int SDL_USER_CECBUTTONUP;
21 
22 CECInput* CECInput::sInstance = nullptr;
23 
24 #ifdef HAVE_LIBCEC
onAlert(void *,const CEC::libcec_alert type,const CEC::libcec_parameter param)25 static void onAlert(void* /*cbParam*/, const CEC::libcec_alert type, const CEC::libcec_parameter param)
26 {
27 	LOG(LogDebug) << "CECInput::onAlert type: " << CECInput::getAlertTypeString(type) << " parameter: " << (char*)(param.paramData);
28 
29 } // onAlert
30 
onCommand(void *,const CEC::cec_command * command)31 static void onCommand(void* /*cbParam*/, const CEC::cec_command* command)
32 {
33 	LOG(LogDebug) << "CECInput::onCommand opcode: " << CECInput::getOpCodeString(command->opcode);
34 
35 } // onCommand
36 
onKeyPress(void *,const CEC::cec_keypress * key)37 static void onKeyPress(void* /*cbParam*/, const CEC::cec_keypress* key)
38 {
39 	LOG(LogDebug) << "CECInput::onKeyPress keycode: " << CECInput::getKeyCodeString(key->keycode);
40 
41 	SDL_Event event;
42 	event.type      = (key->duration > 0) ? SDL_USER_CECBUTTONUP : SDL_USER_CECBUTTONDOWN;
43 	event.user.code = key->keycode;
44 	SDL_PushEvent(&event);
45 
46 } // onKeyPress
47 
onLogMessage(void *,const CEC::cec_log_message * message)48 static void onLogMessage(void* /*cbParam*/, const CEC::cec_log_message* message)
49 {
50 	LOG(LogDebug) << "CECInput::onLogMessage message: " << message->message;
51 
52 } // onLogMessage
53 
54 #ifdef _RPI_
vchi_tv_and_cec_init()55 static void vchi_tv_and_cec_init()
56 {
57 	VCHI_INSTANCE_T vchi_instance;
58 	VCHI_CONNECTION_T* vchi_connection;
59 	vc_host_get_vchi_state(&vchi_instance, &vchi_connection);
60 
61 	vc_vchi_tv_init(vchi_instance, &vchi_connection, 1);
62 	vc_vchi_cec_init(vchi_instance, &vchi_connection, 1);
63 
64 } // vchi_tv_and_cec_init
65 
vchi_tv_and_cec_deinit()66 static void vchi_tv_and_cec_deinit()
67 {
68 	vc_vchi_cec_stop();
69 	vc_vchi_tv_stop();
70 
71 } // vchi_tv_and_cec_deinit
72 #endif // _RPI_
73 #endif // HAVE_LIBCEC
74 
init()75 void CECInput::init()
76 {
77 	if(!sInstance)
78 		sInstance = new CECInput();
79 
80 } // init
81 
deinit()82 void CECInput::deinit()
83 {
84 	if(sInstance)
85 	{
86 		delete sInstance;
87 		sInstance = nullptr;
88 	}
89 
90 } // deinit
91 
CECInput()92 CECInput::CECInput() : mlibCEC(nullptr)
93 {
94 
95 #ifdef HAVE_LIBCEC
96 #ifdef _RPI_
97 	// restart vchi tv and cec in case we just came back from another app using cec (like Kodi)
98 	vchi_tv_and_cec_deinit();
99 	vchi_tv_and_cec_init();
100 #endif // _RPI_
101 
102 	CEC::ICECCallbacks        callbacks;
103 	CEC::libcec_configuration config;
104 	callbacks.Clear();
105 	config.Clear();
106 
107 	callbacks.alert           = &onAlert;
108 	callbacks.commandReceived = &onCommand;
109 	callbacks.keyPress        = &onKeyPress;
110 	callbacks.logMessage      = &onLogMessage;
111 
112 	sprintf(config.strDeviceName, "RetroPie ES");
113 	config.clientVersion   = CEC::LIBCEC_VERSION_CURRENT;
114 	config.bActivateSource = 0;
115 	config.callbacks       = &callbacks;
116 	config.deviceTypes.Add(CEC::CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
117 
118 	mlibCEC = LibCecInitialise(&config);
119 
120 	if(!mlibCEC)
121 	{
122 		LOG(LogError) << "CECInput::LibCecInitialise failed";
123 		return;
124 	}
125 
126 	CEC::cec_adapter_descriptor adapters[10];
127 	int                         numAdapters = mlibCEC->DetectAdapters(adapters, 10, nullptr, true);
128 
129 	if(numAdapters <= 0)
130 	{
131 		LOG(LogError) << "CECInput::mAdapter->DetectAdapters failed";
132 		UnloadLibCec(mlibCEC);
133 		mlibCEC = nullptr;
134 		return;
135 	}
136 
137 	for(int i = 0; i < numAdapters; ++i)
138 		LOG(LogDebug) << "CEC adapter: " << i << " path: " << adapters[i].strComPath << " name: " << adapters[i].strComName;
139 
140 	if(!mlibCEC->Open(adapters[0].strComName))
141 	{
142 		LOG(LogError) << "CECInput::mAdapter->Open failed";
143 		UnloadLibCec(mlibCEC);
144 		mlibCEC = nullptr;
145 		return;
146 	}
147 #endif // HAVE_LIBCEC
148 
149 } // CECInput
150 
~CECInput()151 CECInput::~CECInput()
152 {
153 
154 #ifdef HAVE_LIBCEC
155 	if(mlibCEC)
156 	{
157 		mlibCEC->Close();
158 		UnloadLibCec(mlibCEC);
159 		mlibCEC = nullptr;
160 	}
161 
162 #ifdef _RPI_
163 	// deinit vchi tv and cec in case we are going to launch another app using cec (like Kodi)
164 	vchi_tv_and_cec_deinit();
165 #endif // _RPI_
166 #endif // HAVE_LIBCEC
167 
168 } // ~CECInput
169 
getAlertTypeString(const unsigned int _type)170 std::string CECInput::getAlertTypeString(const unsigned int _type)
171 {
172 	switch(_type)
173 	{
174 
175 #ifdef HAVE_LIBCEC
176 		case CEC::CEC_ALERT_SERVICE_DEVICE:         { return "Service-Device";         } break;
177 		case CEC::CEC_ALERT_CONNECTION_LOST:        { return "Connection-Lost";        } break;
178 		case CEC::CEC_ALERT_PERMISSION_ERROR:       { return "Permission-Error";       } break;
179 		case CEC::CEC_ALERT_PORT_BUSY:              { return "Port-Busy";              } break;
180 		case CEC::CEC_ALERT_PHYSICAL_ADDRESS_ERROR: { return "Physical-Address-Error"; } break;
181 		case CEC::CEC_ALERT_TV_POLL_FAILED:         { return "TV-Poll-Failed";         } break;
182 #else // HAVE_LIBCEC
183 		case 0:
184 #endif // HAVE_LIBCEC
185 
186 		default:                                    { return "Unknown";                } break;
187 	}
188 
189 } // getAlertTypeString
190 
getOpCodeString(const unsigned int _opCode)191 std::string CECInput::getOpCodeString(const unsigned int _opCode)
192 {
193 	switch(_opCode)
194 	{
195 
196 #ifdef HAVE_LIBCEC
197 		case CEC::CEC_OPCODE_ACTIVE_SOURCE:                 { return "Active-Source";                 } break;
198 		case CEC::CEC_OPCODE_IMAGE_VIEW_ON:                 { return "Image-View-On";                 } break;
199 		case CEC::CEC_OPCODE_TEXT_VIEW_ON:                  { return "Text-View-On";                  } break;
200 		case CEC::CEC_OPCODE_INACTIVE_SOURCE:               { return "Inactive-Source";               } break;
201 		case CEC::CEC_OPCODE_REQUEST_ACTIVE_SOURCE:         { return "Request-Active-Source";         } break;
202 		case CEC::CEC_OPCODE_ROUTING_CHANGE:                { return "Routing-Change";                } break;
203 		case CEC::CEC_OPCODE_ROUTING_INFORMATION:           { return "Routing-Information";           } break;
204 		case CEC::CEC_OPCODE_SET_STREAM_PATH:               { return "Set-Stream-Path";               } break;
205 		case CEC::CEC_OPCODE_STANDBY:                       { return "Standby";                       } break;
206 		case CEC::CEC_OPCODE_RECORD_OFF:                    { return "Record-Off";                    } break;
207 		case CEC::CEC_OPCODE_RECORD_ON:                     { return "Record-On";                     } break;
208 		case CEC::CEC_OPCODE_RECORD_STATUS:                 { return "Record-Status";                 } break;
209 		case CEC::CEC_OPCODE_RECORD_TV_SCREEN:              { return "Record-TV-Screen";              } break;
210 		case CEC::CEC_OPCODE_CLEAR_ANALOGUE_TIMER:          { return "Clear-Analogue-Timer";          } break;
211 		case CEC::CEC_OPCODE_CLEAR_DIGITAL_TIMER:           { return "Clear-Digital-Timer";           } break;
212 		case CEC::CEC_OPCODE_CLEAR_EXTERNAL_TIMER:          { return "Clear-External-Timer";          } break;
213 		case CEC::CEC_OPCODE_SET_ANALOGUE_TIMER:            { return "Set-Analogue-Timer";            } break;
214 		case CEC::CEC_OPCODE_SET_DIGITAL_TIMER:             { return "Set-Digital-Timer";             } break;
215 		case CEC::CEC_OPCODE_SET_EXTERNAL_TIMER:            { return "Set-External-Timer";            } break;
216 		case CEC::CEC_OPCODE_SET_TIMER_PROGRAM_TITLE:       { return "Set-Timer-Program-Title";       } break;
217 		case CEC::CEC_OPCODE_TIMER_CLEARED_STATUS:          { return "Timer-Cleared-Status";          } break;
218 		case CEC::CEC_OPCODE_TIMER_STATUS:                  { return "Timer-Status";                  } break;
219 		case CEC::CEC_OPCODE_CEC_VERSION:                   { return "CEC-Version";                   } break;
220 		case CEC::CEC_OPCODE_GET_CEC_VERSION:               { return "Get-CEC-Version";               } break;
221 		case CEC::CEC_OPCODE_GIVE_PHYSICAL_ADDRESS:         { return "Give-Physical-Address";         } break;
222 		case CEC::CEC_OPCODE_GET_MENU_LANGUAGE:             { return "Get-Menu-Language";             } break;
223 		case CEC::CEC_OPCODE_REPORT_PHYSICAL_ADDRESS:       { return "Report-Physical-Address";       } break;
224 		case CEC::CEC_OPCODE_SET_MENU_LANGUAGE:             { return "Set-Menu-Language";             } break;
225 		case CEC::CEC_OPCODE_DECK_CONTROL:                  { return "Deck-Control";                  } break;
226 		case CEC::CEC_OPCODE_DECK_STATUS:                   { return "Deck-Status";                   } break;
227 		case CEC::CEC_OPCODE_GIVE_DECK_STATUS:              { return "Give-Deck-Status";              } break;
228 		case CEC::CEC_OPCODE_PLAY:                          { return "Play";                          } break;
229 		case CEC::CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS:      { return "Give-Tuner-Device-Status";      } break;
230 		case CEC::CEC_OPCODE_SELECT_ANALOGUE_SERVICE:       { return "Select-Analogue-Service";       } break;
231 		case CEC::CEC_OPCODE_SELECT_DIGITAL_SERVICE:        { return "Select-Digital-Service";        } break;
232 		case CEC::CEC_OPCODE_TUNER_DEVICE_STATUS:           { return "Tuner-Device-Status";           } break;
233 		case CEC::CEC_OPCODE_TUNER_STEP_DECREMENT:          { return "Tuner-Step-Decrement";          } break;
234 		case CEC::CEC_OPCODE_TUNER_STEP_INCREMENT:          { return "Tuner-Step-Increment";          } break;
235 		case CEC::CEC_OPCODE_DEVICE_VENDOR_ID:              { return "Device-Vendor-ID";              } break;
236 		case CEC::CEC_OPCODE_GIVE_DEVICE_VENDOR_ID:         { return "Give-Device-Vendor-ID";         } break;
237 		case CEC::CEC_OPCODE_VENDOR_COMMAND:                { return "Vendor-Command";                } break;
238 		case CEC::CEC_OPCODE_VENDOR_COMMAND_WITH_ID:        { return "Vendor-Command-With-ID";        } break;
239 		case CEC::CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN:     { return "Vendor-Remote-Button-Down";     } break;
240 		case CEC::CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP:       { return "Vendor-Remote-Button-Up";       } break;
241 		case CEC::CEC_OPCODE_SET_OSD_STRING:                { return "Set-OSD-String";                } break;
242 		case CEC::CEC_OPCODE_GIVE_OSD_NAME:                 { return "Give-OSD-Name";                 } break;
243 		case CEC::CEC_OPCODE_SET_OSD_NAME:                  { return "Set-OSD-Name";                  } break;
244 		case CEC::CEC_OPCODE_MENU_REQUEST:                  { return "Menu-Request";                  } break;
245 		case CEC::CEC_OPCODE_MENU_STATUS:                   { return "Menu-Status";                   } break;
246 		case CEC::CEC_OPCODE_USER_CONTROL_PRESSED:          { return "User-Control-Pressed";          } break;
247 		case CEC::CEC_OPCODE_USER_CONTROL_RELEASE:          { return "User-Control-Release";          } break;
248 		case CEC::CEC_OPCODE_GIVE_DEVICE_POWER_STATUS:      { return "Give-Device-Power-Status";      } break;
249 		case CEC::CEC_OPCODE_REPORT_POWER_STATUS:           { return "Report-Power-Status";           } break;
250 		case CEC::CEC_OPCODE_FEATURE_ABORT:                 { return "Feature-Abort";                 } break;
251 		case CEC::CEC_OPCODE_ABORT:                         { return "Abort";                         } break;
252 		case CEC::CEC_OPCODE_GIVE_AUDIO_STATUS:             { return "Give-Audio-Status";             } break;
253 		case CEC::CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS: { return "Give-System-Audio-Mode-Status"; } break;
254 		case CEC::CEC_OPCODE_REPORT_AUDIO_STATUS:           { return "Report-Audio-Status";           } break;
255 		case CEC::CEC_OPCODE_SET_SYSTEM_AUDIO_MODE:         { return "Set-System-Audio-Mode";         } break;
256 		case CEC::CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST:     { return "System-Audio-Mode-Request";     } break;
257 		case CEC::CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS:      { return "System-Audio-Mode-Status";      } break;
258 		case CEC::CEC_OPCODE_SET_AUDIO_RATE:                { return "Set-Audio-Rate";                } break;
259 		case CEC::CEC_OPCODE_START_ARC:                     { return "Start-Arc";                     } break;
260 		case CEC::CEC_OPCODE_REPORT_ARC_STARTED:            { return "Report-Arc-Started";            } break;
261 		case CEC::CEC_OPCODE_REPORT_ARC_ENDED:              { return "Report-Arc-Ended";              } break;
262 		case CEC::CEC_OPCODE_REQUEST_ARC_START:             { return "Request-Arc-Start";             } break;
263 		case CEC::CEC_OPCODE_REQUEST_ARC_END:               { return "Request-Arc-End";               } break;
264 		case CEC::CEC_OPCODE_END_ARC:                       { return "End-Arc";                       } break;
265 		case CEC::CEC_OPCODE_CDC:                           { return "CDC";                           } break;
266 		case CEC::CEC_OPCODE_NONE:                          { return "None";                          } break;
267 #else // HAVE_LIBCEC
268 		case 0:
269 #endif // HAVE_LIBCEC
270 
271 		default:                                            { return "Unknown";                       } break;
272 	}
273 
274 } // getOpCodeString
275 
getKeyCodeString(const unsigned int _keyCode)276 std::string CECInput::getKeyCodeString(const unsigned int _keyCode)
277 {
278 	switch(_keyCode)
279 	{
280 
281 #ifdef HAVE_LIBCEC
282 		case CEC::CEC_USER_CONTROL_CODE_SELECT:                      { return "Select";                      } break;
283 		case CEC::CEC_USER_CONTROL_CODE_UP:                          { return "Up";                          } break;
284 		case CEC::CEC_USER_CONTROL_CODE_DOWN:                        { return "Down";                        } break;
285 		case CEC::CEC_USER_CONTROL_CODE_LEFT:                        { return "Left";                        } break;
286 		case CEC::CEC_USER_CONTROL_CODE_RIGHT:                       { return "Right";                       } break;
287 		case CEC::CEC_USER_CONTROL_CODE_RIGHT_UP:                    { return "Right-Up";                    } break;
288 		case CEC::CEC_USER_CONTROL_CODE_RIGHT_DOWN:                  { return "Right-Down";                  } break;
289 		case CEC::CEC_USER_CONTROL_CODE_LEFT_UP:                     { return "Left-Up";                     } break;
290 		case CEC::CEC_USER_CONTROL_CODE_LEFT_DOWN:                   { return "Left-Down";                   } break;
291 		case CEC::CEC_USER_CONTROL_CODE_ROOT_MENU:                   { return "Root-Menu";                   } break;
292 		case CEC::CEC_USER_CONTROL_CODE_SETUP_MENU:                  { return "Setup-Menu";                  } break;
293 		case CEC::CEC_USER_CONTROL_CODE_CONTENTS_MENU:               { return "Contents-Menu";               } break;
294 		case CEC::CEC_USER_CONTROL_CODE_FAVORITE_MENU:               { return "Favorite-Menu";               } break;
295 		case CEC::CEC_USER_CONTROL_CODE_EXIT:                        { return "Exit";                        } break;
296 		case CEC::CEC_USER_CONTROL_CODE_TOP_MENU:                    { return "Top-Menu";                    } break;
297 		case CEC::CEC_USER_CONTROL_CODE_DVD_MENU:                    { return "DVD-Menu";                    } break;
298 		case CEC::CEC_USER_CONTROL_CODE_NUMBER_ENTRY_MODE:           { return "Number-Entry-Mode";           } break;
299 		case CEC::CEC_USER_CONTROL_CODE_NUMBER11:                    { return "Number11";                    } break;
300 		case CEC::CEC_USER_CONTROL_CODE_NUMBER12:                    { return "Number12";                    } break;
301 		case CEC::CEC_USER_CONTROL_CODE_NUMBER0:                     { return "Number0";                     } break;
302 		case CEC::CEC_USER_CONTROL_CODE_NUMBER1:                     { return "Number1";                     } break;
303 		case CEC::CEC_USER_CONTROL_CODE_NUMBER2:                     { return "Number2";                     } break;
304 		case CEC::CEC_USER_CONTROL_CODE_NUMBER3:                     { return "Number3";                     } break;
305 		case CEC::CEC_USER_CONTROL_CODE_NUMBER4:                     { return "Number4";                     } break;
306 		case CEC::CEC_USER_CONTROL_CODE_NUMBER5:                     { return "Number5";                     } break;
307 		case CEC::CEC_USER_CONTROL_CODE_NUMBER6:                     { return "Number6";                     } break;
308 		case CEC::CEC_USER_CONTROL_CODE_NUMBER7:                     { return "Number7";                     } break;
309 		case CEC::CEC_USER_CONTROL_CODE_NUMBER8:                     { return "Number8";                     } break;
310 		case CEC::CEC_USER_CONTROL_CODE_NUMBER9:                     { return "Number9";                     } break;
311 		case CEC::CEC_USER_CONTROL_CODE_DOT:                         { return "Dot";                         } break;
312 		case CEC::CEC_USER_CONTROL_CODE_ENTER:                       { return "Enter";                       } break;
313 		case CEC::CEC_USER_CONTROL_CODE_CLEAR:                       { return "Clear";                       } break;
314 		case CEC::CEC_USER_CONTROL_CODE_NEXT_FAVORITE:               { return "Next-Favorite";               } break;
315 		case CEC::CEC_USER_CONTROL_CODE_CHANNEL_UP:                  { return "Channel-Up";                  } break;
316 		case CEC::CEC_USER_CONTROL_CODE_CHANNEL_DOWN:                { return "Channel-Down";                } break;
317 		case CEC::CEC_USER_CONTROL_CODE_PREVIOUS_CHANNEL:            { return "Previous-Channel";            } break;
318 		case CEC::CEC_USER_CONTROL_CODE_SOUND_SELECT:                { return "Sound-Select";                } break;
319 		case CEC::CEC_USER_CONTROL_CODE_INPUT_SELECT:                { return "Input-Select";                } break;
320 		case CEC::CEC_USER_CONTROL_CODE_DISPLAY_INFORMATION:         { return "Display-Information";         } break;
321 		case CEC::CEC_USER_CONTROL_CODE_HELP:                        { return "Help";                        } break;
322 		case CEC::CEC_USER_CONTROL_CODE_PAGE_UP:                     { return "Page-Up";                     } break;
323 		case CEC::CEC_USER_CONTROL_CODE_PAGE_DOWN:                   { return "Page-Down";                   } break;
324 		case CEC::CEC_USER_CONTROL_CODE_POWER:                       { return "Power";                       } break;
325 		case CEC::CEC_USER_CONTROL_CODE_VOLUME_UP:                   { return "Volume-Up";                   } break;
326 		case CEC::CEC_USER_CONTROL_CODE_VOLUME_DOWN:                 { return "Volume-Down";                 } break;
327 		case CEC::CEC_USER_CONTROL_CODE_MUTE:                        { return "Mute";                        } break;
328 		case CEC::CEC_USER_CONTROL_CODE_PLAY:                        { return "Play";                        } break;
329 		case CEC::CEC_USER_CONTROL_CODE_STOP:                        { return "Stop";                        } break;
330 		case CEC::CEC_USER_CONTROL_CODE_PAUSE:                       { return "Pause";                       } break;
331 		case CEC::CEC_USER_CONTROL_CODE_RECORD:                      { return "Record";                      } break;
332 		case CEC::CEC_USER_CONTROL_CODE_REWIND:                      { return "Rewind";                      } break;
333 		case CEC::CEC_USER_CONTROL_CODE_FAST_FORWARD:                { return "Fast-Forward";                } break;
334 		case CEC::CEC_USER_CONTROL_CODE_EJECT:                       { return "Eject";                       } break;
335 		case CEC::CEC_USER_CONTROL_CODE_FORWARD:                     { return "Forward";                     } break;
336 		case CEC::CEC_USER_CONTROL_CODE_BACKWARD:                    { return "Backward";                    } break;
337 		case CEC::CEC_USER_CONTROL_CODE_STOP_RECORD:                 { return "Stop-Record";                 } break;
338 		case CEC::CEC_USER_CONTROL_CODE_PAUSE_RECORD:                { return "Pause-Record";                } break;
339 		case CEC::CEC_USER_CONTROL_CODE_ANGLE:                       { return "Angle";                       } break;
340 		case CEC::CEC_USER_CONTROL_CODE_SUB_PICTURE:                 { return "Sub-Picture";                 } break;
341 		case CEC::CEC_USER_CONTROL_CODE_VIDEO_ON_DEMAND:             { return "Video-On-Demand";             } break;
342 		case CEC::CEC_USER_CONTROL_CODE_ELECTRONIC_PROGRAM_GUIDE:    { return "Electronic-Program-Guide";    } break;
343 		case CEC::CEC_USER_CONTROL_CODE_TIMER_PROGRAMMING:           { return "Timer-Programming";           } break;
344 		case CEC::CEC_USER_CONTROL_CODE_INITIAL_CONFIGURATION:       { return "Initial-Configuration";       } break;
345 		case CEC::CEC_USER_CONTROL_CODE_SELECT_BROADCAST_TYPE:       { return "Select-Broadcast-Type";       } break;
346 		case CEC::CEC_USER_CONTROL_CODE_SELECT_SOUND_PRESENTATION:   { return "Select-Sound-Presentation";   } break;
347 		case CEC::CEC_USER_CONTROL_CODE_PLAY_FUNCTION:               { return "Play-Function";               } break;
348 		case CEC::CEC_USER_CONTROL_CODE_PAUSE_PLAY_FUNCTION:         { return "Pause-Play-Function";         } break;
349 		case CEC::CEC_USER_CONTROL_CODE_RECORD_FUNCTION:             { return "Record-Function";             } break;
350 		case CEC::CEC_USER_CONTROL_CODE_PAUSE_RECORD_FUNCTION:       { return "Pause-Record-Function";       } break;
351 		case CEC::CEC_USER_CONTROL_CODE_STOP_FUNCTION:               { return "Stop-Function";               } break;
352 		case CEC::CEC_USER_CONTROL_CODE_MUTE_FUNCTION:               { return "Mute-Function";               } break;
353 		case CEC::CEC_USER_CONTROL_CODE_RESTORE_VOLUME_FUNCTION:     { return "Restore-Volume-Function";     } break;
354 		case CEC::CEC_USER_CONTROL_CODE_TUNE_FUNCTION:               { return "Tune-Function";               } break;
355 		case CEC::CEC_USER_CONTROL_CODE_SELECT_MEDIA_FUNCTION:       { return "Select-Media-Function";       } break;
356 		case CEC::CEC_USER_CONTROL_CODE_SELECT_AV_INPUT_FUNCTION:    { return "Select-AV-Input-Function";    } break;
357 		case CEC::CEC_USER_CONTROL_CODE_SELECT_AUDIO_INPUT_FUNCTION: { return "Select-Audio-Input-Function"; } break;
358 		case CEC::CEC_USER_CONTROL_CODE_POWER_TOGGLE_FUNCTION:       { return "Power-Toggle-Function";       } break;
359 		case CEC::CEC_USER_CONTROL_CODE_POWER_OFF_FUNCTION:          { return "Power-Off-Function";          } break;
360 		case CEC::CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION:           { return "Power-On-Function";           } break;
361 		case CEC::CEC_USER_CONTROL_CODE_F1_BLUE:                     { return "F1-Blue";                     } break;
362 		case CEC::CEC_USER_CONTROL_CODE_F2_RED:                      { return "F2-Red";                      } break;
363 		case CEC::CEC_USER_CONTROL_CODE_F3_GREEN:                    { return "F3-Green";                    } break;
364 		case CEC::CEC_USER_CONTROL_CODE_F4_YELLOW:                   { return "F4-Yellow";                   } break;
365 		case CEC::CEC_USER_CONTROL_CODE_F5:                          { return "F5";                          } break;
366 		case CEC::CEC_USER_CONTROL_CODE_DATA:                        { return "Data";                        } break;
367 		case CEC::CEC_USER_CONTROL_CODE_AN_RETURN:                   { return "AN-Return";                   } break;
368 		case CEC::CEC_USER_CONTROL_CODE_AN_CHANNELS_LIST:            { return "AN-Channels-List";            } break;
369 #else // HAVE_LIBCEC
370 		case 0:
371 #endif // HAVE_LIBCEC
372 
373 		default:                                                     { return "Unknown";                     } break;
374 	}
375 
376 } // getKeyCodeString
377