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:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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[]) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -227,3 +227,6 @@ int getMenubarHeight() {
|
||||
// return MENUBAR_HEIGHT;
|
||||
return 20;
|
||||
}
|
||||
|
||||
void adjust_window_for_menubar(int mode, unsigned int width, unsigned int height) {
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
Reference in New Issue
Block a user