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