forked from Ports/perfect_dark
Allow changing video resolution at runtime + more
Fullscreen exclusive mode is togglable at runtime, as well, as a dropdown menu with "borderless" and "exclusive" options. All variables pertaining to video resolution, exclusive mode, and maximized state are now synchronized. For instance, Alt+Enter will affect the state of the fullscreen video option and maximizing the window via the title bar will update the maximize window option accordingly.
This commit is contained in:
7 files changed
+273
-15
No files matched your search
@@ -0,0 +1,8 @@
|
||||
#ifndef GFX_MODES_H
|
||||
#define GFX_MODES_H
|
||||
|
||||
struct GfxModes {
|
||||
char mode[10];
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "platform.h"
|
||||
#include "system.h"
|
||||
|
||||
#include "gfx_modes.h"
|
||||
#include "gfx_window_manager_api.h"
|
||||
#include "gfx_screen_config.h"
|
||||
|
||||
@@ -30,10 +31,30 @@ static uint64_t qpc_freq;
|
||||
#define FRAME_INTERVAL_US_NUMERATOR 1000000
|
||||
#define FRAME_INTERVAL_US_DENOMINATOR (target_fps)
|
||||
|
||||
static void set_fullscreen(bool on, bool call_callback) {
|
||||
if (fullscreen_state == on) {
|
||||
return;
|
||||
static int32_t gfx_sdl_get_maximized_state(void) {
|
||||
return (int32_t)maximized_state;
|
||||
}
|
||||
|
||||
static int32_t gfx_sdl_get_fullscreen_state(void) {
|
||||
return (int32_t)fullscreen_state;
|
||||
}
|
||||
|
||||
static int32_t gfx_sdl_get_fullscreen_flag_mode(void) {
|
||||
return fullscreen_flag == SDL_WINDOW_FULLSCREEN_DESKTOP ? 0 : 1;
|
||||
}
|
||||
|
||||
static void gfx_sdl_set_fullscreen_flag(int32_t mode) {
|
||||
switch (mode) {
|
||||
case 0: {
|
||||
fullscreen_flag = SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
} break;
|
||||
case 1: {
|
||||
fullscreen_flag = SDL_WINDOW_FULLSCREEN;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_fullscreen(bool on, bool call_callback) {
|
||||
fullscreen_state = on;
|
||||
SDL_SetWindowFullscreen(wnd, on ? fullscreen_flag : 0);
|
||||
if (call_callback && on_fullscreen_changed_callback) {
|
||||
@@ -42,9 +63,6 @@ static void set_fullscreen(bool on, bool call_callback) {
|
||||
}
|
||||
|
||||
static void set_maximize_window(bool on) {
|
||||
if (maximized_state == on) {
|
||||
return;
|
||||
}
|
||||
maximized_state = on;
|
||||
if (on) {
|
||||
SDL_MaximizeWindow(wnd);
|
||||
@@ -217,6 +235,20 @@ static void gfx_sdl_set_cursor_visibility(bool visible) {
|
||||
}
|
||||
}
|
||||
|
||||
static void gfx_sdl_set_closest_resolution(int32_t width, int32_t height) {
|
||||
const SDL_DisplayMode mode = {.w = width, .h = height};
|
||||
SDL_DisplayMode closest = {};
|
||||
if (SDL_GetClosestDisplayMode(0, &mode, &closest)) {
|
||||
SDL_SetWindowDisplayMode(wnd, &closest);
|
||||
SDL_SetWindowSize(wnd, closest.w, closest.h);
|
||||
}
|
||||
}
|
||||
|
||||
static void gfx_sdl_set_dimensions(uint32_t width, uint32_t height, int32_t posX, int32_t posY) {
|
||||
SDL_SetWindowSize(wnd, width, height);
|
||||
SDL_SetWindowPosition(wnd, posX, posY);
|
||||
}
|
||||
|
||||
static void gfx_sdl_get_dimensions(uint32_t* width, uint32_t* height, int32_t* posX, int32_t* posY) {
|
||||
SDL_GL_GetDrawableSize(wnd, static_cast<int*>((void*)width), static_cast<int*>((void*)height));
|
||||
SDL_GetWindowPosition(wnd, static_cast<int*>(posX), static_cast<int*>(posY));
|
||||
@@ -235,6 +267,9 @@ static void gfx_sdl_handle_events(void) {
|
||||
case SDL_WINDOWEVENT:
|
||||
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
|
||||
SDL_GL_GetDrawableSize(wnd, &window_width, &window_height);
|
||||
if (!fullscreen_state) {
|
||||
maximized_state = SDL_GetWindowFlags(wnd) & SDL_WINDOW_MAXIMIZED ? true : false;
|
||||
}
|
||||
} else if (event.window.event == SDL_WINDOWEVENT_CLOSE &&
|
||||
event.window.windowID == SDL_GetWindowID(wnd)) {
|
||||
// We listen specifically for main window close because closing main window
|
||||
@@ -324,15 +359,63 @@ static bool gfx_sdl_set_swap_interval(int interval) {
|
||||
return success;
|
||||
}
|
||||
|
||||
struct GfxWindowManagerAPI gfx_sdl = {
|
||||
static GfxModes *gfx_sdl_alloc_display_modes(GfxModes *modes, int *num_modes) {
|
||||
static const char *alloc_failed_msg = "failed to allocate video display modes";
|
||||
const int disp_idx = SDL_GetWindowDisplayIndex(wnd);
|
||||
const int display_modes = SDL_GetNumDisplayModes(disp_idx);
|
||||
int modes_sz = sizeof *modes * 16;
|
||||
|
||||
modes = (GfxModes *)malloc(modes_sz);
|
||||
if (!modes) {
|
||||
sysLogPrintf(LOG_ERROR, "malloc: %s", alloc_failed_msg);
|
||||
}
|
||||
|
||||
strncpy(modes[0].mode, "Custom", sizeof modes[0].mode);
|
||||
modes[0].mode[sizeof modes[0].mode - 1] = '\0';
|
||||
|
||||
int j = 1;
|
||||
for (int i = 0; i < display_modes; ++i) {
|
||||
SDL_DisplayMode mode = {};
|
||||
if (SDL_GetDisplayMode(disp_idx, i, &mode) == 0) {
|
||||
if (j == (int)(modes_sz / sizeof *modes)) {
|
||||
modes = (GfxModes *)realloc(modes, modes_sz <<= 1);
|
||||
if (!modes) {
|
||||
sysLogPrintf(LOG_ERROR, "realloc: %s", alloc_failed_msg);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(modes[j].mode, sizeof modes[0].mode, "%dx%d", mode.w, mode.h);
|
||||
|
||||
if (j && strcmp(modes[j].mode, modes[j - 1].mode) != 0) {
|
||||
++j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*num_modes = j;
|
||||
modes_sz = *num_modes * sizeof *modes;
|
||||
modes = (GfxModes *)realloc(modes, modes_sz);
|
||||
if (!modes) {
|
||||
sysLogPrintf(LOG_ERROR, "realloc: %s", alloc_failed_msg);
|
||||
}
|
||||
return modes;
|
||||
}
|
||||
|
||||
struct GfxWindowManagerAPI gfx_sdl = {
|
||||
gfx_sdl_init,
|
||||
gfx_sdl_close,
|
||||
gfx_sdl_get_fullscreen_state,
|
||||
gfx_sdl_set_fullscreen_changed_callback,
|
||||
gfx_sdl_set_fullscreen,
|
||||
gfx_sdl_set_fullscreen_exclusive,
|
||||
gfx_sdl_set_fullscreen_flag,
|
||||
gfx_sdl_get_fullscreen_flag_mode,
|
||||
gfx_sdl_get_maximized_state,
|
||||
gfx_sdl_set_maximize_window,
|
||||
gfx_sdl_get_active_window_refresh_rate,
|
||||
gfx_sdl_set_cursor_visibility,
|
||||
gfx_sdl_set_closest_resolution,
|
||||
gfx_sdl_set_dimensions,
|
||||
gfx_sdl_get_dimensions,
|
||||
gfx_sdl_handle_events,
|
||||
gfx_sdl_start_frame,
|
||||
@@ -344,4 +427,5 @@ struct GfxWindowManagerAPI gfx_sdl = {
|
||||
gfx_sdl_get_window_handle,
|
||||
gfx_sdl_set_window_title,
|
||||
gfx_sdl_set_swap_interval,
|
||||
gfx_sdl_alloc_display_modes,
|
||||
};
|
||||
@@ -19,12 +19,18 @@ struct GfxWindowInitSettings {
|
||||
struct GfxWindowManagerAPI {
|
||||
void (*init)(const struct GfxWindowInitSettings *settings);
|
||||
void (*close)(void);
|
||||
int32_t (*get_fullscreen_state)(void);
|
||||
void (*set_fullscreen_changed_callback)(void (*on_fullscreen_changed)(bool is_now_fullscreen));
|
||||
void (*set_fullscreen)(bool enable);
|
||||
void (*set_fullscreen_exclusive)(bool exc);
|
||||
void (*set_fullscreen_flag)(int32_t mode);
|
||||
int32_t (*get_fullscreen_flag_mode)(void);
|
||||
int32_t (*get_maximized_state)(void);
|
||||
void (*set_maximize)(bool enable);
|
||||
void (*get_active_window_refresh_rate)(uint32_t* refresh_rate);
|
||||
void (*set_cursor_visibility)(bool visible);
|
||||
void (*set_closest_resolution)(int32_t width, int32_t height);
|
||||
void (*set_dimensions)(uint32_t width, uint32_t height, int32_t posX, int32_t posY);
|
||||
void (*get_dimensions)(uint32_t* width, uint32_t* height, int32_t* posX, int32_t* posY);
|
||||
void (*handle_events)(void);
|
||||
bool (*start_frame)(void);
|
||||
@@ -36,6 +42,7 @@ struct GfxWindowManagerAPI {
|
||||
void *(*get_window_handle)(void);
|
||||
void (*set_window_title)(const char *);
|
||||
bool (*set_swap_interval)(int);
|
||||
struct GfxModes *(*alloc_display_modes)(struct GfxModes *, int *);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -27,17 +27,25 @@ s32 videoGetWidth(void);
|
||||
s32 videoGetHeight(void);
|
||||
f32 videoGetAspect(void);
|
||||
s32 videoGetFullscreen(void);
|
||||
s32 videoGetFullscreenMode(void);
|
||||
s32 videoGetMaximizeWindow(void);
|
||||
void videoSetMaximizeWindow(s32 fs);
|
||||
u32 videoGetTextureFilter(void);
|
||||
s32 videoGetTextureFilter2D(void);
|
||||
s32 videoGetDetailTextures(void);
|
||||
s32 videoGetDisplayModeIndex(void);
|
||||
struct GfxModes *videoGetDisplayModes(void);
|
||||
s32 videoGetNumDisplayModes(void);
|
||||
|
||||
void videoAllocDisplayModes(void);
|
||||
|
||||
void videoSetWindowOffset(s32 x, s32 y);
|
||||
void videoSetFullscreen(s32 fs);
|
||||
void videoSetFullscreenMode(s32 mode);
|
||||
void videoSetTextureFilter(u32 filter);
|
||||
void videoSetTextureFilter2D(s32 filter);
|
||||
void videoSetDetailTextures(s32 detail);
|
||||
void videoSetDisplayMode(const s32 mode_idx);
|
||||
|
||||
s32 videoCreateFramebuffer(u32 w, u32 h, s32 upscale, s32 autoresize);
|
||||
void videoSetFramebuffer(s32 target);
|
||||
@@ -49,4 +57,6 @@ s32 videoFramebuffersSupported(void);
|
||||
void videoResetTextureCache(void);
|
||||
void videoFreeCachedTexture(const void *texptr);
|
||||
|
||||
void videoShutdown(void);
|
||||
|
||||
#endif
|
||||
@@ -88,6 +88,7 @@ static void cleanup(void)
|
||||
sysLogPrintf(LOG_NOTE, "shutdown");
|
||||
inputSaveBinds();
|
||||
configSave(CONFIG_PATH);
|
||||
videoShutdown();
|
||||
crashShutdown();
|
||||
// TODO: actually shut down all subsystems
|
||||
}
|
||||
|
||||
+71
-8
@@ -9,6 +9,7 @@
|
||||
#include "game/mainmenu.h"
|
||||
#include "game/menu.h"
|
||||
#include "game/gamefile.h"
|
||||
#include "../fast3d/gfx_modes.h"
|
||||
#include "video.h"
|
||||
#include "input.h"
|
||||
#include "config.h"
|
||||
@@ -687,6 +688,29 @@ static MenuItemHandlerResult menuhandlerFullScreen(s32 operation, struct menuite
|
||||
return 0;
|
||||
}
|
||||
|
||||
static MenuItemHandlerResult menuhandlerFullScreenMode(s32 operation, struct menuitem *item, union handlerdata *data)
|
||||
{
|
||||
static const char *opts[] = {
|
||||
"Borderless",
|
||||
"Exclusive"
|
||||
};
|
||||
|
||||
switch (operation) {
|
||||
case MENUOP_GETOPTIONCOUNT:
|
||||
data->dropdown.value = ARRAYCOUNT(opts);
|
||||
break;
|
||||
case MENUOP_GETOPTIONTEXT:
|
||||
return (intptr_t)opts[data->dropdown.value];
|
||||
case MENUOP_SET:
|
||||
videoSetFullscreenMode(data->dropdown.value);
|
||||
break;
|
||||
case MENUOP_GETSELECTEDINDEX:
|
||||
data->dropdown.value = videoGetFullscreenMode();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static MenuItemHandlerResult menuhandlerMaximizeWindow(s32 operation, struct menuitem *item, union handlerdata *data)
|
||||
{
|
||||
switch (operation) {
|
||||
@@ -700,6 +724,29 @@ static MenuItemHandlerResult menuhandlerMaximizeWindow(s32 operation, struct men
|
||||
return 0;
|
||||
}
|
||||
|
||||
static MenuItemHandlerResult menuhandlerResolution(s32 operation, struct menuitem *item, union handlerdata *data)
|
||||
{
|
||||
switch (operation) {
|
||||
case MENUOP_CHECKDISABLED:
|
||||
if (videoGetFullscreen() && videoGetFullscreenMode() == 0) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case MENUOP_GETOPTIONCOUNT:
|
||||
data->dropdown.value = videoGetNumDisplayModes();
|
||||
break;
|
||||
case MENUOP_GETOPTIONTEXT:
|
||||
return (intptr_t)videoGetDisplayModes()[data->dropdown.value].mode;
|
||||
case MENUOP_SET:
|
||||
videoSetDisplayMode(data->dropdown.value);
|
||||
break;
|
||||
case MENUOP_GETSELECTEDINDEX:
|
||||
data->dropdown.value = videoGetDisplayModeIndex();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static MenuItemHandlerResult menuhandlerTexFilter(s32 operation, struct menuitem *item, union handlerdata *data)
|
||||
{
|
||||
static const char *opts[] = {
|
||||
@@ -820,6 +867,30 @@ struct menuitem g_ExtendedVideoMenuItems[] = {
|
||||
0,
|
||||
menuhandlerFullScreen,
|
||||
},
|
||||
{
|
||||
MENUITEMTYPE_DROPDOWN,
|
||||
0,
|
||||
MENUITEMFLAG_LITERAL_TEXT,
|
||||
(uintptr_t)"Full Screen Mode",
|
||||
0,
|
||||
menuhandlerFullScreenMode,
|
||||
},
|
||||
{
|
||||
MENUITEMTYPE_DROPDOWN,
|
||||
0,
|
||||
MENUITEMFLAG_LITERAL_TEXT,
|
||||
(uintptr_t)"Resolution",
|
||||
0,
|
||||
menuhandlerResolution,
|
||||
},
|
||||
{
|
||||
MENUITEMTYPE_DROPDOWN,
|
||||
0,
|
||||
MENUITEMFLAG_LITERAL_TEXT,
|
||||
(uintptr_t)"Texture Filtering",
|
||||
0,
|
||||
menuhandlerTexFilter,
|
||||
},
|
||||
{
|
||||
MENUITEMTYPE_CHECKBOX,
|
||||
0,
|
||||
@@ -836,14 +907,6 @@ struct menuitem g_ExtendedVideoMenuItems[] = {
|
||||
0,
|
||||
menuhandlerTexDetail,
|
||||
},
|
||||
{
|
||||
MENUITEMTYPE_DROPDOWN,
|
||||
0,
|
||||
MENUITEMFLAG_LITERAL_TEXT,
|
||||
(uintptr_t)"Texture Filtering",
|
||||
0,
|
||||
menuhandlerTexFilter,
|
||||
},
|
||||
{
|
||||
MENUITEMTYPE_CHECKBOX,
|
||||
0,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <PR/gbi.h>
|
||||
#include "platform.h"
|
||||
#include "config.h"
|
||||
#include "../fast3d/gfx_modes.h"
|
||||
#include "video.h"
|
||||
|
||||
#include "../fast3d/gfx_api.h"
|
||||
@@ -39,6 +40,8 @@ static s32 vidAllowHiDpi = false;
|
||||
static s32 vidVsync = 1;
|
||||
static s32 vidMSAA = 1;
|
||||
static s32 vidFramerateLimit = 0;
|
||||
static struct GfxModes *vidModes = NULL; // Allocated in videoAllocDisplayModes(). Freed in videoShutdown().
|
||||
static s32 vidNumModes = 0;
|
||||
|
||||
static s32 texFilter = FILTER_LINEAR;
|
||||
static s32 texFilter2D = true;
|
||||
@@ -78,6 +81,7 @@ s32 videoInit(void)
|
||||
};
|
||||
|
||||
gfx_init(&set);
|
||||
videoAllocDisplayModes();
|
||||
|
||||
if (!wmAPI->set_swap_interval(vidVsync)) {
|
||||
vidVsync = 0;
|
||||
@@ -177,11 +181,19 @@ s32 videoGetHeight(void)
|
||||
|
||||
s32 videoGetFullscreen(void)
|
||||
{
|
||||
vidFullscreen = wmAPI->get_fullscreen_state();
|
||||
return vidFullscreen;
|
||||
}
|
||||
|
||||
s32 videoGetFullscreenMode(void)
|
||||
{
|
||||
vidFullscreenExclusive = wmAPI->get_fullscreen_flag_mode();
|
||||
return vidFullscreenExclusive;
|
||||
}
|
||||
|
||||
s32 videoGetMaximizeWindow(void)
|
||||
{
|
||||
vidMaximize = wmAPI->get_maximized_state();
|
||||
return vidMaximize;
|
||||
}
|
||||
|
||||
@@ -190,6 +202,59 @@ f32 videoGetAspect(void)
|
||||
return gfx_current_dimensions.aspect_ratio;
|
||||
}
|
||||
|
||||
s32 videoGetDisplayModeIndex(void)
|
||||
{
|
||||
for (int i = 1; i < vidNumModes; ++i) {
|
||||
char tmp[10] = {0};
|
||||
snprintf(tmp, sizeof tmp, "%dx%d", gfx_current_dimensions.width, gfx_current_dimensions.height);
|
||||
if (strcmp(tmp, vidModes[i].mode) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// Current dimensions don't match any known mode, so return index 0, "Custom".
|
||||
return 0;
|
||||
}
|
||||
|
||||
void videoAllocDisplayModes(void)
|
||||
{
|
||||
vidModes = wmAPI->alloc_display_modes(vidModes, &vidNumModes);
|
||||
}
|
||||
|
||||
struct GfxModes *videoGetDisplayModes(void)
|
||||
{
|
||||
return vidModes;
|
||||
}
|
||||
|
||||
s32 videoGetNumDisplayModes(void)
|
||||
{
|
||||
return vidNumModes;
|
||||
}
|
||||
|
||||
void videoSetDisplayMode(const s32 mode_idx)
|
||||
{
|
||||
const char *cur_mode = vidModes[mode_idx].mode;
|
||||
char *x_pos = NULL;
|
||||
|
||||
if (mode_idx == 0) {
|
||||
// "Custom" video mode.
|
||||
return;
|
||||
}
|
||||
|
||||
vidWidth = strtol(cur_mode, &x_pos, 10);
|
||||
cur_mode = x_pos + 1;
|
||||
vidHeight = strtol(cur_mode, NULL, 10);
|
||||
|
||||
if (vidFullscreen) {
|
||||
wmAPI->set_closest_resolution(vidWidth, vidHeight);
|
||||
} else {
|
||||
if (vidMaximize) {
|
||||
videoSetMaximizeWindow(false);
|
||||
}
|
||||
wmAPI->set_dimensions(vidWidth, vidHeight, 100, 100);
|
||||
}
|
||||
}
|
||||
|
||||
s32 videoGetTextureFilter2D(void)
|
||||
{
|
||||
return texFilter2D;
|
||||
@@ -215,7 +280,22 @@ void videoSetFullscreen(s32 fs)
|
||||
{
|
||||
if (fs != vidFullscreen) {
|
||||
vidFullscreen = !!fs;
|
||||
wmAPI->set_closest_resolution(vidWidth, vidHeight);
|
||||
wmAPI->set_fullscreen(vidFullscreen);
|
||||
if (!vidFullscreen && vidMaximize) {
|
||||
wmAPI->set_maximize(false);
|
||||
wmAPI->set_maximize(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void videoSetFullscreenMode(s32 mode)
|
||||
{
|
||||
vidFullscreenExclusive = mode;
|
||||
wmAPI->set_fullscreen_flag(mode);
|
||||
if (vidFullscreen) {
|
||||
wmAPI->set_fullscreen(false);
|
||||
wmAPI->set_fullscreen(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,6 +367,11 @@ void videoFreeCachedTexture(const void *texptr)
|
||||
gfx_texture_cache_delete(texptr);
|
||||
}
|
||||
|
||||
void videoShutdown(void)
|
||||
{
|
||||
free(vidModes);
|
||||
}
|
||||
|
||||
PD_CONSTRUCTOR static void videoConfigInit(void)
|
||||
{
|
||||
configRegisterInt("Video.DefaultFullscreen", &vidFullscreen, 0, 1);
|
||||
|
||||
Reference in new issue
Block a user