aboutsummaryrefslogtreecommitdiffstats
path: root/pugl
diff options
context:
space:
mode:
Diffstat (limited to 'pugl')
-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
5 files changed, 151 insertions, 71 deletions
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