From a02dd604ff43264757460ca4d87a2dde6ed7bbf0 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Tue, 10 Jan 2023 21:21:59 -0500 Subject: Remove cached frame from view This was just a source of ambiguity and bugs, since it represented different things at different times and could become stale. Redundant data is always trouble, so eliminate it, leaving just two positions/sizes: the defaults (used when the view is not yet realized), and the last configuration. --- src/common.c | 28 ++++++- src/internal.c | 19 +---- src/mac.h | 3 - src/mac.m | 229 ++++++++++++++++++++++++++++++++++---------------------- src/mac_cairo.m | 4 +- src/mac_stub.m | 7 +- src/types.h | 3 +- src/win.c | 149 ++++++++++++++++++++++-------------- src/win_cairo.c | 6 +- src/x11.c | 130 ++++++++++++++++++++++---------- 10 files changed, 364 insertions(+), 214 deletions(-) diff --git a/src/common.c b/src/common.c index 5c39b39..20e844a 100644 --- a/src/common.c +++ b/src/common.c @@ -10,7 +10,9 @@ #include "pugl/pugl.h" +#include #include +#include #include #include @@ -124,6 +126,8 @@ puglNewView(PuglWorld* const world) view->world = world; view->sizeHints[PUGL_MIN_SIZE].width = 1; view->sizeHints[PUGL_MIN_SIZE].height = 1; + view->defaultX = INT_MIN; + view->defaultY = INT_MIN; puglSetDefaultHints(view->hints); @@ -237,7 +241,29 @@ puglGetViewHint(const PuglView* view, PuglViewHint hint) PuglRect puglGetFrame(const PuglView* view) { - return view->frame; + if (view->lastConfigure.type == PUGL_CONFIGURE) { + // Return the last configured frame + const PuglRect frame = {view->lastConfigure.x, + view->lastConfigure.y, + view->lastConfigure.width, + view->lastConfigure.height}; + return frame; + } + + // Get the default position if set, or fallback to (0, 0) + int x = view->defaultX; + int y = view->defaultY; + if (x < INT16_MIN || x > INT16_MAX || y < INT16_MIN || y > INT16_MAX) { + x = 0; + y = 0; + } + + // Return the default frame, sanitized if necessary + const PuglRect frame = {(PuglCoord)x, + (PuglCoord)y, + view->sizeHints[PUGL_DEFAULT_SIZE].width, + view->sizeHints[PUGL_DEFAULT_SIZE].height}; + return frame; } const char* diff --git a/src/internal.c b/src/internal.c index cfb444e..2da5e9e 100644 --- a/src/internal.c +++ b/src/internal.c @@ -122,15 +122,10 @@ puglPreRealize(PuglView* const view) return PUGL_BAD_CONFIGURATION; } - // Set the size to the default if it hasn't already been set - if (!isValidSize(view->frame.width, view->frame.height)) { - const PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; - if (!isValidSize(defaultSize.width, defaultSize.height)) { - return PUGL_BAD_CONFIGURATION; - } - - view->frame.width = defaultSize.width; - view->frame.height = defaultSize.height; + // Ensure that the default size is set to a valid size + const PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; + if (!isValidSize(defaultSize.width, defaultSize.height)) { + return PUGL_BAD_CONFIGURATION; } return PUGL_SUCCESS; @@ -159,12 +154,6 @@ puglConfigure(PuglView* view, const PuglEvent* event) PuglStatus st = PUGL_SUCCESS; assert(event->type == PUGL_CONFIGURE); - - view->frame.x = event->configure.x; - view->frame.y = event->configure.y; - view->frame.width = event->configure.width; - view->frame.height = event->configure.height; - if (puglMustConfigure(view, &event->configure)) { st = view->eventFunc(view, event); view->lastConfigure = event->configure; diff --git a/src/mac.h b/src/mac.h index 7473231..119e7c8 100644 --- a/src/mac.h +++ b/src/mac.h @@ -21,9 +21,6 @@ @end @interface PuglWindow : NSWindow - -- (void)setPuglview:(PuglView*)view; - @end struct PuglWorldInternalsImpl { diff --git a/src/mac.m b/src/mac.m index 7b4e3a2..ca1ec4d 100644 --- a/src/mac.m +++ b/src/mac.m @@ -163,27 +163,6 @@ sizePoints(PuglView* view, const double width, const double height) return NSMakeSize(width / scaleFactor, height / scaleFactor); } -static void -updateViewRect(PuglView* view) -{ - NSWindow* const window = view->impl->window; - if (window) { - const NSRect screenFramePt = [[NSScreen mainScreen] frame]; - const NSRect screenFramePx = nsRectFromPoints(view, screenFramePt); - const NSRect framePt = [window frame]; - const NSRect contentPt = [window contentRectForFrameRect:framePt]; - const NSRect contentPx = nsRectFromPoints(view, contentPt); - const double screenHeight = screenFramePx.size.height; - - view->frame.x = (PuglCoord)contentPx.origin.x; - view->frame.y = - (PuglCoord)(screenHeight - contentPx.origin.y - contentPx.size.height); - - view->frame.width = (PuglSpan)contentPx.size.width; - view->frame.height = (PuglSpan)contentPx.size.height; - } -} - static PuglViewStyleFlags getCurrentViewStyleFlags(PuglView* const view) { @@ -207,6 +186,32 @@ getCurrentViewStyleFlags(PuglView* const view) (isResizing ? PUGL_VIEW_STYLE_RESIZING : 0U); } +PuglStatus +dispatchCurrentChildViewConfiguration(PuglView* const view) +{ + const NSRect framePt = [view->impl->wrapperView frame]; + const NSRect framePx = nsRectFromPoints(view, framePt); + + if (view->stage < PUGL_VIEW_STAGE_REALIZED) { + return PUGL_SUCCESS; + } + + const PuglConfigureEvent ev = { + PUGL_CONFIGURE, + 0, + (PuglCoord)framePx.origin.x, + (PuglCoord)framePx.origin.y, + (PuglSpan)framePx.size.width, + (PuglSpan)framePx.size.height, + getCurrentViewStyleFlags(view), + }; + + PuglEvent configureEvent; + configureEvent.configure = ev; + + return puglDispatchEvent(view, &configureEvent); +} + @implementation PuglWindow { @public PuglView* puglview; @@ -228,22 +233,26 @@ getCurrentViewStyleFlags(PuglView* const view) return (PuglWindow*)result; } -- (void)setPuglview:(PuglView*)view +- (PuglStatus)dispatchCurrentConfiguration { - puglview = view; + if (puglview->stage < PUGL_VIEW_STAGE_REALIZED) { + return PUGL_SUCCESS; + } - [self setContentSize:sizePoints(view, view->frame.width, view->frame.height)]; -} + const NSRect screenFramePt = [[NSScreen mainScreen] frame]; + const NSRect screenFramePx = nsRectFromPoints(puglview, screenFramePt); + const NSRect framePt = [self frame]; + const NSRect contentPt = [self contentRectForFrameRect:framePt]; + const NSRect contentPx = nsRectFromPoints(puglview, contentPt); + const double screenHeight = screenFramePx.size.height; -- (PuglStatus)dispatchCurrentConfiguration -{ const PuglConfigureEvent ev = { PUGL_CONFIGURE, 0, - puglview->frame.x, - puglview->frame.y, - puglview->frame.width, - puglview->frame.height, + (PuglCoord)contentPx.origin.x, + (PuglCoord)(screenHeight - contentPx.origin.y - contentPx.size.height), + (PuglSpan)contentPx.size.width, + (PuglSpan)contentPx.size.height, getCurrentViewStyleFlags(puglview), }; @@ -304,21 +313,11 @@ getCurrentViewStyleFlags(PuglView* const view) const double scaleFactor = [[NSScreen mainScreen] backingScaleFactor]; if (reshaped) { - updateViewRect(puglview); - - const PuglConfigureEvent ev = { - PUGL_CONFIGURE, - 0, - puglview->frame.x, - puglview->frame.y, - puglview->frame.width, - puglview->frame.height, - getCurrentViewStyleFlags(puglview), - }; - - PuglEvent configureEvent; - configureEvent.configure = ev; - puglDispatchEvent(puglview, &configureEvent); + if (puglview->impl->window) { + [puglview->impl->window dispatchCurrentConfiguration]; + } else { + dispatchCurrentChildViewConfiguration(puglview); + } reshaped = false; } @@ -944,7 +943,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) { (void)notification; - updateViewRect(window->puglview); + [window dispatchCurrentConfiguration]; } - (void)windowDidBecomeKey:(NSNotification*)notification @@ -1120,9 +1119,29 @@ updateSizeHints(PuglView* const view) } } -static void -puglMacSetDefaultPosition(PuglView* const view) +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 int x = view->defaultX; + const int y = view->defaultY; + if (x >= INT16_MIN && x <= INT16_MAX && y >= INT16_MIN && y <= INT16_MAX) { + // 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 = @@ -1132,11 +1151,16 @@ puglMacSetDefaultPosition(PuglView* const view) : [screen frame]); // Center the frame around the center of the bounding rectangle - 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; - view->frame.x = (PuglCoord)(centerX - (view->frame.width / 2U)); - view->frame.y = (PuglCoord)(centerY - (view->frame.height / 2U)); + const PuglViewSize 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; } PuglStatus @@ -1184,12 +1208,11 @@ puglRealize(PuglView* view) CVDisplayLinkRelease(link); } - // Center top-level windows if a position has not been set - if (!view->parent && !view->frame.x && !view->frame.y) { - puglMacSetDefaultPosition(view); - } + // Get the initial frame to use from the defaults or last configuration + const PuglRect initialFrame = getInitialFrame(view); - const NSRect framePx = rectToNsRect(view->frame); + // Convert frame to points + const NSRect framePx = rectToNsRect(initialFrame); const NSRect framePt = NSMakeRect(framePx.origin.x / scaleFactor, framePx.origin.y / scaleFactor, framePx.size.width / scaleFactor, @@ -1258,7 +1281,10 @@ puglRealize(PuglView* view) styleMask:style backing:NSBackingStoreBuffered defer:NO] retain]; - [window setPuglview:view]; + + window->puglview = view; + impl->window = window; + [window setContentSize:framePt.size]; if (view->title) { NSString* titleString = @@ -1272,10 +1298,9 @@ puglRealize(PuglView* view) ((NSWindow*)window).delegate = [[PuglWindowDelegate alloc] initWithPuglWindow:window]; - impl->window = window; - updateSizeHints(view); - puglSetFrame(view, view->frame); + + puglSetFrame(view, initialFrame); if (view->transientParent) { puglSetTransientParent(view, view->transientParent); } @@ -1342,7 +1367,6 @@ puglShow(PuglView* view, const PuglShowCommand command) if (![window isVisible]) { [window setIsVisible:YES]; [view->impl->drawView setNeedsDisplay:YES]; - updateViewRect(view); } switch (command) { @@ -1688,8 +1712,14 @@ puglSetFrame(PuglView* view, const PuglRect frame) const NSRect framePx = rectToNsRect(frame); const NSRect framePt = nsRectToPoints(view, framePx); - // Update view frame to exactly the requested frame - view->frame = frame; + if (!impl->wrapperView) { + // Set defaults to be used when realized + view->defaultX = frame.x; + view->defaultY = frame.y; + view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)frame.width; + view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)frame.height; + return PUGL_SUCCESS; + } if (impl->window) { const NSRect screenPt = rectToScreen(viewScreen(view), framePt); @@ -1703,16 +1733,17 @@ puglSetFrame(PuglView* view, const PuglRect frame) const NSRect sizePt = [impl->drawView convertRectFromBacking:sizePx]; [impl->wrapperView setFrame:sizePt]; [impl->drawView setFrame:sizePt]; - } else { - // Resize view - const NSRect sizePx = NSMakeRect(0, 0, frame.width, frame.height); - const NSRect sizePt = [impl->drawView convertRectFromBacking:sizePx]; - - [impl->wrapperView setFrame:framePt]; - [impl->drawView setFrame:sizePt]; + [impl->window dispatchCurrentConfiguration]; + return PUGL_SUCCESS; } - return PUGL_SUCCESS; + // Resize view + const NSRect sizePx = NSMakeRect(0, 0, frame.width, frame.height); + const NSRect sizePt = [impl->drawView convertRectFromBacking:sizePx]; + + [impl->wrapperView setFrame:framePt]; + [impl->drawView setFrame:sizePt]; + return dispatchCurrentChildViewConfiguration(view); } PuglStatus @@ -1722,24 +1753,36 @@ puglSetPosition(PuglView* const view, const int x, const int y) return PUGL_BAD_PARAMETER; } - const PuglRect frame = { - (PuglCoord)x, (PuglCoord)y, view->frame.height, view->frame.height}; - PuglInternals* const impl = view->impl; + if (!impl->wrapperView) { + // Set defaults to be used when realized + view->defaultX = x; + view->defaultY = y; + return PUGL_SUCCESS; + } + + const PuglRect frame = {(PuglCoord)x, + (PuglCoord)y, + view->lastConfigure.width, + view->lastConfigure.height}; + if (impl->window) { + // Adjust top-level window frame return puglSetFrame(view, frame); } + // Set wrapper view origin const NSRect framePx = rectToNsRect(frame); const NSRect framePt = nsRectToPoints(view, framePx); [impl->wrapperView setFrameOrigin:framePt.origin]; + // Set draw view origin const NSRect drawPx = NSMakeRect(0, 0, frame.width, frame.height); const NSRect drawPt = [impl->drawView convertRectFromBacking:drawPx]; [impl->drawView setFrameOrigin:drawPt.origin]; - view->frame = frame; - return PUGL_SUCCESS; + // Dispatch new configuration + return dispatchCurrentChildViewConfiguration(view); } PuglStatus @@ -1749,24 +1792,34 @@ puglSetSize(PuglView* const view, const unsigned width, const unsigned height) return PUGL_BAD_PARAMETER; } - const PuglRect frame = { - view->frame.x, view->frame.y, (PuglSpan)width, (PuglSpan)height}; - PuglInternals* const impl = view->impl; + if (!impl->wrapperView) { + // Set defaults to be used when realized + view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)width; + view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)height; + return PUGL_SUCCESS; + } + if (impl->window) { + // Adjust top-level window frame + PuglRect frame = puglGetFrame(view); + frame.width = (PuglSpan)width; + frame.height = (PuglSpan)height; return puglSetFrame(view, frame); } - const NSRect framePx = rectToNsRect(frame); - const NSRect framePt = nsRectToPoints(view, framePx); - [impl->wrapperView setFrameSize:framePt.size]; + // Set wrapper view size + const double scaleFactor = [viewScreen(view) backingScaleFactor]; + const CGSize frameSizePt = {width / scaleFactor, height / scaleFactor}; + [impl->wrapperView setFrameSize:frameSizePt]; - const NSRect drawPx = NSMakeRect(0, 0, frame.width, frame.height); + // Set draw view size + const NSRect drawPx = NSMakeRect(0, 0, width, height); const NSRect drawPt = [impl->drawView convertRectFromBacking:drawPx]; [impl->drawView setFrameSize:drawPt.size]; - view->frame = frame; - return PUGL_SUCCESS; + // Dispatch new configuration + return dispatchCurrentChildViewConfiguration(view); } PuglStatus diff --git a/src/mac_cairo.m b/src/mac_cairo.m index 66af5ba..8e28985 100644 --- a/src/mac_cairo.m +++ b/src/mac_cairo.m @@ -90,8 +90,8 @@ puglMacCairoEnter(PuglView* view, const PuglExposeEvent* expose) CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; - const CGSize sizePx = {(CGFloat)view->frame.width, - (CGFloat)view->frame.height}; + const CGSize sizePx = {(CGFloat)view->lastConfigure.width, + (CGFloat)view->lastConfigure.height}; const CGSize sizePt = CGContextConvertSizeToUserSpace(context, sizePx); diff --git a/src/mac_stub.m b/src/mac_stub.m index ceffa6e..9ebedca 100644 --- a/src/mac_stub.m +++ b/src/mac_stub.m @@ -41,8 +41,11 @@ puglMacStubCreate(PuglView* view) PuglStubView* drawView = [PuglStubView alloc]; drawView->puglview = view; - [drawView - initWithFrame:NSMakeRect(0, 0, view->frame.width, view->frame.height)]; + [drawView initWithFrame:NSMakeRect(0, + 0, + view->lastConfigure.width, + view->lastConfigure.height)]; + if (view->hints[PUGL_RESIZABLE]) { [drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; } else { diff --git a/src/types.h b/src/types.h index da0a295..35f29c5 100644 --- a/src/types.h +++ b/src/types.h @@ -56,10 +56,11 @@ struct PuglViewImpl { char* title; PuglNativeView parent; uintptr_t transientParent; - PuglRect frame; PuglConfigureEvent lastConfigure; PuglHints hints; PuglViewSize sizeHints[PUGL_NUM_SIZE_HINTS]; + int defaultX; + int defaultY; PuglViewStage stage; bool resizing; }; diff --git a/src/win.c b/src/win.c index d8c574b..13184aa 100644 --- a/src/win.c +++ b/src/win.c @@ -153,7 +153,8 @@ puglWinGetMonitor(const PuglView* const view) return MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); } - const POINT point = {(long)view->frame.x, (long)view->frame.y}; + const POINT point = {(long)view->lastConfigure.x, + (long)view->lastConfigure.y}; return MonitorFromPoint(point, MONITOR_DEFAULTTOPRIMARY); } @@ -289,7 +290,6 @@ puglRealize(PuglView* view) view->impl->scaleFactor = puglWinGetViewScaleFactor(view); view->impl->cursor = LoadCursor(NULL, IDC_ARROW); - puglSetFrame(view, view->frame); SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view); return puglDispatchSimpleEvent(view, PUGL_REALIZE); @@ -589,12 +589,9 @@ handleConfigure(PuglView* view, PuglEvent* event) const LONG width = rect.right - rect.left; const LONG height = rect.bottom - rect.top; - view->frame.x = (PuglCoord)rect.left; - view->frame.y = (PuglCoord)rect.top; - event->configure.type = PUGL_CONFIGURE; - event->configure.x = (PuglCoord)view->frame.x; - event->configure.y = (PuglCoord)view->frame.y; + event->configure.x = (PuglCoord)rect.left; + event->configure.y = (PuglCoord)rect.top; event->configure.width = (PuglSpan)width; event->configure.height = (PuglSpan)height; @@ -1200,23 +1197,27 @@ puglGetScaleFactor(const PuglView* const view) PuglStatus puglSetFrame(PuglView* view, const PuglRect frame) { - if (view->impl->hwnd) { - const RECT rect = - adjustedWindowRect(view, frame.x, frame.y, frame.width, frame.height); + if (!view->impl->hwnd) { + // Set defaults to be used when realized + view->defaultX = frame.x; + view->defaultY = frame.y; + view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)frame.width; + view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)frame.height; + return PUGL_SUCCESS; + } + + const RECT rect = + adjustedWindowRect(view, frame.x, frame.y, frame.width, frame.height); - if (!SetWindowPos(view->impl->hwnd, + return SetWindowPos(view->impl->hwnd, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, - SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER)) { - return PUGL_UNKNOWN_ERROR; - } - } - - view->frame = frame; - return PUGL_SUCCESS; + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER) + ? PUGL_SUCCESS + : PUGL_UNKNOWN_ERROR; } PuglStatus @@ -1226,25 +1227,26 @@ puglSetPosition(PuglView* const view, const int x, const int y) return PUGL_BAD_PARAMETER; } - if (view->impl->hwnd) { - const RECT rect = - adjustedWindowRect(view, x, y, view->frame.width, view->frame.height); + if (!view->impl->hwnd) { + // Set defaults to be used when realized + view->defaultX = x; + view->defaultY = y; + return PUGL_SUCCESS; + } + + const RECT rect = adjustedWindowRect( + view, x, y, view->lastConfigure.width, view->lastConfigure.height); - if (!SetWindowPos(view->impl->hwnd, + return SetWindowPos(view->impl->hwnd, HWND_TOP, rect.left, rect.top, 0, 0, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | - SWP_NOSIZE)) { - return PUGL_UNKNOWN_ERROR; - } - } - - view->frame.x = (PuglCoord)x; - view->frame.y = (PuglCoord)y; - return PUGL_SUCCESS; + SWP_NOSIZE) + ? PUGL_SUCCESS + : PUGL_UNKNOWN_ERROR; } PuglStatus @@ -1254,25 +1256,29 @@ puglSetSize(PuglView* const view, const unsigned width, const unsigned height) return PUGL_BAD_PARAMETER; } - if (view->impl->hwnd) { - const RECT rect = adjustedWindowRect( - view, view->frame.x, view->frame.y, (long)width, (long)height); + if (!view->impl->hwnd) { + // Set defaults to be used when realized + view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)width; + view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)height; + return PUGL_SUCCESS; + } + + const RECT rect = adjustedWindowRect(view, + view->lastConfigure.x, + view->lastConfigure.y, + (long)width, + (long)height); - if (!SetWindowPos(view->impl->hwnd, + return SetWindowPos(view->impl->hwnd, HWND_TOP, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | - SWP_NOMOVE)) { - return PUGL_UNKNOWN_ERROR; - } - } - - view->frame.width = (PuglSpan)width; - view->frame.height = (PuglSpan)height; - return PUGL_SUCCESS; + SWP_NOMOVE) + ? PUGL_SUCCESS + : PUGL_UNKNOWN_ERROR; } PuglStatus @@ -1502,19 +1508,42 @@ puglWinGetPixelFormatDescriptor(const PuglHints hints) return pfd; } -static void -puglWinSetDefaultPosition(PuglView* const view) +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 (x >= INT16_MIN && x <= INT16_MAX && y >= INT16_MIN && y <= INT16_MAX) { + // 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); // 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; - view->frame.x = (PuglCoord)(centerX - (view->frame.width / 2U)); - view->frame.y = (PuglCoord)(centerY - (view->frame.height / 2U)); + 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 / 2U)), + (PuglCoord)(centerY - (defaultHeight / 2U)), + defaultWidth, + defaultHeight}; + return frame; } PuglStatus @@ -1526,20 +1555,16 @@ puglWinCreateWindow(PuglView* const view, const char* className = (const char*)view->world->className; const unsigned winFlags = puglWinGetWindowFlags(view); const unsigned winExFlags = puglWinGetWindowExFlags(view); - - // Center top-level windows if a position has not been set - if (!view->parent && !view->frame.x && !view->frame.y) { - puglWinSetDefaultPosition(view); - } + const PuglRect frame = getInitialFrame(view); // The meaning of "parent" depends on the window type (WS_CHILD) PuglNativeView parent = view->parent ? view->parent : view->transientParent; - // Calculate total window size to accommodate requested view size - RECT wr = {(long)view->frame.x, - (long)view->frame.y, - (long)view->frame.width, - (long)view->frame.height}; + // Calculate initial window rectangle + RECT wr = {(long)frame.x, + (long)frame.y, + (long)frame.x + frame.width, + (long)frame.y + frame.height}; AdjustWindowRectEx(&wr, winFlags, FALSE, winExFlags); // Create window and get drawing context @@ -1558,6 +1583,14 @@ puglWinCreateWindow(PuglView* const view, return PUGL_REALIZE_FAILED; } + SetWindowPos(view->impl->hwnd, + HWND_TOP, + wr.left, + wr.top, + wr.right - wr.left, + wr.bottom - wr.top, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); + if (!(*hdc = GetDC(*hwnd))) { DestroyWindow(*hwnd); *hwnd = NULL; diff --git a/src/win_cairo.c b/src/win_cairo.c index ddc9554..75c61db 100644 --- a/src/win_cairo.c +++ b/src/win_cairo.c @@ -27,7 +27,7 @@ puglWinCairoCreateDrawContext(PuglView* view) surface->drawDc = CreateCompatibleDC(impl->hdc); surface->drawBitmap = CreateCompatibleBitmap( - impl->hdc, (int)view->frame.width, (int)view->frame.height); + impl->hdc, (int)view->lastConfigure.width, (int)view->lastConfigure.height); DeleteObject(SelectObject(surface->drawDc, surface->drawBitmap)); @@ -126,8 +126,8 @@ puglWinCairoLeave(PuglView* view, const PuglExposeEvent* expose) BitBlt(impl->hdc, 0, 0, - (int)view->frame.width, - (int)view->frame.height, + (int)view->lastConfigure.width, + (int)view->lastConfigure.height, surface->drawDc, 0, 0, diff --git a/src/x11.c b/src/x11.c index f1a077b..2b9b0f6 100644 --- a/src/x11.c +++ b/src/x11.c @@ -374,13 +374,14 @@ updateSizeHints(const PuglView* const view) XSizeHints sizeHints = PUGL_INIT_STRUCT; if (!view->hints[PUGL_RESIZABLE]) { + const PuglRect frame = puglGetFrame(view); sizeHints.flags = PBaseSize | PMinSize | PMaxSize; - sizeHints.base_width = (int)view->frame.width; - sizeHints.base_height = (int)view->frame.height; - sizeHints.min_width = (int)view->frame.width; - sizeHints.min_height = (int)view->frame.height; - sizeHints.max_width = (int)view->frame.width; - sizeHints.max_height = (int)view->frame.height; + sizeHints.base_width = (int)frame.width; + sizeHints.base_height = (int)frame.height; + sizeHints.min_width = (int)frame.width; + sizeHints.min_height = (int)frame.height; + sizeHints.max_width = (int)frame.width; + sizeHints.max_height = (int)frame.height; } else { const PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; if (defaultSize.width && defaultSize.height) { @@ -478,6 +479,50 @@ 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 (x >= INT16_MIN && x <= INT16_MAX && y >= INT16_MIN && y <= INT16_MAX) { + // 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); + + // 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 / 2U)), + (PuglCoord)(centerY - (defaultHeight / 2U)), + defaultWidth, + defaultHeight}; + return frame; +} + PuglStatus puglRealize(PuglView* const view) { @@ -501,15 +546,6 @@ puglRealize(PuglView* const view) return st; } - // Center top-level windows if a position has not been set - if (!view->parent && !view->frame.x && !view->frame.y) { - const int screenWidth = DisplayWidth(display, screen); - const int screenHeight = DisplayHeight(display, screen); - - view->frame.x = (PuglCoord)((screenWidth - view->frame.width) / 2); - view->frame.y = (PuglCoord)((screenHeight - view->frame.height) / 2); - } - // Configure the backend to get the visual info impl->screen = screen; if ((st = view->backend->configure(view)) || !impl->vi) { @@ -534,13 +570,16 @@ puglRealize(PuglView* const view) attr.event_mask |= StructureNotifyMask; attr.event_mask |= VisibilityChangeMask; + // Calculate the initial window rectangle + const PuglRect initialFrame = getInitialFrame(view); + // Create the window impl->win = XCreateWindow(display, parent, - view->frame.x, - view->frame.y, - view->frame.width, - view->frame.height, + initialFrame.x, + initialFrame.y, + initialFrame.width, + initialFrame.height, 0, impl->vi->depth, InputOutput, @@ -1762,7 +1801,9 @@ puglGetTime(const PuglWorld* const world) PuglStatus puglPostRedisplay(PuglView* const view) { - const PuglRect rect = {0, 0, view->frame.width, view->frame.height}; + PuglRect rect = puglGetFrame(view); + rect.x = 0; + rect.y = 0; return puglPostRedisplayRect(view, rect); } @@ -1824,57 +1865,64 @@ puglGetScaleFactor(const PuglView* const view) PuglStatus puglSetFrame(PuglView* const view, const PuglRect frame) { - if (view->impl->win) { - if (!XMoveResizeWindow(view->world->impl->display, + if (!view->impl->win) { + // Set defaults to be used when realized + view->defaultX = frame.x; + view->defaultY = frame.y; + view->sizeHints[PUGL_DEFAULT_SIZE].width = frame.width; + view->sizeHints[PUGL_DEFAULT_SIZE].height = frame.height; + return PUGL_SUCCESS; + } + + return XMoveResizeWindow(view->world->impl->display, view->impl->win, frame.x, frame.y, frame.width, - frame.height)) { - return PUGL_UNKNOWN_ERROR; - } - } - - view->frame = frame; - return PUGL_SUCCESS; + frame.height) + ? PUGL_SUCCESS + : PUGL_UNKNOWN_ERROR; } PuglStatus puglSetPosition(PuglView* const view, const int x, const int y) { Display* const display = view->world->impl->display; - const Window win = view->impl->win; if (x < INT16_MIN || x > INT16_MAX || y < INT16_MIN || y > INT16_MAX) { return PUGL_BAD_PARAMETER; } - if (win && !XMoveWindow(display, win, x, y)) { - return PUGL_UNKNOWN_ERROR; + if (!view->impl->win) { + // Set defaults to be used when realized + view->defaultX = x; + view->defaultY = y; + return PUGL_SUCCESS; } - view->frame.x = (PuglCoord)x; - view->frame.y = (PuglCoord)y; - return PUGL_SUCCESS; + return XMoveWindow(display, view->impl->win, x, y) ? PUGL_SUCCESS + : PUGL_UNKNOWN_ERROR; } PuglStatus puglSetSize(PuglView* const view, const unsigned width, const unsigned height) { Display* const display = view->world->impl->display; - const Window win = view->impl->win; if (width > INT16_MAX || height > INT16_MAX) { return PUGL_BAD_PARAMETER; } - if (win && !XResizeWindow(display, win, width, height)) { - return PUGL_UNKNOWN_ERROR; + if (!view->impl->win) { + // Set defaults to be used when realized + view->sizeHints[PUGL_DEFAULT_SIZE].width = (PuglSpan)width; + view->sizeHints[PUGL_DEFAULT_SIZE].height = (PuglSpan)height; + return PUGL_SUCCESS; } - view->frame.width = (PuglSpan)width; - view->frame.height = (PuglSpan)height; - return PUGL_SUCCESS; + return XResizeWindow(display, view->impl->win, width, height) + ? PUGL_SUCCESS + : PUGL_UNKNOWN_ERROR; } PuglStatus -- cgit v1.2.1