PythonWrapper: cwiidmodule.c

File cwiidmodule.c, 15.2 kB (added by jmtulloss, 5 years ago)

had a reference count error that is now fixed

Line 
1/*
2 * Copyright (C) 2007 Justin M. Tulloss <jmtulloss@gmail.com>
3 *
4 * Interface from Python to libcwiid
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA  02110-1301  USA
20 *
21 * ChangeLog:
22 * 2007-05-07 Justin M. Tulloss <jmtulloss@gmail.com>
23 * * Refactored according to dsmith's wishes, removed unnecessary locks
24 *
25 * 2007-04-26 Justin M. Tulloss <jmtulloss@gmail.com>
26 * * Updated for new libcwiid API
27 *
28 * 2007-04-24 Justin M. Tulloss <jmtulloss@gmail.com>
29 * * Initial Changelog
30 */
31
32//Apparently this has to be first for every python interpreter extension
33#include "Python.h"
34
35//Standard Includes
36#include <stdio.h>
37#include <string.h>
38#include <errno.h>
39#include <limits.h>
40#include <stdlib.h>
41#include <dlfcn.h>
42#include <pthread.h>
43
44//Interesting includes
45#include "cwiid.h"
46#include "structmember.h"
47
48//Python Function declarations
49void initcwiidmodule(void);
50
51static int Wiimote_constructor(PyObject* self, PyObject* args);
52PyObject* Wiimote_read(PyObject* self, PyObject* args);
53PyObject* Wiimote_write(PyObject* self, PyObject* args);
54PyObject* Wiimote_command(PyObject* self, PyObject* args);
55PyObject* Wiimote_disconnect(PyObject* self, PyObject* args);
56PyObject* Wiimote_enable(PyObject* self, PyObject* args);
57PyObject* Wiimote_disable(PyObject* self, PyObject* args);
58PyObject* Wiimote_get_mesg(PyObject* self, PyObject* args);
59PyObject* Wiimote_set_callback(PyObject* self, PyObject* args);
60PyObject* Wiimote_get_state(PyObject* self, PyObject* args);
61
62
63//Types
64
65typedef struct {
66    PyObject_HEAD
67    cwiid_wiimote_t* wiimote;
68    PyObject* callback;
69}cwiidmodule;
70
71//Type private functions
72static void cwiidmodule_dealloc(cwiidmodule* self);
73static PyObject*
74    cwiidmodule_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
75
76//Helper functions
77void callbackBridge(cwiid_wiimote_t* wiimote,
78    int mesg_count, union cwiid_mesg mesg[]);
79static PyObject* processMesgs(int mesg_count, union cwiid_mesg mesg[]);
80static int cwiid_start(cwiidmodule* self, int flags);
81static PyObject* notImplemented();
82
83static bdaddr_t btAddr;
84
85
86//Associates cwiid functions with python ones
87static PyMethodDef modMethods[] = 
88{
89    {NULL, NULL}
90};
91
92//Our type methods
93static PyMethodDef cwiidMethods[] =
94{
95
96    //{"__init__", constructorwii, METH_VARARGS, "cwiid(function)"},
97    {"read", Wiimote_read, METH_VARARGS, "read from wiimote"},
98    {"write", Wiimote_write, METH_VARARGS, "write to wiimote"},
99    {"command", Wiimote_command, METH_VARARGS, "send wiimote command"},
100    {"enable", Wiimote_enable, METH_VARARGS, "enable flags on wiimote"},
101    {"disable", Wiimote_disable, METH_VARARGS, "disable flags on wiimote"},
102    {"get_mesg", Wiimote_get_mesg, METH_VARARGS, "blocking call to get messages"},
103    {"set_callback", Wiimote_set_callback, METH_VARARGS, "setup a mesg processing callback"},
104    {"get_state", Wiimote_get_state, METH_VARARGS, "polling interface"},
105    {"disconnect", Wiimote_disconnect, METH_VARARGS, "disconnect wiimote"},
106    {NULL, NULL}
107};
108
109static PyMemberDef cwiidMembers[] = {
110    {"_wiimote", T_OBJECT_EX, offsetof(cwiidmodule, wiimote), 0, "wiimote"},
111    {"_callback", T_OBJECT_EX,offsetof(cwiidmodule, callback),0,"callback"},
112    {NULL}
113};
114
115
116//Defines a new type in python
117static PyTypeObject WiimoteType= {
118    PyObject_HEAD_INIT(NULL)
119    0,                         /*ob_size*/
120    "cwiidmodule.Wiimote", /*tp_name*/
121    sizeof(cwiidmodule),       /*tp_basicsize*/
122    0,                         /*tp_itemsize*/
123    (destructor)cwiidmodule_dealloc, /*tp_dealloc*/
124    0,                         /*tp_print*/
125    0,                         /*tp_getattr*/
126    0,                         /*tp_setattr*/
127    0,                         /*tp_compare*/
128    0,                         /*tp_repr*/
129    0,                         /*tp_as_number*/
130    0,                         /*tp_as_sequence*/
131    0,                         /*tp_as_mapping*/
132    0,                         /*tp_hash */
133    0,                         /*tp_call*/
134    0,                         /*tp_str*/
135    0,                         /*tp_getattro*/
136    0,                         /*tp_setattro*/
137    0,                         /*tp_as_buffer*/
138    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
139    "cwiid c-python interface",/* tp_doc */
140    0,                         /* tp_traverse */
141    0,                         /* tp_clear */
142    0,                         /* tp_richcompare */
143    0,                         /* tp_weaklistoffset */
144    0,                         /* tp_iter */
145    0,                         /* tp_iternext */
146    cwiidMethods,              /* tp_methods */
147    cwiidMembers,              /* tp_members */
148    0,                         /* tp_getset */
149    0,                         /* tp_base */
150    0,                         /* tp_dict */
151    0,                         /* tp_descr_get */
152    0,                         /* tp_descr_set */
153    0,                         /* tp_dictoffset */
154    (initproc)Wiimote_constructor,  /* tp_init */
155    0,                         /* tp_alloc */
156    cwiidmodule_new,                 /* tp_new */
157};
158
159//Allocate and deallocate functions
160static void
161cwiidmodule_dealloc(cwiidmodule* self)
162{
163    cwiid_disconnect(self->wiimote);
164    Py_XDECREF(self->callback);
165    self->ob_type->tp_free((PyObject*)self);
166}
167
168static PyObject*
169cwiidmodule_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
170{
171    cwiidmodule* self;
172
173    self = (cwiidmodule*) type->tp_alloc(type, 0);
174
175    return (PyObject*) self;
176}
177
178PyMODINIT_FUNC
179initcwiidmodule(void)
180{
181    PyObject* m;
182
183    if (PyType_Ready(&WiimoteType) <0 )
184        return;
185
186    //Open the cwiid library
187    dlopen("libcwiid.so", RTLD_LAZY);
188    if (!(m = Py_InitModule3("cwiidmodule", modMethods, 
189      "Module for accessing the wiimote through cwiid")))
190        return;
191
192    Py_INCREF(&WiimoteType);
193    PyModule_AddObject(m, "Wiimote", (PyObject*)&WiimoteType);
194
195}
196
197int
198cwiid_start(cwiidmodule* self, int flags)
199{
200    cwiid_wiimote_t* theMote;
201    btAddr = *BDADDR_ANY;
202
203    //Set up wiimote
204    if(!(theMote = cwiid_connect(&btAddr, flags)))
205    {
206        return -1;
207    }
208    cwiid_set_data(theMote,(void*)self); //keep pyobject with wiimote
209    self->wiimote = theMote; //keep wiimote with pyobject
210    self->callback = Py_None;
211    return 0;
212}
213
214static int
215Wiimote_constructor(PyObject* self, PyObject* args)
216{
217    PyObject* pyFlags;
218
219    //Get out parameters
220    if (PyArg_UnpackTuple(args, "constructor", 1, 1, &pyFlags))
221    {
222        if (!PyInt_Check(pyFlags))
223        {
224            PyErr_SetString(PyExc_TypeError, "Parameter must be int");
225            return 0;
226        }
227    }
228
229    if(cwiid_start((cwiidmodule*)self, PyInt_AsLong(pyFlags))<0)
230    {
231        PyErr_SetString(PyExc_IOError, "Could not connect to wiimote");
232    }
233    PyEval_InitThreads();
234   
235    return 0;
236}
237
238
239PyObject* 
240Wiimote_read(PyObject* self, PyObject* args)
241{
242        //Python types
243        PyObject* pyflags;
244        PyObject* pyoffset;
245        PyObject* pylength;
246
247        PyObject* pyRetBuf;
248
249        //C types
250        uint8_t flags;
251        uint32_t offset;
252        uint32_t length;
253        void * buf;
254
255    PyArg_UnpackTuple(args, "read", 3, 3, &pyflags,&pyoffset,&pylength);
256    if(!(PyInt_Check(pyflags) 
257                && PyInt_Check(pyoffset) 
258                && PyInt_Check(pylength)))
259    {
260        PyErr_SetString(PyExc_TypeError, "arguments must be ints");
261    }
262       
263    //marshal everything over
264    flags  = (uint8_t)  PyInt_AsLong(pyflags);
265    offset = (uint32_t) PyInt_AsLong(pyoffset);
266        length = (uint32_t) PyInt_AsLong(pylength);
267
268        //TODO: More error checking
269        buf = malloc(length);
270        cwiid_read(((cwiidmodule*)self)->wiimote,flags,offset,length,buf);
271        pyRetBuf = PyBuffer_FromMemory((char*)buf, length);
272
273        Py_XINCREF(pyRetBuf);
274        return pyRetBuf;
275       
276}
277
278PyObject* 
279Wiimote_write(PyObject* self, PyObject* args)
280{
281    return notImplemented();
282}
283
284PyObject* 
285Wiimote_command(PyObject* self, PyObject* args)
286{
287    //Python types
288    PyObject* pycommand;
289    PyObject* pyflags;
290
291    //C types
292    enum cwiid_command command;
293    uint8_t flags;
294
295    PyArg_UnpackTuple(args, "command", 2, 2, &pycommand, &pyflags);
296    if(!(PyInt_Check(pycommand) && PyInt_Check(pyflags)))
297    {
298        PyErr_SetString(PyExc_TypeError, "command and flags must be ints");
299    }
300
301    //marshal everything over
302    command = (enum cwiid_command) PyInt_AsLong(pycommand);
303    flags = (uint8_t) PyInt_AsLong(pyflags);
304
305
306    //finally, send the command to the wiimote
307    cwiid_command(((cwiidmodule*)self)->wiimote, command, flags);
308
309    Py_RETURN_NONE;
310}
311
312PyObject* 
313Wiimote_disconnect(PyObject* self, PyObject* args)
314{
315    return notImplemented();
316}
317
318PyObject* 
319Wiimote_enable(PyObject* self, PyObject* args)
320{
321    return notImplemented();
322}
323
324PyObject* 
325Wiimote_disable(PyObject* self, PyObject* args)
326{
327    return notImplemented();
328}
329
330PyObject* 
331Wiimote_get_mesg(PyObject* self, PyObject* args)
332{
333    union cwiid_mesg** mesgs;
334    int mesg_count;
335
336    //get the messages from Mr. Wiimote
337
338    return processMesgs(mesg_count, *mesgs);
339}
340
341PyObject* 
342Wiimote_set_callback(PyObject* self, PyObject* args)
343{
344    PyObject* pyCallback;
345
346    PyArg_UnpackTuple(args, "set_callback", 1, 1, &pyCallback);
347    if (!PyCallable_Check(pyCallback))
348    {
349        PyErr_SetString(PyExc_TypeError, "callback must be callable!");
350    }
351        Py_XINCREF(pyCallback);
352
353    //Set this callback as an attribute in the class
354    if (((cwiidmodule*)self)->callback== Py_None)//wasn't a callback before
355    {
356        cwiid_set_mesg_callback(((cwiidmodule*)self)->wiimote, 
357            (cwiid_mesg_callback_t*) callbackBridge);
358    }
359    ((cwiidmodule*)self)->callback = pyCallback;
360
361    Py_RETURN_NONE;
362}
363
364PyObject* 
365Wiimote_get_state(PyObject* self, PyObject* args)
366{
367    return notImplemented();
368}
369
370static PyObject*
371notImplemented()
372{
373    PyErr_SetString(PyExc_NotImplementedError, "This has not yet been implemented");
374   
375    Py_RETURN_NONE;
376}
377
378void 
379callbackBridge(cwiid_wiimote_t* wiimote, 
380    int mesg_count, union cwiid_mesg mesg[])
381{
382    PyObject* argTuple;
383    PyObject* pyself;
384    //PyObject* pyCallback;
385    PyGILState_STATE gstate;
386   
387    gstate = PyGILState_Ensure();
388
389    argTuple = processMesgs(mesg_count, mesg);
390
391    //Put id and the list of messages as the arguments to the callback
392    pyself = (PyObject*) cwiid_get_data(wiimote);
393    if (PyMethod_Check(((cwiidmodule*)pyself)->callback))
394    {
395        //Sorry for the ugliness here.
396        //After determining that the callback is a method in a class,
397        //this line calls that function object with self and the argtuple
398        //as arguments.
399        PyObject_CallFunction(
400            PyMethod_Function(((cwiidmodule*)pyself)->callback),"(OO)",
401            PyMethod_Class(((cwiidmodule*)pyself)->callback), argTuple
402            );
403    }
404    else
405        PyObject_CallFunction(((cwiidmodule*)pyself)->callback, 
406            "(O)",argTuple);
407
408    Py_XDECREF(argTuple); //actually need to decref the entire structure
409    PyGILState_Release(gstate);
410}
411
412/* This is the function responsible for marshaling the cwiid messages from
413 * C to python. It's rather complicated since it uses a complex C union
414 * to store the data and multiple enumerations to figure out what data is
415 * actually being sent. Neither of these common C types really translate
416 * well into Python. I've done my best to translate it to python as follows:
417 *
418 * Python callback takes arg (mesgs). The mesgs is a list of
419 * mesg tuples which contain the mesg type and a dict of the arguments.
420 *
421 * Ex:
422 * mesgs =>[(CWIID_BTN_MESG,{"buttons":btnMask}),
423 *          (CWIID_ACC_MESG,{"x":xVal, "y":yVal, "z":zVal})]
424 */
425static PyObject* 
426processMesgs(int mesg_count, union cwiid_mesg mesg[])
427{
428    PyObject* mesglist; //List of message tuples
429    PyObject* amesg; //A single message (type, [arguments])
430    PyObject* mesgVal; //Dictionary of arguments for a message
431
432    int i;
433
434    mesglist = PyList_New(0);
435    Py_XINCREF(mesglist);
436    for (i = 0; i < mesg_count; i++)
437    {
438
439        mesgVal = PyDict_New();
440        Py_XINCREF(mesgVal);
441        switch (mesg[i].type) {
442            case CWIID_MESG_STATUS:
443                PyDict_SetItemString(mesgVal, "battery",
444                    Py_BuildValue("B", mesg[i].status_mesg.battery));
445                switch (mesg[i].status_mesg.ext_type)
446                {
447                    case CWIID_EXT_NONE:
448                        PyDict_SetItemString(mesgVal, "extension",
449                            Py_BuildValue("s","none"));
450                        break;
451                    case CWIID_EXT_NUNCHUK:
452                        PyDict_SetItemString(mesgVal, "extension", 
453                            Py_BuildValue("s","nunchuck"));
454                        break;
455                    case CWIID_EXT_CLASSIC:
456                        PyDict_SetItemString(mesgVal, "extension", 
457                            Py_BuildValue("s","classic"));
458                        break;
459                    default:
460                        break;
461                }
462                break;
463            case CWIID_MESG_BTN:
464                PyDict_SetItemString(mesgVal, "buttons", 
465                    Py_BuildValue("H", mesg[i].btn_mesg.buttons));
466                break;
467            case CWIID_MESG_ACC:
468                PyDict_SetItemString(mesgVal, "x", 
469                    Py_BuildValue("B", mesg[i].acc_mesg.acc[0]));
470                PyDict_SetItemString(mesgVal, "y", 
471                    Py_BuildValue("B", mesg[i].acc_mesg.acc[1]));
472                PyDict_SetItemString(mesgVal, "z", 
473                    Py_BuildValue("B", mesg[i].acc_mesg.acc[2]));
474                break;
475            case CWIID_MESG_IR:
476                break;
477            case CWIID_MESG_NUNCHUK:
478                break;
479            case CWIID_MESG_CLASSIC:
480                break;
481            case CWIID_MESG_ERROR:
482                switch(mesg[i].error_mesg.error)
483                {
484                    case CWIID_ERROR_DISCONNECT:
485                        PyDict_SetItemString(mesgVal, "error",
486                            Py_BuildValue("s", 
487                                "Wiimote was disconnected"));
488                        break;
489                    case CWIID_ERROR_COMM:
490                        PyDict_SetItemString(mesgVal, "error",
491                            Py_BuildValue("s", 
492                                "Communication error occurred"));
493                        break;
494                    default:
495                        PyDict_SetItemString(mesgVal, "error",
496                            Py_BuildValue("s","An Unknown error occurred"));
497                        break;
498                }
499                break;
500            default:
501                PyDict_SetItemString(mesgVal, "error",
502                    Py_BuildValue("s","Unknown message arrived"));
503                break;
504        }
505
506        //Finally Put the type next to the message in a tuple and
507        //append them to the list of messages
508        amesg = Py_BuildValue("(iO)", mesg[i].type, mesgVal);
509        Py_XINCREF(amesg);
510        PyList_Append(mesglist, amesg);
511    }
512
513    return mesglist;
514}