1 // python-gphoto2 - Python interface to libgphoto2
2 // http://github.com/jim-easterbrook/python-gphoto2
3 // Copyright (C) 2014-20  Jim Easterbrook  jim@jim-easterbrook.me.uk
4 //
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 
18 %module(package="gphoto2") context
19 
20 %include "common/preamble.i"
21 
22 %rename(Context) _GPContext;
23 
24 #ifndef SWIGIMPORTED
25 
26 // Make docstring parameter types more Pythonic
27 %typemap(doc) GPContext * "$1_name: gphoto2.$*1_type";
28 
29 // gp_camera_autodetect() returns a pointer in an output parameter
30 NEW_ARGOUT(CameraList *, gp_list_new, gp_list_unref)
31 
32 // Ignore "backend" functions
33 %ignore gp_context_cancel;
34 %ignore gp_context_error;
35 %ignore gp_context_idle;
36 %ignore gp_context_message;
37 %ignore gp_context_progress_start;
38 %ignore gp_context_progress_stop;
39 %ignore gp_context_progress_update;
40 %ignore gp_context_question;
41 %ignore gp_context_ref;
42 %ignore gp_context_status;
43 %ignore gp_context_unref;
44 
45 // Structs to store callback details
46 %ignore CallbackDetails::context;
47 %ignore CallbackDetails::func;
48 %ignore CallbackDetails::data;
49 %ignore CallbackDetails::remove;
50 %ignore del_CallbackDetails;
51 
52 %inline %{
53 typedef void (*RemoveFunc) (GPContext *context, void *func, void *data);
54 
55 typedef struct CallbackDetails {
56     GPContext   *context;
57     PyObject    *func_1;
58     PyObject    *func_2;
59     PyObject    *func_3;
60     PyObject    *data;
61     RemoveFunc  remove;
62 } CallbackDetails;
63 
64 // Function to remove progress callbacks, compatible with RemoveFunc
unset_progress_funcs(GPContext * context,GPContextProgressStartFunc start_func,void * data)65 static void unset_progress_funcs(GPContext *context,
66                                  GPContextProgressStartFunc start_func,
67                                  void *data) {
68     gp_context_set_progress_funcs(context, NULL, NULL, NULL, NULL);
69 };
70 
71 // Destructor
del_CallbackDetails(struct CallbackDetails * this)72 static int del_CallbackDetails(struct CallbackDetails *this) {
73     if (this->context)
74         this->remove(this->context, NULL, NULL);
75     Py_XDECREF(this->func_1);
76     Py_XDECREF(this->func_2);
77     Py_XDECREF(this->func_3);
78     Py_XDECREF(this->data);
79     free(this);
80     return GP_OK;
81 };
82 %}
83 DEFAULT_DTOR(CallbackDetails, del_CallbackDetails);
84 
85 // Define wrapper functions to call Python callbacks from C callbacks
86 %define CB_WRAPPER(rtn_type, cb_name, cb_args, py3_arglist, py2_arglist, function)
87 %{
88 static rtn_type cb_name cb_args {
89     PyGILState_STATE gstate = PyGILState_Ensure();
90     CallbackDetails *this = data;
91     PyObject *result = NULL;
92     PyObject *arglist = NULL;
93     PyObject *self = NULL;
94 %}
95 #if #rtn_type == "int"
96 %{
97     rtn_type c_result = 0;
98 %}
99 #elif #rtn_type == "GPContextFeedback"
100 %{
101     rtn_type c_result = GP_CONTEXT_FEEDBACK_OK;
102 %}
103 #endif
104 %{
105     PyObject *py_context = SWIG_NewPointerObj(
106         SWIG_as_voidptr(context), SWIGTYPE_p__GPContext, 0);
107 #if PY_VERSION_HEX >= 0x03000000
108     arglist = Py_BuildValue py3_arglist;
109 #else
110     arglist = Py_BuildValue py2_arglist;
111 #endif
112     if (arglist == NULL) {
113         PyErr_Print();
114         goto fail;
115     }
116     result = PyObject_CallObject(function, arglist);
117     Py_DECREF(arglist);
118     if (result == NULL) {
119         PyErr_Print();
120         goto fail;
121     }
122 %}
123 #if #rtn_type != "void"
124 %{
125     c_result = PyInt_AsLong(result);
126 %}
127 #endif
128 %{
129     Py_DECREF(result);
130 fail:
131     PyGILState_Release(gstate);
132 %}
133 #if #rtn_type != "void"
134 %{
135     return c_result;
136 %}
137 #endif
138 %{
139 };
140 %}
141 %enddef // CB_WRAPPER
142 
143 CB_WRAPPER(void, wrap_idle_func, (GPContext *context, void *data),
144            ("(OO)", py_context, this->data),
145            ("(OO)", py_context, this->data),
146            this->func_1)
147 
148 CB_WRAPPER(void, wrap_error_func, (GPContext *context, const char *text, void *data),
149            ("(OyO)", py_context, text, this->data),
150            ("(OsO)", py_context, text, this->data),
151            this->func_1)
152 
153 CB_WRAPPER(void, wrap_status_func, (GPContext *context, const char *text, void *data),
154            ("(OyO)", py_context, text, this->data),
155            ("(OsO)", py_context, text, this->data),
156            this->func_1)
157 
158 CB_WRAPPER(void, wrap_message_func, (GPContext *context, const char *text, void *data),
159            ("(OyO)", py_context, text, this->data),
160            ("(OsO)", py_context, text, this->data),
161            this->func_1)
162 
163 CB_WRAPPER(GPContextFeedback, wrap_question_func,
164            (GPContext *context, const char *text, void *data),
165            ("(OyO)", py_context, text, this->data),
166            ("(OsO)", py_context, text, this->data),
167            this->func_1)
168 
169 CB_WRAPPER(GPContextFeedback, wrap_cancel_func, (GPContext *context, void *data),
170            ("(OO)", py_context, this->data),
171            ("(OO)", py_context, this->data),
172            this->func_1)
173 
174 CB_WRAPPER(int, py_progress_start,
175            (GPContext *context, float target, const char *text, void *data),
176            ("(OfyO)", py_context, target, text, this->data),
177            ("(OfsO)", py_context, target, text, this->data),
178            this->func_1)
179 
180 CB_WRAPPER(void, py_progress_update,
181            (GPContext *context, unsigned int id, float current, void *data),
182            ("(OifO)", py_context, id, current, this->data),
183            ("(OifO)", py_context, id, current, this->data),
184            this->func_2)
185 
186 CB_WRAPPER(void, py_progress_stop, (GPContext *context, unsigned int id, void *data),
187            ("(OiO)", py_context, id, this->data),
188            ("(OiO)", py_context, id, this->data),
189            this->func_3)
190 
191 // Typemaps for all callback setting functions
192 %typemap(in) void *data {
193     _global_callbacks->data = $input;
194     Py_INCREF(_global_callbacks->data);
195     $1 = _global_callbacks;
196 }
197 %typemap(doc) void *data "$1_name: object"
198 
199 %typemap(check) GPContext *context {
200     _global_callbacks->context = $1;
201 }
202 
203 // Macro to define typemaps for the six single callback function variants
204 %define SINGLE_CALLBACK_FUNCTION(cb_func_type, remove_func, cb_wrapper)
205 
206 %typemap(arginit) cb_func_type (CallbackDetails *_global_callbacks) {
207     _global_callbacks = malloc(sizeof(CallbackDetails));
208     if (!_global_callbacks) {
209         PyErr_SetNone(PyExc_MemoryError);
210         SWIG_fail;
211     }
212     _global_callbacks->context = NULL;
213     _global_callbacks->func_1 = NULL;
214     _global_callbacks->func_2 = NULL;
215     _global_callbacks->func_3 = NULL;
216     _global_callbacks->data = NULL;
217     _global_callbacks->remove = (RemoveFunc) remove_func;
218 }
219 %typemap(freearg) cb_func_type {
220     if (_global_callbacks)
221         del_CallbackDetails(_global_callbacks);
222 }
223 %typemap(in) cb_func_type {
224     if (!PyCallable_Check($input)) {
225         SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname" "', argument " "$argnum" " is not callable");
226     }
227     _global_callbacks->func_1 = $input;
228     Py_INCREF(_global_callbacks->func_1);
229     $1 = (cb_func_type) cb_wrapper;
230 }
231 %typemap(doc) cb_func_type "$1_name: callable function"
232 
233 %typemap(argout) cb_func_type {
234     $result = SWIG_Python_AppendOutput($result,
235         SWIG_NewPointerObj(_global_callbacks, SWIGTYPE_p_CallbackDetails, SWIG_POINTER_OWN));
236     _global_callbacks = NULL;
237 }
238 
239 %enddef // SINGLE_CALLBACK_FUNCTION
240 
241 SINGLE_CALLBACK_FUNCTION(GPContextIdleFunc,
242                          gp_context_set_idle_func, wrap_idle_func)
243 SINGLE_CALLBACK_FUNCTION(GPContextErrorFunc,
244                          gp_context_set_error_func, wrap_error_func)
245 SINGLE_CALLBACK_FUNCTION(GPContextStatusFunc,
246                          gp_context_set_status_func, wrap_status_func)
247 SINGLE_CALLBACK_FUNCTION(GPContextMessageFunc,
248                          gp_context_set_message_func, wrap_message_func)
249 SINGLE_CALLBACK_FUNCTION(GPContextQuestionFunc,
250                          gp_context_set_question_func, wrap_question_func)
251 SINGLE_CALLBACK_FUNCTION(GPContextCancelFunc,
252                          gp_context_set_cancel_func, wrap_cancel_func)
253 
254 // Progress callbacks are more complicated
255 // Use macro for first function
256 SINGLE_CALLBACK_FUNCTION(GPContextProgressStartFunc,
257                          unset_progress_funcs, py_progress_start)
258 
259 // Use typemaps for other two functions
260 %typemap(in) GPContextProgressUpdateFunc {
261     if (!PyCallable_Check($input)) {
262         SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname" "', argument " "$argnum" " is not callable");
263     }
264     _global_callbacks->func_2 = $input;
265     Py_INCREF(_global_callbacks->func_2);
266     $1 = (GPContextProgressUpdateFunc) py_progress_update;
267 }
268 %typemap(doc) GPContextProgressUpdateFunc "$1_name: callable function"
269 
270 %typemap(in) GPContextProgressStopFunc {
271     if (!PyCallable_Check($input)) {
272         SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname" "', argument " "$argnum" " is not callable");
273     }
274     _global_callbacks->func_3 = $input;
275     Py_INCREF(_global_callbacks->func_3);
276     $1 = (GPContextProgressStopFunc) py_progress_stop;
277 }
278 %typemap(doc) GPContextProgressStopFunc "$1_name: callable function"
279 
280 // Add member methods to _GPContext
281 %exception _GPContext::camera_autodetect {
282   if (PyErr_WarnEx(PyExc_DeprecationWarning,
283       "Camera.autodetect replaces Context().camera_autodetect", 1) < 0) SWIG_fail;
284   $action
285   if (PyErr_Occurred() != NULL) SWIG_fail;
286 }
287 %extend _GPContext {
camera_autodetect(CameraList * list)288   void camera_autodetect(CameraList *list) {
289     int error = gp_camera_autodetect(list, $self);
290     if (error < GP_OK) GPHOTO2_ERROR(error)
291   }
292 };
293 VOID_MEMBER_FUNCTION(_GPContext,
294     set_idle_func, (GPContextIdleFunc func, void *data),
295     gp_context_set_idle_func, ($self, func, data))
296 VOID_MEMBER_FUNCTION(_GPContext,
297     set_error_func, (GPContextErrorFunc func, void *data),
298     gp_context_set_error_func, ($self, func, data))
299 VOID_MEMBER_FUNCTION(_GPContext,
300     set_message_func, (GPContextMessageFunc func, void *data),
301     gp_context_set_message_func, ($self, func, data))
302 VOID_MEMBER_FUNCTION(_GPContext,
303     set_question_func, (GPContextQuestionFunc func, void *data),
304     gp_context_set_question_func, ($self, func, data))
305 VOID_MEMBER_FUNCTION(_GPContext,
306     set_cancel_func, (GPContextCancelFunc func, void *data),
307     gp_context_set_cancel_func, ($self, func, data))
308 VOID_MEMBER_FUNCTION(_GPContext,
309     set_progress_funcs, (GPContextProgressStartFunc start_func,
310                          GPContextProgressUpdateFunc update_func,
311                          GPContextProgressStopFunc stop_func,
312                          void *data),
313     gp_context_set_progress_funcs, ($self, start_func, update_func, stop_func, data))
314 VOID_MEMBER_FUNCTION(_GPContext,
315     set_status_func, (GPContextStatusFunc func, void *data),
316     gp_context_set_status_func, ($self, func, data))
317 
318 #endif //ifndef SWIGIMPORTED
319 
320 // Add default constructor and destructor
321 struct _GPContext {};
322 %extend _GPContext {
_GPContext()323   _GPContext() {
324     return gp_context_new();
325   }
~_GPContext()326   ~_GPContext() {
327     gp_context_unref($self);
328   }
329 };
330 %ignore _GPContext;
331 %newobject gp_context_new;
332 %delobject gp_context_unref;
333 
334 %include "gphoto2/gphoto2-context.h"
335