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);

View File

@@ -142,16 +142,9 @@ int main(int argc, char* argv[]) {
sf::FloatRect compute_viewport(const sf::RenderWindow& mainPtr, float ui_scale) {
// See compute_viewport() in boe.graphics.cpp
int const os_specific_y_offset =
#if defined(SFML_SYSTEM_WINDOWS) || defined(SFML_SYSTEM_MAC)
0;
#else
getMenubarHeight();
#endif
sf::FloatRect viewport;
viewport.top = float(os_specific_y_offset) / mainPtr.getSize().y;
viewport.top = float(os_specific_y_offset()) / mainPtr.getSize().y;
viewport.left = 0;
viewport.width = ui_scale;
viewport.height = ui_scale;
@@ -164,11 +157,7 @@ void adjust_window (sf::RenderWindow& mainPtr, sf::View& mainView) {
double ui_scale = get_float_pref("UIScale", 1.0);
int const width = ui_scale * 590;
int const height = ui_scale * 440
#ifndef SFML_SYSTEM_WINDOWS
+ getMenubarHeight()
#endif
;
int const height = ui_scale * 440 + os_specific_y_offset();
mainPtr.create(sf::VideoMode(width, height), "Blades of Exile Character Editor", sf::Style::Titlebar | sf::Style::Close);
sf::VideoMode desktop = sf::VideoMode::getDesktopMode();
@@ -191,6 +180,7 @@ void adjust_window (sf::RenderWindow& mainPtr, sf::View& mainView) {
#endif
init_menubar();
adjust_window_for_menubar(5, width, height);
}
void handle_events() {

View File

@@ -55,16 +55,10 @@ void init_menubar() {
if(winHandle == NULL) return;
if(menuHandle == NULL)
menuHandle = LoadMenu(GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_MENU1));
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));
// 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);
SetMenu(winHandle, menuHandle);
// And now initialize the mapping from Windows menu commands to eMenu constants
static bool inited = false;
@@ -130,6 +124,16 @@ void menu_activate() {
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

@@ -144,16 +144,9 @@ static void init_scrollbars() {
sf::FloatRect compute_viewport(const sf::RenderWindow & mainPtr, float ui_scale) {
// See compute_viewport() in boe.graphics.cpp
const int os_specific_y_offset =
#if defined(SFML_SYSTEM_WINDOWS) || defined(SFML_SYSTEM_MAC)
0;
#else
getMenubarHeight();
#endif
sf::FloatRect viewport;
viewport.top = float(os_specific_y_offset) / mainPtr.getSize().y;
viewport.top = float(os_specific_y_offset()) / mainPtr.getSize().y;
viewport.left = 0;
viewport.width = ui_scale;
viewport.height = ui_scale;
@@ -169,11 +162,7 @@ void adjust_windows (sf::RenderWindow & mainPtr, sf::View & mainView) {
double ui_scale = get_float_pref("UIScale", 1.0);
const int width = ui_scale * 584;
const int height = ui_scale * 420
#ifndef SFML_SYSTEM_WINDOWS
+ getMenubarHeight()
#endif
;
const int height = ui_scale * 420 + os_specific_y_offset();
mainPtr.create(sf::VideoMode(width, height), "Blades of Exile Scenario Editor", sf::Style::Titlebar | sf::Style::Close);
sf::VideoMode desktop = sf::VideoMode::getDesktopMode();
@@ -195,6 +184,7 @@ void adjust_windows (sf::RenderWindow & mainPtr, sf::View & mainView) {
mainPtr.setIcon(icon->getSize().x, icon->getSize().y, icon->copyToImage().getPixelsPtr());
#endif
init_menubar();
adjust_window_for_menubar(5, width, height);
}
void process_args(int argc, char* argv[]) {

View File

@@ -58,16 +58,10 @@ void init_menubar() {
if(winHandle == NULL) return;
if(menuHandle == NULL)
menuHandle = LoadMenu(GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_MENU1));
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));
// 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);
SetMenu(winHandle, menuHandle);
// And now initialize the mapping from Windows menu commands to eMenu constants
static bool inited = false;
@@ -218,6 +212,16 @@ void update_edit_menu() {
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

@@ -46,6 +46,20 @@ void beep();
// Calculates how much of the window is occupied by the menubar
int getMenubarHeight();
// 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.
inline int os_specific_y_offset() {
return
#if defined(SFML_SYSTEM_WINDOWS) || defined(SFML_SYSTEM_MAC)
0;
#else
getMenubarHeight();
#endif
}
void adjust_window_for_menubar(int mode, unsigned int width, unsigned int height);
class ModalSession {
void* session;
sf::Window* parent;

View File

@@ -227,3 +227,6 @@ int getMenubarHeight() {
// return MENUBAR_HEIGHT;
return 20;
}
void adjust_window_for_menubar(int mode, unsigned int width, unsigned int height) {
}

View File

@@ -182,6 +182,9 @@ int getMenubarHeight() {
return 0;
}
void adjust_window_for_menubar(int mode, unsigned int width, unsigned int height) {
}
NSOpenPanel* dlg_get_scen;
NSOpenPanel* dlg_get_game;
NSOpenPanel* dlg_get_rsrc;

View File

@@ -444,5 +444,30 @@ ModalSession::~ModalSession() {
}
int getMenubarHeight() {
return GetSystemMetrics(SM_CYMENU);
MENUBARINFO info;
info.cbSize = sizeof(MENUBARINFO);
if(GetMenuBarInfo(mainPtr.getSystemHandle(), OBJID_MENU, 0, &info)) {
return info.rcBar.bottom - info.rcBar.top;
} else {
return GetSystemMetrics(SM_CYMENU);
}
}
void adjust_window_for_menubar(int mode, unsigned int width, unsigned int height) {
// On Windows, the menubar DOES take up space in the window,
// but this is not handled with os_specific_y_offset() because
// y = 0 still refers to the bottom of the menubar on Windows.
// getMenuBarHeight() has to be called again AFTER init_menubar() for this,
// because different combinations of OS and BOE scaling options can
// result in a menubar with more than one row, which can only be measured
// after it is placed in the window
if(mode != 5) {
sf::VideoMode desktop = sf::VideoMode::getDesktopMode();
width = desktop.width;
height = desktop.height;
}
height += getMenubarHeight();
mainPtr.setSize({ width, height });
}