aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clant.json4
-rw-r--r--bindings/cpp/include/pugl/pugl.hpp15
-rw-r--r--doc/c/event-loop.rst5
-rw-r--r--doc/cpp/event-loop.rst5
-rw-r--r--examples/pugl_cairo_demo.c49
-rw-r--r--examples/pugl_clipboard_demo.c4
-rw-r--r--examples/pugl_cpp_demo.cpp4
-rw-r--r--examples/pugl_embed_demo.c14
-rw-r--r--examples/pugl_management_demo.c26
-rw-r--r--examples/pugl_shader_demo.c4
-rw-r--r--examples/pugl_vulkan_cpp_demo.cpp4
-rw-r--r--examples/pugl_vulkan_demo.c2
-rw-r--r--examples/pugl_window_demo.c4
-rw-r--r--include/pugl/pugl.h64
-rw-r--r--meson.build2
-rw-r--r--src/internal.c47
-rw-r--r--src/internal.h12
-rw-r--r--src/mac.m83
-rw-r--r--src/win.c83
-rw-r--r--src/x11.c120
-rw-r--r--test/test_redisplay.c24
-rw-r--r--test/test_update.c2
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);
diff --git a/src/mac.m b/src/mac.m
index b5b617b..5f9d71e 100644
--- a/src/mac.m
+++ b/src/mac.m
@@ -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;
}
diff --git a/src/win.c b/src/win.c
index 1efc9a2..fe8d1b4 100644
--- a/src/win.c
+++ b/src/win.c
@@ -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);
diff --git a/src/x11.c b/src/x11.c
index 6e9d6c0..f60528b 100644
--- a/src/x11.c
+++ b/src/x11.c
@@ -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;