LibCecSharp: better handling of callbacks
[deb_libcec.git] / src / LibCecSharp / LibCecSharp.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 "CecSharpTypes.h"
34 #using <System.dll>
35
36 using namespace System;
37 using namespace System::Runtime::InteropServices;
38 using namespace CEC;
39 using namespace msclr::interop;
40
41 namespace CecSharp
42 {
43 public ref class LibCecSharp : public CecCallbackMethods
44 {
45 public:
46 LibCecSharp(LibCECConfiguration ^config)
47 {
48 m_callbacks = config->Callbacks;
49 CecCallbackMethods::EnableCallbacks(m_callbacks);
50 if (!InitialiseLibCec(config))
51 throw gcnew Exception("Could not initialise LibCecSharp");
52 }
53
54 LibCecSharp(String ^ strDeviceName, CecDeviceTypeList ^ deviceTypes)
55 {
56 m_callbacks = gcnew CecCallbackMethods();
57 LibCECConfiguration ^config = gcnew LibCECConfiguration();
58 config->SetCallbacks(this);
59 config->DeviceName = strDeviceName;
60 config->DeviceTypes = deviceTypes;
61 if (!InitialiseLibCec(config))
62 throw gcnew Exception("Could not initialise LibCecSharp");
63 }
64
65 ~LibCecSharp(void)
66 {
67 Close();
68 m_libCec = NULL;
69 }
70
71 private:
72 !LibCecSharp(void)
73 {
74 Close();
75 m_libCec = NULL;
76 }
77
78 bool InitialiseLibCec(LibCECConfiguration ^config)
79 {
80 marshal_context ^ context = gcnew marshal_context();
81 libcec_configuration libCecConfig;
82 ConvertConfiguration(context, config, libCecConfig);
83
84 m_libCec = (ICECAdapter *) CECInitialise(&libCecConfig);
85 config->Update(libCecConfig);
86
87 delete context;
88 return m_libCec != NULL;
89 }
90
91 void ConvertConfiguration(marshal_context ^context, LibCECConfiguration ^netConfig, CEC::libcec_configuration &config)
92 {
93 config.Clear();
94
95 const char *strDeviceName = context->marshal_as<const char*>(netConfig->DeviceName);
96 memcpy_s(config.strDeviceName, 13, strDeviceName, 13);
97 for (unsigned int iPtr = 0; iPtr < 5; iPtr++)
98 config.deviceTypes.types[iPtr] = (cec_device_type)netConfig->DeviceTypes->Types[iPtr];
99
100 config.bAutodetectAddress = netConfig->AutodetectAddress ? 1 : 0;
101 config.iPhysicalAddress = netConfig->PhysicalAddress;
102 config.baseDevice = (cec_logical_address)netConfig->BaseDevice;
103 config.iHDMIPort = netConfig->HDMIPort;
104 config.clientVersion = (cec_client_version)netConfig->ClientVersion;
105 config.bGetSettingsFromROM = netConfig->GetSettingsFromROM ? 1 : 0;
106 config.bActivateSource = netConfig->ActivateSource ? 1 : 0;
107 config.tvVendor = (cec_vendor_id)netConfig->TvVendor;
108 config.wakeDevices.Clear();
109 for (int iPtr = 0; iPtr < 16; iPtr++)
110 {
111 if (netConfig->WakeDevices->IsSet((CecLogicalAddress)iPtr))
112 config.wakeDevices.Set((cec_logical_address)iPtr);
113 }
114 config.powerOffDevices.Clear();
115 for (int iPtr = 0; iPtr < 16; iPtr++)
116 {
117 if (netConfig->PowerOffDevices->IsSet((CecLogicalAddress)iPtr))
118 config.powerOffDevices.Set((cec_logical_address)iPtr);
119 }
120 config.bPowerOffScreensaver = netConfig->PowerOffScreensaver ? 1 : 0;
121 config.bPowerOffOnStandby = netConfig->PowerOffOnStandby ? 1 : 0;
122
123 if (netConfig->ServerVersion >= CecServerVersion::Version1_5_1)
124 config.bSendInactiveSource = netConfig->SendInactiveSource ? 1 : 0;
125
126 if (netConfig->ServerVersion >= CecServerVersion::Version1_6_0)
127 {
128 config.bPowerOffDevicesOnStandby = netConfig->PowerOffDevicesOnStandby ? 1 : 0;
129 config.bShutdownOnStandby = netConfig->ShutdownOnStandby ? 1 : 0;
130 }
131
132 if (netConfig->ServerVersion >= CecServerVersion::Version1_6_2)
133 {
134 const char *strDeviceLanguage = context->marshal_as<const char*>(netConfig->DeviceLanguage);
135 memcpy_s(config.strDeviceLanguage, 3, strDeviceLanguage, 3);
136 }
137
138 if (netConfig->ServerVersion >= CecServerVersion::Version1_6_3)
139 config.bMonitorOnly = netConfig->MonitorOnlyClient ? 1 : 0;
140
141 config.callbacks = &g_cecCallbacks;
142 }
143
144 public:
145 array<CecAdapter ^> ^ FindAdapters(String ^ path)
146 {
147 cec_adapter *devices = new cec_adapter[10];
148
149 marshal_context ^ context = gcnew marshal_context();
150 const char* strPathC = path->Length > 0 ? context->marshal_as<const char*>(path) : NULL;
151
152 uint8_t iDevicesFound = m_libCec->FindAdapters(devices, 10, NULL);
153
154 array<CecAdapter ^> ^ adapters = gcnew array<CecAdapter ^>(iDevicesFound);
155 for (unsigned int iPtr = 0; iPtr < iDevicesFound; iPtr++)
156 adapters[iPtr] = gcnew CecAdapter(gcnew String(devices[iPtr].path), gcnew String(devices[iPtr].comm));
157
158 delete devices;
159 delete context;
160 return adapters;
161 }
162
163 bool Open(String ^ strPort, int iTimeoutMs)
164 {
165 CecCallbackMethods::EnableCallbacks(m_callbacks);
166 EnableCallbacks(m_callbacks);
167 marshal_context ^ context = gcnew marshal_context();
168 const char* strPortC = context->marshal_as<const char*>(strPort);
169 bool bReturn = m_libCec->Open(strPortC, iTimeoutMs);
170 delete context;
171 return bReturn;
172 }
173
174 void Close(void)
175 {
176 DisableCallbacks();
177 m_libCec->Close();
178 }
179
180 virtual void DisableCallbacks(void) override
181 {
182 // delete the callbacks, since these might already have been destroyed in .NET
183 CecCallbackMethods::DisableCallbacks();
184 if (m_libCec)
185 m_libCec->EnableCallbacks(NULL, NULL);
186 }
187
188 virtual bool EnableCallbacks(CecCallbackMethods ^ callbacks) override
189 {
190 if (m_libCec && CecCallbackMethods::EnableCallbacks(callbacks))
191 return m_libCec->EnableCallbacks((void*)GetCallbackPtr(), &g_cecCallbacks);
192
193 return false;
194 }
195
196 bool PingAdapter(void)
197 {
198 return m_libCec->PingAdapter();
199 }
200
201 bool StartBootloader(void)
202 {
203 return m_libCec->StartBootloader();
204 }
205
206 int GetMinLibVersion(void)
207 {
208 return m_libCec->GetMinLibVersion();
209 }
210
211 int GetLibVersionMajor(void)
212 {
213 return m_libCec->GetLibVersionMajor();
214 }
215
216 int GetLibVersionMinor(void)
217 {
218 return m_libCec->GetLibVersionMinor();
219 }
220
221 CecLogMessage ^ GetNextLogMessage(void)
222 {
223 cec_log_message msg;
224 if (m_libCec->GetNextLogMessage(&msg))
225 {
226 return gcnew CecLogMessage(gcnew String(msg.message), (CecLogLevel)msg.level, msg.time);
227 }
228
229 return gcnew CecLogMessage();
230 }
231
232 CecKeypress ^ GetNextKeypress(void)
233 {
234 cec_keypress key;
235 if (m_libCec->GetNextKeypress(&key))
236 {
237 return gcnew CecKeypress((CecUserControlCode)key.keycode, key.duration);
238 }
239
240 return gcnew CecKeypress();
241 }
242
243 CecCommand ^ GetNextCommand(void)
244 {
245 cec_command command;
246 if (m_libCec->GetNextCommand(&command))
247 {
248 CecCommand ^ retVal = gcnew CecCommand((CecLogicalAddress)command.initiator, (CecLogicalAddress)command.destination, command.ack == 1 ? true : false, command.eom == 1 ? true : false, (CecOpcode)command.opcode, command.transmit_timeout);
249 for (uint8_t iPtr = 0; iPtr < command.parameters.size; iPtr++)
250 retVal->Parameters->PushBack(command.parameters[iPtr]);
251 return retVal;
252 }
253
254 return gcnew CecCommand();
255 }
256
257 bool Transmit(CecCommand ^ command)
258 {
259 cec_command ccommand;
260 cec_command::Format(ccommand, (cec_logical_address)command->Initiator, (cec_logical_address)command->Destination, (cec_opcode)command->Opcode);
261 ccommand.transmit_timeout = command->TransmitTimeout;
262 ccommand.eom = command->Eom;
263 ccommand.ack = command->Ack;
264 for (unsigned int iPtr = 0; iPtr < command->Parameters->Size; iPtr++)
265 ccommand.parameters.PushBack(command->Parameters->Data[iPtr]);
266
267 return m_libCec->Transmit(ccommand);
268 }
269
270 bool SetLogicalAddress(CecLogicalAddress logicalAddress)
271 {
272 return m_libCec->SetLogicalAddress((cec_logical_address) logicalAddress);
273 }
274
275 bool SetPhysicalAddress(uint16_t physicalAddress)
276 {
277 return m_libCec->SetPhysicalAddress(physicalAddress);
278 }
279
280 bool PowerOnDevices(CecLogicalAddress logicalAddress)
281 {
282 return m_libCec->PowerOnDevices((cec_logical_address) logicalAddress);
283 }
284
285 bool StandbyDevices(CecLogicalAddress logicalAddress)
286 {
287 return m_libCec->StandbyDevices((cec_logical_address) logicalAddress);
288 }
289
290 bool PollDevice(CecLogicalAddress logicalAddress)
291 {
292 return m_libCec->PollDevice((cec_logical_address) logicalAddress);
293 }
294
295 bool SetActiveSource(CecDeviceType type)
296 {
297 return m_libCec->SetActiveSource((cec_device_type) type);
298 }
299
300 bool SetDeckControlMode(CecDeckControlMode mode, bool sendUpdate)
301 {
302 return m_libCec->SetDeckControlMode((cec_deck_control_mode) mode, sendUpdate);
303 }
304
305 bool SetDeckInfo(CecDeckInfo info, bool sendUpdate)
306 {
307 return m_libCec->SetDeckInfo((cec_deck_info) info, sendUpdate);
308 }
309
310 bool SetInactiveView(void)
311 {
312 return m_libCec->SetInactiveView();
313 }
314
315 bool SetMenuState(CecMenuState state, bool sendUpdate)
316 {
317 return m_libCec->SetMenuState((cec_menu_state) state, sendUpdate);
318 }
319
320 bool SetOSDString(CecLogicalAddress logicalAddress, CecDisplayControl duration, String ^ message)
321 {
322 marshal_context ^ context = gcnew marshal_context();
323 const char* strMessageC = context->marshal_as<const char*>(message);
324
325 bool bReturn = m_libCec->SetOSDString((cec_logical_address) logicalAddress, (cec_display_control) duration, strMessageC);
326
327 delete context;
328 return bReturn;
329 }
330
331 bool SwitchMonitoring(bool enable)
332 {
333 return m_libCec->SwitchMonitoring(enable);
334 }
335
336 CecVersion GetDeviceCecVersion(CecLogicalAddress logicalAddress)
337 {
338 return (CecVersion) m_libCec->GetDeviceCecVersion((cec_logical_address) logicalAddress);
339 }
340
341 String ^ GetDeviceMenuLanguage(CecLogicalAddress logicalAddress)
342 {
343 cec_menu_language lang;
344 if (m_libCec->GetDeviceMenuLanguage((cec_logical_address) logicalAddress, &lang))
345 {
346 return gcnew String(lang.language);
347 }
348
349 return gcnew String("");
350 }
351
352 CecVendorId GetDeviceVendorId(CecLogicalAddress logicalAddress)
353 {
354 return (CecVendorId)m_libCec->GetDeviceVendorId((cec_logical_address) logicalAddress);
355 }
356
357 CecPowerStatus GetDevicePowerStatus(CecLogicalAddress logicalAddress)
358 {
359 return (CecPowerStatus) m_libCec->GetDevicePowerStatus((cec_logical_address) logicalAddress);
360 }
361
362 void RescanActiveDevices(void)
363 {
364 m_libCec->RescanActiveDevices();
365 }
366
367 CecLogicalAddresses ^ GetActiveDevices(void)
368 {
369 CecLogicalAddresses ^ retVal = gcnew CecLogicalAddresses();
370 unsigned int iDevices = 0;
371
372 cec_logical_addresses activeDevices = m_libCec->GetActiveDevices();
373
374 for (uint8_t iPtr = 0; iPtr < 16; iPtr++)
375 if (activeDevices[iPtr])
376 retVal->Addresses[iDevices++] = (CecLogicalAddress)iPtr;
377
378 return retVal;
379 }
380
381 bool IsActiveDevice(CecLogicalAddress logicalAddress)
382 {
383 return m_libCec->IsActiveDevice((cec_logical_address)logicalAddress);
384 }
385
386 bool IsActiveDeviceType(CecDeviceType type)
387 {
388 return m_libCec->IsActiveDeviceType((cec_device_type)type);
389 }
390
391 bool SetHDMIPort(CecLogicalAddress address, uint8_t port)
392 {
393 return m_libCec->SetHDMIPort((cec_logical_address)address, port);
394 }
395
396 uint8_t VolumeUp(bool wait)
397 {
398 return m_libCec->VolumeUp(wait);
399 }
400
401 uint8_t VolumeDown(bool wait)
402 {
403 return m_libCec->VolumeDown(wait);
404 }
405
406 uint8_t MuteAudio(bool wait)
407 {
408 return m_libCec->MuteAudio(wait);
409 }
410
411 bool SendKeypress(CecLogicalAddress destination, CecUserControlCode key, bool wait)
412 {
413 return m_libCec->SendKeypress((cec_logical_address)destination, (cec_user_control_code)key, wait);
414 }
415
416 bool SendKeyRelease(CecLogicalAddress destination, bool wait)
417 {
418 return m_libCec->SendKeyRelease((cec_logical_address)destination, wait);
419 }
420
421 String ^ GetDeviceOSDName(CecLogicalAddress logicalAddress)
422 {
423 cec_osd_name osd = m_libCec->GetDeviceOSDName((cec_logical_address) logicalAddress);
424 return gcnew String(osd.name);
425 }
426
427 CecLogicalAddress GetActiveSource()
428 {
429 return (CecLogicalAddress)m_libCec->GetActiveSource();
430 }
431
432 bool IsActiveSource(CecLogicalAddress logicalAddress)
433 {
434 return m_libCec->IsActiveSource((cec_logical_address)logicalAddress);
435 }
436
437 uint16_t GetDevicePhysicalAddress(CecLogicalAddress iAddress)
438 {
439 return m_libCec->GetDevicePhysicalAddress((cec_logical_address)iAddress);
440 }
441
442 bool SetStreamPath(CecLogicalAddress iAddress)
443 {
444 return m_libCec->SetStreamPath((cec_logical_address)iAddress);
445 }
446
447 bool SetStreamPath(uint16_t iPhysicalAddress)
448 {
449 return m_libCec->SetStreamPath(iPhysicalAddress);
450 }
451
452 CecLogicalAddresses ^GetLogicalAddresses(void)
453 {
454 CecLogicalAddresses ^addr = gcnew CecLogicalAddresses();
455 cec_logical_addresses libAddr = m_libCec->GetLogicalAddresses();
456 for (unsigned int iPtr = 0; iPtr < 16; iPtr++)
457 addr->Addresses[iPtr] = (CecLogicalAddress)libAddr.addresses[iPtr];
458 addr->Primary = (CecLogicalAddress)libAddr.primary;
459 return addr;
460 }
461
462 bool GetCurrentConfiguration(LibCECConfiguration ^configuration)
463 {
464 libcec_configuration config;
465 config.Clear();
466
467 if (m_libCec->GetCurrentConfiguration(&config))
468 {
469 configuration->Update(config);
470 return true;
471 }
472 return false;
473 }
474
475 bool CanPersistConfiguration(void)
476 {
477 return m_libCec->CanPersistConfiguration();
478 }
479
480 bool PersistConfiguration(LibCECConfiguration ^configuration)
481 {
482 marshal_context ^ context = gcnew marshal_context();
483 libcec_configuration config;
484 ConvertConfiguration(context, configuration, config);
485
486 bool bReturn = m_libCec->PersistConfiguration(&config);
487
488 delete context;
489 return bReturn;
490 }
491
492 bool SetConfiguration(LibCECConfiguration ^configuration)
493 {
494 marshal_context ^ context = gcnew marshal_context();
495 libcec_configuration config;
496 ConvertConfiguration(context, configuration, config);
497
498 bool bReturn = m_libCec->SetConfiguration(&config);
499
500 delete context;
501 return bReturn;
502 }
503
504 bool IsLibCECActiveSource()
505 {
506 return m_libCec->IsLibCECActiveSource();
507 }
508
509 bool GetDeviceInformation(String ^ port, LibCECConfiguration ^configuration, uint32_t timeoutMs)
510 {
511 bool bReturn(false);
512 marshal_context ^ context = gcnew marshal_context();
513
514 libcec_configuration config;
515 config.Clear();
516
517 const char* strPortC = port->Length > 0 ? context->marshal_as<const char*>(port) : NULL;
518
519 if (m_libCec->GetDeviceInformation(strPortC, &config, timeoutMs))
520 {
521 configuration->Update(config);
522 bReturn = true;
523 }
524
525 delete context;
526 return bReturn;
527 }
528
529 String ^ ToString(CecLogicalAddress iAddress)
530 {
531 const char *retVal = m_libCec->ToString((cec_logical_address)iAddress);
532 return gcnew String(retVal);
533 }
534
535 String ^ ToString(CecVendorId iVendorId)
536 {
537 const char *retVal = m_libCec->ToString((cec_vendor_id)iVendorId);
538 return gcnew String(retVal);
539 }
540
541 String ^ ToString(CecVersion iVersion)
542 {
543 const char *retVal = m_libCec->ToString((cec_version)iVersion);
544 return gcnew String(retVal);
545 }
546
547 String ^ ToString(CecPowerStatus iState)
548 {
549 const char *retVal = m_libCec->ToString((cec_power_status)iState);
550 return gcnew String(retVal);
551 }
552
553 String ^ ToString(CecMenuState iState)
554 {
555 const char *retVal = m_libCec->ToString((cec_menu_state)iState);
556 return gcnew String(retVal);
557 }
558
559 String ^ ToString(CecDeckControlMode iMode)
560 {
561 const char *retVal = m_libCec->ToString((cec_deck_control_mode)iMode);
562 return gcnew String(retVal);
563 }
564
565 String ^ ToString(CecDeckInfo status)
566 {
567 const char *retVal = m_libCec->ToString((cec_deck_info)status);
568 return gcnew String(retVal);
569 }
570
571 String ^ ToString(CecOpcode opcode)
572 {
573 const char *retVal = m_libCec->ToString((cec_opcode)opcode);
574 return gcnew String(retVal);
575 }
576
577 String ^ ToString(CecSystemAudioStatus mode)
578 {
579 const char *retVal = m_libCec->ToString((cec_system_audio_status)mode);
580 return gcnew String(retVal);
581 }
582
583 String ^ ToString(CecAudioStatus status)
584 {
585 const char *retVal = m_libCec->ToString((cec_audio_status)status);
586 return gcnew String(retVal);
587 }
588
589 String ^ ToString(CecClientVersion version)
590 {
591 const char *retVal = m_libCec->ToString((cec_client_version)version);
592 return gcnew String(retVal);
593 }
594
595 String ^ ToString(CecServerVersion version)
596 {
597 const char *retVal = m_libCec->ToString((cec_server_version)version);
598 return gcnew String(retVal);
599 }
600
601 private:
602 ICECAdapter * m_libCec;
603 CecCallbackMethods ^ m_callbacks;
604 };
605 }