Commits

metagriffin  committed b36d1ac

added optional `timeout` parameter to pygame.event.wait()

  • Participants
  • Parent commits 5223141

Comments (0)

Files changed (3)

File docs/reST/ref/event.rst

 
    | :sl:`wait for a single event from the queue`
    | :sg:`wait() -> EventType instance`
+   | :sg:`wait(timeout) -> EventType instance`
 
    Returns a single event from the queue. If the queue is empty this function
    will wait until one is created. The event is removed from the queue once it
    state. This is important for programs that want to share the system with
    other applications.
 
+   If a parameter is specified, it is taken to be a `timeout` value in
+   milliseconds, which only applies if no event is pending in which case it
+   will wait at most `timeout` milliseconds before returning. If the timeout
+   occurs before an event becomes available, ``None`` is returned. If there is
+   an event pending, then this will return immediately with the event.
+
+   Please note that pygame depends on the SDL library's SDL_WaitEvent
+   implementation; some implementations use a **polling wait**, i.e. a busy
+   loop, with a sleep to temper CPU usage (such as libsdl, which uses a 10 msec
+   sleep).
+
    .. ## pygame.event.wait ##
 
 .. function:: peek

File src/doc/event_doc.h

 
 #define DOC_PYGAMEEVENTPOLL "poll() -> EventType instance\nget a single event from the queue"
 
-#define DOC_PYGAMEEVENTWAIT "wait() -> EventType instance\nwait for a single event from the queue"
+#define DOC_PYGAMEEVENTWAIT "wait() -> EventType instance\nwait(timeout) -> EventType instance\nwait for a single event from the queue (with optional timeout)"
 
 #define DOC_PYGAMEEVENTPEEK "peek(type) -> bool\npeek(typelist) -> bool\ntest if event types are waiting on the queue"
 
  __dict__ -> dict
 Event object attribute dictionary
 
-*/
+*/
     Py_RETURN_NONE;
 }
 
+/*
+ * NOTE: `SDL_WaitEventTimeout` was not added until SDL 1.3, but the
+ * standard SDL in the wild is currently 1.2.15, so providing a back-
+ * ported version here. when 1.3+ becomes standard, revert to using
+ * `SDL_WaitEventTimeout` and remove this.
+ */
+#define BACKPORT_SDL_TICKS_PASSED(A, B)  ((Sint32)((B) - (A)) <= 0)
+static int
+BackPort_SDL_WaitEventTimeout(SDL_Event * event, int timeout)
+{
+    Uint32 expiration = 0;
+    if (timeout > 0)
+        expiration = SDL_GetTicks() + timeout;
+    for (;;) {
+        SDL_PumpEvents();
+        /* switch (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) {*/
+        switch (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS)) {
+        case -1:
+            return 0;
+        case 1:
+            return 1;
+        case 0:
+            if (timeout == 0) {
+                /* Polling and no events, just return */
+                return 0;
+            }
+            if (timeout > 0 && BACKPORT_SDL_TICKS_PASSED(SDL_GetTicks(), expiration)) {
+                /* Timeout expired and no events */
+                return 0;
+            }
+            SDL_Delay(10);
+            break;
+        }
+    }
+}
+
 static PyObject*
 pygame_wait (PyObject* self, PyObject* args)
 {
     SDL_Event event;
     int status;
+    PyObject* arg0;
+    int timeout;
+
+    if (PyTuple_Size (args) != 0 && PyTuple_Size (args) != 1)
+        return RAISE (PyExc_ValueError, "wait([timeout]) requires 0 or 1 argument");
+
+    if (PyTuple_Size (args) != 0)
+    {
+        arg0 = PyTuple_GET_ITEM (args, 0);
+        if (!PyInt_Check (arg0))
+            return RAISE (PyExc_TypeError, "`timeout` must be an integer");
+        timeout = PyInt_AsLong (arg0);
+    }
 
     VIDEO_INIT_CHECK ();
 
     Py_BEGIN_ALLOW_THREADS;
-    status = SDL_WaitEvent (&event);
+    if (PyTuple_Size (args) == 0)
+        status = SDL_WaitEvent (&event);
+    else
+        /*
+         * `SDL_WaitEventTimeout` was not added until SDL 1.3, but the
+         * standard SDL in the wild is currently 1.2.15, so using the
+         * backported version... when 1.3+ becomes standard, revert to
+         * using:
+         *     status = SDL_WaitEventTimeout (&event, timeout);
+         */
+        status = BackPort_SDL_WaitEventTimeout (&event, timeout);
     Py_END_ALLOW_THREADS;
 
     if (!status)
-        return RAISE (PyExc_SDLError, SDL_GetError ());
+    {
+        if (PyTuple_Size (args) == 0)
+            return RAISE (PyExc_SDLError, SDL_GetError ());
+        else
+            Py_RETURN_NONE;
+    }
 
     return PyEvent_New (&event);
 }
     { "get_grab", (PyCFunction) get_grab, METH_NOARGS, DOC_PYGAMEEVENTGETGRAB },
 
     { "pump", (PyCFunction) pygame_pump, METH_NOARGS, DOC_PYGAMEEVENTPUMP },
-    { "wait", (PyCFunction) pygame_wait, METH_NOARGS, DOC_PYGAMEEVENTWAIT },
+    { "wait", pygame_wait, METH_VARARGS, DOC_PYGAMEEVENTWAIT },
     { "poll", (PyCFunction) pygame_poll, METH_NOARGS, DOC_PYGAMEEVENTPOLL },
     { "clear", event_clear, METH_VARARGS, DOC_PYGAMEEVENTCLEAR },
     { "get", event_get, METH_VARARGS, DOC_PYGAMEEVENTGET },