cec: send a feature abort again for all unhandled commands, removed statics, refactor...
[deb_libcec.git] / src / lib / CECClient.cpp
CommitLineData
004b8382
LOK
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
40using namespace CEC;
41using namespace PLATFORM;
42
43#define LIB_CEC m_processor->GetLib()
44#define ToString(x) LIB_CEC->ToString(x)
45
46CCECClient::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
56CCECClient::~CCECClient(void)
57{
58 if (m_processor)
59 m_processor->UnregisterClient(this);
60}
61
62bool CCECClient::IsInitialised(void)
63{
64 CLockObject lock(m_mutex);
65 return m_bInitialised && m_processor;
66}
67
68void CCECClient::SetInitialised(bool bSetTo)
69{
70 CLockObject lock(m_mutex);
71 m_bInitialised = bSetTo;
72}
73
74bool CCECClient::IsRegistered(void)
75{
76 CLockObject lock(m_mutex);
77 return m_bRegistered && m_processor;
78}
79
80void CCECClient::SetRegistered(bool bSetTo)
81{
82 CLockObject lock(m_mutex);
83 m_bRegistered = bSetTo;
84}
85
86bool 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
123bool 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
173bool 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
223bool 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
261cec_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
276cec_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
293cec_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
308cec_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
319CCECBusDevice *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
333bool 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
400bool 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
414bool CCECClient::Transmit(const cec_command &data)
415{
416 return m_processor ? m_processor->Transmit(data) : false;
417}
418
419bool 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
431bool 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
443bool 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
471CCECPlaybackDevice *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
491CCECBusDevice *CCECClient::GetPrimaryDevice(void)
492{
493 return m_processor->GetDevice(m_configuration.logicalAddresses.primary);
494}
495
496bool 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
513bool 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
530bool 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
545bool 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
556bool 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
565cec_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
573bool 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
584cec_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
601uint16_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
609cec_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
617uint64_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
625uint8_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
635uint8_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
645uint8_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
655bool 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
665bool 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
675bool 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
721bool 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.serverVersion = LIBCEC_VERSION_CURRENT;
737 m_configuration.clientVersion = configuration->clientVersion;
738 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using client version '%s'", __FUNCTION__, ToString((cec_client_version)configuration->clientVersion));
739
740 // client version 1.5.0
741
742 // device types
743 bool bDeviceTypeChanged = bIsRunning && m_configuration.deviceTypes != configuration->deviceTypes;
744 m_configuration.deviceTypes = configuration->deviceTypes;
745 if (bDeviceTypeChanged)
746 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using primary device type '%s'", __FUNCTION__, ToString(configuration->deviceTypes[0]));
747
748 bool bPhysicalAddressChanged(false);
749
750 // autodetect address
751 bool bPhysicalAutodetected(false);
752 if (bIsRunning && configuration->bAutodetectAddress == 1)
753 {
754 uint16_t iPhysicalAddress = m_processor->GetDetectedPhysicalAddress();
755 if (CLibCEC::IsValidPhysicalAddress(iPhysicalAddress))
756 {
757 if (bIsRunning)
758 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - autodetected physical address '%04X'", __FUNCTION__, iPhysicalAddress);
759 else
760 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using physical address '%04X'", __FUNCTION__, iPhysicalAddress);
761 bPhysicalAddressChanged = (m_configuration.iPhysicalAddress != iPhysicalAddress);
762 m_configuration.iPhysicalAddress = iPhysicalAddress;
763 m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE;
764 m_configuration.baseDevice = CECDEVICE_UNKNOWN;
765 bPhysicalAutodetected = true;
766 }
767 }
768
769 // physical address
770 if (!bPhysicalAutodetected)
771 {
772 uint16_t iPhysicalAddress(CLibCEC::IsValidPhysicalAddress(configuration->iPhysicalAddress) ? configuration->iPhysicalAddress : CEC_PHYSICAL_ADDRESS_TV);
773 bPhysicalAddressChanged = bIsRunning && m_configuration.iPhysicalAddress != iPhysicalAddress;
774 if (bPhysicalAddressChanged)
775 {
776 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - physical address '%04X'", __FUNCTION__, iPhysicalAddress);
777 m_configuration.iPhysicalAddress = iPhysicalAddress;
778 }
779 }
780
781 bool bHdmiPortChanged(false);
782 if (!bPhysicalAutodetected && !CLibCEC::IsValidPhysicalAddress(configuration->iPhysicalAddress))
783 {
784 // base device
785 bHdmiPortChanged = bIsRunning && m_configuration.baseDevice != configuration->baseDevice;
786 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using base device '%x'", __FUNCTION__, (int)configuration->baseDevice);
787 m_configuration.baseDevice = configuration->baseDevice;
788
789 // hdmi port
790 bHdmiPortChanged |= bIsRunning && m_configuration.iHDMIPort != configuration->iHDMIPort;
791 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using HDMI port '%d'", __FUNCTION__, configuration->iHDMIPort);
792 m_configuration.iHDMIPort = configuration->iHDMIPort;
793 }
794 else
795 {
796 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - resetting HDMI port and base device to defaults", __FUNCTION__);
797 m_configuration.baseDevice = CECDEVICE_UNKNOWN;
798 m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE;
799 }
800
801 bReinit = bPhysicalAddressChanged || bHdmiPortChanged || bDeviceTypeChanged;
802
803 // device name
804 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using OSD name '%s'", __FUNCTION__, configuration->strDeviceName);
805 snprintf(m_configuration.strDeviceName, 13, "%s", configuration->strDeviceName);
806 if (primary && !primary->GetOSDName(m_configuration.logicalAddresses.primary, false).Equals(m_configuration.strDeviceName))
807 {
808 primary->SetOSDName(m_configuration.strDeviceName);
809 if (!bReinit && bIsRunning)
810 primary->TransmitOSDName(CECDEVICE_TV);
811 }
812
813 // tv vendor id override
814 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vendor id '%s'", __FUNCTION__, ToString((cec_vendor_id)configuration->tvVendor));
815 if (m_processor && m_configuration.tvVendor != configuration->tvVendor)
816 {
817 m_configuration.tvVendor= configuration->tvVendor;
818 m_processor->GetTV()->SetVendorId((uint64_t)m_configuration.tvVendor);
819 }
820
821 // wake CEC devices
822 if (m_configuration.wakeDevices != configuration->wakeDevices)
823 {
824 m_configuration.wakeDevices = configuration->wakeDevices;
825 if (!bReinit && bIsRunning)
826 SendPowerOnDevices();
827 }
828
829 // just copy these
830 m_configuration.bUseTVMenuLanguage = configuration->bUseTVMenuLanguage;
831 m_configuration.bActivateSource = configuration->bActivateSource;
832 m_configuration.bGetSettingsFromROM = configuration->bGetSettingsFromROM;
833 m_configuration.powerOffDevices = configuration->powerOffDevices;
834 m_configuration.bPowerOffScreensaver = configuration->bPowerOffScreensaver;
835 m_configuration.bPowerOffOnStandby = configuration->bPowerOffOnStandby;
836
837 // client version 1.5.1
838 if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_5_1)
839 m_configuration.bSendInactiveSource = configuration->bSendInactiveSource;
840
841 // client version 1.6.0
842 if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_0)
843 {
844 m_configuration.bPowerOffDevicesOnStandby = configuration->bPowerOffDevicesOnStandby;
845 m_configuration.bShutdownOnStandby = configuration->bShutdownOnStandby;
846 }
847
848 // client version 1.6.2
849 if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_2)
850 {
851 memcpy(m_configuration.strDeviceLanguage, configuration->strDeviceLanguage, 3);
852 }
853
854 // ensure that there is at least 1 device type set
855 if (m_configuration.deviceTypes.IsEmpty())
856 m_configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
857
858 if (bIsRunning)
859 m_processor->GetTV()->ReplaceHandler(false);
860
861 bool bReturn(true);
862 if (bReinit || m_configuration.logicalAddresses.IsEmpty())
863 {
864 if (bDeviceTypeChanged)
865 bReturn = ChangeDeviceType(oldPrimaryType, m_configuration.deviceTypes[0]);
866 else if (CLibCEC::IsValidPhysicalAddress(m_configuration.iPhysicalAddress))
867 bReturn = SetPhysicalAddress(m_configuration.iPhysicalAddress);
868 else if (m_configuration.baseDevice != CECDEVICE_UNKNOWN && m_configuration.iHDMIPort != CEC_HDMI_PORTNUMBER_NONE)
869 bReturn = SetHDMIPort(m_configuration.baseDevice, m_configuration.iHDMIPort);
870 }
871 else if (m_configuration.bActivateSource == 1 && bIsRunning && !m_processor->IsActiveSource(m_configuration.logicalAddresses.primary))
872 {
873 // activate the source if we're not already the active source
874 m_processor->SetActiveSource(m_configuration.deviceTypes.types[0]);
875 }
876
877 // persist the configuration
878 if (bIsRunning)
879 m_processor->PersistConfiguration(&m_configuration);
880
881 return bReturn;
882}
883
884void CCECClient::AddCommand(const cec_command &command)
885{
886 CLockObject lock(m_mutex);
887
888 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);
889
890 if (m_configuration.callbacks && m_configuration.callbacks->CBCecCommand)
891 m_configuration.callbacks->CBCecCommand(m_configuration.callbackParam, command);
892 else if (!m_commandBuffer.Push(command))
893 LIB_CEC->AddLog(CEC_LOG_WARNING, "command buffer is full");
894}
895
896int CCECClient::MenuStateChanged(const cec_menu_state newState)
897{
898 CLockObject lock(m_mutex);
899
900 LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> %s: %s", ToString(CEC_OPCODE_MENU_REQUEST), ToString(newState));
901
902 if (m_configuration.callbacks &&
903 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_2 &&
904 m_configuration.callbacks->CBCecMenuStateChanged)
905 return m_configuration.callbacks->CBCecMenuStateChanged(m_configuration.callbackParam, newState);
906
907 return 0;
908}
909
910void CCECClient::Alert(const libcec_alert type, const libcec_parameter &param)
911{
912 CLockObject lock(m_mutex);
913
914 if (m_configuration.callbacks &&
915 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_0 &&
916 m_configuration.callbacks->CBCecAlert)
917 m_configuration.callbacks->CBCecAlert(m_configuration.callbackParam, type, param);
918}
919
920void CCECClient::AddLog(const cec_log_message &message)
921{
922 CLockObject lock(m_logMutex);
923 if (m_configuration.callbacks && m_configuration.callbacks->CBCecLogMessage)
924 m_configuration.callbacks->CBCecLogMessage(m_configuration.callbackParam, message);
925 else
926 m_logBuffer.Push(message);
927}
928
929void CCECClient::AddKey(void)
930{
931 CLockObject lock(m_mutex);
932
933 if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN)
934 {
935 cec_keypress key;
936
937 key.duration = (unsigned int) (GetTimeMs() - m_buttontime);
938 key.keycode = m_iCurrentButton;
939 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key released: %1x", key.keycode);
940
941 if (m_configuration.callbacks && m_configuration.callbacks->CBCecKeyPress)
942 m_configuration.callbacks->CBCecKeyPress(m_configuration.callbackParam, key);
943 else
944 m_keyBuffer.Push(key);
945 m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
946 }
947
948 m_buttontime = 0;
949}
950
951void CCECClient::AddKey(const cec_keypress &key)
952{
953 CLockObject lock(m_mutex);
954
955 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key pressed: %1x", key.keycode);
956
957 if (m_configuration.callbacks && m_configuration.callbacks->CBCecKeyPress)
958 m_configuration.callbacks->CBCecKeyPress(m_configuration.callbackParam, key);
959 else
960 m_keyBuffer.Push(key);
961
962 m_iCurrentButton = key.duration > 0 ? CEC_USER_CONTROL_CODE_UNKNOWN : key.keycode;
963 m_buttontime = key.duration > 0 ? 0 : GetTimeMs();
964}
965
966void CCECClient::SetCurrentButton(cec_user_control_code iButtonCode)
967{
968 /* push keypress to the keybuffer with 0 duration.
969 push another press to the keybuffer with the duration set when the button is released */
970 cec_keypress key;
971 key.duration = 0;
972 key.keycode = iButtonCode;
973
974 AddKey(key);
975}
976
977void CCECClient::CheckKeypressTimeout(void)
978{
979 if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN && GetTimeMs() - m_buttontime > CEC_BUTTON_TIMEOUT)
980 {
981 AddKey();
982 m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
983 }
984}
985
986void CCECClient::ConfigurationChanged(const libcec_configuration &config)
987{
988 CLockObject lock(m_mutex);
989
990 if (m_configuration.callbacks &&
991 m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0 &&
992 m_configuration.callbacks->CBCecConfigurationChanged &&
993 m_processor->CECInitialised())
994 m_configuration.callbacks->CBCecConfigurationChanged(m_configuration.callbackParam, config);
995}
996
997bool CCECClient::EnableCallbacks(void *cbParam, ICECCallbacks *callbacks)
998{
999 CLockObject lock(m_mutex);
1000 m_configuration.callbackParam = cbParam;
1001 m_configuration.callbacks = callbacks;
1002 return true;
1003}
1004
1005bool CCECClient::GetNextLogMessage(cec_log_message *message)
1006{
1007 return (m_logBuffer.Pop(*message));
1008}
1009
1010bool CCECClient::GetNextKeypress(cec_keypress *key)
1011{
1012 return m_keyBuffer.Pop(*key);
1013}
1014
1015bool CCECClient::GetNextCommand(cec_command *command)
1016{
1017 return m_commandBuffer.Pop(*command);
1018}