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