Files
jak-project/game/system/hid/display_manager.h
T
Tyler Wilding 2d64108af5 display: be more generous when filtering resolutions by refresh rate (#3974)
Some monitors are very specific about their reported refresh rates:
```
[54:37] [debug] [DISPLAY]: Skipping 640x480 as it requires 360.11hz but the monitor is currently set to 360hz
```

The current code truncated the display mode, but would compare against
the reported refresh rate. As per the log, they are obviously not equal.
- Retain the float component and only round it off when sending the
value to GOAL
- Make the comparison more generous, +/- 1.0hz
2025-07-02 22:57:57 -04:00

145 lines
4.9 KiB
C++

#pragma once
#include <mutex>
#include <optional>
#include <queue>
#include <string>
#include <unordered_map>
#include <variant>
#include "game/settings/settings.h"
#include "game/system/hid/input_manager.h"
#include "third-party/SDL/include/SDL3/SDL.h"
/*
TODO:
- hiDPI support, see https://wiki.libsdl.org/SDL2/SDL_GetRendererOutputSize
*/
enum class WindowState { Minimized, Maximized, Restored };
enum class Orientation { Landscape, LandscapeFlipped, Portrait, PortraitFlipped, Unknown };
/// https://wiki.libsdl.org/SDL3/SDL_DisplayMode
struct DisplayMode {
SDL_DisplayID sdl_display_id;
std::string display_name;
/// https://wiki.libsdl.org/SDL3/SDL_PixelFormat
uint32_t sdl_pixel_format;
int screen_width;
int screen_height;
/// refresh rate (in Hz), or 0 for unspecified
float refresh_rate;
Orientation orientation;
};
// Describes an available resolution
struct Resolution {
int width;
int height;
float aspect_ratio;
};
/// Monitors and handles all SDL events related to monitors and the window position
/// Stores related info for other parts of the application to use
/// Manages display related operations and querying
class DisplayManager {
private:
enum class EEDisplayEventType { SET_WINDOW_SIZE, SET_DISPLAY_MODE, SET_DISPLAY_ID };
game_settings::DisplaySettings::DisplayMode m_previous_fullscreen_display_mode =
game_settings::DisplaySettings::DisplayMode::Fullscreen;
struct EEDisplayEvent {
EEDisplayEventType type;
std::variant<bool, int, game_settings::DisplaySettings::DisplayMode> param1;
std::variant<bool, int> param2;
std::variant<int> param3;
};
public:
DisplayManager(SDL_Window* window);
~DisplayManager();
/// Propagate and handle the SDL event, ignoring it if it's not relevant
void process_sdl_event(const SDL_Event& event);
/// Any event coming from the EE thread that interacts directly with SDL should be enqueued as an
/// event so it can be ran from the proper thread context (the graphics thread)
void process_ee_events();
int get_active_display_index() { return m_display_settings.display_id; }
bool is_window_active() { return m_window != nullptr; }
bool is_minimized() { return m_window_state == WindowState::Minimized; }
int get_window_width() { return m_window_width; }
int get_window_height() { return m_window_height; }
int get_window_game_width() { return m_window_game_width; }
int get_window_game_height() { return m_window_game_height; }
float get_window_scale_x() { return m_window_scale_x; }
float get_window_scale_y() { return m_window_scale_y; }
int num_connected_displays() { return m_current_display_modes.size(); }
std::string get_connected_display_name(int id);
int get_active_display_refresh_rate();
int get_screen_width();
int get_screen_height();
game_settings::DisplaySettings::DisplayMode get_display_mode() {
return m_display_settings.display_mode;
}
int get_num_resolutions(bool for_window_size);
Resolution get_resolution(int id, bool for_window_size);
bool is_supported_resolution(int width, int height);
// Mutators
void enqueue_set_window_size(int width, int height);
void enqueue_set_window_display_mode(game_settings::DisplaySettings::DisplayMode mode,
const int window_width,
const int window_height);
void toggle_display_mode();
void enqueue_set_display_id(int display_id);
void set_game_size(int width, int height) {
m_window_game_width = width;
m_window_game_height = height;
}
void set_input_manager(std::shared_ptr<InputManager> input_manager) {
m_input_manager = input_manager;
}
private:
SDL_Window* m_window;
game_settings::DisplaySettings m_display_settings;
std::optional<std::shared_ptr<InputManager>> m_input_manager;
std::mutex event_queue_mtx;
std::queue<EEDisplayEvent> ee_event_queue;
int m_window_xpos = 0;
int m_window_ypos = 0;
int m_window_width = 0;
int m_window_height = 0;
int m_window_game_width = 0;
int m_window_game_height = 0;
float m_window_scale_x = 1.0;
float m_window_scale_y = 1.0;
WindowState m_window_state = WindowState::Restored;
// The currently set display mode for each display
// There is no reason to keep track of all display modes for all monitors
// the only one that matters is the one the user _currently_ has configured
//
// ie. allowing someone to set 150fps on a monitor set to 60hz is not correct
std::vector<DisplayMode> m_current_display_modes;
std::vector<Resolution> m_available_resolutions;
std::vector<Resolution> m_available_window_sizes;
void initialize_window_position_from_settings();
void update_curr_display_info();
void update_video_modes();
void update_resolutions();
void set_window_size(int width, int height);
void set_display_mode(game_settings::DisplaySettings::DisplayMode mode,
const int window_width,
const int window_height);
void set_display_id(int display_id);
};