Preferred monitor: chosen in Optionen > Allgemein, used at the next start
The Internet-Optionen frame becomes Bildschirm with a Monitor dropdown (Hauptmonitor or a connected monitor, kept by device path in [display] Monitor=). The borderless window and the render size use that monitor, falling back to the main monitor when it is not connected. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
c7d54a6cfd
commit
9bc557cd95
10 files changed
+407
-14
No files matched your search
@@ -156,11 +156,16 @@ void SetTextOverride(uint32_t id, const std::wstring& text) {
|
||||
entry->string = {begin, begin + entry->text.size(), begin + entry->text.size()};
|
||||
}
|
||||
|
||||
std::wstring GameTextByKey(const char* key) {
|
||||
uint32_t GameTextIdByKey(const char* key) {
|
||||
// The key's hash (0x80e010, cdecl), looked up "by key" as GetTextByKey does.
|
||||
using HashFn = uint32_t(__cdecl*)(const char*);
|
||||
const uint32_t hash = reinterpret_cast<HashFn>(0x0080e010)(key);
|
||||
return hash ? GameText(hash | 0x80000000) : std::wstring();
|
||||
return hash ? hash | 0x80000000 : 0;
|
||||
}
|
||||
|
||||
std::wstring GameTextByKey(const char* key) {
|
||||
const uint32_t id = GameTextIdByKey(key);
|
||||
return id ? GameText(id) : std::wstring();
|
||||
}
|
||||
|
||||
std::wstring WideStringText(const void* string) {
|
||||
|
||||
@@ -33,6 +33,9 @@ void SetTextOverride(uint32_t id, const std::wstring& text);
|
||||
// The game's text under key `key` (TextManager::GetTextByKey 0x672fd0), e.g.
|
||||
// "UI_ATTRIB_STRENGTH"; empty if there is none.
|
||||
std::wstring GameTextByKey(const char* key);
|
||||
// The id of the game's text under key `key` (0 if the key has no hash), e.g.
|
||||
// for SetTextOverride.
|
||||
uint32_t GameTextIdByKey(const char* key);
|
||||
|
||||
// A game wide string ({begin, end, capacity end}, e.g. a tooltip's text) as
|
||||
// text, and replaced by `value` (grown with the game's allocator).
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// The game renders at a fixed resolution (1024x768). Unmodified, windowed mode
|
||||
// shows that as a 1024x768 popup centred on the desktop; fullscreen mode
|
||||
// switches the display mode. With this mod the game always runs windowed, the
|
||||
// popup covers the whole monitor, and each frame is scaled up into the largest
|
||||
// popup covers the whole monitor (mods/monitor.h: the preferred one), and each frame is scaled up into the largest
|
||||
// centred rectangle with the game's aspect ratio (black bars elsewhere).
|
||||
//
|
||||
// Mouse positions are mapped back from that rectangle to game pixels, both in
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "game/render/dx_driver7.h"
|
||||
#include "mods/hud.h"
|
||||
#include "mods/input.h"
|
||||
#include "mods/monitor.h"
|
||||
#include "mods/widescreen.h"
|
||||
#include "re/config.h"
|
||||
#include "game/addresses.h"
|
||||
@@ -90,9 +91,7 @@ RECT FitRect(int areaWidth, int areaHeight, int width, int height, bool integerS
|
||||
|
||||
void ApplyBorderless(game::DxDriver7* driver) {
|
||||
HWND window = driver->window;
|
||||
MONITORINFO monitor = {sizeof(monitor)};
|
||||
GetMonitorInfoW(MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST), &monitor);
|
||||
const RECT& area = monitor.rcMonitor;
|
||||
const RECT area = mods::monitor::GameMonitorRect();
|
||||
const int areaWidth = area.right - area.left;
|
||||
const int areaHeight = area.bottom - area.top;
|
||||
|
||||
@@ -171,7 +170,7 @@ int __fastcall DxDriver7_SetDisplayMode(DxDriver7* self, void* edx, void* device
|
||||
}
|
||||
DxDisplayMode windowed = *mode;
|
||||
windowed.fullscreen = 0;
|
||||
mods::widescreen::ApplyRenderSize(windowed, self->window);
|
||||
mods::widescreen::ApplyRenderSize(windowed);
|
||||
g_viewport.active = false;
|
||||
const int result = DxDriver7_SetDisplayMode_original(self, edx, deviceCaps, &windowed, unknown);
|
||||
if (result) {
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
// Preferred monitor (request of 2026-10-25): a setting in the graphics options
|
||||
// picks the monitor the game starts on; with none set, or the preferred one
|
||||
// not connected, the system's main monitor.
|
||||
//
|
||||
// The game window and its render size are decided when the display mode is
|
||||
// set at startup (borderless.cpp, widescreen.cpp ApplyRenderSize): both ask
|
||||
// GameMonitorRect. A change applies at the next start.
|
||||
//
|
||||
// Monitors are told apart by their device path (DisplayConfigGetDeviceInfo,
|
||||
// target name: "\\?\DISPLAY#GSM5B7F#..."), which survives restarts, monitor
|
||||
// order changes and other ports' changes, unlike the GDI names \\.\DISPLAYn.
|
||||
//
|
||||
// sacredK.ini:
|
||||
// [display]
|
||||
// Monitor=<device path> ; empty: the main monitor
|
||||
|
||||
#include "mods/monitor.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "re/config.h"
|
||||
#include "re/log.h"
|
||||
|
||||
namespace mods::monitor {
|
||||
namespace {
|
||||
|
||||
constexpr const char* kSection = "display";
|
||||
constexpr const char* kKey = "Monitor";
|
||||
|
||||
std::string Utf8(const wchar_t* text) {
|
||||
const int size = WideCharToMultiByte(CP_UTF8, 0, text, -1, nullptr, 0, nullptr, nullptr);
|
||||
if (size <= 1) {
|
||||
return {};
|
||||
}
|
||||
std::string result(static_cast<size_t>(size - 1), '\0');
|
||||
WideCharToMultiByte(CP_UTF8, 0, text, -1, result.data(), size, nullptr, nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
struct Target {
|
||||
std::wstring gdiName; // \\.\DISPLAYn of the source showing it
|
||||
std::string id;
|
||||
std::wstring name;
|
||||
};
|
||||
|
||||
// The active display paths: which monitor shows which GDI display.
|
||||
std::vector<Target> Targets() {
|
||||
UINT32 pathCount = 0;
|
||||
UINT32 modeCount = 0;
|
||||
std::vector<DISPLAYCONFIG_PATH_INFO> paths;
|
||||
std::vector<DISPLAYCONFIG_MODE_INFO> modes;
|
||||
LONG result;
|
||||
do {
|
||||
if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount) != ERROR_SUCCESS) {
|
||||
return {};
|
||||
}
|
||||
paths.resize(pathCount);
|
||||
modes.resize(modeCount);
|
||||
result = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathCount, paths.data(), &modeCount, modes.data(),
|
||||
nullptr);
|
||||
} while (result == ERROR_INSUFFICIENT_BUFFER);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
return {};
|
||||
}
|
||||
paths.resize(pathCount);
|
||||
std::vector<Target> targets;
|
||||
for (const DISPLAYCONFIG_PATH_INFO& path : paths) {
|
||||
DISPLAYCONFIG_SOURCE_DEVICE_NAME source = {};
|
||||
source.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
|
||||
source.header.size = sizeof(source);
|
||||
source.header.adapterId = path.sourceInfo.adapterId;
|
||||
source.header.id = path.sourceInfo.id;
|
||||
DISPLAYCONFIG_TARGET_DEVICE_NAME target = {};
|
||||
target.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
|
||||
target.header.size = sizeof(target);
|
||||
target.header.adapterId = path.targetInfo.adapterId;
|
||||
target.header.id = path.targetInfo.id;
|
||||
if (DisplayConfigGetDeviceInfo(&source.header) != ERROR_SUCCESS ||
|
||||
DisplayConfigGetDeviceInfo(&target.header) != ERROR_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
targets.push_back({source.viewGdiDeviceName, Utf8(target.monitorDevicePath), target.monitorFriendlyDeviceName});
|
||||
}
|
||||
return targets;
|
||||
}
|
||||
|
||||
BOOL CALLBACK AddMonitor(HMONITOR handle, HDC, LPRECT, LPARAM data) {
|
||||
auto* gdi = reinterpret_cast<std::vector<MONITORINFOEXW>*>(data);
|
||||
MONITORINFOEXW info = {};
|
||||
info.cbSize = sizeof(info);
|
||||
if (GetMonitorInfoW(handle, &info)) {
|
||||
gdi->push_back(info);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::vector<Monitor> List() {
|
||||
std::vector<MONITORINFOEXW> gdi;
|
||||
EnumDisplayMonitors(nullptr, nullptr, &AddMonitor, reinterpret_cast<LPARAM>(&gdi));
|
||||
const std::vector<Target> targets = Targets();
|
||||
std::vector<Monitor> monitors;
|
||||
for (const MONITORINFOEXW& info : gdi) {
|
||||
Monitor monitor{"", L"", info.rcMonitor, (info.dwFlags & MONITORINFOF_PRIMARY) != 0};
|
||||
for (const Target& target : targets) {
|
||||
if (target.gdiName == info.szDevice) { // mirrored displays: the first monitor
|
||||
monitor.id = target.id;
|
||||
monitor.name = target.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (monitor.id.empty()) {
|
||||
monitor.id = Utf8(info.szDevice); // no display config (remote desktop, ...)
|
||||
}
|
||||
if (monitor.name.empty()) {
|
||||
monitor.name = L"Monitor " + std::to_wstring(monitors.size() + 1);
|
||||
}
|
||||
monitors.push_back(monitor);
|
||||
}
|
||||
std::stable_sort(monitors.begin(), monitors.end(),
|
||||
[](const Monitor& a, const Monitor& b) { return a.primary && !b.primary; });
|
||||
return monitors;
|
||||
}
|
||||
|
||||
std::string PreferredId() {
|
||||
return re::ConfigString(kSection, kKey, "");
|
||||
}
|
||||
|
||||
void SetPreferredId(const std::string& id) {
|
||||
re::SetConfigString(kSection, kKey, id.c_str());
|
||||
}
|
||||
|
||||
RECT GameMonitorRect() {
|
||||
const std::vector<Monitor> monitors = List();
|
||||
const std::string preferred = PreferredId();
|
||||
const Monitor* chosen = nullptr;
|
||||
for (const Monitor& monitor : monitors) {
|
||||
if ((!preferred.empty() && monitor.id == preferred) || (!chosen && monitor.primary)) {
|
||||
chosen = &monitor;
|
||||
}
|
||||
}
|
||||
if (!chosen) {
|
||||
// No monitor reported: the main monitor's rectangle as Windows has it.
|
||||
MONITORINFO info = {sizeof(info)};
|
||||
GetMonitorInfoW(MonitorFromPoint({0, 0}, MONITOR_DEFAULTTOPRIMARY), &info);
|
||||
return info.rcMonitor;
|
||||
}
|
||||
const bool found = !preferred.empty() && chosen->id == preferred;
|
||||
static bool logged = false;
|
||||
if (!logged) {
|
||||
logged = true;
|
||||
re::Log("monitor: %ls %ldx%ld at (%ld,%ld)%s", chosen->name.c_str(), chosen->rect.right - chosen->rect.left,
|
||||
chosen->rect.bottom - chosen->rect.top, chosen->rect.left, chosen->rect.top,
|
||||
found ? "" : preferred.empty() ? ", main monitor" : ", preferred monitor not connected: main monitor");
|
||||
}
|
||||
return chosen->rect;
|
||||
}
|
||||
|
||||
} // namespace mods::monitor
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
// The monitor the game runs on (request of 2026-10-25): a preferred monitor
|
||||
// from the options, or the system's main monitor when none is set or the
|
||||
// preferred one is not connected. See monitor.cpp.
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace mods::monitor {
|
||||
|
||||
struct Monitor {
|
||||
std::string id; // stable across restarts and reordering: the monitor's device path
|
||||
std::wstring name; // the monitor's own name (EDID), e.g. "LG ULTRAGEAR"
|
||||
RECT rect; // desktop coordinates
|
||||
bool primary;
|
||||
};
|
||||
|
||||
// The connected monitors, the main monitor first.
|
||||
std::vector<Monitor> List();
|
||||
|
||||
// The preferred monitor's id from sacredK.ini, empty for none.
|
||||
std::string PreferredId();
|
||||
void SetPreferredId(const std::string& id);
|
||||
|
||||
// The rectangle of the monitor to run on: the preferred one if connected,
|
||||
// else the main monitor.
|
||||
RECT GameMonitorRect();
|
||||
|
||||
} // namespace mods::monitor
|
||||
+166
-1
@@ -41,6 +41,7 @@
|
||||
#include "mods/hud.h"
|
||||
#include "mods/input.h"
|
||||
#include "mods/lootfilter.h"
|
||||
#include "mods/monitor.h"
|
||||
#include "re/hook.h"
|
||||
#include "re/log.h"
|
||||
|
||||
@@ -684,6 +685,160 @@ bool HandleSavesEvent(Window& w, void* event) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- monitor (Allgemein page) ----------------------------------------------------
|
||||
|
||||
// Preferred monitor (request of 2026-10-25, mods/monitor.h) in place of the
|
||||
// Internet-Optionen frame (Datendurchsatz, multiplayer only; the user's
|
||||
// choice): its controls are hidden (the game keeps the saved value), its title
|
||||
// reads "Bildschirm" (text UI_CFG_WAN, set by 0x672a20 / 0x724d50 when the
|
||||
// window is made). One row: "Monitor" and a dropdown box; the list opens
|
||||
// upwards (the frame is the lowest one), a click picks, a click elsewhere
|
||||
// closes it. OK saves the choice, which applies at the next start.
|
||||
constexpr const char* kScreenFrameName = "UI_FRAME_INTERNET";
|
||||
constexpr const char* kScreenTitleName = "UI_LABEL_WAN";
|
||||
constexpr const char* kScreenTitleKey = "UI_CFG_WAN";
|
||||
constexpr int kScreenLabelWidth = 0x70;
|
||||
constexpr int kArrowWidth = 9; // the box's down arrow, 5 rows
|
||||
|
||||
struct ScreenChoice {
|
||||
std::string id; // empty: the main monitor
|
||||
std::wstring text;
|
||||
};
|
||||
|
||||
game::UiRect g_screenFrame = {};
|
||||
std::vector<ScreenChoice> g_screenChoices;
|
||||
std::string g_screenPending;
|
||||
bool g_screenOpen = false;
|
||||
int g_screenHover = -1; // list entry under the mouse
|
||||
|
||||
void CreateScreenControls(Window& w) {
|
||||
for (const Control& c : w.controls) {
|
||||
if (IsFrame(c.control) && game::ControlNamed(c.control, kScreenFrameName)) {
|
||||
g_screenFrame = *reinterpret_cast<const game::UiRect*>(c.control + game::kControlRect);
|
||||
}
|
||||
}
|
||||
for (Control& c : w.controls) {
|
||||
const game::UiRect r = *reinterpret_cast<const game::UiRect*>(c.control + game::kControlRect);
|
||||
const bool inside = r.x >= g_screenFrame.x && r.y >= g_screenFrame.y &&
|
||||
r.x < g_screenFrame.x + g_screenFrame.w && r.y < g_screenFrame.y + g_screenFrame.h;
|
||||
if (c.group == Group::Common &&
|
||||
(game::ControlNamed(c.control, kScreenTitleName) || (inside && !IsFrame(c.control)))) {
|
||||
c.group = Group::Hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LoadScreenPending() {
|
||||
g_screenChoices = {{"", L"Hauptmonitor"}};
|
||||
for (const mods::monitor::Monitor& m : mods::monitor::List()) {
|
||||
g_screenChoices.push_back({m.id, m.name + L" (" + std::to_wstring(m.rect.right - m.rect.left) + L"x" +
|
||||
std::to_wstring(m.rect.bottom - m.rect.top) + L")"});
|
||||
}
|
||||
g_screenPending = mods::monitor::PreferredId();
|
||||
g_screenOpen = false;
|
||||
g_screenHover = -1;
|
||||
}
|
||||
|
||||
const wchar_t* ScreenPendingText() {
|
||||
for (const ScreenChoice& choice : g_screenChoices) {
|
||||
if (choice.id == g_screenPending) {
|
||||
return choice.text.c_str();
|
||||
}
|
||||
}
|
||||
return L"Nicht verbunden";
|
||||
}
|
||||
|
||||
game::UiRect ScreenRowRect(void* window, int dx, int width) {
|
||||
const game::UiRect origin = game::ControlScreenRect(window);
|
||||
return {origin.x + g_screenFrame.x + kTextInset + dx,
|
||||
origin.y + g_screenFrame.y + (g_screenFrame.h - kBoxHeight) / 2, static_cast<int16_t>(width),
|
||||
static_cast<int16_t>(kBoxHeight)};
|
||||
}
|
||||
|
||||
game::UiRect ScreenBoxRect(void* window) {
|
||||
return ScreenRowRect(window, kScreenLabelWidth, g_screenFrame.w - 2 * kTextInset - kScreenLabelWidth);
|
||||
}
|
||||
|
||||
// List entry `index`, upwards from the box: the last entry right above it.
|
||||
game::UiRect ScreenEntryRect(void* window, int index) {
|
||||
game::UiRect r = ScreenBoxRect(window);
|
||||
r.y -= static_cast<int>(g_screenChoices.size() - index) * kBoxHeight;
|
||||
return r;
|
||||
}
|
||||
|
||||
int ScreenEntryAt(void* window, int x, int y) {
|
||||
for (int i = 0; i < static_cast<int>(g_screenChoices.size()); ++i) {
|
||||
if (game::Inside(ScreenEntryRect(window, i), x, y)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void DrawScreen(const Window& w, void* device) {
|
||||
void* window = w.window;
|
||||
game::DrawText(device, kFont, game::CenterLineVertically(kFont, ScreenRowRect(window, 0, kScreenLabelWidth)),
|
||||
L"Monitor", kTextColour, 0);
|
||||
const game::UiRect box = ScreenBoxRect(window);
|
||||
FillBox(device, box, g_screenOpen ? kSavesBoxHoverColour : kSavesBoxColour);
|
||||
game::UiRect text = box;
|
||||
text.x += 6;
|
||||
text.w = static_cast<int16_t>(text.w - 6 - kArrowWidth - 8);
|
||||
game::DrawText(device, kFont, game::CenterLineVertically(kFont, text), ScreenPendingText(), kTextColour, 0);
|
||||
// The arrow: rows narrowing to a point.
|
||||
const int arrowX = box.x + box.w - kArrowWidth - 6;
|
||||
const int arrowY = box.y + (box.h - 5) / 2;
|
||||
for (int row = 0; row < 5; ++row) {
|
||||
FillBox(device, {arrowX + row, arrowY + row, static_cast<int16_t>(kArrowWidth - 2 * row), 1}, kTextColour);
|
||||
}
|
||||
if (g_screenPending != mods::monitor::PreferredId()) {
|
||||
game::UiRect hint = {box.x, box.y + box.h + 2, box.w, static_cast<int16_t>(game::FontLineHeight(kFont))};
|
||||
game::DrawText(device, kFont, hint, L"Ab dem nächsten Spielstart", kUnboundColour, 0);
|
||||
}
|
||||
if (!g_screenOpen) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < static_cast<int>(g_screenChoices.size()); ++i) {
|
||||
const game::UiRect entry = ScreenEntryRect(window, i);
|
||||
const bool selected = g_screenChoices[i].id == g_screenPending;
|
||||
FillBox(device, entry, selected ? kBoxSelectedColour : i == g_screenHover ? kBoxHoverColour : kBoxColour);
|
||||
game::UiRect label = entry;
|
||||
label.x += 6;
|
||||
game::DrawText(device, kFont, game::CenterLineVertically(kFont, label), g_screenChoices[i].text.c_str(),
|
||||
kTextColour, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool HandleScreenEvent(Window& w, void* event) {
|
||||
switch (game::MouseEventKind(event)) {
|
||||
case game::MouseEvent::Move:
|
||||
g_screenHover =
|
||||
g_screenOpen ? ScreenEntryAt(w.window, game::EventField(event, 0xc), game::EventField(event, 0x10)) : -1;
|
||||
return false;
|
||||
case game::MouseEvent::ButtonDown: {
|
||||
const int x = game::EventField(event, game::kMouseButtonX);
|
||||
const int y = game::EventField(event, game::kMouseButtonY);
|
||||
if (g_screenOpen) {
|
||||
const int entry = ScreenEntryAt(w.window, x, y);
|
||||
if (entry >= 0 && game::EventField(event, game::kMouseButton) == game::kLeftButton) {
|
||||
g_screenPending = g_screenChoices[entry].id;
|
||||
}
|
||||
g_screenOpen = false;
|
||||
g_screenHover = -1;
|
||||
return true; // the click only closes the list
|
||||
}
|
||||
if (game::EventField(event, game::kMouseButton) == game::kLeftButton &&
|
||||
game::Inside(ScreenBoxRect(w.window), x, y)) {
|
||||
g_screenOpen = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// --- hooks ---------------------------------------------------------------------
|
||||
|
||||
struct RenderCall {
|
||||
@@ -706,6 +861,7 @@ int RenderBody(void* context) {
|
||||
LoadPending();
|
||||
LoadLootPending();
|
||||
g_autosavePending = mods::characters::AutosaveMinutes();
|
||||
LoadScreenPending();
|
||||
}
|
||||
w->wasVisible = visible;
|
||||
const uint16_t page = game::ActiveTab(w->sheet);
|
||||
@@ -720,6 +876,8 @@ int RenderBody(void* context) {
|
||||
}
|
||||
if (!visible || page != kPageCommon) {
|
||||
g_savesHover = -1;
|
||||
g_screenOpen = false;
|
||||
g_screenHover = -1;
|
||||
}
|
||||
if (!visible) {
|
||||
return result;
|
||||
@@ -731,6 +889,7 @@ int RenderBody(void* context) {
|
||||
DrawLootPage(*w, call->device);
|
||||
} else if (page == kPageCommon && g_pendingLoaded) {
|
||||
DrawSaves(*w, call->device);
|
||||
DrawScreen(*w, call->device);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -773,7 +932,7 @@ int EventBody(void* context) {
|
||||
return 1;
|
||||
}
|
||||
if (w && w->sheet && game::ControlShown(call->window) && game::ActiveTab(w->sheet) == kPageCommon &&
|
||||
g_pendingLoaded && HandleSavesEvent(*w, call->event)) {
|
||||
g_pendingLoaded && (HandleScreenEvent(*w, call->event) || HandleSavesEvent(*w, call->event))) {
|
||||
return 1;
|
||||
}
|
||||
if (w && w->sheet && game::ControlShown(call->window) && game::ActiveTab(w->sheet) == kPageKeys &&
|
||||
@@ -824,11 +983,16 @@ void* __fastcall UiOptions_Construct(void* self, void* edx, const char* name, Ui
|
||||
RE_HOOK(0x00716570, UiOptions_Construct);
|
||||
void* __fastcall UiOptions_Construct(void* self, void* edx, const char* name, UiRect rect, void* parent,
|
||||
uint32_t flags) {
|
||||
static const bool titled = [] {
|
||||
game::SetTextOverride(game::GameTextIdByKey(kScreenTitleKey), L"Bildschirm");
|
||||
return true;
|
||||
}();
|
||||
void* window = UiOptions_Construct_original(self, edx, name, rect, parent, flags);
|
||||
if (window) {
|
||||
g_windows.push_back({static_cast<uint8_t*>(window)});
|
||||
Window& w = g_windows.back();
|
||||
Classify(w);
|
||||
CreateScreenControls(w);
|
||||
CreateKeyControls(w);
|
||||
CreateLootControls(w);
|
||||
CreateSavesControls(w);
|
||||
@@ -873,6 +1037,7 @@ void __fastcall UiOptions_Apply(void* self, void* edx) {
|
||||
mods::input::SetBindingKeys(g_pending);
|
||||
mods::lootfilter::SetModes(g_lootPending);
|
||||
mods::characters::SetAutosaveMinutes(g_autosavePending);
|
||||
mods::monitor::SetPreferredId(g_screenPending);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "mods/hud.h"
|
||||
#include "mods/loadingscreen.h"
|
||||
#include "mods/megamap.h"
|
||||
#include "mods/monitor.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
@@ -270,17 +271,16 @@ void PatchLetterbox(int width) {
|
||||
|
||||
} // namespace
|
||||
|
||||
void ApplyRenderSize(game::DxDisplayMode& mode, HWND window) {
|
||||
void ApplyRenderSize(game::DxDisplayMode& mode) {
|
||||
if (!Enabled()) {
|
||||
return;
|
||||
}
|
||||
MONITORINFO monitor = {sizeof(monitor)};
|
||||
GetMonitorInfoW(MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY), &monitor);
|
||||
const RECT monitor = mods::monitor::GameMonitorRect();
|
||||
// Default: keep the original 768 pixels of world height (so everything
|
||||
// keeps its on-screen size once scaled to the monitor) and extend the width
|
||||
// to the monitor's aspect ratio. Height=0 renders at the monitor height 1:1.
|
||||
const int monitorWidth = monitor.rcMonitor.right - monitor.rcMonitor.left;
|
||||
const int monitorHeight = monitor.rcMonitor.bottom - monitor.rcMonitor.top;
|
||||
const int monitorWidth = monitor.right - monitor.left;
|
||||
const int monitorHeight = monitor.bottom - monitor.top;
|
||||
int height = re::ConfigInt("widescreen", "Height", static_cast<int>(kOriginalHeight));
|
||||
if (height <= 0) {
|
||||
height = monitorHeight;
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace mods::widescreen {
|
||||
// Called by the borderless mod before the renderer creates its surfaces:
|
||||
// sets the render size to the monitor (or the configured size) and resizes
|
||||
// the world view to match. No-op when disabled.
|
||||
void ApplyRenderSize(game::DxDisplayMode& mode, HWND window);
|
||||
void ApplyRenderSize(game::DxDisplayMode& mode);
|
||||
|
||||
// True while the current thread draws the load / save progress screen.
|
||||
bool IsLoadingScreen();
|
||||
|
||||
Reference in new issue
Block a user