aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-tidy1
-rw-r--r--COPYING2
-rw-r--r--examples/pugl_cairo_demo.c14
-rw-r--r--examples/pugl_embed_demo.c21
-rw-r--r--examples/pugl_gl3_demo.c8
-rw-r--r--examples/pugl_print_events.c5
-rw-r--r--examples/pugl_window_demo.c13
-rw-r--r--pugl/detail/implementation.c14
-rw-r--r--pugl/detail/mac.m51
-rw-r--r--pugl/detail/win.c39
-rw-r--r--pugl/detail/x11.c54
-rw-r--r--pugl/pugl.h64
-rw-r--r--test/test_redisplay.c17
-rw-r--r--test/test_show_hide.c6
-rw-r--r--test/test_update.c124
-rw-r--r--test/test_utils.h4
-rw-r--r--wscript2
17 files changed, 321 insertions, 118 deletions
diff --git a/.clang-tidy b/.clang-tidy
index 0f46134..ee84a8a 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -5,6 +5,7 @@ Checks: >
-android-cloexec-fopen,
-bugprone-suspicious-string-compare,
-clang-analyzer-alpha.*,
+ -clang-analyzer-security.FloatLoopCounter,
-hicpp-multiway-paths-covered,
-hicpp-signed-bitwise,
-llvm-header-guard,
diff --git a/COPYING b/COPYING
index b16a139..4a287b9 100644
--- a/COPYING
+++ b/COPYING
@@ -1,4 +1,4 @@
-Copyright 2011-2019 David Robillard <http://drobilla.net>
+Copyright 2011-2020 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
diff --git a/examples/pugl_cairo_demo.c b/examples/pugl_cairo_demo.c
index d2e57f8..a1423ae 100644
--- a/examples/pugl_cairo_demo.c
+++ b/examples/pugl_cairo_demo.c
@@ -203,6 +203,11 @@ onEvent(PuglView* view, const PuglEvent* event)
app->entered = false;
puglPostRedisplay(view);
break;
+ case PUGL_UPDATE:
+ if (app->opts.continuous) {
+ puglPostRedisplay(view);
+ }
+ break;
case PUGL_EXPOSE:
onDisplay(app, view, &event->expose);
break;
@@ -249,14 +254,9 @@ main(int argc, char** argv)
puglShowWindow(view);
PuglFpsPrinter fpsPrinter = { puglGetTime(app.world) };
+ const double timeout = app.opts.continuous ? (1 / 60.0) : -1.0;
while (!app.quit) {
- if (app.opts.continuous) {
- postButtonRedisplay(view);
- } else {
- puglPollEvents(app.world, -1);
- }
-
- puglDispatchEvents(app.world);
+ puglUpdate(app.world, timeout);
if (app.opts.continuous) {
puglPrintFps(app.world, &fpsPrinter, &app.framesDrawn);
diff --git a/examples/pugl_embed_demo.c b/examples/pugl_embed_demo.c
index 6bafae5..174fd36 100644
--- a/examples/pugl_embed_demo.c
+++ b/examples/pugl_embed_demo.c
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -164,6 +164,11 @@ onParentEvent(PuglView* view, const PuglEvent* event)
reshapeCube((int)event->configure.width, (int)event->configure.height);
puglSetFrame(app->child, getChildFrame(parentFrame));
break;
+ case PUGL_UPDATE:
+ if (app->continuous) {
+ puglPostRedisplay(view);
+ }
+ break;
case PUGL_EXPOSE:
if (puglHasFocus(app->parent)) {
glMatrixMode(GL_MODELVIEW);
@@ -208,6 +213,11 @@ onEvent(PuglView* view, const PuglEvent* event)
case PUGL_CONFIGURE:
reshapeCube((int)event->configure.width, (int)event->configure.height);
break;
+ case PUGL_UPDATE:
+ if (app->continuous) {
+ puglPostRedisplay(view);
+ }
+ break;
case PUGL_EXPOSE:
onDisplay(view);
break;
@@ -317,14 +327,7 @@ main(int argc, char** argv)
while (!app.quit) {
const double thisTime = puglGetTime(app.world);
- if (app.continuous) {
- puglPostRedisplay(app.parent);
- puglPostRedisplay(app.child);
- } else {
- puglPollEvents(app.world, -1);
- }
-
- puglDispatchEvents(app.world);
+ puglUpdate(app.world, app.continuous ? 0.0 : -1.0);
++framesDrawn;
if (!requestedAttention && thisTime > 5.0) {
diff --git a/examples/pugl_gl3_demo.c b/examples/pugl_gl3_demo.c
index e0c63ca..26db852 100644
--- a/examples/pugl_gl3_demo.c
+++ b/examples/pugl_gl3_demo.c
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -177,6 +177,9 @@ onEvent(PuglView* view, const PuglEvent* event)
case PUGL_CONFIGURE:
onConfigure(view, event->configure.width, event->configure.height);
break;
+ case PUGL_UPDATE:
+ puglPostRedisplay(view);
+ break;
case PUGL_EXPOSE: onExpose(view); break;
case PUGL_CLOSE: app->quit = 1; break;
case PUGL_KEY_PRESS:
@@ -411,8 +414,7 @@ main(int argc, char** argv)
// Grind away, drawing continuously
PuglFpsPrinter fpsPrinter = {puglGetTime(app.world)};
while (!app.quit) {
- puglPostRedisplay(app.view);
- puglDispatchEvents(app.world);
+ puglUpdate(app.world, 0.0);
puglPrintFps(app.world, &fpsPrinter, &app.framesDrawn);
}
diff --git a/examples/pugl_print_events.c b/examples/pugl_print_events.c
index c662117..9c81033 100644
--- a/examples/pugl_print_events.c
+++ b/examples/pugl_print_events.c
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -68,8 +68,7 @@ main(void)
puglShowWindow(app.view);
while (!app.quit) {
- puglPollEvents(app.world, -1);
- puglDispatchEvents(app.world);
+ puglUpdate(app.world, -1.0);
}
puglFreeView(app.view);
diff --git a/examples/pugl_window_demo.c b/examples/pugl_window_demo.c
index 394b959..248d44e 100644
--- a/examples/pugl_window_demo.c
+++ b/examples/pugl_window_demo.c
@@ -128,6 +128,9 @@ onEvent(PuglView* view, const PuglEvent* event)
case PUGL_CONFIGURE:
reshapeCube((int)event->configure.width, (int)event->configure.height);
break;
+ case PUGL_UPDATE:
+ puglPostRedisplay(view);
+ break;
case PUGL_EXPOSE:
onDisplay(view);
break;
@@ -224,15 +227,7 @@ main(int argc, char** argv)
PuglFpsPrinter fpsPrinter = {puglGetTime(app.world)};
unsigned framesDrawn = 0;
while (!app.quit) {
- if (app.continuous) {
- for (size_t i = 0; i < 2; ++i) {
- puglPostRedisplay(app.cubes[i].view);
- }
- } else {
- puglPollEvents(app.world, -1);
- }
-
- puglDispatchEvents(app.world);
+ puglUpdate(app.world, app.continuous ? 0.0 : -1.0);
++framesDrawn;
if (app.continuous) {
diff --git a/pugl/detail/implementation.c b/pugl/detail/implementation.c
index b2306c6..d7e7058 100644
--- a/pugl/detail/implementation.c
+++ b/pugl/detail/implementation.c
@@ -290,6 +290,18 @@ puglGetContext(PuglView* view)
#ifndef PUGL_DISABLE_DEPRECATED
PuglStatus
+puglPollEvents(PuglWorld* world, double timeout)
+{
+ return puglUpdate(world, timeout);
+}
+
+PuglStatus
+puglDispatchEvents(PuglWorld* world)
+{
+ return puglUpdate(world, 0.0);
+}
+
+PuglStatus
puglEnterContext(PuglView* view, bool drawing)
{
const PuglEventExpose expose = {
@@ -369,7 +381,7 @@ void
puglDispatchSimpleEvent(PuglView* view, const PuglEventType type)
{
assert(type == PUGL_CREATE || type == PUGL_DESTROY || type == PUGL_MAP ||
- type == PUGL_UNMAP || type == PUGL_CLOSE);
+ type == PUGL_UNMAP || type == PUGL_UPDATE || type == PUGL_CLOSE);
const PuglEvent event = {{type, 0}};
puglDispatchEvent(view, &event);
diff --git a/pugl/detail/mac.m b/pugl/detail/mac.m
index ab59b99..22f8088 100644
--- a/pugl/detail/mac.m
+++ b/pugl/detail/mac.m
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <http://drobilla.net>
Copyright 2017 Hanspeter Portner <dev@open-music-kontrollers.ch>
Permission to use, copy, modify, and/or distribute this software for any
@@ -614,6 +614,12 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
[super viewWillStartLiveResize];
}
+- (void) viewWillDraw
+{
+ puglDispatchSimpleEvent(puglview, PUGL_UPDATE);
+ [super viewWillDraw];
+}
+
- (void) resizeTick
{
puglPostRedisplay(puglview);
@@ -915,27 +921,6 @@ puglRequestAttention(PuglView* view)
return PUGL_SUCCESS;
}
-PuglStatus
-puglPollEvents(PuglWorld* world, const double timeout)
-{
- NSDate* date = ((timeout < 0) ? [NSDate distantFuture] :
- (timeout == 0) ? nil :
- [NSDate dateWithTimeIntervalSinceNow:timeout]);
-
- /* Note that dequeue:NO is broken (it blocks forever even when events are
- pending), so we work around this by dequeueing the event then posting it
- back to the front of the queue. */
- NSEvent* event = [world->impl->app
- nextEventMatchingMask:NSAnyEventMask
- untilDate:date
- inMode:NSDefaultRunLoopMode
- dequeue:YES];
-
- [world->impl->app postEvent:event atStart:true];
-
- return PUGL_SUCCESS;
-}
-
PuglStatus puglSendEvent(PuglView* view, const PuglEvent* event)
{
if (event->type == PUGL_CLIENT) {
@@ -990,28 +975,32 @@ dispatchClientEvent(PuglWorld* world, NSEvent* ev)
}
PuglStatus
-puglDispatchEvents(PuglWorld* world)
+puglUpdate(PuglWorld* world, const double timeout)
{
- const NSTimeInterval startTime = [[NSProcessInfo processInfo] systemUptime];
+ NSDate* date = ((timeout < 0)
+ ? [NSDate distantFuture]
+ : [NSDate dateWithTimeIntervalSinceNow:timeout]);
for (NSEvent* ev = NULL;
(ev = [world->impl->app nextEventMatchingMask:NSAnyEventMask
- untilDate:nil
+ untilDate:date
inMode:NSDefaultRunLoopMode
dequeue:YES]);) {
- if ([ev timestamp] > startTime) {
- // Event is later, put it back for the next iteration and return
- [world->impl->app postEvent:ev atStart:true];
- break;
- } else if ([ev type] == NSApplicationDefined &&
- [ev subtype] == PUGL_CLIENT) {
+ if ([ev type] == NSApplicationDefined && [ev subtype] == PUGL_CLIENT) {
dispatchClientEvent(world, ev);
}
[world->impl->app sendEvent: ev];
}
+ for (size_t i = 0; i < world->numViews; ++i) {
+ PuglView* const view = world->views[i];
+
+ puglDispatchSimpleEvent(view, PUGL_UPDATE);
+ [view->impl->drawView displayIfNeeded];
+ }
+
return PUGL_SUCCESS;
}
diff --git a/pugl/detail/win.c b/pugl/detail/win.c
index bbaa872..22a0a25 100644
--- a/pugl/detail/win.c
+++ b/pugl/detail/win.c
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -145,8 +145,8 @@ puglInitViewInternals(void)
return (PuglInternals*)calloc(1, sizeof(PuglInternals));
}
-PuglStatus
-puglPollEvents(PuglWorld* world, const double timeout)
+static PuglStatus
+puglPollWinEvents(PuglWorld* world, const double timeout)
{
(void)world;
@@ -792,8 +792,8 @@ puglDispatchViewEvents(PuglView* view)
return PUGL_SUCCESS;
}
-PuglStatus
-puglDispatchEvents(PuglWorld* world)
+static PuglStatus
+puglDispatchWinEvents(PuglWorld* world)
{
for (size_t i = 0; i < world->numViews; ++i) {
PostMessage(world->views[i]->impl->hwnd, PUGL_LOCAL_MARK_MSG, 0, 0);
@@ -803,18 +803,43 @@ puglDispatchEvents(PuglWorld* world)
puglDispatchViewEvents(world->views[i]);
}
+ return PUGL_SUCCESS;
+}
+
+PuglStatus
+puglUpdate(PuglWorld* world, double timeout)
+{
+ const double startTime = puglGetTime(world);
+ PuglStatus st = PUGL_SUCCESS;
+
+ if (timeout < 0.0) {
+ st = puglPollWinEvents(world, timeout);
+ st = st ? st : puglDispatchWinEvents(world);
+ } else if (timeout == 0.0) {
+ st = puglDispatchWinEvents(world);
+ } else {
+ const double endTime = startTime + timeout - 0.001;
+ for (double t = startTime; t < endTime; t = puglGetTime(world)) {
+ if ((st = puglPollWinEvents(world, endTime - t)) ||
+ (st = puglDispatchWinEvents(world))) {
+ break;
+ }
+ }
+ }
+
for (size_t i = 0; i < world->numViews; ++i) {
+ puglDispatchSimpleEvent(world->views[i], PUGL_UPDATE);
UpdateWindow(world->views[i]->impl->hwnd);
}
- return PUGL_SUCCESS;
+ return st;
}
#ifndef PUGL_DISABLE_DEPRECATED
PuglStatus
puglProcessEvents(PuglView* view)
{
- return puglDispatchEvents(view->world);
+ return puglUpdate(view->world, 0.0);
}
#endif
diff --git a/pugl/detail/x11.c b/pugl/detail/x11.c
index 780213d..58451e9 100644
--- a/pugl/detail/x11.c
+++ b/pugl/detail/x11.c
@@ -117,8 +117,8 @@ puglInitViewInternals(void)
return (PuglInternals*)calloc(1, sizeof(PuglInternals));
}
-PuglStatus
-puglPollEvents(PuglWorld* world, const double timeout)
+static PuglStatus
+puglPollX11Socket(PuglWorld* world, const double timeout)
{
if (XPending(world->impl->display) > 0) {
return PUGL_SUCCESS;
@@ -140,8 +140,7 @@ puglPollEvents(PuglWorld* world, const double timeout)
ret = select(nfds, &fds, NULL, NULL, &tv);
}
- return ret < 0 ? PUGL_UNKNOWN_ERROR
- : ret == 0 ? PUGL_FAILURE : PUGL_SUCCESS;
+ return ret < 0 ? PUGL_UNKNOWN_ERROR : PUGL_SUCCESS;
}
static PuglView*
@@ -750,6 +749,8 @@ flushExposures(PuglWorld* world)
PuglEvent* const configure = &view->impl->pendingConfigure;
PuglEvent* const expose = &view->impl->pendingExpose;
+ puglDispatchSimpleEvent(view, PUGL_UPDATE);
+
if (configure->type || expose->type) {
view->backend->enter(view, expose->type ? &expose->expose : NULL);
view->eventFunc(view, configure);
@@ -762,8 +763,8 @@ flushExposures(PuglWorld* world)
}
}
-PuglStatus
-puglDispatchEvents(PuglWorld* world)
+static PuglStatus
+puglDispatchX11Events(PuglWorld* world)
{
const PuglX11Atoms* const atoms = &world->impl->atoms;
@@ -771,8 +772,6 @@ puglDispatchEvents(PuglWorld* world)
Display* display = world->impl->display;
XFlush(display);
- world->impl->dispatchingEvents = true;
-
// Process all queued events (without further flushing)
while (XEventsQueued(display, QueuedAfterReading) > 0) {
XEvent xevent;
@@ -827,10 +826,6 @@ puglDispatchEvents(PuglWorld* world)
}
}
- flushExposures(world);
-
- world->impl->dispatchingEvents = false;
-
return PUGL_SUCCESS;
}
@@ -838,10 +833,40 @@ puglDispatchEvents(PuglWorld* world)
PuglStatus
puglProcessEvents(PuglView* view)
{
- return puglDispatchEvents(view->world);
+ return puglUpdate(view->world, 0.0);
}
#endif
+PuglStatus
+puglUpdate(PuglWorld* world, double timeout)
+{
+ const double startTime = puglGetTime(world);
+ PuglStatus st = PUGL_SUCCESS;
+
+ world->impl->dispatchingEvents = true;
+
+ if (timeout < 0.0) {
+ st = puglPollX11Socket(world, timeout);
+ st = st ? st : puglDispatchX11Events(world);
+ } else if (timeout == 0.0) {
+ st = puglDispatchX11Events(world);
+ } else {
+ const double endTime = startTime + timeout - 0.001;
+ for (double t = startTime; t < endTime; t = puglGetTime(world)) {
+ if ((st = puglPollX11Socket(world, endTime - t)) ||
+ (st = puglDispatchX11Events(world))) {
+ break;
+ }
+ }
+ }
+
+ flushExposures(world);
+
+ world->impl->dispatchingEvents = false;
+
+ return st;
+}
+
double
puglGetTime(const PuglWorld* world)
{
@@ -988,8 +1013,7 @@ puglGetClipboard(PuglView* const view,
// Run event loop until data is received
while (!view->clipboard.data) {
- puglPollEvents(view->world, -1);
- puglDispatchEvents(view->world);
+ puglUpdate(view->world, -1.0);
}
}
diff --git a/pugl/pugl.h b/pugl/pugl.h
index d215749..85d0e8f 100644
--- a/pugl/pugl.h
+++ b/pugl/pugl.h
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -172,6 +172,7 @@ typedef enum {
PUGL_DESTROY, ///< View destroyed, a #PuglEventAny
PUGL_MAP, ///< View made visible, a #PuglEventAny
PUGL_UNMAP, ///< View made invisible, a #PuglEventAny
+ PUGL_UPDATE, ///< View ready to draw, a #PuglEventAny
PUGL_CONFIGURE, ///< View moved/resized, a #PuglEventConfigure
PUGL_EXPOSE, ///< View must be drawn, a #PuglEventExpose
PUGL_CLOSE, ///< View will be closed, a #PuglEventAny
@@ -620,12 +621,21 @@ PUGL_API double
puglGetTime(const PuglWorld* world);
/**
- Poll for events that are ready to be processed.
+ Update by processing events from the window system.
- This polls for events that are ready for any view in the world, potentially
- blocking depending on `timeout`.
+ This function is a single iteration of the main loop, and should be called
+ repeatedly to update all views.
- @param world The world to poll for events.
+ If a positive timeout is given, then events will be processed for that
+ amount of time, starting from when this function was called. For purely
+ event-driven programs, a timeout of -1.0 can be used to block indefinitely
+ until something happens. For continuously animating programs, a timeout
+ that is a reasonable fraction of the ideal frame period should be used, to
+ minimise input latency by ensuring that as many input events are consumed as
+ possible before drawing. Plugins should always use a timeout of 0.0 to
+ avoid blocking the host.
+
+ @param world The world to update.
@param timeout Maximum time to wait, in seconds. If zero, the call returns
immediately, if negative, the call blocks indefinitely.
@@ -633,18 +643,7 @@ puglGetTime(const PuglWorld* world);
@return #PUGL_SUCCESS if events are read, #PUGL_FAILURE if not, or an error.
*/
PUGL_API PuglStatus
-puglPollEvents(PuglWorld* world, double timeout);
-
-/**
- Dispatch any pending events to views.
-
- This processes all pending events, dispatching them to the appropriate
- views. View event handlers will be called in the scope of this call. This
- function does not block, if no events are pending then it will return
- immediately.
-*/
-PUGL_API PuglStatus
-puglDispatchEvents(PuglWorld* world);
+puglUpdate(PuglWorld* world, double timeout);
/**
@}
@@ -1263,6 +1262,37 @@ PUGL_API PUGL_DEPRECATED_BY("puglDispatchEvents") PuglStatus
puglProcessEvents(PuglView* view);
/**
+ Poll for events that are ready to be processed.
+
+ This polls for events that are ready for any view in the world, potentially
+ blocking depending on `timeout`.
+
+ @param world The world to poll for events.
+
+ @param timeout Maximum time to wait, in seconds. If zero, the call returns
+ immediately, if negative, the call blocks indefinitely.
+
+ @return #PUGL_SUCCESS if events are read, #PUGL_FAILURE if not, or an error.
+
+ @deprecated Use puglUpdate().
+*/
+PUGL_API PUGL_DEPRECATED_BY("puglUpdate") PuglStatus
+puglPollEvents(PuglWorld* world, double timeout);
+
+/**
+ Dispatch any pending events to views.
+
+ This processes all pending events, dispatching them to the appropriate
+ views. View event handlers will be called in the scope of this call. This
+ function does not block, if no events are pending then it will return
+ immediately.
+
+ @deprecated Use puglUpdate().
+*/
+PUGL_API PUGL_DEPRECATED_BY("puglUpdate") PuglStatus
+puglDispatchEvents(PuglWorld* world);
+
+/**
Enter the graphics context.
Note that, unlike some similar libraries, Pugl automatically enters and
diff --git a/test/test_redisplay.c b/test/test_redisplay.c
index 4470053..6ec5bb8 100644
--- a/test/test_redisplay.c
+++ b/test/test_redisplay.c
@@ -31,6 +31,12 @@
#include <stddef.h>
#include <stdint.h>
+#ifdef __APPLE__
+static const double timeout = 1 / 60.0;
+#else
+static const double timeout = -1.0;
+#endif
+
typedef enum {
START,
EXPOSED,
@@ -85,13 +91,6 @@ onEvent(PuglView* view, const PuglEvent* event)
return PUGL_SUCCESS;
}
-static void
-tick(PuglWorld* world)
-{
- assert(!puglPollEvents(world, -1));
- assert(!puglDispatchEvents(world));
-}
-
int
main(int argc, char** argv)
{
@@ -111,7 +110,7 @@ main(int argc, char** argv)
assert(!puglCreateWindow(app.view, "Pugl Test"));
assert(!puglShowWindow(app.view));
while (app.state != EXPOSED) {
- tick(app.world);
+ assert(!puglUpdate(app.world, timeout));
}
// Send a custom event to trigger a redisplay in the event loop
@@ -121,7 +120,7 @@ main(int argc, char** argv)
// Loop until an expose happens in the same iteration as the redisplay
app.state = SHOULD_REDISPLAY;
while (app.state != REDISPLAYED) {
- tick(app.world);
+ assert(!puglUpdate(app.world, timeout));
assert(app.state != POSTED_REDISPLAY);
}
diff --git a/test/test_show_hide.c b/test/test_show_hide.c
index b942f45..ed5ede7 100644
--- a/test/test_show_hide.c
+++ b/test/test_show_hide.c
@@ -95,12 +95,10 @@ tick(PuglWorld* world)
#ifdef __APPLE__
// FIXME: Expose events are not events on MacOS, so we can't block
// indefinitely here since it will block forever
- assert(!puglPollEvents(world, 1 / 30.0));
+ assert(!puglUpdate(world, 1 / 30.0));
#else
- assert(!puglPollEvents(world, -1));
+ assert(!puglUpdate(world, -1));
#endif
-
- assert(!puglDispatchEvents(world));
}
int
diff --git a/test/test_update.c b/test/test_update.c
new file mode 100644
index 0000000..bbda94a
--- /dev/null
+++ b/test/test_update.c
@@ -0,0 +1,124 @@
+/*
+ Copyright 2020 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ Tests that redisplays posted in the event handler are dispatched at the end
+ of the same event loop iteration.
+*/
+
+#undef NDEBUG
+
+#include "test_utils.h"
+
+#include "pugl/pugl.h"
+#include "pugl/pugl_stub.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#ifdef __APPLE__
+static const double timeout = 1 / 60.0;
+#else
+static const double timeout = -1.0;
+#endif
+
+typedef enum {
+ START,
+ EXPOSED1,
+ UPDATED,
+ EXPOSED2,
+} State;
+
+typedef struct {
+ PuglTestOptions opts;
+ PuglWorld* world;
+ PuglView* view;
+ State state;
+} PuglTest;
+
+static PuglStatus
+onEvent(PuglView* view, const PuglEvent* event)
+{
+ PuglTest* test = (PuglTest*)puglGetHandle(view);
+
+ if (test->opts.verbose) {
+ printEvent(event, "Event: ", true);
+ }
+
+ switch (event->type) {
+ case PUGL_EXPOSE:
+ switch (test->state) {
+ case START:
+ test->state = EXPOSED1;
+ break;
+ case UPDATED:
+ test->state = EXPOSED2;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case PUGL_UPDATE:
+ if (test->state == EXPOSED1) {
+ puglPostRedisplay(view);
+ test->state = UPDATED;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return PUGL_SUCCESS;
+}
+
+int
+main(int argc, char** argv)
+{
+ PuglTest app = {puglParseTestOptions(&argc, &argv),
+ puglNewWorld(PUGL_PROGRAM, 0),
+ NULL,
+ START};
+
+ // Set up view
+ app.view = puglNewView(app.world);
+ puglSetClassName(app.world, "Pugl Test");
+ puglSetBackend(app.view, puglStubBackend());
+ puglSetHandle(app.view, &app);
+ puglSetEventFunc(app.view, onEvent);
+
+ // Create and show window
+ assert(!puglCreateWindow(app.view, "Pugl Test"));
+ assert(!puglShowWindow(app.view));
+
+ // Tick until an expose happens
+ while (app.state <= EXPOSED1) {
+ assert(!puglUpdate(app.world, timeout));
+ assert(app.state != UPDATED);
+ }
+
+ // Tick once and ensure the update and the expose it posted both happened
+ assert(!puglUpdate(app.world, 0.0));
+ assert(app.state == EXPOSED2);
+
+ // Tear down
+ puglFreeView(app.view);
+ puglFreeWorld(app.world);
+
+ return 0;
+}
diff --git a/test/test_utils.h b/test/test_utils.h
index 7dc6e6e..3e62714 100644
--- a/test/test_utils.h
+++ b/test/test_utils.h
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -140,6 +140,8 @@ printEvent(const PuglEvent* event, const char* prefix, const bool verbose)
return fprintf(stderr, "%sMap\n", prefix);
case PUGL_UNMAP:
return fprintf(stderr, "%sUnmap\n", prefix);
+ case PUGL_UPDATE:
+ return fprintf(stderr, "%sUpdate\n", prefix);
case PUGL_CONFIGURE:
return PRINT("%sConfigure " PFMT " " PFMT "\n",
prefix,
diff --git a/wscript b/wscript
index 7d6d36c..5e2b833 100644
--- a/wscript
+++ b/wscript
@@ -172,7 +172,7 @@ def _build_pc_file(bld, name, desc, target, libname, deps={}, requires=[]):
LIBS=' '.join(link_flags))
-tests = ['redisplay', 'show_hide']
+tests = ['redisplay', 'show_hide', 'update']
def build(bld):