jak-project/game/system/hid/display_manager.h
ManDude 4185123bd8
fix mouse mispositioning when letterboxed (#2818)
Not sure if this is the best way to go about it.

Fixes #2259
2023-07-08 23:05:03 +01:00

145 lines
4.7 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/SDL.h"
/*
TODO:
- hiDPI support, see https://wiki.libsdl.org/SDL2/SDL_GetRendererOutputSize
*/
enum class WindowDisplayMode { Windowed = 0, Fullscreen = 1, Borderless = 2 };
enum class WindowState { Minimized, Maximized, Restored };
enum class Orientation { Landscape, LandscapeFlipped, Portrait, PortraitFlipped, Unknown };
/// https://wiki.libsdl.org/SDL2/SDL_DisplayMode
struct DisplayMode {
std::string display_name;
/// https://wiki.libsdl.org/SDL2/SDL_PixelFormatEnum
uint32_t sdl_pixel_format;
int screen_width;
int screen_height;
/// refresh rate (in Hz), or 0 for unspecified
int 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_WINDOW_DISPLAY_MODE,
SET_FULLSCREEN_DISPLAY_ID
};
struct EEDisplayEvent {
EEDisplayEventType type;
std::variant<bool, int, WindowDisplayMode> param1;
std::variant<bool, int, WindowDisplayMode> param2;
};
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();
// Accessors
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();
WindowDisplayMode get_window_display_mode() { return m_window_display_mode; }
int get_num_resolutions() { return m_available_resolutions.size(); }
Resolution get_resolution(int id);
// Mutators
void enqueue_set_window_size(int width, int height);
void enqueue_set_window_display_mode(WindowDisplayMode mode);
void enqueue_set_fullscreen_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;
WindowDisplayMode m_window_display_mode = WindowDisplayMode::Windowed;
int m_active_display_id;
// This can differ from the active display id, this is set by the game
// to explicitly define which monitor the game should be fullscreened with
int m_selected_fullscreen_display_id;
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::unordered_map<int, DisplayMode> m_current_display_modes;
std::vector<Resolution> m_available_resolutions;
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_window_display_mode(WindowDisplayMode mode);
void set_fullscreen_display_id(int display_id);
};