mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-10-05 09:48:11 -07:00
3f0921d983
## Link to GitHub Issue or related Pull Request, if one exists User reported ## Description of change Sometimes the game moved the main window after `CreateWindowExA` returned but before D3D initialization called `graphics_capture_initial_window`. During this gap, `GRAPHICS_HOOKED_WINDOW` was not set, so the placement hooks could not identify the main window and preserve its monitor override. GITADORA main, LEFT, and RIGHT windows are now registered immediately after creation so later placement calls cannot undo their overrides. This also adds `-gdwsmallsize` and `-gdwsmallpos` for explicitly setting the SMALL window size and position. Explicit values override the corresponding monitor-derived geometry, allowing the SMALL window's 10:16 aspect ratio to be preserved instead of stretching it to fill a monitor. ## Testing
870 lines
28 KiB
C++
870 lines
28 KiB
C++
#include "graphics.h"
|
|
|
|
#include "avs/game.h"
|
|
#include "games/gitadora/gitadora.h"
|
|
#include "cfg/screen_resize.h"
|
|
#include "overlay/overlay.h"
|
|
#include "util/logging.h"
|
|
#include "util/utils.h"
|
|
#include "touch/touch.h"
|
|
|
|
#if 0
|
|
#define log_debug(module, format_str, ...) logger::push( \
|
|
LOG_FORMAT("M", module, format_str, ## __VA_ARGS__), logger::Style::GREY)
|
|
#else
|
|
#define log_debug(module, format_str, ...)
|
|
#endif
|
|
|
|
void graphics_load_windowed_parameters();
|
|
void graphics_wm_style_changed(HWND hWnd, bool changed);
|
|
void graphics_wm_sizing_aspect_ratio(int edge, RECT& rect);
|
|
|
|
std::optional<int> GRAPHICS_WINDOW_STYLE;
|
|
std::optional<std::pair<uint32_t, uint32_t>> GRAPHICS_WINDOW_SIZE;
|
|
std::optional<std::string> GRAPHICS_WINDOW_POS;
|
|
bool GRAPHICS_WINDOW_ALWAYS_ON_TOP = false;
|
|
bool GRAPHICS_WINDOW_BACKBUFFER_SCALE = false;
|
|
bool GRAPHICS_WINDOW_DISABLE_ROUNDED_CORNERS = false;
|
|
std::optional<HWND> GRAPHICS_HOOKED_WINDOW;
|
|
std::string GRAPHICS_GITADORA_MAIN_MONITOR;
|
|
std::string GRAPHICS_GITADORA_LEFT_MONITOR;
|
|
std::string GRAPHICS_GITADORA_RIGHT_MONITOR;
|
|
std::string GRAPHICS_GITADORA_SMALL_MONITOR;
|
|
std::optional<std::string> GRAPHICS_GITADORA_SMALL_SIZE;
|
|
std::optional<std::string> GRAPHICS_GITADORA_SMALL_POS;
|
|
|
|
// IIDX Windowed Subscreen - starts out as false, enabled by IIDX module on pre-attach as needed
|
|
bool GRAPHICS_IIDX_WSUB = false;
|
|
std::optional<std::string> GRAPHICS_WSUB_SIZE;
|
|
std::optional<std::string> GRAPHICS_WSUB_POS;
|
|
int GRAPHICS_WSUB_WIDTH = 1280;
|
|
int GRAPHICS_WSUB_HEIGHT = 720;
|
|
int GRAPHICS_WSUB_X = 0;
|
|
int GRAPHICS_WSUB_Y = 0;
|
|
bool GRAPHICS_WSUB_BORDERLESS = false;
|
|
bool GRAPHICS_WSUB_ALWAYS_ON_TOP = false;
|
|
|
|
// these flags are carefully constructed to ensure maximum compatibility
|
|
// (e.g., DDR likes to hang when SetWindowPos is called with certain params)
|
|
static const DWORD SETWINDOWPOS_NOOP =
|
|
SWP_NOMOVE |
|
|
SWP_NOSIZE |
|
|
SWP_NOREDRAW |
|
|
SWP_NOCOPYBITS |
|
|
SWP_NOACTIVATE |
|
|
SWP_NOSENDCHANGING |
|
|
SWP_DEFERERASE |
|
|
SWP_NOZORDER |
|
|
SWP_ASYNCWINDOWPOS;
|
|
|
|
static int graphics_effective_window_decoration() {
|
|
if (GRAPHICS_WINDOW_STYLE.has_value()) {
|
|
return GRAPHICS_WINDOW_STYLE.value();
|
|
}
|
|
if (cfg::SCREENRESIZE != nullptr) {
|
|
return cfg::SCREENRESIZE->window_decoration;
|
|
}
|
|
return cfg::WindowDecorationMode::Default;
|
|
}
|
|
|
|
bool graphics_gitadora_is_borderless_windowed() {
|
|
return GRAPHICS_WINDOWED &&
|
|
graphics_effective_window_decoration() == cfg::WindowDecorationMode::Borderless;
|
|
}
|
|
|
|
static const std::string &graphics_gitadora_monitor_for_window_name(
|
|
const std::string &window_name) {
|
|
if (window_name == "GITADORA") {
|
|
return GRAPHICS_GITADORA_MAIN_MONITOR;
|
|
}
|
|
if (window_name == "LEFT") {
|
|
return GRAPHICS_GITADORA_LEFT_MONITOR;
|
|
}
|
|
if (window_name == "RIGHT") {
|
|
return GRAPHICS_GITADORA_RIGHT_MONITOR;
|
|
}
|
|
if (window_name == "SMALL") {
|
|
return GRAPHICS_GITADORA_SMALL_MONITOR;
|
|
}
|
|
|
|
static const std::string empty;
|
|
return empty;
|
|
}
|
|
|
|
bool graphics_gitadora_has_window_override(const std::string &window_name) {
|
|
const auto &monitor_name = graphics_gitadora_monitor_for_window_name(window_name);
|
|
return !monitor_name.empty() ||
|
|
(window_name == "SMALL" &&
|
|
(GRAPHICS_GITADORA_SMALL_SIZE.has_value() ||
|
|
GRAPHICS_GITADORA_SMALL_POS.has_value()));
|
|
}
|
|
|
|
static bool graphics_monitor_rect_from_name(
|
|
const std::string &monitor_name,
|
|
RECT &rect,
|
|
bool log_change) {
|
|
DEVMODEA devmode {};
|
|
devmode.dmSize = sizeof(devmode);
|
|
if (!EnumDisplaySettingsA(monitor_name.c_str(), ENUM_CURRENT_SETTINGS, &devmode)) {
|
|
if (log_change) {
|
|
log_warning(
|
|
"graphics-windowed",
|
|
"failed to get monitor settings for {}",
|
|
monitor_name);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
rect.left = devmode.dmPosition.x;
|
|
rect.top = devmode.dmPosition.y;
|
|
rect.right = rect.left + static_cast<LONG>(devmode.dmPelsWidth);
|
|
rect.bottom = rect.top + static_cast<LONG>(devmode.dmPelsHeight);
|
|
return true;
|
|
}
|
|
|
|
static bool graphics_gitadora_apply_monitor_rect(
|
|
const std::string &monitor_name,
|
|
const std::string &window_name,
|
|
int &x,
|
|
int &y,
|
|
int &width,
|
|
int &height,
|
|
bool log_change) {
|
|
if (!GRAPHICS_WINDOWED || !games::gitadora::is_arena_model()) {
|
|
return false;
|
|
}
|
|
if (monitor_name.empty()) {
|
|
return false;
|
|
}
|
|
|
|
RECT rect {};
|
|
if (!graphics_monitor_rect_from_name(monitor_name, rect, log_change)) {
|
|
return false;
|
|
}
|
|
|
|
x = rect.left;
|
|
y = rect.top;
|
|
width = rect.right - rect.left;
|
|
height = rect.bottom - rect.top;
|
|
|
|
if (log_change) {
|
|
log_info(
|
|
"graphics-windowed",
|
|
"GITADORA {} monitor override: {} => pos=({}, {}), size={}x{}",
|
|
window_name,
|
|
monitor_name,
|
|
x,
|
|
y,
|
|
width,
|
|
height);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool graphics_gitadora_apply_main_monitor_to_config() {
|
|
if (cfg::SCREENRESIZE == nullptr || !graphics_gitadora_is_borderless_windowed()) {
|
|
return false;
|
|
}
|
|
|
|
int x = 0;
|
|
int y = 0;
|
|
int width = 0;
|
|
int height = 0;
|
|
if (!graphics_gitadora_apply_monitor_rect(
|
|
GRAPHICS_GITADORA_MAIN_MONITOR,
|
|
"GITADORA",
|
|
x,
|
|
y,
|
|
width,
|
|
height,
|
|
false)) {
|
|
return false;
|
|
}
|
|
|
|
cfg::SCREENRESIZE->enable_window_resize = true;
|
|
cfg::SCREENRESIZE->client_keep_aspect_ratio = false;
|
|
cfg::SCREENRESIZE->window_offset_x = x;
|
|
cfg::SCREENRESIZE->window_offset_y = y;
|
|
cfg::SCREENRESIZE->client_width = width;
|
|
cfg::SCREENRESIZE->client_height = height;
|
|
return true;
|
|
}
|
|
|
|
void graphics_gitadora_apply_window_style(DWORD &style, DWORD &style_ex) {
|
|
if (!GRAPHICS_WINDOWED || !games::gitadora::is_arena_model()) {
|
|
return;
|
|
}
|
|
|
|
switch (graphics_effective_window_decoration()) {
|
|
case cfg::WindowDecorationMode::Borderless:
|
|
style &= ~WS_OVERLAPPEDWINDOW;
|
|
style_ex &= ~WS_EX_CLIENTEDGE;
|
|
style_ex &= ~WS_EX_WINDOWEDGE;
|
|
break;
|
|
case cfg::WindowDecorationMode::ResizableFrame:
|
|
style |= WS_OVERLAPPEDWINDOW;
|
|
break;
|
|
case cfg::WindowDecorationMode::Default:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool graphics_gitadora_apply_window_monitor(
|
|
const std::string &window_name,
|
|
int &x,
|
|
int &y,
|
|
int &width,
|
|
int &height,
|
|
bool log_change) {
|
|
if (!GRAPHICS_WINDOWED || !games::gitadora::is_arena_model()) {
|
|
return false;
|
|
}
|
|
|
|
bool applied = graphics_gitadora_apply_monitor_rect(
|
|
graphics_gitadora_monitor_for_window_name(window_name),
|
|
window_name,
|
|
x,
|
|
y,
|
|
width,
|
|
height,
|
|
log_change);
|
|
|
|
if (window_name != "SMALL") {
|
|
return applied;
|
|
}
|
|
|
|
std::pair<uint32_t, uint32_t> result;
|
|
if (GRAPHICS_GITADORA_SMALL_SIZE.has_value()) {
|
|
if (parse_width_height(GRAPHICS_GITADORA_SMALL_SIZE.value(), result)) {
|
|
width = result.first;
|
|
height = result.second;
|
|
applied = true;
|
|
} else {
|
|
log_fatal("graphics-windowed", "failed to parse -gdwsmallsize");
|
|
}
|
|
}
|
|
if (GRAPHICS_GITADORA_SMALL_POS.has_value()) {
|
|
if (parse_width_height(GRAPHICS_GITADORA_SMALL_POS.value(), result)) {
|
|
x = result.first;
|
|
y = result.second;
|
|
applied = true;
|
|
} else {
|
|
log_fatal("graphics-windowed", "failed to parse -gdwsmallpos");
|
|
}
|
|
}
|
|
|
|
if (applied && log_change &&
|
|
(GRAPHICS_GITADORA_SMALL_SIZE.has_value() || GRAPHICS_GITADORA_SMALL_POS.has_value())) {
|
|
log_info(
|
|
"graphics-windowed",
|
|
"GITADORA SMALL custom override: pos=({}, {}), size={}x{}",
|
|
x,
|
|
y,
|
|
width,
|
|
height);
|
|
}
|
|
return applied;
|
|
}
|
|
|
|
void graphics_capture_initial_window(HWND hWnd) {
|
|
if (!GRAPHICS_WINDOWED) {
|
|
return;
|
|
}
|
|
|
|
graphics_load_windowed_parameters();
|
|
|
|
GRAPHICS_HOOKED_WINDOW = hWnd;
|
|
|
|
cfg::SCREENRESIZE->init_window_style = GetWindowLong(hWnd, GWL_STYLE);
|
|
cfg::SCREENRESIZE->init_window_style_ex = GetWindowLong(hWnd, GWL_EXSTYLE);
|
|
|
|
log_debug(
|
|
"graphics-windowed",
|
|
"graphics_capture_initial_window called, hWnd={}",
|
|
fmt::ptr(hWnd));
|
|
|
|
RECT rect;
|
|
if (!GetClientRect(hWnd, &rect)) {
|
|
log_warning(
|
|
"graphics-windowed",
|
|
"[{}] graphics_capture_initial_window - GetClientRect failed, GLE: {}",
|
|
fmt::ptr(hWnd),
|
|
GetLastError());
|
|
return;
|
|
}
|
|
const int client_w = rect.right - rect.left;
|
|
const int client_h = rect.bottom - rect.top;
|
|
|
|
cfg::SCREENRESIZE->init_client_width = client_w;
|
|
cfg::SCREENRESIZE->init_client_height = client_h;
|
|
cfg::SCREENRESIZE->init_client_aspect_ratio = (float)client_w / (float)client_h;
|
|
log_misc(
|
|
"graphics-windowed",
|
|
"[{}] graphics_capture_initial_window initial window size {}x{}, ratio {}",
|
|
fmt::ptr(hWnd),
|
|
client_w, client_h, cfg::SCREENRESIZE->init_client_aspect_ratio);
|
|
|
|
// ensure frame size is captured
|
|
graphics_wm_style_changed(hWnd, false);
|
|
|
|
// if the game is not compatible, ignore user-supplied values and keep the current size
|
|
if (graphics_window_resize_breaks_game()) {
|
|
cfg::SCREENRESIZE->client_width = client_w;
|
|
cfg::SCREENRESIZE->client_height = client_h;
|
|
}
|
|
if (graphics_window_move_and_resize_breaks_game()) {
|
|
cfg::SCREENRESIZE->enable_window_resize = false;
|
|
}
|
|
|
|
// if there was no user-supplied dimension, seed it with the current size
|
|
// so that the next resize operation will work
|
|
if (cfg::SCREENRESIZE->client_width == 0) {
|
|
cfg::SCREENRESIZE->client_width = client_w;
|
|
}
|
|
if (cfg::SCREENRESIZE->client_height == 0) {
|
|
cfg::SCREENRESIZE->client_height = client_h;
|
|
}
|
|
|
|
// apply the config that was loaded from disk
|
|
// resize must be done before applying the border
|
|
if (cfg::SCREENRESIZE->enable_window_resize) {
|
|
log_info(
|
|
"graphics-windowed", "[{}] change window rect - window offset: {}x{}, client size: {}x{}",
|
|
fmt::ptr(hWnd),
|
|
cfg::SCREENRESIZE->window_offset_x,
|
|
cfg::SCREENRESIZE->window_offset_y,
|
|
cfg::SCREENRESIZE->client_width,
|
|
cfg::SCREENRESIZE->client_height);
|
|
graphics_move_resize_window(hWnd);
|
|
}
|
|
if (cfg::SCREENRESIZE->window_decoration != cfg::WindowDecorationMode::Default) {
|
|
log_info(
|
|
"graphics-windowed", "[{}] change window style - decoration: {}",
|
|
fmt::ptr(hWnd),
|
|
cfg::SCREENRESIZE->window_decoration);
|
|
graphics_update_window_style(hWnd, true); // force
|
|
}
|
|
if (cfg::SCREENRESIZE->window_always_on_top) {
|
|
log_info("graphics-windowed", "[{}] change window z-order - always on top", fmt::ptr(hWnd));
|
|
graphics_update_z_order(hWnd, true);
|
|
}
|
|
if (cfg::SCREENRESIZE->window_disable_round_corners) {
|
|
log_info("graphics-windowed", "[{}] disabling rounded corners", fmt::ptr(hWnd));
|
|
graphics_set_corner_preference(hWnd, true);
|
|
}
|
|
|
|
// ensure spicetouch coordinates are initialized
|
|
update_spicetouch_window_dimensions(hWnd);
|
|
|
|
log_debug("graphics-windowed", "graphics_capture_initial_window returned");
|
|
}
|
|
|
|
void graphics_load_windowed_parameters() {
|
|
if (!GRAPHICS_WINDOWED) {
|
|
return;
|
|
}
|
|
|
|
log_debug("graphics-windowed", "graphics_load_windowed_parameters called");
|
|
if (GRAPHICS_WINDOW_STYLE.has_value()) {
|
|
log_debug(
|
|
"graphics-windowed",
|
|
"graphics_load_windowed_parameters - load GRAPHICS_WINDOW_STYLE");
|
|
cfg::SCREENRESIZE->window_decoration = GRAPHICS_WINDOW_STYLE.value();
|
|
}
|
|
|
|
if (GRAPHICS_WINDOW_SIZE.has_value()) {
|
|
log_debug(
|
|
"graphics-windowed",
|
|
"graphics_load_windowed_parameters - load GRAPHICS_WINDOW_SIZE");
|
|
cfg::SCREENRESIZE->enable_window_resize = true;
|
|
cfg::SCREENRESIZE->client_keep_aspect_ratio = false;
|
|
cfg::SCREENRESIZE->client_width = GRAPHICS_WINDOW_SIZE.value().first;
|
|
cfg::SCREENRESIZE->client_height = GRAPHICS_WINDOW_SIZE.value().second;
|
|
}
|
|
|
|
if (GRAPHICS_WINDOW_POS.has_value()) {
|
|
log_debug(
|
|
"graphics-windowed",
|
|
"graphics_load_windowed_parameters - load GRAPHICS_WINDOW_POS");
|
|
std::pair<uint32_t, uint32_t> result;
|
|
if (parse_width_height(GRAPHICS_WINDOW_POS.value(), result)) {
|
|
cfg::SCREENRESIZE->enable_window_resize = true;
|
|
cfg::SCREENRESIZE->window_offset_x = result.first;
|
|
cfg::SCREENRESIZE->window_offset_y = result.second;
|
|
} else {
|
|
log_fatal("graphics-windowed", "failed to parse -windowpos");
|
|
}
|
|
}
|
|
|
|
graphics_gitadora_apply_main_monitor_to_config();
|
|
|
|
// only override if true; don't stomp on user setting
|
|
if (GRAPHICS_WINDOW_ALWAYS_ON_TOP) {
|
|
cfg::SCREENRESIZE->window_always_on_top = true;
|
|
}
|
|
|
|
if (GRAPHICS_WINDOW_DISABLE_ROUNDED_CORNERS) {
|
|
cfg::SCREENRESIZE->window_disable_round_corners = true;
|
|
}
|
|
|
|
log_debug("graphics-windowed", "graphics_load_windowed_parameters returned");
|
|
}
|
|
|
|
void graphics_load_windowed_subscreen_parameters() {
|
|
if (!GRAPHICS_WINDOWED) {
|
|
return;
|
|
}
|
|
|
|
log_debug("graphics-windowed", "graphics_load_windowed_subscreen_parameters called");
|
|
|
|
if (GRAPHICS_WSUB_SIZE.has_value()) {
|
|
log_debug(
|
|
"graphics-windowed",
|
|
"graphics_load_windowed_parameters - load GRAPHICS_WSUB_SIZE");
|
|
|
|
std::pair<uint32_t, uint32_t> result;
|
|
if (parse_width_height(GRAPHICS_WSUB_SIZE.value(), result)) {
|
|
GRAPHICS_WSUB_WIDTH = result.first;
|
|
GRAPHICS_WSUB_HEIGHT = result.second;
|
|
} else {
|
|
log_fatal("graphics-windowed", "failed to parse -iidxwsubsize / -sdvxwsubsize");
|
|
}
|
|
}
|
|
|
|
if (GRAPHICS_WSUB_POS.has_value()) {
|
|
log_debug(
|
|
"graphics-windowed",
|
|
"graphics_load_windowed_parameters - load GRAPHICS_WSUB_POS");
|
|
|
|
std::pair<uint32_t, uint32_t> result;
|
|
if (parse_width_height(GRAPHICS_WSUB_POS.value(), result)) {
|
|
GRAPHICS_WSUB_X = result.first;
|
|
GRAPHICS_WSUB_Y = result.second;
|
|
} else {
|
|
log_fatal("graphics-windowed", "failed to parse -iidxwsubpos / -sdvxwsubpos");
|
|
}
|
|
}
|
|
}
|
|
|
|
void graphics_windowed_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
if (!GRAPHICS_WINDOWED) {
|
|
return;
|
|
}
|
|
|
|
switch (uMsg) {
|
|
case WM_MOVE: {
|
|
log_debug("graphics-windowed", "graphics_windowed_wndproc called with WM_MOVE");
|
|
RECT rect;
|
|
if (GetWindowRect(hWnd, &rect)) {
|
|
cfg::SCREENRESIZE->window_offset_x = rect.left;
|
|
cfg::SCREENRESIZE->window_offset_y = rect.top;
|
|
}
|
|
break;
|
|
}
|
|
case WM_SIZE: {
|
|
log_debug("graphics-windowed", "graphics_windowed_wndproc called with WM_SIZE");
|
|
if (wParam == SIZE_MINIMIZED) {
|
|
break;
|
|
}
|
|
RECT rect;
|
|
if (GetClientRect(hWnd, &rect)) {
|
|
cfg::SCREENRESIZE->client_width = rect.right - rect.left;
|
|
cfg::SCREENRESIZE->client_height = rect.bottom - rect.top;
|
|
}
|
|
break;
|
|
}
|
|
case WM_SIZING: {
|
|
if (cfg::SCREENRESIZE->client_keep_aspect_ratio) {
|
|
graphics_wm_sizing_aspect_ratio(
|
|
static_cast<int>(wParam), *reinterpret_cast<LPRECT>(lParam));
|
|
}
|
|
break;
|
|
}
|
|
case WM_STYLECHANGED: {
|
|
graphics_wm_style_changed(hWnd, true);
|
|
break;
|
|
}
|
|
case WM_GETMINMAXINFO: {
|
|
if (cfg::SCREENRESIZE->client_keep_aspect_ratio) {
|
|
auto info = reinterpret_cast<MINMAXINFO *>(lParam);
|
|
info->ptMinTrackSize.y =
|
|
cfg::SCREENRESIZE->window_deco_height +
|
|
((info->ptMinTrackSize.x - cfg::SCREENRESIZE->window_deco_width) /
|
|
cfg::SCREENRESIZE->init_client_aspect_ratio);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void graphics_wm_style_changed(HWND hWnd, bool changed) {
|
|
log_debug("graphics-windowed", "graphics_wm_style_changed called");
|
|
RECT rect;
|
|
|
|
// ensure the style change takes in effect before doing the calculations
|
|
if (changed) {
|
|
// ensure client size doesn't change as a result of this
|
|
// since SetWindowPos will still send WM_SIZE and WM_MOVE
|
|
const auto client_w = cfg::SCREENRESIZE->client_width;
|
|
const auto client_h = cfg::SCREENRESIZE->client_height;
|
|
const auto flags = SETWINDOWPOS_NOOP | SWP_FRAMECHANGED;
|
|
SetWindowPos(
|
|
hWnd,
|
|
nullptr,
|
|
0, 0, 0, 0,
|
|
flags);
|
|
cfg::SCREENRESIZE->client_width = client_w;
|
|
cfg::SCREENRESIZE->client_height = client_h;
|
|
}
|
|
|
|
// get window size with decoration...
|
|
if (!GetWindowRect(hWnd, &rect)) {
|
|
log_warning(
|
|
"graphics",
|
|
"graphics_wm_style_changed - GetWindowRect failed, GLE: {}",
|
|
GetLastError());
|
|
return;
|
|
}
|
|
const int window_w = rect.right - rect.left;
|
|
const int window_h = rect.bottom - rect.top;
|
|
|
|
// get client area (without decoration)...
|
|
if (!GetClientRect(hWnd, &rect)) {
|
|
return;
|
|
}
|
|
const int client_w = rect.right - rect.left;
|
|
const int client_h = rect.bottom - rect.top;
|
|
|
|
// update window decoration size
|
|
cfg::SCREENRESIZE->window_deco_width = window_w - client_w;
|
|
cfg::SCREENRESIZE->window_deco_height = window_h - client_h;
|
|
log_debug(
|
|
"graphics-windowed",
|
|
"graphics_wm_style_changed updating frame dimensions {}x{}",
|
|
cfg::SCREENRESIZE->window_deco_width,
|
|
cfg::SCREENRESIZE->window_deco_height);
|
|
|
|
// adjust window to ensure client area remains the same
|
|
if (changed) {
|
|
graphics_move_resize_window(hWnd);
|
|
}
|
|
|
|
log_debug("graphics-windowed", "graphics_wm_style_changed returned");
|
|
}
|
|
|
|
void graphics_wm_sizing_aspect_ratio(int edge, RECT& rect) {
|
|
log_debug("graphics-windowed", "graphics_wm_sizing_aspect_ratio called");
|
|
|
|
const auto deco_w = cfg::SCREENRESIZE->window_deco_width;
|
|
const auto deco_h = cfg::SCREENRESIZE->window_deco_height;
|
|
const LONG desired_w = (rect.right - rect.left) - deco_w;
|
|
const LONG desired_h = (rect.bottom - rect.top) - deco_h;
|
|
const auto aspect_ratio = cfg::SCREENRESIZE->init_client_aspect_ratio;
|
|
|
|
// based on http://playtechs.blogspot.com/2007/10/forcing-window-to-maintain-particular.html
|
|
switch (edge) {
|
|
case WMSZ_BOTTOM:
|
|
case WMSZ_TOP: {
|
|
const LONG w = deco_w + (desired_h * aspect_ratio);
|
|
rect.right = rect.left + w;
|
|
break;
|
|
}
|
|
case WMSZ_LEFT:
|
|
case WMSZ_RIGHT: {
|
|
const LONG h = deco_h + (desired_w / aspect_ratio);
|
|
rect.bottom = rect.top + h;
|
|
break;
|
|
}
|
|
case WMSZ_TOPLEFT:
|
|
case WMSZ_TOPRIGHT:
|
|
case WMSZ_BOTTOMLEFT:
|
|
case WMSZ_BOTTOMRIGHT: {
|
|
int w;
|
|
int h;
|
|
if (desired_h * aspect_ratio < desired_w) {
|
|
w = rect.right - rect.left;
|
|
h = deco_h + (desired_w / aspect_ratio);
|
|
} else {
|
|
w = deco_w + (desired_h * aspect_ratio);
|
|
h = rect.bottom - rect.top;
|
|
}
|
|
if (edge == WMSZ_TOPLEFT) {
|
|
rect.left = rect.right - w;
|
|
rect.top = rect.bottom - h;
|
|
} else if (edge == WMSZ_TOPRIGHT) {
|
|
rect.right = rect.left + w;
|
|
rect.top = rect.bottom - h;
|
|
} else if (edge == WMSZ_BOTTOMLEFT) {
|
|
rect.left = rect.right - w;
|
|
rect.bottom = rect.top + h;
|
|
} else if (edge == WMSZ_BOTTOMRIGHT) {
|
|
rect.right = rect.left + w;
|
|
rect.bottom = rect.top + h;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
log_debug("graphics-windowed", "graphics_wm_sizing_aspect_ratio returned");
|
|
}
|
|
|
|
void graphics_update_window_style(HWND hWnd, bool force_update) {
|
|
if (!GRAPHICS_WINDOWED) {
|
|
return;
|
|
}
|
|
if (!force_update && graphics_window_decoration_change_crashes_game()) {
|
|
return;
|
|
}
|
|
|
|
log_debug(
|
|
"graphics-windowed",
|
|
"graphics_update_window_style called for hWnd={}",
|
|
fmt::ptr(hWnd));
|
|
|
|
// update frame style
|
|
auto style = cfg::SCREENRESIZE->init_window_style;
|
|
auto style_ex = cfg::SCREENRESIZE->init_window_style_ex;
|
|
switch (cfg::SCREENRESIZE->window_decoration) {
|
|
case cfg::WindowDecorationMode::Borderless:
|
|
style &= ~WS_OVERLAPPEDWINDOW;
|
|
style_ex &= ~WS_EX_CLIENTEDGE;
|
|
style_ex &= ~WS_EX_WINDOWEDGE;
|
|
break;
|
|
case cfg::WindowDecorationMode::ResizableFrame:
|
|
style |= WS_OVERLAPPEDWINDOW;
|
|
if (graphics_window_resize_breaks_game()) {
|
|
style &= ~WS_SIZEBOX;
|
|
}
|
|
break;
|
|
case cfg::WindowDecorationMode::Default:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
log_debug(
|
|
"graphics-windowed",
|
|
"graphics_update_window_style - calling SetWindowLong with Mode {}, style 0x{:x}",
|
|
static_cast<int>(cfg::SCREENRESIZE->window_decoration),
|
|
style);
|
|
|
|
const auto ret = SetWindowLong(hWnd, GWL_STYLE, style);
|
|
(void)ret;
|
|
|
|
log_debug(
|
|
"graphics-windowed",
|
|
"graphics_update_window_style - SetWindowLong(GWL_STYLE) returned {:x}, GLE={}",
|
|
ret,
|
|
GetLastError());
|
|
|
|
const auto ret2 = SetWindowLong(hWnd, GWL_EXSTYLE, style_ex);
|
|
(void)ret2;
|
|
log_debug(
|
|
"graphics-windowed",
|
|
"graphics_update_window_style SetWindowLong(GWL_EXSTYLE) returned {:x}, GLE={}",
|
|
ret2,
|
|
GetLastError());
|
|
|
|
// SetWindowPos must be called after SetWindowLong if the frame style changed
|
|
// this will be done in WM_STYLECHANGED handler
|
|
}
|
|
|
|
void graphics_update_z_order(HWND hWnd, bool always_on_top) {
|
|
if (!GRAPHICS_WINDOWED) {
|
|
return;
|
|
}
|
|
|
|
log_debug("graphics-windowed", "graphics_update_z_order called");
|
|
|
|
HWND insert_after = nullptr;
|
|
if (always_on_top) {
|
|
insert_after = HWND_TOPMOST;
|
|
} else {
|
|
insert_after = HWND_NOTOPMOST;
|
|
}
|
|
|
|
auto flags = SETWINDOWPOS_NOOP;
|
|
flags &= ~SWP_NOZORDER;
|
|
SetWindowPos(
|
|
hWnd,
|
|
insert_after,
|
|
0, 0, 0, 0,
|
|
flags);
|
|
|
|
log_debug("graphics-windowed", "graphics_update_z_order returned");
|
|
}
|
|
|
|
void graphics_set_corner_preference(HWND hWnd, bool disable) {
|
|
if (!GRAPHICS_WINDOWED) {
|
|
return;
|
|
}
|
|
|
|
#if !SPICE_XP
|
|
log_debug("graphics-windowed", "graphics_set_corner_preference called");
|
|
|
|
DWM_WINDOW_CORNER_PREFERENCE corner_preference = disable ? DWMWCP_DONOTROUND : DWMWCP_DEFAULT;
|
|
DwmSetWindowAttribute(
|
|
hWnd,
|
|
DWMWA_WINDOW_CORNER_PREFERENCE,
|
|
&corner_preference, sizeof(corner_preference));
|
|
|
|
log_debug("graphics-windowed", "graphics_set_corner_preference returned");
|
|
#else
|
|
// don't link against DwmSetWindowAttribute on XP
|
|
log_debug("graphics-windowed", "graphics_set_corner_preference called; ignoring");
|
|
#endif
|
|
}
|
|
|
|
void graphics_move_resize_window(HWND hWnd) {
|
|
if (!GRAPHICS_WINDOWED) {
|
|
return;
|
|
}
|
|
|
|
log_debug("graphics-windowed", "graphics_move_resize_window called");
|
|
|
|
cfg::SCREENRESIZE->client_width =
|
|
CLAMP(cfg::SCREENRESIZE->client_width, 640, 1920 * 8);
|
|
cfg::SCREENRESIZE->client_height =
|
|
CLAMP(cfg::SCREENRESIZE->client_height, 480, 1080 * 8);
|
|
|
|
const auto w = cfg::SCREENRESIZE->client_width + cfg::SCREENRESIZE->window_deco_width;
|
|
const auto h = cfg::SCREENRESIZE->client_height + cfg::SCREENRESIZE->window_deco_height;
|
|
|
|
auto flags = SETWINDOWPOS_NOOP;
|
|
flags &= ~SWP_NOSIZE;
|
|
flags &= ~SWP_NOMOVE;
|
|
SetWindowPos(
|
|
hWnd,
|
|
nullptr,
|
|
cfg::SCREENRESIZE->window_offset_x,
|
|
cfg::SCREENRESIZE->window_offset_y,
|
|
w, h,
|
|
flags);
|
|
|
|
log_debug("graphics-windowed", "graphics_move_resize_window returned");
|
|
}
|
|
|
|
bool graphics_window_decoration_change_crashes_game() {
|
|
static std::once_flag flag;
|
|
static bool result = false;
|
|
std::call_once(flag, []() {
|
|
// ddr and ftt crash when frame style changes
|
|
if (avs::game::is_model("MDX") || avs::game::is_model("MMD")) {
|
|
result = true;
|
|
}
|
|
|
|
// arena model hangs immediately when decoration changes
|
|
if (games::gitadora::is_arena_model()) {
|
|
result = true;
|
|
}
|
|
|
|
// changing window decoration also changes dimensions and position
|
|
// so this must be prevented
|
|
if (graphics_window_move_and_resize_breaks_game()) {
|
|
result = true;
|
|
}
|
|
|
|
if (result) {
|
|
log_warning(
|
|
"graphics-windowed",
|
|
"ignoring changes to window style due to incompatibility with this game");
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
|
|
bool graphics_window_move_and_resize_breaks_game() {
|
|
static std::once_flag flag;
|
|
static bool result = false;
|
|
|
|
std::call_once(flag, []() {
|
|
// games like reflec beat in use spice touch overlay for windowed mode
|
|
// and mouse input breaks if window is moved as the overlay does not
|
|
// currently support window moves/resizes
|
|
result = avs::game::is_model({"KBR", "LBR", "MBR"});
|
|
if (result) {
|
|
log_warning(
|
|
"graphics-windowed",
|
|
"ignoring changes to window properties due to incompatibility with this game");
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
|
|
bool graphics_window_resize_breaks_game() {
|
|
// for whatever reason, gitadora games on dx/sd/sd2 cabs behave poorly when window is resized
|
|
// (eihter at launch or while it's running)
|
|
// usually results in: soft lock during scene transtions or really long transitions,
|
|
// backgrounds of menus not rendering, etc.
|
|
bool is_gitadora = avs::game::is_model({ "J32", "J33", "K32", "K33", "L32", "L33", "M32" });
|
|
bool is_arena = games::gitadora::is_arena_model();
|
|
|
|
bool result = graphics_window_move_and_resize_breaks_game() || (is_gitadora && !is_arena);
|
|
|
|
static std::once_flag flag;
|
|
std::call_once(flag, [&result]() {
|
|
if (result) {
|
|
log_warning(
|
|
"graphics-windowed",
|
|
"ignoring changes to window size due to incompatibility with this game");
|
|
}
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
void graphics_window_check_bounds_before_creation(int &x, int &y, const int width, const int height) {
|
|
RECT rect = {
|
|
.left = x,
|
|
.top = y,
|
|
.right = x + width,
|
|
.bottom = y + height
|
|
};
|
|
|
|
HMONITOR mon = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL);
|
|
if (mon != nullptr) {
|
|
return;
|
|
}
|
|
|
|
log_warning(
|
|
"graphics-windowed",
|
|
"window is completely out of bounds, resetting position: ({}, {}) => (0, 0)",
|
|
x, y);
|
|
x = 0;
|
|
y = 0;
|
|
rect = {
|
|
.left = x,
|
|
.top = y,
|
|
.right = x + width,
|
|
.bottom = y + height
|
|
};
|
|
|
|
mon = MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST);
|
|
MONITORINFO mi = {};
|
|
mi.cbSize = sizeof(mi);
|
|
if (!GetMonitorInfo(mon, &mi)) {
|
|
log_warning(
|
|
"graphics-windowed",
|
|
"failed to get monitor info for out of bounds window");
|
|
return;
|
|
}
|
|
|
|
log_misc(
|
|
"graphics-windowed",
|
|
"nearest monitor for this window has work area ({}, {}),({}, {})",
|
|
mi.rcWork.left, mi.rcWork.top, mi.rcWork.right, mi.rcWork.bottom);
|
|
|
|
RECT bounds = mi.rcWork;
|
|
const bool fully_visible =
|
|
(rect.left >= bounds.left &&
|
|
rect.top >= bounds.top &&
|
|
rect.right <= bounds.right &&
|
|
rect.bottom <= bounds.bottom);
|
|
|
|
if (!fully_visible) {
|
|
log_warning("graphics-windowed", "window will not be fully visible on monitor");
|
|
}
|
|
}
|