SDL2:调整 window 大小时如何保持纵横比
SDL2: How to keep aspect ratio when resizing the window
我正在尝试创建一个 SDL window,它在发生调整大小事件时保持其纵横比。如果用户加宽 window,高度会增加,反之亦然。我捕获 SDL_WINDOWEVENT_RESIZED 事件,计算保持纵横比的新宽度或高度,然后使用计算值调用 SDL_SetWindowSize()。
问题是在事件轮询循环中调用 SDL_SetWindowSize() 函数在屏幕上没有任何反应。 SDL 确实更新了 window 大小变量(在我的主循环中调用 SDL_GetWindowSize() returns 更新的 window 维度)。但是,实际的window并没有更新。
唯一能让它工作的方法是在主循环中不断调用 SDL_SetWindowSize(),但我认为这是错误的做事方式。下面的代码说明了我的问题。有没有更好更简洁的方法来让它工作?
我在 GNOME 桌面上使用 SDL 2.0.3 和 64 位 Ubuntu Linux。
#include <SDL2/SDL.h>
static const float ASPECT_RATIO = 16.f/9.f;
SDL_Window* window;
SDL_Renderer* renderer;
uint32_t windowID;
SDL_Rect screen;
bool done = false;
bool resizeDone = false;
void handle_events()
{
SDL_Event e;
while (SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_WINDOWEVENT:
if(e.window.windowID == windowID) {
switch(e.window.event) {
case SDL_WINDOWEVENT_RESIZED: {
int width = e.window.data1;
int height = e.window.data2;
float aspectRatio = (float)width/(float)height;
if(aspectRatio != ASPECT_RATIO) {
if(aspectRatio > ASPECT_RATIO) {
height = (1.f / ASPECT_RATIO) * width;
}
else {
width = ASPECT_RATIO * height;
}
printf("Setting window size to %d, %d, aspect ratio: %f\n",
width, height, (float)width/(float)height);
}
screen.w = width;
screen.h = height;
SDL_SetWindowSize(window, width, height); // <-- does not work
resizeDone = true;
break;
}
}
}
break;
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
}
void run() {
while(!done) {
//SDL_SetWindowSize(window, screen.w, screen.h); // <-- works
handle_events();
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
if(resizeDone) {
int w, h;
SDL_GetWindowSize(window, &w, &h);
printf("SDL_GetWindowSize: %d, %d\n", w, h);
resizeDone = false;
}
}
}
int main(int, char**) {
SDL_Init(SDL_INIT_VIDEO);
uint32_t window_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
window = SDL_CreateWindow("Test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags);
windowID = SDL_GetWindowID(window);
renderer = SDL_CreateRenderer(window, -1, 0);
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
run();
SDL_Quit();
return 0;
}
一些 window 经理似乎忽略了在 WM 本身调整大小时 window 时发出的调整大小请求(例如,按住鼠标按钮时)。相反,SDL_GetWindowSize
returns 缓存值,在特定情况下有时恰好是错误的。
除了以防万一在每一帧上不断调用 SDL_SetWindowSize
之外,我看不到任何与平台无关的方法来实现这一点。不过,它可以使用特定于平台的 API 来实现(比如 SDL_GetWindowSysWMInfo
然后使用 Xlib)。
在macOS上,我是这样解决的:
cocoa.m:
#import <Cocoa/Cocoa.h>
void SetWindowRatio(void *window) {
NSWindow *win = (__bridge NSWindow*) window;
win.aspectRatio = NSMakeSize( 1280, 720 );
}
main.cpp:
#include <SDL.h>
#include <SDL_syswm.h>
extern "C" void SetWindowRatio(void *window);
// and later..
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(sdl.window, &wmInfo);
SetWindowRatio(wmInfo.info.cocoa.window);
也许可以在 Linux 上做类似的事情,只访问 wmInfo.info 的不同部分。并调用本机函数?
我正在尝试创建一个 SDL window,它在发生调整大小事件时保持其纵横比。如果用户加宽 window,高度会增加,反之亦然。我捕获 SDL_WINDOWEVENT_RESIZED 事件,计算保持纵横比的新宽度或高度,然后使用计算值调用 SDL_SetWindowSize()。
问题是在事件轮询循环中调用 SDL_SetWindowSize() 函数在屏幕上没有任何反应。 SDL 确实更新了 window 大小变量(在我的主循环中调用 SDL_GetWindowSize() returns 更新的 window 维度)。但是,实际的window并没有更新。
唯一能让它工作的方法是在主循环中不断调用 SDL_SetWindowSize(),但我认为这是错误的做事方式。下面的代码说明了我的问题。有没有更好更简洁的方法来让它工作?
我在 GNOME 桌面上使用 SDL 2.0.3 和 64 位 Ubuntu Linux。
#include <SDL2/SDL.h>
static const float ASPECT_RATIO = 16.f/9.f;
SDL_Window* window;
SDL_Renderer* renderer;
uint32_t windowID;
SDL_Rect screen;
bool done = false;
bool resizeDone = false;
void handle_events()
{
SDL_Event e;
while (SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_WINDOWEVENT:
if(e.window.windowID == windowID) {
switch(e.window.event) {
case SDL_WINDOWEVENT_RESIZED: {
int width = e.window.data1;
int height = e.window.data2;
float aspectRatio = (float)width/(float)height;
if(aspectRatio != ASPECT_RATIO) {
if(aspectRatio > ASPECT_RATIO) {
height = (1.f / ASPECT_RATIO) * width;
}
else {
width = ASPECT_RATIO * height;
}
printf("Setting window size to %d, %d, aspect ratio: %f\n",
width, height, (float)width/(float)height);
}
screen.w = width;
screen.h = height;
SDL_SetWindowSize(window, width, height); // <-- does not work
resizeDone = true;
break;
}
}
}
break;
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
}
void run() {
while(!done) {
//SDL_SetWindowSize(window, screen.w, screen.h); // <-- works
handle_events();
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
if(resizeDone) {
int w, h;
SDL_GetWindowSize(window, &w, &h);
printf("SDL_GetWindowSize: %d, %d\n", w, h);
resizeDone = false;
}
}
}
int main(int, char**) {
SDL_Init(SDL_INIT_VIDEO);
uint32_t window_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
window = SDL_CreateWindow("Test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags);
windowID = SDL_GetWindowID(window);
renderer = SDL_CreateRenderer(window, -1, 0);
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
run();
SDL_Quit();
return 0;
}
一些 window 经理似乎忽略了在 WM 本身调整大小时 window 时发出的调整大小请求(例如,按住鼠标按钮时)。相反,SDL_GetWindowSize
returns 缓存值,在特定情况下有时恰好是错误的。
除了以防万一在每一帧上不断调用 SDL_SetWindowSize
之外,我看不到任何与平台无关的方法来实现这一点。不过,它可以使用特定于平台的 API 来实现(比如 SDL_GetWindowSysWMInfo
然后使用 Xlib)。
在macOS上,我是这样解决的:
cocoa.m:
#import <Cocoa/Cocoa.h>
void SetWindowRatio(void *window) {
NSWindow *win = (__bridge NSWindow*) window;
win.aspectRatio = NSMakeSize( 1280, 720 );
}
main.cpp:
#include <SDL.h>
#include <SDL_syswm.h>
extern "C" void SetWindowRatio(void *window);
// and later..
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(sdl.window, &wmInfo);
SetWindowRatio(wmInfo.info.cocoa.window);
也许可以在 Linux 上做类似的事情,只访问 wmInfo.info 的不同部分。并调用本机函数?