diff options
author | David Robillard <d@drobilla.net> | 2025-01-23 17:00:37 -0500 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2025-01-23 18:02:52 -0500 |
commit | ef551ed4b85bc29b3eb48775faeadcf41596c40a (patch) | |
tree | 9ce41788cd4c06b9e9e3ce3f6fbfa01cd4931e8b | |
parent | d7a93950b3af397580572adf2e366f3b162104a9 (diff) | |
download | pugl-ef551ed4b85bc29b3eb48775faeadcf41596c40a.tar.gz pugl-ef551ed4b85bc29b3eb48775faeadcf41596c40a.tar.bz2 pugl-ef551ed4b85bc29b3eb48775faeadcf41596c40a.zip |
Replace puglPostRedisplayRect() with puglObscureRegion()
-rw-r--r-- | bindings/cpp/include/pugl/pugl.hpp | 9 | ||||
-rw-r--r-- | doc/c/event-loop.rst | 2 | ||||
-rw-r--r-- | examples/pugl_cairo_demo.c | 43 | ||||
-rw-r--r-- | include/pugl/pugl.h | 42 | ||||
-rw-r--r-- | meson.build | 2 | ||||
-rw-r--r-- | src/mac.m | 30 | ||||
-rw-r--r-- | src/win.c | 20 | ||||
-rw-r--r-- | src/x11.c | 25 | ||||
-rw-r--r-- | test/test_redisplay.c | 22 |
9 files changed, 129 insertions, 66 deletions
diff --git a/bindings/cpp/include/pugl/pugl.hpp b/bindings/cpp/include/pugl/pugl.hpp index c74962b..bf87ed3 100644 --- a/bindings/cpp/include/pugl/pugl.hpp +++ b/bindings/cpp/include/pugl/pugl.hpp @@ -615,10 +615,13 @@ public: return static_cast<Status>(puglObscureView(cobj())); } - /// @copydoc puglPostRedisplayRect - Status postRedisplayRect(const Rect& rect) noexcept + /// "Obscure" a region so it will be exposed in the next render + Status obscure(const int x, + const int y, + const unsigned width, + const unsigned height) { - return static_cast<Status>(puglPostRedisplayRect(cobj(), rect)); + return static_cast<Status>(puglObscureRegion(cobj(), x, y, width, height)); } /** diff --git a/doc/c/event-loop.rst b/doc/c/event-loop.rst index 430566f..7bee397 100644 --- a/doc/c/event-loop.rst +++ b/doc/c/event-loop.rst @@ -23,7 +23,7 @@ while those that draw continuously may use a significant fraction of the frame p Redrawing ********* -Occasional redrawing can be requested by calling :func:`puglObscureView` or :func:`puglPostRedisplayRect`. +Occasional redrawing can be requested by calling :func:`puglObscureView` or :func:`puglObscureRegion`. After these are called, a :struct:`PuglExposeEvent` will be dispatched on the next call to :func:`puglUpdate`. diff --git a/examples/pugl_cairo_demo.c b/examples/pugl_cairo_demo.c index 2859ff0..d973d69 100644 --- a/examples/pugl_cairo_demo.c +++ b/examples/pugl_cairo_demo.c @@ -114,13 +114,12 @@ postButtonRedisplay(PuglView* view) const ViewScale scale = getScale(view); for (const Button* b = buttons; b->label; ++b) { - const double span = sqrt(b->w * b->w + b->h * b->h); - const PuglRect rect = {(PuglCoord)((b->x - span) * scale.x), - (PuglCoord)((b->y - span) * scale.y), - (PuglSpan)ceil(span * 2.0 * scale.x), - (PuglSpan)ceil(span * 2.0 * scale.y)}; - - puglPostRedisplayRect(view, rect); + const double span = sqrt(b->w * b->w + b->h * b->h); + puglObscureRegion(view, + (int)((b->x - span) * scale.x), + (int)((b->y - span) * scale.y), + (unsigned)ceil(span * 2.0 * scale.x), + (unsigned)ceil(span * 2.0 * scale.y)); } } @@ -172,18 +171,17 @@ onClose(PuglView* view) app->quit = 1; } -static PuglRect -mouseCursorViewBounds(const PuglView* const view, - const double mouseX, - const double mouseY) +static PuglStatus +obscureMouseCursor(PuglView* const view, + const ViewScale scale, + const double mouseX, + const double mouseY) { - const ViewScale scale = getScale(view); - const PuglRect rect = {(PuglCoord)floor(mouseX - (10.0 * scale.x)), - (PuglCoord)floor(mouseY - (10.0 * scale.y)), - (PuglSpan)ceil(20.0 * scale.x), - (PuglSpan)ceil(20.0 * scale.y)}; - - return rect; + return puglObscureRegion(view, + (int)floor(mouseX - (10.0 * scale.x)), + (int)floor(mouseY - (10.0 * scale.y)), + (unsigned)ceil(20.0 * scale.x), + (unsigned)ceil(20.0 * scale.y)); } static PuglStatus @@ -193,6 +191,7 @@ onEvent(PuglView* view, const PuglEvent* event) printEvent(event, "Event: ", app->opts.verbose); + const ViewScale scale = getScale(view); switch (event->type) { case PUGL_KEY_PRESS: if (event->key.key == 'q' || event->key.key == PUGL_KEY_ESCAPE) { @@ -209,16 +208,12 @@ onEvent(PuglView* view, const PuglEvent* event) break; case PUGL_MOTION: // Redisplay to clear the old cursor position - puglPostRedisplayRect( - view, - mouseCursorViewBounds(view, app->lastDrawnMouseX, app->lastDrawnMouseY)); + obscureMouseCursor(view, scale, app->lastDrawnMouseX, app->lastDrawnMouseY); // Redisplay to show the new cursor position app->currentMouseX = event->motion.x; app->currentMouseY = event->motion.y; - puglPostRedisplayRect( - view, - mouseCursorViewBounds(view, app->currentMouseX, app->currentMouseY)); + obscureMouseCursor(view, scale, app->currentMouseX, app->currentMouseY); app->lastDrawnMouseX = app->currentMouseX; app->lastDrawnMouseY = app->currentMouseY; diff --git a/include/pugl/pugl.h b/include/pugl/pugl.h index 49e5fe5..d7366c6 100644 --- a/include/pugl/pugl.h +++ b/include/pugl/pugl.h @@ -298,7 +298,7 @@ typedef PuglAnyEvent PuglCloseEvent; This event is sent to every view near the end of a main loop iteration when any pending exposures are about to be redrawn. It is typically used to mark - regions to expose with puglObscureView() or puglPostRedisplayRect(). For + regions to expose with puglObscureView() or puglObscureRegion(). For example, to continuously animate, obscure the view when an update event is received, and it will receive an expose event shortly afterwards. */ @@ -1425,14 +1425,29 @@ PuglStatus puglObscureView(PuglView* view); /** - Request a redisplay of the given rectangle within the view. + "Obscure" a region so it will be exposed in the next render. + + This will cause an expose event to be dispatched later. If called from + within the event handler, the expose should arrive at the end of the current + event loop iteration, though this is not strictly guaranteed on all + platforms. If called elsewhere, an expose will be enqueued to be processed + in the next event loop iteration. + + The region is clamped to the size of the view if necessary. - This has the same semantics as puglObscureView(), but allows giving a precise - region for redrawing only a portion of the view. + @param view The view to expose later. + @param x The top-left X coordinate of the rectangle to obscure. + @param y The top-left Y coordinate of the rectangle to obscure. + @param width The width of the rectangle to obscure. + @param height The height coordinate of the rectangle to obscure. */ PUGL_API PuglStatus -puglPostRedisplayRect(PuglView* view, PuglRect rect); +puglObscureRegion(PuglView* view, + int x, + int y, + unsigned width, + unsigned height); /** @} @@ -1634,8 +1649,8 @@ puglStopTimer(PuglView* view, uintptr_t id); Currently, only #PUGL_CLIENT events are supported on all platforms. X11: A #PUGL_EXPOSE event can be sent, which is similar to calling - puglPostRedisplayRect(), but will always send a message to the X server, - even when called in an event handler. + puglObscureRegion(), but will always send a message to the X server, even + when called in an event handler. @return #PUGL_UNSUPPORTED if sending events of this type is not supported, #PUGL_UNKNOWN_ERROR if sending the event failed. @@ -2210,6 +2225,19 @@ puglPostRedisplay(PuglView* view) return puglObscureView(view); } +/** + Request a redisplay of the given rectangle within the view. + + This has the same semantics as puglPostRedisplay(), but allows giving a + precise region for redrawing only a portion of the view. +*/ +static inline PUGL_DEPRECATED_BY("puglObscureRegion") +PuglStatus +puglPostRedisplayRect(PuglView* view, PuglRect rect) +{ + return puglObscureRegion(view, rect.x, rect.y, rect.width, rect.height); +} + #endif // PUGL_DISABLE_DEPRECATED /** diff --git a/meson.build b/meson.build index ec58fb8..1c2d8ca 100644 --- a/meson.build +++ b/meson.build @@ -12,7 +12,7 @@ project( ], license: 'ISC', meson_version: '>= 0.54.0', - version: '0.5.3', + version: '0.5.5', ) pugl_src_root = meson.current_source_dir() @@ -7,6 +7,7 @@ #include "mac.h" #include "internal.h" +#include "macros.h" #include "platform.h" #include "pugl/pugl.h" @@ -1651,15 +1652,30 @@ puglObscureView(PuglView* view) } PuglStatus -puglPostRedisplayRect(PuglView* view, const PuglRect rect) +puglObscureRegion(PuglView* view, + const int x, + const int y, + const unsigned width, + const unsigned height) { - const NSRect rectPx = { - {(double)rect.x, - (double)view->lastConfigure.height - (rect.y + rect.height)}, - {(double)rect.width, (double)rect.height}, - }; + if (!puglIsValidPosition(x, y) || !puglIsValidSize(width, height)) { + return PUGL_BAD_PARAMETER; + } + + const PuglSpan viewHeight = view->lastConfigure.height; + + const int cx = MAX(0, x); + const int cy = MAX(0, viewHeight - y - (int)height); + const unsigned cw = MIN(view->lastConfigure.width, width); + const unsigned ch = MIN(view->lastConfigure.height, height); - [view->impl->drawView setNeedsDisplayInRect:nsRectToPoints(view, rectPx)]; + if (cw == view->lastConfigure.width && ch == view->lastConfigure.height) { + [view->impl->drawView setNeedsDisplay:YES]; + } else { + const NSRect rectPx = NSMakeRect(cx, cy, cw, ch); + + [view->impl->drawView setNeedsDisplayInRect:nsRectToPoints(view, rectPx)]; + } return PUGL_SUCCESS; } @@ -4,6 +4,7 @@ #include "win.h" #include "internal.h" +#include "macros.h" #include "platform.h" #include "pugl/pugl.h" @@ -1216,13 +1217,22 @@ puglObscureView(PuglView* view) } PuglStatus -puglPostRedisplayRect(PuglView* view, const PuglRect rect) +puglObscureRegion(PuglView* const view, + const int x, + const int y, + const unsigned width, + const unsigned height) { - const RECT r = {(long)floor(rect.x), - (long)floor(rect.y), - (long)ceil(rect.x + rect.width), - (long)ceil(rect.y + rect.height)}; + if (!puglIsValidPosition(x, y) || !puglIsValidSize(width, height)) { + return PUGL_BAD_PARAMETER; + } + + const int cx = MAX(0, x); + const int cy = MAX(0, y); + const unsigned cw = MIN(view->lastConfigure.width, width); + const unsigned ch = MIN(view->lastConfigure.height, height); + const RECT r = {cx, cy, cx + (long)cw, cy + (long)ch}; InvalidateRect(view->impl->hwnd, &r, false); return PUGL_SUCCESS; @@ -1879,18 +1879,27 @@ puglGetTime(const PuglWorld* const world) PuglStatus puglObscureView(PuglView* const view) { - PuglRect rect = puglGetFrame(view); - rect.x = 0; - rect.y = 0; - - return puglPostRedisplayRect(view, rect); + return puglObscureRegion( + view, 0, 0, view->lastConfigure.width, view->lastConfigure.height); } PuglStatus -puglPostRedisplayRect(PuglView* const view, const PuglRect rect) +puglObscureRegion(PuglView* const view, + const int x, + const int y, + const unsigned width, + const unsigned height) { - const PuglExposeEvent event = { - PUGL_EXPOSE, 0, rect.x, rect.y, rect.width, rect.height}; + if (!puglIsValidPosition(x, y) || !puglIsValidSize(width, height)) { + return PUGL_BAD_PARAMETER; + } + + const PuglCoord cx = MAX((PuglCoord)0, (PuglCoord)x); + const PuglCoord cy = MAX((PuglCoord)0, (PuglCoord)y); + const PuglSpan cw = MIN(view->lastConfigure.width, (PuglSpan)width); + const PuglSpan ch = MIN(view->lastConfigure.height, (PuglSpan)height); + + const PuglExposeEvent event = {PUGL_EXPOSE, 0, cx, cy, cw, ch}; if (view->world->impl->dispatchingEvents) { // Currently dispatching events, add/expand expose for the loop end diff --git a/test/test_redisplay.c b/test/test_redisplay.c index f17e4c7..ae7267d 100644 --- a/test/test_redisplay.c +++ b/test/test_redisplay.c @@ -51,8 +51,11 @@ typedef struct { State state; } PuglTest; -static const PuglRect redisplayRect = {2, 4, 8, 16}; -static const uintptr_t postRedisplayId = 42; +static const PuglCoord obscureX = 2; +static const PuglCoord obscureY = 4; +static const PuglSpan obscureWidth = 8; +static const PuglSpan obscureHeight = 16; +static const uintptr_t obscureId = 42; static PuglStatus onEvent(PuglView* view, const PuglEvent* event) @@ -67,7 +70,7 @@ onEvent(PuglView* view, const PuglEvent* event) switch (event->type) { case PUGL_UPDATE: if (test->state == SHOULD_REDISPLAY) { - puglPostRedisplayRect(view, redisplayRect); + puglObscureRegion(view, obscureX, obscureY, obscureWidth, obscureHeight); test->state = POSTED_REDISPLAY; } break; @@ -75,13 +78,12 @@ onEvent(PuglView* view, const PuglEvent* event) case PUGL_EXPOSE: if (test->state == START) { test->state = EXPOSED; - } else if (test->state == POSTED_REDISPLAY && - event->expose.x <= redisplayRect.x && - event->expose.y <= redisplayRect.y && + } else if (test->state == POSTED_REDISPLAY && event->expose.x <= obscureX && + event->expose.y <= obscureY && (event->expose.x + event->expose.width >= - redisplayRect.x + redisplayRect.width) && + obscureX + obscureWidth) && (event->expose.y + event->expose.height >= - redisplayRect.y + redisplayRect.height)) { + obscureY + obscureHeight)) { test->state = REDISPLAYED; } else if (test->state == REDISPLAYED) { test->state = REREDISPLAYED; @@ -89,7 +91,7 @@ onEvent(PuglView* view, const PuglEvent* event) break; case PUGL_CLIENT: - if (event->client.data1 == postRedisplayId) { + if (event->client.data1 == obscureId) { test->state = SHOULD_REDISPLAY; } break; @@ -128,7 +130,7 @@ main(int argc, char** argv) // Send a custom event to trigger a redisplay in the event loop PuglEvent client_event = {{PUGL_CLIENT, 0}}; - client_event.client.data1 = postRedisplayId; + client_event.client.data1 = obscureId; client_event.client.data2 = 0; assert(!puglSendEvent(test.view, &client_event)); |