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