diff options
-rw-r--r-- | .clant.json | 4 | ||||
-rw-r--r-- | bindings/cpp/include/pugl/pugl.hpp | 15 | ||||
-rw-r--r-- | doc/c/event-loop.rst | 5 | ||||
-rw-r--r-- | doc/cpp/event-loop.rst | 5 | ||||
-rw-r--r-- | examples/pugl_cairo_demo.c | 49 | ||||
-rw-r--r-- | examples/pugl_clipboard_demo.c | 4 | ||||
-rw-r--r-- | examples/pugl_cpp_demo.cpp | 4 | ||||
-rw-r--r-- | examples/pugl_embed_demo.c | 14 | ||||
-rw-r--r-- | examples/pugl_management_demo.c | 26 | ||||
-rw-r--r-- | examples/pugl_shader_demo.c | 4 | ||||
-rw-r--r-- | examples/pugl_vulkan_cpp_demo.cpp | 4 | ||||
-rw-r--r-- | examples/pugl_vulkan_demo.c | 2 | ||||
-rw-r--r-- | examples/pugl_window_demo.c | 4 | ||||
-rw-r--r-- | include/pugl/pugl.h | 64 | ||||
-rw-r--r-- | meson.build | 2 | ||||
-rw-r--r-- | src/internal.c | 47 | ||||
-rw-r--r-- | src/internal.h | 12 | ||||
-rw-r--r-- | src/mac.m | 83 | ||||
-rw-r--r-- | src/win.c | 83 | ||||
-rw-r--r-- | src/x11.c | 120 | ||||
-rw-r--r-- | test/test_redisplay.c | 24 | ||||
-rw-r--r-- | test/test_update.c | 2 |
22 files changed, 328 insertions, 249 deletions
diff --git a/.clant.json b/.clant.json index cb178c7..27ce960 100644 --- a/.clant.json +++ b/.clant.json @@ -1,9 +1,5 @@ { "version": "1.0.0", - "include_dirs": [ - "bindings/cpp/include", - "include" - ], "exclude_patterns": [ "glad\\.c" ], diff --git a/bindings/cpp/include/pugl/pugl.hpp b/bindings/cpp/include/pugl/pugl.hpp index a4ee273..bf87ed3 100644 --- a/bindings/cpp/include/pugl/pugl.hpp +++ b/bindings/cpp/include/pugl/pugl.hpp @@ -609,16 +609,19 @@ public: /// @copydoc puglGetContext void* context() noexcept { return puglGetContext(cobj()); } - /// @copydoc puglPostRedisplay - Status postRedisplay() noexcept + /// @copydoc puglObscure + Status obscure() noexcept { - return static_cast<Status>(puglPostRedisplay(cobj())); + 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 be4e315..7bee397 100644 --- a/doc/c/event-loop.rst +++ b/doc/c/event-loop.rst @@ -23,12 +23,11 @@ while those that draw continuously may use a significant fraction of the frame p Redrawing ********* -Occasional redrawing can be requested by calling :func:`puglPostRedisplay` 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`. -For continuous redrawing, -call :func:`puglPostRedisplay` while handling a :struct:`PuglUpdateEvent` event. +For continuous redrawing, obscure the view while handling a :struct:`PuglUpdateEvent` event. This event is sent just before views are redrawn, so it can be used as a hook to expand the update region right before the view is exposed. Anything else that needs to be done every frame can be handled similarly. diff --git a/doc/cpp/event-loop.rst b/doc/cpp/event-loop.rst index 1d2ac41..3223069 100644 --- a/doc/cpp/event-loop.rst +++ b/doc/cpp/event-loop.rst @@ -24,14 +24,13 @@ while those that draw continuously may use a significant fraction of the frame p Redrawing ********* -Occasional redrawing can be requested by calling :func:`View::postRedisplay` or :func:`View::postRedisplayRect`. +Occasional redrawing can be requested by calling :func:`View::obscure`. After these are called, a :type:`ExposeEvent` will be dispatched on the next call to :func:`World::update`. Note, however, that this will not wake up a blocked :func:`World::update` call on MacOS (which does not handle drawing via events). -For continuous redrawing, -call :func:`View::postRedisplay` while handling a :type:`UpdateEvent`. +For continuous redrawing, obscure the view while handling a :type:`UpdateEvent`. This event is sent just before views are redrawn, so it can be used as a hook to expand the update region right before the view is exposed. Anything else that needs to be done every frame can be handled similarly. diff --git a/examples/pugl_cairo_demo.c b/examples/pugl_cairo_demo.c index 6608bab..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,31 +208,27 @@ 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; break; case PUGL_POINTER_IN: app->entered = true; - puglPostRedisplay(view); + puglObscureView(view); break; case PUGL_POINTER_OUT: app->entered = false; - puglPostRedisplay(view); + puglObscureView(view); break; case PUGL_UPDATE: if (app->opts.continuous) { - puglPostRedisplay(view); + puglObscureView(view); } break; case PUGL_EXPOSE: diff --git a/examples/pugl_clipboard_demo.c b/examples/pugl_clipboard_demo.c index efeb1eb..78ea4e8 100644 --- a/examples/pugl_clipboard_demo.c +++ b/examples/pugl_clipboard_demo.c @@ -116,7 +116,7 @@ static void redisplayView(PuglTestApp* app, PuglView* view) { if (!app->continuous) { - puglPostRedisplay(view); + puglObscureView(view); } } @@ -135,7 +135,7 @@ onEvent(PuglView* view, const PuglEvent* event) break; case PUGL_UPDATE: if (app->continuous) { - puglPostRedisplay(view); + puglObscureView(view); } break; case PUGL_EXPOSE: diff --git a/examples/pugl_cpp_demo.cpp b/examples/pugl_cpp_demo.cpp index 2b3c0a1..6b52d18 100644 --- a/examples/pugl_cpp_demo.cpp +++ b/examples/pugl_cpp_demo.cpp @@ -53,8 +53,8 @@ CubeView::onEvent(const pugl::ConfigureEvent& event) noexcept pugl::Status CubeView::onEvent(const pugl::UpdateEvent&) noexcept { - // Normally, we would post a redisplay: - // return postRedisplay(); + // Normally, we would obscure the view + // return obscure(); // But for testing, use sendEvent() instead: return sendEvent(pugl::ExposeEvent{ diff --git a/examples/pugl_embed_demo.c b/examples/pugl_embed_demo.c index 0274ab4..c14afde 100644 --- a/examples/pugl_embed_demo.c +++ b/examples/pugl_embed_demo.c @@ -92,8 +92,8 @@ swapFocus(PuglTestApp* app) } if (!app->continuous) { - puglPostRedisplay(app->parent); - puglPostRedisplay(app->child); + puglObscureView(app->parent); + puglObscureView(app->child); } } @@ -146,7 +146,7 @@ onParentEvent(PuglView* view, const PuglEvent* event) break; case PUGL_UPDATE: if (app->continuous) { - puglPostRedisplay(view); + puglObscureView(view); } break; case PUGL_EXPOSE: @@ -195,7 +195,7 @@ onEvent(PuglView* view, const PuglEvent* event) break; case PUGL_UPDATE: if (app->continuous) { - puglPostRedisplay(view); + puglObscureView(view); } break; case PUGL_EXPOSE: @@ -213,14 +213,14 @@ onEvent(PuglView* view, const PuglEvent* event) app->lastMouseX = event->motion.x; app->lastMouseY = event->motion.y; if (!app->continuous) { - puglPostRedisplay(view); - puglPostRedisplay(app->parent); + puglObscureView(view); + puglObscureView(app->parent); } break; case PUGL_SCROLL: app->dist = fmaxf(10.0f, app->dist + (float)event->scroll.dy); if (!app->continuous) { - puglPostRedisplay(view); + puglObscureView(view); } break; case PUGL_POINTER_IN: diff --git a/examples/pugl_management_demo.c b/examples/pugl_management_demo.c index 50109a4..51d2f43 100644 --- a/examples/pugl_management_demo.c +++ b/examples/pugl_management_demo.c @@ -58,12 +58,32 @@ onExpose(PuglView* const view, const PuglExposeEvent* const event) char buf[128] = {0}; cairo_text_extents_t extents = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; cairo_set_font_size(cr, 30.0); + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + + // Draw position label + snprintf(buf, sizeof(buf), "Position: %5d, %5d", frame.x, frame.y); + cairo_text_extents(cr, buf, &extents); + cairo_move_to( + cr, cx - extents.width / 2.0, cy + extents.height / 2.0 - 192.0); + cairo_show_text(cr, buf); + + // Draw size label + snprintf(buf, sizeof(buf), "Size: %5u, %5u", frame.width, frame.height); + cairo_text_extents(cr, buf, &extents); + cairo_move_to( + cr, cx - extents.width / 2.0, cy + extents.height / 2.0 - 144.0); + cairo_show_text(cr, buf); + + // Draw scale label + snprintf(buf, sizeof(buf), "Scale: %g", puglGetScaleFactor(view)); + cairo_text_extents(cr, buf, &extents); + cairo_move_to(cr, cx - extents.width / 2.0, cy + extents.height / 2.0 - 96.0); + cairo_show_text(cr, buf); // Draw time label snprintf(buf, sizeof(buf), "Draw time: %g", puglGetTime(world)); cairo_text_extents(cr, buf, &extents); cairo_move_to(cr, cx - extents.width / 2.0, cy + extents.height / 2.0 - 48.0); - cairo_set_source_rgb(cr, 0.9, 0.9, 0.9); cairo_show_text(cr, buf); // Draw style label @@ -81,7 +101,6 @@ onExpose(PuglView* const view, const PuglExposeEvent* const event) style & PUGL_VIEW_STYLE_RESIZING ? " resizing" : ""); cairo_text_extents(cr, buf, &extents); cairo_move_to(cr, cx - extents.width / 2.0, cy + extents.height / 2.0); - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); cairo_show_text(cr, buf); if (view == app->mainView.view) { @@ -90,7 +109,6 @@ onExpose(PuglView* const view, const PuglExposeEvent* const event) cairo_text_extents(cr, buf, &extents); cairo_move_to( cr, cx - extents.width / 2.0, cy + extents.height / 2.0 + 48.0); - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); cairo_show_text(cr, buf); } @@ -185,7 +203,7 @@ onCommonEvent(PuglView* view, const PuglEvent* const event) } break; case PUGL_CONFIGURE: - return puglPostRedisplay(view); + return puglObscureView(view); case PUGL_EXPOSE: return onExpose(view, &event->expose); case PUGL_KEY_PRESS: diff --git a/examples/pugl_shader_demo.c b/examples/pugl_shader_demo.c index 4d6821a..2b122a9 100644 --- a/examples/pugl_shader_demo.c +++ b/examples/pugl_shader_demo.c @@ -168,7 +168,7 @@ onEvent(PuglView* view, const PuglEvent* event) onConfigure(view, event->configure.width, event->configure.height); break; case PUGL_UPDATE: - puglPostRedisplay(view); + puglObscureView(view); break; case PUGL_EXPOSE: onExpose(view); @@ -195,7 +195,7 @@ onEvent(PuglView* view, const PuglEvent* event) break; case PUGL_TIMER: if (event->timer.id == resizeTimerId) { - puglPostRedisplay(view); + puglObscureView(view); } break; default: diff --git a/examples/pugl_vulkan_cpp_demo.cpp b/examples/pugl_vulkan_cpp_demo.cpp index fd93394..a1d6954 100644 --- a/examples/pugl_vulkan_cpp_demo.cpp +++ b/examples/pugl_vulkan_cpp_demo.cpp @@ -1522,7 +1522,7 @@ View::onEvent(const pugl::ConfigureEvent& event) pugl::Status View::onEvent(const pugl::UpdateEvent&) { - return postRedisplay(); + return obscure(); } VkResult @@ -1708,7 +1708,7 @@ View::onEvent(const pugl::LoopEnterEvent&) pugl::Status View::onEvent(const pugl::TimerEvent&) { - return postRedisplay(); + return obscure(); } pugl::Status diff --git a/examples/pugl_vulkan_demo.c b/examples/pugl_vulkan_demo.c index 35be97f..708a28f 100644 --- a/examples/pugl_vulkan_demo.c +++ b/examples/pugl_vulkan_demo.c @@ -1009,7 +1009,7 @@ onEvent(PuglView* const view, const PuglEvent* const e) switch (e->type) { case PUGL_UPDATE: - return app->opts.continuous ? puglPostRedisplay(view) : PUGL_SUCCESS; + return app->opts.continuous ? puglObscureView(view) : PUGL_SUCCESS; case PUGL_EXPOSE: return onExpose(view); case PUGL_CONFIGURE: diff --git a/examples/pugl_window_demo.c b/examples/pugl_window_demo.c index 67086bb..cd4fd67 100644 --- a/examples/pugl_window_demo.c +++ b/examples/pugl_window_demo.c @@ -95,7 +95,7 @@ static void redisplayView(PuglTestApp* app, PuglView* view) { if (!app->continuous) { - puglPostRedisplay(view); + puglObscureView(view); } } @@ -115,7 +115,7 @@ onEvent(PuglView* view, const PuglEvent* event) break; case PUGL_UPDATE: if (app->continuous) { - puglPostRedisplay(view); + puglObscureView(view); } break; case PUGL_EXPOSE: diff --git a/include/pugl/pugl.h b/include/pugl/pugl.h index b70b91d..d7366c6 100644 --- a/include/pugl/pugl.h +++ b/include/pugl/pugl.h @@ -298,9 +298,9 @@ 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 puglPostRedisplay() or puglPostRedisplayRect(). For - example, to continuously animate, a view calls puglPostRedisplay() when an - update event is received, and it will then shortly receive an expose event. + 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. */ typedef PuglAnyEvent PuglUpdateEvent; @@ -1422,17 +1422,32 @@ puglGetContext(PuglView* view); */ PUGL_API PuglStatus -puglPostRedisplay(PuglView* view); +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 has the same semantics as puglPostRedisplay(), but allows giving a - precise region for redrawing only a portion of the view. + 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. + + @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. @@ -2194,6 +2209,35 @@ puglGetParentWindow(PuglView* view) return puglGetParent(view); } +/** + Request a redisplay for the entire view. + + 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. +*/ +static inline PUGL_DEPRECATED_BY("puglObscureView") +PuglStatus +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() diff --git a/src/internal.c b/src/internal.c index 31345f3..38a595a 100644 --- a/src/internal.c +++ b/src/internal.c @@ -9,9 +9,17 @@ #include <assert.h> #include <stdbool.h> +#include <stdint.h> #include <stdlib.h> #include <string.h> +static PuglPoint +make_point(const PuglCoord x, const PuglCoord y) +{ + const PuglPoint point = {x, y}; + return point; +} + bool puglIsValidPosition(const int x, const int y) { @@ -30,6 +38,45 @@ puglIsValidArea(const PuglArea size) return size.width && size.height; } +PuglArea +puglGetInitialSize(const PuglView* const view) +{ + if (view->lastConfigure.type == PUGL_CONFIGURE) { + // Use the last configured size + const PuglConfigureEvent config = view->lastConfigure; + const PuglArea size = {config.width, config.height}; + return size; + } + + // Use the default size hint set by the application + return view->sizeHints[PUGL_DEFAULT_SIZE]; +} + +PuglPoint +puglGetInitialPosition(const PuglView* const view, const PuglArea size) +{ + if (view->lastConfigure.type == PUGL_CONFIGURE) { + // Use the last configured frame + return make_point(view->lastConfigure.x, view->lastConfigure.y); + } + + if (puglIsValidPosition(view->defaultX, view->defaultY)) { + // Use the default position hint set by the application + return make_point((PuglCoord)view->defaultX, (PuglCoord)view->defaultY); + } + + if (view->parent) { + // Default to the top/left origin of the parent + return make_point(0, 0); + } + + // Center frame on a transient ancestor, or failing that, the screen + const PuglPoint center = puglGetAncestorCenter(view); + const PuglPoint pos = {(PuglCoord)(center.x - (size.width / 2)), + (PuglCoord)(center.y - (size.height / 2))}; + return pos; +} + void puglEnsureHint(PuglView* const view, const PuglViewHint hint, const int value) { diff --git a/src/internal.h b/src/internal.h index 8497c50..7387494 100644 --- a/src/internal.h +++ b/src/internal.h @@ -30,6 +30,18 @@ puglIsValidSize(unsigned x, unsigned y); bool puglIsValidArea(PuglArea size); +/// Return the center point of some "soft" ancestor (parent window or screen) +PuglPoint +puglGetAncestorCenter(const PuglView* view); + +/// Return the initial size of a view +PuglArea +puglGetInitialSize(const PuglView* view); + +/// Return the initial position of a view if known, or an invalid position +PuglPoint +puglGetInitialPosition(const PuglView* view, PuglArea size); + /// Set hint to a default value if it is unset (PUGL_DONT_CARE) void puglEnsureHint(PuglView* view, PuglViewHint hint, int value); @@ -7,6 +7,7 @@ #include "mac.h" #include "internal.h" +#include "macros.h" #include "platform.h" #include "pugl/pugl.h" @@ -121,7 +122,7 @@ viewScreen(const PuglView* view) } static NSRect -nsRectToPoints(PuglView* view, const NSRect rect) +nsRectToPoints(const PuglView* view, const NSRect rect) { const double scaleFactor = [viewScreen(view) backingScaleFactor]; @@ -132,7 +133,7 @@ nsRectToPoints(PuglView* view, const NSRect rect) } static NSRect -nsRectFromPoints(PuglView* view, const NSRect rect) +nsRectFromPoints(const PuglView* view, const NSRect rect) { const double scaleFactor = [viewScreen(view) backingScaleFactor]; @@ -143,7 +144,7 @@ nsRectFromPoints(PuglView* view, const NSRect rect) } static NSPoint -nsPointFromPoints(PuglView* view, const NSPoint point) +nsPointFromPoints(const PuglView* view, const NSPoint point) { const double scaleFactor = [viewScreen(view) backingScaleFactor]; @@ -1153,30 +1154,9 @@ updateSizeHints(PuglView* const view) } } -static PuglRect -getInitialFrame(PuglView* const view) +PuglPoint +puglGetAncestorCenter(const PuglView* const view) { - if (view->lastConfigure.type == PUGL_CONFIGURE) { - // Use the last configured frame - const PuglRect frame = {view->lastConfigure.x, - view->lastConfigure.y, - view->lastConfigure.width, - view->lastConfigure.height}; - return frame; - } - - const int x = view->defaultX; - const int y = view->defaultY; - if (puglIsValidPosition(x, y)) { - // Use the default position set with puglSetPosition while unrealized - const PuglRect frame = {(PuglCoord)x, - (PuglCoord)y, - view->sizeHints[PUGL_DEFAULT_SIZE].width, - view->sizeHints[PUGL_DEFAULT_SIZE].height}; - return frame; - } - - // Get a bounding rect from the transient parent or the screen const NSScreen* const screen = viewScreen(view); const NSRect boundsPt = rectFromScreen(screen, @@ -1184,17 +1164,11 @@ getInitialFrame(PuglView* const view) ? [[(const NSView*)view->transientParent window] frame] : [screen frame]); - // Center the frame around the center of the bounding rectangle - const PuglArea defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; - const NSRect boundsPx = nsRectFromPoints(view, boundsPt); - const double centerX = boundsPx.origin.x + boundsPx.size.width / 2; - const double centerY = boundsPx.origin.y + boundsPx.size.height / 2; - - const PuglRect frame = {(PuglCoord)(centerX - (defaultSize.width / 2U)), - (PuglCoord)(centerY - (defaultSize.height / 2U)), - view->sizeHints[PUGL_DEFAULT_SIZE].width, - view->sizeHints[PUGL_DEFAULT_SIZE].height}; - return frame; + const NSRect boundsPx = nsRectFromPoints(view, boundsPt); + const PuglPoint center = { + (PuglCoord)(boundsPx.origin.x + boundsPx.size.width / 2.0), + (PuglCoord)(boundsPx.origin.y + boundsPx.size.height / 2.0)}; + return center; } PuglStatus @@ -1243,7 +1217,9 @@ puglRealize(PuglView* view) } // Get the initial frame to use from the defaults or last configuration - const PuglRect initialFrame = getInitialFrame(view); + const PuglArea size = puglGetInitialSize(view); + const PuglPoint pos = puglGetInitialPosition(view, size); + const PuglRect initialFrame = {pos.x, pos.y, size.width, size.height}; // Convert frame to points const NSRect framePx = rectToNsRect(initialFrame); @@ -1669,22 +1645,37 @@ puglGetTime(const PuglWorld* world) } PuglStatus -puglPostRedisplay(PuglView* view) +puglObscureView(PuglView* view) { [view->impl->drawView setNeedsDisplay:YES]; return PUGL_SUCCESS; } 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; - [view->impl->drawView setNeedsDisplayInRect:nsRectToPoints(view, rectPx)]; + 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); + + 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" @@ -1042,7 +1043,7 @@ puglSetViewStyle(PuglView* const view, const PuglViewStyleFlags flags) const bool newMaximized = styleIsMaximized(flags); if (oldMaximized != newMaximized) { ShowWindow(impl->hwnd, newMaximized ? SW_SHOWMAXIMIZED : SW_RESTORE); - puglPostRedisplay(view); + puglObscureView(view); } return PUGL_SUCCESS; @@ -1209,20 +1210,29 @@ puglGetTime(const PuglWorld* world) } PuglStatus -puglPostRedisplay(PuglView* view) +puglObscureView(PuglView* view) { InvalidateRect(view->impl->hwnd, NULL, false); return PUGL_SUCCESS; } 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; @@ -1576,42 +1586,18 @@ puglWinGetPixelFormatDescriptor(const PuglHints hints) return pfd; } -static PuglRect -getInitialFrame(PuglView* const view) +PuglPoint +puglGetAncestorCenter(const PuglView* const view) { - if (view->lastConfigure.type == PUGL_CONFIGURE) { - // Use the last configured frame - const PuglRect frame = {view->lastConfigure.x, - view->lastConfigure.y, - view->lastConfigure.width, - view->lastConfigure.height}; - return frame; - } - - const PuglSpan defaultWidth = view->sizeHints[PUGL_DEFAULT_SIZE].width; - const PuglSpan defaultHeight = view->sizeHints[PUGL_DEFAULT_SIZE].height; - const int x = view->defaultX; - const int y = view->defaultY; - if (puglIsValidPosition(x, y)) { - // Use the default position set with puglSetPosition while unrealized - const PuglRect frame = { - (PuglCoord)x, (PuglCoord)y, defaultWidth, defaultHeight}; - return frame; - } - - // Get a bounding rect from the "nearest" parent or parent-like window - const HWND hwnd = puglWinGetWindow(view); - RECT rect = {0, 0, 0, 0}; - GetWindowRect(hwnd ? hwnd : GetDesktopWindow(), &rect); + RECT rect = {0, 0, 0, 0}; + GetWindowRect(view->transientParent ? (HWND)view->transientParent + : GetDesktopWindow(), + &rect); - // Center the frame around the center of the bounding rectangle - const LONG centerX = rect.left + (rect.right - rect.left) / 2; - const LONG centerY = rect.top + (rect.bottom - rect.top) / 2; - const PuglRect frame = {(PuglCoord)(centerX - (defaultWidth / 2)), - (PuglCoord)(centerY - (defaultHeight / 2)), - defaultWidth, - defaultHeight}; - return frame; + const PuglPoint center = { + (PuglCoord)(rect.left + (rect.right - rect.left) / 2), + (PuglCoord)(rect.top + (rect.bottom - rect.top) / 2)}; + return center; } PuglStatus @@ -1626,13 +1612,14 @@ puglWinCreateWindow(PuglView* const view, PuglNativeView parent = view->parent ? view->parent : view->transientParent; // Calculate initial window rectangle - const unsigned winFlags = puglWinGetWindowFlags(view); - const unsigned winExFlags = puglWinGetWindowExFlags(view); - const PuglRect frame = getInitialFrame(view); - RECT wr = {(long)frame.x, - (long)frame.y, - (long)frame.x + frame.width, - (long)frame.y + frame.height}; + const unsigned winFlags = puglWinGetWindowFlags(view); + const unsigned winExFlags = puglWinGetWindowExFlags(view); + const PuglArea size = puglGetInitialSize(view); + const PuglPoint pos = puglGetInitialPosition(view, size); + RECT wr = {(long)pos.x, + (long)pos.y, + (long)pos.x + size.width, + (long)pos.y + size.height}; AdjustWindowRectEx(&wr, winFlags, FALSE, winExFlags); ArgStringChar* const classNameArg = puglArgStringNew(className); @@ -510,48 +510,21 @@ clearX11Clipboard(PuglX11Clipboard* const board) board->data.len = 0; } -static PuglRect -getInitialFrame(PuglView* const view) -{ - if (view->lastConfigure.type == PUGL_CONFIGURE) { - // Use the last configured frame - const PuglRect frame = {view->lastConfigure.x, - view->lastConfigure.y, - view->lastConfigure.width, - view->lastConfigure.height}; - return frame; - } - - const PuglSpan defaultWidth = view->sizeHints[PUGL_DEFAULT_SIZE].width; - const PuglSpan defaultHeight = view->sizeHints[PUGL_DEFAULT_SIZE].height; - const int x = view->defaultX; - const int y = view->defaultY; - if (puglIsValidPosition(x, y)) { - // Use the default position set with puglSetPosition while unrealized - const PuglRect frame = { - (PuglCoord)x, (PuglCoord)y, defaultWidth, defaultHeight}; - return frame; - } - - // Get the best "parentish" window to position the window in - Display* const display = view->world->impl->display; - const Window parent = - (view->parent ? (Window)view->parent - : view->transientParent ? (Window)view->transientParent - : RootWindow(display, view->impl->screen)); - - // Get the position/size of the parent as bounds for the new window - XWindowAttributes parentAttrs = PUGL_INIT_STRUCT; - XGetWindowAttributes(display, parent, &parentAttrs); +PuglPoint +puglGetAncestorCenter(const PuglView* const view) +{ + Display* const display = view->world->impl->display; + const int screen = view->impl->screen; + XWindowAttributes ancestorAttrs = PUGL_INIT_STRUCT; + XGetWindowAttributes(display, + view->transientParent ? (Window)view->transientParent + : RootWindow(display, screen), + &ancestorAttrs); - // Center the frame within the parent bounds - const int centerX = parentAttrs.x + parentAttrs.width / 2; - const int centerY = parentAttrs.y + parentAttrs.height / 2; - const PuglRect frame = {(PuglCoord)(centerX - (defaultWidth / 2)), - (PuglCoord)(centerY - (defaultHeight / 2)), - defaultWidth, - defaultHeight}; - return frame; + const PuglPoint center = { + (PuglCoord)(ancestorAttrs.x + ancestorAttrs.width / 2), + (PuglCoord)(ancestorAttrs.y + ancestorAttrs.height / 2)}; + return center; } PuglStatus @@ -606,16 +579,17 @@ puglRealize(PuglView* const view) attr.event_mask |= StructureNotifyMask; attr.event_mask |= VisibilityChangeMask; - // Calculate the initial window rectangle - const PuglRect initialFrame = getInitialFrame(view); + // Calculate the initial window frame + const PuglArea initialSize = puglGetInitialSize(view); + const PuglPoint initialPos = puglGetInitialPosition(view, initialSize); // Create the window impl->win = XCreateWindow(display, parent, - initialFrame.x, - initialFrame.y, - initialFrame.width, - initialFrame.height, + initialPos.x, + initialPos.y, + initialSize.width, + initialSize.height, 0, impl->vi->depth, InputOutput, @@ -770,7 +744,7 @@ puglShow(PuglView* const view, const PuglShowCommand command) } if (view->stage == PUGL_VIEW_STAGE_CONFIGURED) { - st = puglPostRedisplay(view); + st = puglObscureView(view); } } @@ -1099,17 +1073,19 @@ getCurrentConfiguration(PuglView* const view) XWindowAttributes attrs; XGetWindowAttributes(display, view->impl->win, &attrs); - // Get window position relative to the root window + // Get window position (relative to the root window if not a child) Window ignoredChild = 0; - int rootX = 0; - int rootY = 0; - XTranslateCoordinates( - display, view->impl->win, attrs.root, 0, 0, &rootX, &rootY, &ignoredChild); + int x = attrs.x; + int y = attrs.y; + if (!view->parent) { + XTranslateCoordinates( + display, view->impl->win, attrs.root, 0, 0, &x, &y, &ignoredChild); + } // Build a configure event based on the current window configuration PuglEvent configureEvent = {{PUGL_CONFIGURE, 0}}; - configureEvent.configure.x = (PuglCoord)rootX; - configureEvent.configure.y = (PuglCoord)rootY; + configureEvent.configure.x = (PuglCoord)x; + configureEvent.configure.y = (PuglCoord)y; configureEvent.configure.width = (PuglSpan)attrs.width; configureEvent.configure.height = (PuglSpan)attrs.height; configureEvent.configure.style = getCurrentViewStyleFlags(view); @@ -1546,7 +1522,7 @@ static void mergeExposeEvents(PuglExposeEvent* const dst, const PuglExposeEvent* const src) { if (!dst->type) { - if (src->width > 0.0 && src->height > 0.0) { + if (src->width && src->height) { *dst = *src; } } else { @@ -1867,8 +1843,9 @@ puglUpdate(PuglWorld* const world, const double timeout) world->impl->dispatchingEvents = true; if (timeout < 0.0) { - st0 = pollX11Socket(world, timeout); - st0 = st0 ? st0 : dispatchX11Events(world); + if (!(st0 = pollX11Socket(world, timeout))) { + st0 = dispatchX11Events(world); + } } else if (timeout <= 0.001) { st0 = dispatchX11Events(world); } else { @@ -1900,20 +1877,29 @@ puglGetTime(const PuglWorld* const world) } PuglStatus -puglPostRedisplay(PuglView* const view) +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 1276136..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)); @@ -140,7 +142,7 @@ main(int argc, char** argv) } // Redisplay from outside the event handler - puglPostRedisplay(test.view); + puglObscureView(test.view); while (test.state != REREDISPLAYED) { assert(!puglUpdate(test.world, timeout)); } diff --git a/test/test_update.c b/test/test_update.c index 4710ca2..c5997ba 100644 --- a/test/test_update.c +++ b/test/test_update.c @@ -63,7 +63,7 @@ onEvent(PuglView* view, const PuglEvent* event) case PUGL_UPDATE: if (test->state == EXPOSED1) { test->state = UPDATED; - puglPostRedisplay(view); + puglObscureView(view); } break; |