Merge branch 'master' into release
[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 return true;
806 }
807
808 bool CCECClient::SetConfiguration(const libcec_configuration &configuration)
809 {
810 bool bIsRunning(m_processor && m_processor->CECInitialised());
811 CCECBusDevice *primary = bIsRunning ? GetPrimaryDevice() : NULL;
812 uint16_t iPA = primary ? primary->GetCurrentPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS;
813
814 // update the callbacks
815 if (configuration.callbacks)
816 EnableCallbacks(configuration.callbackParam, configuration.callbacks);
817
818 // update the client version
819 SetClientVersion((cec_client_version)configuration.clientVersion);
820
821 // update the OSD name
822 CStdString strOSDName(configuration.strDeviceName);
823 SetOSDName(strOSDName);
824
825 // update the TV vendor override
826 SetTVVendorOverride((cec_vendor_id)configuration.tvVendor);
827
828 // just copy these
829 {
830 CLockObject lock(m_mutex);
831 m_configuration.bUseTVMenuLanguage = configuration.bUseTVMenuLanguage;
832 m_configuration.bActivateSource = configuration.bActivateSource;
833 m_configuration.bGetSettingsFromROM = configuration.bGetSettingsFromROM;
834 m_configuration.wakeDevices = configuration.wakeDevices;
835 m_configuration.powerOffDevices = configuration.powerOffDevices;
836 m_configuration.bPowerOffScreensaver = configuration.bPowerOffScreensaver;
837 m_configuration.bPowerOffOnStandby = configuration.bPowerOffOnStandby;
838
839 // client version 1.5.1
840 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_1)
841 m_configuration.bSendInactiveSource = configuration.bSendInactiveSource;
842
843 // client version 1.6.0
844 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_0)
845 {
846 m_configuration.bPowerOffDevicesOnStandby = configuration.bPowerOffDevicesOnStandby;
847 m_configuration.bShutdownOnStandby = configuration.bShutdownOnStandby;
848 }
849
850 // client version 1.6.2
851 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_2)
852 {
853 memcpy(m_configuration.strDeviceLanguage, configuration.strDeviceLanguage, 3);
854 }
855
856 // client version 1.6.3
857 if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_3)
858 {
859 m_configuration.bMonitorOnly = configuration.bMonitorOnly;
860 }
861
862 // ensure that there is at least 1 device type set
863 if (m_configuration.deviceTypes.IsEmpty())
864 m_configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
865 }
866
867 bool bNeedReinit(false);
868
869 // device types
870 if (SetDeviceTypes(configuration.deviceTypes))
871 {
872 // the device type changed. just copy the rest, and re-register
873 {
874 CLockObject lock(m_mutex);
875 m_configuration.iPhysicalAddress = configuration.iPhysicalAddress;
876 m_configuration.baseDevice = configuration.baseDevice;
877 m_configuration.iHDMIPort = configuration.iHDMIPort;
878 bNeedReinit = true;
879 }
880 }
881 else
882 {
883 // set the physical address
884 SetPhysicalAddress(configuration);
885 }
886
887 // persist the new configuration
888 PersistConfiguration(m_configuration);
889
890 if (!primary)
891 primary = GetPrimaryDevice();
892
893 if (bNeedReinit || !primary || primary->GetCurrentPhysicalAddress() != iPA)
894 {
895 // PA or device type changed
896 m_processor->RegisterClient(this);
897 }
898 else if (primary && configuration.bActivateSource == 1 && bIsRunning && !primary->IsActiveSource())
899 {
900 // activate the source if we're not already the active source
901 primary->ActivateSource();
902 }
903
904 return true;
905 }
906
907 void CCECClient::AddCommand(const cec_command &command)
908 {
909 // don't forward the standby opcode more than once every 10 seconds
910 if (command.opcode == CEC_OPCODE_STANDBY)
911 {
912 CLockObject lock(m_mutex);
913 if (m_iPreventForwardingPowerOffCommand != 0 &&
914 m_iPreventForwardingPowerOffCommand > GetTimeMs())
915 return;
916 else
917 m_iPreventForwardingPowerOffCommand = GetTimeMs() + CEC_FORWARD_STANDBY_MIN_INTERVAL;
918 }
919
920 if (command.destination == CECDEVICE_BROADCAST || GetLogicalAddresses().IsSet(command.destination))
921 {
922 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);
923 CallbackAddCommand(command);
924 }
925 }
926
927 int CCECClient::MenuStateChanged(const cec_menu_state newState)
928 {
929 LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> %s: %s", ToString(CEC_OPCODE_MENU_REQUEST), ToString(newState));
930 return CallbackMenuStateChanged(newState);
931 }
932
933 void CCECClient::AddKey(void)
934 {
935 cec_keypress key;
936 key.keycode = CEC_USER_CONTROL_CODE_UNKNOWN;
937
938 {
939 CLockObject lock(m_mutex);
940 if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN)
941 {
942 key.duration = (unsigned int) (GetTimeMs() - m_buttontime);
943 key.keycode = m_iCurrentButton;
944
945 m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
946 m_buttontime = 0;
947 }
948 }
949
950 if (key.keycode != CEC_USER_CONTROL_CODE_UNKNOWN)
951 {
952 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key released: %s (%1x)", ToString(key.keycode), key.keycode);
953 CallbackAddKey(key);
954 }
955 }
956
957 void CCECClient::AddKey(const cec_keypress &key)
958 {
959 {
960 CLockObject lock(m_mutex);
961 m_iCurrentButton = key.duration > 0 ? CEC_USER_CONTROL_CODE_UNKNOWN : key.keycode;
962 m_buttontime = key.duration > 0 ? 0 : GetTimeMs();
963 }
964
965 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key pressed: %s (%1x)", ToString(key.keycode), key.keycode);
966 CallbackAddKey(key);
967 }
968
969 void CCECClient::SetCurrentButton(const cec_user_control_code iButtonCode)
970 {
971 // push a keypress to the buffer with 0 duration and another with the duration set when released
972 cec_keypress key;
973 key.duration = 0;
974 key.keycode = iButtonCode;
975
976 AddKey(key);
977 }
978
979 void CCECClient::CheckKeypressTimeout(void)
980 {
981 cec_keypress key;
982
983 {
984 CLockObject lock(m_mutex);
985 uint64_t iNow = GetTimeMs();
986
987 if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN &&
988 iNow - m_buttontime > CEC_BUTTON_TIMEOUT)
989 {
990 key.duration = (unsigned int) (iNow - m_buttontime);
991 key.keycode = m_iCurrentButton;
992
993 m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
994 m_buttontime = 0;
995 }
996 else
997 {
998 return;
999 }
1000 }
1001
1002 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key auto-released: %s (%1x)", ToString(key.keycode), key.keycode);
1003 CallbackAddKey(key);
1004 }
1005
1006 bool CCECClient::EnableCallbacks(void *cbParam, ICECCallbacks *callbacks)
1007 {
1008 CLockObject lock(m_cbMutex);
1009 m_configuration.callbackParam = cbParam;
1010 m_configuration.callbacks = callbacks;
1011 return true;
1012 }
1013
1014 bool CCECClient::PingAdapter(void)
1015 {
1016 return m_processor ? m_processor->PingAdapter() : false;
1017 }
1018
1019 bool CCECClient::GetNextLogMessage(cec_log_message *message)
1020 {
1021 return (m_logBuffer.Pop(*message));
1022 }
1023
1024 bool CCECClient::GetNextKeypress(cec_keypress *key)
1025 {
1026 return m_keyBuffer.Pop(*key);
1027 }
1028
1029 bool CCECClient::GetNextCommand(cec_command *command)
1030 {
1031 return m_commandBuffer.Pop(*command);
1032 }
1033
1034 std::string CCECClient::GetConnectionInfo(void)
1035 {
1036 CStdString strLog;
1037 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);
1038 if (m_configuration.iFirmwareBuildDate != CEC_FW_BUILD_UNKNOWN)
1039 {
1040 time_t buildTime = (time_t)m_configuration.iFirmwareBuildDate;
1041 strLog.AppendFormat(", firmware build date: %s", asctime(gmtime(&buildTime)));
1042 strLog = strLog.Left((int)strLog.length() - 1); // strip \n added by asctime
1043 strLog.append(" +0000");
1044 }
1045
1046 // log the addresses that are being used
1047 if (!m_configuration.logicalAddresses.IsEmpty())
1048 {
1049 strLog.append(", logical address(es) = ");
1050 CECDEVICEVEC devices;
1051 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
1052 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
1053 strLog.AppendFormat("%s (%X) ", (*it)->GetLogicalAddressName(), (*it)->GetLogicalAddress());
1054 }
1055
1056 if (!CLibCEC::IsValidPhysicalAddress(m_configuration.iPhysicalAddress))
1057 strLog.AppendFormat(", base device: %s (%X), HDMI port number: %d", ToString(m_configuration.baseDevice), m_configuration.baseDevice, m_configuration.iHDMIPort);
1058 else
1059 strLog.AppendFormat(", physical address: %04x", m_configuration.iPhysicalAddress);
1060
1061 strLog.AppendFormat(", %s", LIB_CEC->GetLibInfo());
1062
1063 std::string strReturn(strLog.c_str());
1064 return strReturn;
1065 }
1066
1067 void CCECClient::SetTVVendorOverride(const cec_vendor_id id)
1068 {
1069 {
1070 CLockObject lock(m_mutex);
1071 m_configuration.tvVendor = id;
1072 }
1073
1074 if (id != CEC_VENDOR_UNKNOWN)
1075 {
1076 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vendor id '%s'", __FUNCTION__, ToString(id));
1077
1078 CCECBusDevice *tv = m_processor ? m_processor->GetTV() : NULL;
1079 if (tv)
1080 tv->SetVendorId((uint64_t)id);
1081 }
1082
1083 // persist the new configuration
1084 PersistConfiguration(m_configuration);
1085 }
1086
1087 cec_vendor_id CCECClient::GetTVVendorOverride(void)
1088 {
1089 CLockObject lock(m_mutex);
1090 return (cec_vendor_id)m_configuration.tvVendor;
1091 }
1092
1093 void CCECClient::SetOSDName(const std::string &strDeviceName)
1094 {
1095 {
1096 CLockObject lock(m_mutex);
1097 snprintf(m_configuration.strDeviceName, 13, "%s", strDeviceName.c_str());
1098 }
1099
1100 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using OSD name '%s'", __FUNCTION__, strDeviceName.c_str());
1101
1102 CCECBusDevice *primary = GetPrimaryDevice();
1103 if (primary && !primary->GetCurrentOSDName().Equals(strDeviceName.c_str()))
1104 {
1105 primary->SetOSDName(strDeviceName);
1106 if (m_processor && m_processor->CECInitialised())
1107 primary->TransmitOSDName(CECDEVICE_TV, false);
1108 }
1109
1110 // persist the new configuration
1111 PersistConfiguration(m_configuration);
1112 }
1113
1114 std::string CCECClient::GetOSDName(void)
1115 {
1116 CLockObject lock(m_mutex);
1117 std::string strOSDName(m_configuration.strDeviceName);
1118 return strOSDName;
1119 }
1120
1121 void CCECClient::SetWakeDevices(const cec_logical_addresses &addresses)
1122 {
1123 {
1124 CLockObject lock(m_mutex);
1125 m_configuration.wakeDevices = addresses;
1126 }
1127 // persist the new configuration
1128 PersistConfiguration(m_configuration);
1129 }
1130
1131 cec_logical_addresses CCECClient::GetWakeDevices(void)
1132 {
1133 CLockObject lock(m_mutex);
1134 return m_configuration.wakeDevices;
1135 }
1136
1137 bool CCECClient::AutodetectPhysicalAddress(void)
1138 {
1139 bool bPhysicalAutodetected(false);
1140 uint16_t iPhysicalAddress = m_processor ? m_processor->GetDetectedPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS;
1141
1142 if (CLibCEC::IsValidPhysicalAddress(iPhysicalAddress))
1143 {
1144 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - autodetected physical address '%04X'", __FUNCTION__, iPhysicalAddress);
1145
1146 CLockObject lock(m_mutex);
1147 m_configuration.iPhysicalAddress = iPhysicalAddress;
1148 m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE;
1149 m_configuration.baseDevice = CECDEVICE_UNKNOWN;
1150 bPhysicalAutodetected = true;
1151 }
1152
1153 SetDevicePhysicalAddress(iPhysicalAddress);
1154
1155 return bPhysicalAutodetected;
1156 }
1157
1158 void CCECClient::SetClientVersion(const cec_client_version version)
1159 {
1160 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using client version '%s'", __FUNCTION__, ToString(version));
1161
1162 CLockObject lock(m_mutex);
1163 m_configuration.clientVersion = (uint32_t)version;
1164 }
1165
1166 cec_client_version CCECClient::GetClientVersion(void)
1167 {
1168 CLockObject lock(m_mutex);
1169 return (cec_client_version)m_configuration.clientVersion;
1170 }
1171
1172 bool CCECClient::SetDeviceTypes(const cec_device_type_list &deviceTypes)
1173 {
1174 bool bNeedReinit(false);
1175
1176 {
1177 CLockObject lock(m_mutex);
1178 bNeedReinit = m_processor && m_processor->CECInitialised() &&
1179 (m_configuration.deviceTypes != deviceTypes);
1180 m_configuration.deviceTypes = deviceTypes;
1181 }
1182
1183 // persist the new configuration
1184 PersistConfiguration(m_configuration);
1185
1186 if (bNeedReinit)
1187 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using primary device type '%s'", __FUNCTION__, ToString(deviceTypes[0]));
1188
1189 return bNeedReinit;
1190 }
1191
1192 cec_device_type_list CCECClient::GetDeviceTypes(void)
1193 {
1194 cec_device_type_list retVal;
1195 CLockObject lock(m_mutex);
1196 retVal = m_configuration.deviceTypes;
1197 return retVal;
1198 }
1199
1200 bool CCECClient::SetDevicePhysicalAddress(const uint16_t iPhysicalAddress)
1201 {
1202 if (!CLibCEC::IsValidPhysicalAddress(iPhysicalAddress))
1203 {
1204 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - not setting invalid physical address %04x", __FUNCTION__, iPhysicalAddress);
1205 return false;
1206 }
1207
1208 // reconfigure all devices
1209 cec_logical_address reactivateSource(CECDEVICE_UNKNOWN);
1210 CECDEVICEVEC devices;
1211 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
1212 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
1213 {
1214 // if this device was the active source, reactivate it afterwards
1215 if ((*it)->IsActiveSource())
1216 reactivateSource = (*it)->GetLogicalAddress();
1217
1218 // mark the device as inactive source
1219 if (IsInitialised())
1220 (*it)->MarkAsInactiveSource();
1221
1222 // set the new physical address
1223 (*it)->SetPhysicalAddress(iPhysicalAddress);
1224
1225 // and transmit it
1226 if (IsInitialised())
1227 (*it)->TransmitPhysicalAddress(false);
1228 }
1229
1230 // reactivate the previous active source
1231 if (reactivateSource != CECDEVICE_UNKNOWN &&
1232 m_processor->CECInitialised() &&
1233 IsInitialised())
1234 {
1235 CCECBusDevice *device = m_processor->GetDevice(reactivateSource);
1236 if (device)
1237 device->ActivateSource();
1238 }
1239
1240 // persist the new configuration
1241 PersistConfiguration(m_configuration);
1242
1243 return true;
1244 }
1245
1246 bool CCECClient::SwitchMonitoring(bool bEnable)
1247 {
1248 LIB_CEC->AddLog(CEC_LOG_NOTICE, "== %s monitoring mode ==", bEnable ? "enabling" : "disabling");
1249
1250 if (m_processor)
1251 {
1252 if (bEnable)
1253 return m_processor->UnregisterClient(this);
1254 else
1255 {
1256 m_configuration.bMonitorOnly = false;
1257 return m_processor->RegisterClient(this);
1258 }
1259 }
1260
1261 return false;
1262 }
1263
1264 bool CCECClient::PollDevice(const cec_logical_address iAddress)
1265 {
1266 // try to find the primary device
1267 CCECBusDevice *primary = GetPrimaryDevice();
1268 // poll the destination, with the primary as source
1269 if (primary)
1270 return primary->TransmitPoll(iAddress, false);
1271
1272 return m_processor ? m_processor->PollDevice(iAddress) : false;
1273 }
1274
1275 cec_logical_addresses CCECClient::GetActiveDevices(void)
1276 {
1277 CECDEVICEVEC activeDevices;
1278 if (m_processor)
1279 m_processor->GetDevices()->GetActive(activeDevices);
1280 return CCECDeviceMap::ToLogicalAddresses(activeDevices);
1281 }
1282
1283 bool CCECClient::IsActiveDevice(const cec_logical_address iAddress)
1284 {
1285 cec_logical_addresses activeDevices = GetActiveDevices();
1286 return activeDevices.IsSet(iAddress);
1287 }
1288
1289 bool CCECClient::IsActiveDeviceType(const cec_device_type type)
1290 {
1291 CECDEVICEVEC activeDevices;
1292 if (m_processor)
1293 m_processor->GetDevices()->GetActive(activeDevices);
1294 CCECDeviceMap::FilterType(type, activeDevices);
1295 return !activeDevices.empty();
1296 }
1297
1298 cec_logical_address CCECClient::GetActiveSource(void)
1299 {
1300 return m_processor ? m_processor->GetActiveSource() : CECDEVICE_UNKNOWN;
1301 }
1302
1303 bool CCECClient::IsActiveSource(const cec_logical_address iAddress)
1304 {
1305 return m_processor ? m_processor->IsActiveSource(iAddress) : false;
1306 }
1307
1308 bool CCECClient::SetStreamPath(const cec_logical_address iAddress)
1309 {
1310 uint16_t iPhysicalAddress = GetDevicePhysicalAddress(iAddress);
1311 if (iPhysicalAddress != CEC_INVALID_PHYSICAL_ADDRESS)
1312 return SetStreamPath(iPhysicalAddress);
1313 return false;
1314 }
1315
1316 bool CCECClient::SetStreamPath(const uint16_t iPhysicalAddress)
1317 {
1318 bool bReturn(false);
1319
1320 CCECBusDevice *device = GetDeviceByType(CEC_DEVICE_TYPE_TV);
1321 if (device)
1322 {
1323 device->SetStreamPath(iPhysicalAddress);
1324 bReturn = device->GetHandler()->TransmitSetStreamPath(iPhysicalAddress, false);
1325 device->MarkHandlerReady();
1326 }
1327 else
1328 {
1329 LIB_CEC->AddLog(CEC_LOG_ERROR, "only the TV is allowed to send CEC_OPCODE_SET_STREAM_PATH");
1330 }
1331
1332 return bReturn;
1333 }
1334
1335 cec_logical_addresses CCECClient::GetLogicalAddresses(void)
1336 {
1337 cec_logical_addresses addresses;
1338 CLockObject lock(m_mutex);
1339 addresses = m_configuration.logicalAddresses;
1340 return addresses;
1341 }
1342
1343 bool CCECClient::CanPersistConfiguration(void)
1344 {
1345 return m_processor ? m_processor->CanPersistConfiguration() : false;
1346 }
1347
1348 bool CCECClient::PersistConfiguration(const libcec_configuration &configuration)
1349 {
1350 return m_processor && IsRegistered() ?
1351 m_processor->PersistConfiguration(configuration) :
1352 false;
1353 }
1354
1355 void CCECClient::RescanActiveDevices(void)
1356 {
1357 if (m_processor)
1358 m_processor->RescanActiveDevices();
1359 }
1360
1361 bool CCECClient::IsLibCECActiveSource(void)
1362 {
1363 bool bReturn(false);
1364 if (m_processor)
1365 {
1366 cec_logical_address activeSource = m_processor->GetActiveSource();
1367 CCECBusDevice *device = m_processor->GetDevice(activeSource);
1368 if (device)
1369 bReturn = device->IsHandledByLibCEC();
1370 }
1371 return bReturn;
1372 }
1373
1374 void CCECClient::SourceActivated(const cec_logical_address logicalAddress)
1375 {
1376 LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> source activated: %s (%x)", ToString(logicalAddress), logicalAddress);
1377 CallbackSourceActivated(true, logicalAddress);
1378 }
1379
1380 void CCECClient::SourceDeactivated(const cec_logical_address logicalAddress)
1381 {
1382 LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> source deactivated: %s (%x)", ToString(logicalAddress), logicalAddress);
1383 CallbackSourceActivated(false, logicalAddress);
1384 }
1385
1386 void CCECClient::CallbackAddCommand(const cec_command &command)
1387 {
1388 {
1389 CLockObject lock(m_cbMutex);
1390 if (m_configuration.callbacks && m_configuration.callbacks->CBCecCommand)
1391 {
1392 m_configuration.callbacks->CBCecCommand(m_configuration.callbackParam, command);
1393 return;
1394 }
1395 }
1396 m_commandBuffer.Push(command);
1397 }
1398
1399 void CCECClient::CallbackAddKey(const cec_keypress &key)
1400 {
1401 {
1402 CLockObject lock(m_cbMutex);
1403 if (m_configuration.callbacks && m_configuration.callbacks->CBCecKeyPress)
1404 {
1405 m_configuration.callbacks->CBCecKeyPress(m_configuration.callbackParam, key);
1406 return;
1407 }
1408 }
1409 m_keyBuffer.Push(key);
1410 }
1411
1412 void CCECClient::CallbackAddLog(const cec_log_message &message)
1413 {
1414 {
1415 CLockObject lock(m_cbMutex);
1416 if (m_configuration.callbacks && m_configuration.callbacks->CBCecLogMessage)
1417 {
1418 m_configuration.callbacks->CBCecLogMessage(m_configuration.callbackParam, message);
1419 return;
1420 }
1421 }
1422 m_logBuffer.Push(message);
1423 }
1424
1425 void CCECClient::CallbackConfigurationChanged(const libcec_configuration &config)
1426 {
1427 CLockObject lock(m_cbMutex);
1428 if (m_configuration.callbacks &&
1429 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0 &&
1430 m_configuration.callbacks->CBCecConfigurationChanged &&
1431 m_processor->CECInitialised())
1432 m_configuration.callbacks->CBCecConfigurationChanged(m_configuration.callbackParam, config);
1433 }
1434
1435 void CCECClient::CallbackSourceActivated(bool bActivated, const cec_logical_address logicalAddress)
1436 {
1437 CLockObject lock(m_cbMutex);
1438 if (m_configuration.callbacks &&
1439 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_7_1 &&
1440 m_configuration.callbacks->CBCecSourceActivated)
1441 m_configuration.callbacks->CBCecSourceActivated(m_configuration.callbackParam, logicalAddress, bActivated ? 1 : 0);
1442 }
1443
1444 void CCECClient::CallbackAlert(const libcec_alert type, const libcec_parameter &param)
1445 {
1446 CLockObject lock(m_cbMutex);
1447 if (m_configuration.callbacks &&
1448 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_0 &&
1449 m_configuration.callbacks->CBCecAlert)
1450 m_configuration.callbacks->CBCecAlert(m_configuration.callbackParam, type, param);
1451 }
1452
1453 int CCECClient::CallbackMenuStateChanged(const cec_menu_state newState)
1454 {
1455 CLockObject lock(m_cbMutex);
1456 if (m_configuration.callbacks &&
1457 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_2 &&
1458 m_configuration.callbacks->CBCecMenuStateChanged)
1459 return m_configuration.callbacks->CBCecMenuStateChanged(m_configuration.callbackParam, newState);
1460 return 0;
1461 }