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 &param)
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