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