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