From 6a531e535b933bbe7f066f2778fbbfc0a745ba16 Mon Sep 17 00:00:00 2001 From: joachim Date: Thu, 2 Mar 2023 14:01:52 +0100 Subject: [PATCH 01/11] Allow joystick to be fetch if windows has no focus in SDL input mode. This is usefull for multiple instance of supermodel networked on the same machine. --- Src/OSD/SDL/SDLInputSystem.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Src/OSD/SDL/SDLInputSystem.cpp b/Src/OSD/SDL/SDLInputSystem.cpp index e4d4a7fd..4c6c901a 100644 --- a/Src/OSD/SDL/SDLInputSystem.cpp +++ b/Src/OSD/SDL/SDLInputSystem.cpp @@ -192,6 +192,11 @@ void CSDLInputSystem::OpenJoysticks() int numHapticAxes = 0; int possibleEffect = 0; + //allow joystick to be fetch if windows has no focus. + //this is usefull for multiple instance networked on the same machine + SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); + + for (int joyNum = 0; joyNum < numJoys; joyNum++) { numHapticAxes = 0; From b7a156f3f33492f9b52c65dc29496c6d6e345690 Mon Sep 17 00:00:00 2001 From: joachim Date: Thu, 2 Mar 2023 14:07:06 +0100 Subject: [PATCH 02/11] Add two video settings: -pos=, Position [Default: centered] -borderless Windowed mode with no border These 2 settings are usefull when setting up a multiplayer game on one machine. Example for 4 windows in fullHD: start "Master" /D"Master_P4" Supermodel.exe -input-system=sdl ..\ROMS\dayto2pe.zip -res=960,540 -borderless -pos=0,0 start "Slave1" /D"Slave1_P4" Supermodel.exe -input-system=sdl ..\ROMS\dayto2pe.zip -res=960,540 -borderless -pos=960,0 start "Slave2" /D"Slave2_P4" Supermodel.exe -input-system=sdl ..\ROMS\dayto2pe.zip -res=960,540 -borderless -pos=0,540 start "Slave3" /D"Slave3_P4" Supermodel.exe -input-system=sdl ..\ROMS\dayto2pe.zip -res=960,540 -borderless -pos=960,540 --- Src/OSD/SDL/Main.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp index 5722b322..b688b44f 100644 --- a/Src/OSD/SDL/Main.cpp +++ b/Src/OSD/SDL/Main.cpp @@ -1086,8 +1086,16 @@ int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *In sprintf(baseTitleStr, "Supermodel - %s", game.title.c_str()); SDL_SetWindowTitle(s_window, baseTitleStr); SDL_SetWindowSize(s_window, totalXRes, totalYRes); + + if ( s_runtime_config["Xpos"].ValueAs() != "NA" && + s_runtime_config["Ypos"].ValueAs() != "NA" ) + SDL_SetWindowPosition(s_window, s_runtime_config["Xpos"].ValueAs(), s_runtime_config["Ypos"].ValueAs()); + else SDL_SetWindowPosition(s_window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + if (s_runtime_config["BorderLess"].ValueAs()) + SDL_SetWindowBordered(s_window, SDL_FALSE); + SetFullScreenRefreshRate(); bool stretch = s_runtime_config["Stretch"].ValueAs(); @@ -1629,7 +1637,11 @@ static Util::Config::Node DefaultConfig() config.Set("QuadRendering", false); config.Set("XResolution", "496"); config.Set("YResolution", "384"); + config.Set("XPos", "NA"); + config.Set("YPos", "NA"); config.Set("FullScreen", false); + config.Set("BorderLess", false); + config.Set("WideScreen", false); config.Set("Stretch", false); config.Set("WideBackground", false); @@ -1708,7 +1720,9 @@ static void Help(void) puts(""); puts("Video Options:"); puts(" -res=, Resolution [Default: 496,384]"); + puts(" -pos=, Position [Default: centered]"); puts(" -window Windowed mode [Default]"); + puts(" -borderless Windowed mode with no border"); puts(" -fullscreen Full screen mode"); puts(" -wide-screen Expand 3D field of view to screen width"); puts(" -wide-bg When wide-screen mode is enabled, also expand the 2D"); @@ -1830,6 +1844,7 @@ static ParsedCommandLine ParseCommandLine(int argc, char **argv) { "-no-gpu-thread", { "GPUMultiThreaded", false } }, { "-window", { "FullScreen", false } }, { "-fullscreen", { "FullScreen", true } }, + { "-borderless", { "BorderLess", true } }, { "-no-wide-screen", { "WideScreen", false } }, { "-wide-screen", { "WideScreen", true } }, { "-stretch", { "Stretch", true } }, @@ -1936,6 +1951,31 @@ static ParsedCommandLine ParseCommandLine(int argc, char **argv) } } } + else if (arg == "-pos" || arg.find("-pos=") == 0) + { + std::vector parts = Util::Format(arg).Split('='); + if (parts.size() != 2) + { + ErrorLog("'-pos' requires both a X and Y (e.g., '-pos=10,0')."); + cmd_line.error = true; + } + else + { + unsigned x, y; + if (2 == sscanf(&argv[i][4], "=%u,%u", &x, &y)) + { + std::string xres = Util::Format() << x; + std::string yres = Util::Format() << y; + cmd_line.config.Set("Xpos", xres); + cmd_line.config.Set("Ypos", yres); + } + else + { + ErrorLog("'-pos' requires both a X and Y (e.g., '-pos=10,0')."); + cmd_line.error = true; + } + } + } else if (arg == "-true-hz") cmd_line.config.Set("RefreshRate", 57.524f); else if (arg == "-print-gl-info") From 1b5b38db7201711142bf72f661a0ddc024aeaa66 Mon Sep 17 00:00:00 2001 From: joachim Date: Thu, 2 Mar 2023 14:30:12 +0100 Subject: [PATCH 03/11] -pos=, -> correct error when not present --- Src/OSD/SDL/Main.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp index b688b44f..9c557d00 100644 --- a/Src/OSD/SDL/Main.cpp +++ b/Src/OSD/SDL/Main.cpp @@ -1087,11 +1087,10 @@ int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *In SDL_SetWindowTitle(s_window, baseTitleStr); SDL_SetWindowSize(s_window, totalXRes, totalYRes); - if ( s_runtime_config["Xpos"].ValueAs() != "NA" && - s_runtime_config["Ypos"].ValueAs() != "NA" ) + if ( !s_runtime_config["Xpos"].Empty() && !s_runtime_config["Xpos"].Empty()) SDL_SetWindowPosition(s_window, s_runtime_config["Xpos"].ValueAs(), s_runtime_config["Ypos"].ValueAs()); else - SDL_SetWindowPosition(s_window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + SDL_SetWindowPosition(s_window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); if (s_runtime_config["BorderLess"].ValueAs()) SDL_SetWindowBordered(s_window, SDL_FALSE); From 722c70bf865892fa64cc369e368e2fe170b1b403 Mon Sep 17 00:00:00 2001 From: joachim Date: Sat, 4 Mar 2023 01:04:25 +0100 Subject: [PATCH 04/11] comply to @trzy requested changes --- Src/OSD/SDL/Main.cpp | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp index 9c557d00..30a1248f 100644 --- a/Src/OSD/SDL/Main.cpp +++ b/Src/OSD/SDL/Main.cpp @@ -1087,13 +1087,19 @@ int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *In SDL_SetWindowTitle(s_window, baseTitleStr); SDL_SetWindowSize(s_window, totalXRes, totalYRes); - if ( !s_runtime_config["Xpos"].Empty() && !s_runtime_config["Xpos"].Empty()) - SDL_SetWindowPosition(s_window, s_runtime_config["Xpos"].ValueAs(), s_runtime_config["Ypos"].ValueAs()); - else - SDL_SetWindowPosition(s_window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + if (!s_runtime_config["WindowXPosition"].Empty() && !s_runtime_config["WindowYPosition"].Empty()) + { + SDL_SetWindowPosition(s_window, s_runtime_config["WindowXPosition"].ValueAs(), s_runtime_config["WindowYPosition"].ValueAs()); + } + else + { + SDL_SetWindowPosition(s_window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + } - if (s_runtime_config["BorderLess"].ValueAs()) + if (s_runtime_config["BorderlessWindow"].ValueAs()) + { SDL_SetWindowBordered(s_window, SDL_FALSE); + } SetFullScreenRefreshRate(); @@ -1636,10 +1642,10 @@ static Util::Config::Node DefaultConfig() config.Set("QuadRendering", false); config.Set("XResolution", "496"); config.Set("YResolution", "384"); - config.Set("XPos", "NA"); - config.Set("YPos", "NA"); + config.Set("WindowXPosition", "NA"); + config.Set("WindowYPosition", "NA"); config.Set("FullScreen", false); - config.Set("BorderLess", false); + config.Set("BorderlessWindow", false); config.Set("WideScreen", false); config.Set("Stretch", false); @@ -1719,7 +1725,7 @@ static void Help(void) puts(""); puts("Video Options:"); puts(" -res=, Resolution [Default: 496,384]"); - puts(" -pos=, Position [Default: centered]"); + puts(" -window-pos=, Position [Default: centered]"); puts(" -window Windowed mode [Default]"); puts(" -borderless Windowed mode with no border"); puts(" -fullscreen Full screen mode"); @@ -1843,7 +1849,7 @@ static ParsedCommandLine ParseCommandLine(int argc, char **argv) { "-no-gpu-thread", { "GPUMultiThreaded", false } }, { "-window", { "FullScreen", false } }, { "-fullscreen", { "FullScreen", true } }, - { "-borderless", { "BorderLess", true } }, + { "-borderless", { "BorderlessWindow", true } }, { "-no-wide-screen", { "WideScreen", false } }, { "-wide-screen", { "WideScreen", true } }, { "-stretch", { "Stretch", true } }, @@ -1950,12 +1956,12 @@ static ParsedCommandLine ParseCommandLine(int argc, char **argv) } } } - else if (arg == "-pos" || arg.find("-pos=") == 0) + else if (arg == "-window-pos" || arg.find("-window-pos=") == 0) { std::vector parts = Util::Format(arg).Split('='); if (parts.size() != 2) { - ErrorLog("'-pos' requires both a X and Y (e.g., '-pos=10,0')."); + ErrorLog("'-window-pos' requires both an X and Y position (e.g., '-window-pos=10,0')."); cmd_line.error = true; } else @@ -1965,12 +1971,12 @@ static ParsedCommandLine ParseCommandLine(int argc, char **argv) { std::string xres = Util::Format() << x; std::string yres = Util::Format() << y; - cmd_line.config.Set("Xpos", xres); - cmd_line.config.Set("Ypos", yres); + cmd_line.config.Set("WindowXPosition", xres); + cmd_line.config.Set("WindowYPosition", yres); } else { - ErrorLog("'-pos' requires both a X and Y (e.g., '-pos=10,0')."); + ErrorLog("'-window-pos' requires both an X and Y position (e.g., '-window-pos=10,0')."); cmd_line.error = true; } } From 65559ccffb6ce217324c751cebbb76c0fa60b107 Mon Sep 17 00:00:00 2001 From: joachim Date: Sat, 4 Mar 2023 12:40:57 +0100 Subject: [PATCH 05/11] comply to @trzy requested changes # --- Src/OSD/SDL/Main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp index 30a1248f..2f2184ea 100644 --- a/Src/OSD/SDL/Main.cpp +++ b/Src/OSD/SDL/Main.cpp @@ -1093,7 +1093,7 @@ int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *In } else { - SDL_SetWindowPosition(s_window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + SDL_SetWindowPosition(s_window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); } if (s_runtime_config["BorderlessWindow"].ValueAs()) @@ -1725,7 +1725,7 @@ static void Help(void) puts(""); puts("Video Options:"); puts(" -res=, Resolution [Default: 496,384]"); - puts(" -window-pos=, Position [Default: centered]"); + puts(" -window-pos=, Window position [Default: centered]"); puts(" -window Windowed mode [Default]"); puts(" -borderless Windowed mode with no border"); puts(" -fullscreen Full screen mode"); From 43833a2e808f2030a26bd2149d9b0552fadf0309 Mon Sep 17 00:00:00 2001 From: Bart Trzynadlowski Date: Sat, 4 Mar 2023 11:58:52 -0800 Subject: [PATCH 06/11] Config nodes: added the ability to clear out node values or create empty leaf nodes --- Src/Util/NewConfig.cpp | 64 +++++++++++++++++++++++++++++++++++------- Src/Util/NewConfig.h | 50 ++++++++++++++++----------------- 2 files changed, 78 insertions(+), 36 deletions(-) diff --git a/Src/Util/NewConfig.cpp b/Src/Util/NewConfig.cpp index 015414a5..37826d8b 100644 --- a/Src/Util/NewConfig.cpp +++ b/Src/Util/NewConfig.cpp @@ -7,7 +7,7 @@ * * A hierarchical data structure supporting arbitrary nesting. Each node * (Config::Node) has a key and either a value or children (in fact, it may - * have both, but this rarely makes semantic sense and so the config tree + * have both, but this rarely makes semantic sense and so the config tree * builders take care not to allow it). * * Nesting is denoted with the '/' separator. For example: @@ -20,10 +20,10 @@ * [1] accesses the value of the node (more on this below). [2] accesses the * a child node with key "foo". [3] accesses the value of the child node "foo". * [4] accesses child "bar" of child "foo", and so forth. - * + * * Similar to map semantics, the operator [] never fails. If the node does not * exist, it creates a dummy "missing" node that is retained as a hidden child. - * This node will have an empty value, which cannot be accessed, except using + * This node will have an empty value, which cannot be accessed, except using * the ValueAsDefault<> method. This scheme exists to simplify lookup code for * keys known at compile time, the logic being that any "missing" key should * could just as well have been there in the first place, thus making the added @@ -39,7 +39,7 @@ * * Nodes at the same nesting level (siblings) are strung together in a * linked list. Parents also maintain pointers to the first and last of their - * children (for order-preserving iteration) as well as a map for direct + * children (for order-preserving iteration) as well as a map for direct * lookup by key. * * Keys may be reused at a given level. Key-value pairs will have their order @@ -80,7 +80,7 @@ * * Conversions as supported but are implemented via serialization and de- * serialization using sstream. Most "sane" conversions will work as expected. - * When a conversion to T is desired, or if the stored value type is not + * When a conversion to T is desired, or if the stored value type is not * precisely known, use: * * node.ValueAs() @@ -103,7 +103,7 @@ * - Section nodes have their key set to the section name and value empty. * They are the only nodes that can have children (i.e., IsLeaf() == false, * HasChildren() == true). - * - Top-level node in the tree is the global section, and its key is + * - Top-level node in the tree is the global section, and its key is * "Global". * - Only the global section (top-level section) may have child nodes that are * sections. The config tree can therefore only be up to 2 levels deep: the @@ -133,7 +133,7 @@ * [ Section4, , Section5 ] * SettingX = bar * - * In this example, SettingX will be set to "foo" in Section1, Section2, and + * In this example, SettingX will be set to "foo" in Section1, Section2, and * Section3. It will be set to "bar" in Section4, Section5, and the "Global" * section because of the unnamed element. * @@ -147,6 +147,7 @@ */ #include "Util/NewConfig.h" +#include namespace Util { @@ -251,8 +252,36 @@ namespace Util return os.str(); } + // Adds an empty node (no value and where Empty() will return true) + Node &Node::AddEmpty(const std::string &path) + { + std::vector keys = Util::Format(path).Split('/'); + Node *parent = this; + ptr_t node; + for (size_t i = 0; i < keys.size(); i++) + { + bool leaf = i == keys.size() - 1; + auto it = parent->m_children.find(keys[i]); + if (leaf || it == parent->m_children.end()) + { + // Create node at this level and leave it empty + node = std::make_shared(keys[i]); + // Attach node to parent and move down to next nesting level: last + // created node is new parent + AddChild(*parent, node); + parent = node.get(); + } + else + { + // Descend deeper... + parent = it->second.get(); + } + } + return *node; + } + // Adds a newly-created node (which, among other things, implies no - // children) as a child + // children) as a child void Node::AddChild(Node &parent, ptr_t &node) { if (!parent.m_last_child) @@ -264,7 +293,7 @@ namespace Util { parent.m_last_child->m_next_sibling = node; parent.m_last_child = node; - } + } parent.m_children[node->m_key] = node; } @@ -290,7 +319,7 @@ namespace Util m_last_child.swap(rhs.m_last_child); m_children.swap(rhs.m_children); const_cast(&m_key)->swap(*const_cast(&rhs.m_key)); - m_value.swap(rhs.m_value); + m_value.swap(rhs.m_value); } Node &Node::operator=(const Node &rhs) @@ -340,5 +369,20 @@ namespace Util { //std::cout << ">>> Destroyed " << m_key << " (" << this << ")" << std::endl; } + + void PrintConfigTree(const Node &config, int indent_level, int tab_stops) + { + std::fill_n(std::ostream_iterator(std::cout), tab_stops * indent_level, ' '); + std::cout << config.Key(); + if (config.Exists()) + { + std::cout << " = " << config.ValueAs(); + } + std::cout << std::endl; + for (const Node &child: config) + { + PrintConfigTree(child, indent_level + 1, tab_stops); + } + } } // Config } // Util diff --git a/Src/Util/NewConfig.h b/Src/Util/NewConfig.h index 4ca138ea..c28e8f82 100644 --- a/Src/Util/NewConfig.h +++ b/Src/Util/NewConfig.h @@ -36,6 +36,7 @@ namespace Util void CheckEmptyOrMissing() const; const Node &MissingNode(const std::string &key) const; + Node &AddEmpty(const std::string &path); void AddChild(Node &parent, ptr_t &node); void DeepCopy(const Node &that); void Swap(Node &rhs); @@ -168,6 +169,11 @@ namespace Util return m_value; } + inline void Clear() + { + m_value = nullptr; + } + inline void SetValue(const std::shared_ptr &value) { m_value = value; @@ -222,32 +228,9 @@ namespace Util template Node &Add(const std::string &path, const T &value) { - std::vector keys = Util::Format(path).Split('/'); - Node *parent = this; - ptr_t node; - for (size_t i = 0; i < keys.size(); i++) - { - bool leaf = i == keys.size() - 1; - auto it = parent->m_children.find(keys[i]); - if (leaf || it == parent->m_children.end()) - { - // Create node at this level - node = std::make_shared(keys[i]); - // The leaf node gets the value - if (leaf) - node->SetValue(value); - // Attach node to parent and move down to next nesting level: last - // created node is new parent - AddChild(*parent, node); - parent = node.get(); - } - else - { - // Descend deeper... - parent = it->second.get(); - } - } - return *node; + Node &new_leaf_node = AddEmpty(path); + new_leaf_node.SetValue(value); + return new_leaf_node; } Node &Add(const std::string &path) @@ -266,6 +249,19 @@ namespace Util Add(key, value); } + void SetEmpty(const std::string &key) + { + Node *node = TryGet(key); + if (node) + { + node->Clear(); + } + else + { + AddEmpty(key); + } + } + // True if value is empty (does not exist) inline bool Empty() const { @@ -312,6 +308,8 @@ namespace Util Node(Node&& that) noexcept; ~Node(); }; + + void PrintConfigTree(const Node &config, int indent_level = 0, int tab_stops = 2); } // Config } // Util From bb8b31e3c6acce021836968825f190c479c604af Mon Sep 17 00:00:00 2001 From: Bart Trzynadlowski Date: Sat, 4 Mar 2023 11:59:49 -0800 Subject: [PATCH 07/11] Fixed window position config storage and command line parsing --- Src/OSD/SDL/Main.cpp | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp index 2f2184ea..f87d71c4 100644 --- a/Src/OSD/SDL/Main.cpp +++ b/Src/OSD/SDL/Main.cpp @@ -177,7 +177,7 @@ static void GLAPIENTRY DebugCallback(GLenum source, GLenum type, GLuint id, GLen } // In windows with an nvidia card (sorry not tested anything else) you can customise the resolution. -// This also allows you to set a totally custom refresh rate. Apparently you can drive most monitors at +// This also allows you to set a totally custom refresh rate. Apparently you can drive most monitors at // 57.5fps with no issues. Anyway this code will automatically pick up your custom refresh rate, and set it if it exists // It it doesn't exist, then it'll probably just default to 60 or whatever your refresh rate is. static void SetFullScreenRefreshRate() @@ -828,7 +828,7 @@ struct BasicDraw const char* vertexShader = R"glsl( #version 410 core - + uniform mat4 mvp; layout(location = 0) in vec3 inVertices; @@ -841,7 +841,7 @@ struct BasicDraw const char* fragmentShader = R"glsl( #version 410 core - + uniform vec4 colour; out vec4 fragColour; @@ -1087,15 +1087,10 @@ int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *In SDL_SetWindowTitle(s_window, baseTitleStr); SDL_SetWindowSize(s_window, totalXRes, totalYRes); - if (!s_runtime_config["WindowXPosition"].Empty() && !s_runtime_config["WindowYPosition"].Empty()) - { - SDL_SetWindowPosition(s_window, s_runtime_config["WindowXPosition"].ValueAs(), s_runtime_config["WindowYPosition"].ValueAs()); - } - else - { - SDL_SetWindowPosition(s_window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); - } - + int xpos = s_runtime_config["WindowXPosition"].Exists() ? s_runtime_config["WindowXPosition"].ValueAs() : SDL_WINDOWPOS_CENTERED; + int ypos = s_runtime_config["WindowYPosition"].Exists() ? s_runtime_config["WindowYPosition"].ValueAs() : SDL_WINDOWPOS_CENTERED; + SDL_SetWindowPosition(s_window, xpos, ypos); + if (s_runtime_config["BorderlessWindow"].ValueAs()) { SDL_SetWindowBordered(s_window, SDL_FALSE); @@ -1642,8 +1637,8 @@ static Util::Config::Node DefaultConfig() config.Set("QuadRendering", false); config.Set("XResolution", "496"); config.Set("YResolution", "384"); - config.Set("WindowXPosition", "NA"); - config.Set("WindowYPosition", "NA"); + config.SetEmpty("WindowXPosition"); + config.SetEmpty("WindowYPosition"); config.Set("FullScreen", false); config.Set("BorderlessWindow", false); @@ -1725,7 +1720,7 @@ static void Help(void) puts(""); puts("Video Options:"); puts(" -res=, Resolution [Default: 496,384]"); - puts(" -window-pos=, Window position [Default: centered]"); + puts(" -window-pos=, Window position [Default: centered]"); puts(" -window Windowed mode [Default]"); puts(" -borderless Windowed mode with no border"); puts(" -fullscreen Full screen mode"); @@ -1966,13 +1961,11 @@ static ParsedCommandLine ParseCommandLine(int argc, char **argv) } else { - unsigned x, y; - if (2 == sscanf(&argv[i][4], "=%u,%u", &x, &y)) + int xpos, ypos; + if (2 == sscanf(&argv[i][11], "=%d,%d", &xpos, &ypos)) { - std::string xres = Util::Format() << x; - std::string yres = Util::Format() << y; - cmd_line.config.Set("WindowXPosition", xres); - cmd_line.config.Set("WindowYPosition", yres); + cmd_line.config.Set("WindowXPosition", xpos); + cmd_line.config.Set("WindowYPosition", ypos); } else { From 45e076d6b956bc8207430877322c3d432eca0a9e Mon Sep 17 00:00:00 2001 From: joachim Date: Sat, 11 Mar 2023 13:40:05 +0100 Subject: [PATCH 08/11] Add a white border around image to be compatible with "Sinden-like" video based lightgun. --- Src/OSD/SDL/Main.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp index f87d71c4..6c92935d 100644 --- a/Src/OSD/SDL/Main.cpp +++ b/Src/OSD/SDL/Main.cpp @@ -104,6 +104,7 @@ SDL_Window *s_window = nullptr; static unsigned xOffset, yOffset; // offset of renderer output within OpenGL viewport static unsigned xRes, yRes; // renderer output resolution (can be smaller than GL viewport) static unsigned totalXRes, totalYRes; // total resolution (the whole GL viewport) +static unsigned whiteBorder = 0; static bool SetGLGeometry(unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned *xResPtr, unsigned *yResPtr, unsigned *totalXResPtr, unsigned *totalYResPtr, bool keepAspectRatio) { @@ -117,6 +118,15 @@ static bool SetGLGeometry(unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned * // If required, fix the aspect ratio of the resolution that the user passed to match Model 3 ratio float xRes = float(*xResPtr); float yRes = float(*yResPtr); + + //if we want to draw a white border, we have also to put a black border around it so we are sure lightgun will find it... + if (s_runtime_config["DrawWhiteBorder"].ValueAs()) + { + int blackborder = whiteBorder = 50; //50px borders + xRes -= (whiteBorder + blackborder); + yRes -= (whiteBorder + blackborder); + } + if (keepAspectRatio) { float model3Ratio = float(496.0/384.0); @@ -778,7 +788,7 @@ static void LoadNVRAM(IEmulator *Model3) /****************************************************************************** UI Rendering - Currently, only does crosshairs for light gun games. + Currently, only crosshairs and white border for light gun games. ******************************************************************************/ struct BasicDraw @@ -980,6 +990,36 @@ static void UpdateCrosshairs(uint32_t currentInputs, CInputs *Inputs, unsigned c } +void GenQuad(std::vector &verts, float x, float y, float w, float h) +{ + verts.emplace_back(x, y); + verts.emplace_back(x, y + h); + verts.emplace_back(x + w, y + h); + verts.emplace_back(x, y); + verts.emplace_back(x + w, y + h); + verts.emplace_back(x + w, y); +} + +void DrawWhiteBorder() +{ + glUseProgram(0); + glViewport(0, 0, totalXRes, totalYRes); + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + + New3D::Mat4 m; + m.Ortho(0.0, totalXRes, totalYRes, 0.0, -1.0f, 1.0f); + std::vector verts; + int internalWhiteBorder = whiteBorder / 2; + GenQuad(verts, xOffset - internalWhiteBorder, yOffset - internalWhiteBorder, internalWhiteBorder, yRes + internalWhiteBorder * 2); + GenQuad(verts, xOffset + xRes, yOffset - internalWhiteBorder, internalWhiteBorder, yRes + internalWhiteBorder * 2); + GenQuad(verts, xOffset, yOffset - internalWhiteBorder, xRes , internalWhiteBorder); + GenQuad(verts, xOffset, yOffset + yRes, xRes, internalWhiteBorder); + basicDraw.Draw(GL_TRIANGLES, m, verts.data(), (int)verts.size(), 1.0f, 1.0f, 1.0f, 1.0f); + glEnable(GL_SCISSOR_TEST); +} + /****************************************************************************** Video Callbacks ******************************************************************************/ @@ -997,7 +1037,12 @@ void EndFrameVideo() // Show crosshairs for light gun games if (videoInputs) UpdateCrosshairs(currentInputs, videoInputs, s_runtime_config["Crosshairs"].ValueAs()); - + + //do we need to draw white border? + if (whiteBorder != 0) + { + DrawWhiteBorder(); + } // Swap the buffers SDL_GL_SwapWindow(s_window); } @@ -1641,6 +1686,7 @@ static Util::Config::Node DefaultConfig() config.SetEmpty("WindowYPosition"); config.Set("FullScreen", false); config.Set("BorderlessWindow", false); + config.Set("DrawWhiteBorder", false); config.Set("WideScreen", false); config.Set("Stretch", false); @@ -1723,6 +1769,7 @@ static void Help(void) puts(" -window-pos=, Window position [Default: centered]"); puts(" -window Windowed mode [Default]"); puts(" -borderless Windowed mode with no border"); + puts(" -draw-white-border Draw a whiteborder around image: usefull for some lightgun type (Sinden LightGun)"); puts(" -fullscreen Full screen mode"); puts(" -wide-screen Expand 3D field of view to screen width"); puts(" -wide-bg When wide-screen mode is enabled, also expand the 2D"); @@ -1845,6 +1892,7 @@ static ParsedCommandLine ParseCommandLine(int argc, char **argv) { "-window", { "FullScreen", false } }, { "-fullscreen", { "FullScreen", true } }, { "-borderless", { "BorderlessWindow", true } }, + { "-draw-white-border", { "DrawWhiteBorder", true } }, { "-no-wide-screen", { "WideScreen", false } }, { "-wide-screen", { "WideScreen", true } }, { "-stretch", { "Stretch", true } }, From 3bf71339291582720b54e807ddbd1cf73e08a3a5 Mon Sep 17 00:00:00 2001 From: joachim Date: Thu, 23 Mar 2023 13:19:23 +0100 Subject: [PATCH 09/11] refactor --- Src/OSD/SDL/Crosshair.cpp | 6 +- Src/OSD/SDL/Crosshair.h | 29 +------- Src/OSD/SDL/WhiteBorder.cpp | 136 +++++++++++++++++------------------- Src/OSD/SDL/WhiteBorder.h | 6 +- Src/OSD/SDL/basicDrawable.h | 2 +- 5 files changed, 74 insertions(+), 105 deletions(-) diff --git a/Src/OSD/SDL/Crosshair.cpp b/Src/OSD/SDL/Crosshair.cpp index d5354b57..94a9e1b7 100644 --- a/Src/OSD/SDL/Crosshair.cpp +++ b/Src/OSD/SDL/Crosshair.cpp @@ -331,10 +331,10 @@ void CCrosshair::GunToViewCoords(float* x, float* y) } CCrosshair::CCrosshair(const Util::Config::Node& config) - : m_config(config), - m_vertexShader(nullptr), - m_fragmentShader(nullptr) + : m_config(config) { + m_vertexShader = nullptr; + m_fragmentShader = nullptr; } CCrosshair::~CCrosshair() diff --git a/Src/OSD/SDL/Crosshair.h b/Src/OSD/SDL/Crosshair.h index 0bb1fd07..be799339 100644 --- a/Src/OSD/SDL/Crosshair.h +++ b/Src/OSD/SDL/Crosshair.h @@ -25,8 +25,9 @@ #include "Supermodel.h" #include "Graphics/New3D/New3D.h" #include "Inputs/Inputs.h" +#include "basicDrawable.h" -class CCrosshair +class CCrosshair : public CBasicDrawable { private: const Util::Config::Node& m_config; @@ -42,31 +43,7 @@ class CCrosshair float m_dpiMultiplicator = 0.0f; const float m_scaleBitmap = 0.1f; - struct BasicVertex - { - BasicVertex(float x, float y, float z) : x(x), y(y), z(z) {} - BasicVertex(float x, float y) : x(x), y(y), z(0.0f) {} - float x, y, z; - }; - - struct UVCoords - { - UVCoords(float x, float y) : x(x), y(y) {} - float x, y; - }; - - std::vector m_verts; - std::vector m_uvCoord; - - GLSLShader m_shader; - VBO m_vbo; - VBO m_textvbo; - GLuint m_vao = 0; - int m_textureCoordsCount = 0; - const char* m_vertexShader; - const char* m_fragmentShader; - - const int MaxVerts = 1024; // per draw call + void BuildCrosshairVertices(unsigned int xRes, unsigned int yRes); void DrawCrosshair(New3D::Mat4 matrix, float x, float y, int player, unsigned int xRes, unsigned int yRes); diff --git a/Src/OSD/SDL/WhiteBorder.cpp b/Src/OSD/SDL/WhiteBorder.cpp index c5192102..cc7cc088 100644 --- a/Src/OSD/SDL/WhiteBorder.cpp +++ b/Src/OSD/SDL/WhiteBorder.cpp @@ -48,7 +48,7 @@ bool CWhiteBorder::Init() } )glsl"; - m_fragmentShader = R"glsl( + m_fragmentShader = R"glsl( #version 410 core @@ -61,40 +61,37 @@ bool CWhiteBorder::Init() } )glsl"; - m_shader.LoadShaders(m_vertexShader, m_fragmentShader); - m_shader.GetUniformLocationMap("mvp"); - m_shader.GetUniformLocationMap("colour"); - m_vbo.Create(GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, sizeof(BasicVertex) * (MaxVerts)); - m_vbo.Bind(true); + m_shader.LoadShaders(m_vertexShader, m_fragmentShader); + m_shader.GetUniformLocationMap("mvp"); + m_shader.GetUniformLocationMap("colour"); + m_vbo.Create(GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, sizeof(BasicVertex) * (MaxVerts)); + m_vbo.Bind(true); -//FIXME: memory leak upon screen resolution change - glGenVertexArrays(1, &m_vao); - glBindVertexArray(m_vao); - - m_vbo.Bind(true); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(BasicVertex), 0); - m_vbo.Bind(false); + glGenVertexArrays(1, &m_vao); + glBindVertexArray(m_vao); - + m_vbo.Bind(true); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(BasicVertex), 0); + m_vbo.Bind(false); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - - glBindVertexArray(0); - m_drawWhiteBorder = true; + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + + glBindVertexArray(0); + m_drawWhiteBorder = true; } return OKAY; } -void CWhiteBorder::GenQuad(std::vector &verts, float x, float y, float w, float h) +void CWhiteBorder::GenQuad(std::vector &verts, unsigned int x, unsigned int y, unsigned int w, unsigned int h) { - verts.emplace_back(x, y); - verts.emplace_back(x, y + h); - verts.emplace_back(x + w, y + h); - verts.emplace_back(x, y); - verts.emplace_back(x + w, y + h); - verts.emplace_back(x + w, y); + verts.emplace_back(float(x), float(y)); + verts.emplace_back(float(x), float(y + h)); + verts.emplace_back(float(x + w), float(y + h)); + verts.emplace_back(float(x), float(y)); + verts.emplace_back(float(x + w), float(y + h)); + verts.emplace_back(float(x + w), float(y)); } @@ -103,53 +100,51 @@ void CWhiteBorder::Update(unsigned int xOffset, unsigned int yOffset, unsigned i //white border if (m_drawWhiteBorder) { - if(m_xRes != xRes || - m_yRes != yRes || - m_xOffset != xOffset || - m_yOffset != yOffset ) - { - - m_xRes = xRes ; - m_yRes = yRes ; - m_xOffset = xOffset; - m_yOffset = yOffset; + if(m_xRes != xRes || + m_yRes != yRes || + m_xOffset != xOffset || + m_yOffset != yOffset ) + { + m_xRes = xRes ; + m_yRes = yRes ; + m_xOffset = xOffset; + m_yOffset = yOffset; int whiteBorder = m_width; - int internalWhiteBorder = m_width / 2; - //generate border geometry - m_verts.clear(); - GenQuad(m_verts, m_xOffset - internalWhiteBorder, m_yOffset - internalWhiteBorder, internalWhiteBorder, m_yRes + internalWhiteBorder * 2); - GenQuad(m_verts, m_xOffset + m_xRes, m_yOffset - internalWhiteBorder, internalWhiteBorder, m_yRes + internalWhiteBorder * 2); - GenQuad(m_verts, m_xOffset, m_yOffset - internalWhiteBorder, m_xRes , internalWhiteBorder); - GenQuad(m_verts, m_xOffset, m_yOffset + m_yRes, m_xRes, internalWhiteBorder); - - // update vbo mem - m_vbo.Bind(true); - m_vbo.BufferSubData(0, m_verts.size() * sizeof(BasicVertex), m_verts.data()); - } - - - glUseProgram(0); - glViewport(0, 0, totalXRes, totalYRes); - glDisable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - glDisable(GL_SCISSOR_TEST); - - New3D::Mat4 mvpMatrix; - mvpMatrix.Ortho(0.0, totalXRes, totalYRes, 0.0, -1.0f, 1.0f); - - m_shader.EnableShader(); + int internalWhiteBorder = m_width / 2; + //generate border geometry + m_verts.clear(); + GenQuad(m_verts, m_xOffset - internalWhiteBorder, m_yOffset - internalWhiteBorder, internalWhiteBorder, m_yRes + internalWhiteBorder * 2); + GenQuad(m_verts, m_xOffset + m_xRes, m_yOffset - internalWhiteBorder, internalWhiteBorder, m_yRes + internalWhiteBorder * 2); + GenQuad(m_verts, m_xOffset, m_yOffset - internalWhiteBorder, m_xRes , internalWhiteBorder); + GenQuad(m_verts, m_xOffset, m_yOffset + m_yRes, m_xRes, internalWhiteBorder); + + // update vbo mem + m_vbo.Bind(true); + m_vbo.BufferSubData(0, m_verts.size() * sizeof(BasicVertex), m_verts.data()); + } + glUseProgram(0); + glViewport(0, 0, totalXRes, totalYRes); + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDisable(GL_SCISSOR_TEST); + + New3D::Mat4 mvpMatrix; + mvpMatrix.Ortho(0.0, float(totalXRes), float(totalYRes), 0.0, -1.0f, 1.0f); + + m_shader.EnableShader(); + // update uniform memory glUniformMatrix4fv(m_shader.uniformLocMap["mvp"], 1, GL_FALSE, mvpMatrix); glUniform4f(m_shader.uniformLocMap["colour"], 1.0f, 1.0f, 1.0f, 1.0f); - + glBindVertexArray(m_vao); - glDrawArrays(GL_TRIANGLES, 0, m_verts.size()); + glDrawArrays(GL_TRIANGLES, 0, GLsizei(m_verts.size())); glBindVertexArray(0); - + m_shader.DisableShader(); - - glEnable(GL_SCISSOR_TEST); + + glEnable(GL_SCISSOR_TEST); } } @@ -166,15 +161,14 @@ unsigned int CWhiteBorder::Width() CWhiteBorder::CWhiteBorder(const Util::Config::Node& config) : m_config(config) { + m_drawWhiteBorder = m_config["DrawWhiteBorder"].ValueAs(); m_vertexShader = nullptr; m_fragmentShader = nullptr; - m_drawWhiteBorder = m_config["DrawWhiteBorder"].ValueAs(); - m_xRes = 0; - m_yRes = 0; - m_xOffset = 0; - m_yOffset = 0; + m_xRes = 0; + m_yRes = 0; + m_xOffset = 0; + m_yOffset = 0; m_width = 50; - } CWhiteBorder::~CWhiteBorder() diff --git a/Src/OSD/SDL/WhiteBorder.h b/Src/OSD/SDL/WhiteBorder.h index 3bfff250..c1661215 100644 --- a/Src/OSD/SDL/WhiteBorder.h +++ b/Src/OSD/SDL/WhiteBorder.h @@ -1,7 +1,7 @@ /** ** Supermodel ** A Sega Model 3 Arcade Emulator. - ** Copyright 2003-2022 The Supermodel Team + ** Copyright 2003-2023 The Supermodel Team ** ** This file is part of Supermodel. ** @@ -39,12 +39,10 @@ class CWhiteBorder : public CBasicDrawable unsigned int m_yOffset; unsigned int m_width; - void GenQuad(std::vector& verts, float x, float y, float w, float h); + void GenQuad(std::vector& verts, unsigned int x, unsigned int y, unsigned int w, unsigned int h); public: CWhiteBorder(const Util::Config::Node& config); ~CWhiteBorder(); - - //called when resolution is changing... bool Init(); void Update(unsigned int xOffset, unsigned int yOffset, unsigned int xRes, unsigned int yRes, unsigned int totalXRes, unsigned int totalYRes); bool IsEnabled(); diff --git a/Src/OSD/SDL/basicDrawable.h b/Src/OSD/SDL/basicDrawable.h index 856daa09..cc6411d8 100644 --- a/Src/OSD/SDL/basicDrawable.h +++ b/Src/OSD/SDL/basicDrawable.h @@ -1,7 +1,7 @@ /** ** Supermodel ** A Sega Model 3 Arcade Emulator. - ** Copyright 2003-2022 The Supermodel Team + ** Copyright 2003-2023 The Supermodel Team ** ** This file is part of Supermodel. ** From 31dbef3c641d306f990f789557f68ad35f35f7c8 Mon Sep 17 00:00:00 2001 From: joachim Date: Thu, 23 Mar 2023 14:42:13 +0100 Subject: [PATCH 10/11] merge Error --- Src/OSD/SDL/Crosshair.cpp | 1 - Src/OSD/SDL/Crosshair.h | 30 ------------------------------ 2 files changed, 31 deletions(-) diff --git a/Src/OSD/SDL/Crosshair.cpp b/Src/OSD/SDL/Crosshair.cpp index 50fdd919..94a9e1b7 100644 --- a/Src/OSD/SDL/Crosshair.cpp +++ b/Src/OSD/SDL/Crosshair.cpp @@ -332,7 +332,6 @@ void CCrosshair::GunToViewCoords(float* x, float* y) CCrosshair::CCrosshair(const Util::Config::Node& config) : m_config(config) -{ { m_vertexShader = nullptr; m_fragmentShader = nullptr; diff --git a/Src/OSD/SDL/Crosshair.h b/Src/OSD/SDL/Crosshair.h index 06a7e11f..d487dd8d 100644 --- a/Src/OSD/SDL/Crosshair.h +++ b/Src/OSD/SDL/Crosshair.h @@ -43,36 +43,6 @@ class CCrosshair : public CBasicDrawable float m_dpiMultiplicator = 0.0f; const float m_scaleBitmap = 0.1f; -<<<<<<< HEAD - -======= - struct BasicVertex - { - BasicVertex(float x, float y, float z) : x(x), y(y), z(z) {} - BasicVertex(float x, float y) : x(x), y(y), z(0.0f) {} - float x, y, z; - }; - - struct UVCoords - { - UVCoords(float x, float y) : x(x), y(y) {} - float x, y; - }; - - std::vector m_verts; - std::vector m_uvCoord; - - GLSLShader m_shader; - VBO m_vbo; - VBO m_textvbo; - GLuint m_vao = 0; - int m_textureCoordsCount = 0; - const char* m_vertexShader; - const char* m_fragmentShader; - - const int MaxVerts = 1024; // per draw call ->>>>>>> 87de86f7d19aba5cb16c675a3b0ee8142da72bd7 - void BuildCrosshairVertices(unsigned int xRes, unsigned int yRes); void DrawCrosshair(New3D::Mat4 matrix, float x, float y, int player, unsigned int xRes, unsigned int yRes); void GunToViewCoords(float* x, float* y); From 490c492422677e7d3091b31a6e4cc4f48cefe8dd Mon Sep 17 00:00:00 2001 From: joachim Date: Thu, 23 Mar 2023 15:02:59 +0100 Subject: [PATCH 11/11] indentation issue --- Src/OSD/SDL/Main.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp index 99639dbc..38c9b066 100644 --- a/Src/OSD/SDL/Main.cpp +++ b/Src/OSD/SDL/Main.cpp @@ -829,8 +829,8 @@ void EndFrameVideo() if (videoInputs) s_crosshair->Update(currentInputs, videoInputs, xOffset, yOffset, xRes, yRes); - //Update whiteborders - s_whiteBorder->Update(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes); + //Update whiteborders + s_whiteBorder->Update(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes); // Swap the buffers SDL_GL_SwapWindow(s_window); } @@ -2092,9 +2092,9 @@ int main(int argc, char **argv) if (Outputs != NULL) delete Outputs; if (s_crosshair != NULL) - delete s_crosshair; - if (s_whiteBorder != NULL) - delete s_whiteBorder; + delete s_crosshair; + if (s_whiteBorder != NULL) + delete s_whiteBorder; DestroyGLScreen(); SDL_Quit();