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