0bbc081191652ba7b65a80bb88bd2070d5358e40
[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 // send back the previous key if there is one
976 AddKey();
977
978 {
979 CLockObject lock(m_mutex);
980 m_iCurrentButton = key.duration > 0 || key.keycode > CEC_USER_CONTROL_CODE_MAX ?
981 CEC_USER_CONTROL_CODE_UNKNOWN :
982 key.keycode;
983 m_buttontime = key.duration > 0 ? 0 : GetTimeMs();
984 }
985
986 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key pressed: %s (%1x)", ToString(key.keycode), key.keycode);
987 CallbackAddKey(key);
988 }
989
990 void CCECClient::SetCurrentButton(const cec_user_control_code iButtonCode)
991 {
992 // push a keypress to the buffer with 0 duration and another with the duration set when released
993 cec_keypress key;
994 key.duration = 0;
995 key.keycode = iButtonCode;
996
997 AddKey(key);
998 }
999
1000 void CCECClient::CheckKeypressTimeout(void)
1001 {
1002 cec_keypress key;
1003
1004 {
1005 CLockObject lock(m_mutex);
1006 uint64_t iNow = GetTimeMs();
1007
1008 if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN &&
1009 iNow - m_buttontime > CEC_BUTTON_TIMEOUT)
1010 {
1011 key.duration = (unsigned int) (iNow - m_buttontime);
1012 key.keycode = m_iCurrentButton;
1013
1014 m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
1015 m_buttontime = 0;
1016 }
1017 else
1018 {
1019 return;
1020 }
1021 }
1022
1023 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key auto-released: %s (%1x)", ToString(key.keycode), key.keycode);
1024 CallbackAddKey(key);
1025 }
1026
1027 bool CCECClient::EnableCallbacks(void *cbParam, ICECCallbacks *callbacks)
1028 {
1029 CLockObject lock(m_cbMutex);
1030 m_configuration.callbackParam = cbParam;
1031 m_configuration.callbacks = callbacks;
1032 return true;
1033 }
1034
1035 bool CCECClient::PingAdapter(void)
1036 {
1037 return m_processor ? m_processor->PingAdapter() : false;
1038 }
1039
1040 bool CCECClient::GetNextLogMessage(cec_log_message *message)
1041 {
1042 return (m_logBuffer.Pop(*message));
1043 }
1044
1045 bool CCECClient::GetNextKeypress(cec_keypress *key)
1046 {
1047 return m_keyBuffer.Pop(*key);
1048 }
1049
1050 bool CCECClient::GetNextCommand(cec_command *command)
1051 {
1052 return m_commandBuffer.Pop(*command);
1053 }
1054
1055 std::string CCECClient::GetConnectionInfo(void)
1056 {
1057 CStdString strLog;
1058 strLog.Format("libCEC version = %s, client version = %s, firmware version = %d", ToString((cec_server_version)m_configuration.serverVersion), ToString((cec_client_version)m_configuration.clientVersion), m_configuration.iFirmwareVersion);
1059 if (m_configuration.iFirmwareBuildDate != CEC_FW_BUILD_UNKNOWN)
1060 {
1061 time_t buildTime = (time_t)m_configuration.iFirmwareBuildDate;
1062 strLog.AppendFormat(", firmware build date: %s", asctime(gmtime(&buildTime)));
1063 strLog = strLog.Left((int)strLog.length() - 1); // strip \n added by asctime
1064 strLog.append(" +0000");
1065 }
1066
1067 // log the addresses that are being used
1068 if (!m_configuration.logicalAddresses.IsEmpty())
1069 {
1070 strLog.append(", logical address(es) = ");
1071 CECDEVICEVEC devices;
1072 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
1073 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
1074 strLog.AppendFormat("%s (%X) ", (*it)->GetLogicalAddressName(), (*it)->GetLogicalAddress());
1075 }
1076
1077 if (!CLibCEC::IsValidPhysicalAddress(m_configuration.iPhysicalAddress))
1078 strLog.AppendFormat(", base device: %s (%X), HDMI port number: %d", ToString(m_configuration.baseDevice), m_configuration.baseDevice, m_configuration.iHDMIPort);
1079 else
1080 strLog.AppendFormat(", physical address: %04x", m_configuration.iPhysicalAddress);
1081
1082 strLog.AppendFormat(", %s", LIB_CEC->GetLibInfo());
1083
1084 std::string strReturn(strLog.c_str());
1085 return strReturn;
1086 }
1087
1088 void CCECClient::SetTVVendorOverride(const cec_vendor_id id)
1089 {
1090 {
1091 CLockObject lock(m_mutex);
1092 m_configuration.tvVendor = id;
1093 }
1094
1095 if (id != CEC_VENDOR_UNKNOWN)
1096 {
1097 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vendor id '%s'", __FUNCTION__, ToString(id));
1098
1099 CCECBusDevice *tv = m_processor ? m_processor->GetTV() : NULL;
1100 if (tv)
1101 tv->SetVendorId((uint64_t)id);
1102 }
1103
1104 // persist the new configuration
1105 PersistConfiguration(m_configuration);
1106 }
1107
1108 cec_vendor_id CCECClient::GetTVVendorOverride(void)
1109 {
1110 CLockObject lock(m_mutex);
1111 return (cec_vendor_id)m_configuration.tvVendor;
1112 }
1113
1114 void CCECClient::SetOSDName(const std::string &strDeviceName)
1115 {
1116 {
1117 CLockObject lock(m_mutex);
1118 snprintf(m_configuration.strDeviceName, 13, "%s", strDeviceName.c_str());
1119 }
1120
1121 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using OSD name '%s'", __FUNCTION__, strDeviceName.c_str());
1122
1123 CCECBusDevice *primary = GetPrimaryDevice();
1124 if (primary && !primary->GetCurrentOSDName().Equals(strDeviceName.c_str()))
1125 {
1126 primary->SetOSDName(strDeviceName);
1127 if (m_processor && m_processor->CECInitialised())
1128 primary->TransmitOSDName(CECDEVICE_TV, false);
1129 }
1130
1131 // persist the new configuration
1132 PersistConfiguration(m_configuration);
1133 }
1134
1135 std::string CCECClient::GetOSDName(void)
1136 {
1137 CLockObject lock(m_mutex);
1138 std::string strOSDName(m_configuration.strDeviceName);
1139 return strOSDName;
1140 }
1141
1142 void CCECClient::SetWakeDevices(const cec_logical_addresses &addresses)
1143 {
1144 {
1145 CLockObject lock(m_mutex);
1146 m_configuration.wakeDevices = addresses;
1147 }
1148 // persist the new configuration
1149 PersistConfiguration(m_configuration);
1150 }
1151
1152 cec_logical_addresses CCECClient::GetWakeDevices(void)
1153 {
1154 CLockObject lock(m_mutex);
1155 return m_configuration.wakeDevices;
1156 }
1157
1158 bool CCECClient::AutodetectPhysicalAddress(void)
1159 {
1160 bool bPhysicalAutodetected(false);
1161 uint16_t iPhysicalAddress = m_processor ? m_processor->GetDetectedPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS;
1162
1163 if (CLibCEC::IsValidPhysicalAddress(iPhysicalAddress))
1164 {
1165 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - autodetected physical address '%04X'", __FUNCTION__, iPhysicalAddress);
1166
1167 CLockObject lock(m_mutex);
1168 m_configuration.iPhysicalAddress = iPhysicalAddress;
1169 m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE;
1170 m_configuration.baseDevice = CECDEVICE_UNKNOWN;
1171 bPhysicalAutodetected = true;
1172 }
1173
1174 SetDevicePhysicalAddress(iPhysicalAddress);
1175
1176 return bPhysicalAutodetected;
1177 }
1178
1179 void CCECClient::SetClientVersion(const cec_client_version version)
1180 {
1181 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using client version '%s'", __FUNCTION__, ToString(version));
1182
1183 CLockObject lock(m_mutex);
1184 m_configuration.clientVersion = (uint32_t)version;
1185 }
1186
1187 cec_client_version CCECClient::GetClientVersion(void)
1188 {
1189 CLockObject lock(m_mutex);
1190 return (cec_client_version)m_configuration.clientVersion;
1191 }
1192
1193 bool CCECClient::SetDeviceTypes(const cec_device_type_list &deviceTypes)
1194 {
1195 bool bNeedReinit(false);
1196
1197 {
1198 CLockObject lock(m_mutex);
1199 bNeedReinit = m_processor && m_processor->CECInitialised() &&
1200 (m_configuration.deviceTypes != deviceTypes);
1201 m_configuration.deviceTypes = deviceTypes;
1202 }
1203
1204 // persist the new configuration
1205 PersistConfiguration(m_configuration);
1206
1207 if (bNeedReinit)
1208 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using primary device type '%s'", __FUNCTION__, ToString(deviceTypes[0]));
1209
1210 return bNeedReinit;
1211 }
1212
1213 cec_device_type_list CCECClient::GetDeviceTypes(void)
1214 {
1215 cec_device_type_list retVal;
1216 CLockObject lock(m_mutex);
1217 retVal = m_configuration.deviceTypes;
1218 return retVal;
1219 }
1220
1221 bool CCECClient::SetDevicePhysicalAddress(const uint16_t iPhysicalAddress)
1222 {
1223 if (!CLibCEC::IsValidPhysicalAddress(iPhysicalAddress))
1224 {
1225 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - not setting invalid physical address %04x", __FUNCTION__, iPhysicalAddress);
1226 return false;
1227 }
1228
1229 // reconfigure all devices
1230 cec_logical_address reactivateSource(CECDEVICE_UNKNOWN);
1231 CECDEVICEVEC devices;
1232 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
1233 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
1234 {
1235 // if this device was the active source, reactivate it afterwards
1236 if ((*it)->IsActiveSource())
1237 reactivateSource = (*it)->GetLogicalAddress();
1238
1239 // mark the device as inactive source
1240 if (IsInitialised())
1241 (*it)->MarkAsInactiveSource();
1242
1243 // set the new physical address
1244 (*it)->SetPhysicalAddress(iPhysicalAddress);
1245
1246 // and transmit it
1247 if (IsInitialised())
1248 (*it)->TransmitPhysicalAddress(false);
1249 }
1250
1251 // reactivate the previous active source
1252 if (reactivateSource != CECDEVICE_UNKNOWN &&
1253 m_processor->CECInitialised() &&
1254 IsInitialised())
1255 {
1256 CCECBusDevice *device = m_processor->GetDevice(reactivateSource);
1257 if (device)
1258 device->ActivateSource();
1259 }
1260
1261 // persist the new configuration
1262 PersistConfiguration(m_configuration);
1263
1264 return true;
1265 }
1266
1267 bool CCECClient::SwitchMonitoring(bool bEnable)
1268 {
1269 LIB_CEC->AddLog(CEC_LOG_NOTICE, "== %s monitoring mode ==", bEnable ? "enabling" : "disabling");
1270
1271 if (m_processor)
1272 {
1273 m_processor->SwitchMonitoring(bEnable);
1274 m_configuration.bMonitorOnly = bEnable;
1275 return bEnable ? true: m_processor->RegisterClient(this);
1276 }
1277
1278 return false;
1279 }
1280
1281 bool CCECClient::PollDevice(const cec_logical_address iAddress)
1282 {
1283 // try to find the primary device
1284 CCECBusDevice *primary = GetPrimaryDevice();
1285 // poll the destination, with the primary as source
1286 if (primary)
1287 return primary->TransmitPoll(iAddress, false);
1288
1289 return m_processor ? m_processor->PollDevice(iAddress) : false;
1290 }
1291
1292 cec_logical_addresses CCECClient::GetActiveDevices(void)
1293 {
1294 CECDEVICEVEC activeDevices;
1295 if (m_processor)
1296 m_processor->GetDevices()->GetActive(activeDevices);
1297 return CCECDeviceMap::ToLogicalAddresses(activeDevices);
1298 }
1299
1300 bool CCECClient::IsActiveDevice(const cec_logical_address iAddress)
1301 {
1302 cec_logical_addresses activeDevices = GetActiveDevices();
1303 return activeDevices.IsSet(iAddress);
1304 }
1305
1306 bool CCECClient::IsActiveDeviceType(const cec_device_type type)
1307 {
1308 CECDEVICEVEC activeDevices;
1309 if (m_processor)
1310 m_processor->GetDevices()->GetActive(activeDevices);
1311 CCECDeviceMap::FilterType(type, activeDevices);
1312 return !activeDevices.empty();
1313 }
1314
1315 cec_logical_address CCECClient::GetActiveSource(void)
1316 {
1317 return m_processor ? m_processor->GetActiveSource() : CECDEVICE_UNKNOWN;
1318 }
1319
1320 bool CCECClient::IsActiveSource(const cec_logical_address iAddress)
1321 {
1322 return m_processor ? m_processor->IsActiveSource(iAddress) : false;
1323 }
1324
1325 bool CCECClient::SetStreamPath(const cec_logical_address iAddress)
1326 {
1327 uint16_t iPhysicalAddress = GetDevicePhysicalAddress(iAddress);
1328 if (iPhysicalAddress != CEC_INVALID_PHYSICAL_ADDRESS)
1329 return SetStreamPath(iPhysicalAddress);
1330 return false;
1331 }
1332
1333 bool CCECClient::SetStreamPath(const uint16_t iPhysicalAddress)
1334 {
1335 bool bReturn(false);
1336
1337 CCECBusDevice *device = GetDeviceByType(CEC_DEVICE_TYPE_TV);
1338 if (device)
1339 {
1340 device->SetStreamPath(iPhysicalAddress);
1341 bReturn = device->GetHandler()->TransmitSetStreamPath(iPhysicalAddress, false);
1342 device->MarkHandlerReady();
1343 }
1344 else
1345 {
1346 LIB_CEC->AddLog(CEC_LOG_ERROR, "only the TV is allowed to send CEC_OPCODE_SET_STREAM_PATH");
1347 }
1348
1349 return bReturn;
1350 }
1351
1352 cec_logical_addresses CCECClient::GetLogicalAddresses(void)
1353 {
1354 cec_logical_addresses addresses;
1355 CLockObject lock(m_mutex);
1356 addresses = m_configuration.logicalAddresses;
1357 return addresses;
1358 }
1359
1360 bool CCECClient::CanPersistConfiguration(void)
1361 {
1362 return m_processor ? m_processor->CanPersistConfiguration() : false;
1363 }
1364
1365 bool CCECClient::PersistConfiguration(const libcec_configuration &configuration)
1366 {
1367 return m_processor && IsRegistered() ?
1368 m_processor->PersistConfiguration(configuration) :
1369 false;
1370 }
1371
1372 void CCECClient::RescanActiveDevices(void)
1373 {
1374 if (m_processor)
1375 m_processor->RescanActiveDevices();
1376 }
1377
1378 bool CCECClient::IsLibCECActiveSource(void)
1379 {
1380 bool bReturn(false);
1381 if (m_processor)
1382 {
1383 cec_logical_address activeSource = m_processor->GetActiveSource();
1384 CCECBusDevice *device = m_processor->GetDevice(activeSource);
1385 if (device)
1386 bReturn = device->IsHandledByLibCEC();
1387 }
1388 return bReturn;
1389 }
1390
1391 void CCECClient::SourceActivated(const cec_logical_address logicalAddress)
1392 {
1393 LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> source activated: %s (%x)", ToString(logicalAddress), logicalAddress);
1394 CallbackSourceActivated(true, logicalAddress);
1395 }
1396
1397 void CCECClient::SourceDeactivated(const cec_logical_address logicalAddress)
1398 {
1399 LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> source deactivated: %s (%x)", ToString(logicalAddress), logicalAddress);
1400 CallbackSourceActivated(false, logicalAddress);
1401 }
1402
1403 void CCECClient::CallbackAddCommand(const cec_command &command)
1404 {
1405 {
1406 CLockObject lock(m_cbMutex);
1407 if (m_configuration.callbacks && m_configuration.callbacks->CBCecCommand)
1408 {
1409 m_configuration.callbacks->CBCecCommand(m_configuration.callbackParam, command);
1410 return;
1411 }
1412 }
1413 m_commandBuffer.Push(command);
1414 }
1415
1416 void CCECClient::CallbackAddKey(const cec_keypress &key)
1417 {
1418 {
1419 CLockObject lock(m_cbMutex);
1420 if (m_configuration.callbacks && m_configuration.callbacks->CBCecKeyPress)
1421 {
1422 m_configuration.callbacks->CBCecKeyPress(m_configuration.callbackParam, key);
1423 return;
1424 }
1425 }
1426 m_keyBuffer.Push(key);
1427 }
1428
1429 void CCECClient::CallbackAddLog(const cec_log_message &message)
1430 {
1431 {
1432 CLockObject lock(m_cbMutex);
1433 if (m_configuration.callbacks && m_configuration.callbacks->CBCecLogMessage)
1434 {
1435 m_configuration.callbacks->CBCecLogMessage(m_configuration.callbackParam, message);
1436 return;
1437 }
1438 }
1439 m_logBuffer.Push(message);
1440 }
1441
1442 void CCECClient::CallbackConfigurationChanged(const libcec_configuration &config)
1443 {
1444 CLockObject lock(m_cbMutex);
1445 if (m_configuration.callbacks &&
1446 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0 &&
1447 m_configuration.callbacks->CBCecConfigurationChanged &&
1448 m_processor->CECInitialised())
1449 m_configuration.callbacks->CBCecConfigurationChanged(m_configuration.callbackParam, config);
1450 }
1451
1452 void CCECClient::CallbackSourceActivated(bool bActivated, const cec_logical_address logicalAddress)
1453 {
1454 CLockObject lock(m_cbMutex);
1455 if (m_configuration.callbacks &&
1456 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_7_1 &&
1457 m_configuration.callbacks->CBCecSourceActivated)
1458 m_configuration.callbacks->CBCecSourceActivated(m_configuration.callbackParam, logicalAddress, bActivated ? 1 : 0);
1459 }
1460
1461 void CCECClient::CallbackAlert(const libcec_alert type, const libcec_parameter &param)
1462 {
1463 CLockObject lock(m_cbMutex);
1464 if (m_configuration.callbacks &&
1465 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_0 &&
1466 m_configuration.callbacks->CBCecAlert)
1467 m_configuration.callbacks->CBCecAlert(m_configuration.callbackParam, type, param);
1468 }
1469
1470 int CCECClient::CallbackMenuStateChanged(const cec_menu_state newState)
1471 {
1472 CLockObject lock(m_cbMutex);
1473 if (m_configuration.callbacks &&
1474 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_2 &&
1475 m_configuration.callbacks->CBCecMenuStateChanged)
1476 return m_configuration.callbacks->CBCecMenuStateChanged(m_configuration.callbackParam, newState);
1477 return 0;
1478 }