diff --git a/src/spice2x/CMakeLists.txt b/src/spice2x/CMakeLists.txt index 1990b00..859eac8 100644 --- a/src/spice2x/CMakeLists.txt +++ b/src/spice2x/CMakeLists.txt @@ -302,6 +302,7 @@ set(SOURCE_FILES ${SOURCE_FILES} api/modules/drs.cpp api/modules/lcd.cpp api/modules/ddr.cpp + api/modules/resize.cpp # avs avs/core.cpp diff --git a/src/spice2x/README.md b/src/spice2x/README.md index ce350ec..979ae65 100644 --- a/src/spice2x/README.md +++ b/src/spice2x/README.md @@ -246,6 +246,12 @@ which also means that your hex edits are applicable directly. - info() - returns information about the serial LCD controller some games use +#### Resize +- image_resize_enable(enable: bool) + - enables or disables image resize state +- image_resize_set_scene(scene: int) + - sets the active scene for image resize state; set to 0 to disable resize + ## License Unless otherwise noted, all files are licensed under the GPLv3. See the LICENSE file for the full license text. diff --git a/src/spice2x/api/controller.cpp b/src/spice2x/api/controller.cpp index 83ed6b2..1c08e38 100644 --- a/src/spice2x/api/controller.cpp +++ b/src/spice2x/api/controller.cpp @@ -27,6 +27,7 @@ #include "modules/lights.h" #include "modules/memory.h" #include "modules/touch.h" +#include "modules/resize.h" #include "request.h" #include "response.h" @@ -403,6 +404,7 @@ void Controller::init_state(api::ClientState *state) { state->modules.push_back(new modules::Lights()); state->modules.push_back(new modules::Memory()); state->modules.push_back(new modules::Touch()); + state->modules.push_back(new modules::Resize()); } void Controller::free_state(api::ClientState *state) { diff --git a/src/spice2x/api/modules/resize.cpp b/src/spice2x/api/modules/resize.cpp new file mode 100644 index 0000000..84e70e5 --- /dev/null +++ b/src/spice2x/api/modules/resize.cpp @@ -0,0 +1,53 @@ +#include "resize.h" +#include "external/rapidjson/document.h" +#include "cfg/screen_resize.h" + +using namespace std::placeholders; +using namespace rapidjson; + +namespace api::modules { + + static thread_local std::vector CAPTURE_BUFFER; + + Resize::Resize() : Module("resize") { + functions["image_resize_enable"] = std::bind(&Resize::image_resize_enable, this, _1, _2); + functions["image_resize_set_scene"] = std::bind(&Resize::image_resize_set_scene, this, _1, _2); + } + + /** + * image_resize_enable(enable: bool) + */ + void Resize::image_resize_enable(Request &req, Response &res) { + if (req.params.Size() < 1) { + return error_params_insufficient(res); + } + if (!req.params[0].IsBool()) { + return error_type(res, "enable", "bool"); + } + + cfg::SCREENRESIZE->enable_screen_resize = req.params[0].GetBool(); + } + + /** + * image_resize_set_scene(scene: int) + */ + void Resize::image_resize_set_scene(Request &req, Response &res) { + if (req.params.Size() < 1) { + return error_params_insufficient(res); + } + if (!req.params[0].IsInt()) { + return error_type(res, "scene", "int"); + } + + const auto scene = req.params[0].GetInt(); + if (scene < 0 || (int)std::size(cfg::SCREENRESIZE->scene_settings) < scene) { + return error(res, "invalid scene number"); + } + if (scene == 0) { + cfg::SCREENRESIZE->enable_screen_resize = false; + } else { + cfg::SCREENRESIZE->enable_screen_resize = true; + cfg::SCREENRESIZE->screen_resize_current_scene = scene - 1; + } + } +} diff --git a/src/spice2x/api/modules/resize.h b/src/spice2x/api/modules/resize.h new file mode 100644 index 0000000..ca6f1da --- /dev/null +++ b/src/spice2x/api/modules/resize.h @@ -0,0 +1,18 @@ +#pragma once + +#include "api/module.h" +#include "api/request.h" + +namespace api::modules { + + class Resize : public Module { + public: + Resize(); + + private: + + // function definitions + void image_resize_enable(Request &req, Response &res); + void image_resize_set_scene(Request &req, Response &res); + }; +} diff --git a/src/spice2x/api/resources/python/spiceapi/__init__.py b/src/spice2x/api/resources/python/spiceapi/__init__.py index 5cd290a..b7fe465 100644 --- a/src/spice2x/api/resources/python/spiceapi/__init__.py +++ b/src/spice2x/api/resources/python/spiceapi/__init__.py @@ -12,3 +12,4 @@ from .keypads import * from .lights import * from .memory import * from .touch import * +from .resize import * \ No newline at end of file diff --git a/src/spice2x/api/resources/python/spiceapi/resize.py b/src/spice2x/api/resources/python/spiceapi/resize.py new file mode 100644 index 0000000..92f585e --- /dev/null +++ b/src/spice2x/api/resources/python/spiceapi/resize.py @@ -0,0 +1,12 @@ +from .connection import Connection +from .request import Request + +def image_resize_enable(con: Connection, enable: bool): + req = Request("resize", "image_resize_enable") + req.add_param(enable) + con.request(req) + +def image_resize_set_scene(con: Connection, scene: int): + req = Request("resize", "image_resize_set_scene") + req.add_param(scene) + con.request(req) diff --git a/src/spice2x/api/resources/python/spiceremote.py b/src/spice2x/api/resources/python/spiceremote.py index 2749711..52e37f1 100644 --- a/src/spice2x/api/resources/python/spiceremote.py +++ b/src/spice2x/api/resources/python/spiceremote.py @@ -397,6 +397,65 @@ class LightsTab(ttk.Frame): # set text self.txt_lights.set_text(txt) +class ResizeTab(ttk.Frame): + """Resize tab.""" + + def __init__(self, app, parent, **kwargs): + + # init frame + ttk.Frame.__init__(self, parent, **kwargs) + self.app = app + self.parent = parent + + # scale grid + self.columnconfigure(0, weight=1) + + # image resize + self.resize = ttk.Frame(self, padding=(8, 8, 8, 8)) + self.resize.grid(row=0, column=0, sticky=tk.E+tk.W) + self.resize.columnconfigure(0, weight=1) + self.resize.columnconfigure(1, weight=1) + self.resize_lbl = ttk.Label(self.resize, text="Image Resize") + self.resize_lbl.grid(row=0, columnspan=2) + + self.resize_off = ttk.Button(self.resize, text="Disable", command=self.action_resize_false) + self.resize_off.grid(row=2, column=0, sticky=NSEW, padx=2, pady=2) + + self.resize_on = ttk.Button(self.resize, text="Enable", command=self.action_resize_on) + self.resize_on.grid(row=2, column=1, sticky=NSEW, padx=2, pady=2) + + self.resize_scene_1 = ttk.Button(self.resize, text="Scene 1", command=self.action_resize_scene_1) + self.resize_scene_1.grid(row=3, column=0, sticky=NSEW, padx=2, pady=2) + self.resize_scene_2 = ttk.Button(self.resize, text="Scene 2", command=self.action_resize_scene_2) + self.resize_scene_2.grid(row=3, column=1, sticky=NSEW, padx=2, pady=2) + self.resize_scene_3 = ttk.Button(self.resize, text="Scene 3", command=self.action_resize_scene_3) + self.resize_scene_3.grid(row=4, column=0, sticky=NSEW, padx=2, pady=2) + self.resize_scene_4 = ttk.Button(self.resize, text="Scene 4", command=self.action_resize_scene_4) + self.resize_scene_4.grid(row=4, column=1, sticky=NSEW, padx=2, pady=2) + + @api_action + def action_resize_on(self): + spiceapi.image_resize_enable(self.app.connection, True) + + @api_action + def action_resize_false(self): + spiceapi.image_resize_enable(self.app.connection, False) + + @api_action + def action_resize_scene_1(self): + spiceapi.image_resize_set_scene(self.app.connection, 1) + + @api_action + def action_resize_scene_2(self): + spiceapi.image_resize_set_scene(self.app.connection, 2) + + @api_action + def action_resize_scene_3(self): + spiceapi.image_resize_set_scene(self.app.connection, 3) + + @api_action + def action_resize_scene_4(self): + spiceapi.image_resize_set_scene(self.app.connection, 4) class MainApp(ttk.Frame): """The main application frame.""" @@ -419,6 +478,8 @@ class MainApp(ttk.Frame): self.tabs.add(self.tab_analogs, text="Analogs") self.tab_lights = LightsTab(self, self.tabs) self.tabs.add(self.tab_lights, text="Lights") + self.tab_resize = ResizeTab(self, self.tabs) + self.tabs.add(self.tab_resize, text="Resize") self.tab_manual = ManualTab(self, self.tabs) self.tabs.add(self.tab_manual, text="Manual") self.tabs.pack(expand=True, fill=tk.BOTH) diff --git a/src/spice2x/cfg/screen_resize.cpp b/src/spice2x/cfg/screen_resize.cpp index 7de74ec..10956e8 100644 --- a/src/spice2x/cfg/screen_resize.cpp +++ b/src/spice2x/cfg/screen_resize.cpp @@ -76,14 +76,22 @@ namespace cfg { eamuse_get_game(), use_game_setting, root); - load_int_value(doc, root + "offset_x", this->offset_x); - load_int_value(doc, root + "offset_y", this->offset_y); - load_float_value(doc, root + "scale_x", this->scale_x); - load_float_value(doc, root + "scale_y", this->scale_y); + load_bool_value(doc, root + "enable_screen_resize", this->enable_screen_resize); load_bool_value(doc, root + "enable_linear_filter", this->enable_linear_filter); - load_bool_value(doc, root + "keep_aspect_ratio", this->keep_aspect_ratio); - load_bool_value(doc, root + "centered", this->centered); + for (size_t i = 0; i < std::size(this->scene_settings); i++) { + auto& scene = this->scene_settings[i]; + std::string prefix = ""; + if (0 < i) { + prefix += fmt::format("scenes/{}/", i-1); + } + load_int_value(doc, root + prefix + "offset_x", scene.offset_x); + load_int_value(doc, root + prefix + "offset_y", scene.offset_y); + load_float_value(doc, root + prefix + "scale_x", scene.scale_x); + load_float_value(doc, root + prefix + "scale_y", scene.scale_y); + load_bool_value(doc, root + prefix + "keep_aspect_ratio", scene.keep_aspect_ratio); + load_bool_value(doc, root + prefix + "centered", scene.centered); + } // windowed settings are always under game settings root = "/sp2x_games/" + eamuse_get_game() + "/"; @@ -189,14 +197,21 @@ namespace cfg { root); // full screen image settings - rapidjson::Pointer(root + "offset_x").Set(doc, this->offset_x); - rapidjson::Pointer(root + "offset_y").Set(doc, this->offset_y); - rapidjson::Pointer(root + "scale_x").Set(doc, this->scale_x); - rapidjson::Pointer(root + "scale_y").Set(doc, this->scale_y); rapidjson::Pointer(root + "enable_screen_resize").Set(doc, this->enable_screen_resize); rapidjson::Pointer(root + "enable_linear_filter").Set(doc, this->enable_linear_filter); - rapidjson::Pointer(root + "keep_aspect_ratio").Set(doc, this->keep_aspect_ratio); - rapidjson::Pointer(root + "centered").Set(doc, this->centered); + for (size_t i = 0; i < std::size(this->scene_settings); i++) { + auto& scene = this->scene_settings[i]; + std::string prefix = ""; + if (0 < i) { + prefix += fmt::format("scenes/{}/", i-1); + } + rapidjson::Pointer(root + prefix + "offset_x").Set(doc, scene.offset_x); + rapidjson::Pointer(root + prefix + "offset_y").Set(doc, scene.offset_y); + rapidjson::Pointer(root + prefix + "scale_x").Set(doc, scene.scale_x); + rapidjson::Pointer(root + prefix + "scale_y").Set(doc, scene.scale_y); + rapidjson::Pointer(root + prefix + "keep_aspect_ratio").Set(doc, scene.keep_aspect_ratio); + rapidjson::Pointer(root + prefix + "centered").Set(doc, scene.centered); + } // windowed mode settings rapidjson::Pointer(root + "w_always_on_top").Set(doc, this->window_always_on_top); diff --git a/src/spice2x/cfg/screen_resize.h b/src/spice2x/cfg/screen_resize.h index 2be45dd..103ae77 100644 --- a/src/spice2x/cfg/screen_resize.h +++ b/src/spice2x/cfg/screen_resize.h @@ -14,6 +14,15 @@ namespace cfg { ResizableFrame = 2 }; + struct fullscreen_setting { + int offset_x = 0; + int offset_y = 0; + float scale_x = 1.0; + float scale_y = 1.0; + bool keep_aspect_ratio = true; + bool centered = true; + }; + extern std::optional SCREEN_RESIZE_CFG_PATH_OVERRIDE; class ScreenResize { @@ -31,14 +40,10 @@ namespace cfg { ~ScreenResize(); // full screen (directx) image settings - int offset_x = 0; - int offset_y = 0; - float scale_x = 1.0; - float scale_y = 1.0; bool enable_screen_resize = false; + int8_t screen_resize_current_scene = 0; bool enable_linear_filter = true; - bool keep_aspect_ratio = true; - bool centered = true; + fullscreen_setting scene_settings[4]; // windowed mode sizing // Windows terminology: diff --git a/src/spice2x/games/io.cpp b/src/spice2x/games/io.cpp index 0019ca0..f1b1c94 100644 --- a/src/spice2x/games/io.cpp +++ b/src/spice2x/games/io.cpp @@ -439,6 +439,14 @@ namespace games { vkey_defaults.push_back(0xFF); names.emplace_back("Screen Resize"); vkey_defaults.push_back(0xFF); + names.emplace_back("Screen Resize Scene 1"); + vkey_defaults.push_back(0xFF); + names.emplace_back("Screen Resize Scene 2"); + vkey_defaults.push_back(0xFF); + names.emplace_back("Screen Resize Scene 3"); + vkey_defaults.push_back(0xFF); + names.emplace_back("Screen Resize Scene 4"); + vkey_defaults.push_back(0xFF); names.emplace_back("Force Exit Game"); vkey_defaults.push_back(0xFF); names.emplace_back("Navigator Activate"); diff --git a/src/spice2x/games/io.h b/src/spice2x/games/io.h index 46345a6..b399b08 100644 --- a/src/spice2x/games/io.h +++ b/src/spice2x/games/io.h @@ -23,6 +23,10 @@ namespace games { ToggleOverlay, ToggleCameraControl, ScreenResize, + ScreenResizeScene1, + ScreenResizeScene2, + ScreenResizeScene3, + ScreenResizeScene4, SuperExit, NavigatorActivate, NavigatorCancel, diff --git a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp index 043455d..25b7945 100644 --- a/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp +++ b/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp @@ -740,9 +740,10 @@ void SurfaceHook(IDirect3DDevice9 *pReal) { topSurface->UnlockRect(); // do the actual zoom / offset math - if (cfg::SCREENRESIZE->centered) { - targetRect.right = (w + rectLeft) / cfg::SCREENRESIZE->scale_x; - targetRect.bottom = (h + rectTop) / cfg::SCREENRESIZE->scale_y; + auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene]; + if (scene.centered) { + targetRect.right = (w + rectLeft) / scene.scale_x; + targetRect.bottom = (h + rectTop) / scene.scale_y; const LONG deltaH = ((targetRect.bottom - targetRect.top) - h) / 2; const LONG deltaW = ((targetRect.right - targetRect.left) - w) / 2; targetRect.top -= deltaH; @@ -750,12 +751,12 @@ void SurfaceHook(IDirect3DDevice9 *pReal) { targetRect.left -= deltaW; targetRect.right -= deltaW; } else { - targetRect.left -= cfg::SCREENRESIZE->offset_x; - targetRect.top += cfg::SCREENRESIZE->offset_y; - targetRect.right = -cfg::SCREENRESIZE->offset_x; - targetRect.right += (w + rectLeft) / cfg::SCREENRESIZE->scale_x; - targetRect.bottom = cfg::SCREENRESIZE->offset_y; - targetRect.bottom += (h + rectTop) / cfg::SCREENRESIZE->scale_y; + targetRect.left -= scene.offset_x; + targetRect.top += scene.offset_y; + targetRect.right = -scene.offset_x; + targetRect.right += (w + rectLeft) / scene.scale_x; + targetRect.bottom = scene.offset_y; + targetRect.bottom += (h + rectTop) / scene.scale_y; } // draw to back buffer diff --git a/src/spice2x/overlay/windows/screen_resize.cpp b/src/spice2x/overlay/windows/screen_resize.cpp index a39da0b..03143ef 100644 --- a/src/spice2x/overlay/windows/screen_resize.cpp +++ b/src/spice2x/overlay/windows/screen_resize.cpp @@ -17,6 +17,10 @@ namespace overlay::windows { this->init_pos = ImVec2(10, 10); this->toggle_button = games::OverlayButtons::ToggleScreenResize; this->toggle_screen_resize = games::OverlayButtons::ScreenResize; + this->toggle_scene[0] = games::OverlayButtons::ScreenResizeScene1; + this->toggle_scene[1] = games::OverlayButtons::ScreenResizeScene2; + this->toggle_scene[2] = games::OverlayButtons::ScreenResizeScene3; + this->toggle_scene[3] = games::OverlayButtons::ScreenResizeScene4; } ScreenResize::~ScreenResize() { @@ -42,13 +46,17 @@ namespace overlay::windows { void ScreenResize::reset_vars_to_default() { cfg::SCREENRESIZE->enable_screen_resize = false; + cfg::SCREENRESIZE->screen_resize_current_scene = 0; cfg::SCREENRESIZE->enable_linear_filter = true; - cfg::SCREENRESIZE->keep_aspect_ratio = true; - cfg::SCREENRESIZE->centered = true; - cfg::SCREENRESIZE->offset_x = 0; - cfg::SCREENRESIZE->offset_y = 0; - cfg::SCREENRESIZE->scale_x = 1.f; - cfg::SCREENRESIZE->scale_y = 1.f; + for (size_t i = 0; i < std::size(cfg::SCREENRESIZE->scene_settings); i++) { + auto& scene = cfg::SCREENRESIZE->scene_settings[i]; + scene.keep_aspect_ratio = true; + scene.centered = true; + scene.offset_x = 0; + scene.offset_y = 0; + scene.scale_x = 1.f; + scene.scale_y = 1.f; + } cfg::SCREENRESIZE->enable_window_resize = false; cfg::SCREENRESIZE->window_always_on_top = false; @@ -88,28 +96,51 @@ namespace overlay::windows { void ScreenResize::build_fullscreen_config() { // enable checkbox - ImGui::TextWrapped("Hint: bind a key to Screen Resize for quickly toggling this on/off."); ImGui::Checkbox("Enable", &cfg::SCREENRESIZE->enable_screen_resize); + ImGui::SameLine(); + ImGui::HelpMarker("Hint: bind a key to Screen Resize for a quick toggle."); ImGui::BeginDisabled(!cfg::SCREENRESIZE->enable_screen_resize); + ImGui::Checkbox("Linear Filter", &cfg::SCREENRESIZE->enable_linear_filter); + + if (ImGui::RadioButton("Scene 1", cfg::SCREENRESIZE->screen_resize_current_scene == 0)) { + cfg::SCREENRESIZE->screen_resize_current_scene = 0; + } + ImGui::SameLine(); + if (ImGui::RadioButton("Scene 2", cfg::SCREENRESIZE->screen_resize_current_scene == 1)) { + cfg::SCREENRESIZE->screen_resize_current_scene = 1; + } + ImGui::SameLine(); + if (ImGui::RadioButton("Scene 3", cfg::SCREENRESIZE->screen_resize_current_scene == 2)) { + cfg::SCREENRESIZE->screen_resize_current_scene = 2; + } + ImGui::SameLine(); + if (ImGui::RadioButton("Scene 4", cfg::SCREENRESIZE->screen_resize_current_scene == 3)) { + cfg::SCREENRESIZE->screen_resize_current_scene = 3; + } + ImGui::SameLine(); + ImGui::HelpMarker( + "Hint: bind a key to Screen Resize 1/2/3/4 for quick scene switching. " + "Scene 1 is the default scene activated when starting the game."); + + auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene]; // general settings - ImGui::Checkbox("Linear Filter", &cfg::SCREENRESIZE->enable_linear_filter); - ImGui::Checkbox("Centered", &cfg::SCREENRESIZE->centered); - if (!cfg::SCREENRESIZE->centered) { - ImGui::InputInt("X Offset", &cfg::SCREENRESIZE->offset_x); - ImGui::InputInt("Y Offset", &cfg::SCREENRESIZE->offset_y); + ImGui::Checkbox("Centered", &scene.centered); + if (!scene.centered) { + ImGui::InputInt("X Offset", &scene.offset_x); + ImGui::InputInt("Y Offset", &scene.offset_y); } // aspect ratio - ImGui::Checkbox("Keep Aspect Ratio", &cfg::SCREENRESIZE->keep_aspect_ratio); - if (cfg::SCREENRESIZE->keep_aspect_ratio) { - if (ImGui::SliderFloat("Scale", &cfg::SCREENRESIZE->scale_x, 0.65f, 2.0f)) { - cfg::SCREENRESIZE->scale_y = cfg::SCREENRESIZE->scale_x; + ImGui::Checkbox("Keep Aspect Ratio", &scene.keep_aspect_ratio); + if (scene.keep_aspect_ratio) { + if (ImGui::SliderFloat("Scale", &scene.scale_x, 0.65f, 2.0f)) { + scene.scale_y = scene.scale_x; } } else { - ImGui::SliderFloat("Width Scale", &cfg::SCREENRESIZE->scale_x, 0.65f, 2.0f); - ImGui::SliderFloat("Height Scale", &cfg::SCREENRESIZE->scale_y, 0.65f, 2.0f); + ImGui::SliderFloat("Width Scale", &scene.scale_x, 0.65f, 2.0f); + ImGui::SliderFloat("Height Scale", &scene.scale_y, 0.65f, 2.0f); } ImGui::EndDisabled(); @@ -217,8 +248,10 @@ namespace overlay::windows { void ScreenResize::update() { Window::update(); + auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game()); + + // toggle if (this->toggle_screen_resize != ~0u) { - auto overlay_buttons = games::get_buttons_overlay(eamuse_get_game()); bool toggle_screen_resize_new = overlay_buttons && this->overlay->hotkeys_triggered() && GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(this->toggle_screen_resize)); @@ -228,5 +261,36 @@ namespace overlay::windows { } this->toggle_screen_resize_state = toggle_screen_resize_new; } + + // scene switch + auto toggle_scene_state_new = ~0u; + for (size_t i = 0; i < std::size(this->toggle_scene); i++) { + if (this->toggle_scene[i] == ~0u) { + continue; + } + bool scene_switched = overlay_buttons + && this->overlay->hotkeys_triggered() + && GameAPI::Buttons::getState(RI_MGR, overlay_buttons->at(this->toggle_scene[i])); + + if (scene_switched) { + toggle_scene_state_new = (uint32_t)i; + } + + // only detect rising edges of key presses + if (scene_switched && (this->toggle_scene_state != i)) { + if (cfg::SCREENRESIZE->screen_resize_current_scene == (int8_t)i && + cfg::SCREENRESIZE->enable_screen_resize) { + // this scene is already active, turn scaling off + cfg::SCREENRESIZE->enable_screen_resize = false; + } else { + // switch to scene + cfg::SCREENRESIZE->enable_screen_resize = true; + cfg::SCREENRESIZE->screen_resize_current_scene = i; + } + break; + } + } + // remember if a key was pressed (or nothing pressed) this frame + this->toggle_scene_state = toggle_scene_state_new; } } diff --git a/src/spice2x/overlay/windows/screen_resize.h b/src/spice2x/overlay/windows/screen_resize.h index 8f144f9..d4e7073 100644 --- a/src/spice2x/overlay/windows/screen_resize.h +++ b/src/spice2x/overlay/windows/screen_resize.h @@ -16,6 +16,9 @@ namespace overlay::windows { size_t toggle_screen_resize = ~0u; bool toggle_screen_resize_state = false; + size_t toggle_scene[4] = { ~0u, ~0u, ~0u, ~0u }; + uint32_t toggle_scene_state = ~0u; + void build_fullscreen_config(); void build_windowed_config(); void build_footer();