1 #pragma once
2 /*
3 * This file is part of the libCEC(R) library.
4 *
5 * libCEC(R) is Copyright (C) 2011-2015 Pulse-Eight Limited. All rights reserved.
6 * libCEC(R) is an original work, containing original code.
7 *
8 * libCEC(R) is a trademark of Pulse-Eight Limited.
9 *
10 * This program is dual-licensed; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 * 02110-1301 USA
24 *
25 *
26 * Alternatively, you can license this library under a commercial license,
27 * please contact Pulse-Eight Licensing for more information.
28 *
29 * For more information contact:
30 * Pulse-Eight Licensing <license@pulse-eight.com>
31 * http://www.pulse-eight.com/
32 * http://www.pulse-eight.net/
33 */
34
35 #define SWIG_FILE_WITH_INIT
36 #define SWIG_PYTHON_THREADS
37 #define SWIG_PYTHON_USE_GIL
38 #define LIBCEC_SWIG_EXPORTS
39
40 #include "env.h"
41 #include "cectypes.h"
42 #include "cec.h"
43 #include "CECTypeUtils.h"
44 #include "p8-platform/threads/mutex.h"
45 /** XXX only to keep the IDE happy, using the actual Python.h with the correct system version when building */
46 #ifndef Py_PYTHON_H
47 #include <python2.7/Python.h>
48 #include <assert.h>
49 #endif
50
51 namespace CEC
52 {
53 enum libcecSwigCallback {
54 PYTHON_CB_LOG_MESSAGE,
55 PYTHON_CB_KEY_PRESS,
56 PYTHON_CB_COMMAND,
57 PYTHON_CB_ALERT,
58 PYTHON_CB_MENU_STATE,
59 PYTHON_CB_SOURCE_ACTIVATED,
60 PYTHON_CB_CONFIGURATION,
61 NB_PYTHON_CB,
62 };
63
64 class CCecPythonCallbacks
65 {
66 public:
67 /**
68 * Create a new python callbacks instance, and set callbacks in the configuration
69 * @param config the configuration to set the callbacks in
70 */
CCecPythonCallbacks(libcec_configuration * config)71 CCecPythonCallbacks(libcec_configuration* config) :
72 m_configuration(config)
73 {
74 assert(!!config);
75 assert(!!m_configuration);
76
77 config->callbacks = new ICECCallbacks;
78 if (!config->callbacks)
79 throw std::bad_alloc();
80
81 for (size_t ptr = 0; ptr < NB_PYTHON_CB; ++ptr)
82 m_callbacks[ptr] = NULL;
83
84 m_configuration->callbacks->logMessage = CBCecLogMessage;
85 m_configuration->callbacks->keyPress = CBCecKeyPress;
86 m_configuration->callbacks->commandReceived = CBCecCommand;
87 m_configuration->callbacks->configurationChanged = CBCecConfigurationChanged;
88 m_configuration->callbacks->alert = CBCecAlert;
89 m_configuration->callbacks->menuStateChanged = CBCecMenuStateChanged;
90 m_configuration->callbacks->sourceActivated = CBCecSourceActivated;
91 }
92
93 /**
94 * Unreferences all python callbacks, and deletes the callbacks
95 */
~CCecPythonCallbacks(void)96 virtual ~CCecPythonCallbacks(void)
97 {
98 for (size_t ptr = 0; ptr < NB_PYTHON_CB; ++ptr)
99 if (m_callbacks[ptr])
100 Py_XDECREF(m_callbacks[ptr]);
101 delete m_configuration->callbacks;
102 m_configuration->callbacks = nullptr;
103 }
104
105 /**
106 * Call a python callback (if set)
107 * @param callback the callback to call
108 * @param arglist the arguments to pass to the callback
109 * @return 0 if the callback failed, the result returned by python otherwise
110 */
CallPythonCallback(enum libcecSwigCallback callback,PyObject * arglist)111 int CallPythonCallback(enum libcecSwigCallback callback, PyObject* arglist)
112 {
113 int retval = 0;
114
115 if (callback >= NB_PYTHON_CB || !m_callbacks[callback])
116 return retval;
117
118 PyObject* result = nullptr;
119 if (!!m_callbacks[callback])
120 {
121 /** call the callback */
122 result = PyEval_CallObject(m_callbacks[callback], arglist);
123
124 /** unref the argument and result */
125 if (!!arglist)
126 Py_DECREF(arglist);
127 if (!!result)
128 {
129 if (PyInt_Check(result))
130 retval = (int)PyInt_AsLong(result);
131 Py_XDECREF(result);
132 }
133 }
134
135 return retval;
136 }
137
138 /**
139 * Set a python callable as callback
140 * @param cb the callback to set
141 * @param pyfunc the python callable to call
142 */
SetCallback(size_t cb,PyObject * pyfunc)143 void SetCallback(size_t cb, PyObject* pyfunc)
144 {
145 assert(cb < NB_PYTHON_CB);
146 assert(PyCallable_Check(pyfunc));
147
148 /** unref previous callback (if any) */
149 if (!!m_callbacks[cb])
150 Py_XDECREF(m_callbacks[cb]);
151
152 /** set the new callback */
153 m_callbacks[cb] = pyfunc;
154 Py_XINCREF(pyfunc);
155 }
156
157 private:
CallPythonCallback(void * param,enum libcecSwigCallback callback,PyObject * arglist)158 static inline int CallPythonCallback(void* param, enum libcecSwigCallback callback, PyObject* arglist)
159 {
160 CCecPythonCallbacks* pCallbacks = static_cast<CCecPythonCallbacks*>(param);
161 return !!pCallbacks ?
162 pCallbacks->CallPythonCallback(callback, arglist) :
163 0;
164 }
165
CBCecLogMessage(void * param,const CEC::cec_log_message * message)166 static void CBCecLogMessage(void* param, const CEC::cec_log_message* message)
167 {
168 PyGILState_STATE gstate = PyGILState_Ensure();
169 PyObject* arglist = Py_BuildValue("(I,I,s)", message->level, (long)message->time, message->message);
170 CallPythonCallback(param, PYTHON_CB_LOG_MESSAGE, arglist);
171 PyGILState_Release(gstate);
172 }
173
CBCecKeyPress(void * param,const CEC::cec_keypress * key)174 static void CBCecKeyPress(void* param, const CEC::cec_keypress* key)
175 {
176 PyGILState_STATE gstate = PyGILState_Ensure();
177 CallPythonCallback(param, PYTHON_CB_KEY_PRESS,
178 Py_BuildValue("(I,I)", (long)key->keycode, (long)key->duration));
179 PyGILState_Release(gstate);
180 }
181
CBCecCommand(void * param,const CEC::cec_command * command)182 static void CBCecCommand(void* param, const CEC::cec_command* command)
183 {
184 PyGILState_STATE gstate = PyGILState_Ensure();
185 CallPythonCallback(param, PYTHON_CB_COMMAND,
186 Py_BuildValue("(s)", CEC::CCECTypeUtils::ToString(*command).c_str()));
187 PyGILState_Release(gstate);
188 }
189
CBCecMenuStateChanged(void * param,const CEC::cec_menu_state state)190 static int CBCecMenuStateChanged(void* param, const CEC::cec_menu_state state)
191 {
192 PyGILState_STATE gstate = PyGILState_Ensure();
193 int retval = CallPythonCallback(param, PYTHON_CB_MENU_STATE,
194 Py_BuildValue("(I)", state));
195 PyGILState_Release(gstate);
196 return retval;
197 }
198
CBCecSourceActivated(void * param,const CEC::cec_logical_address logicalAddress,const uint8_t activated)199 static void CBCecSourceActivated(void* param, const CEC::cec_logical_address logicalAddress, const uint8_t activated)
200 {
201 PyGILState_STATE gstate = PyGILState_Ensure();
202 CallPythonCallback(param, PYTHON_CB_SOURCE_ACTIVATED,
203 Py_BuildValue("(I,I)", logicalAddress, activated));
204 PyGILState_Release(gstate);
205 }
206
CBCecAlert(void * param,const libcec_alert alert,const libcec_parameter cbparam)207 static void CBCecAlert(void* param, const libcec_alert alert, const libcec_parameter cbparam)
208 {
209 PyGILState_STATE gstate = PyGILState_Ensure();
210 CallPythonCallback(param, PYTHON_CB_ALERT,
211 Py_BuildValue("(I,I)", alert, cbparam));
212 PyGILState_Release(gstate);
213 }
214
CBCecConfigurationChanged(void * param,const libcec_configuration * configuration)215 static void CBCecConfigurationChanged(void* param, const libcec_configuration* configuration)
216 {
217 PyGILState_STATE gstate = PyGILState_Ensure();
218 CallPythonCallback(param, PYTHON_CB_CONFIGURATION,
219 Py_BuildValue("(I)", configuration));
220 PyGILState_Release(gstate);
221 }
222
223 PyObject* m_callbacks[NB_PYTHON_CB];
224 libcec_configuration* m_configuration;
225 };
226
_GetCallbacks(CEC::libcec_configuration * self)227 static CCecPythonCallbacks* _GetCallbacks(CEC::libcec_configuration* self)
228 {
229 /** allocate callbacks struct and python callbacks if needed */
230 if (!self->callbackParam)
231 self->callbackParam = new CCecPythonCallbacks(self);
232 if (!self->callbackParam)
233 throw std::bad_alloc();
234 return static_cast<CCecPythonCallbacks*>(self->callbackParam);
235 }
236 }
237
_SetCallback(CEC::libcec_configuration * self,size_t cb,PyObject * pyfunc)238 static void _SetCallback(CEC::libcec_configuration* self, size_t cb, PyObject* pyfunc)
239 {
240 assert(!!self);
241 CEC::CCecPythonCallbacks* pCallbacks = CEC::_GetCallbacks(self);
242 if (!!pCallbacks)
243 pCallbacks->SetCallback(cb, pyfunc);
244 else
245 throw std::bad_alloc();
246 }
247
_ClearCallbacks(CEC::libcec_configuration * self)248 void _ClearCallbacks(CEC::libcec_configuration* self)
249 {
250 assert(!!self);
251 CEC::CCecPythonCallbacks* pCallbacks = static_cast<CEC::CCecPythonCallbacks*>(self->callbackParam);
252 if (!!pCallbacks)
253 delete pCallbacks;
254 self->callbackParam = NULL;
255 }
256
257