Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix graphics flickering/stretching on Windows #367

Merged
merged 12 commits into from
Jun 27, 2024
30 changes: 9 additions & 21 deletions src/game/boe.graphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -174,6 +171,7 @@ void adjust_window_mode() {
#endif

init_menubar();
adjust_window_for_menubar(mode, width, height);
showMenuBar();
}

Expand All @@ -185,16 +183,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();
Expand All @@ -210,32 +198,32 @@ 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();
// DIRTY HACK: windRect in fullscreen modes gives us the entire display, but at the same time
// 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;
Expand Down
1 change: 1 addition & 0 deletions src/game/boe.graphics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);


Expand Down
19 changes: 12 additions & 7 deletions src/game/boe.menus.win.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
20 changes: 13 additions & 7 deletions src/game/boe.startup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)};
Expand All @@ -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);
Expand Down
16 changes: 3 additions & 13 deletions src/pcedit/pc.main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
Expand All @@ -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() {
Expand Down
18 changes: 11 additions & 7 deletions src/pcedit/pc.menus.win.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
16 changes: 3 additions & 13 deletions src/scenedit/scen.main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
Expand All @@ -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[]) {
Expand Down
18 changes: 11 additions & 7 deletions src/scenedit/scen.menus.win.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
14 changes: 14 additions & 0 deletions src/tools/winutil.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions src/tools/winutil.linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,6 @@ int getMenubarHeight() {
// return MENUBAR_HEIGHT;
return 20;
}

void adjust_window_for_menubar(int mode, unsigned int width, unsigned int height) {
}
3 changes: 3 additions & 0 deletions src/tools/winutil.mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
27 changes: 26 additions & 1 deletion src/tools/winutil.win.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
}
Loading