diff --git a/decompiler/config/all-types.gc b/decompiler/config/all-types.gc index dd13277109..acefd3cd88 100644 --- a/decompiler/config/all-types.gc +++ b/decompiler/config/all-types.gc @@ -1481,6 +1481,8 @@ (subtitle-enabled #x1040) (subtitle-disabled #x1041) (text-language #x1042) + (display #x1043) + (display-fmt #x1044) (msaa #x1050) (x-times-fmt #x1051) (2-times #x1052) @@ -15736,6 +15738,7 @@ (flava-player) (memcard-disable-auto-save) (memcard-auto-save-disabled) + (monitor) ;; the last one! (max) @@ -15769,6 +15772,7 @@ (button-music) (button-flava) (cheat-toggle) + (monitor) ) (defenum game-option-menu diff --git a/game/assets/jak1/text/game_text_en.gs b/game/assets/jak1/text/game_text_en.gs index 6151e8e070..21379c33d4 100644 --- a/game/assets/jak1/text/game_text_en.gs +++ b/game/assets/jak1/text/game_text_en.gs @@ -104,6 +104,10 @@ "SUBTITLES DISABLED") (#x1042 "TEXT LANGUAGE" "TEXT LANGUAGE") +(#x1043 "DISPLAY" + "DISPLAY") +(#x1044 "DISPLAY ~D" + "DISPLAY ~D") (#x1050 "MSAA" "MSAA") @@ -334,7 +338,6 @@ (#x1116 "NORSK" "NORSK") - ;; ----------------- ;; test diff --git a/game/graphics/display.cpp b/game/graphics/display.cpp index 8c1a5c780f..dbbc4da5b4 100644 --- a/game/graphics/display.cpp +++ b/game/graphics/display.cpp @@ -61,12 +61,6 @@ int GfxDisplay::height() { return h; } -void GfxDisplay::backup_params() { - get_size(&m_width, &m_height); - get_position(&m_xpos, &m_ypos); - fmt::print("backed up window: {},{} {}x{}\n", m_xpos, m_ypos, m_width, m_height); -} - /* ******************************** * DISPLAY diff --git a/game/graphics/display.h b/game/graphics/display.h index 1e3091660f..ee54d0c91e 100644 --- a/game/graphics/display.h +++ b/game/graphics/display.h @@ -19,21 +19,23 @@ class GfxDisplay { const char* m_title; - // NOT actual size! just backups - int m_width; - int m_height; - // same here - int m_xpos; - int m_ypos; - - int m_fullscreen_screen; - int m_fullscreen_target_screen; + int m_fullscreen_screen = -1; + int m_fullscreen_target_screen = -1; bool m_imgui_visible; protected: bool m_main; + // next mode GfxDisplayMode m_fullscreen_target_mode = GfxDisplayMode::Windowed; - GfxDisplayMode m_last_fullscreen_mode; + // current mode (start as -1 to force an initial fullscreen update) + GfxDisplayMode m_fullscreen_mode = (GfxDisplayMode)-1; + // previous mode (last frame) + GfxDisplayMode m_last_fullscreen_mode = GfxDisplayMode::Windowed; + + int m_last_windowed_xpos = 0; + int m_last_windowed_ypos = 0; + int m_last_windowed_width = 640; + int m_last_windowed_height = 480; public: virtual ~GfxDisplay() {} @@ -45,37 +47,37 @@ class GfxDisplay { virtual int get_screen_vmode_count() = 0; virtual void get_screen_size(int vmode_idx, s32* w, s32* h) = 0; virtual int get_screen_rate(int vmode_idx) = 0; + virtual int get_monitor_count() = 0; virtual void get_position(int* x, int* y) = 0; virtual void get_size(int* w, int* h) = 0; - virtual GfxDisplayMode get_fullscreen() = 0; virtual void render() = 0; virtual void set_lock(bool lock) = 0; virtual bool minimized() = 0; + virtual bool fullscreen_pending() { + return fullscreen_mode() != m_fullscreen_target_mode || + m_fullscreen_screen != m_fullscreen_target_screen; + } + virtual void fullscreen_flush() { + update_fullscreen(m_fullscreen_target_mode, m_fullscreen_target_screen); + + m_fullscreen_mode = m_fullscreen_target_mode; + m_fullscreen_screen = m_fullscreen_target_screen; + } + bool is_active() const { return get_window() != nullptr; } void set_title(const char* title); const char* title() const { return m_title; } - - bool fullscreen_pending() { return get_fullscreen() != m_fullscreen_target_mode; } - void fullscreen_flush() { - update_fullscreen(m_fullscreen_target_mode, m_fullscreen_target_screen); - // TODO no - m_fullscreen_screen = m_fullscreen_target_screen; - } void set_fullscreen(GfxDisplayMode mode, int screen) { m_fullscreen_target_mode = mode; m_fullscreen_target_screen = screen; } - void update_last_fullscreen_mode() { m_last_fullscreen_mode = get_fullscreen(); } + void update_last_fullscreen_mode() { m_last_fullscreen_mode = fullscreen_mode(); } GfxDisplayMode last_fullscreen_mode() const { return m_last_fullscreen_mode; } + GfxDisplayMode fullscreen_mode() { return m_fullscreen_mode; } int fullscreen_screen() const { return m_fullscreen_screen; } void set_imgui_visible(bool visible) { m_imgui_visible = visible; } bool is_imgui_visible() const { return m_imgui_visible; } - bool windowed() { return get_fullscreen() == GfxDisplayMode::Windowed; } - void backup_params(); - int width_backup() const { return m_width; } - int height_backup() const { return m_height; } - int xpos_backup() const { return m_xpos; } - int ypos_backup() const { return m_ypos; } + bool windowed() { return fullscreen_mode() == GfxDisplayMode::Windowed; } int width(); int height(); diff --git a/game/graphics/gfx.cpp b/game/graphics/gfx.cpp index 294ec9e959..597c570e6b 100644 --- a/game/graphics/gfx.cpp +++ b/game/graphics/gfx.cpp @@ -223,7 +223,7 @@ void get_window_scale(float* x, float* y) { GfxDisplayMode get_fullscreen() { if (Display::GetMainDisplay()) { - return Display::GetMainDisplay()->get_fullscreen(); + return Display::GetMainDisplay()->fullscreen_mode(); } else { return GfxDisplayMode::Windowed; } @@ -243,6 +243,13 @@ int get_screen_rate(s64 vmode_idx) { return 0; } +int get_monitor_count() { + if (Display::GetMainDisplay()) { + return Display::GetMainDisplay()->get_monitor_count(); + } + return 0; +} + void get_screen_size(s64 vmode_idx, s32* w, s32* h) { if (Display::GetMainDisplay()) { Display::GetMainDisplay()->get_screen_size(vmode_idx, w, h); diff --git a/game/graphics/gfx.h b/game/graphics/gfx.h index 844987bbd4..aaf0945b01 100644 --- a/game/graphics/gfx.h +++ b/game/graphics/gfx.h @@ -138,6 +138,7 @@ void get_window_scale(float* x, float* y); GfxDisplayMode get_fullscreen(); int get_screen_vmode_count(); int get_screen_rate(s64 vmode_idx); +int get_monitor_count(); void get_screen_size(s64 vmode_idx, s32* w, s32* h); void set_frame_rate(int rate); void set_vsync(bool vsync); diff --git a/game/graphics/pipelines/opengl.cpp b/game/graphics/pipelines/opengl.cpp index 5889f3d122..3f65a50003 100644 --- a/game/graphics/pipelines/opengl.cpp +++ b/game/graphics/pipelines/opengl.cpp @@ -78,24 +78,37 @@ struct GraphicsData { std::unique_ptr g_gfx_data; -void SetDisplayCallbacks(GLFWwindow* d) { - glfwSetKeyCallback( - d, [](GLFWwindow* window, int key, int /*scancode*/, int action, int /*mods*/) { - if (action == GlfwKeyAction::Press) { - // lg::debug("KEY PRESS: key: {} scancode: {} mods: {:X}", key, scancode, mods); - Pad::OnKeyPress(key); - } else if (action == GlfwKeyAction::Release) { - // lg::debug("KEY RELEASE: key: {} scancode: {} mods: {:X}", key, scancode, mods); - Pad::OnKeyRelease(key); - GLDisplay* display = reinterpret_cast(glfwGetWindowUserPointer(window)); - if (display != NULL) { // toggle ImGui when pressing Alt - if ((key == GLFW_KEY_LEFT_ALT || key == GLFW_KEY_RIGHT_ALT) && - glfwGetWindowAttrib(window, GLFW_FOCUSED)) { - display->set_imgui_visible(!display->is_imgui_visible()); - } - } - } - }); +struct { + bool callbacks_registered = false; + GLFWmonitor** monitors; + int monitor_count; +} g_glfw_state; + +void SetGlobalGLFWCallbacks() { + if (g_glfw_state.callbacks_registered) { + lg::warn("Global GLFW callbacks were already registered!"); + } + + // Get initial state + g_glfw_state.monitors = glfwGetMonitors(&g_glfw_state.monitor_count); + + // Listen for events + glfwSetMonitorCallback([](GLFWmonitor* /*monitor*/, int /*event*/) { + // Reload monitor list + g_glfw_state.monitors = glfwGetMonitors(&g_glfw_state.monitor_count); + }); + + g_glfw_state.callbacks_registered = true; +} + +void ClearGlobalGLFWCallbacks() { + if (!g_glfw_state.callbacks_registered) { + return; + } + + glfwSetMonitorCallback(NULL); + + g_glfw_state.callbacks_registered = false; } void ErrorCallback(int err, const char* msg) { @@ -141,6 +154,7 @@ static int gl_init(GfxSettings& settings) { } static void gl_exit() { + ClearGlobalGLFWCallbacks(); g_gfx_data.reset(); glfwTerminate(); glfwSetErrorCallback(NULL); @@ -186,7 +200,7 @@ static std::shared_ptr gl_make_display(int width, lg::error("Could not load icon for OpenGL window"); } - SetDisplayCallbacks(window); + SetGlobalGLFWCallbacks(); Pad::initialize(); if (HasError()) { @@ -228,13 +242,43 @@ static std::shared_ptr gl_make_display(int width, GLDisplay::GLDisplay(GLFWwindow* window, bool is_main) : m_window(window) { m_main = is_main; + + // Get initial state + get_position(&m_last_windowed_xpos, &m_last_windowed_ypos); + get_size(&m_last_windowed_width, &m_last_windowed_height); + + // Listen for window-specific GLFW events glfwSetWindowUserPointer(window, reinterpret_cast(this)); + + glfwSetKeyCallback(window, [](GLFWwindow* window, int key, int scancode, int action, int mods) { + GLDisplay* display = reinterpret_cast(glfwGetWindowUserPointer(window)); + display->on_key(window, key, scancode, action, mods); + }); + + glfwSetWindowPosCallback(window, [](GLFWwindow* window, int xpos, int ypos) { + GLDisplay* display = reinterpret_cast(glfwGetWindowUserPointer(window)); + display->on_window_pos(window, xpos, ypos); + }); + + glfwSetWindowSizeCallback(window, [](GLFWwindow* window, int width, int height) { + GLDisplay* display = reinterpret_cast(glfwGetWindowUserPointer(window)); + display->on_window_size(window, width, height); + }); + + glfwSetWindowIconifyCallback(window, [](GLFWwindow* window, int iconified) { + GLDisplay* display = reinterpret_cast(glfwGetWindowUserPointer(window)); + display->on_iconify(window, iconified); + }); } GLDisplay::~GLDisplay() { ImGuiIO& io = ImGui::GetIO(); io.IniFilename = nullptr; io.LogFilename = nullptr; + glfwSetKeyCallback(m_window, NULL); + glfwSetWindowPosCallback(m_window, NULL); + glfwSetWindowSizeCallback(m_window, NULL); + glfwSetWindowIconifyCallback(m_window, NULL); glfwSetWindowUserPointer(m_window, nullptr); ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); @@ -245,6 +289,38 @@ GLDisplay::~GLDisplay() { } } +void GLDisplay::on_key(GLFWwindow* window, int key, int /*scancode*/, int action, int /*mods*/) { + if (action == GlfwKeyAction::Press) { + // lg::debug("KEY PRESS: key: {} scancode: {} mods: {:X}", key, scancode, mods); + Pad::OnKeyPress(key); + } else if (action == GlfwKeyAction::Release) { + // lg::debug("KEY RELEASE: key: {} scancode: {} mods: {:X}", key, scancode, mods); + Pad::OnKeyRelease(key); + if ((key == GLFW_KEY_LEFT_ALT || key == GLFW_KEY_RIGHT_ALT) && + glfwGetWindowAttrib(window, GLFW_FOCUSED)) { + set_imgui_visible(!is_imgui_visible()); + } + } +} + +void GLDisplay::on_window_pos(GLFWwindow* /*window*/, int xpos, int ypos) { + if (m_fullscreen_target_mode == GfxDisplayMode::Windowed) { + m_last_windowed_xpos = xpos; + m_last_windowed_ypos = ypos; + } +} + +void GLDisplay::on_window_size(GLFWwindow* /*window*/, int width, int height) { + if (m_fullscreen_target_mode == GfxDisplayMode::Windowed) { + m_last_windowed_width = width; + m_last_windowed_height = height; + } +} + +void GLDisplay::on_iconify(GLFWwindow* window, int iconified) { + m_minimized = iconified == GLFW_TRUE; +} + namespace { std::string make_output_file_name(const std::string& file_name) { file_util::create_dir_if_needed(file_util::get_file_path({"gfx_dumps"})); @@ -353,25 +429,52 @@ void GLDisplay::get_scale(float* xs, float* ys) { void GLDisplay::set_size(int width, int height) { glfwSetWindowSize(m_window, width, height); + + if (windowed()) { + m_last_windowed_width = width; + m_last_windowed_height = height; + } } -void GLDisplay::update_fullscreen(GfxDisplayMode mode, int /*screen*/) { - GLFWmonitor* monitor = glfwGetPrimaryMonitor(); // todo +void GLDisplay::update_fullscreen(GfxDisplayMode mode, int screen) { + GLFWmonitor* monitor = get_monitor(screen); + switch (mode) { case GfxDisplayMode::Windowed: { // windowed + int x, y, width, height; + + if (m_last_fullscreen_mode == GfxDisplayMode::Windowed) { + // windowed -> windowed, keep position and size + width = m_last_windowed_width; + height = m_last_windowed_height; + x = m_last_windowed_xpos; + y = m_last_windowed_ypos; + } else { + // fullscreen -> windowed, use last windowed size but on the monitor previously fullscreened + int monitorX, monitorY, monitorWidth, monitorHeight; + glfwGetMonitorWorkarea(monitor, &monitorX, &monitorY, &monitorWidth, &monitorHeight); + + width = m_last_windowed_width; + height = m_last_windowed_height; + x = monitorX + (monitorWidth / 2) - (width / 2); + y = monitorY + (monitorHeight / 2) - (height / 2); + } + glfwSetWindowAttrib(m_window, GLFW_DECORATED, GLFW_TRUE); glfwSetWindowFocusCallback(m_window, NULL); glfwSetWindowAttrib(m_window, GLFW_FLOATING, GLFW_FALSE); - glfwSetWindowMonitor(m_window, NULL, xpos_backup(), ypos_backup(), width_backup(), - height_backup(), GLFW_DONT_CARE); + glfwSetWindowMonitor(m_window, NULL, x, y, width, height, GLFW_DONT_CARE); set_imgui_visible(true); + + // these might have changed + m_last_windowed_width = width; + m_last_windowed_height = height; + m_last_windowed_xpos = x; + m_last_windowed_ypos = y; } break; case GfxDisplayMode::Fullscreen: { // fullscreen - if (windowed()) { - backup_params(); - } const GLFWvidmode* vmode = glfwGetVideoMode(monitor); glfwSetWindowAttrib(m_window, GLFW_DECORATED, GLFW_FALSE); glfwSetWindowFocusCallback(m_window, NULL); @@ -381,9 +484,6 @@ void GLDisplay::update_fullscreen(GfxDisplayMode mode, int /*screen*/) { } break; case GfxDisplayMode::Borderless: { // borderless fullscreen - if (windowed()) { - backup_params(); - } int x, y; glfwGetMonitorPos(monitor, &x, &y); const GLFWvidmode* vmode = glfwGetVideoMode(monitor); @@ -400,31 +500,20 @@ void GLDisplay::update_fullscreen(GfxDisplayMode mode, int /*screen*/) { } } -GfxDisplayMode GLDisplay::get_fullscreen() { - GLFWmonitor* monitor = glfwGetPrimaryMonitor(); // todo - const GLFWvidmode* vmode = glfwGetVideoMode(monitor); - if (glfwGetWindowMonitor(m_window) != NULL) { - return GfxDisplayMode::Fullscreen; - } else if (width() >= vmode->width && height() >= vmode->height) { - return GfxDisplayMode::Borderless; - } else { - return GfxDisplayMode::Windowed; - } -} - int GLDisplay::get_screen_vmode_count() { int count = 0; - glfwGetVideoModes(glfwGetPrimaryMonitor(), &count); + glfwGetVideoModes(get_monitor(fullscreen_screen()), &count); return count; } void GLDisplay::get_screen_size(int vmode_idx, s32* w_out, s32* h_out) { - auto vmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + GLFWmonitor* monitor = get_monitor(fullscreen_screen()); + auto vmode = glfwGetVideoMode(monitor); int count = 0; - auto vmodes = glfwGetVideoModes(glfwGetPrimaryMonitor(), &count); + auto vmodes = glfwGetVideoModes(monitor, &count); if (vmode_idx >= 0) { vmode = &vmodes[vmode_idx]; - } else if (get_fullscreen() == GfxDisplayMode::Fullscreen) { + } else if (fullscreen_mode() == GfxDisplayMode::Fullscreen) { for (int i = 0; i < count; ++i) { if (!vmode || vmode->height < vmodes[i].height) { vmode = &vmodes[i]; @@ -440,12 +529,13 @@ void GLDisplay::get_screen_size(int vmode_idx, s32* w_out, s32* h_out) { } int GLDisplay::get_screen_rate(int vmode_idx) { - auto vmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + GLFWmonitor* monitor = get_monitor(fullscreen_screen()); + auto vmode = glfwGetVideoMode(monitor); int count = 0; - auto vmodes = glfwGetVideoModes(glfwGetPrimaryMonitor(), &count); + auto vmodes = glfwGetVideoModes(monitor, &count); if (vmode_idx >= 0) { vmode = &vmodes[vmode_idx]; - } else if (get_fullscreen() == GfxDisplayMode::Fullscreen) { + } else if (fullscreen_mode() == GfxDisplayMode::Fullscreen) { for (int i = 0; i < count; ++i) { if (!vmode || vmode->refreshRate < vmodes[i].refreshRate) { vmode = &vmodes[i]; @@ -455,14 +545,45 @@ int GLDisplay::get_screen_rate(int vmode_idx) { return vmode->refreshRate; } +GLFWmonitor* GLDisplay::get_monitor(int index) { + if (index < 0 || index >= g_glfw_state.monitor_count) { + // out of bounds, default to primary monitor + index = 0; + } + + return g_glfw_state.monitors[index]; +} + +int GLDisplay::get_monitor_count() { + return g_glfw_state.monitor_count; +} + bool GLDisplay::minimized() { - return glfwGetWindowAttrib(m_window, GLFW_ICONIFIED); + return m_minimized; } void GLDisplay::set_lock(bool lock) { glfwSetWindowAttrib(m_window, GLFW_RESIZABLE, lock ? GLFW_TRUE : GLFW_FALSE); } +bool GLDisplay::fullscreen_pending() { + GLFWmonitor* monitor = get_monitor(fullscreen_screen()); + auto vmode = glfwGetVideoMode(monitor); + + return GfxDisplay::fullscreen_pending() || + (vmode->width != m_last_video_mode.width || vmode->height != m_last_video_mode.height || + vmode->refreshRate != m_last_video_mode.refreshRate); +} + +void GLDisplay::fullscreen_flush() { + GfxDisplay::fullscreen_flush(); + + GLFWmonitor* monitor = get_monitor(fullscreen_screen()); + auto vmode = glfwGetVideoMode(monitor); + + m_last_video_mode = *vmode; +} + void update_global_profiler() { if (g_gfx_data->debug_gui.dump_events) { prof().set_enable(false); @@ -567,15 +688,14 @@ void GLDisplay::render() { g_gfx_data->sync_cv.notify_all(); } + // update fullscreen mode, if requested { auto p = scoped_prof("fullscreen-update"); - // slow, takes ~0.15 ms on linux - auto current_fullscreen_mode = get_fullscreen(); - // checking minimized also takes ~0.1 ms, only check if we need to update fullscreen modes - if (current_fullscreen_mode != m_fullscreen_target_mode && !minimized()) { + update_last_fullscreen_mode(); + + if (fullscreen_pending() && !minimized()) { fullscreen_flush(); } - m_last_fullscreen_mode = current_fullscreen_mode; } // reboot whole game, if requested diff --git a/game/graphics/pipelines/opengl.h b/game/graphics/pipelines/opengl.h index 0f13215c67..4e9e798c3c 100644 --- a/game/graphics/pipelines/opengl.h +++ b/game/graphics/pipelines/opengl.h @@ -19,8 +19,6 @@ enum GlfwKeyAction { }; class GLDisplay : public GfxDisplay { - GLFWwindow* m_window; - public: GLDisplay(GLFWwindow* window, bool is_main); virtual ~GLDisplay(); @@ -32,12 +30,25 @@ class GLDisplay : public GfxDisplay { void get_screen_size(int vmode_idx, s32* w, s32* h); int get_screen_rate(int vmode_idx); int get_screen_vmode_count(); - GfxDisplayMode get_fullscreen(); + int get_monitor_count(); void set_size(int w, int h); void update_fullscreen(GfxDisplayMode mode, int screen); void render(); bool minimized(); + bool fullscreen_pending() override; + void fullscreen_flush() override; void set_lock(bool lock); + void on_key(GLFWwindow* window, int key, int scancode, int action, int mods); + void on_window_pos(GLFWwindow* window, int xpos, int ypos); + void on_window_size(GLFWwindow* window, int width, int height); + void on_iconify(GLFWwindow* window, int iconified); + + private: + GLFWwindow* m_window; + bool m_minimized = false; + GLFWvidmode m_last_video_mode = {0, 0, 0, 0, 0, 0}; + + GLFWmonitor* get_monitor(int index); }; extern const GfxRendererModule gRendererOpenGL; diff --git a/game/kernel/common/kmachine.cpp b/game/kernel/common/kmachine.cpp index f2cbdc0e14..83a8da9a83 100644 --- a/game/kernel/common/kmachine.cpp +++ b/game/kernel/common/kmachine.cpp @@ -404,6 +404,13 @@ s64 get_screen_vmode_count() { return Gfx::get_screen_vmode_count(); } +/*! + * Returns the number of available monitors. + */ +int get_monitor_count() { + return Gfx::get_monitor_count(); +} + void mkdir_path(u32 filepath) { auto filepath_str = std::string(Ptr(filepath).c()->data()); file_util::create_dir_if_needed_for_file(filepath_str); diff --git a/game/kernel/common/kmachine.h b/game/kernel/common/kmachine.h index 61e1290616..1eb1229f70 100644 --- a/game/kernel/common/kmachine.h +++ b/game/kernel/common/kmachine.h @@ -63,6 +63,7 @@ void get_window_scale(u32 x_ptr, u32 y_ptr); void get_screen_size(s64 vmode_idx, u32 w_ptr, u32 h_ptr); s64 get_screen_rate(s64 vmode_idx); s64 get_screen_vmode_count(); +int get_monitor_count(); void mkdir_path(u32 filepath); u64 filepath_exists(u32 filepath); void prof_event(u32 name, u32 kind); @@ -74,4 +75,4 @@ void set_collision_wireframe(u32 symptr); void set_collision_mask(GfxGlobalSettings::CollisionRendererMode mode, int mask, u32 symptr); u32 get_collision_mask(GfxGlobalSettings::CollisionRendererMode mode, int mask); u32 offset_of_s7(); -void vif_interrupt_callback(); \ No newline at end of file +void vif_interrupt_callback(); diff --git a/game/kernel/jak1/kmachine.cpp b/game/kernel/jak1/kmachine.cpp index eb58a3872b..019733fceb 100644 --- a/game/kernel/jak1/kmachine.cpp +++ b/game/kernel/jak1/kmachine.cpp @@ -622,6 +622,7 @@ void InitMachine_PCPort() { make_function_symbol_from_c("pc-get-screen-size", (void*)get_screen_size); make_function_symbol_from_c("pc-get-screen-rate", (void*)get_screen_rate); make_function_symbol_from_c("pc-get-screen-vmode-count", (void*)get_screen_vmode_count); + make_function_symbol_from_c("pc-get-monitor-count", (void*)get_monitor_count); make_function_symbol_from_c("pc-set-window-size", (void*)Gfx::set_window_size); make_function_symbol_from_c("pc-set-fullscreen", (void*)set_fullscreen); make_function_symbol_from_c("pc-set-frame-rate", (void*)set_frame_rate); diff --git a/goal_src/jak1/engine/ui/progress/progress-h.gc b/goal_src/jak1/engine/ui/progress/progress-h.gc index b0756a68f5..bed356092e 100644 --- a/goal_src/jak1/engine/ui/progress/progress-h.gc +++ b/goal_src/jak1/engine/ui/progress/progress-h.gc @@ -122,6 +122,7 @@ (flava-player) (memcard-disable-auto-save) (memcard-auto-save-disabled) + (monitor) ;; the last one! (max) @@ -155,6 +156,7 @@ (button-music) (button-flava) (cheat-toggle) + (monitor) ) ) ) diff --git a/goal_src/jak1/engine/ui/progress/progress.gc b/goal_src/jak1/engine/ui/progress/progress.gc index 7f61819b01..b7e436b7de 100644 --- a/goal_src/jak1/engine/ui/progress/progress.gc +++ b/goal_src/jak1/engine/ui/progress/progress.gc @@ -842,6 +842,7 @@ (= v1-2 (progress-screen cheats)) (= v1-2 (progress-screen music-player)) (= v1-2 (progress-screen flava-player)) + (= v1-2 (progress-screen monitor)) ) ) ) diff --git a/goal_src/jak1/engine/ui/text-h.gc b/goal_src/jak1/engine/ui/text-h.gc index e36e99277c..2a12ef73b1 100644 --- a/goal_src/jak1/engine/ui/text-h.gc +++ b/goal_src/jak1/engine/ui/text-h.gc @@ -631,6 +631,8 @@ (subtitle-enabled #x1040) (subtitle-disabled #x1041) (text-language #x1042) + (display #x1043) + (display-fmt #x1044) (msaa #x1050) (x-times-fmt #x1051) (2-times #x1052) diff --git a/goal_src/jak1/kernel-defs.gc b/goal_src/jak1/kernel-defs.gc index 92b5f5eaf0..d45a355ff5 100644 --- a/goal_src/jak1/kernel-defs.gc +++ b/goal_src/jak1/kernel-defs.gc @@ -337,6 +337,7 @@ (define-extern pc-get-screen-size (function int (pointer int32) (pointer int32) none)) (define-extern pc-get-screen-rate (function int int)) (define-extern pc-get-screen-vmode-count (function int)) +(define-extern pc-get-monitor-count (function int)) (define-extern pc-get-os (function symbol)) (define-extern pc-get-window-size (function (pointer int32) (pointer int32) none)) (define-extern pc-get-window-scale (function (pointer float) (pointer float) none)) diff --git a/goal_src/jak1/pc/pckernel-h.gc b/goal_src/jak1/pc/pckernel-h.gc index 1ca271ef7a..f976eca28f 100644 --- a/goal_src/jak1/pc/pckernel-h.gc +++ b/goal_src/jak1/pc/pckernel-h.gc @@ -219,6 +219,7 @@ (vsync? symbol) ;; vsync. (font-scale float) ;; font scaling. (window-lock? symbol) ;; whether the window can be resized by the user or not. + (monitor int32) ;; index of which monitor to use when fullscreen or borderless ;; debug settings (os symbol) ;; windows, linux, macos @@ -330,6 +331,7 @@ (set-aspect-ratio! (_type_ float) none) (set-window-lock! (_type_ symbol) symbol) (set-frame-rate! (_type_ int) int) + (set-monitor! (_type_ int) none) (read-from-file (_type_ string) symbol) (write-to-file (_type_ string) symbol) (update-cheats (_type_) int) diff --git a/goal_src/jak1/pc/pckernel.gc b/goal_src/jak1/pc/pckernel.gc index f2f888156d..30159918a8 100644 --- a/goal_src/jak1/pc/pckernel.gc +++ b/goal_src/jak1/pc/pckernel.gc @@ -122,6 +122,11 @@ rate) +(defmethod set-monitor! pc-settings ((obj pc-settings) (monitor int)) + "set the monitor to use when in fullscreen/borderless" + (set! (-> obj monitor) monitor) + (none)) + (defmethod commit-to-file pc-settings ((obj pc-settings)) "commits the current settings to the file" ;; auto load settings if available @@ -187,8 +192,16 @@ ) ) + ;; if monitor selection is out of bounds (e.g. if a monitor got disconnected), + ;; then default to the primary monitor + (when (>= (-> obj monitor) (pc-get-monitor-count)) + (format 0 "Monitor selection out of bounds, defaulting to primary monitor.~%") + (set! (-> obj monitor) 0) + ) + ;; set fullscreen to what we want - (pc-set-fullscreen (-> obj display-mode) 0) + (pc-set-fullscreen (-> obj display-mode) (-> obj monitor)) + ;; set window size and fps automatically if the window lock is enabled. (when (-> obj window-lock?) (pc-set-vsync (-> obj vsync?)) @@ -786,6 +799,7 @@ ) ) (("display-mode") (set-display-mode! obj (file-stream-read-symbol file))) + (("monitor") (set-monitor! obj (file-stream-read-int file))) (("letterbox") (set! (-> obj letterbox?) (file-stream-read-symbol file))) (("vsync") (set! (-> obj vsync?) (file-stream-read-symbol file))) (("font-scale") (set! (-> obj font-scale) (file-stream-read-float file))) @@ -903,6 +917,7 @@ (-> obj aspect-custom-x) (-> obj aspect-custom-y) (-> obj aspect-ratio-auto?)) (format file " (display-mode ~A)~%" (-> obj display-mode)) + (format file " (monitor ~D)~%" (-> obj monitor)) (format file " (letterbox ~A)~%" (-> obj letterbox?)) (format file " (vsync ~A)~%" (-> obj vsync?)) (format file " (font-scale ~f)~%" (-> obj font-scale)) diff --git a/goal_src/jak1/pc/progress-pc.gc b/goal_src/jak1/pc/progress-pc.gc index 8031eba102..fa32e33736 100644 --- a/goal_src/jak1/pc/progress-pc.gc +++ b/goal_src/jak1/pc/progress-pc.gc @@ -138,6 +138,7 @@ (define *graphic-options-pc* (new 'static 'boxed-array :type game-option (new 'static 'game-option :option-type (game-option-type menu) :name (game-text-id game-resolution) :scale #t :param3 (game-option-menu resolution)) (new 'static 'game-option :option-type (game-option-type display-mode) :name (game-text-id display-mode) :scale #t) + (new 'static 'game-option :option-type (game-option-type menu) :name (game-text-id display) :scale #t :param3 (game-option-menu monitor)) (new 'static 'game-option :option-type (game-option-type on-off) :name (game-text-id vsync) :scale #t) (new 'static 'game-option :option-type (game-option-type menu) :name (game-text-id aspect-ratio) :scale #t :param3 (game-option-menu aspect-ratio)) (new 'static 'game-option :option-type (game-option-type msaa) :name (game-text-id msaa) :scale #t) @@ -502,6 +503,22 @@ *temp-options* ) +(defun build-monitor-options () + (set! (-> *temp-options* length) 0) + + (dotimes (i (pc-get-monitor-count)) + (let ((option (-> *temp-options* (length *temp-options*)))) + (set! (-> option option-type) (game-option-type monitor)) + (set! (-> option name) (game-text-id display-fmt)) + (set! (-> option param1) (the float i)) + (set! (-> option scale) #t) + + (1+! (-> *temp-options* length)) + ) + ) + + (add-back-option) + ) @@ -681,6 +698,7 @@ (set! (-> *options-remap* (progress-screen flava-player)) *temp-options*) (set! (-> *options-remap* (progress-screen memcard-disable-auto-save)) *yes-no-options*) (set! (-> *options-remap* (progress-screen memcard-auto-save-disabled)) *ok-options*) + (set! (-> *options-remap* (progress-screen monitor)) *temp-options*) ;; set default params (set! (-> *progress-state* aspect-ratio-choice) (get-aspect-ratio)) @@ -710,9 +728,10 @@ (set! (-> *game-options-pc* 6 value-to-modify) (&-> *progress-carousell* int-backup)) (set! (-> *game-options-pc* 7 value-to-modify) (&-> *progress-carousell* int-backup)) (set! (-> *graphic-options-pc* 1 value-to-modify) (&-> *progress-carousell* int-backup)) - (set! (-> *graphic-options-pc* 2 value-to-modify) (&-> *pc-settings* vsync?)) - (set! (-> *graphic-options-pc* 4 value-to-modify) (&-> *progress-carousell* int-backup)) + (set! (-> *graphic-options-pc* 2 value-to-modify) (&-> *pc-settings* monitor)) + (set! (-> *graphic-options-pc* 3 value-to-modify) (&-> *pc-settings* vsync?)) (set! (-> *graphic-options-pc* 5 value-to-modify) (&-> *progress-carousell* int-backup)) + (set! (-> *graphic-options-pc* 6 value-to-modify) (&-> *progress-carousell* int-backup)) (set! (-> *graphic-options-no-frame-rate-pc* 1 value-to-modify) (&-> *progress-carousell* int-backup)) (set! (-> *graphic-options-no-frame-rate-pc* 2 value-to-modify) (&-> *pc-settings* vsync?)) (set! (-> *graphic-options-no-frame-rate-pc* 4 value-to-modify) (&-> *progress-carousell* int-backup)) @@ -772,6 +791,9 @@ (set! (-> *options-remap* (-> obj display-state)) *secrets-title*) (set! (-> *options-remap* (-> obj display-state)) *secrets*)) ) + (((progress-screen monitor)) + (build-monitor-options) + ) ) ;; run nav code (let ((options (-> *options-remap* (-> obj display-state)))) @@ -1174,6 +1196,17 @@ (set! (-> obj next-display-state) (progress-screen invalid)) (commit-to-file *pc-settings*) ) + ((= (-> options (-> obj option-index) option-type) (game-option-type monitor)) + ;; monitor button + (let ((monitor (the int (-> options (-> obj option-index) param1)))) + (set-monitor! *pc-settings* monitor) + ) + (cpad-clear! 0 x) + (cpad-clear! 0 circle) + (sound-play "cursor-options") + (set! (-> obj next-display-state) (progress-screen invalid)) + (commit-to-file *pc-settings*) + ) ((= (-> options (-> obj option-index) option-type) (game-option-type aspect-new)) ;; aspect ratio button. (let ((newx (the int (-> options (-> obj option-index) param1))) @@ -1725,6 +1758,14 @@ (set! option-str (string-format (lookup-text! *common-text* (-> options index name) #f) (the int (-> options index param1)) (the int (-> options index param2)))) ) + (((game-option-type monitor)) + ;; monitor list + (set! option-str + (string-format (lookup-text! *common-text* (-> options index name) #f) + (+ 1 (the int (-> options index param1))) + ) + ) + ) (else (cond ((and (-> obj selected-option) (= (-> obj option-index) index)) @@ -2143,6 +2184,7 @@ (progress-screen aspect-ratio) (progress-screen secrets) (progress-screen cheats) + (progress-screen monitor) ) (hide-progress-icons) (draw-options self 115 25 0.82) diff --git a/third-party/imgui/imgui_impl_glfw.cpp b/third-party/imgui/imgui_impl_glfw.cpp index 9cfe8a9330..0f5e145749 100644 --- a/third-party/imgui/imgui_impl_glfw.cpp +++ b/third-party/imgui/imgui_impl_glfw.cpp @@ -197,8 +197,12 @@ void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) io.AddInputCharacter(c); } -void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int) +void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event) { + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (bd->PrevUserCallbackMonitor != NULL) + bd->PrevUserCallbackMonitor(monitor, event); + // Unused in 'master' branch but 'docking' branch will use this, so we declare it ahead of it so if you have to install callbacks you can install this one too. }