Fix graphics flickering/stretching on Windows (#367)

* DRY, standardized window top offset
* handle_splash_events() handle multiple events per frame
* accurate windows menubar height for multiple rows
* Windows filter a resize event triggered by the menubar
* windows expand small window to fit menubar
* splash screens draw in view rect, not window rect
This commit is contained in:
2024-06-27 06:40:34 -06:00
committed by GitHub
parent d6ae801203
commit c251fee834
12 changed files with 109 additions and 76 deletions

View File

@@ -145,12 +145,9 @@ void adjust_window_mode() {
// 0 - center 1- ul 2 - ur 3 - dl 4 - dr 5 - small win
int mode = get_int_pref("DisplayMode");
if(mode == 5) {
// Increase window height to make room for the menubar
// Increase window height to make room for the menubar on Linux
int winHeight = height;
#ifndef _WIN32
// Not on Windows, for some reason
winHeight += getMenubarHeight();
#endif
winHeight += os_specific_y_offset();
mainPtr.create(sf::VideoMode(width, winHeight, 32), "Blades of Exile", sf::Style::Titlebar | sf::Style::Close, winSettings);
@@ -179,6 +176,7 @@ void adjust_window_mode() {
init_fileio();
#endif
init_menubar();
adjust_window_for_menubar(mode, width, height);
showMenuBar();
}
@@ -190,16 +188,6 @@ sf::FloatRect compute_viewport(const sf::RenderWindow& mainPtr, int mode, float
// Dimensions of the OS window.
rectangle windRect { mainPtr };
// This is an additional offset between the "logical" top of the window an the UI.
// On Windows and Mac no offset is needed because the menubar is not a part of the mainPtr, but
// on Linux it is.
int os_specific_y_offset =
#if defined(SFML_SYSTEM_WINDOWS) || defined(SFML_SYSTEM_MAC)
0;
#else
getMenubarHeight();
#endif
// Width and height: how large the viewport is. They seem to be calculated
// in terms of *source* dimensions, with values above 1 resulting in an upscale.
viewport.width = ui_scale * width / windRect.width();
@@ -215,15 +203,15 @@ sf::FloatRect compute_viewport(const sf::RenderWindow& mainPtr, int mode, float
if(mode == 0) {
// Fullscreen centered
viewport.left = float((windRect.width() - width) / 2) / windRect.width();
viewport.top = float((windRect.height() - height - os_specific_y_offset) / 2)/ windRect.height();
viewport.top = float((windRect.height() - height - os_specific_y_offset()) / 2)/ windRect.height();
} else if(mode == 1) {
// Fullscreen top left
viewport.left = float(extra_horizontal_buffer) / windRect.width();
viewport.top = float(os_specific_y_offset) / windRect.height();
viewport.top = float(os_specific_y_offset()) / windRect.height();
} else if(mode == 2) {
// Fullscreen top right
viewport.left = float(windRect.right - width - extra_horizontal_buffer) / windRect.width();
viewport.top = float(os_specific_y_offset) / windRect.height();
viewport.top = float(os_specific_y_offset()) / windRect.height();
} else if(mode == 3) {
// Fullscreen bottom left
viewport.left = float(extra_horizontal_buffer) / windRect.width();
@@ -231,16 +219,16 @@ sf::FloatRect compute_viewport(const sf::RenderWindow& mainPtr, int mode, float
// there could be a windows taskbar / mac os dock / xfce taskbar / etc that consumes a part
// of that display, and that we do not know size of. So we need to account for that somehow,
// so we add 28 more pixels (this was the amount in the previous version of this code).
viewport.top = float(windRect.bottom - height - os_specific_y_offset - 28) / windRect.height();
viewport.top = float(windRect.bottom - height - os_specific_y_offset() - 28) / windRect.height();
} else if(mode == 4) {
// Fullscreen bottom right
viewport.left = float(windRect.right - width - extra_horizontal_buffer) / windRect.width();
// DIRTY HACK: same as for mode 3
viewport.top = float(windRect.bottom - height - os_specific_y_offset - 28) / windRect.height();
viewport.top = float(windRect.bottom - height - os_specific_y_offset() - 28) / windRect.height();
} else if(mode == 5) {
// Small windowed
viewport.left = 0;
viewport.top = float(os_specific_y_offset) / windRect.height();
viewport.top = float(os_specific_y_offset()) / windRect.height();
}
return viewport;

View File

@@ -51,6 +51,7 @@ void draw_startup_stats();
void draw_trim(short q,short r,short which_trim,ter_num_t ground_ter);
sf::FloatRect compute_viewport(const sf::RenderWindow&, int mode, float ui_scale, float width, float height);
void draw_startup_anim(bool advance);

View File

@@ -69,17 +69,12 @@ void init_menubar() {
if(winHandle == NULL) return;
if(menuHandle == NULL)
menuHandle = LoadMenu(GetModuleHandle(NULL), MAKEINTRESOURCE(IDC_BLADESOFEXILE));
SetMenu(winHandle, menuHandle);
// Now we have to do a little hack to handle menu messages.
// We replace SFML's window procedure with our own, which checks for menu events and then forwards to SFML's procedure.
mainProc = SetWindowLongPtr(winHandle, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&menuProc));
SetMenu(winHandle, menuHandle);
mainPtr.setActive();
// Fix the window's viewport so that everything is drawn correctly
sf::Vector2u sz = mainPtr.getSize();
double menubarHeight = getMenubarHeight();
double usableHeight = sz.y - menubarHeight;
sf::View view(sf::FloatRect(0, 0, sz.x, usableHeight));
mainPtr.setView(view);
// And now initialize the mapping from Windows menu commands to eMenu constants
static bool inited = false;
@@ -291,6 +286,16 @@ void showMenuBar() {
LRESULT CALLBACK menuProc(HWND handle, UINT message, WPARAM wParam, LPARAM lParam) {
MSG msg = {handle, message, wParam, lParam};
// Adding the menubar to the window for the first time triggers an unwanted
// resizing of the window, which we skip processing because it skews our
// viewport:
static bool menubarTriggeredResize = false;
if(message == WM_SIZE && !menubarTriggeredResize) {
menubarTriggeredResize = true;
return true;
}
if(HIWORD(wParam) != 1 || message != WM_COMMAND) {
if(TranslateAccelerator(handle, accel.handle, &msg))
return 0;

View File

@@ -109,18 +109,23 @@ bool handle_startup_press(location the_point) {
void handle_splash_events() {
sf::Event event;
if(!mainPtr.pollEvent(event)) return;
if(event.type == sf::Event::GainedFocus || event.type == sf::Event::MouseMoved)
set_cursor(sword_curs);
while(mainPtr.pollEvent(event)) {
if(event.type == sf::Event::GainedFocus || event.type == sf::Event::MouseMoved)
set_cursor(sword_curs);
}
}
rectangle view_rect() {
sf::Vector2f size = mainPtr.getView().getSize();
return rectangle(0, 0, size.y, size.x);
}
void show_logo() {
rectangle whole_window;
rectangle whole_window = view_rect();
if(get_int_pref("DisplayMode") != 5)
hideMenuBar();
whole_window = rectangle(mainPtr);
double ui_scale = get_float_pref("UIScale", 1.0);
if(ui_scale < 1) ui_scale = 1;
rectangle logo_from = {0, 0, int(ui_scale *350), int(ui_scale * 350)};
@@ -141,11 +146,12 @@ void show_logo() {
}
void plop_fancy_startup() {
rectangle whole_window = view_rect();
float ui_scale = get_float_pref("UIScale", 1.0);
if (ui_scale<1) ui_scale=1;
rectangle whole_window,from_rect;
rectangle from_rect;
rectangle intro_from = {0, 0, int(ui_scale * 480), int(ui_scale * 640)};
whole_window = rectangle(mainPtr);
sf::Time delay = time_in_ticks(220);
intro_from.offset((whole_window.right - intro_from.right) / 2,(whole_window.bottom - intro_from.bottom) / 2);
sf::Texture& pict_to_draw = *ResMgr::graphics.get("startsplash", true);