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