From c74886ab001979d9a3395bc608df7d61cbf56daa Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 27 May 2024 15:23:04 -0700
Subject: [PATCH] Added SDL_SetWindowAspectRatio() and
SDL_GetWindowAspectRatio()
Fixes https://github.com/libsdl-org/SDL/issues/1573
---
include/SDL3/SDL_test_common.h | 3 +
include/SDL3/SDL_video.h | 55 +++++++++--
src/SDL_utils.c | 39 ++++++++
src/SDL_utils_c.h | 4 +-
src/dynapi/SDL_dynapi.sym | 2 +
src/dynapi/SDL_dynapi_overrides.h | 2 +
src/dynapi/SDL_dynapi_procs.h | 2 +
src/test/SDL_test_common.c | 78 ++++++++++------
src/video/SDL_sysvideo.h | 3 +
src/video/SDL_video.c | 40 ++++++++
src/video/cocoa/SDL_cocoawindow.m | 21 +++++
src/video/windows/SDL_windowsevents.c | 128 ++++++++++++++++++++++++++
src/video/x11/SDL_x11video.c | 1 +
src/video/x11/SDL_x11window.c | 21 ++++-
src/video/x11/SDL_x11window.h | 1 +
15 files changed, 364 insertions(+), 36 deletions(-)
diff --git a/include/SDL3/SDL_test_common.h b/include/SDL3/SDL_test_common.h
index d881303e3378c..a7ec4dfca202d 100644
--- a/include/SDL3/SDL_test_common.h
+++ b/include/SDL3/SDL_test_common.h
@@ -76,6 +76,8 @@ typedef struct
int window_minH;
int window_maxW;
int window_maxH;
+ float window_min_aspect;
+ float window_max_aspect;
int logical_w;
int logical_h;
SDL_bool auto_scale_content;
@@ -84,6 +86,7 @@ typedef struct
float scale;
int depth;
float refresh_rate;
+ SDL_bool fill_usable_bounds;
SDL_bool fullscreen_exclusive;
SDL_DisplayMode fullscreen_mode;
int num_windows;
diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h
index 22f128f0fa6b3..88e53c896122d 100644
--- a/include/SDL3/SDL_video.h
+++ b/include/SDL3/SDL_video.h
@@ -1346,9 +1346,6 @@ extern SDL_DECLSPEC int SDLCALL SDL_GetWindowPosition(SDL_Window *window, int *x
/**
* Request that the size of a window's client area be set.
*
- * NULL can safely be passed as the `w` or `h` parameter if the width or
- * height value is not desired.
- *
* If, at the time of this request, the window in a fixed-size state, such as
* maximized or fullscreen, the request will be deferred until the window
* exits this state and becomes resizable again.
@@ -1385,9 +1382,6 @@ extern SDL_DECLSPEC int SDLCALL SDL_SetWindowSize(SDL_Window *window, int w, int
/**
* Get the size of a window's client area.
*
- * NULL can safely be passed as the `w` or `h` parameter if the width or
- * height value is not desired.
- *
* The window pixel size may differ from its window coordinate size if the
* window is on a high pixel density display. Use SDL_GetWindowSizeInPixels()
* or SDL_GetRenderOutputSize() to get the real client area size in pixels.
@@ -1406,6 +1400,55 @@ extern SDL_DECLSPEC int SDLCALL SDL_SetWindowSize(SDL_Window *window, int w, int
*/
extern SDL_DECLSPEC int SDLCALL SDL_GetWindowSize(SDL_Window *window, int *w, int *h);
+/**
+ * Request that the aspect ratio of a window's client area be set.
+ *
+ * The aspect ratio is the ratio of width divided by height, e.g. 2560x1600 would be 1.6. Larger aspect ratios are wider and smaller aspect ratios are narrower.
+ *
+ * If, at the time of this request, the window in a fixed-size state, such as
+ * maximized or fullscreen, the request will be deferred until the window
+ * exits this state and becomes resizable again.
+ *
+ * On some windowing systems, this request is asynchronous and the new window
+ * aspect ratio may not have have been applied immediately upon the return of this
+ * function. If an immediate change is required, call SDL_SyncWindow() to
+ * block until the changes have taken effect.
+ *
+ * When the window size changes, an SDL_EVENT_WINDOW_RESIZED event will be
+ * emitted with the new window dimensions. Note that the new dimensions may
+ * not match the exact aspect ratio requested, as some windowing systems can restrict
+ * the window size in certain scenarios (e.g. constraining the size of the
+ * content area to remain within the usable desktop bounds). Additionally, as
+ * this is just a request, it can be denied by the windowing system.
+ *
+ * \param window the window to change
+ * \param min_aspect the minimum aspect ratio of the window, or 0.0f for no limit
+ * \param max_aspect the maximum aspect ratio of the window, or 0.0f for no limit
+ * \returns 0 on success or a negative error code on failure; call
+ * SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_GetWindowAspectRatio
+ * \sa SDL_SyncWindow
+ */
+extern SDL_DECLSPEC int SDLCALL SDL_SetWindowAspectRatio(SDL_Window *window, float min_aspect, float max_aspect);
+
+/**
+ * Get the size of a window's client area.
+ *
+ * \param window the window to query the width and height from
+ * \param min_aspect a pointer filled in with the minimum aspect ratio of the window, may be NULL
+ * \param max_aspect a pointer filled in with the maximum aspect ratio of the window, may be NULL
+ * \returns 0 on success or a negative error code on failure; call
+ * SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_SetWindowAspectRatio
+ */
+extern SDL_DECLSPEC int SDLCALL SDL_GetWindowAspectRatio(SDL_Window *window, float *min_aspect, float *max_aspect);
+
/**
* Get the size of a window's borders (decorations) around the client area.
*
diff --git a/src/SDL_utils.c b/src/SDL_utils.c
index 581964b0d8a52..e2ce2939d7a19 100644
--- a/src/SDL_utils.c
+++ b/src/SDL_utils.c
@@ -49,6 +49,45 @@ int SDL_powerof2(int x)
return value;
}
+// Algorithm adapted with thanks from John Cook's blog post:
+// http://www.johndcook.com/blog/2010/10/20/best-rational-approximation
+void SDL_CalculateFraction(float x, int *numerator, int *denominator)
+{
+ const int N = 1000;
+ int a = 0, b = 1;
+ int c = 1, d = 0;
+
+ while (b <= N && d <= N) {
+ float mediant = (float)(a + c) / (b + d);
+ if (x == mediant) {
+ if (b + d <= N) {
+ *numerator = a + c;
+ *denominator = b + d;
+ } else if (d > b) {
+ *numerator = c;
+ *denominator = d;
+ } else {
+ *numerator = a;
+ *denominator = b;
+ }
+ return;
+ } else if (x > mediant) {
+ a = a + c;
+ b = b + d;
+ } else {
+ c = a + c;
+ d = b + d;
+ }
+ }
+ if (b > N) {
+ *numerator = c;
+ *denominator = d;
+ } else {
+ *numerator = a;
+ *denominator = b;
+ }
+}
+
SDL_bool SDL_endswith(const char *string, const char *suffix)
{
size_t string_length = string ? SDL_strlen(string) : 0;
diff --git a/src/SDL_utils_c.h b/src/SDL_utils_c.h
index 98658c3613599..bf0a4fc81ebe6 100644
--- a/src/SDL_utils_c.h
+++ b/src/SDL_utils_c.h
@@ -28,6 +28,8 @@
/* Return the smallest power of 2 greater than or equal to 'x' */
extern int SDL_powerof2(int x);
-SDL_bool SDL_endswith(const char *string, const char *suffix);
+extern void SDL_CalculateFraction(float x, int *numerator, int *denominator);
+
+extern SDL_bool SDL_endswith(const char *string, const char *suffix);
#endif /* SDL_utils_h_ */
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index d91b6f14b665a..53de3ff3ef217 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -453,6 +453,7 @@ SDL3_0.0.0 {
SDL_GetUserFolder;
SDL_GetVersion;
SDL_GetVideoDriver;
+ SDL_GetWindowAspectRatio;
SDL_GetWindowBordersSize;
SDL_GetWindowDisplayScale;
SDL_GetWindowFlags;
@@ -748,6 +749,7 @@ SDL3_0.0.0 {
SDL_SetTextureScaleMode;
SDL_SetThreadPriority;
SDL_SetWindowAlwaysOnTop;
+ SDL_SetWindowAspectRatio;
SDL_SetWindowBordered;
SDL_SetWindowFocusable;
SDL_SetWindowFullscreen;
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 967b6bfb2649a..f64025b4ea3de 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -478,6 +478,7 @@
#define SDL_GetUserFolder SDL_GetUserFolder_REAL
#define SDL_GetVersion SDL_GetVersion_REAL
#define SDL_GetVideoDriver SDL_GetVideoDriver_REAL
+#define SDL_GetWindowAspectRatio SDL_GetWindowAspectRatio_REAL
#define SDL_GetWindowBordersSize SDL_GetWindowBordersSize_REAL
#define SDL_GetWindowDisplayScale SDL_GetWindowDisplayScale_REAL
#define SDL_GetWindowFlags SDL_GetWindowFlags_REAL
@@ -773,6 +774,7 @@
#define SDL_SetTextureScaleMode SDL_SetTextureScaleMode_REAL
#define SDL_SetThreadPriority SDL_SetThreadPriority_REAL
#define SDL_SetWindowAlwaysOnTop SDL_SetWindowAlwaysOnTop_REAL
+#define SDL_SetWindowAspectRatio SDL_SetWindowAspectRatio_REAL
#define SDL_SetWindowBordered SDL_SetWindowBordered_REAL
#define SDL_SetWindowFocusable SDL_SetWindowFocusable_REAL
#define SDL_SetWindowFullscreen SDL_SetWindowFullscreen_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 251660ff987d4..1cbcb92d155de 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -498,6 +498,7 @@ SDL_DYNAPI_PROC(SDL_Finger**,SDL_GetTouchFingers,(SDL_TouchID a, int *b),(a,b),r
SDL_DYNAPI_PROC(char*,SDL_GetUserFolder,(SDL_Folder a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetVersion,(void),(),return)
SDL_DYNAPI_PROC(const char*,SDL_GetVideoDriver,(int a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_GetWindowAspectRatio,(SDL_Window *a, float *b, float *c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_GetWindowBordersSize,(SDL_Window *a, int *b, int *c, int *d, int *e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(float,SDL_GetWindowDisplayScale,(SDL_Window *a),(a),return)
SDL_DYNAPI_PROC(SDL_WindowFlags,SDL_GetWindowFlags,(SDL_Window *a),(a),return)
@@ -783,6 +784,7 @@ SDL_DYNAPI_PROC(int,SDL_SetTextureColorModFloat,(SDL_Texture *a, float b, float
SDL_DYNAPI_PROC(int,SDL_SetTextureScaleMode,(SDL_Texture *a, SDL_ScaleMode b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_SetThreadPriority,(SDL_ThreadPriority a),(a),return)
SDL_DYNAPI_PROC(int,SDL_SetWindowAlwaysOnTop,(SDL_Window *a, SDL_bool b),(a,b),return)
+SDL_DYNAPI_PROC(int,SDL_SetWindowAspectRatio,(SDL_Window *a, float b, float c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_SetWindowBordered,(SDL_Window *a, SDL_bool b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_SetWindowFocusable,(SDL_Window *a, SDL_bool b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_SetWindowFullscreen,(SDL_Window *a, SDL_bool b),(a,b),return)
diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c
index f63db16af5846..25d64352a61f3 100644
--- a/src/test/SDL_test_common.c
+++ b/src/test/SDL_test_common.c
@@ -35,6 +35,7 @@ static const char *common_usage[] = {
static const char *video_usage[] = {
"[--always-on-top]",
+ "[--aspect min-max]",
"[--auto-scale-content]",
"[--center | --position X,Y]",
"[--confine-cursor X,Y,W,H]",
@@ -373,12 +374,7 @@ int SDLTest_CommonArg(SDLTest_CommonState *state, int index)
return 2;
}
if (SDL_strcasecmp(argv[index], "--usable-bounds") == 0) {
- /* !!! FIXME: this is a bit of a hack, but I don't want to add a
- !!! FIXME: flag to the public structure in 2.0.x */
- state->window_x = -1;
- state->window_y = -1;
- state->window_w = -1;
- state->window_h = -1;
+ state->fill_usable_bounds = SDL_TRUE;
return 1;
}
if (SDL_strcasecmp(argv[index], "--geometry") == 0) {
@@ -438,6 +434,26 @@ int SDLTest_CommonArg(SDLTest_CommonState *state, int index)
state->window_maxH = SDL_atoi(h);
return 2;
}
+ if (SDL_strcasecmp(argv[index], "--aspect") == 0) {
+ char *min_aspect, *max_aspect;
+ ++index;
+ if (!argv[index]) {
+ return -1;
+ }
+ min_aspect = argv[index];
+ max_aspect = argv[index];
+ while (*max_aspect && *max_aspect != '-') {
+ ++max_aspect;
+ }
+ if (*max_aspect) {
+ *max_aspect++ = '\0';
+ } else {
+ max_aspect = min_aspect;
+ }
+ state->window_min_aspect = SDL_atof(min_aspect);
+ state->window_max_aspect = SDL_atof(max_aspect);
+ return 2;
+ }
if (SDL_strcasecmp(argv[index], "--logical") == 0) {
char *w, *h;
++index;
@@ -1308,19 +1324,18 @@ SDL_bool SDLTest_CommonInit(SDLTest_CommonState *state)
SDL_Rect r;
SDL_PropertiesID props;
- r.x = state->window_x;
- r.y = state->window_y;
- r.w = state->window_w;
- r.h = state->window_h;
- if (state->auto_scale_content) {
- float scale = SDL_GetDisplayContentScale(state->displayID);
- r.w = (int)SDL_ceilf(r.w * scale);
- r.h = (int)SDL_ceilf(r.h * scale);
- }
-
- /* !!! FIXME: hack to make --usable-bounds work for now. */
- if ((r.x == -1) && (r.y == -1) && (r.w == -1) && (r.h == -1)) {
+ if (state->fill_usable_bounds) {
SDL_GetDisplayUsableBounds(state->displayID, &r);
+ } else {
+ r.x = state->window_x;
+ r.y = state->window_y;
+ r.w = state->window_w;
+ r.h = state->window_h;
+ if (state->auto_scale_content) {
+ float scale = SDL_GetDisplayContentScale(state->displayID);
+ r.w = (int)SDL_ceilf(r.w * scale);
+ r.h = (int)SDL_ceilf(r.h * scale);
+ }
}
if (state->num_windows > 1) {
@@ -1349,6 +1364,9 @@ SDL_bool SDLTest_CommonInit(SDLTest_CommonState *state)
if (state->window_maxW || state->window_maxH) {
SDL_SetWindowMaximumSize(state->windows[i], state->window_maxW, state->window_maxH);
}
+ if (state->window_min_aspect || state->window_max_aspect) {
+ SDL_SetWindowAspectRatio(state->windows[i], state->window_min_aspect, state->window_max_aspect);
+ }
SDL_GetWindowSize(state->windows[i], &w, &h);
if (!(state->window_flags & SDL_WINDOW_RESIZABLE) && (w != r.w || h != r.h)) {
SDL_Log("Window requested size %dx%d, got %dx%d\n", r.w, r.h, w, h);
@@ -2376,15 +2394,21 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event
break;
case SDLK_a:
if (withControl) {
- /* Ctrl-A reports absolute mouse position. */
- float x, y;
- const SDL_MouseButtonFlags mask = SDL_GetGlobalMouseState(&x, &y);
- SDL_Log("ABSOLUTE MOUSE: (%g, %g)%s%s%s%s%s\n", x, y,
- (mask & SDL_BUTTON_LMASK) ? " [LBUTTON]" : "",
- (mask & SDL_BUTTON_MMASK) ? " [MBUTTON]" : "",
- (mask & SDL_BUTTON_RMASK) ? " [RBUTTON]" : "",
- (mask & SDL_BUTTON_X1MASK) ? " [X2BUTTON]" : "",
- (mask & SDL_BUTTON_X2MASK) ? " [X2BUTTON]" : "");
+ /* Ctrl-A toggle aspect ratio */
+ SDL_Window *window = SDL_GetWindowFromID(event->key.windowID);
+ if (window) {
+ float min_aspect = 0.0f, max_aspect = 0.0f;
+
+ SDL_GetWindowAspectRatio(window, &min_aspect, &max_aspect);
+ if (min_aspect > 0.0f || max_aspect > 0.0f) {
+ min_aspect = 0.0f;
+ max_aspect = 0.0f;
+ } else {
+ min_aspect = 1.0f;
+ max_aspect = 1.0f;
+ }
+ SDL_SetWindowAspectRatio(window, min_aspect, max_aspect);
+ }
}
break;
case SDLK_0:
diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h
index 1de2c3d74af29..ba9b6e54d12fc 100644
--- a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -45,6 +45,8 @@ struct SDL_Window
int w, h;
int min_w, min_h;
int max_w, max_h;
+ float min_aspect;
+ float max_aspect;
int last_pixel_w, last_pixel_h;
SDL_WindowFlags flags;
SDL_WindowFlags pending_flags;
@@ -240,6 +242,7 @@ struct SDL_VideoDevice
void (*SetWindowSize)(SDL_VideoDevice *_this, SDL_Window *window);
void (*SetWindowMinimumSize)(SDL_VideoDevice *_this, SDL_Window *window);
void (*SetWindowMaximumSize)(SDL_VideoDevice *_this, SDL_Window *window);
+ void (*SetWindowAspectRatio)(SDL_VideoDevice *_this, SDL_Window *window);
int (*GetWindowBordersSize)(SDL_VideoDevice *_this, SDL_Window *window, int *top, int *left, int *bottom, int *right);
void (*GetWindowSizeInPixels)(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h);
int (*SetWindowOpacity)(SDL_VideoDevice *_this, SDL_Window *window, float opacity);
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 7f30b46ffa1b6..1d4911941e654 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -2504,6 +2504,10 @@ int SDL_RecreateWindow(SDL_Window *window, SDL_WindowFlags flags)
_this->SetWindowMaximumSize(_this, window);
}
+ if (_this->SetWindowAspectRatio && (window->min_aspect > 0.0f || window->max_aspect > 0.0f)) {
+ _this->SetWindowAspectRatio(_this, window);
+ }
+
if (window->hit_test) {
_this->SetWindowHitTest(window, SDL_TRUE);
}
@@ -2776,6 +2780,7 @@ int SDL_SetWindowAlwaysOnTop(SDL_Window *window, SDL_bool on_top)
int SDL_SetWindowSize(SDL_Window *window, int w, int h)
{
CHECK_WINDOW_MAGIC(window, -1);
+
if (w <= 0) {
return SDL_InvalidParamError("w");
}
@@ -2783,6 +2788,16 @@ int SDL_SetWindowSize(SDL_Window *window, int w, int h)
return SDL_InvalidParamError("h");
}
+ /* It is possible for the aspect ratio contraints to not satisfy the size constraints. */
+ /* The size constraints will override the aspect ratio contraints so we will apply the */
+ /* the aspect ratio constraints first */
+ float new_aspect = w / (float)h;
+ if (window->max_aspect > 0.0f && new_aspect > window->max_aspect) {
+ w = (int)SDL_roundf(h * window->max_aspect);
+ } else if (window->min_aspect > 0.0f && new_aspect < window->min_aspect) {
+ h = (int)SDL_roundf(w / window->min_aspect);
+ }
+
/* Make sure we don't exceed any window size limits */
if (window->min_w && w < window->min_w) {
w = window->min_w;
@@ -2821,6 +2836,31 @@ int SDL_GetWindowSize(SDL_Window *window, int *w, int *h)
return 0;
}
+int SDL_SetWindowAspectRatio(SDL_Window *window, float min_aspect, float max_aspect)
+{
+ CHECK_WINDOW_MAGIC(window, -1);
+
+ window->min_aspect = min_aspect;
+ window->max_aspect = max_aspect;
+ if (_this->SetWindowAspectRatio) {
+ _this->SetWindowAspectRatio(_this, window);
+ }
+ return SDL_SetWindowSize(window, window->floating.w, window->floating.h);
+}
+
+int SDL_GetWindowAspectRatio(SDL_Window *window, float *min_aspect, float *max_aspect)
+{
+ CHECK_WINDOW_MAGIC(window, -1);
+
+ if (min_aspect) {
+ *min_aspect = window->min_aspect;
+ }
+ if (max_aspect) {
+ *max_aspect = window->max_aspect;
+ }
+ return 0;
+}
+
int SDL_GetWindowBordersSize(SDL_Window *window, int *top, int *left, int *bottom, int *right)
{
int dummy = 0;
diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m
index 067c23af4c0ca..005f75ed74f14 100644
--- a/src/video/cocoa/SDL_cocoawindow.m
+++ b/src/video/cocoa/SDL_cocoawindow.m
@@ -986,6 +986,27 @@ - (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize
_data.checking_zoom = NO;
}
+ if (window->min_aspect > 0.0f || window->max_aspect > 0.0f) {
+ NSWindow *nswindow = _data.nswindow;
+ NSRect newContentRect = [nswindow contentRectForFrameRect:NSMakeRect(0, 0, frameSize.width, frameSize.height)];
+ NSSize newSize = newContentRect.size;
+ CGFloat minAspectRatio = window->min_aspect;
+ CGFloat maxAspectRatio = window->max_aspect;
+ CGFloat aspectRatio;
+
+ if (newSize.height > 0) {
+ aspectRatio = newSize.width / newSize.height;
+
+ if (maxAspectRatio > 0.0f && aspectRatio > maxAspectRatio) {
+ newSize.width = (int)SDL_roundf(newSize.height * maxAspectRatio);
+ } else if (minAspectRatio > 0.0f && aspectRatio < minAspectRatio) {
+ newSize.height = (int)SDL_roundf(newSize.width / minAspectRatio);
+ }
+
+ NSRect newFrameRect = [nswindow frameRectForContentRect:NSMakeRect(0, 0, newSize.width, newSize.height)];
+ frameSize = newFrameRect.size;
+ }
+ }
return frameSize;
}
diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c
index 3aff7ed0dbaaa..b4dda500b1034 100644
--- a/src/video/windows/SDL_windowsevents.c
+++ b/src/video/windows/SDL_windowsevents.c
@@ -1479,6 +1479,134 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
KillTimer(hwnd, (UINT_PTR)SDL_IterateMainCallbacks);
} break;
+ case WM_SIZING:
+ {
+ Uint32 edge = wParam;
+ RECT* dragRect = (RECT*)lParam;
+ RECT clientDragRect = *dragRect;
+ SDL_bool lock_aspect_ratio = (data->window->max_aspect == data->window->min_aspect) ? SDL_TRUE : SDL_FALSE;
+ RECT rc;
+ LONG w, h;
+ float new_aspect;
+
+ /* if aspect ratio constraints are not enabled then skip this message */
+ if (data->window->min_aspect <= 0 && data->window->max_aspect <= 0) {
+ break;
+ }
+
+ /* unadjust the dragRect from the window rect to the client rect */
+ SetRectEmpty(&rc);
+ if (!AdjustWindowRectEx(&rc, GetWindowStyle(hwnd), GetMenu(hwnd) != NULL, GetWindowExStyle(hwnd))) {
+ break;
+ }
+
+ clientDragRect.left -= rc.left;
+ clientDragRect.top -= rc.top;
+ clientDragRect.right -= rc.right;
+ clientDragRect.bottom -= rc.bottom;
+
+ w = clientDragRect.right - clientDragRect.left;
+ h = clientDragRect.bottom - clientDragRect.top;
+ new_aspect = w / (float)h;
+
+ /* handle the special case in which the min ar and max ar are the same so the window can size symmetrically */
+ if (lock_aspect_ratio) {
+ switch (edge) {
+ case WMSZ_LEFT:
+ case WMSZ_RIGHT:
+ h = (int)(w / data->window->max_aspect);
+ break;
+ default:
+ /* resizing via corners or top or bottom */
+ w = (int)(h*data->window->max_aspect);
+ break;
+ }
+ } else {
+ switch (edge) {
+ case WMSZ_LEFT:
+ case WMSZ_RIGHT:
+ if (data->window->max_aspect > 0.0f && new_aspect > data->window->max_aspect) {
+ w = (int)SDL_roundf(h * data->window->max_aspect);
+ } else if (data->window->min_aspect > 0.0f && new_aspect < data->window->min_aspect) {
+ w = (int)SDL_roundf(h * data->window->min_aspect);
+ }
+ break;
+ case WMSZ_TOP:
+ case WMSZ_BOTTOM:
+ if (data->window->min_aspect > 0.0f && new_aspect < data->window->min_aspect) {
+ h = (int)SDL_roundf(w / data->window->min_aspect);
+ } else if (data->window->max_aspect > 0.0f && new_aspect > data->window->max_aspect) {
+ h = (int)SDL_roundf(w / data->window->max_aspect);
+ }
+ break;
+
+ default:
+ /* resizing via corners */
+ if (data->window->max_aspect > 0.0f && new_aspect > data->window->max_aspect) {
+ w = (int)SDL_roundf(h * data->window->max_aspect);
+ } else if (data->window->min_aspect > 0.0f && new_aspect < data->window->min_aspect) {
+ h = (int)SDL_roundf(w / data->window->min_aspect);
+ }
+ break;
+ }
+ }
+
+ switch (edge) {
+ case WMSZ_LEFT:
+ clientDragRect.left = clientDragRect.right - w;
+ if (lock_aspect_ratio) {
+ clientDragRect.top = (clientDragRect.bottom + clientDragRect.top - h) / 2;
+ }
+ clientDragRect.bottom = h + clientDragRect.top;
+ break;
+ case WMSZ_BOTTOMLEFT:
+ clientDragRect.left = clientDragRect.right - w;
+ clientDragRect.bottom = h + clientDragRect.top;
+ break;
+ case WMSZ_RIGHT:
+ clientDragRect.right = w + clientDragRect.left;
+ if (lock_aspect_ratio) {
+ clientDragRect.top = (clientDragRect.bottom + clientDragRect.top - h) / 2;
+ }
+ clientDragRect.bottom = h + clientDragRect.top;
+ break;
+ case WMSZ_TOPRIGHT:
+ clientDragRect.right = w + clientDragRect.left;
+ clientDragRect.top = clientDragRect.bottom - h;
+ break;
+ case WMSZ_TOP:
+ if (lock_aspect_ratio) {
+ clientDragRect.left = (clientDragRect.right + clientDragRect.left - w) / 2;
+ }
+ clientDragRect.right = w + clientDragRect.left;
+ clientDragRect.top = clientDragRect.bottom - h;
+ break;
+ case WMSZ_TOPLEFT:
+ clientDragRect.left = clientDragRect.right - w;
+ clientDragRect.top = clientDragRect.bottom - h;
+ break;
+ case WMSZ_BOTTOM:
+ if (lock_aspect_ratio) {
+ clientDragRect.left = (clientDragRect.right + clientDragRect.left - w) / 2;
+ }
+ clientDragRect.right = w + clientDragRect.left;
+ clientDragRect.bottom = h + clientDragRect.top;
+ break;
+ case WMSZ_BOTTOMRIGHT:
+ clientDragRect.right = w + clientDragRect.left;
+ clientDragRect.bottom = h + clientDragRect.top;
+ break;
+ }
+
+ /* convert the client rect to a window rect */
+ if (!AdjustWindowRectEx(&clientDragRect, GetWindowStyle(hwnd), GetMenu(hwnd) != NULL, GetWindowExStyle(hwnd))) {
+ break;
+ }
+
+ *dragRect = clientDragRect;
+ }
+ break;
+
case WM_SETCURSOR:
{
Uint16 hittest;
diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c
index 952c501d97e74..4a2692faa4cb0 100644
--- a/src/video/x11/SDL_x11video.c
+++ b/src/video/x11/SDL_x11video.c
@@ -194,6 +194,7 @@ static SDL_VideoDevice *X11_CreateDevice(void)
device->SetWindowSize = X11_SetWindowSize;
device->SetWindowMinimumSize = X11_SetWindowMinimumSize;
device->SetWindowMaximumSize = X11_SetWindowMaximumSize;
+ device->SetWindowAspectRatio = X11_SetWindowAspectRatio;
device->GetWindowBordersSize = X11_GetWindowBordersSize;
device->SetWindowOpacity = X11_SetWindowOpacity;
device->SetWindowModalFor = X11_SetWindowModalFor;
diff --git a/src/video/x11/SDL_x11window.c b/src/video/x11/SDL_x11window.c
index e2dca2d5d083b..02737c6480d03 100644
--- a/src/video/x11/SDL_x11window.c
+++ b/src/video/x11/SDL_x11window.c
@@ -28,6 +28,7 @@
#include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_events_c.h"
#include "../../core/unix/SDL_appid.h"
+#include "../../SDL_utils_c.h"
#include "SDL_x11video.h"
#include "SDL_x11mouse.h"
@@ -1086,7 +1087,7 @@ void X11_SetWindowMinMax(SDL_Window *window, SDL_bool use_current)
long hint_flags = 0;
X11_XGetWMNormalHints(display, data->xwindow, sizehints, &hint_flags);
- sizehints->flags &= ~(PMinSize | PMaxSize);
+ sizehints->flags &= ~(PMinSize | PMaxSize | PAspect);
if (data->window->flags & SDL_WINDOW_RESIZABLE) {
if (data->window->min_w || data->window->min_h) {
@@ -1099,6 +1100,11 @@ void X11_SetWindowMinMax(SDL_Window *window, SDL_bool use_current)
sizehints->max_width = data->window->max_w;
sizehints->max_height = data->window->max_h;
}
+ if (data->window->min_aspect > 0.0f || data->window->max_aspect > 0.0f) {
+ sizehints->flags |= PAspect;
+ SDL_CalculateFraction(data->window->min_aspect, &sizehints->min_aspect.x, &sizehints->min_aspect.y);
+ SDL_CalculateFraction(data->window->max_aspect, &sizehints->max_aspect.x, &sizehints->max_aspect.y);
+ }
} else {
/* Set the min/max to the same values to make the window non-resizable */
sizehints->flags |= PMinSize | PMaxSize;
@@ -1132,6 +1138,17 @@ void X11_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *window)
}
}
+void X11_SetWindowAspectRatio(SDL_VideoDevice *_this, SDL_Window *window)
+{
+ if (window->driverdata->pending_operation & X11_PENDING_OP_FULLSCREEN) {
+ X11_SyncWindow(_this, window);
+ }
+
+ if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
+ X11_SetWindowMinMax(window, SDL_TRUE);
+ }
+}
+
void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *data = window->driverdata;
@@ -1645,7 +1662,7 @@ static int X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this, SDL_Window *wind
long flags = 0;
X11_XGetWMNormalHints(display, data->xwindow, sizehints, &flags);
/* we are going fullscreen so turn the flags off */
- sizehints->flags &= ~(PMinSize | PMaxSize);
+ sizehints->flags &= ~(PMinSize | PMaxSize | PAspect);
X11_XSetWMNormalHints(display, data->xwindow, sizehints);
X11_XFree(sizehints);
}
diff --git a/src/video/x11/SDL_x11window.h b/src/video/x11/SDL_x11window.h
index 58b2a253d34a0..204c7e4af5b4f 100644
--- a/src/video/x11/SDL_x11window.h
+++ b/src/video/x11/SDL_x11window.h
@@ -114,6 +114,7 @@ extern int X11_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Sur
extern int X11_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_SetWindowMinimumSize(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *window);
+extern void X11_SetWindowAspectRatio(SDL_VideoDevice *_this, SDL_Window *window);
extern int X11_GetWindowBordersSize(SDL_VideoDevice *_this, SDL_Window *window, int *top, int *left, int *bottom, int *right);
extern int X11_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity);
extern int X11_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window
(Patch may be truncated, please check the link at the top of this post.)