cec: fixed - ensure that the correct libCEC version is set after initialising the...
[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 "CECClient.h"
34 #include "CECProcessor.h"
35 #include "LibCEC.h"
36 #include "devices/CECPlaybackDevice.h"
37 #include "devices/CECAudioSystem.h"
38 #include "devices/CECTV.h"
39
40 using namespace CEC;
41 using namespace PLATFORM;
42
43 #define LIB_CEC m_processor->GetLib()
44 #define ToString(x) LIB_CEC->ToString(x)
45
46 CCECClient::CCECClient(CCECProcessor *processor, const libcec_configuration *configuration) :
47 m_processor(processor),
48 m_bInitialised(false),
49 m_bRegistered(false),
50 m_iCurrentButton(CEC_USER_CONTROL_CODE_UNKNOWN),
51 m_buttontime(0)
52 {
53 SetConfiguration(configuration);
54 }
55
56 CCECClient::~CCECClient(void)
57 {
58 if (m_processor)
59 m_processor->UnregisterClient(this);
60 }
61
62 bool CCECClient::IsInitialised(void)
63 {
64 CLockObject lock(m_mutex);
65 return m_bInitialised && m_processor;
66 }
67
68 void CCECClient::SetInitialised(bool bSetTo)
69 {
70 CLockObject lock(m_mutex);
71 m_bInitialised = bSetTo;
72 }
73
74 bool CCECClient::IsRegistered(void)
75 {
76 CLockObject lock(m_mutex);
77 return m_bRegistered && m_processor;
78 }
79
80 void CCECClient::SetRegistered(bool bSetTo)
81 {
82 CLockObject lock(m_mutex);
83 m_bRegistered = bSetTo;
84 }
85
86 bool CCECClient::Initialise(void)
87 {
88 if (IsInitialised())
89 return true;
90
91 //TODO do the same for the other devices
92 CCECBusDevice *primary = m_processor->GetDevice(m_configuration.logicalAddresses.primary);
93 if (!primary)
94 {
95 LIB_CEC->AddLog(CEC_LOG_WARNING, "cannot find the primary device (logical address %x)", m_configuration.logicalAddresses.primary);
96 return false;
97 }
98
99 /* only set our OSD name for the primary device */
100 primary->SetOSDName(m_configuration.strDeviceName);
101
102 /* set the default menu language for devices we control */
103 primary->SetMenuLanguage(m_configuration.strDeviceLanguage);
104
105 if (CLibCEC::IsValidPhysicalAddress(m_configuration.iPhysicalAddress))
106 {
107 primary->SetPhysicalAddress(m_configuration.iPhysicalAddress);
108 primary->TransmitPhysicalAddress();
109 }
110 else
111 {
112 SetHDMIPort(m_configuration.baseDevice, m_configuration.iHDMIPort, true);
113 }
114
115 /* make the primary device the active source if the option is set */
116 if (m_configuration.bActivateSource == 1)
117 primary->ActivateSource();
118
119 SetInitialised(true);
120 return true;
121 }
122
123 bool CCECClient::SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort, bool bForce /* = false */)
124 {
125 bool bReturn(false);
126
127 // limit the HDMI port range to 1-15
128 if (iPort < CEC_MIN_HDMI_PORTNUMBER ||
129 iPort > CEC_MAX_HDMI_PORTNUMBER)
130 return bReturn;
131
132 {
133 CLockObject lock(m_mutex);
134 m_configuration.baseDevice = iBaseDevice;
135 m_configuration.iHDMIPort = iPort;
136 }
137
138 if (!m_processor->IsRunning() && !bForce)
139 return true;
140
141 LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting HDMI port to %d on device %s (%d)", iPort, ToString(iBaseDevice), (int)iBaseDevice);
142
143 uint16_t iPhysicalAddress(CEC_INVALID_PHYSICAL_ADDRESS);
144 CCECBusDevice *baseDevice = m_processor->GetDevice(iBaseDevice);
145 if (baseDevice)
146 iPhysicalAddress = baseDevice->GetPhysicalAddress(m_configuration.logicalAddresses.primary);
147
148 if (iPhysicalAddress <= CEC_MAX_PHYSICAL_ADDRESS)
149 {
150 if (iPhysicalAddress == 0)
151 iPhysicalAddress += 0x1000 * iPort;
152 else if (iPhysicalAddress % 0x1000 == 0)
153 iPhysicalAddress += 0x100 * iPort;
154 else if (iPhysicalAddress % 0x100 == 0)
155 iPhysicalAddress += 0x10 * iPort;
156 else if (iPhysicalAddress % 0x10 == 0)
157 iPhysicalAddress += iPort;
158
159 bReturn = true;
160 }
161
162 if (!bReturn)
163 {
164 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);
165 iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS;
166 }
167
168 SetPhysicalAddress(iPhysicalAddress);
169
170 return bReturn;
171 }
172
173 bool CCECClient::SetPhysicalAddress(uint16_t iPhysicalAddress)
174 {
175 bool bSendActiveView(false);
176 bool bReturn(false);
177 bool bSendUpdate = m_processor->CECInitialised();
178
179 CECDEVICEVEC sendUpdatesTo;
180 {
181 CLockObject lock(m_mutex);
182 m_configuration.iPhysicalAddress = iPhysicalAddress;
183 LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting physical address to '%04X'", iPhysicalAddress);
184
185 bool bWasActiveSource(false);
186 CECDEVICEVEC devices;
187 // TODO
188 m_processor->GetDevices()->GetLibCECControlled(devices);
189
190 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
191 {
192 bWasActiveSource |= (*it)->IsActiveSource();
193 (*it)->MarkAsInactiveSource();
194 (*it)->SetPhysicalAddress(iPhysicalAddress);
195 if (bSendUpdate)
196 sendUpdatesTo.push_back(*it);
197 }
198
199 bSendActiveView = bWasActiveSource && bSendUpdate;
200 bReturn = true;
201 }
202
203 for (CECDEVICEVEC::iterator it = sendUpdatesTo.begin(); it != sendUpdatesTo.end(); it++)
204 {
205 (*it)->TransmitPhysicalAddress();
206 if (bSendActiveView && m_configuration.logicalAddresses.primary == (*it)->GetLogicalAddress())
207 {
208 (*it)->MarkAsActiveSource();
209 if ((*it)->HasValidPhysicalAddress())
210 (*it)->ActivateSource();
211 }
212 }
213
214 if (bReturn)
215 {
216 m_processor->PersistConfiguration(&m_configuration);
217 ConfigurationChanged(m_configuration);
218 }
219
220 return bReturn;
221 }
222
223 bool CCECClient::FindLogicalAddresses(void)
224 {
225 m_configuration.logicalAddresses.Clear();
226
227 if (m_configuration.deviceTypes.IsEmpty())
228 {
229 LIB_CEC->AddLog(CEC_LOG_ERROR, "no device types given");
230 return false;
231 }
232
233 for (uint8_t iPtr = 0; iPtr < 5; iPtr++)
234 {
235 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
236 continue;
237
238 cec_logical_address address(CECDEVICE_UNKNOWN);
239 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RECORDING_DEVICE)
240 address = FindLogicalAddressRecordingDevice();
241 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_TUNER)
242 address = FindLogicalAddressTuner();
243 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_PLAYBACK_DEVICE)
244 address = FindLogicalAddressPlaybackDevice();
245 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
246 address = FindLogicalAddressAudioSystem();
247
248 if (address == CECDEVICE_UNKNOWN)
249 {
250 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - failed to allocate device '%d', type '%s'", __FUNCTION__, iPtr, ToString(m_configuration.deviceTypes.types[iPtr]));
251 return false;
252 }
253
254 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - device '%d', type '%s', LA '%X'", __FUNCTION__, iPtr, ToString(m_configuration.deviceTypes.types[iPtr]), address);
255 m_configuration.logicalAddresses.Set(address);
256 }
257
258 return true;
259 }
260
261 cec_logical_address CCECClient::FindLogicalAddressRecordingDevice(void)
262 {
263 cec_logical_address retVal(CECDEVICE_UNKNOWN);
264
265 LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'recording device'");
266 if (m_processor->TryLogicalAddress(CECDEVICE_RECORDINGDEVICE1))
267 retVal = CECDEVICE_RECORDINGDEVICE1;
268 else if (m_processor->TryLogicalAddress(CECDEVICE_RECORDINGDEVICE2))
269 retVal = CECDEVICE_RECORDINGDEVICE2;
270 else if (m_processor->TryLogicalAddress(CECDEVICE_RECORDINGDEVICE3))
271 retVal = CECDEVICE_RECORDINGDEVICE3;
272
273 return retVal;
274 }
275
276 cec_logical_address CCECClient::FindLogicalAddressTuner(void)
277 {
278 cec_logical_address retVal(CECDEVICE_UNKNOWN);
279
280 LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'tuner'");
281 if (m_processor->TryLogicalAddress(CECDEVICE_TUNER1))
282 retVal = CECDEVICE_TUNER1;
283 else if (m_processor->TryLogicalAddress(CECDEVICE_TUNER2))
284 retVal = CECDEVICE_TUNER2;
285 else if (m_processor->TryLogicalAddress(CECDEVICE_TUNER3))
286 retVal = CECDEVICE_TUNER3;
287 else if (m_processor->TryLogicalAddress(CECDEVICE_TUNER4))
288 retVal = CECDEVICE_TUNER4;
289
290 return retVal;
291 }
292
293 cec_logical_address CCECClient::FindLogicalAddressPlaybackDevice(void)
294 {
295 cec_logical_address retVal(CECDEVICE_UNKNOWN);
296
297 LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'playback device'");
298 if (m_processor->TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE1))
299 retVal = CECDEVICE_PLAYBACKDEVICE1;
300 else if (m_processor->TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE2))
301 retVal = CECDEVICE_PLAYBACKDEVICE2;
302 else if (m_processor->TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE3))
303 retVal = CECDEVICE_PLAYBACKDEVICE3;
304
305 return retVal;
306 }
307
308 cec_logical_address CCECClient::FindLogicalAddressAudioSystem(void)
309 {
310 cec_logical_address retVal(CECDEVICE_UNKNOWN);
311
312 LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'audiosystem'");
313 if (m_processor->TryLogicalAddress(CECDEVICE_AUDIOSYSTEM))
314 retVal = CECDEVICE_AUDIOSYSTEM;
315
316 return retVal;
317 }
318
319 CCECBusDevice *CCECClient::GetDeviceByType(const cec_device_type type) const
320 {
321 // get all devices that match our logical addresses
322 CECDEVICEVEC devices;
323 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
324
325 // filter the type we need
326 CCECDeviceMap::FilterType(type, devices);
327
328 return devices.empty() ?
329 NULL :
330 *devices.begin();
331 }
332
333 bool CCECClient::ChangeDeviceType(cec_device_type from, cec_device_type to)
334 {
335 bool bChanged(false);
336
337 LIB_CEC->AddLog(CEC_LOG_NOTICE, "changing device type '%s' into '%s'", ToString(from), ToString(to));
338
339 CLockObject lock(m_mutex);
340
341 CCECBusDevice *previousDevice = GetDeviceByType(from);
342 if (!previousDevice)
343 return false;
344
345 m_processor->UnregisterClient(this);
346
347 m_configuration.logicalAddresses.primary = CECDEVICE_UNREGISTERED;
348
349 for (uint8_t iPtr = 0; iPtr < 5; iPtr++)
350 {
351 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
352 continue;
353
354 if (m_configuration.deviceTypes.types[iPtr] == from)
355 {
356 bChanged = true;
357 m_configuration.deviceTypes.types[iPtr] = to;
358 }
359 else if (m_configuration.deviceTypes.types[iPtr] == to && bChanged)
360 {
361 m_configuration.deviceTypes.types[iPtr] = CEC_DEVICE_TYPE_RESERVED;
362 }
363 }
364
365 if (bChanged)
366 {
367 // re-register the client to set the new ackmask
368 if (!m_processor->RegisterClient(this))
369 return false;
370
371 // copy the data from the previous device
372 CCECBusDevice *newDevice = GetDeviceByType(to);
373 if (previousDevice && newDevice)
374 {
375 newDevice->SetDeviceStatus(CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC);
376 newDevice->SetCecVersion(previousDevice->GetCecVersion(m_configuration.logicalAddresses.primary, false));
377 newDevice->SetMenuLanguage(previousDevice->GetMenuLanguage(m_configuration.logicalAddresses.primary, false));
378 newDevice->SetMenuState(previousDevice->GetMenuState(m_configuration.logicalAddresses.primary));
379 newDevice->SetOSDName(previousDevice->GetOSDName(m_configuration.logicalAddresses.primary, false));
380 newDevice->SetPhysicalAddress(previousDevice->GetCurrentPhysicalAddress());
381 newDevice->SetPowerStatus(previousDevice->GetPowerStatus(m_configuration.logicalAddresses.primary, false));
382 newDevice->SetVendorId(previousDevice->GetVendorId(m_configuration.logicalAddresses.primary, false));
383
384 if ((from == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || from == CEC_DEVICE_TYPE_RECORDING_DEVICE) &&
385 (to == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || to == CEC_DEVICE_TYPE_RECORDING_DEVICE))
386 {
387 newDevice->AsPlaybackDevice()->SetDeckControlMode(previousDevice->AsPlaybackDevice()->GetDeckControlMode(m_configuration.logicalAddresses.primary));
388 newDevice->AsPlaybackDevice()->SetDeckStatus(previousDevice->AsPlaybackDevice()->GetDeckStatus(m_configuration.logicalAddresses.primary));
389 }
390 }
391
392 // and reset the previous device to the initial state
393 if (previousDevice)
394 previousDevice->ResetDeviceStatus();
395 }
396
397 return true;
398 }
399
400 bool CCECClient::SetLogicalAddress(cec_logical_address iLogicalAddress)
401 {
402 CLockObject lock(m_mutex);
403 if (m_configuration.logicalAddresses.primary != iLogicalAddress)
404 {
405 LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< setting primary logical address to %1x", iLogicalAddress);
406 m_configuration.logicalAddresses.primary = iLogicalAddress;
407 m_configuration.logicalAddresses.Set(iLogicalAddress);
408 return m_processor->RegisterClient(this);
409 }
410
411 return true;
412 }
413
414 bool CCECClient::Transmit(const cec_command &data)
415 {
416 return m_processor ? m_processor->Transmit(data) : false;
417 }
418
419 bool CCECClient::SendPowerOnDevices(cec_logical_address address /* = CECDEVICE_TV */)
420 {
421 if (address == CECDEVICE_BROADCAST && m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0)
422 {
423 CECDEVICEVEC devices;
424 m_processor->GetDevices()->GetWakeDevices(m_configuration, devices);
425 return m_processor->PowerOnDevices(m_configuration.logicalAddresses.primary, devices);
426 }
427
428 return m_processor->PowerOnDevice(m_configuration.logicalAddresses.primary, address);
429 }
430
431 bool CCECClient::SendStandbyDevices(cec_logical_address address /* = CECDEVICE_BROADCAST */)
432 {
433 if (address == CECDEVICE_BROADCAST && m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0)
434 {
435 CECDEVICEVEC devices;
436 m_processor->GetDevices()->GetPowerOffDevices(m_configuration, devices);
437 return m_processor->StandbyDevices(m_configuration.logicalAddresses.primary, devices);
438 }
439
440 return m_processor->StandbyDevice(m_configuration.logicalAddresses.primary, address);
441 }
442
443 bool CCECClient::SendSetActiveSource(cec_device_type type /* = CEC_DEVICE_TYPE_RESERVED */)
444 {
445 bool bReturn(false);
446
447 CCECBusDevice *device(NULL);
448 CECDEVICEVEC devices;
449 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
450
451 if (type != CEC_DEVICE_TYPE_RESERVED)
452 CCECDeviceMap::FilterType(type, devices);
453
454 // no devices left
455 if (devices.empty())
456 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
457
458 if (!devices.empty())
459 device = *devices.begin();
460
461 if (device)
462 {
463 bReturn = true;
464 if (m_processor->IsRunning() && device->HasValidPhysicalAddress())
465 bReturn = device->ActivateSource();
466 }
467
468 return bReturn;
469 }
470
471 CCECPlaybackDevice *CCECClient::GetPlaybackDevice(void)
472 {
473 CCECPlaybackDevice *device(NULL);
474 CECDEVICEVEC devices;
475 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
476 CCECDeviceMap::FilterType(CEC_DEVICE_TYPE_PLAYBACK_DEVICE, devices);
477
478 // no devices left
479 if (devices.empty())
480 {
481 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
482 CCECDeviceMap::FilterType(CEC_DEVICE_TYPE_RECORDING_DEVICE, devices);
483 }
484
485 if (!devices.empty())
486 device = (*devices.begin())->AsPlaybackDevice();
487
488 return device;
489 }
490
491 CCECBusDevice *CCECClient::GetPrimaryDevice(void)
492 {
493 return m_processor->GetDevice(m_configuration.logicalAddresses.primary);
494 }
495
496 bool CCECClient::SendSetDeckControlMode(cec_deck_control_mode mode, bool bSendUpdate /* = true */)
497 {
498 bool bReturn(false);
499
500 CCECBusDevice *device = GetPlaybackDevice();
501 if (device)
502 {
503 device->AsPlaybackDevice()->SetDeckControlMode(mode);
504 if (bSendUpdate)
505 bReturn = device->AsPlaybackDevice()->TransmitDeckStatus(CECDEVICE_TV);
506 else
507 bReturn = true;
508 }
509
510 return false;
511 }
512
513 bool CCECClient::SendSetDeckInfo(cec_deck_info info, bool bSendUpdate /* = true */)
514 {
515 bool bReturn(false);
516
517 CCECBusDevice *device = GetPlaybackDevice();
518 if (device)
519 {
520 device->AsPlaybackDevice()->SetDeckStatus(info);
521 if (bSendUpdate)
522 bReturn = device->AsPlaybackDevice()->TransmitDeckStatus(CECDEVICE_TV);
523 else
524 bReturn = true;
525 }
526
527 return false;
528 }
529
530 bool CCECClient::SendSetMenuState(cec_menu_state state, bool bSendUpdate /* = true */)
531 {
532 CECDEVICEVEC devices;
533 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
534
535 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
536 {
537 (*it)->SetMenuState(state);
538 if (bSendUpdate)
539 (*it)->TransmitMenuState(CECDEVICE_TV);
540 }
541
542 return true;
543 }
544
545 bool CCECClient::SendSetInactiveView(void)
546 {
547 CCECBusDevice *primary = GetPrimaryDevice();
548 if (primary)
549 {
550 primary->MarkAsInactiveSource();
551 return primary->TransmitInactiveSource();
552 }
553 return false;
554 }
555
556 bool CCECClient::SendSetOSDString(cec_logical_address iLogicalAddress, cec_display_control duration, const char *strMessage)
557 {
558 CCECBusDevice *primary = GetPrimaryDevice();
559 if (primary)
560 return primary->TransmitOSDString(iLogicalAddress, duration, strMessage);
561
562 return false;
563 }
564
565 cec_version CCECClient::GetDeviceCecVersion(cec_logical_address iAddress)
566 {
567 CCECBusDevice *device = m_processor->GetDevice(iAddress);
568 if (device)
569 return device->GetCecVersion(m_configuration.logicalAddresses.primary);
570 return CEC_VERSION_UNKNOWN;
571 }
572
573 bool CCECClient::GetDeviceMenuLanguage(cec_logical_address iAddress, cec_menu_language *language)
574 {
575 CCECBusDevice *device = m_processor->GetDevice(iAddress);
576 if (device)
577 {
578 *language = device->GetMenuLanguage(m_configuration.logicalAddresses.primary);
579 return (strcmp(language->language, "???") != 0);
580 }
581 return false;
582 }
583
584 cec_osd_name CCECClient::GetDeviceOSDName(cec_logical_address iAddress)
585 {
586 cec_osd_name retVal;
587 retVal.device = iAddress;
588 retVal.name[0] = 0;
589
590 CCECBusDevice *device = m_processor->GetDevice(iAddress);
591 if (device)
592 {
593 CStdString strOSDName = device->GetOSDName(m_configuration.logicalAddresses.primary);
594 snprintf(retVal.name, sizeof(retVal.name), "%s", strOSDName.c_str());
595 retVal.device = iAddress;
596 }
597
598 return retVal;
599 }
600
601 uint16_t CCECClient::GetDevicePhysicalAddress(cec_logical_address iAddress)
602 {
603 CCECBusDevice *device = m_processor->GetDevice(iAddress);
604 if (device)
605 return device->GetPhysicalAddress(m_configuration.logicalAddresses.primary);
606 return CEC_INVALID_PHYSICAL_ADDRESS;
607 }
608
609 cec_power_status CCECClient::GetDevicePowerStatus(cec_logical_address iAddress)
610 {
611 CCECBusDevice *device = m_processor->GetDevice(iAddress);
612 if (device)
613 return device->GetPowerStatus(m_configuration.logicalAddresses.primary);
614 return CEC_POWER_STATUS_UNKNOWN;
615 }
616
617 uint64_t CCECClient::GetDeviceVendorId(cec_logical_address iAddress)
618 {
619 CCECBusDevice *device = m_processor->GetDevice(iAddress);
620 if (device)
621 return device->GetVendorId(m_configuration.logicalAddresses.primary);
622 return CEC_VENDOR_UNKNOWN;
623 }
624
625 uint8_t CCECClient::SendVolumeUp(bool bSendRelease /* = true */)
626 {
627 CCECBusDevice *device = GetPrimaryDevice();
628 CCECAudioSystem *audio = m_processor->GetAudioSystem();
629
630 return device && audio && audio->IsPresent() ?
631 audio->VolumeUp(device->GetLogicalAddress(), bSendRelease) :
632 (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
633 }
634
635 uint8_t CCECClient::SendVolumeDown(bool bSendRelease /* = true */)
636 {
637 CCECBusDevice *device = GetPrimaryDevice();
638 CCECAudioSystem *audio = m_processor->GetAudioSystem();
639
640 return device && audio && audio->IsPresent() ?
641 audio->VolumeDown(device->GetLogicalAddress(), bSendRelease) :
642 (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
643 }
644
645 uint8_t CCECClient::SendMuteAudio(void)
646 {
647 CCECBusDevice *device = GetPrimaryDevice();
648 CCECAudioSystem *audio = m_processor->GetAudioSystem();
649
650 return device && audio && audio->IsPresent() ?
651 audio->MuteAudio(device->GetLogicalAddress()) :
652 (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
653 }
654
655 bool CCECClient::SendKeypress(cec_logical_address iDestination, cec_user_control_code key, bool bWait /* = true */)
656 {
657 CCECBusDevice *device = GetPrimaryDevice();
658 CCECBusDevice *dest = m_processor->GetDevice(iDestination);
659
660 return device && dest ?
661 device->TransmitKeypress(m_configuration.logicalAddresses.primary, key, bWait) :
662 false;
663 }
664
665 bool CCECClient::SendKeyRelease(cec_logical_address iDestination, bool bWait /* = true */)
666 {
667 CCECBusDevice *device = GetPrimaryDevice();
668 CCECBusDevice *dest = m_processor->GetDevice(iDestination);
669
670 return device && dest ?
671 device->TransmitKeyRelease(m_configuration.logicalAddresses.primary, bWait) :
672 false;
673 }
674
675 bool CCECClient::GetCurrentConfiguration(libcec_configuration *configuration)
676 {
677 // client version 1.5.0
678 snprintf(configuration->strDeviceName, 13, "%s", m_configuration.strDeviceName);
679 configuration->deviceTypes = m_configuration.deviceTypes;
680 configuration->bAutodetectAddress = m_configuration.bAutodetectAddress;
681 configuration->iPhysicalAddress = m_configuration.iPhysicalAddress;
682 configuration->baseDevice = m_configuration.baseDevice;
683 configuration->iHDMIPort = m_configuration.iHDMIPort;
684 configuration->clientVersion = m_configuration.clientVersion;
685 configuration->serverVersion = m_configuration.serverVersion;
686 configuration->tvVendor = m_configuration.tvVendor;
687
688 configuration->bGetSettingsFromROM = m_configuration.bGetSettingsFromROM;
689 configuration->bUseTVMenuLanguage = m_configuration.bUseTVMenuLanguage;
690 configuration->bActivateSource = m_configuration.bActivateSource;
691 configuration->wakeDevices = m_configuration.wakeDevices;
692 configuration->powerOffDevices = m_configuration.powerOffDevices;
693 configuration->bPowerOffScreensaver = m_configuration.bPowerOffScreensaver;
694 configuration->bPowerOffOnStandby = m_configuration.bPowerOffOnStandby;
695
696 // client version 1.5.1
697 if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_5_1)
698 configuration->bSendInactiveSource = m_configuration.bSendInactiveSource;
699
700 // client version 1.5.3
701 if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_5_3)
702 configuration->logicalAddresses = m_configuration.logicalAddresses;
703
704 // client version 1.6.0
705 if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_0)
706 {
707 configuration->iFirmwareVersion = m_configuration.iFirmwareVersion;
708 configuration->bPowerOffDevicesOnStandby = m_configuration.bPowerOffDevicesOnStandby;
709 configuration->bShutdownOnStandby = m_configuration.bShutdownOnStandby;
710 }
711
712 // client version 1.6.2
713 if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_2)
714 {
715 memcpy(configuration->strDeviceLanguage, m_configuration.strDeviceLanguage, 3);
716 configuration->iFirmwareBuildDate = m_configuration.iFirmwareBuildDate;
717 }
718 return true;
719 }
720
721 bool CCECClient::SetConfiguration(const libcec_configuration *configuration)
722 {
723 bool bReinit(false);
724 bool bIsRunning(m_processor && m_processor->IsRunning());
725
726 if (configuration->callbacks)
727 {
728 m_configuration.callbacks = configuration->callbacks;
729 m_configuration.callbackParam = configuration->callbackParam;
730 }
731
732 //TODO
733 CCECBusDevice *primary = bIsRunning ? GetPrimaryDevice() : NULL;
734 cec_device_type oldPrimaryType = primary ? primary->GetType() : CEC_DEVICE_TYPE_RECORDING_DEVICE;
735
736 m_configuration.clientVersion = configuration->clientVersion;
737 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using client version '%s'", __FUNCTION__, ToString((cec_client_version)configuration->clientVersion));
738
739 // client version 1.5.0
740
741 // device types
742 bool bDeviceTypeChanged = bIsRunning && m_configuration.deviceTypes != configuration->deviceTypes;
743 m_configuration.deviceTypes = configuration->deviceTypes;
744 if (bDeviceTypeChanged)
745 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using primary device type '%s'", __FUNCTION__, ToString(configuration->deviceTypes[0]));
746
747 bool bPhysicalAddressChanged(false);
748
749 // autodetect address
750 bool bPhysicalAutodetected(false);
751 if (bIsRunning && configuration->bAutodetectAddress == 1)
752 {
753 uint16_t iPhysicalAddress = m_processor->GetDetectedPhysicalAddress();
754 if (CLibCEC::IsValidPhysicalAddress(iPhysicalAddress))
755 {
756 if (bIsRunning)
757 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - autodetected physical address '%04X'", __FUNCTION__, iPhysicalAddress);
758 else
759 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using physical address '%04X'", __FUNCTION__, iPhysicalAddress);
760 bPhysicalAddressChanged = (m_configuration.iPhysicalAddress != iPhysicalAddress);
761 m_configuration.iPhysicalAddress = iPhysicalAddress;
762 m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE;
763 m_configuration.baseDevice = CECDEVICE_UNKNOWN;
764 bPhysicalAutodetected = true;
765 }
766 }
767
768 // physical address
769 if (!bPhysicalAutodetected)
770 {
771 uint16_t iPhysicalAddress(CLibCEC::IsValidPhysicalAddress(configuration->iPhysicalAddress) ? configuration->iPhysicalAddress : CEC_PHYSICAL_ADDRESS_TV);
772 bPhysicalAddressChanged = bIsRunning && m_configuration.iPhysicalAddress != iPhysicalAddress;
773 if (bPhysicalAddressChanged)
774 {
775 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - physical address '%04X'", __FUNCTION__, iPhysicalAddress);
776 m_configuration.iPhysicalAddress = iPhysicalAddress;
777 }
778 }
779
780 bool bHdmiPortChanged(false);
781 if (!bPhysicalAutodetected && !CLibCEC::IsValidPhysicalAddress(configuration->iPhysicalAddress))
782 {
783 // base device
784 bHdmiPortChanged = bIsRunning && m_configuration.baseDevice != configuration->baseDevice;
785 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using base device '%x'", __FUNCTION__, (int)configuration->baseDevice);
786 m_configuration.baseDevice = configuration->baseDevice;
787
788 // hdmi port
789 bHdmiPortChanged |= bIsRunning && m_configuration.iHDMIPort != configuration->iHDMIPort;
790 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using HDMI port '%d'", __FUNCTION__, configuration->iHDMIPort);
791 m_configuration.iHDMIPort = configuration->iHDMIPort;
792 }
793 else
794 {
795 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - resetting HDMI port and base device to defaults", __FUNCTION__);
796 m_configuration.baseDevice = CECDEVICE_UNKNOWN;
797 m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE;
798 }
799
800 bReinit = bPhysicalAddressChanged || bHdmiPortChanged || bDeviceTypeChanged;
801
802 // device name
803 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using OSD name '%s'", __FUNCTION__, configuration->strDeviceName);
804 snprintf(m_configuration.strDeviceName, 13, "%s", configuration->strDeviceName);
805 if (primary && !primary->GetOSDName(m_configuration.logicalAddresses.primary, false).Equals(m_configuration.strDeviceName))
806 {
807 primary->SetOSDName(m_configuration.strDeviceName);
808 if (!bReinit && bIsRunning)
809 primary->TransmitOSDName(CECDEVICE_TV);
810 }
811
812 // tv vendor id override
813 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vendor id '%s'", __FUNCTION__, ToString((cec_vendor_id)configuration->tvVendor));
814 if (m_processor && m_configuration.tvVendor != configuration->tvVendor)
815 {
816 m_configuration.tvVendor= configuration->tvVendor;
817 m_processor->GetTV()->SetVendorId((uint64_t)m_configuration.tvVendor);
818 }
819
820 // wake CEC devices
821 if (m_configuration.wakeDevices != configuration->wakeDevices)
822 {
823 m_configuration.wakeDevices = configuration->wakeDevices;
824 if (!bReinit && bIsRunning)
825 SendPowerOnDevices();
826 }
827
828 // just copy these
829 m_configuration.bUseTVMenuLanguage = configuration->bUseTVMenuLanguage;
830 m_configuration.bActivateSource = configuration->bActivateSource;
831 m_configuration.bGetSettingsFromROM = configuration->bGetSettingsFromROM;
832 m_configuration.powerOffDevices = configuration->powerOffDevices;
833 m_configuration.bPowerOffScreensaver = configuration->bPowerOffScreensaver;
834 m_configuration.bPowerOffOnStandby = configuration->bPowerOffOnStandby;
835
836 // client version 1.5.1
837 if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_5_1)
838 m_configuration.bSendInactiveSource = configuration->bSendInactiveSource;
839
840 // client version 1.6.0
841 if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_0)
842 {
843 m_configuration.bPowerOffDevicesOnStandby = configuration->bPowerOffDevicesOnStandby;
844 m_configuration.bShutdownOnStandby = configuration->bShutdownOnStandby;
845 }
846
847 // client version 1.6.2
848 if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_2)
849 {
850 memcpy(m_configuration.strDeviceLanguage, configuration->strDeviceLanguage, 3);
851 }
852
853 // ensure that there is at least 1 device type set
854 if (m_configuration.deviceTypes.IsEmpty())
855 m_configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
856
857 if (bIsRunning)
858 m_processor->GetTV()->ReplaceHandler(false);
859
860 bool bReturn(true);
861 if (bReinit || m_configuration.logicalAddresses.IsEmpty())
862 {
863 if (bDeviceTypeChanged)
864 bReturn = ChangeDeviceType(oldPrimaryType, m_configuration.deviceTypes[0]);
865 else if (CLibCEC::IsValidPhysicalAddress(m_configuration.iPhysicalAddress))
866 bReturn = SetPhysicalAddress(m_configuration.iPhysicalAddress);
867 else if (m_configuration.baseDevice != CECDEVICE_UNKNOWN && m_configuration.iHDMIPort != CEC_HDMI_PORTNUMBER_NONE)
868 bReturn = SetHDMIPort(m_configuration.baseDevice, m_configuration.iHDMIPort);
869 }
870 else if (m_configuration.bActivateSource == 1 && bIsRunning && !m_processor->IsActiveSource(m_configuration.logicalAddresses.primary))
871 {
872 // activate the source if we're not already the active source
873 m_processor->SetActiveSource(m_configuration.deviceTypes.types[0]);
874 }
875
876 // persist the configuration
877 if (bIsRunning)
878 m_processor->PersistConfiguration(&m_configuration);
879
880 return bReturn;
881 }
882
883 void CCECClient::AddCommand(const cec_command &command)
884 {
885 CLockObject lock(m_mutex);
886
887 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);
888
889 if (m_configuration.callbacks && m_configuration.callbacks->CBCecCommand)
890 m_configuration.callbacks->CBCecCommand(m_configuration.callbackParam, command);
891 else if (!m_commandBuffer.Push(command))
892 LIB_CEC->AddLog(CEC_LOG_WARNING, "command buffer is full");
893 }
894
895 int CCECClient::MenuStateChanged(const cec_menu_state newState)
896 {
897 CLockObject lock(m_mutex);
898
899 LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> %s: %s", ToString(CEC_OPCODE_MENU_REQUEST), ToString(newState));
900
901 if (m_configuration.callbacks &&
902 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_2 &&
903 m_configuration.callbacks->CBCecMenuStateChanged)
904 return m_configuration.callbacks->CBCecMenuStateChanged(m_configuration.callbackParam, newState);
905
906 return 0;
907 }
908
909 void CCECClient::Alert(const libcec_alert type, const libcec_parameter &param)
910 {
911 CLockObject lock(m_mutex);
912
913 if (m_configuration.callbacks &&
914 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_0 &&
915 m_configuration.callbacks->CBCecAlert)
916 m_configuration.callbacks->CBCecAlert(m_configuration.callbackParam, type, param);
917 }
918
919 void CCECClient::AddLog(const cec_log_message &message)
920 {
921 CLockObject lock(m_logMutex);
922 if (m_configuration.callbacks && m_configuration.callbacks->CBCecLogMessage)
923 m_configuration.callbacks->CBCecLogMessage(m_configuration.callbackParam, message);
924 else
925 m_logBuffer.Push(message);
926 }
927
928 void CCECClient::AddKey(void)
929 {
930 CLockObject lock(m_mutex);
931
932 if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN)
933 {
934 cec_keypress key;
935
936 key.duration = (unsigned int) (GetTimeMs() - m_buttontime);
937 key.keycode = m_iCurrentButton;
938 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key released: %1x", key.keycode);
939
940 if (m_configuration.callbacks && m_configuration.callbacks->CBCecKeyPress)
941 m_configuration.callbacks->CBCecKeyPress(m_configuration.callbackParam, key);
942 else
943 m_keyBuffer.Push(key);
944 m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
945 }
946
947 m_buttontime = 0;
948 }
949
950 void CCECClient::AddKey(const cec_keypress &key)
951 {
952 CLockObject lock(m_mutex);
953
954 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key pressed: %1x", key.keycode);
955
956 if (m_configuration.callbacks && m_configuration.callbacks->CBCecKeyPress)
957 m_configuration.callbacks->CBCecKeyPress(m_configuration.callbackParam, key);
958 else
959 m_keyBuffer.Push(key);
960
961 m_iCurrentButton = key.duration > 0 ? CEC_USER_CONTROL_CODE_UNKNOWN : key.keycode;
962 m_buttontime = key.duration > 0 ? 0 : GetTimeMs();
963 }
964
965 void CCECClient::SetCurrentButton(cec_user_control_code iButtonCode)
966 {
967 /* push keypress to the keybuffer with 0 duration.
968 push another press to the keybuffer with the duration set when the button is released */
969 cec_keypress key;
970 key.duration = 0;
971 key.keycode = iButtonCode;
972
973 AddKey(key);
974 }
975
976 void CCECClient::CheckKeypressTimeout(void)
977 {
978 if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN && GetTimeMs() - m_buttontime > CEC_BUTTON_TIMEOUT)
979 {
980 AddKey();
981 m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
982 }
983 }
984
985 void CCECClient::ConfigurationChanged(const libcec_configuration &config)
986 {
987 CLockObject lock(m_mutex);
988
989 if (m_configuration.callbacks &&
990 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0 &&
991 m_configuration.callbacks->CBCecConfigurationChanged &&
992 m_processor->CECInitialised())
993 m_configuration.callbacks->CBCecConfigurationChanged(m_configuration.callbackParam, config);
994 }
995
996 bool CCECClient::EnableCallbacks(void *cbParam, ICECCallbacks *callbacks)
997 {
998 CLockObject lock(m_mutex);
999 m_configuration.callbackParam = cbParam;
1000 m_configuration.callbacks = callbacks;
1001 return true;
1002 }
1003
1004 bool CCECClient::GetNextLogMessage(cec_log_message *message)
1005 {
1006 return (m_logBuffer.Pop(*message));
1007 }
1008
1009 bool CCECClient::GetNextKeypress(cec_keypress *key)
1010 {
1011 return m_keyBuffer.Pop(*key);
1012 }
1013
1014 bool CCECClient::GetNextCommand(cec_command *command)
1015 {
1016 return m_commandBuffer.Pop(*command);
1017 }