cec: fixed - delayed source switch time was reset, resulting in an attempt every...
[deb_libcec.git] / src / lib / CECClient.cpp
CommitLineData
004b8382
LOK
1/*
2 * This file is part of the libCEC(R) library.
3 *
4 * libCEC(R) is Copyright (C) 2011-2012 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 *
23 *
24 * Alternatively, you can license this library under a commercial license,
25 * please contact Pulse-Eight Licensing for more information.
26 *
27 * For more information contact:
28 * Pulse-Eight Licensing <license@pulse-eight.com>
29 * http://www.pulse-eight.com/
30 * http://www.pulse-eight.net/
31 */
32
2b44051c 33#include "env.h"
004b8382 34#include "CECClient.h"
2b44051c 35
004b8382
LOK
36#include "CECProcessor.h"
37#include "LibCEC.h"
0d800fe5 38#include "CECTypeUtils.h"
004b8382
LOK
39#include "devices/CECPlaybackDevice.h"
40#include "devices/CECAudioSystem.h"
41#include "devices/CECTV.h"
42d28d15 42#include "implementations/CECCommandHandler.h"
004b8382
LOK
43
44using namespace CEC;
45using namespace PLATFORM;
46
47#define LIB_CEC m_processor->GetLib()
0d800fe5 48#define ToString(x) CCECTypeUtils::ToString(x)
004b8382 49
c0152c09 50CCECClient::CCECClient(CCECProcessor *processor, const libcec_configuration &configuration) :
004b8382
LOK
51 m_processor(processor),
52 m_bInitialised(false),
53 m_bRegistered(false),
54 m_iCurrentButton(CEC_USER_CONTROL_CODE_UNKNOWN),
55c18d43
LOK
55 m_buttontime(0),
56 m_iPreventForwardingPowerOffCommand(0)
004b8382 57{
38f1fbcc 58 m_configuration.Clear();
c0152c09 59 // set the initial configuration
004b8382
LOK
60 SetConfiguration(configuration);
61}
62
63CCECClient::~CCECClient(void)
64{
c0152c09
LOK
65 // unregister the client
66 if (m_processor && IsRegistered())
004b8382
LOK
67 m_processor->UnregisterClient(this);
68}
69
70bool CCECClient::IsInitialised(void)
71{
72 CLockObject lock(m_mutex);
73 return m_bInitialised && m_processor;
74}
75
76void CCECClient::SetInitialised(bool bSetTo)
77{
78 CLockObject lock(m_mutex);
79 m_bInitialised = bSetTo;
80}
81
82bool CCECClient::IsRegistered(void)
83{
84 CLockObject lock(m_mutex);
85 return m_bRegistered && m_processor;
86}
87
88void CCECClient::SetRegistered(bool bSetTo)
89{
90 CLockObject lock(m_mutex);
91 m_bRegistered = bSetTo;
92}
93
c0152c09 94bool CCECClient::OnRegister(void)
004b8382 95{
c0152c09 96 // return false if already initialised
004b8382
LOK
97 if (IsInitialised())
98 return true;
99
c0152c09
LOK
100 // get all device we control
101 CECDEVICEVEC devices;
102 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
103
104 // return false when no devices were found
105 if (devices.empty())
004b8382 106 {
c0152c09 107 LIB_CEC->AddLog(CEC_LOG_WARNING, "cannot find the primary device (logical address %x)", GetPrimaryLogicalAdddress());
004b8382
LOK
108 return false;
109 }
110
c0152c09
LOK
111 // mark as initialised
112 SetInitialised(true);
004b8382 113
c0152c09
LOK
114 // configure all devices
115 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
004b8382 116 {
c0152c09
LOK
117 // only set our OSD name for the primary device
118 if ((*it)->GetLogicalAddress() == GetPrimaryLogicalAdddress())
119 (*it)->SetOSDName(m_configuration.strDeviceName);
120
121 // set the default menu language for devices we control
122 (*it)->SetMenuLanguage(m_configuration.strDeviceLanguage);
004b8382
LOK
123 }
124
c0152c09
LOK
125 // set the physical address
126 SetPhysicalAddress(m_configuration);
127
c0152c09 128 // make the primary device the active source if the option is set
004b8382 129 if (m_configuration.bActivateSource == 1)
060a7b5e 130 GetPrimaryDevice()->ActivateSource(500);
004b8382 131
004b8382
LOK
132 return true;
133}
134
c0152c09 135bool CCECClient::SetHDMIPort(const cec_logical_address iBaseDevice, const uint8_t iPort, bool bForce /* = false */)
004b8382
LOK
136{
137 bool bReturn(false);
138
139 // limit the HDMI port range to 1-15
140 if (iPort < CEC_MIN_HDMI_PORTNUMBER ||
141 iPort > CEC_MAX_HDMI_PORTNUMBER)
142 return bReturn;
143
aa169fd7 144 LIB_CEC->AddLog(CEC_LOG_NOTICE, "setting HDMI port to %d on device %s (%d)", iPort, ToString(iBaseDevice), (int)iBaseDevice);
c0152c09
LOK
145
146 // update the configuration
004b8382
LOK
147 {
148 CLockObject lock(m_mutex);
149 m_configuration.baseDevice = iBaseDevice;
150 m_configuration.iHDMIPort = iPort;
151 }
152
c0152c09 153 // don't continue if the connection isn't opened
0b8c7eab 154 if (!m_processor->CECInitialised() && !bForce)
004b8382
LOK
155 return true;
156
c0152c09 157 // get the PA of the base device
004b8382
LOK
158 uint16_t iPhysicalAddress(CEC_INVALID_PHYSICAL_ADDRESS);
159 CCECBusDevice *baseDevice = m_processor->GetDevice(iBaseDevice);
160 if (baseDevice)
c0152c09 161 iPhysicalAddress = baseDevice->GetPhysicalAddress(GetPrimaryLogicalAdddress());
004b8382 162
c0152c09 163 // add our port number
004b8382
LOK
164 if (iPhysicalAddress <= CEC_MAX_PHYSICAL_ADDRESS)
165 {
166 if (iPhysicalAddress == 0)
167 iPhysicalAddress += 0x1000 * iPort;
168 else if (iPhysicalAddress % 0x1000 == 0)
169 iPhysicalAddress += 0x100 * iPort;
170 else if (iPhysicalAddress % 0x100 == 0)
171 iPhysicalAddress += 0x10 * iPort;
172 else if (iPhysicalAddress % 0x10 == 0)
173 iPhysicalAddress += iPort;
174
175 bReturn = true;
176 }
177
c0152c09 178 // set the default address when something went wrong
004b8382
LOK
179 if (!bReturn)
180 {
181 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);
182 iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS;
183 }
184
c0152c09
LOK
185 // and set the address
186 SetDevicePhysicalAddress(iPhysicalAddress);
187
2b44051c 188 CallbackConfigurationChanged(m_configuration);
004b8382
LOK
189
190 return bReturn;
191}
192
c0152c09 193void CCECClient::ResetPhysicalAddress(void)
004b8382 194{
c0152c09
LOK
195 SetPhysicalAddress(m_configuration);
196}
004b8382 197
c0152c09
LOK
198void CCECClient::SetPhysicalAddress(const libcec_configuration &configuration)
199{
c0152c09 200 bool bPASet(false);
004b8382 201
20be20ea 202 // override the physical address from configuration.iPhysicalAddress if it's set
c0152c09
LOK
203 if (!bPASet && CLibCEC::IsValidPhysicalAddress(configuration.iPhysicalAddress))
204 bPASet = SetPhysicalAddress(configuration.iPhysicalAddress);
004b8382 205
20be20ea
LOK
206 // try to autodetect the address
207 if (!bPASet && m_processor->CECInitialised())
208 {
209 bPASet = AutodetectPhysicalAddress();
210 m_configuration.bAutodetectAddress = bPASet ? 1 : 0;
211 }
212
c0152c09
LOK
213 // use the base device + hdmi port settings
214 if (!bPASet)
215 bPASet = SetHDMIPort(configuration.baseDevice, configuration.iHDMIPort);
004b8382 216
c0152c09
LOK
217 // reset to defaults if something went wrong
218 if (!bPASet)
219 {
220 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - resetting HDMI port and base device to defaults", __FUNCTION__);
221 m_configuration.baseDevice = CECDEVICE_UNKNOWN;
222 m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE;
004b8382 223 }
c0152c09 224}
004b8382 225
c0152c09
LOK
226bool CCECClient::SetPhysicalAddress(const uint16_t iPhysicalAddress)
227{
228 // update the configuration
2b44051c 229 bool bChanged(true);
004b8382 230 {
c0152c09
LOK
231 CLockObject lock(m_mutex);
232 if (m_configuration.iPhysicalAddress == iPhysicalAddress)
2b44051c 233 bChanged = false;
c0152c09 234 else
c0152c09 235 m_configuration.iPhysicalAddress = iPhysicalAddress;
2b44051c
LOK
236 }
237 if (!bChanged)
238 {
239 LIB_CEC->AddLog(CEC_LOG_DEBUG, "physical address unchanged (%04X)", iPhysicalAddress);
240 return true;
004b8382
LOK
241 }
242
2b44051c 243 LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting physical address to '%04X'", iPhysicalAddress);
004b8382 244
c0152c09
LOK
245 // set the physical address for each device
246 SetDevicePhysicalAddress(iPhysicalAddress);
247
248 // and send back the updated configuration
2b44051c 249 CallbackConfigurationChanged(m_configuration);
c0152c09
LOK
250
251 return true;
004b8382
LOK
252}
253
42d28d15
LOK
254void CCECClient::SetSupportedDeviceTypes(void)
255{
256 cec_device_type_list types;
257 types.Clear();
258
259 // get the command handler for the tv
260 CCECCommandHandler *tvHandler = m_processor->GetTV()->GetHandler();
261 if (!tvHandler)
262 return;
263
264 // check all device types
265 for (uint8_t iPtr = 0; iPtr < 5; iPtr++)
266 {
267 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
268 continue;
269
270 // get the supported device type. the handler will replace types it doesn't support by one it does support
271 cec_device_type type = tvHandler->GetReplacementDeviceType(m_configuration.deviceTypes.types[iPtr]);
272 if (!types.IsSet(type))
273 types.Add(type);
274 }
91ef4e2d 275 m_processor->GetTV()->MarkHandlerReady();
42d28d15
LOK
276
277 // set the new type list
278 m_configuration.deviceTypes = types;
2b44051c
LOK
279
280 // persist the new configuration
281 PersistConfiguration(m_configuration);
42d28d15
LOK
282}
283
c0152c09 284bool CCECClient::AllocateLogicalAddresses(void)
004b8382 285{
c0152c09 286 // reset all previous LAs that were set
004b8382
LOK
287 m_configuration.logicalAddresses.Clear();
288
42d28d15
LOK
289 // get the supported device types from the command handler of the TV
290 SetSupportedDeviceTypes();
291
c0152c09 292 // display an error if no device types are set
004b8382
LOK
293 if (m_configuration.deviceTypes.IsEmpty())
294 {
295 LIB_CEC->AddLog(CEC_LOG_ERROR, "no device types given");
296 return false;
297 }
298
c0152c09 299 // check each entry of the list
004b8382
LOK
300 for (uint8_t iPtr = 0; iPtr < 5; iPtr++)
301 {
302 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
303 continue;
304
c0152c09 305 // find an LA for this type
004b8382
LOK
306 cec_logical_address address(CECDEVICE_UNKNOWN);
307 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RECORDING_DEVICE)
c0152c09 308 address = AllocateLogicalAddressRecordingDevice();
004b8382 309 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_TUNER)
c0152c09 310 address = AllocateLogicalAddressTuner();
004b8382 311 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_PLAYBACK_DEVICE)
c0152c09 312 address = AllocateLogicalAddressPlaybackDevice();
004b8382 313 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
c0152c09 314 address = AllocateLogicalAddressAudioSystem();
004b8382 315
c0152c09 316 // display an error if no LA could be allocated
004b8382
LOK
317 if (address == CECDEVICE_UNKNOWN)
318 {
319 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - failed to allocate device '%d', type '%s'", __FUNCTION__, iPtr, ToString(m_configuration.deviceTypes.types[iPtr]));
320 return false;
321 }
322
c0152c09 323 // display the registered LA
004b8382
LOK
324 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - device '%d', type '%s', LA '%X'", __FUNCTION__, iPtr, ToString(m_configuration.deviceTypes.types[iPtr]), address);
325 m_configuration.logicalAddresses.Set(address);
326 }
327
2b44051c
LOK
328 // persist the new configuration
329 PersistConfiguration(m_configuration);
330
004b8382
LOK
331 return true;
332}
333
c0152c09 334cec_logical_address CCECClient::AllocateLogicalAddressRecordingDevice(void)
004b8382
LOK
335{
336 cec_logical_address retVal(CECDEVICE_UNKNOWN);
337
338 LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'recording device'");
2b44051c 339 if (m_processor->TryLogicalAddress(CECDEVICE_RECORDINGDEVICE1, m_configuration.cecVersion))
004b8382 340 retVal = CECDEVICE_RECORDINGDEVICE1;
2b44051c 341 else if (m_processor->TryLogicalAddress(CECDEVICE_RECORDINGDEVICE2, m_configuration.cecVersion))
004b8382 342 retVal = CECDEVICE_RECORDINGDEVICE2;
2b44051c 343 else if (m_processor->TryLogicalAddress(CECDEVICE_RECORDINGDEVICE3, m_configuration.cecVersion))
004b8382
LOK
344 retVal = CECDEVICE_RECORDINGDEVICE3;
345
346 return retVal;
347}
348
c0152c09 349cec_logical_address CCECClient::AllocateLogicalAddressTuner(void)
004b8382
LOK
350{
351 cec_logical_address retVal(CECDEVICE_UNKNOWN);
352
353 LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'tuner'");
2b44051c 354 if (m_processor->TryLogicalAddress(CECDEVICE_TUNER1, m_configuration.cecVersion))
004b8382 355 retVal = CECDEVICE_TUNER1;
2b44051c 356 else if (m_processor->TryLogicalAddress(CECDEVICE_TUNER2, m_configuration.cecVersion))
004b8382 357 retVal = CECDEVICE_TUNER2;
2b44051c 358 else if (m_processor->TryLogicalAddress(CECDEVICE_TUNER3, m_configuration.cecVersion))
004b8382 359 retVal = CECDEVICE_TUNER3;
2b44051c 360 else if (m_processor->TryLogicalAddress(CECDEVICE_TUNER4, m_configuration.cecVersion))
004b8382
LOK
361 retVal = CECDEVICE_TUNER4;
362
363 return retVal;
364}
365
c0152c09 366cec_logical_address CCECClient::AllocateLogicalAddressPlaybackDevice(void)
004b8382
LOK
367{
368 cec_logical_address retVal(CECDEVICE_UNKNOWN);
369
370 LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'playback device'");
2b44051c 371 if (m_processor->TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE1, m_configuration.cecVersion))
004b8382 372 retVal = CECDEVICE_PLAYBACKDEVICE1;
2b44051c 373 else if (m_processor->TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE2, m_configuration.cecVersion))
004b8382 374 retVal = CECDEVICE_PLAYBACKDEVICE2;
2b44051c 375 else if (m_processor->TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE3, m_configuration.cecVersion))
004b8382
LOK
376 retVal = CECDEVICE_PLAYBACKDEVICE3;
377
378 return retVal;
379}
380
c0152c09 381cec_logical_address CCECClient::AllocateLogicalAddressAudioSystem(void)
004b8382
LOK
382{
383 cec_logical_address retVal(CECDEVICE_UNKNOWN);
384
385 LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'audiosystem'");
2b44051c 386 if (m_processor->TryLogicalAddress(CECDEVICE_AUDIOSYSTEM, m_configuration.cecVersion))
004b8382
LOK
387 retVal = CECDEVICE_AUDIOSYSTEM;
388
389 return retVal;
390}
391
392CCECBusDevice *CCECClient::GetDeviceByType(const cec_device_type type) const
393{
394 // get all devices that match our logical addresses
395 CECDEVICEVEC devices;
396 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
397
398 // filter the type we need
399 CCECDeviceMap::FilterType(type, devices);
400
401 return devices.empty() ?
402 NULL :
403 *devices.begin();
404}
405
c0152c09 406bool CCECClient::ChangeDeviceType(const cec_device_type from, const cec_device_type to)
004b8382 407{
2b44051c
LOK
408 if (from == to)
409 return true;
004b8382 410
2b44051c 411 LIB_CEC->AddLog(CEC_LOG_NOTICE, "changing device type '%s' into '%s'", ToString(from), ToString(to));
004b8382 412
004b8382 413 {
2b44051c 414 CLockObject lock(m_mutex);
004b8382 415
2b44051c
LOK
416 // get the previous device that was allocated
417 CCECBusDevice *previousDevice = GetDeviceByType(from);
418 if (!previousDevice)
419 return false;
420
421 // change the type in the device type list
422 bool bChanged(false);
423 for (uint8_t iPtr = 0; iPtr < 5; iPtr++)
004b8382 424 {
2b44051c
LOK
425 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
426 continue;
427
428 if (m_configuration.deviceTypes.types[iPtr] == from)
429 {
430 bChanged = true;
431 m_configuration.deviceTypes.types[iPtr] = to;
432 }
433 else if (m_configuration.deviceTypes.types[iPtr] == to && bChanged)
434 {
435 // ensure that dupes are removed
436 m_configuration.deviceTypes.types[iPtr] = CEC_DEVICE_TYPE_RESERVED;
437 }
004b8382
LOK
438 }
439 }
440
c0152c09
LOK
441 // re-register the client to set the new ackmask
442 if (!m_processor->RegisterClient(this))
443 return false;
004b8382 444
2b44051c
LOK
445 // persist the new configuration
446 PersistConfiguration(m_configuration);
447
004b8382
LOK
448 return true;
449}
450
c0152c09 451bool CCECClient::SetLogicalAddress(const cec_logical_address iLogicalAddress)
004b8382 452{
2b44051c
LOK
453 bool bReturn(true);
454
c0152c09 455 if (GetPrimaryLogicalAdddress() != iLogicalAddress)
004b8382 456 {
2b44051c 457 LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< setting primary logical address to %1x", iLogicalAddress);
c0152c09
LOK
458 {
459 CLockObject lock(m_mutex);
c0152c09
LOK
460 m_configuration.logicalAddresses.primary = iLogicalAddress;
461 m_configuration.logicalAddresses.Set(iLogicalAddress);
462 }
2b44051c
LOK
463
464 bReturn = m_processor->RegisterClient(this);
465
466 // persist the new configuration
467 if (bReturn)
468 PersistConfiguration(m_configuration);
004b8382
LOK
469 }
470
2b44051c 471 return bReturn;
004b8382
LOK
472}
473
2b44051c 474bool CCECClient::Transmit(const cec_command &data, bool bIsReply)
004b8382 475{
2b44051c 476 return m_processor ? m_processor->Transmit(data, bIsReply) : false;
004b8382
LOK
477}
478
c0152c09 479bool CCECClient::SendPowerOnDevices(const cec_logical_address address /* = CECDEVICE_TV */)
004b8382 480{
c0152c09 481 // if the broadcast address if set as destination and the client version >=1.5.0, read the wakeDevices setting
004b8382
LOK
482 if (address == CECDEVICE_BROADCAST && m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0)
483 {
484 CECDEVICEVEC devices;
485 m_processor->GetDevices()->GetWakeDevices(m_configuration, devices);
c0152c09 486 return m_processor->PowerOnDevices(GetPrimaryLogicalAdddress(), devices);
004b8382
LOK
487 }
488
c0152c09 489 return m_processor->PowerOnDevice(GetPrimaryLogicalAdddress(), address);
004b8382
LOK
490}
491
c0152c09 492bool CCECClient::SendStandbyDevices(const cec_logical_address address /* = CECDEVICE_BROADCAST */)
004b8382 493{
c0152c09 494 // if the broadcast address if set as destination and the client version >=1.5.0, read the standbyDevices setting
004b8382
LOK
495 if (address == CECDEVICE_BROADCAST && m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0)
496 {
497 CECDEVICEVEC devices;
498 m_processor->GetDevices()->GetPowerOffDevices(m_configuration, devices);
c0152c09 499 return m_processor->StandbyDevices(GetPrimaryLogicalAdddress(), devices);
004b8382
LOK
500 }
501
c0152c09 502 return m_processor->StandbyDevice(GetPrimaryLogicalAdddress(), address);
004b8382
LOK
503}
504
c0152c09 505bool CCECClient::SendSetActiveSource(const cec_device_type type /* = CEC_DEVICE_TYPE_RESERVED */)
004b8382 506{
c0152c09 507 // get the devices that are controlled by us
004b8382
LOK
508 CECDEVICEVEC devices;
509 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
510
c0152c09 511 // filter out the device that matches the given type
004b8382
LOK
512 if (type != CEC_DEVICE_TYPE_RESERVED)
513 CCECDeviceMap::FilterType(type, devices);
514
c0152c09 515 // no devices left, re-fetch the list of devices that are controlled by us
004b8382
LOK
516 if (devices.empty())
517 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
518
519 if (!devices.empty())
004b8382 520 {
c0152c09
LOK
521 // get the first device from the list
522 CCECBusDevice *device = *devices.begin();
523
524 // and activate it
0b8c7eab 525 if (!m_processor->CECInitialised())
c0152c09
LOK
526 device->MarkAsActiveSource();
527 else if (device->HasValidPhysicalAddress())
528 return device->ActivateSource();
004b8382
LOK
529 }
530
c0152c09 531 return false;
004b8382
LOK
532}
533
534CCECPlaybackDevice *CCECClient::GetPlaybackDevice(void)
535{
536 CCECPlaybackDevice *device(NULL);
537 CECDEVICEVEC devices;
c0152c09
LOK
538
539 // get the playback devices
004b8382
LOK
540 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
541 CCECDeviceMap::FilterType(CEC_DEVICE_TYPE_PLAYBACK_DEVICE, devices);
542
c0152c09 543 // no matches, get the recording devices
004b8382
LOK
544 if (devices.empty())
545 {
546 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
547 CCECDeviceMap::FilterType(CEC_DEVICE_TYPE_RECORDING_DEVICE, devices);
548 }
549
c0152c09 550 // get the first device that matches, and cast it to CCECPlaybackDevice
004b8382
LOK
551 if (!devices.empty())
552 device = (*devices.begin())->AsPlaybackDevice();
553
554 return device;
555}
556
c0152c09 557cec_logical_address CCECClient::GetPrimaryLogicalAdddress(void)
004b8382 558{
c0152c09
LOK
559 CLockObject lock(m_mutex);
560 return m_configuration.logicalAddresses.primary;
004b8382
LOK
561}
562
c0152c09 563CCECBusDevice *CCECClient::GetPrimaryDevice(void)
004b8382 564{
c0152c09
LOK
565 return m_processor->GetDevice(GetPrimaryLogicalAdddress());
566}
004b8382 567
c0152c09
LOK
568bool CCECClient::SendSetDeckControlMode(const cec_deck_control_mode mode, bool bSendUpdate /* = true */)
569{
570 // find a playback device that we control
571 CCECPlaybackDevice *device = GetPlaybackDevice();
004b8382
LOK
572 if (device)
573 {
c0152c09
LOK
574 // and set the deck control mode if there is a match
575 device->SetDeckControlMode(mode);
004b8382 576 if (bSendUpdate)
2b44051c 577 return device->TransmitDeckStatus(CECDEVICE_TV, false);
c0152c09 578 return true;
004b8382
LOK
579 }
580
c0152c09 581 // no match
004b8382
LOK
582 return false;
583}
584
c0152c09 585bool CCECClient::SendSetDeckInfo(const cec_deck_info info, bool bSendUpdate /* = true */)
004b8382 586{
c0152c09
LOK
587 // find a playback device that we control
588 CCECPlaybackDevice *device = GetPlaybackDevice();
004b8382
LOK
589 if (device)
590 {
c0152c09
LOK
591 // and set the deck status if there is a match
592 device->SetDeckStatus(info);
004b8382 593 if (bSendUpdate)
2b44051c 594 return device->AsPlaybackDevice()->TransmitDeckStatus(CECDEVICE_TV, false);
c0152c09 595 return true;
004b8382
LOK
596 }
597
c0152c09 598 // no match
004b8382
LOK
599 return false;
600}
601
c0152c09 602bool CCECClient::SendSetMenuState(const cec_menu_state state, bool bSendUpdate /* = true */)
004b8382
LOK
603{
604 CECDEVICEVEC devices;
004b8382 605
c0152c09
LOK
606 // set the menu state for all devices that are controlled by us
607 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
004b8382
LOK
608 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
609 {
610 (*it)->SetMenuState(state);
611 if (bSendUpdate)
2b44051c 612 (*it)->TransmitMenuState(CECDEVICE_TV, false);
004b8382
LOK
613 }
614
615 return true;
616}
617
618bool CCECClient::SendSetInactiveView(void)
619{
c0152c09
LOK
620 CECDEVICEVEC devices;
621
622 // mark all devices that are controlled by us as inactive source
623 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
624 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
004b8382 625 {
c0152c09
LOK
626 if ((*it)->IsActiveSource())
627 {
628 (*it)->MarkAsInactiveSource();
629 return (*it)->TransmitInactiveSource();
630 }
004b8382 631 }
c0152c09
LOK
632
633 return true;
004b8382
LOK
634}
635
c0152c09 636bool CCECClient::SendSetOSDString(const cec_logical_address iLogicalAddress, const cec_display_control duration, const char *strMessage)
004b8382
LOK
637{
638 CCECBusDevice *primary = GetPrimaryDevice();
639 if (primary)
2b44051c 640 return primary->TransmitOSDString(iLogicalAddress, duration, strMessage, false);
004b8382
LOK
641
642 return false;
643}
644
c0152c09 645cec_version CCECClient::GetDeviceCecVersion(const cec_logical_address iAddress)
004b8382
LOK
646{
647 CCECBusDevice *device = m_processor->GetDevice(iAddress);
648 if (device)
c0152c09 649 return device->GetCecVersion(GetPrimaryLogicalAdddress());
004b8382
LOK
650 return CEC_VERSION_UNKNOWN;
651}
652
c0152c09 653bool CCECClient::GetDeviceMenuLanguage(const cec_logical_address iAddress, cec_menu_language &language)
004b8382
LOK
654{
655 CCECBusDevice *device = m_processor->GetDevice(iAddress);
656 if (device)
657 {
c0152c09
LOK
658 language = device->GetMenuLanguage(GetPrimaryLogicalAdddress());
659 return (strcmp(language.language, "???") != 0);
004b8382
LOK
660 }
661 return false;
662}
663
c0152c09 664cec_osd_name CCECClient::GetDeviceOSDName(const cec_logical_address iAddress)
004b8382
LOK
665{
666 cec_osd_name retVal;
667 retVal.device = iAddress;
668 retVal.name[0] = 0;
669
670 CCECBusDevice *device = m_processor->GetDevice(iAddress);
671 if (device)
672 {
c0152c09 673 CStdString strOSDName = device->GetOSDName(GetPrimaryLogicalAdddress());
004b8382
LOK
674 snprintf(retVal.name, sizeof(retVal.name), "%s", strOSDName.c_str());
675 retVal.device = iAddress;
676 }
677
678 return retVal;
679}
680
c0152c09 681uint16_t CCECClient::GetDevicePhysicalAddress(const cec_logical_address iAddress)
004b8382
LOK
682{
683 CCECBusDevice *device = m_processor->GetDevice(iAddress);
684 if (device)
c0152c09 685 return device->GetPhysicalAddress(GetPrimaryLogicalAdddress());
004b8382
LOK
686 return CEC_INVALID_PHYSICAL_ADDRESS;
687}
688
c0152c09 689cec_power_status CCECClient::GetDevicePowerStatus(const cec_logical_address iAddress)
004b8382
LOK
690{
691 CCECBusDevice *device = m_processor->GetDevice(iAddress);
692 if (device)
c0152c09 693 return device->GetPowerStatus(GetPrimaryLogicalAdddress());
004b8382
LOK
694 return CEC_POWER_STATUS_UNKNOWN;
695}
696
c0152c09 697uint64_t CCECClient::GetDeviceVendorId(const cec_logical_address iAddress)
004b8382
LOK
698{
699 CCECBusDevice *device = m_processor->GetDevice(iAddress);
700 if (device)
c0152c09 701 return device->GetVendorId(GetPrimaryLogicalAdddress());
004b8382
LOK
702 return CEC_VENDOR_UNKNOWN;
703}
704
705uint8_t CCECClient::SendVolumeUp(bool bSendRelease /* = true */)
706{
707 CCECBusDevice *device = GetPrimaryDevice();
708 CCECAudioSystem *audio = m_processor->GetAudioSystem();
709
710 return device && audio && audio->IsPresent() ?
711 audio->VolumeUp(device->GetLogicalAddress(), bSendRelease) :
712 (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
713}
714
715uint8_t CCECClient::SendVolumeDown(bool bSendRelease /* = true */)
716{
717 CCECBusDevice *device = GetPrimaryDevice();
718 CCECAudioSystem *audio = m_processor->GetAudioSystem();
719
720 return device && audio && audio->IsPresent() ?
721 audio->VolumeDown(device->GetLogicalAddress(), bSendRelease) :
722 (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
723}
724
725uint8_t CCECClient::SendMuteAudio(void)
726{
727 CCECBusDevice *device = GetPrimaryDevice();
728 CCECAudioSystem *audio = m_processor->GetAudioSystem();
729
730 return device && audio && audio->IsPresent() ?
731 audio->MuteAudio(device->GetLogicalAddress()) :
732 (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
733}
734
c0152c09 735bool CCECClient::SendKeypress(const cec_logical_address iDestination, const cec_user_control_code key, bool bWait /* = true */)
004b8382 736{
004b8382
LOK
737 CCECBusDevice *dest = m_processor->GetDevice(iDestination);
738
7fe6a9d8
LOK
739 return dest ?
740 dest->TransmitKeypress(GetPrimaryLogicalAdddress(), key, bWait) :
004b8382
LOK
741 false;
742}
743
c0152c09 744bool CCECClient::SendKeyRelease(const cec_logical_address iDestination, bool bWait /* = true */)
004b8382 745{
004b8382
LOK
746 CCECBusDevice *dest = m_processor->GetDevice(iDestination);
747
7fe6a9d8
LOK
748 return dest ?
749 dest->TransmitKeyRelease(GetPrimaryLogicalAdddress(), bWait) :
004b8382
LOK
750 false;
751}
752
c0152c09 753bool CCECClient::GetCurrentConfiguration(libcec_configuration &configuration)
004b8382 754{
c0152c09
LOK
755 CLockObject lock(m_mutex);
756
004b8382 757 // client version 1.5.0
c0152c09
LOK
758 snprintf(configuration.strDeviceName, 13, "%s", m_configuration.strDeviceName);
759 configuration.deviceTypes = m_configuration.deviceTypes;
760 configuration.bAutodetectAddress = m_configuration.bAutodetectAddress;
761 configuration.iPhysicalAddress = m_configuration.iPhysicalAddress;
762 configuration.baseDevice = m_configuration.baseDevice;
763 configuration.iHDMIPort = m_configuration.iHDMIPort;
764 configuration.clientVersion = m_configuration.clientVersion;
765 configuration.serverVersion = m_configuration.serverVersion;
766 configuration.tvVendor = m_configuration.tvVendor;
767
768 configuration.bGetSettingsFromROM = m_configuration.bGetSettingsFromROM;
769 configuration.bUseTVMenuLanguage = m_configuration.bUseTVMenuLanguage;
770 configuration.bActivateSource = m_configuration.bActivateSource;
771 configuration.wakeDevices = m_configuration.wakeDevices;
772 configuration.powerOffDevices = m_configuration.powerOffDevices;
773 configuration.bPowerOffScreensaver = m_configuration.bPowerOffScreensaver;
774 configuration.bPowerOffOnStandby = m_configuration.bPowerOffOnStandby;
004b8382
LOK
775
776 // client version 1.5.1
c0152c09
LOK
777 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_1)
778 configuration.bSendInactiveSource = m_configuration.bSendInactiveSource;
004b8382
LOK
779
780 // client version 1.5.3
c0152c09
LOK
781 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_3)
782 configuration.logicalAddresses = m_configuration.logicalAddresses;
004b8382
LOK
783
784 // client version 1.6.0
c0152c09 785 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_0)
004b8382 786 {
c0152c09
LOK
787 configuration.iFirmwareVersion = m_configuration.iFirmwareVersion;
788 configuration.bPowerOffDevicesOnStandby = m_configuration.bPowerOffDevicesOnStandby;
789 configuration.bShutdownOnStandby = m_configuration.bShutdownOnStandby;
004b8382
LOK
790 }
791
792 // client version 1.6.2
c0152c09 793 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_2)
004b8382 794 {
c0152c09
LOK
795 memcpy(configuration.strDeviceLanguage, m_configuration.strDeviceLanguage, 3);
796 configuration.iFirmwareBuildDate = m_configuration.iFirmwareBuildDate;
004b8382 797 }
5f2f3609
LOK
798
799 // client version 1.6.3
800 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_3)
801 {
802 configuration.bMonitorOnly = m_configuration.bMonitorOnly;
803 }
804
004b8382
LOK
805 return true;
806}
807
c0152c09 808bool CCECClient::SetConfiguration(const libcec_configuration &configuration)
004b8382 809{
0b8c7eab 810 bool bIsRunning(m_processor && m_processor->CECInitialised());
004b8382 811 CCECBusDevice *primary = bIsRunning ? GetPrimaryDevice() : NULL;
c0152c09 812 uint16_t iPA = primary ? primary->GetCurrentPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS;
004b8382 813
c0152c09
LOK
814 // update the callbacks
815 if (configuration.callbacks)
816 EnableCallbacks(configuration.callbackParam, configuration.callbacks);
004b8382 817
c0152c09
LOK
818 // update the client version
819 SetClientVersion((cec_client_version)configuration.clientVersion);
004b8382 820
c0152c09
LOK
821 // update the OSD name
822 CStdString strOSDName(configuration.strDeviceName);
823 SetOSDName(strOSDName);
004b8382 824
c0152c09
LOK
825 // update the TV vendor override
826 SetTVVendorOverride((cec_vendor_id)configuration.tvVendor);
004b8382 827
c0152c09 828 // just copy these
004b8382 829 {
c0152c09
LOK
830 CLockObject lock(m_mutex);
831 m_configuration.bUseTVMenuLanguage = configuration.bUseTVMenuLanguage;
832 m_configuration.bActivateSource = configuration.bActivateSource;
833 m_configuration.bGetSettingsFromROM = configuration.bGetSettingsFromROM;
834 m_configuration.wakeDevices = configuration.wakeDevices;
835 m_configuration.powerOffDevices = configuration.powerOffDevices;
836 m_configuration.bPowerOffScreensaver = configuration.bPowerOffScreensaver;
837 m_configuration.bPowerOffOnStandby = configuration.bPowerOffOnStandby;
838
839 // client version 1.5.1
840 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_1)
841 m_configuration.bSendInactiveSource = configuration.bSendInactiveSource;
842
843 // client version 1.6.0
844 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_0)
004b8382 845 {
c0152c09
LOK
846 m_configuration.bPowerOffDevicesOnStandby = configuration.bPowerOffDevicesOnStandby;
847 m_configuration.bShutdownOnStandby = configuration.bShutdownOnStandby;
004b8382 848 }
004b8382 849
c0152c09
LOK
850 // client version 1.6.2
851 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_2)
004b8382 852 {
c0152c09 853 memcpy(m_configuration.strDeviceLanguage, configuration.strDeviceLanguage, 3);
004b8382 854 }
004b8382 855
5f2f3609
LOK
856 // client version 1.6.3
857 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_3)
858 {
859 m_configuration.bMonitorOnly = configuration.bMonitorOnly;
860 }
861
c0152c09
LOK
862 // ensure that there is at least 1 device type set
863 if (m_configuration.deviceTypes.IsEmpty())
864 m_configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
004b8382
LOK
865 }
866
c0152c09 867 bool bNeedReinit(false);
004b8382 868
c0152c09
LOK
869 // device types
870 if (SetDeviceTypes(configuration.deviceTypes))
004b8382 871 {
c0152c09
LOK
872 // the device type changed. just copy the rest, and re-register
873 {
874 CLockObject lock(m_mutex);
875 m_configuration.iPhysicalAddress = configuration.iPhysicalAddress;
876 m_configuration.baseDevice = configuration.baseDevice;
877 m_configuration.iHDMIPort = configuration.iHDMIPort;
878 bNeedReinit = true;
879 }
004b8382 880 }
c0152c09 881 else
004b8382 882 {
c0152c09
LOK
883 // set the physical address
884 SetPhysicalAddress(configuration);
004b8382
LOK
885 }
886
2b44051c
LOK
887 // persist the new configuration
888 PersistConfiguration(m_configuration);
004b8382 889
c0152c09
LOK
890 if (!primary)
891 primary = GetPrimaryDevice();
892
893 if (bNeedReinit || !primary || primary->GetCurrentPhysicalAddress() != iPA)
004b8382 894 {
c0152c09
LOK
895 // PA or device type changed
896 m_processor->RegisterClient(this);
004b8382 897 }
c0152c09 898 else if (primary && configuration.bActivateSource == 1 && bIsRunning && !primary->IsActiveSource())
004b8382
LOK
899 {
900 // activate the source if we're not already the active source
c0152c09 901 primary->ActivateSource();
004b8382
LOK
902 }
903
c0152c09 904 return true;
004b8382
LOK
905}
906
907void CCECClient::AddCommand(const cec_command &command)
908{
55c18d43
LOK
909 // don't forward the standby opcode more than once every 10 seconds
910 if (command.opcode == CEC_OPCODE_STANDBY)
911 {
912 CLockObject lock(m_mutex);
913 if (m_iPreventForwardingPowerOffCommand != 0 &&
914 m_iPreventForwardingPowerOffCommand > GetTimeMs())
915 return;
916 else
917 m_iPreventForwardingPowerOffCommand = GetTimeMs() + CEC_FORWARD_STANDBY_MIN_INTERVAL;
918 }
919
5d19989f
LOK
920 if (command.destination == CECDEVICE_BROADCAST || GetLogicalAddresses().IsSet(command.destination))
921 {
5d19989f 922 LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> %s (%X) -> %s (%X): %s (%2X)", ToString(command.initiator), command.initiator, ToString(command.destination), command.destination, ToString(command.opcode), command.opcode);
2b44051c 923 CallbackAddCommand(command);
5d19989f 924 }
004b8382
LOK
925}
926
927int CCECClient::MenuStateChanged(const cec_menu_state newState)
928{
004b8382 929 LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> %s: %s", ToString(CEC_OPCODE_MENU_REQUEST), ToString(newState));
2b44051c 930 return CallbackMenuStateChanged(newState);
004b8382
LOK
931}
932
933void CCECClient::AddKey(void)
934{
2b44051c
LOK
935 cec_keypress key;
936 key.keycode = CEC_USER_CONTROL_CODE_UNKNOWN;
004b8382 937
004b8382 938 {
2b44051c
LOK
939 CLockObject lock(m_mutex);
940 if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN)
941 {
942 key.duration = (unsigned int) (GetTimeMs() - m_buttontime);
943 key.keycode = m_iCurrentButton;
004b8382 944
2b44051c
LOK
945 m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
946 m_buttontime = 0;
947 }
004b8382
LOK
948 }
949
2b44051c
LOK
950 if (key.keycode != CEC_USER_CONTROL_CODE_UNKNOWN)
951 {
952 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key released: %s (%1x)", ToString(key.keycode), key.keycode);
953 CallbackAddKey(key);
954 }
004b8382
LOK
955}
956
957void CCECClient::AddKey(const cec_keypress &key)
958{
2b44051c
LOK
959 {
960 CLockObject lock(m_mutex);
961 m_iCurrentButton = key.duration > 0 ? CEC_USER_CONTROL_CODE_UNKNOWN : key.keycode;
962 m_buttontime = key.duration > 0 ? 0 : GetTimeMs();
963 }
004b8382 964
2b44051c
LOK
965 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key pressed: %s (%1x)", ToString(key.keycode), key.keycode);
966 CallbackAddKey(key);
004b8382
LOK
967}
968
c0152c09 969void CCECClient::SetCurrentButton(const cec_user_control_code iButtonCode)
004b8382 970{
c0152c09 971 // push a keypress to the buffer with 0 duration and another with the duration set when released
004b8382
LOK
972 cec_keypress key;
973 key.duration = 0;
974 key.keycode = iButtonCode;
975
976 AddKey(key);
977}
978
979void CCECClient::CheckKeypressTimeout(void)
980{
2b44051c
LOK
981 cec_keypress key;
982
004b8382 983 {
2b44051c
LOK
984 CLockObject lock(m_mutex);
985 uint64_t iNow = GetTimeMs();
004b8382 986
2b44051c
LOK
987 if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN &&
988 iNow - m_buttontime > CEC_BUTTON_TIMEOUT)
989 {
990 key.duration = (unsigned int) (iNow - m_buttontime);
991 key.keycode = m_iCurrentButton;
004b8382 992
2b44051c
LOK
993 m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
994 m_buttontime = 0;
995 }
996 else
997 {
998 return;
999 }
1000 }
1001
1002 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key auto-released: %s (%1x)", ToString(key.keycode), key.keycode);
1003 CallbackAddKey(key);
004b8382
LOK
1004}
1005
1006bool CCECClient::EnableCallbacks(void *cbParam, ICECCallbacks *callbacks)
1007{
2b44051c 1008 CLockObject lock(m_cbMutex);
004b8382
LOK
1009 m_configuration.callbackParam = cbParam;
1010 m_configuration.callbacks = callbacks;
1011 return true;
1012}
1013
c0152c09
LOK
1014bool CCECClient::PingAdapter(void)
1015{
1016 return m_processor ? m_processor->PingAdapter() : false;
1017}
1018
004b8382
LOK
1019bool CCECClient::GetNextLogMessage(cec_log_message *message)
1020{
1021 return (m_logBuffer.Pop(*message));
1022}
1023
1024bool CCECClient::GetNextKeypress(cec_keypress *key)
1025{
1026 return m_keyBuffer.Pop(*key);
1027}
1028
1029bool CCECClient::GetNextCommand(cec_command *command)
1030{
1031 return m_commandBuffer.Pop(*command);
1032}
c0152c09 1033
2b44051c 1034std::string CCECClient::GetConnectionInfo(void)
c0152c09
LOK
1035{
1036 CStdString strLog;
1037 strLog.Format("libCEC version = %s, client version = %s, firmware version = %d", ToString((cec_server_version)m_configuration.serverVersion), ToString((cec_client_version)m_configuration.clientVersion), m_configuration.iFirmwareVersion);
1038 if (m_configuration.iFirmwareBuildDate != CEC_FW_BUILD_UNKNOWN)
1039 {
1040 time_t buildTime = (time_t)m_configuration.iFirmwareBuildDate;
1041 strLog.AppendFormat(", firmware build date: %s", asctime(gmtime(&buildTime)));
1042 strLog = strLog.Left((int)strLog.length() - 1); // strip \n added by asctime
1043 strLog.append(" +0000");
1044 }
1045
1046 // log the addresses that are being used
1047 if (!m_configuration.logicalAddresses.IsEmpty())
1048 {
1049 strLog.append(", logical address(es) = ");
1050 CECDEVICEVEC devices;
1051 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
1052 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
1053 strLog.AppendFormat("%s (%X) ", (*it)->GetLogicalAddressName(), (*it)->GetLogicalAddress());
1054 }
1055
1056 if (!CLibCEC::IsValidPhysicalAddress(m_configuration.iPhysicalAddress))
1057 strLog.AppendFormat(", base device: %s (%X), HDMI port number: %d", ToString(m_configuration.baseDevice), m_configuration.baseDevice, m_configuration.iHDMIPort);
1058 else
1059 strLog.AppendFormat(", physical address: %04x", m_configuration.iPhysicalAddress);
1060
2b44051c
LOK
1061 strLog.AppendFormat(", %s", LIB_CEC->GetLibInfo());
1062
1063 std::string strReturn(strLog.c_str());
1064 return strReturn;
c0152c09
LOK
1065}
1066
1067void CCECClient::SetTVVendorOverride(const cec_vendor_id id)
1068{
1069 {
1070 CLockObject lock(m_mutex);
1071 m_configuration.tvVendor = id;
1072 }
1073
1074 if (id != CEC_VENDOR_UNKNOWN)
1075 {
1076 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vendor id '%s'", __FUNCTION__, ToString(id));
1077
1078 CCECBusDevice *tv = m_processor ? m_processor->GetTV() : NULL;
1079 if (tv)
1080 tv->SetVendorId((uint64_t)id);
1081 }
2b44051c
LOK
1082
1083 // persist the new configuration
1084 PersistConfiguration(m_configuration);
c0152c09
LOK
1085}
1086
1087cec_vendor_id CCECClient::GetTVVendorOverride(void)
1088{
1089 CLockObject lock(m_mutex);
1090 return (cec_vendor_id)m_configuration.tvVendor;
1091}
1092
2b44051c 1093void CCECClient::SetOSDName(const std::string &strDeviceName)
c0152c09
LOK
1094{
1095 {
1096 CLockObject lock(m_mutex);
1097 snprintf(m_configuration.strDeviceName, 13, "%s", strDeviceName.c_str());
1098 }
1099
1100 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using OSD name '%s'", __FUNCTION__, strDeviceName.c_str());
1101
1102 CCECBusDevice *primary = GetPrimaryDevice();
2b44051c 1103 if (primary && !primary->GetCurrentOSDName().Equals(strDeviceName.c_str()))
c0152c09
LOK
1104 {
1105 primary->SetOSDName(strDeviceName);
0b8c7eab 1106 if (m_processor && m_processor->CECInitialised())
2b44051c 1107 primary->TransmitOSDName(CECDEVICE_TV, false);
c0152c09 1108 }
2b44051c
LOK
1109
1110 // persist the new configuration
1111 PersistConfiguration(m_configuration);
c0152c09
LOK
1112}
1113
2b44051c 1114std::string CCECClient::GetOSDName(void)
c0152c09
LOK
1115{
1116 CLockObject lock(m_mutex);
2b44051c 1117 std::string strOSDName(m_configuration.strDeviceName);
c0152c09
LOK
1118 return strOSDName;
1119}
1120
1121void CCECClient::SetWakeDevices(const cec_logical_addresses &addresses)
1122{
2b44051c
LOK
1123 {
1124 CLockObject lock(m_mutex);
1125 m_configuration.wakeDevices = addresses;
1126 }
1127 // persist the new configuration
1128 PersistConfiguration(m_configuration);
c0152c09
LOK
1129}
1130
1131cec_logical_addresses CCECClient::GetWakeDevices(void)
1132{
1133 CLockObject lock(m_mutex);
1134 return m_configuration.wakeDevices;
1135}
1136
1137bool CCECClient::AutodetectPhysicalAddress(void)
1138{
1139 bool bPhysicalAutodetected(false);
1140 uint16_t iPhysicalAddress = m_processor ? m_processor->GetDetectedPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS;
1141
1142 if (CLibCEC::IsValidPhysicalAddress(iPhysicalAddress))
1143 {
1144 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - autodetected physical address '%04X'", __FUNCTION__, iPhysicalAddress);
1145
1146 CLockObject lock(m_mutex);
1147 m_configuration.iPhysicalAddress = iPhysicalAddress;
1148 m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE;
1149 m_configuration.baseDevice = CECDEVICE_UNKNOWN;
1150 bPhysicalAutodetected = true;
1151 }
1152
1153 SetDevicePhysicalAddress(iPhysicalAddress);
1154
1155 return bPhysicalAutodetected;
1156}
1157
1158void CCECClient::SetClientVersion(const cec_client_version version)
1159{
1160 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using client version '%s'", __FUNCTION__, ToString(version));
1161
1162 CLockObject lock(m_mutex);
1163 m_configuration.clientVersion = (uint32_t)version;
1164}
1165
1166cec_client_version CCECClient::GetClientVersion(void)
1167{
1168 CLockObject lock(m_mutex);
1169 return (cec_client_version)m_configuration.clientVersion;
1170}
1171
1172bool CCECClient::SetDeviceTypes(const cec_device_type_list &deviceTypes)
1173{
1174 bool bNeedReinit(false);
1175
1176 {
1177 CLockObject lock(m_mutex);
0b8c7eab 1178 bNeedReinit = m_processor && m_processor->CECInitialised() &&
c0152c09
LOK
1179 (m_configuration.deviceTypes != deviceTypes);
1180 m_configuration.deviceTypes = deviceTypes;
1181 }
1182
2b44051c
LOK
1183 // persist the new configuration
1184 PersistConfiguration(m_configuration);
1185
c0152c09
LOK
1186 if (bNeedReinit)
1187 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using primary device type '%s'", __FUNCTION__, ToString(deviceTypes[0]));
1188
1189 return bNeedReinit;
1190}
1191
1192cec_device_type_list CCECClient::GetDeviceTypes(void)
1193{
1194 cec_device_type_list retVal;
1195 CLockObject lock(m_mutex);
1196 retVal = m_configuration.deviceTypes;
1197 return retVal;
1198}
1199
1200bool CCECClient::SetDevicePhysicalAddress(const uint16_t iPhysicalAddress)
1201{
1202 if (!CLibCEC::IsValidPhysicalAddress(iPhysicalAddress))
b1d821c6 1203 {
ea508fa6 1204 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - not setting invalid physical address %04x", __FUNCTION__, iPhysicalAddress);
c0152c09 1205 return false;
b1d821c6 1206 }
c0152c09
LOK
1207
1208 // reconfigure all devices
1209 cec_logical_address reactivateSource(CECDEVICE_UNKNOWN);
1210 CECDEVICEVEC devices;
1211 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
1212 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
1213 {
1214 // if this device was the active source, reactivate it afterwards
1215 if ((*it)->IsActiveSource())
1216 reactivateSource = (*it)->GetLogicalAddress();
1217
1218 // mark the device as inactive source
1219 if (IsInitialised())
1220 (*it)->MarkAsInactiveSource();
1221
1222 // set the new physical address
1223 (*it)->SetPhysicalAddress(iPhysicalAddress);
1224
1225 // and transmit it
1226 if (IsInitialised())
2b44051c 1227 (*it)->TransmitPhysicalAddress(false);
c0152c09
LOK
1228 }
1229
1230 // reactivate the previous active source
1231 if (reactivateSource != CECDEVICE_UNKNOWN &&
0b8c7eab 1232 m_processor->CECInitialised() &&
c0152c09
LOK
1233 IsInitialised())
1234 {
1235 CCECBusDevice *device = m_processor->GetDevice(reactivateSource);
1236 if (device)
1237 device->ActivateSource();
1238 }
1239
2b44051c
LOK
1240 // persist the new configuration
1241 PersistConfiguration(m_configuration);
1242
c0152c09
LOK
1243 return true;
1244}
1245
1246bool CCECClient::SwitchMonitoring(bool bEnable)
1247{
4a01fcd6
LOK
1248 LIB_CEC->AddLog(CEC_LOG_NOTICE, "== %s monitoring mode ==", bEnable ? "enabling" : "disabling");
1249
1250 if (m_processor)
1251 {
cc0a2977
LOK
1252 m_processor->SwitchMonitoring(bEnable);
1253 m_configuration.bMonitorOnly = bEnable;
1254 return bEnable ? true: m_processor->RegisterClient(this);
4a01fcd6
LOK
1255 }
1256
1257 return false;
c0152c09
LOK
1258}
1259
1260bool CCECClient::PollDevice(const cec_logical_address iAddress)
1261{
1262 // try to find the primary device
1263 CCECBusDevice *primary = GetPrimaryDevice();
1264 // poll the destination, with the primary as source
1265 if (primary)
2b44051c 1266 return primary->TransmitPoll(iAddress, false);
c0152c09
LOK
1267
1268 return m_processor ? m_processor->PollDevice(iAddress) : false;
1269}
1270
1271cec_logical_addresses CCECClient::GetActiveDevices(void)
1272{
1273 CECDEVICEVEC activeDevices;
1274 if (m_processor)
1275 m_processor->GetDevices()->GetActive(activeDevices);
1276 return CCECDeviceMap::ToLogicalAddresses(activeDevices);
1277}
1278
1279bool CCECClient::IsActiveDevice(const cec_logical_address iAddress)
1280{
1281 cec_logical_addresses activeDevices = GetActiveDevices();
1282 return activeDevices.IsSet(iAddress);
1283}
1284
1285bool CCECClient::IsActiveDeviceType(const cec_device_type type)
1286{
1287 CECDEVICEVEC activeDevices;
1288 if (m_processor)
1289 m_processor->GetDevices()->GetActive(activeDevices);
1290 CCECDeviceMap::FilterType(type, activeDevices);
1291 return !activeDevices.empty();
1292}
1293
1294cec_logical_address CCECClient::GetActiveSource(void)
1295{
1296 return m_processor ? m_processor->GetActiveSource() : CECDEVICE_UNKNOWN;
1297}
1298
1299bool CCECClient::IsActiveSource(const cec_logical_address iAddress)
1300{
1301 return m_processor ? m_processor->IsActiveSource(iAddress) : false;
1302}
1303
1304bool CCECClient::SetStreamPath(const cec_logical_address iAddress)
1305{
1306 uint16_t iPhysicalAddress = GetDevicePhysicalAddress(iAddress);
1307 if (iPhysicalAddress != CEC_INVALID_PHYSICAL_ADDRESS)
1308 return SetStreamPath(iPhysicalAddress);
1309 return false;
1310}
1311
1312bool CCECClient::SetStreamPath(const uint16_t iPhysicalAddress)
1313{
2b44051c
LOK
1314 bool bReturn(false);
1315
1316 CCECBusDevice *device = GetDeviceByType(CEC_DEVICE_TYPE_TV);
1317 if (device)
1318 {
1319 device->SetStreamPath(iPhysicalAddress);
1320 bReturn = device->GetHandler()->TransmitSetStreamPath(iPhysicalAddress, false);
1321 device->MarkHandlerReady();
1322 }
1323 else
1324 {
1325 LIB_CEC->AddLog(CEC_LOG_ERROR, "only the TV is allowed to send CEC_OPCODE_SET_STREAM_PATH");
1326 }
1327
1328 return bReturn;
c0152c09
LOK
1329}
1330
1331cec_logical_addresses CCECClient::GetLogicalAddresses(void)
1332{
1333 cec_logical_addresses addresses;
1334 CLockObject lock(m_mutex);
1335 addresses = m_configuration.logicalAddresses;
1336 return addresses;
1337}
1338
1339bool CCECClient::CanPersistConfiguration(void)
1340{
1341 return m_processor ? m_processor->CanPersistConfiguration() : false;
1342}
1343
1344bool CCECClient::PersistConfiguration(const libcec_configuration &configuration)
1345{
2b44051c
LOK
1346 return m_processor && IsRegistered() ?
1347 m_processor->PersistConfiguration(configuration) :
1348 false;
c0152c09
LOK
1349}
1350
1351void CCECClient::RescanActiveDevices(void)
1352{
1353 if (m_processor)
1354 m_processor->RescanActiveDevices();
1355}
1356
1357bool CCECClient::IsLibCECActiveSource(void)
1358{
1359 bool bReturn(false);
1360 if (m_processor)
1361 {
1362 cec_logical_address activeSource = m_processor->GetActiveSource();
1363 CCECBusDevice *device = m_processor->GetDevice(activeSource);
1364 if (device)
1365 bReturn = device->IsHandledByLibCEC();
1366 }
1367 return bReturn;
1368}
2b44051c
LOK
1369
1370void CCECClient::SourceActivated(const cec_logical_address logicalAddress)
1371{
1372 LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> source activated: %s (%x)", ToString(logicalAddress), logicalAddress);
1373 CallbackSourceActivated(true, logicalAddress);
1374}
1375
1376void CCECClient::SourceDeactivated(const cec_logical_address logicalAddress)
1377{
1378 LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> source deactivated: %s (%x)", ToString(logicalAddress), logicalAddress);
1379 CallbackSourceActivated(false, logicalAddress);
1380}
1381
1382void CCECClient::CallbackAddCommand(const cec_command &command)
1383{
1384 {
1385 CLockObject lock(m_cbMutex);
1386 if (m_configuration.callbacks && m_configuration.callbacks->CBCecCommand)
1387 {
1388 m_configuration.callbacks->CBCecCommand(m_configuration.callbackParam, command);
1389 return;
1390 }
1391 }
1392 m_commandBuffer.Push(command);
1393}
1394
1395void CCECClient::CallbackAddKey(const cec_keypress &key)
1396{
1397 {
1398 CLockObject lock(m_cbMutex);
1399 if (m_configuration.callbacks && m_configuration.callbacks->CBCecKeyPress)
1400 {
1401 m_configuration.callbacks->CBCecKeyPress(m_configuration.callbackParam, key);
1402 return;
1403 }
1404 }
1405 m_keyBuffer.Push(key);
1406}
1407
1408void CCECClient::CallbackAddLog(const cec_log_message &message)
1409{
1410 {
1411 CLockObject lock(m_cbMutex);
1412 if (m_configuration.callbacks && m_configuration.callbacks->CBCecLogMessage)
1413 {
1414 m_configuration.callbacks->CBCecLogMessage(m_configuration.callbackParam, message);
1415 return;
1416 }
1417 }
1418 m_logBuffer.Push(message);
1419}
1420
1421void CCECClient::CallbackConfigurationChanged(const libcec_configuration &config)
1422{
1423 CLockObject lock(m_cbMutex);
1424 if (m_configuration.callbacks &&
1425 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0 &&
1426 m_configuration.callbacks->CBCecConfigurationChanged &&
1427 m_processor->CECInitialised())
1428 m_configuration.callbacks->CBCecConfigurationChanged(m_configuration.callbackParam, config);
1429}
1430
1431void CCECClient::CallbackSourceActivated(bool bActivated, const cec_logical_address logicalAddress)
1432{
1433 CLockObject lock(m_cbMutex);
1434 if (m_configuration.callbacks &&
1435 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_7_1 &&
1436 m_configuration.callbacks->CBCecSourceActivated)
1437 m_configuration.callbacks->CBCecSourceActivated(m_configuration.callbackParam, logicalAddress, bActivated ? 1 : 0);
1438}
1439
1440void CCECClient::CallbackAlert(const libcec_alert type, const libcec_parameter &param)
1441{
1442 CLockObject lock(m_cbMutex);
1443 if (m_configuration.callbacks &&
1444 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_0 &&
1445 m_configuration.callbacks->CBCecAlert)
1446 m_configuration.callbacks->CBCecAlert(m_configuration.callbackParam, type, param);
1447}
1448
1449int CCECClient::CallbackMenuStateChanged(const cec_menu_state newState)
1450{
1451 CLockObject lock(m_cbMutex);
1452 if (m_configuration.callbacks &&
1453 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_2 &&
1454 m_configuration.callbacks->CBCecMenuStateChanged)
1455 return m_configuration.callbacks->CBCecMenuStateChanged(m_configuration.callbackParam, newState);
1456 return 0;
1457}