moved code that handles the previous keypress
[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
2d418322
LOK
805 // client version 1.8.0
806 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_8_0)
807 configuration.cecVersion = m_configuration.cecVersion;
808
809 // client version 1.8.2
810 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_8_2)
811 configuration.adapterType = m_configuration.adapterType;
812
004b8382
LOK
813 return true;
814}
815
c0152c09 816bool CCECClient::SetConfiguration(const libcec_configuration &configuration)
004b8382 817{
0b8c7eab 818 bool bIsRunning(m_processor && m_processor->CECInitialised());
004b8382 819 CCECBusDevice *primary = bIsRunning ? GetPrimaryDevice() : NULL;
c0152c09 820 uint16_t iPA = primary ? primary->GetCurrentPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS;
004b8382 821
c0152c09
LOK
822 // update the callbacks
823 if (configuration.callbacks)
824 EnableCallbacks(configuration.callbackParam, configuration.callbacks);
004b8382 825
c0152c09
LOK
826 // update the client version
827 SetClientVersion((cec_client_version)configuration.clientVersion);
004b8382 828
c0152c09
LOK
829 // update the OSD name
830 CStdString strOSDName(configuration.strDeviceName);
831 SetOSDName(strOSDName);
004b8382 832
c0152c09
LOK
833 // update the TV vendor override
834 SetTVVendorOverride((cec_vendor_id)configuration.tvVendor);
004b8382 835
c0152c09 836 // just copy these
004b8382 837 {
c0152c09
LOK
838 CLockObject lock(m_mutex);
839 m_configuration.bUseTVMenuLanguage = configuration.bUseTVMenuLanguage;
840 m_configuration.bActivateSource = configuration.bActivateSource;
841 m_configuration.bGetSettingsFromROM = configuration.bGetSettingsFromROM;
842 m_configuration.wakeDevices = configuration.wakeDevices;
843 m_configuration.powerOffDevices = configuration.powerOffDevices;
844 m_configuration.bPowerOffScreensaver = configuration.bPowerOffScreensaver;
845 m_configuration.bPowerOffOnStandby = configuration.bPowerOffOnStandby;
846
847 // client version 1.5.1
848 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_1)
849 m_configuration.bSendInactiveSource = configuration.bSendInactiveSource;
850
851 // client version 1.6.0
852 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_0)
004b8382 853 {
c0152c09
LOK
854 m_configuration.bPowerOffDevicesOnStandby = configuration.bPowerOffDevicesOnStandby;
855 m_configuration.bShutdownOnStandby = configuration.bShutdownOnStandby;
004b8382 856 }
004b8382 857
c0152c09
LOK
858 // client version 1.6.2
859 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_2)
004b8382 860 {
c0152c09 861 memcpy(m_configuration.strDeviceLanguage, configuration.strDeviceLanguage, 3);
004b8382 862 }
004b8382 863
5f2f3609
LOK
864 // client version 1.6.3
865 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_3)
866 {
867 m_configuration.bMonitorOnly = configuration.bMonitorOnly;
868 }
869
2d418322
LOK
870 // client version 1.8.0
871 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_8_0)
872 m_configuration.cecVersion = configuration.cecVersion;
873
874 // client version 1.8.2
875 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_8_2)
876 m_configuration.adapterType = configuration.adapterType;
877
c0152c09
LOK
878 // ensure that there is at least 1 device type set
879 if (m_configuration.deviceTypes.IsEmpty())
880 m_configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
004b8382
LOK
881 }
882
c0152c09 883 bool bNeedReinit(false);
004b8382 884
c0152c09
LOK
885 // device types
886 if (SetDeviceTypes(configuration.deviceTypes))
004b8382 887 {
c0152c09
LOK
888 // the device type changed. just copy the rest, and re-register
889 {
890 CLockObject lock(m_mutex);
891 m_configuration.iPhysicalAddress = configuration.iPhysicalAddress;
892 m_configuration.baseDevice = configuration.baseDevice;
893 m_configuration.iHDMIPort = configuration.iHDMIPort;
894 bNeedReinit = true;
895 }
004b8382 896 }
c0152c09 897 else
004b8382 898 {
c0152c09
LOK
899 // set the physical address
900 SetPhysicalAddress(configuration);
004b8382
LOK
901 }
902
2b44051c
LOK
903 // persist the new configuration
904 PersistConfiguration(m_configuration);
004b8382 905
c0152c09
LOK
906 if (!primary)
907 primary = GetPrimaryDevice();
908
909 if (bNeedReinit || !primary || primary->GetCurrentPhysicalAddress() != iPA)
004b8382 910 {
c0152c09
LOK
911 // PA or device type changed
912 m_processor->RegisterClient(this);
004b8382 913 }
c0152c09 914 else if (primary && configuration.bActivateSource == 1 && bIsRunning && !primary->IsActiveSource())
004b8382
LOK
915 {
916 // activate the source if we're not already the active source
c0152c09 917 primary->ActivateSource();
004b8382
LOK
918 }
919
c0152c09 920 return true;
004b8382
LOK
921}
922
923void CCECClient::AddCommand(const cec_command &command)
924{
55c18d43
LOK
925 // don't forward the standby opcode more than once every 10 seconds
926 if (command.opcode == CEC_OPCODE_STANDBY)
927 {
928 CLockObject lock(m_mutex);
929 if (m_iPreventForwardingPowerOffCommand != 0 &&
930 m_iPreventForwardingPowerOffCommand > GetTimeMs())
931 return;
932 else
933 m_iPreventForwardingPowerOffCommand = GetTimeMs() + CEC_FORWARD_STANDBY_MIN_INTERVAL;
934 }
935
5d19989f
LOK
936 if (command.destination == CECDEVICE_BROADCAST || GetLogicalAddresses().IsSet(command.destination))
937 {
5d19989f 938 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 939 CallbackAddCommand(command);
5d19989f 940 }
004b8382
LOK
941}
942
943int CCECClient::MenuStateChanged(const cec_menu_state newState)
944{
004b8382 945 LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> %s: %s", ToString(CEC_OPCODE_MENU_REQUEST), ToString(newState));
2b44051c 946 return CallbackMenuStateChanged(newState);
004b8382
LOK
947}
948
949void CCECClient::AddKey(void)
950{
2b44051c
LOK
951 cec_keypress key;
952 key.keycode = CEC_USER_CONTROL_CODE_UNKNOWN;
004b8382 953
004b8382 954 {
2b44051c
LOK
955 CLockObject lock(m_mutex);
956 if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN)
957 {
958 key.duration = (unsigned int) (GetTimeMs() - m_buttontime);
959 key.keycode = m_iCurrentButton;
004b8382 960
2b44051c
LOK
961 m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
962 m_buttontime = 0;
963 }
004b8382
LOK
964 }
965
2b44051c
LOK
966 if (key.keycode != CEC_USER_CONTROL_CODE_UNKNOWN)
967 {
968 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key released: %s (%1x)", ToString(key.keycode), key.keycode);
969 CallbackAddKey(key);
970 }
004b8382
LOK
971}
972
973void CCECClient::AddKey(const cec_keypress &key)
974{
21fa005b
LOK
975 // send back the previous key if there is one
976 AddKey();
977
2b44051c
LOK
978 {
979 CLockObject lock(m_mutex);
21fa005b
LOK
980 m_iCurrentButton = key.duration > 0 || key.keycode > CEC_USER_CONTROL_CODE_MAX ?
981 CEC_USER_CONTROL_CODE_UNKNOWN :
982 key.keycode;
2b44051c
LOK
983 m_buttontime = key.duration > 0 ? 0 : GetTimeMs();
984 }
004b8382 985
2b44051c
LOK
986 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key pressed: %s (%1x)", ToString(key.keycode), key.keycode);
987 CallbackAddKey(key);
004b8382
LOK
988}
989
c0152c09 990void CCECClient::SetCurrentButton(const cec_user_control_code iButtonCode)
004b8382 991{
c0152c09 992 // push a keypress to the buffer with 0 duration and another with the duration set when released
004b8382
LOK
993 cec_keypress key;
994 key.duration = 0;
995 key.keycode = iButtonCode;
996
997 AddKey(key);
998}
999
1000void CCECClient::CheckKeypressTimeout(void)
1001{
2b44051c
LOK
1002 cec_keypress key;
1003
004b8382 1004 {
2b44051c
LOK
1005 CLockObject lock(m_mutex);
1006 uint64_t iNow = GetTimeMs();
004b8382 1007
2b44051c
LOK
1008 if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN &&
1009 iNow - m_buttontime > CEC_BUTTON_TIMEOUT)
1010 {
1011 key.duration = (unsigned int) (iNow - m_buttontime);
1012 key.keycode = m_iCurrentButton;
004b8382 1013
2b44051c
LOK
1014 m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
1015 m_buttontime = 0;
1016 }
1017 else
1018 {
1019 return;
1020 }
1021 }
1022
1023 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key auto-released: %s (%1x)", ToString(key.keycode), key.keycode);
1024 CallbackAddKey(key);
004b8382
LOK
1025}
1026
1027bool CCECClient::EnableCallbacks(void *cbParam, ICECCallbacks *callbacks)
1028{
2b44051c 1029 CLockObject lock(m_cbMutex);
004b8382
LOK
1030 m_configuration.callbackParam = cbParam;
1031 m_configuration.callbacks = callbacks;
1032 return true;
1033}
1034
c0152c09
LOK
1035bool CCECClient::PingAdapter(void)
1036{
1037 return m_processor ? m_processor->PingAdapter() : false;
1038}
1039
004b8382
LOK
1040bool CCECClient::GetNextLogMessage(cec_log_message *message)
1041{
1042 return (m_logBuffer.Pop(*message));
1043}
1044
1045bool CCECClient::GetNextKeypress(cec_keypress *key)
1046{
1047 return m_keyBuffer.Pop(*key);
1048}
1049
1050bool CCECClient::GetNextCommand(cec_command *command)
1051{
1052 return m_commandBuffer.Pop(*command);
1053}
c0152c09 1054
2b44051c 1055std::string CCECClient::GetConnectionInfo(void)
c0152c09
LOK
1056{
1057 CStdString strLog;
1058 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);
1059 if (m_configuration.iFirmwareBuildDate != CEC_FW_BUILD_UNKNOWN)
1060 {
1061 time_t buildTime = (time_t)m_configuration.iFirmwareBuildDate;
1062 strLog.AppendFormat(", firmware build date: %s", asctime(gmtime(&buildTime)));
1063 strLog = strLog.Left((int)strLog.length() - 1); // strip \n added by asctime
1064 strLog.append(" +0000");
1065 }
1066
1067 // log the addresses that are being used
1068 if (!m_configuration.logicalAddresses.IsEmpty())
1069 {
1070 strLog.append(", logical address(es) = ");
1071 CECDEVICEVEC devices;
1072 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
1073 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
1074 strLog.AppendFormat("%s (%X) ", (*it)->GetLogicalAddressName(), (*it)->GetLogicalAddress());
1075 }
1076
1077 if (!CLibCEC::IsValidPhysicalAddress(m_configuration.iPhysicalAddress))
1078 strLog.AppendFormat(", base device: %s (%X), HDMI port number: %d", ToString(m_configuration.baseDevice), m_configuration.baseDevice, m_configuration.iHDMIPort);
1079 else
1080 strLog.AppendFormat(", physical address: %04x", m_configuration.iPhysicalAddress);
1081
2b44051c
LOK
1082 strLog.AppendFormat(", %s", LIB_CEC->GetLibInfo());
1083
1084 std::string strReturn(strLog.c_str());
1085 return strReturn;
c0152c09
LOK
1086}
1087
1088void CCECClient::SetTVVendorOverride(const cec_vendor_id id)
1089{
1090 {
1091 CLockObject lock(m_mutex);
1092 m_configuration.tvVendor = id;
1093 }
1094
1095 if (id != CEC_VENDOR_UNKNOWN)
1096 {
1097 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vendor id '%s'", __FUNCTION__, ToString(id));
1098
1099 CCECBusDevice *tv = m_processor ? m_processor->GetTV() : NULL;
1100 if (tv)
1101 tv->SetVendorId((uint64_t)id);
1102 }
2b44051c
LOK
1103
1104 // persist the new configuration
1105 PersistConfiguration(m_configuration);
c0152c09
LOK
1106}
1107
1108cec_vendor_id CCECClient::GetTVVendorOverride(void)
1109{
1110 CLockObject lock(m_mutex);
1111 return (cec_vendor_id)m_configuration.tvVendor;
1112}
1113
2b44051c 1114void CCECClient::SetOSDName(const std::string &strDeviceName)
c0152c09
LOK
1115{
1116 {
1117 CLockObject lock(m_mutex);
1118 snprintf(m_configuration.strDeviceName, 13, "%s", strDeviceName.c_str());
1119 }
1120
1121 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using OSD name '%s'", __FUNCTION__, strDeviceName.c_str());
1122
1123 CCECBusDevice *primary = GetPrimaryDevice();
2b44051c 1124 if (primary && !primary->GetCurrentOSDName().Equals(strDeviceName.c_str()))
c0152c09
LOK
1125 {
1126 primary->SetOSDName(strDeviceName);
0b8c7eab 1127 if (m_processor && m_processor->CECInitialised())
2b44051c 1128 primary->TransmitOSDName(CECDEVICE_TV, false);
c0152c09 1129 }
2b44051c
LOK
1130
1131 // persist the new configuration
1132 PersistConfiguration(m_configuration);
c0152c09
LOK
1133}
1134
2b44051c 1135std::string CCECClient::GetOSDName(void)
c0152c09
LOK
1136{
1137 CLockObject lock(m_mutex);
2b44051c 1138 std::string strOSDName(m_configuration.strDeviceName);
c0152c09
LOK
1139 return strOSDName;
1140}
1141
1142void CCECClient::SetWakeDevices(const cec_logical_addresses &addresses)
1143{
2b44051c
LOK
1144 {
1145 CLockObject lock(m_mutex);
1146 m_configuration.wakeDevices = addresses;
1147 }
1148 // persist the new configuration
1149 PersistConfiguration(m_configuration);
c0152c09
LOK
1150}
1151
1152cec_logical_addresses CCECClient::GetWakeDevices(void)
1153{
1154 CLockObject lock(m_mutex);
1155 return m_configuration.wakeDevices;
1156}
1157
1158bool CCECClient::AutodetectPhysicalAddress(void)
1159{
1160 bool bPhysicalAutodetected(false);
1161 uint16_t iPhysicalAddress = m_processor ? m_processor->GetDetectedPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS;
1162
1163 if (CLibCEC::IsValidPhysicalAddress(iPhysicalAddress))
1164 {
1165 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - autodetected physical address '%04X'", __FUNCTION__, iPhysicalAddress);
1166
1167 CLockObject lock(m_mutex);
1168 m_configuration.iPhysicalAddress = iPhysicalAddress;
1169 m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE;
1170 m_configuration.baseDevice = CECDEVICE_UNKNOWN;
1171 bPhysicalAutodetected = true;
1172 }
1173
1174 SetDevicePhysicalAddress(iPhysicalAddress);
1175
1176 return bPhysicalAutodetected;
1177}
1178
1179void CCECClient::SetClientVersion(const cec_client_version version)
1180{
1181 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using client version '%s'", __FUNCTION__, ToString(version));
1182
1183 CLockObject lock(m_mutex);
1184 m_configuration.clientVersion = (uint32_t)version;
1185}
1186
1187cec_client_version CCECClient::GetClientVersion(void)
1188{
1189 CLockObject lock(m_mutex);
1190 return (cec_client_version)m_configuration.clientVersion;
1191}
1192
1193bool CCECClient::SetDeviceTypes(const cec_device_type_list &deviceTypes)
1194{
1195 bool bNeedReinit(false);
1196
1197 {
1198 CLockObject lock(m_mutex);
0b8c7eab 1199 bNeedReinit = m_processor && m_processor->CECInitialised() &&
c0152c09
LOK
1200 (m_configuration.deviceTypes != deviceTypes);
1201 m_configuration.deviceTypes = deviceTypes;
1202 }
1203
2b44051c
LOK
1204 // persist the new configuration
1205 PersistConfiguration(m_configuration);
1206
c0152c09
LOK
1207 if (bNeedReinit)
1208 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using primary device type '%s'", __FUNCTION__, ToString(deviceTypes[0]));
1209
1210 return bNeedReinit;
1211}
1212
1213cec_device_type_list CCECClient::GetDeviceTypes(void)
1214{
1215 cec_device_type_list retVal;
1216 CLockObject lock(m_mutex);
1217 retVal = m_configuration.deviceTypes;
1218 return retVal;
1219}
1220
1221bool CCECClient::SetDevicePhysicalAddress(const uint16_t iPhysicalAddress)
1222{
1223 if (!CLibCEC::IsValidPhysicalAddress(iPhysicalAddress))
b1d821c6 1224 {
ea508fa6 1225 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - not setting invalid physical address %04x", __FUNCTION__, iPhysicalAddress);
c0152c09 1226 return false;
b1d821c6 1227 }
c0152c09
LOK
1228
1229 // reconfigure all devices
1230 cec_logical_address reactivateSource(CECDEVICE_UNKNOWN);
1231 CECDEVICEVEC devices;
1232 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
1233 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
1234 {
1235 // if this device was the active source, reactivate it afterwards
1236 if ((*it)->IsActiveSource())
1237 reactivateSource = (*it)->GetLogicalAddress();
1238
1239 // mark the device as inactive source
1240 if (IsInitialised())
1241 (*it)->MarkAsInactiveSource();
1242
1243 // set the new physical address
1244 (*it)->SetPhysicalAddress(iPhysicalAddress);
1245
1246 // and transmit it
1247 if (IsInitialised())
2b44051c 1248 (*it)->TransmitPhysicalAddress(false);
c0152c09
LOK
1249 }
1250
1251 // reactivate the previous active source
1252 if (reactivateSource != CECDEVICE_UNKNOWN &&
0b8c7eab 1253 m_processor->CECInitialised() &&
c0152c09
LOK
1254 IsInitialised())
1255 {
1256 CCECBusDevice *device = m_processor->GetDevice(reactivateSource);
1257 if (device)
1258 device->ActivateSource();
1259 }
1260
2b44051c
LOK
1261 // persist the new configuration
1262 PersistConfiguration(m_configuration);
1263
c0152c09
LOK
1264 return true;
1265}
1266
1267bool CCECClient::SwitchMonitoring(bool bEnable)
1268{
4a01fcd6
LOK
1269 LIB_CEC->AddLog(CEC_LOG_NOTICE, "== %s monitoring mode ==", bEnable ? "enabling" : "disabling");
1270
1271 if (m_processor)
1272 {
cc0a2977
LOK
1273 m_processor->SwitchMonitoring(bEnable);
1274 m_configuration.bMonitorOnly = bEnable;
1275 return bEnable ? true: m_processor->RegisterClient(this);
4a01fcd6
LOK
1276 }
1277
1278 return false;
c0152c09
LOK
1279}
1280
1281bool CCECClient::PollDevice(const cec_logical_address iAddress)
1282{
1283 // try to find the primary device
1284 CCECBusDevice *primary = GetPrimaryDevice();
1285 // poll the destination, with the primary as source
1286 if (primary)
2b44051c 1287 return primary->TransmitPoll(iAddress, false);
c0152c09
LOK
1288
1289 return m_processor ? m_processor->PollDevice(iAddress) : false;
1290}
1291
1292cec_logical_addresses CCECClient::GetActiveDevices(void)
1293{
1294 CECDEVICEVEC activeDevices;
1295 if (m_processor)
1296 m_processor->GetDevices()->GetActive(activeDevices);
1297 return CCECDeviceMap::ToLogicalAddresses(activeDevices);
1298}
1299
1300bool CCECClient::IsActiveDevice(const cec_logical_address iAddress)
1301{
1302 cec_logical_addresses activeDevices = GetActiveDevices();
1303 return activeDevices.IsSet(iAddress);
1304}
1305
1306bool CCECClient::IsActiveDeviceType(const cec_device_type type)
1307{
1308 CECDEVICEVEC activeDevices;
1309 if (m_processor)
1310 m_processor->GetDevices()->GetActive(activeDevices);
1311 CCECDeviceMap::FilterType(type, activeDevices);
1312 return !activeDevices.empty();
1313}
1314
1315cec_logical_address CCECClient::GetActiveSource(void)
1316{
1317 return m_processor ? m_processor->GetActiveSource() : CECDEVICE_UNKNOWN;
1318}
1319
1320bool CCECClient::IsActiveSource(const cec_logical_address iAddress)
1321{
1322 return m_processor ? m_processor->IsActiveSource(iAddress) : false;
1323}
1324
1325bool CCECClient::SetStreamPath(const cec_logical_address iAddress)
1326{
1327 uint16_t iPhysicalAddress = GetDevicePhysicalAddress(iAddress);
1328 if (iPhysicalAddress != CEC_INVALID_PHYSICAL_ADDRESS)
1329 return SetStreamPath(iPhysicalAddress);
1330 return false;
1331}
1332
1333bool CCECClient::SetStreamPath(const uint16_t iPhysicalAddress)
1334{
2b44051c
LOK
1335 bool bReturn(false);
1336
1337 CCECBusDevice *device = GetDeviceByType(CEC_DEVICE_TYPE_TV);
1338 if (device)
1339 {
1340 device->SetStreamPath(iPhysicalAddress);
1341 bReturn = device->GetHandler()->TransmitSetStreamPath(iPhysicalAddress, false);
1342 device->MarkHandlerReady();
1343 }
1344 else
1345 {
1346 LIB_CEC->AddLog(CEC_LOG_ERROR, "only the TV is allowed to send CEC_OPCODE_SET_STREAM_PATH");
1347 }
1348
1349 return bReturn;
c0152c09
LOK
1350}
1351
1352cec_logical_addresses CCECClient::GetLogicalAddresses(void)
1353{
1354 cec_logical_addresses addresses;
1355 CLockObject lock(m_mutex);
1356 addresses = m_configuration.logicalAddresses;
1357 return addresses;
1358}
1359
1360bool CCECClient::CanPersistConfiguration(void)
1361{
1362 return m_processor ? m_processor->CanPersistConfiguration() : false;
1363}
1364
1365bool CCECClient::PersistConfiguration(const libcec_configuration &configuration)
1366{
2b44051c
LOK
1367 return m_processor && IsRegistered() ?
1368 m_processor->PersistConfiguration(configuration) :
1369 false;
c0152c09
LOK
1370}
1371
1372void CCECClient::RescanActiveDevices(void)
1373{
1374 if (m_processor)
1375 m_processor->RescanActiveDevices();
1376}
1377
1378bool CCECClient::IsLibCECActiveSource(void)
1379{
1380 bool bReturn(false);
1381 if (m_processor)
1382 {
1383 cec_logical_address activeSource = m_processor->GetActiveSource();
1384 CCECBusDevice *device = m_processor->GetDevice(activeSource);
1385 if (device)
1386 bReturn = device->IsHandledByLibCEC();
1387 }
1388 return bReturn;
1389}
2b44051c
LOK
1390
1391void CCECClient::SourceActivated(const cec_logical_address logicalAddress)
1392{
1393 LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> source activated: %s (%x)", ToString(logicalAddress), logicalAddress);
1394 CallbackSourceActivated(true, logicalAddress);
1395}
1396
1397void CCECClient::SourceDeactivated(const cec_logical_address logicalAddress)
1398{
1399 LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> source deactivated: %s (%x)", ToString(logicalAddress), logicalAddress);
1400 CallbackSourceActivated(false, logicalAddress);
1401}
1402
1403void CCECClient::CallbackAddCommand(const cec_command &command)
1404{
1405 {
1406 CLockObject lock(m_cbMutex);
1407 if (m_configuration.callbacks && m_configuration.callbacks->CBCecCommand)
1408 {
1409 m_configuration.callbacks->CBCecCommand(m_configuration.callbackParam, command);
1410 return;
1411 }
1412 }
1413 m_commandBuffer.Push(command);
1414}
1415
1416void CCECClient::CallbackAddKey(const cec_keypress &key)
1417{
1418 {
1419 CLockObject lock(m_cbMutex);
1420 if (m_configuration.callbacks && m_configuration.callbacks->CBCecKeyPress)
1421 {
1422 m_configuration.callbacks->CBCecKeyPress(m_configuration.callbackParam, key);
1423 return;
1424 }
1425 }
1426 m_keyBuffer.Push(key);
1427}
1428
1429void CCECClient::CallbackAddLog(const cec_log_message &message)
1430{
1431 {
1432 CLockObject lock(m_cbMutex);
1433 if (m_configuration.callbacks && m_configuration.callbacks->CBCecLogMessage)
1434 {
1435 m_configuration.callbacks->CBCecLogMessage(m_configuration.callbackParam, message);
1436 return;
1437 }
1438 }
1439 m_logBuffer.Push(message);
1440}
1441
1442void CCECClient::CallbackConfigurationChanged(const libcec_configuration &config)
1443{
1444 CLockObject lock(m_cbMutex);
1445 if (m_configuration.callbacks &&
1446 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0 &&
1447 m_configuration.callbacks->CBCecConfigurationChanged &&
1448 m_processor->CECInitialised())
1449 m_configuration.callbacks->CBCecConfigurationChanged(m_configuration.callbackParam, config);
1450}
1451
1452void CCECClient::CallbackSourceActivated(bool bActivated, const cec_logical_address logicalAddress)
1453{
1454 CLockObject lock(m_cbMutex);
1455 if (m_configuration.callbacks &&
1456 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_7_1 &&
1457 m_configuration.callbacks->CBCecSourceActivated)
1458 m_configuration.callbacks->CBCecSourceActivated(m_configuration.callbackParam, logicalAddress, bActivated ? 1 : 0);
1459}
1460
1461void CCECClient::CallbackAlert(const libcec_alert type, const libcec_parameter &param)
1462{
1463 CLockObject lock(m_cbMutex);
1464 if (m_configuration.callbacks &&
1465 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_0 &&
1466 m_configuration.callbacks->CBCecAlert)
1467 m_configuration.callbacks->CBCecAlert(m_configuration.callbackParam, type, param);
1468}
1469
1470int CCECClient::CallbackMenuStateChanged(const cec_menu_state newState)
1471{
1472 CLockObject lock(m_cbMutex);
1473 if (m_configuration.callbacks &&
1474 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_2 &&
1475 m_configuration.callbacks->CBCecMenuStateChanged)
1476 return m_configuration.callbacks->CBCecMenuStateChanged(m_configuration.callbackParam, newState);
1477 return 0;
1478}