1 /*
2 * This file is part of the libCEC(R) library.
3 *
4 * libCEC(R) is Copyright (C) 2011-2015 Pulse-Eight Limited. All rights reserved.
5 * libCEC(R) is an original work, containing original code.
6 *
7 * libCEC(R) is a trademark of Pulse-Eight Limited.
8 *
9 * This program is dual-licensed; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 * 02110-1301 USA
23 *
24 *
25 * Alternatively, you can license this library under a commercial license,
26 * please contact Pulse-Eight Licensing for more information.
27 *
28 * For more information contact:
29 * Pulse-Eight Licensing <license@pulse-eight.com>
30 * http://www.pulse-eight.com/
31 * http://www.pulse-eight.net/
32 */
33
34 #include "env.h"
35 #include "CECClient.h"
36
37 #include "CECProcessor.h"
38 #include "LibCEC.h"
39 #include "CECTypeUtils.h"
40 #include "devices/CECPlaybackDevice.h"
41 #include "devices/CECAudioSystem.h"
42 #include "devices/CECTV.h"
43 #include "implementations/CECCommandHandler.h"
44 #include <stdio.h>
45
46 using namespace CEC;
47 using namespace P8PLATFORM;
48
49 #define LIB_CEC m_processor->GetLib()
50 #define ToString(x) CCECTypeUtils::ToString(x)
51
CCECClient(CCECProcessor * processor,const libcec_configuration & configuration)52 CCECClient::CCECClient(CCECProcessor *processor, const libcec_configuration &configuration) :
53 m_processor(processor),
54 m_bInitialised(false),
55 m_bRegistered(false),
56 m_iCurrentButton(CEC_USER_CONTROL_CODE_UNKNOWN),
57 m_initialButtontime(0),
58 m_updateButtontime(0),
59 m_repeatButtontime(0),
60 m_releaseButtontime(0),
61 m_pressedButtoncount(0),
62 m_releasedButtoncount(0),
63 m_iPreventForwardingPowerOffCommand(0)
64 {
65 m_configuration.Clear();
66 // set the initial configuration
67 SetConfiguration(configuration);
68 CreateThread(false);
69 }
70
~CCECClient(void)71 CCECClient::~CCECClient(void)
72 {
73 StopThread();
74 CCallbackWrap* cb;
75 while (!m_callbackCalls.IsEmpty())
76 if (m_callbackCalls.Pop(cb, 0))
77 delete cb;
78
79 // unregister the client
80 if (m_processor && IsRegistered())
81 m_processor->UnregisterClient(this);
82 }
83
IsInitialised(void)84 bool CCECClient::IsInitialised(void)
85 {
86 CLockObject lock(m_mutex);
87 return m_bInitialised && m_processor;
88 }
89
SetInitialised(bool bSetTo)90 void CCECClient::SetInitialised(bool bSetTo)
91 {
92 CLockObject lock(m_mutex);
93 m_bInitialised = bSetTo;
94 }
95
IsRegistered(void)96 bool CCECClient::IsRegistered(void)
97 {
98 CLockObject lock(m_mutex);
99 return m_bRegistered && m_processor;
100 }
101
SetRegistered(bool bSetTo)102 void CCECClient::SetRegistered(bool bSetTo)
103 {
104 CLockObject lock(m_mutex);
105 m_bRegistered = bSetTo;
106 }
107
OnRegister(void)108 bool CCECClient::OnRegister(void)
109 {
110 // return false if already initialised
111 if (IsInitialised())
112 return true;
113
114 // get all device we control
115 CECDEVICEVEC devices;
116 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
117
118 // return false when no devices were found
119 if (devices.empty())
120 {
121 LIB_CEC->AddLog(CEC_LOG_WARNING, "cannot find the primary device (logical address %x)", GetPrimaryLogicalAddress());
122 return false;
123 }
124
125 // mark as initialised
126 SetInitialised(true);
127
128 // configure all devices
129 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
130 {
131 // only set our OSD name for the primary device
132 if ((*it)->GetLogicalAddress() == GetPrimaryLogicalAddress())
133 (*it)->SetOSDName(m_configuration.strDeviceName);
134
135 // set the default menu language for devices we control
136 (*it)->SetMenuLanguage(std::string(m_configuration.strDeviceLanguage, 3));
137 }
138
139 // set the physical address
140 SetPhysicalAddress(m_configuration);
141
142 // make the primary device the active source if the option is set
143 if (m_configuration.bActivateSource == 1)
144 GetPrimaryDevice()->ActivateSource(500);
145
146 SaveConfiguration(m_configuration);
147
148 return true;
149 }
150
SetHDMIPort(const cec_logical_address iBaseDevice,const uint8_t iPort,bool bForce)151 bool CCECClient::SetHDMIPort(const cec_logical_address iBaseDevice, const uint8_t iPort, bool bForce /* = false */)
152 {
153 bool bReturn(false);
154
155 // limit the HDMI port range to 1-15
156 if (iPort < CEC_MIN_HDMI_PORTNUMBER ||
157 iPort > CEC_MAX_HDMI_PORTNUMBER)
158 return bReturn;
159
160 // update the configuration
161 {
162 CLockObject lock(m_mutex);
163 if (m_configuration.baseDevice == iBaseDevice &&
164 m_configuration.iHDMIPort == iPort &&
165 CLibCEC::IsValidPhysicalAddress(m_configuration.iPhysicalAddress) &&
166 m_configuration.iPhysicalAddress > 0)
167 return true;
168 m_configuration.baseDevice = iBaseDevice;
169 m_configuration.iHDMIPort = iPort;
170 m_configuration.bAutodetectAddress = 0;
171 }
172
173 LIB_CEC->AddLog(CEC_LOG_NOTICE, "setting HDMI port to %d on device %s (%d)", iPort, ToString(iBaseDevice), (int)iBaseDevice);
174
175 // don't continue if the connection isn't opened
176 if (!m_processor->CECInitialised() && !bForce)
177 return true;
178
179 // get the PA of the base device
180 uint16_t iPhysicalAddress(CEC_INVALID_PHYSICAL_ADDRESS);
181 CCECBusDevice *baseDevice = m_processor->GetDevice(iBaseDevice);
182 if (baseDevice)
183 iPhysicalAddress = baseDevice->GetPhysicalAddress(GetPrimaryLogicalAddress());
184
185 // add our port number
186 if (iPhysicalAddress <= CEC_MAX_PHYSICAL_ADDRESS)
187 {
188 if (iPhysicalAddress == 0)
189 iPhysicalAddress += 0x1000 * iPort;
190 else if (iPhysicalAddress % 0x1000 == 0)
191 iPhysicalAddress += 0x100 * iPort;
192 else if (iPhysicalAddress % 0x100 == 0)
193 iPhysicalAddress += 0x10 * iPort;
194 else if (iPhysicalAddress % 0x10 == 0)
195 iPhysicalAddress += iPort;
196
197 bReturn = true;
198 }
199
200 // set the default address when something went wrong
201 if (!bReturn)
202 {
203 uint16_t iEepromAddress = m_processor->GetPhysicalAddressFromEeprom();
204 if (CLibCEC::IsValidPhysicalAddress(iEepromAddress))
205 {
206 LIB_CEC->AddLog(CEC_LOG_WARNING, "failed to set the physical address to %04X, setting it to the value that was saved in the eeprom: %04X", iPhysicalAddress, iEepromAddress);
207 iPhysicalAddress = iEepromAddress;
208 bReturn = true;
209 }
210 else
211 {
212 LIB_CEC->AddLog(CEC_LOG_WARNING, "failed to set the physical address to %04X, setting it to the default value %04X", iPhysicalAddress, CEC_DEFAULT_PHYSICAL_ADDRESS);
213 iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS;
214 }
215 }
216
217 // and set the address
218 SetDevicePhysicalAddress(iPhysicalAddress);
219
220 QueueConfigurationChanged(m_configuration);
221
222 return bReturn;
223 }
224
ResetPhysicalAddress(void)225 void CCECClient::ResetPhysicalAddress(void)
226 {
227 LIB_CEC->AddLog(CEC_LOG_DEBUG, "resetting HDMI port and base device to defaults");
228 SetHDMIPort(CECDEVICE_TV, CEC_DEFAULT_HDMI_PORT);
229 }
230
SetPhysicalAddress(const libcec_configuration & configuration)231 bool CCECClient::SetPhysicalAddress(const libcec_configuration &configuration)
232 {
233 // override the physical address from configuration.iPhysicalAddress if it's set
234 if (CLibCEC::IsValidPhysicalAddress(configuration.iPhysicalAddress) &&
235 (configuration.iPhysicalAddress != CEC_PHYSICAL_ADDRESS_TV) &&
236 SetPhysicalAddress(configuration.iPhysicalAddress))
237 {
238 if (m_configuration.bAutodetectAddress == 0)
239 LIB_CEC->AddLog(CEC_LOG_DEBUG, "using provided physical address %04X", configuration.iPhysicalAddress);
240 CLockObject lock(m_mutex);
241 m_configuration.baseDevice = CECDEVICE_UNKNOWN;
242 m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE;
243 m_configuration.iPhysicalAddress = configuration.iPhysicalAddress;
244 return true;
245 }
246
247 // try to autodetect the address
248 if (AutodetectPhysicalAddress())
249 {
250 LIB_CEC->AddLog(CEC_LOG_DEBUG, "using auto-detected physical address %04X", m_configuration.iPhysicalAddress);
251 {
252 CLockObject lock(m_mutex);
253 m_configuration.baseDevice = CECDEVICE_UNKNOWN;
254 m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE;
255 m_configuration.iPhysicalAddress = configuration.iPhysicalAddress;
256 }
257 SetDevicePhysicalAddress(m_configuration.iPhysicalAddress);
258 return true;
259 }
260
261 // use the base device + hdmi port settings
262 if ((configuration.baseDevice != CECDEVICE_UNKNOWN) &&
263 (configuration.iHDMIPort != CEC_HDMI_PORTNUMBER_NONE) &&
264 SetHDMIPort(configuration.baseDevice, configuration.iHDMIPort))
265 {
266 LIB_CEC->AddLog(CEC_LOG_DEBUG, "using device/input physical address %04X", m_configuration.iPhysicalAddress);
267 return true;
268 }
269
270 // reset to defaults if something went wrong
271 ResetPhysicalAddress();
272 return false;
273 }
274
SetPhysicalAddress(const uint16_t iPhysicalAddress)275 bool CCECClient::SetPhysicalAddress(const uint16_t iPhysicalAddress)
276 {
277 // update the configuration
278 {
279 CLockObject lock(m_mutex);
280 if (m_configuration.iPhysicalAddress == iPhysicalAddress)
281 return true;
282 m_configuration.iPhysicalAddress = iPhysicalAddress;
283 }
284 LIB_CEC->AddLog(CEC_LOG_DEBUG, "changing physical address to %04X", iPhysicalAddress);
285
286 // set the physical address for each device
287 SetDevicePhysicalAddress(iPhysicalAddress);
288
289 // and send back the updated configuration
290 QueueConfigurationChanged(m_configuration);
291 return true;
292 }
293
SetSupportedDeviceTypes(void)294 void CCECClient::SetSupportedDeviceTypes(void)
295 {
296 cec_device_type_list types;
297 types.Clear();
298
299 // get the command handler for the tv
300 CCECCommandHandler *tvHandler = m_processor->GetTV()->GetHandler();
301 if (!tvHandler)
302 return;
303
304 // check all device types
305 for (uint8_t iPtr = 0; iPtr < 5; iPtr++)
306 {
307 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
308 continue;
309
310 // get the supported device type. the handler will replace types it doesn't support by one it does support
311 cec_device_type type = tvHandler->GetReplacementDeviceType(m_configuration.deviceTypes.types[iPtr]);
312 if (!types.IsSet(type))
313 types.Add(type);
314 }
315 m_processor->GetTV()->MarkHandlerReady();
316
317 // set the new type list
318 m_configuration.deviceTypes = types;
319
320 SaveConfiguration(m_configuration);
321 }
322
AllocateLogicalAddresses(void)323 bool CCECClient::AllocateLogicalAddresses(void)
324 {
325 // reset all previous LAs that were set
326 m_configuration.logicalAddresses.Clear();
327
328 // get the supported device types from the command handler of the TV
329 SetSupportedDeviceTypes();
330
331 // display an error if no device types are set
332 if (m_configuration.deviceTypes.IsEmpty())
333 {
334 LIB_CEC->AddLog(CEC_LOG_ERROR, "no device types given");
335 return false;
336 }
337
338 // check each entry of the list
339 for (uint8_t iPtr = 0; iPtr < 5; iPtr++)
340 {
341 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
342 continue;
343
344 // find an LA for this type
345 cec_logical_address address(CECDEVICE_UNKNOWN);
346 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_TV)
347 address = CECDEVICE_TV;
348 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RECORDING_DEVICE)
349 address = AllocateLogicalAddressRecordingDevice();
350 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_TUNER)
351 address = AllocateLogicalAddressTuner();
352 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_PLAYBACK_DEVICE)
353 address = AllocateLogicalAddressPlaybackDevice();
354 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
355 address = AllocateLogicalAddressAudioSystem();
356
357 // display an error if no LA could be allocated
358 if (address == CECDEVICE_UNKNOWN)
359 {
360 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - failed to allocate device '%d', type '%s'", __FUNCTION__, iPtr, ToString(m_configuration.deviceTypes.types[iPtr]));
361 return false;
362 }
363
364 // display the registered LA
365 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - device '%d', type '%s', LA '%X'", __FUNCTION__, iPtr, ToString(m_configuration.deviceTypes.types[iPtr]), address);
366 m_configuration.logicalAddresses.Set(address);
367 }
368
369 SaveConfiguration(m_configuration);
370
371 return true;
372 }
373
AllocateLogicalAddressRecordingDevice(void)374 cec_logical_address CCECClient::AllocateLogicalAddressRecordingDevice(void)
375 {
376 cec_logical_address retVal(CECDEVICE_UNKNOWN);
377
378 LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'recording device'");
379 if (m_processor->TryLogicalAddress(CECDEVICE_RECORDINGDEVICE1, m_configuration.cecVersion))
380 retVal = CECDEVICE_RECORDINGDEVICE1;
381 else if (m_processor->TryLogicalAddress(CECDEVICE_RECORDINGDEVICE2, m_configuration.cecVersion))
382 retVal = CECDEVICE_RECORDINGDEVICE2;
383 else if (m_processor->TryLogicalAddress(CECDEVICE_RECORDINGDEVICE3, m_configuration.cecVersion))
384 retVal = CECDEVICE_RECORDINGDEVICE3;
385
386 return retVal;
387 }
388
AllocateLogicalAddressTuner(void)389 cec_logical_address CCECClient::AllocateLogicalAddressTuner(void)
390 {
391 cec_logical_address retVal(CECDEVICE_UNKNOWN);
392
393 LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'tuner'");
394 if (m_processor->TryLogicalAddress(CECDEVICE_TUNER1, m_configuration.cecVersion))
395 retVal = CECDEVICE_TUNER1;
396 else if (m_processor->TryLogicalAddress(CECDEVICE_TUNER2, m_configuration.cecVersion))
397 retVal = CECDEVICE_TUNER2;
398 else if (m_processor->TryLogicalAddress(CECDEVICE_TUNER3, m_configuration.cecVersion))
399 retVal = CECDEVICE_TUNER3;
400 else if (m_processor->TryLogicalAddress(CECDEVICE_TUNER4, m_configuration.cecVersion))
401 retVal = CECDEVICE_TUNER4;
402
403 return retVal;
404 }
405
AllocateLogicalAddressPlaybackDevice(void)406 cec_logical_address CCECClient::AllocateLogicalAddressPlaybackDevice(void)
407 {
408 cec_logical_address retVal(CECDEVICE_UNKNOWN);
409
410 LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'playback device'");
411 if (m_processor->TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE1, m_configuration.cecVersion))
412 retVal = CECDEVICE_PLAYBACKDEVICE1;
413 else if (m_processor->TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE2, m_configuration.cecVersion))
414 retVal = CECDEVICE_PLAYBACKDEVICE2;
415 else if (m_processor->TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE3, m_configuration.cecVersion))
416 retVal = CECDEVICE_PLAYBACKDEVICE3;
417
418 return retVal;
419 }
420
AllocateLogicalAddressAudioSystem(void)421 cec_logical_address CCECClient::AllocateLogicalAddressAudioSystem(void)
422 {
423 cec_logical_address retVal(CECDEVICE_UNKNOWN);
424
425 LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'audiosystem'");
426 if (m_processor->TryLogicalAddress(CECDEVICE_AUDIOSYSTEM, m_configuration.cecVersion))
427 retVal = CECDEVICE_AUDIOSYSTEM;
428
429 return retVal;
430 }
431
GetDeviceByType(const cec_device_type type) const432 CCECBusDevice *CCECClient::GetDeviceByType(const cec_device_type type) const
433 {
434 // get all devices that match our logical addresses
435 CECDEVICEVEC devices;
436 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
437
438 // filter the type we need
439 CCECDeviceMap::FilterType(type, devices);
440
441 return devices.empty() ?
442 NULL :
443 *devices.begin();
444 }
445
ChangeDeviceType(const cec_device_type from,const cec_device_type to)446 bool CCECClient::ChangeDeviceType(const cec_device_type from, const cec_device_type to)
447 {
448 if (from == to)
449 return true;
450
451 LIB_CEC->AddLog(CEC_LOG_NOTICE, "changing device type '%s' into '%s'", ToString(from), ToString(to));
452
453 {
454 CLockObject lock(m_mutex);
455
456 // get the previous device that was allocated
457 CCECBusDevice *previousDevice = GetDeviceByType(from);
458 if (!previousDevice)
459 return false;
460
461 // change the type in the device type list
462 bool bChanged(false);
463 for (uint8_t iPtr = 0; iPtr < 5; iPtr++)
464 {
465 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
466 continue;
467
468 if (m_configuration.deviceTypes.types[iPtr] == from)
469 {
470 bChanged = true;
471 m_configuration.deviceTypes.types[iPtr] = to;
472 }
473 else if (m_configuration.deviceTypes.types[iPtr] == to && bChanged)
474 {
475 // ensure that dupes are removed
476 m_configuration.deviceTypes.types[iPtr] = CEC_DEVICE_TYPE_RESERVED;
477 }
478 }
479 }
480
481 // re-register the client to set the new ackmask
482 if (!m_processor->RegisterClient(this))
483 return false;
484
485 SaveConfiguration(m_configuration);
486
487 return true;
488 }
489
SetLogicalAddress(const cec_logical_address iLogicalAddress)490 bool CCECClient::SetLogicalAddress(const cec_logical_address iLogicalAddress)
491 {
492 bool bReturn(true);
493
494 if (GetPrimaryLogicalAddress() != iLogicalAddress)
495 {
496 LIB_CEC->AddLog(CEC_LOG_NOTICE, "setting primary logical address to %1x", iLogicalAddress);
497 {
498 CLockObject lock(m_mutex);
499 m_configuration.logicalAddresses.primary = iLogicalAddress;
500 m_configuration.logicalAddresses.Set(iLogicalAddress);
501 }
502
503 bReturn = m_processor->RegisterClient(this);
504
505 if (bReturn)
506 SaveConfiguration(m_configuration);
507 }
508
509 return bReturn;
510 }
511
Transmit(const cec_command & data,bool bIsReply)512 bool CCECClient::Transmit(const cec_command &data, bool bIsReply)
513 {
514 return m_processor ? m_processor->Transmit(data, bIsReply) : false;
515 }
516
SendPowerOnDevices(const cec_logical_address address)517 bool CCECClient::SendPowerOnDevices(const cec_logical_address address /* = CECDEVICE_TV */)
518 {
519 // if the broadcast address if set as destination, read the wakeDevices setting
520 if (address == CECDEVICE_BROADCAST)
521 {
522 CECDEVICEVEC devices;
523 m_processor->GetDevices()->GetWakeDevices(m_configuration, devices);
524 return m_processor->PowerOnDevices(GetPrimaryLogicalAddress(), devices);
525 }
526
527 return m_processor->PowerOnDevice(GetPrimaryLogicalAddress(), address);
528 }
529
SendStandbyDevices(const cec_logical_address address)530 bool CCECClient::SendStandbyDevices(const cec_logical_address address /* = CECDEVICE_BROADCAST */)
531 {
532 // if the broadcast address if set as destination, read the standbyDevices setting
533 if (address == CECDEVICE_BROADCAST)
534 {
535 CECDEVICEVEC devices;
536 m_processor->GetDevices()->GetPowerOffDevices(m_configuration, devices);
537 return m_processor->StandbyDevices(GetPrimaryLogicalAddress(), devices);
538 }
539
540 return m_processor->StandbyDevice(GetPrimaryLogicalAddress(), address);
541 }
542
SendSetActiveSource(const cec_device_type type)543 bool CCECClient::SendSetActiveSource(const cec_device_type type /* = CEC_DEVICE_TYPE_RESERVED */)
544 {
545 // get the devices that are controlled by us
546 CECDEVICEVEC devices;
547 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
548
549 // filter out the device that matches the given type
550 if (type != CEC_DEVICE_TYPE_RESERVED)
551 CCECDeviceMap::FilterType(type, devices);
552
553 // no devices left, re-fetch the list of devices that are controlled by us
554 if (devices.empty())
555 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
556
557 if (!devices.empty())
558 {
559 // get the first device from the list
560 CCECBusDevice *device = *devices.begin();
561
562 // and activate it
563 if (!m_processor->CECInitialised())
564 device->MarkAsActiveSource();
565 else if (device->HasValidPhysicalAddress())
566 return device->ActivateSource();
567 }
568
569 return false;
570 }
571
GetPlaybackDevice(void)572 CCECPlaybackDevice *CCECClient::GetPlaybackDevice(void)
573 {
574 CCECPlaybackDevice *device(NULL);
575 CECDEVICEVEC devices;
576
577 // get the playback devices
578 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
579 CCECDeviceMap::FilterType(CEC_DEVICE_TYPE_PLAYBACK_DEVICE, devices);
580
581 // no matches, get the recording devices
582 if (devices.empty())
583 {
584 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
585 CCECDeviceMap::FilterType(CEC_DEVICE_TYPE_RECORDING_DEVICE, devices);
586 }
587
588 // get the first device that matches, and cast it to CCECPlaybackDevice
589 if (!devices.empty())
590 device = (*devices.begin())->AsPlaybackDevice();
591
592 return device;
593 }
594
GetPrimaryLogicalAddress(void)595 cec_logical_address CCECClient::GetPrimaryLogicalAddress(void)
596 {
597 CLockObject lock(m_mutex);
598 return m_configuration.logicalAddresses.primary;
599 }
600
GetPrimaryDevice(void)601 CCECBusDevice *CCECClient::GetPrimaryDevice(void)
602 {
603 return m_processor->GetDevice(GetPrimaryLogicalAddress());
604 }
605
SendSetDeckControlMode(const cec_deck_control_mode mode,bool bSendUpdate)606 bool CCECClient::SendSetDeckControlMode(const cec_deck_control_mode mode, bool bSendUpdate /* = true */)
607 {
608 // find a playback device that we control
609 CCECPlaybackDevice *device = GetPlaybackDevice();
610 if (device)
611 {
612 // and set the deck control mode if there is a match
613 device->SetDeckControlMode(mode);
614 if (bSendUpdate)
615 return device->TransmitDeckStatus(CECDEVICE_TV, false);
616 return true;
617 }
618
619 // no match
620 return false;
621 }
622
SendSetDeckInfo(const cec_deck_info info,bool bSendUpdate)623 bool CCECClient::SendSetDeckInfo(const cec_deck_info info, bool bSendUpdate /* = true */)
624 {
625 // find a playback device that we control
626 CCECPlaybackDevice *device = GetPlaybackDevice();
627 if (device)
628 {
629 // and set the deck status if there is a match
630 device->SetDeckStatus(info);
631 if (bSendUpdate)
632 return device->AsPlaybackDevice()->TransmitDeckStatus(CECDEVICE_TV, false);
633 return true;
634 }
635
636 // no match
637 return false;
638 }
639
SendSetMenuState(const cec_menu_state state,bool bSendUpdate)640 bool CCECClient::SendSetMenuState(const cec_menu_state state, bool bSendUpdate /* = true */)
641 {
642 CECDEVICEVEC devices;
643
644 // set the menu state for all devices that are controlled by us
645 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
646 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
647 {
648 (*it)->SetMenuState(state);
649 if (bSendUpdate)
650 (*it)->TransmitMenuState(CECDEVICE_TV, false);
651 }
652
653 return true;
654 }
655
SendSetInactiveView(void)656 bool CCECClient::SendSetInactiveView(void)
657 {
658 CECDEVICEVEC devices;
659
660 // mark all devices that are controlled by us as inactive source
661 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
662 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
663 {
664 if ((*it)->IsActiveSource())
665 {
666 (*it)->MarkAsInactiveSource();
667 return (*it)->TransmitInactiveSource();
668 }
669 }
670
671 return true;
672 }
673
SendSetOSDString(const cec_logical_address iLogicalAddress,const cec_display_control duration,const char * strMessage)674 bool CCECClient::SendSetOSDString(const cec_logical_address iLogicalAddress, const cec_display_control duration, const char *strMessage)
675 {
676 CCECBusDevice *primary = GetPrimaryDevice();
677 if (primary)
678 return primary->TransmitOSDString(iLogicalAddress, duration, strMessage, false);
679
680 return false;
681 }
682
GetDeviceCecVersion(const cec_logical_address iAddress)683 cec_version CCECClient::GetDeviceCecVersion(const cec_logical_address iAddress)
684 {
685 CCECBusDevice *device = m_processor->GetDevice(iAddress);
686 if (device)
687 return device->GetCecVersion(GetPrimaryLogicalAddress());
688 return CEC_VERSION_UNKNOWN;
689 }
690
GetDeviceMenuLanguage(const cec_logical_address iAddress)691 std::string CCECClient::GetDeviceMenuLanguage(const cec_logical_address iAddress)
692 {
693 CCECBusDevice *device = m_processor->GetDevice(iAddress);
694 return !!device ?
695 device->GetMenuLanguage(GetPrimaryLogicalAddress()) :
696 "??";
697 }
698
GetDeviceOSDName(const cec_logical_address iAddress)699 std::string CCECClient::GetDeviceOSDName(const cec_logical_address iAddress)
700 {
701 CCECBusDevice *device = m_processor->GetDevice(iAddress);
702 return !!device?
703 device->GetOSDName(GetPrimaryLogicalAddress()) :
704 "";
705 }
706
GetDevicePhysicalAddress(const cec_logical_address iAddress)707 uint16_t CCECClient::GetDevicePhysicalAddress(const cec_logical_address iAddress)
708 {
709 CCECBusDevice *device = m_processor->GetDevice(iAddress);
710 if (device)
711 return device->GetPhysicalAddress(GetPrimaryLogicalAddress());
712 return CEC_INVALID_PHYSICAL_ADDRESS;
713 }
714
GetDevicePowerStatus(const cec_logical_address iAddress)715 cec_power_status CCECClient::GetDevicePowerStatus(const cec_logical_address iAddress)
716 {
717 CCECBusDevice *device = m_processor->GetDevice(iAddress);
718 if (device)
719 return device->GetPowerStatus(GetPrimaryLogicalAddress());
720 return CEC_POWER_STATUS_UNKNOWN;
721 }
722
GetDeviceVendorId(const cec_logical_address iAddress)723 uint32_t CCECClient::GetDeviceVendorId(const cec_logical_address iAddress)
724 {
725 CCECBusDevice *device = m_processor->GetDevice(iAddress);
726 if (device)
727 return device->GetVendorId(GetPrimaryLogicalAddress());
728 return CEC_VENDOR_UNKNOWN;
729 }
730
SendVolumeUp(bool bSendRelease)731 uint8_t CCECClient::SendVolumeUp(bool bSendRelease /* = true */)
732 {
733 cec_logical_address primary(GetPrimaryLogicalAddress());
734 CCECAudioSystem* audio(m_processor->GetAudioSystem());
735
736 if (primary == CECDEVICE_UNKNOWN)
737 return (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
738
739 if (!audio || !audio->IsPresent())
740 {
741 CCECTV* tv(m_processor->GetTV());
742 tv->TransmitVolumeUp(primary, bSendRelease);
743 return (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
744 }
745 else
746 {
747 return audio->VolumeUp(primary, bSendRelease);
748 }
749 }
750
SendVolumeDown(bool bSendRelease)751 uint8_t CCECClient::SendVolumeDown(bool bSendRelease /* = true */)
752 {
753 cec_logical_address primary(GetPrimaryLogicalAddress());
754 CCECAudioSystem* audio(m_processor->GetAudioSystem());
755
756 if (primary == CECDEVICE_UNKNOWN)
757 return (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
758
759 if (!audio || !audio->IsPresent())
760 {
761 CCECTV* tv(m_processor->GetTV());
762 tv->TransmitVolumeDown(primary, bSendRelease);
763 return (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
764 }
765 else
766 {
767 return audio->VolumeDown(primary, bSendRelease);
768 }
769 }
770
SendMuteAudio(void)771 uint8_t CCECClient::SendMuteAudio(void)
772 {
773 cec_logical_address primary(GetPrimaryLogicalAddress());
774 CCECAudioSystem* audio(m_processor->GetAudioSystem());
775
776 if (primary == CECDEVICE_UNKNOWN)
777 return (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
778
779 if (!audio || !audio->IsPresent())
780 {
781 CCECTV* tv(m_processor->GetTV());
782 tv->TransmitMuteAudio(primary);
783 return (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
784 }
785 else
786 {
787 return audio->MuteAudio(primary);
788 }
789
790 }
791
AudioToggleMute(void)792 uint8_t CCECClient::AudioToggleMute(void)
793 {
794 CCECBusDevice *device = GetPrimaryDevice();
795 CCECAudioSystem *audio = m_processor->GetAudioSystem();
796
797 return device && audio && audio->IsPresent() ?
798 audio->MuteAudio(device->GetLogicalAddress()) :
799 (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
800 }
801
AudioMute(void)802 uint8_t CCECClient::AudioMute(void)
803 {
804 CCECBusDevice *device = GetPrimaryDevice();
805 CCECAudioSystem *audio = m_processor->GetAudioSystem();
806 uint8_t iStatus = device && audio && audio->IsPresent() ? audio->GetAudioStatus(device->GetLogicalAddress()) : (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
807 if ((iStatus & CEC_AUDIO_MUTE_STATUS_MASK) != CEC_AUDIO_MUTE_STATUS_MASK)
808 iStatus = audio->MuteAudio(device->GetLogicalAddress());
809
810 return iStatus;
811 }
812
AudioUnmute(void)813 uint8_t CCECClient::AudioUnmute(void)
814 {
815 CCECBusDevice *device = GetPrimaryDevice();
816 CCECAudioSystem *audio = m_processor->GetAudioSystem();
817 uint8_t iStatus = device && audio && audio->IsPresent() ? audio->GetAudioStatus(device->GetLogicalAddress()) : (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
818 if ((iStatus & CEC_AUDIO_MUTE_STATUS_MASK) == CEC_AUDIO_MUTE_STATUS_MASK)
819 iStatus = audio->MuteAudio(device->GetLogicalAddress());
820
821 return iStatus;
822 }
823
AudioStatus(void)824 uint8_t CCECClient::AudioStatus(void)
825 {
826 CCECBusDevice *device = GetPrimaryDevice();
827 CCECAudioSystem *audio = m_processor->GetAudioSystem();
828 return device && audio && audio->IsPresent() ? audio->GetAudioStatus(device->GetLogicalAddress()) : (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
829 }
830
SendKeypress(const cec_logical_address iDestination,const cec_user_control_code key,bool bWait)831 bool CCECClient::SendKeypress(const cec_logical_address iDestination, const cec_user_control_code key, bool bWait /* = true */)
832 {
833 CCECBusDevice *dest = m_processor->GetDevice(iDestination);
834
835 return dest ?
836 dest->TransmitKeypress(GetPrimaryLogicalAddress(), key, bWait) :
837 false;
838 }
839
SendKeyRelease(const cec_logical_address iDestination,bool bWait)840 bool CCECClient::SendKeyRelease(const cec_logical_address iDestination, bool bWait /* = true */)
841 {
842 CCECBusDevice *dest = m_processor->GetDevice(iDestination);
843
844 return dest ?
845 dest->TransmitKeyRelease(GetPrimaryLogicalAddress(), bWait) :
846 false;
847 }
848
GetCurrentConfiguration(libcec_configuration & configuration)849 bool CCECClient::GetCurrentConfiguration(libcec_configuration &configuration)
850 {
851 CLockObject lock(m_mutex);
852 snprintf(configuration.strDeviceName, LIBCEC_OSD_NAME_SIZE, "%s", m_configuration.strDeviceName);
853 configuration.deviceTypes = m_configuration.deviceTypes;
854 configuration.bAutodetectAddress = m_configuration.bAutodetectAddress;
855 configuration.iPhysicalAddress = m_configuration.iPhysicalAddress;
856 configuration.baseDevice = m_configuration.baseDevice;
857 configuration.iHDMIPort = m_configuration.iHDMIPort;
858 configuration.clientVersion = m_configuration.clientVersion;
859 configuration.serverVersion = LIBCEC_VERSION_CURRENT;
860 configuration.tvVendor = m_configuration.tvVendor;
861 configuration.bGetSettingsFromROM = m_configuration.bGetSettingsFromROM;
862 configuration.bActivateSource = m_configuration.bActivateSource;
863 configuration.wakeDevices = m_configuration.wakeDevices;
864 configuration.powerOffDevices = m_configuration.powerOffDevices;
865 configuration.logicalAddresses = m_configuration.logicalAddresses;
866 configuration.iFirmwareVersion = m_configuration.iFirmwareVersion;
867 memcpy(configuration.strDeviceLanguage, m_configuration.strDeviceLanguage, 3);
868 configuration.iFirmwareBuildDate = m_configuration.iFirmwareBuildDate;
869 configuration.bMonitorOnly = m_configuration.bMonitorOnly;
870 configuration.cecVersion = m_configuration.cecVersion;
871 configuration.adapterType = m_configuration.adapterType;
872 configuration.iDoubleTapTimeoutMs = m_configuration.iDoubleTapTimeoutMs;
873 configuration.iButtonRepeatRateMs = m_configuration.iButtonRepeatRateMs;
874 configuration.iButtonReleaseDelayMs = m_configuration.iButtonReleaseDelayMs;
875 configuration.bAutoWakeAVR = m_configuration.bAutoWakeAVR;
876 #if CEC_LIB_VERSION_MAJOR >= 5
877 configuration.bAutoPowerOn = m_configuration.bAutoPowerOn;
878 #endif
879
880 return true;
881 }
882
SetConfiguration(const libcec_configuration & configuration)883 bool CCECClient::SetConfiguration(const libcec_configuration &configuration)
884 {
885 libcec_configuration defaultSettings;
886 bool bIsRunning(m_processor && m_processor->CECInitialised());
887 CCECBusDevice *primary = bIsRunning ? GetPrimaryDevice() : NULL;
888 uint16_t iPA = primary ? primary->GetCurrentPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS;
889
890 // update the callbacks
891 if (!!configuration.callbacks)
892 EnableCallbacks(configuration.callbackParam, configuration.callbacks);
893
894 // update the client version
895 SetClientVersion(configuration.clientVersion);
896
897 // update the OSD name
898 std::string strOSDName(configuration.strDeviceName);
899 SetOSDName(strOSDName);
900
901 // update the TV vendor override
902 SetTVVendorOverride((cec_vendor_id)configuration.tvVendor);
903
904 // just copy these
905 {
906 CLockObject lock(m_mutex);
907 m_configuration.bActivateSource = configuration.bActivateSource;
908 m_configuration.bGetSettingsFromROM = configuration.bGetSettingsFromROM;
909 m_configuration.wakeDevices = configuration.wakeDevices;
910 m_configuration.powerOffDevices = configuration.powerOffDevices;
911 memcpy(m_configuration.strDeviceLanguage, configuration.strDeviceLanguage, 3);
912 m_configuration.bMonitorOnly = configuration.bMonitorOnly;
913 m_configuration.cecVersion = configuration.cecVersion;
914 m_configuration.adapterType = configuration.adapterType;
915 m_configuration.iDoubleTapTimeoutMs = configuration.iDoubleTapTimeoutMs;
916 m_configuration.deviceTypes = configuration.deviceTypes;
917 m_configuration.comboKey = configuration.comboKey;
918 m_configuration.iComboKeyTimeoutMs = configuration.iComboKeyTimeoutMs;
919 m_configuration.iButtonRepeatRateMs = configuration.iButtonRepeatRateMs;
920 m_configuration.iButtonReleaseDelayMs = configuration.iButtonReleaseDelayMs;
921 m_configuration.bAutoWakeAVR = configuration.bAutoWakeAVR;
922 #if CEC_LIB_VERSION_MAJOR >= 5
923 if ((configuration.bAutoPowerOn == 0) || (configuration.bAutoPowerOn == 1))
924 m_configuration.bAutoPowerOn = configuration.bAutoPowerOn;
925 #endif
926 }
927
928 bool bNeedReinit(false);
929
930 // device types
931 if (m_processor->CECInitialised() &&
932 SetDeviceTypes(configuration.deviceTypes))
933 {
934 // the device type changed. just copy the rest, and re-register
935 {
936 CLockObject lock(m_mutex);
937 m_configuration.iPhysicalAddress = configuration.iPhysicalAddress;
938 m_configuration.baseDevice = configuration.baseDevice;
939 m_configuration.iHDMIPort = configuration.iHDMIPort;
940 bNeedReinit = true;
941 }
942 }
943 else if (
944 configuration.baseDevice != CECDEVICE_UNKNOWN &&
945 configuration.iHDMIPort > 0 &&
946 configuration.iHDMIPort <= 0xF)
947 {
948 // set the configured device+port
949 SetHDMIPort(configuration.baseDevice, configuration.iHDMIPort);
950 }
951 else
952 {
953 // set the physical address
954 SetPhysicalAddress(configuration);
955 }
956
957 SaveConfiguration(m_configuration);
958
959 if (!primary)
960 primary = GetPrimaryDevice();
961
962 if (bNeedReinit || !primary || primary->GetCurrentPhysicalAddress() != iPA)
963 {
964 // PA or device type changed
965 m_processor->RegisterClient(this);
966 }
967 else if (primary && configuration.bActivateSource == 1 && bIsRunning && !primary->IsActiveSource())
968 {
969 // activate the source if we're not already the active source
970 primary->ActivateSource();
971 }
972
973 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s: double tap timeout = %ums, repeat rate = %ums, release delay = %ums", __FUNCTION__, DoubleTapTimeoutMS(), m_configuration.iButtonRepeatRateMs, m_configuration.iButtonReleaseDelayMs);
974 return true;
975 }
976
AddCommand(const cec_command & command)977 void CCECClient::AddCommand(const cec_command &command)
978 {
979 // don't forward the standby opcode more than once every 10 seconds
980 if (command.opcode == CEC_OPCODE_STANDBY)
981 {
982 CLockObject lock(m_mutex);
983 if (m_iPreventForwardingPowerOffCommand != 0 &&
984 m_iPreventForwardingPowerOffCommand > GetTimeMs())
985 return;
986 else
987 m_iPreventForwardingPowerOffCommand = GetTimeMs() + CEC_FORWARD_STANDBY_MIN_INTERVAL;
988 }
989
990 if (command.destination == CECDEVICE_BROADCAST || GetLogicalAddresses().IsSet(command.destination))
991 {
992 LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %s (%X) -> %s (%X): %s (%2X)", ToString(command.initiator), command.initiator, ToString(command.destination), command.destination, ToString(command.opcode), command.opcode);
993 CallbackAddCommand(command);
994 }
995 }
996
AddKey(bool bSendComboKey,bool bButtonRelease)997 void CCECClient::AddKey(bool bSendComboKey /* = false */, bool bButtonRelease /* = false */)
998 {
999 cec_keypress key;
1000 key.keycode = CEC_USER_CONTROL_CODE_UNKNOWN;
1001
1002 {
1003 CLockObject lock(m_mutex);
1004 if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN)
1005 {
1006 unsigned int duration = (unsigned int) (GetTimeMs() - m_updateButtontime);
1007 key.duration = (unsigned int) (GetTimeMs() - m_initialButtontime);
1008
1009 if (duration > m_configuration.iComboKeyTimeoutMs ||
1010 m_configuration.iComboKeyTimeoutMs == 0 ||
1011 m_iCurrentButton != m_configuration.comboKey ||
1012 bSendComboKey)
1013 {
1014 key.keycode = m_iCurrentButton;
1015
1016 m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
1017 m_initialButtontime = 0;
1018 m_updateButtontime = 0;
1019 m_repeatButtontime = 0;
1020 m_releaseButtontime = 0;
1021 m_pressedButtoncount = 0;
1022 m_releasedButtoncount = 0;
1023 }
1024 }
1025 }
1026
1027 // we don't forward releases when supporting repeating keys
1028 if (bButtonRelease && m_configuration.iButtonRepeatRateMs)
1029 return;
1030
1031 if (key.keycode != CEC_USER_CONTROL_CODE_UNKNOWN)
1032 {
1033 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key released: %s (%1x) D:%dms", ToString(key.keycode), key.keycode, key.duration);
1034 QueueAddKey(key);
1035 }
1036 }
1037
AddKey(const cec_keypress & key)1038 void CCECClient::AddKey(const cec_keypress &key)
1039 {
1040 if (key.keycode > CEC_USER_CONTROL_CODE_MAX ||
1041 key.keycode < CEC_USER_CONTROL_CODE_SELECT)
1042 {
1043 // send back the previous key if there is one
1044 LIB_CEC->AddLog(CEC_LOG_DEBUG, "Unexpected key %s (%1x) D:%dms", ToString(key.keycode), key.keycode, key.duration);
1045 AddKey();
1046 return;
1047 }
1048 bool isrepeat = false;
1049 cec_keypress transmitKey(key);
1050 cec_user_control_code comboKey(m_configuration.comboKey);
1051
1052 {
1053 CLockObject lock(m_mutex);
1054 if (m_configuration.iComboKeyTimeoutMs > 0 && m_iCurrentButton == comboKey && key.duration == 0)
1055 {
1056 // stop + ok -> exit
1057 if (key.keycode == CEC_USER_CONTROL_CODE_SELECT)
1058 transmitKey.keycode = CEC_USER_CONTROL_CODE_EXIT;
1059 // stop + pause -> root menu
1060 else if (key.keycode == CEC_USER_CONTROL_CODE_PAUSE)
1061 transmitKey.keycode = CEC_USER_CONTROL_CODE_ROOT_MENU;
1062 // stop + play -> dot (which is handled as context menu in xbmc)
1063 else if (key.keycode == CEC_USER_CONTROL_CODE_PLAY)
1064 transmitKey.keycode = CEC_USER_CONTROL_CODE_DOT;
1065 // default, send back the previous key
1066 else
1067 {
1068 LIB_CEC->AddLog(CEC_LOG_DEBUG, "Combo key %s (%1x) D%dms:", ToString(key.keycode), key.keycode, key.duration);
1069 AddKey(true);
1070 }
1071 }
1072
1073 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key pressed: %s (%1x) current(%lx) duration(%d)", ToString(transmitKey.keycode), transmitKey.keycode, m_iCurrentButton, key.duration);
1074
1075 if (m_iCurrentButton == key.keycode)
1076 {
1077 m_updateButtontime = GetTimeMs();
1078 m_releaseButtontime = m_updateButtontime + (m_configuration.iButtonReleaseDelayMs ? m_configuration.iButtonReleaseDelayMs : CEC_BUTTON_TIMEOUT);
1079 // want to have seen some updated before considering a repeat
1080 if (m_configuration.iButtonRepeatRateMs)
1081 {
1082 if (!m_repeatButtontime && m_pressedButtoncount > 1)
1083 m_repeatButtontime = m_initialButtontime + DoubleTapTimeoutMS();
1084 isrepeat = true;
1085 }
1086 m_pressedButtoncount++;
1087 }
1088 else
1089 {
1090 if (m_iCurrentButton != transmitKey.keycode)
1091 {
1092 LIB_CEC->AddLog(CEC_LOG_DEBUG, "Changed key %s (%1x) D:%dms cur:%lx", ToString(transmitKey.keycode), transmitKey.keycode, transmitKey.duration, m_iCurrentButton);
1093 AddKey();
1094 }
1095 if (key.duration == 0)
1096 {
1097 m_iCurrentButton = transmitKey.keycode;
1098 if (m_iCurrentButton == CEC_USER_CONTROL_CODE_UNKNOWN)
1099 {
1100 m_initialButtontime = 0;
1101 m_updateButtontime = 0;
1102 m_repeatButtontime = 0;
1103 m_releaseButtontime = 0;
1104 m_pressedButtoncount = 0;
1105 m_releasedButtoncount = 0;
1106 }
1107 else
1108 {
1109 m_initialButtontime = GetTimeMs();
1110 m_updateButtontime = m_initialButtontime;
1111 m_repeatButtontime = 0; // set this on next update
1112 m_releaseButtontime = m_initialButtontime + (m_configuration.iButtonReleaseDelayMs ? m_configuration.iButtonReleaseDelayMs : CEC_BUTTON_TIMEOUT);
1113 m_pressedButtoncount = 1;
1114 m_releasedButtoncount = 0;
1115 }
1116 }
1117 }
1118 }
1119
1120 if (!isrepeat && (key.keycode != comboKey || key.duration > 0))
1121 {
1122 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key pressed: %s (%1x, %d)", ToString(transmitKey.keycode), transmitKey.keycode, transmitKey.duration);
1123 QueueAddKey(transmitKey);
1124 }
1125 }
1126
SetCurrentButton(const cec_user_control_code iButtonCode)1127 void CCECClient::SetCurrentButton(const cec_user_control_code iButtonCode)
1128 {
1129 // push a keypress to the buffer with 0 duration and another with the duration set when released
1130 cec_keypress key;
1131 key.duration = 0;
1132 key.keycode = iButtonCode;
1133
1134 LIB_CEC->AddLog(CEC_LOG_DEBUG, "SetCurrentButton %s (%1x) D:%dms cur:%lx", ToString(key.keycode), key.keycode, key.duration);
1135 AddKey(key);
1136 }
1137
CheckKeypressTimeout(void)1138 uint16_t CCECClient::CheckKeypressTimeout(void)
1139 {
1140 // time when we'd like to be called again
1141 uint64_t timeout = CEC_PROCESSOR_SIGNAL_WAIT_TIME;
1142 cec_keypress key;
1143 key.keycode = CEC_USER_CONTROL_CODE_UNKNOWN;
1144 key.duration = 0;
1145
1146 if (m_iCurrentButton == CEC_USER_CONTROL_CODE_UNKNOWN)
1147 return (uint16_t)timeout;
1148 {
1149 CLockObject lock(m_mutex);
1150 uint64_t iNow = GetTimeMs();
1151 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s T:%.3f", __FUNCTION__, iNow*1e-3);
1152 cec_user_control_code comboKey(m_configuration.comboKey);
1153 uint32_t iTimeoutMs(m_configuration.iComboKeyTimeoutMs);
1154
1155 if (m_iCurrentButton == comboKey && iTimeoutMs > 0 && iNow - m_updateButtontime >= iTimeoutMs)
1156 {
1157 key.duration = (unsigned int) (iNow - m_initialButtontime);
1158 key.keycode = m_iCurrentButton;
1159
1160 m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
1161 m_initialButtontime = 0;
1162 m_updateButtontime = 0;
1163 m_repeatButtontime = 0;
1164 m_releaseButtontime = 0;
1165 m_pressedButtoncount = 0;
1166 m_releasedButtoncount = 0;
1167 }
1168 else if (m_iCurrentButton != comboKey && m_releaseButtontime && iNow >= (uint64_t)m_releaseButtontime)
1169 {
1170 key.duration = (unsigned int) (iNow - m_initialButtontime);
1171 key.keycode = CEC_USER_CONTROL_CODE_UNKNOWN;
1172
1173 m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
1174 m_initialButtontime = 0;
1175 m_updateButtontime = 0;
1176 m_repeatButtontime = 0;
1177 m_releaseButtontime = 0;
1178 m_pressedButtoncount = 0;
1179 m_releasedButtoncount = 0;
1180 }
1181 else if (m_iCurrentButton != comboKey && m_repeatButtontime && iNow >= (uint64_t)m_repeatButtontime)
1182 {
1183 key.duration = (unsigned int) (iNow - m_initialButtontime);
1184 key.keycode = m_iCurrentButton;
1185 m_repeatButtontime = iNow + m_configuration.iButtonRepeatRateMs;
1186 timeout = std::min((uint64_t)timeout, m_repeatButtontime - iNow);
1187 }
1188 else
1189 {
1190 if (m_iCurrentButton == comboKey && iTimeoutMs > 0)
1191 timeout = std::min((uint64_t)timeout, m_updateButtontime - iNow + iTimeoutMs);
1192 if (m_iCurrentButton != comboKey && m_releaseButtontime)
1193 timeout = std::min((uint64_t)timeout, m_releaseButtontime - iNow);
1194 if (m_iCurrentButton != comboKey && m_repeatButtontime)
1195 timeout = std::min((uint64_t)timeout, m_repeatButtontime - iNow);
1196 if (timeout > CEC_PROCESSOR_SIGNAL_WAIT_TIME)
1197 {
1198 LIB_CEC->AddLog(CEC_LOG_ERROR, "Unexpected timeout: %d (%.3f %.3f %.3f) k:%02x", timeout, iNow*1e-3, m_updateButtontime*1e-3, m_releaseButtontime*1e-3, m_iCurrentButton);
1199 timeout = CEC_PROCESSOR_SIGNAL_WAIT_TIME;
1200 }
1201 }
1202 LIB_CEC->AddLog(CEC_LOG_DEBUG, "Key %s: %s (duration:%d) (%1x) timeout:%dms (rel:%d,rep:%d,prs:%d,rel:%d)", ToString(m_iCurrentButton), key.keycode == CEC_USER_CONTROL_CODE_UNKNOWN ? "idle" : m_repeatButtontime ? "repeated" : "released", key.duration,
1203 m_iCurrentButton, timeout, (int)(m_releaseButtontime ? m_releaseButtontime - iNow : 0), (int)(m_repeatButtontime ? m_repeatButtontime - iNow : 0), m_pressedButtoncount, m_releasedButtoncount);
1204 }
1205
1206 if (key.keycode != CEC_USER_CONTROL_CODE_UNKNOWN)
1207 QueueAddKey(key);
1208
1209 return (uint16_t)timeout;
1210 }
1211
EnableCallbacks(void * cbParam,ICECCallbacks * callbacks)1212 bool CCECClient::EnableCallbacks(void *cbParam, ICECCallbacks *callbacks)
1213 {
1214 CLockObject lock(m_cbMutex);
1215 m_configuration.callbackParam = cbParam;
1216 m_configuration.callbacks = callbacks;
1217 return true;
1218 }
1219
PingAdapter(void)1220 bool CCECClient::PingAdapter(void)
1221 {
1222 return m_processor ? m_processor->PingAdapter() : false;
1223 }
1224
GetConnectionInfo(void)1225 std::string CCECClient::GetConnectionInfo(void)
1226 {
1227 std::string strLog;
1228 strLog = StringUtils::Format("libCEC version = %s, client version = %s, firmware version = %d",
1229 CCECTypeUtils::VersionToString(m_configuration.serverVersion).c_str(),
1230 CCECTypeUtils::VersionToString(m_configuration.clientVersion).c_str(),
1231 m_configuration.iFirmwareVersion);
1232 if (m_configuration.iFirmwareBuildDate != CEC_FW_BUILD_UNKNOWN)
1233 {
1234 time_t buildTime = (time_t)m_configuration.iFirmwareBuildDate;
1235 strLog += StringUtils::Format(", firmware build date: %s", asctime(gmtime(&buildTime)));
1236 strLog = strLog.substr(0, strLog.length() > 0 ? (size_t)(strLog.length() - 1) : 0); // strip \n added by asctime
1237 strLog.append(" +0000");
1238 }
1239
1240 // log the addresses that are being used
1241 if (!m_configuration.logicalAddresses.IsEmpty())
1242 {
1243 strLog.append(", logical address(es) = ");
1244 CECDEVICEVEC devices;
1245 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
1246 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
1247 strLog += StringUtils::Format("%s (%X) ", (*it)->GetLogicalAddressName(), (*it)->GetLogicalAddress());
1248 }
1249
1250 if (!CLibCEC::IsValidPhysicalAddress(m_configuration.iPhysicalAddress))
1251 strLog += StringUtils::Format(", base device: %s (%X), HDMI port number: %d", ToString(m_configuration.baseDevice), m_configuration.baseDevice, m_configuration.iHDMIPort);
1252 uint16_t iPhysicalAddress = GetPrimaryDevice()->GetPhysicalAddress(GetLogicalAddresses().primary, false);
1253 strLog += StringUtils::Format(", physical address: %x.%x.%x.%x", (iPhysicalAddress >> 12) & 0xF, (iPhysicalAddress >> 8) & 0xF, (iPhysicalAddress >> 4) & 0xF, iPhysicalAddress & 0xF);
1254
1255 strLog += StringUtils::Format(", %s", LIB_CEC->GetLibInfo());
1256
1257 std::string strReturn(strLog.c_str());
1258 return strReturn;
1259 }
1260
SetTVVendorOverride(const cec_vendor_id id)1261 void CCECClient::SetTVVendorOverride(const cec_vendor_id id)
1262 {
1263 {
1264 CLockObject lock(m_mutex);
1265 m_configuration.tvVendor = id;
1266 }
1267
1268 if (id != CEC_VENDOR_UNKNOWN)
1269 {
1270 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vendor id '%s'", __FUNCTION__, ToString(id));
1271
1272 CCECBusDevice *tv = m_processor ? m_processor->GetTV() : NULL;
1273 if (tv)
1274 tv->SetVendorId((uint32_t)id);
1275 }
1276
1277 SaveConfiguration(m_configuration);
1278 }
1279
GetTVVendorOverride(void)1280 cec_vendor_id CCECClient::GetTVVendorOverride(void)
1281 {
1282 CLockObject lock(m_mutex);
1283 return (cec_vendor_id)m_configuration.tvVendor;
1284 }
1285
SetOSDName(const std::string & strDeviceName)1286 void CCECClient::SetOSDName(const std::string &strDeviceName)
1287 {
1288 {
1289 CLockObject lock(m_mutex);
1290 if (!strncmp(m_configuration.strDeviceName, strDeviceName.c_str(), LIBCEC_OSD_NAME_SIZE)) return;
1291 snprintf(m_configuration.strDeviceName, LIBCEC_OSD_NAME_SIZE, "%s", strDeviceName.c_str());
1292 }
1293
1294 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using OSD name '%s'", __FUNCTION__, strDeviceName.c_str());
1295
1296 CCECBusDevice *primary = GetPrimaryDevice();
1297 if (primary && primary->GetCurrentOSDName() != strDeviceName.c_str())
1298 {
1299 primary->SetOSDName(strDeviceName);
1300 if (m_processor && m_processor->CECInitialised())
1301 primary->TransmitOSDName(CECDEVICE_TV, false);
1302 }
1303
1304 SaveConfiguration(m_configuration);
1305 }
1306
GetOSDName(void)1307 std::string CCECClient::GetOSDName(void)
1308 {
1309 CLockObject lock(m_mutex);
1310 std::string strOSDName(m_configuration.strDeviceName);
1311 return strOSDName;
1312 }
1313
SetWakeDevices(const cec_logical_addresses & addresses)1314 void CCECClient::SetWakeDevices(const cec_logical_addresses &addresses)
1315 {
1316 {
1317 CLockObject lock(m_mutex);
1318 m_configuration.wakeDevices = addresses;
1319 }
1320 SaveConfiguration(m_configuration);
1321 }
1322
GetWakeDevices(void)1323 cec_logical_addresses CCECClient::GetWakeDevices(void)
1324 {
1325 CLockObject lock(m_mutex);
1326 return m_configuration.wakeDevices;
1327 }
1328
AutodetectPhysicalAddress(void)1329 bool CCECClient::AutodetectPhysicalAddress(void)
1330 {
1331 uint16_t iPhysicalAddress = !!m_processor ?
1332 m_processor->GetDetectedPhysicalAddress() :
1333 CEC_INVALID_PHYSICAL_ADDRESS;
1334 CLockObject lock(m_mutex);
1335 if (CLibCEC::IsValidPhysicalAddress(iPhysicalAddress) &&
1336 (iPhysicalAddress != CEC_PHYSICAL_ADDRESS_TV))
1337 {
1338 m_configuration.iPhysicalAddress = iPhysicalAddress;
1339 m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE;
1340 m_configuration.baseDevice = CECDEVICE_UNKNOWN;
1341 m_configuration.bAutodetectAddress = 1;
1342 return true;
1343 }
1344
1345 m_configuration.bAutodetectAddress = 0;
1346 return false;
1347 }
1348
SetClientVersion(uint32_t version)1349 void CCECClient::SetClientVersion(uint32_t version)
1350 {
1351 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using client version '%s'", __FUNCTION__, CCECTypeUtils::VersionToString(version).c_str());
1352
1353 CLockObject lock(m_mutex);
1354 m_configuration.clientVersion = (uint32_t)version;
1355 }
1356
GetClientVersion(void)1357 uint32_t CCECClient::GetClientVersion(void)
1358 {
1359 CLockObject lock(m_mutex);
1360 return m_configuration.clientVersion;
1361 }
1362
SetDeviceTypes(const cec_device_type_list & deviceTypes)1363 bool CCECClient::SetDeviceTypes(const cec_device_type_list &deviceTypes)
1364 {
1365 bool bNeedReinit(false);
1366
1367 {
1368 CLockObject lock(m_mutex);
1369 bNeedReinit = m_processor && m_processor->CECInitialised() &&
1370 (m_configuration.deviceTypes != deviceTypes);
1371 m_configuration.deviceTypes = deviceTypes;
1372 }
1373
1374 SaveConfiguration(m_configuration);
1375
1376 if (bNeedReinit)
1377 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using primary device type '%s'", __FUNCTION__, ToString(deviceTypes[0]));
1378
1379 return bNeedReinit;
1380 }
1381
GetDeviceTypes(void)1382 cec_device_type_list CCECClient::GetDeviceTypes(void)
1383 {
1384 cec_device_type_list retVal;
1385 CLockObject lock(m_mutex);
1386 retVal = m_configuration.deviceTypes;
1387 return retVal;
1388 }
1389
SetDevicePhysicalAddress(const uint16_t iPhysicalAddress)1390 bool CCECClient::SetDevicePhysicalAddress(const uint16_t iPhysicalAddress)
1391 {
1392 if (!CLibCEC::IsValidPhysicalAddress(iPhysicalAddress))
1393 {
1394 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - not setting invalid physical address %04x", __FUNCTION__, iPhysicalAddress);
1395 return false;
1396 }
1397
1398 // reconfigure all devices
1399 cec_logical_address reactivateSource(CECDEVICE_UNKNOWN);
1400 CECDEVICEVEC devices;
1401 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
1402 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
1403 {
1404 // if this device was the active source, reactivate it afterwards
1405 if ((*it)->IsActiveSource())
1406 reactivateSource = (*it)->GetLogicalAddress();
1407
1408 // mark the device as inactive source
1409 if (IsInitialised())
1410 (*it)->MarkAsInactiveSource();
1411
1412 // set the new physical address
1413 (*it)->SetPhysicalAddress(iPhysicalAddress);
1414
1415 // and transmit it
1416 if (IsInitialised())
1417 (*it)->TransmitPhysicalAddress(false);
1418 }
1419
1420 // reactivate the previous active source
1421 if (reactivateSource != CECDEVICE_UNKNOWN &&
1422 m_processor->CECInitialised() &&
1423 IsInitialised())
1424 {
1425 CCECBusDevice *device = m_processor->GetDevice(reactivateSource);
1426 if (device)
1427 device->ActivateSource();
1428 }
1429
1430 SaveConfiguration(m_configuration);
1431
1432 return true;
1433 }
1434
SwitchMonitoring(bool bEnable)1435 bool CCECClient::SwitchMonitoring(bool bEnable)
1436 {
1437 LIB_CEC->AddLog(CEC_LOG_NOTICE, "== %s monitoring mode ==", bEnable ? "enabling" : "disabling");
1438
1439 if (m_processor)
1440 {
1441 m_processor->SwitchMonitoring(bEnable);
1442 m_configuration.bMonitorOnly = bEnable;
1443 return bEnable ? true: m_processor->RegisterClient(this);
1444 }
1445
1446 return false;
1447 }
1448
PollDevice(const cec_logical_address iAddress)1449 bool CCECClient::PollDevice(const cec_logical_address iAddress)
1450 {
1451 // try to find the primary device
1452 CCECBusDevice *primary = GetPrimaryDevice();
1453 // poll the destination, with the primary as source
1454 if (primary)
1455 return primary->TransmitPoll(iAddress, true);
1456
1457 return m_processor ? m_processor->PollDevice(iAddress) : false;
1458 }
1459
GetActiveDevices(void)1460 cec_logical_addresses CCECClient::GetActiveDevices(void)
1461 {
1462 CECDEVICEVEC activeDevices;
1463 if (m_processor)
1464 m_processor->GetDevices()->GetActive(activeDevices);
1465 return CCECDeviceMap::ToLogicalAddresses(activeDevices);
1466 }
1467
IsActiveDevice(const cec_logical_address iAddress)1468 bool CCECClient::IsActiveDevice(const cec_logical_address iAddress)
1469 {
1470 cec_logical_addresses activeDevices = GetActiveDevices();
1471 return activeDevices.IsSet(iAddress);
1472 }
1473
IsActiveDeviceType(const cec_device_type type)1474 bool CCECClient::IsActiveDeviceType(const cec_device_type type)
1475 {
1476 return m_processor->GetDevices()->IsActiveType(type, false);
1477 }
1478
GetActiveSource(void)1479 cec_logical_address CCECClient::GetActiveSource(void)
1480 {
1481 return m_processor ? m_processor->GetActiveSource() : CECDEVICE_UNKNOWN;
1482 }
1483
IsActiveSource(const cec_logical_address iAddress)1484 bool CCECClient::IsActiveSource(const cec_logical_address iAddress)
1485 {
1486 return m_processor ? m_processor->IsActiveSource(iAddress) : false;
1487 }
1488
SetStreamPath(const cec_logical_address iAddress)1489 bool CCECClient::SetStreamPath(const cec_logical_address iAddress)
1490 {
1491 uint16_t iPhysicalAddress = GetDevicePhysicalAddress(iAddress);
1492 if (iPhysicalAddress != CEC_INVALID_PHYSICAL_ADDRESS)
1493 return SetStreamPath(iPhysicalAddress);
1494 return false;
1495 }
1496
SetStreamPath(const uint16_t iPhysicalAddress)1497 bool CCECClient::SetStreamPath(const uint16_t iPhysicalAddress)
1498 {
1499 bool bReturn(false);
1500
1501 CCECBusDevice *device = GetDeviceByType(CEC_DEVICE_TYPE_TV);
1502 if (device)
1503 {
1504 device->SetStreamPath(iPhysicalAddress);
1505 bReturn = device->GetHandler()->TransmitSetStreamPath(iPhysicalAddress, false);
1506 device->MarkHandlerReady();
1507 }
1508 else
1509 {
1510 LIB_CEC->AddLog(CEC_LOG_ERROR, "only the TV is allowed to send CEC_OPCODE_SET_STREAM_PATH");
1511 }
1512
1513 return bReturn;
1514 }
1515
GetLogicalAddresses(void)1516 cec_logical_addresses CCECClient::GetLogicalAddresses(void)
1517 {
1518 cec_logical_addresses addresses;
1519 CLockObject lock(m_mutex);
1520 addresses = m_configuration.logicalAddresses;
1521 return addresses;
1522 }
1523
CanSaveConfiguration(void)1524 bool CCECClient::CanSaveConfiguration(void)
1525 {
1526 return m_processor ? m_processor->CanSaveConfiguration() : false;
1527 }
1528
SaveConfiguration(const libcec_configuration & configuration)1529 bool CCECClient::SaveConfiguration(const libcec_configuration &configuration)
1530 {
1531 return m_processor && IsRegistered() ?
1532 m_processor->SaveConfiguration(configuration) :
1533 false;
1534 }
1535
RescanActiveDevices(void)1536 void CCECClient::RescanActiveDevices(void)
1537 {
1538 if (m_processor)
1539 m_processor->RescanActiveDevices();
1540 }
1541
IsLibCECActiveSource(void)1542 bool CCECClient::IsLibCECActiveSource(void)
1543 {
1544 bool bReturn(false);
1545 if (m_processor)
1546 {
1547 cec_logical_address activeSource = m_processor->GetActiveSource();
1548 CCECBusDevice *device = m_processor->GetDevice(activeSource);
1549 if (device)
1550 bReturn = device->IsHandledByLibCEC() && !device->GetHandler()->ActiveSourcePending();
1551 }
1552 return bReturn;
1553 }
1554
SourceActivated(const cec_logical_address logicalAddress)1555 void CCECClient::SourceActivated(const cec_logical_address logicalAddress)
1556 {
1557 LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> source activated: %s (%x)", ToString(logicalAddress), logicalAddress);
1558 QueueSourceActivated(true, logicalAddress);
1559 }
1560
SourceDeactivated(const cec_logical_address logicalAddress)1561 void CCECClient::SourceDeactivated(const cec_logical_address logicalAddress)
1562 {
1563 LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> source deactivated: %s (%x)", ToString(logicalAddress), logicalAddress);
1564 QueueSourceActivated(false, logicalAddress);
1565 }
1566
CallbackAddCommand(const cec_command & command)1567 void CCECClient::CallbackAddCommand(const cec_command &command)
1568 {
1569 CLockObject lock(m_cbMutex);
1570 if (m_configuration.callbacks && !!m_configuration.callbacks->commandReceived)
1571 m_configuration.callbacks->commandReceived(m_configuration.callbackParam, &command);
1572 }
1573
DoubleTapTimeoutMS(void)1574 uint32_t CCECClient::DoubleTapTimeoutMS(void)
1575 {
1576 CLockObject lock(m_mutex);
1577 return m_configuration.iDoubleTapTimeoutMs;
1578 }
1579
QueueAddCommand(const cec_command & command)1580 void CCECClient::QueueAddCommand(const cec_command& command)
1581 {
1582 m_callbackCalls.Push(new CCallbackWrap(command));
1583 }
1584
QueueAddKey(const cec_keypress & key)1585 void CCECClient::QueueAddKey(const cec_keypress& key)
1586 {
1587 m_callbackCalls.Push(new CCallbackWrap(key));
1588 }
1589
QueueAddLog(const cec_log_message_cpp & message)1590 void CCECClient::QueueAddLog(const cec_log_message_cpp& message)
1591 {
1592 m_callbackCalls.Push(new CCallbackWrap(message));
1593 }
1594
QueueAlert(const libcec_alert type,const libcec_parameter & param)1595 void CCECClient::QueueAlert(const libcec_alert type, const libcec_parameter& param)
1596 {
1597 m_callbackCalls.Push(new CCallbackWrap(type, param));
1598 }
1599
QueueConfigurationChanged(const libcec_configuration & config)1600 void CCECClient::QueueConfigurationChanged(const libcec_configuration& config)
1601 {
1602 m_callbackCalls.Push(new CCallbackWrap(config));
1603 }
1604
QueueMenuStateChanged(const cec_menu_state newState)1605 int CCECClient::QueueMenuStateChanged(const cec_menu_state newState)
1606 {
1607 CCallbackWrap *wrapState = new CCallbackWrap(newState, true);
1608 m_callbackCalls.Push(wrapState);
1609 int result(wrapState->Result(1000));
1610
1611 delete wrapState;
1612 return result;
1613 }
1614
QueueSourceActivated(bool bActivated,const cec_logical_address logicalAddress)1615 void CCECClient::QueueSourceActivated(bool bActivated, const cec_logical_address logicalAddress)
1616 {
1617 m_callbackCalls.Push(new CCallbackWrap(bActivated, logicalAddress));
1618 }
1619
Process(void)1620 void* CCECClient::Process(void)
1621 {
1622 CCallbackWrap* cb(NULL);
1623 while (!IsStopped())
1624 {
1625 if (m_callbackCalls.Pop(cb, 500))
1626 {
1627 try
1628 {
1629 switch (cb->m_type)
1630 {
1631 case CCallbackWrap::CEC_CB_LOG_MESSAGE:
1632 CallbackAddLog(cb->m_message);
1633 break;
1634 case CCallbackWrap::CEC_CB_KEY_PRESS:
1635 CallbackAddKey(cb->m_key);
1636 break;
1637 case CCallbackWrap::CEC_CB_COMMAND:
1638 AddCommand(cb->m_command);
1639 break;
1640 case CCallbackWrap::CEC_CB_ALERT:
1641 CallbackAlert(cb->m_alertType, cb->m_alertParam);
1642 break;
1643 case CCallbackWrap::CEC_CB_CONFIGURATION:
1644 CallbackConfigurationChanged(cb->m_config);
1645 break;
1646 case CCallbackWrap::CEC_CB_MENU_STATE:
1647 cb->Report(CallbackMenuStateChanged(cb->m_menuState));
1648 break;
1649 case CCallbackWrap::CEC_CB_SOURCE_ACTIVATED:
1650 CallbackSourceActivated(cb->m_bActivated, cb->m_logicalAddress);
1651 break;
1652 default:
1653 break;
1654 }
1655
1656 if (!cb->m_keepResult)
1657 delete cb;
1658 } catch (...)
1659 {
1660 // don't log a warning but let the app deal with this
1661 delete cb;
1662 }
1663 }
1664 }
1665 return NULL;
1666 }
1667
CallbackAddKey(const cec_keypress & key)1668 void CCECClient::CallbackAddKey(const cec_keypress &key)
1669 {
1670 CLockObject lock(m_cbMutex);
1671 if (!!m_configuration.callbacks &&
1672 !!m_configuration.callbacks->keyPress)
1673 {
1674 m_configuration.callbacks->keyPress(m_configuration.callbackParam, &key);
1675 }
1676 }
1677
CallbackAddLog(const cec_log_message_cpp & message)1678 void CCECClient::CallbackAddLog(const cec_log_message_cpp &message)
1679 {
1680 CLockObject lock(m_cbMutex);
1681 if (!!m_configuration.callbacks &&
1682 !!m_configuration.callbacks->logMessage)
1683 {
1684 cec_log_message toClient;
1685 toClient.level = message.level;
1686 toClient.message = message.message.c_str();
1687 toClient.time = message.time;
1688 m_configuration.callbacks->logMessage(m_configuration.callbackParam, &toClient);
1689 }
1690 }
1691
CallbackConfigurationChanged(const libcec_configuration & config)1692 void CCECClient::CallbackConfigurationChanged(const libcec_configuration &config)
1693 {
1694 CLockObject lock(m_cbMutex);
1695 if (!!m_configuration.callbacks &&
1696 !!m_configuration.callbacks->configurationChanged &&
1697 m_processor->CECInitialised())
1698 {
1699 m_configuration.callbacks->configurationChanged(m_configuration.callbackParam, &config);
1700 }
1701 }
1702
CallbackSourceActivated(bool bActivated,const cec_logical_address logicalAddress)1703 void CCECClient::CallbackSourceActivated(bool bActivated, const cec_logical_address logicalAddress)
1704 {
1705 CLockObject lock(m_cbMutex);
1706 if (!!m_configuration.callbacks &&
1707 !!m_configuration.callbacks->sourceActivated)
1708 {
1709 m_configuration.callbacks->sourceActivated(m_configuration.callbackParam, logicalAddress, bActivated ? 1 : 0);
1710 }
1711 }
1712
CallbackAlert(const libcec_alert type,const libcec_parameter & param)1713 void CCECClient::CallbackAlert(const libcec_alert type, const libcec_parameter ¶m)
1714 {
1715 CLockObject lock(m_cbMutex);
1716 if (!!m_configuration.callbacks &&
1717 !!m_configuration.callbacks->alert)
1718 {
1719 m_configuration.callbacks->alert(m_configuration.callbackParam, type, param);
1720 }
1721 }
1722
CallbackMenuStateChanged(const cec_menu_state newState)1723 int CCECClient::CallbackMenuStateChanged(const cec_menu_state newState)
1724 {
1725 LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %s: %s", ToString(CEC_OPCODE_MENU_REQUEST), ToString(newState));
1726
1727 CLockObject lock(m_cbMutex);
1728 if (!!m_configuration.callbacks &&
1729 !!m_configuration.callbacks->menuStateChanged)
1730 {
1731 return m_configuration.callbacks->menuStateChanged(m_configuration.callbackParam, newState);
1732 }
1733 return 0;
1734 }
1735
AudioEnable(bool enable)1736 bool CCECClient::AudioEnable(bool enable)
1737 {
1738 CCECBusDevice* device = enable ? GetPrimaryDevice() : nullptr;
1739 CCECAudioSystem* audio = m_processor->GetAudioSystem();
1740
1741 return !!audio ?
1742 audio->EnableAudio(device) :
1743 false;
1744 }
1745
1746 #if CEC_LIB_VERSION_MAJOR >= 5
GetStats(struct cec_adapter_stats * stats)1747 bool CCECClient::GetStats(struct cec_adapter_stats* stats)
1748 {
1749 return !!m_processor ?
1750 m_processor->GetStats(stats) :
1751 false;
1752 }
1753 #endif
1754