diff --git a/README.md b/README.md
index 0d2d7fa..b076b37 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# RPi WS281x Python
-This is the official Python distribution of the ws281x library: http://github.com/richardghirst/rpi_ws281x
+This is the official Python distribution of the ws281x library: https://github.com/jgarff/rpi_ws281x
# Installing
@@ -11,3 +11,22 @@ Most users should simply run:
```
sudo pip install rpi_ws281x
```
+
+## Building
+
+Clone with submodules, and enter library directory:
+```
+git clone --recurse-submodules https://github.com/rpi-ws281x/rpi-ws281x-python.git
+cd rpi-ws281x-python/library
+```
+To rebuild SWIG files if needed ("black" for code re-formatting only):
+```
+swig -python -threads rpi_ws281x.i
+black rpi_ws281x.py
+```
+
+Build and Install:
+```
+python3 setup.py build
+sudo python3 setup.py install
+```
diff --git a/library/rpi_ws281x.i b/library/rpi_ws281x.i
index f362345..bc73bbb 100644
--- a/library/rpi_ws281x.i
+++ b/library/rpi_ws281x.i
@@ -1,6 +1,8 @@
// SWIG interface file to define rpi_ws281x library python wrapper.
// Author: Tony DiCola (tony@tonydicola.com), Jeremy Garff (jer@jers.net)
+%nothread;
+
// Define module name rpi_ws281x. This will actually be imported under
// the name _rpi_ws281x following the SWIG & Python conventions.
%module rpi_ws281x
@@ -92,3 +94,12 @@ static int convert_iarray(PyObject *input, uint8_t *ptr, int size) {
return &ws->channel[channelnum];
}
%}
+
+%thread;
+%inline %{
+ ws2811_return_t ws2811_render_nogil(ws2811_t *ws2811)
+ {
+ return ws2811_render(ws2811);
+ }
+%}
+%nothread;
diff --git a/library/rpi_ws281x.py b/library/rpi_ws281x.py
index 2761371..4a62b71 100644
--- a/library/rpi_ws281x.py
+++ b/library/rpi_ws281x.py
@@ -221,3 +221,7 @@ def ws2811_led_set(channel, lednum, color):
def ws2811_channel_get(ws, channelnum):
return _rpi_ws281x.ws2811_channel_get(ws, channelnum)
+
+
+def ws2811_render_nogil(ws2811):
+ return _rpi_ws281x.ws2811_render_nogil(ws2811)
diff --git a/library/rpi_ws281x/rpi_ws281x.py b/library/rpi_ws281x/rpi_ws281x.py
index f9d6a8a..b63a2a7 100644
--- a/library/rpi_ws281x/rpi_ws281x.py
+++ b/library/rpi_ws281x/rpi_ws281x.py
@@ -2,7 +2,7 @@
# Author: Tony DiCola (tony@tonydicola.com), Jeremy Garff (jer@jers.net)
import _rpi_ws281x as ws
import atexit
-
+from threading import Lock
class RGBW(int):
def __new__(self, r, g=None, b=None, w=None):
@@ -50,6 +50,9 @@ def __init__(self, num, pin, freq_hz=800000, dma=10, invert=False,
channel, the PWM channel to use (defaults to 0).
"""
+ self._mutex = Lock()
+ self._render_func = ws.ws2811_render
+
if gamma is None:
# Support gamma in place of strip_type for back-compat with
# previous version of forked library
@@ -142,12 +145,17 @@ def begin(self):
str_resp = ws.ws2811_get_return_t_str(resp)
raise RuntimeError('ws2811_init failed with code {0} ({1})'.format(resp, str_resp))
+ def releaseGIL(self, release = True)
+ """Setup option to release GIL during render function."""
+ self._render_func = ws.ws2811_render_nogil if release else ws.ws2811_render
+
def show(self):
"""Update the display with the data from the LED buffer."""
- resp = ws.ws2811_render(self._leds)
- if resp != 0:
- str_resp = ws.ws2811_get_return_t_str(resp)
- raise RuntimeError('ws2811_render failed with code {0} ({1})'.format(resp, str_resp))
+ with self._mutex:
+ resp = self._render_func(self._leds)
+ if resp != 0:
+ str_resp = ws.ws2811_get_return_t_str(resp)
+ raise RuntimeError('ws2811_render failed with code {0} ({1})'.format(resp, str_resp))
def setPixelColor(self, n, color):
"""Set LED at position n to the provided 24-bit color value (in RGB order).
diff --git a/library/rpi_ws281x_wrap.c b/library/rpi_ws281x_wrap.c
index 10959fe..f9bbc61 100644
--- a/library/rpi_ws281x_wrap.c
+++ b/library/rpi_ws281x_wrap.c
@@ -13,6 +13,7 @@
#define SWIGPYTHON
#endif
+#define SWIG_PYTHON_THREADS
#define SWIG_PYTHON_DIRECTOR_NO_VTABLE
/* -----------------------------------------------------------------------------
@@ -3132,6 +3133,12 @@ SWIG_FromCharPtr(const char *cptr)
return &ws->channel[channelnum];
}
+
+ ws2811_return_t ws2811_render_nogil(ws2811_t *ws2811)
+ {
+ return ws2811_render(ws2811);
+ }
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -4370,6 +4377,33 @@ SWIGINTERN PyObject *_wrap_ws2811_channel_get(PyObject *SWIGUNUSEDPARM(self), Py
}
+SWIGINTERN PyObject *_wrap_ws2811_render_nogil(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+ PyObject *resultobj = 0;
+ ws2811_t *arg1 = (ws2811_t *) 0 ;
+ void *argp1 = 0 ;
+ int res1 = 0 ;
+ PyObject *swig_obj[1] ;
+ ws2811_return_t result;
+
+ if (!args) SWIG_fail;
+ swig_obj[0] = args;
+ res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ws2811_t, 0 | 0 );
+ if (!SWIG_IsOK(res1)) {
+ SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ws2811_render_nogil" "', argument " "1"" of type '" "ws2811_t *""'");
+ }
+ arg1 = (ws2811_t *)(argp1);
+ {
+ SWIG_PYTHON_THREAD_BEGIN_ALLOW;
+ result = (ws2811_return_t)ws2811_render_nogil(arg1);
+ SWIG_PYTHON_THREAD_END_ALLOW;
+ }
+ resultobj = SWIG_From_int((int)(result));
+ return resultobj;
+fail:
+ return NULL;
+}
+
+
static PyMethodDef SwigMethods[] = {
{ "SWIG_PyInstanceMethod_New", SWIG_PyInstanceMethod_New, METH_O, NULL},
{ "ws2811_channel_t_gpionum_set", _wrap_ws2811_channel_t_gpionum_set, METH_VARARGS, NULL},
@@ -4423,6 +4457,7 @@ static PyMethodDef SwigMethods[] = {
{ "ws2811_led_get", _wrap_ws2811_led_get, METH_VARARGS, NULL},
{ "ws2811_led_set", _wrap_ws2811_led_set, METH_VARARGS, NULL},
{ "ws2811_channel_get", _wrap_ws2811_channel_get, METH_VARARGS, NULL},
+ { "ws2811_render_nogil", _wrap_ws2811_render_nogil, METH_O, NULL},
{ NULL, NULL, 0, NULL }
};
@@ -5263,6 +5298,9 @@ SWIG_init(void) {
SWIG_Python_SetConstant(d, "WS2811_ERROR_SPI_SETUP",SWIG_From_int((int)(WS2811_ERROR_SPI_SETUP)));
SWIG_Python_SetConstant(d, "WS2811_ERROR_SPI_TRANSFER",SWIG_From_int((int)(WS2811_ERROR_SPI_TRANSFER)));
SWIG_Python_SetConstant(d, "WS2811_RETURN_STATE_COUNT",SWIG_From_int((int)(WS2811_RETURN_STATE_COUNT)));
+
+ /* Initialize threading */
+ SWIG_PYTHON_INITIALIZE_THREADS;
#if PY_VERSION_HEX >= 0x03000000
return m;
#else