diff --git a/src/config.h b/src/config.h
index 5932bf8a..c57e6ce4 100644
--- a/src/config.h
+++ b/src/config.h
@@ -89,7 +89,6 @@
\
CONFIGDEF_STRING (PLAYERNAME, "playername", "Player") \
CONFIGDEF_INT (FULLSCREEN, "fullscreen", 0) \
- CONFIGDEF_INT (FULLSCREEN_DESKTOP, "fullscreen_desktop_mode", 1) \
CONFIGDEF_INT (VID_WIDTH, "vid_width", RESX) \
CONFIGDEF_INT (VID_HEIGHT, "vid_height", RESY) \
CONFIGDEF_INT (VID_DISPLAY, "vid_display", 0) \
diff --git a/src/menu/options.c b/src/menu/options.c
index dfac5065..03898fd0 100644
--- a/src/menu/options.c
+++ b/src/menu/options.c
@@ -28,6 +28,7 @@ typedef enum BindingType {
BT_KeyBinding,
BT_StrValue,
BT_Resolution,
+ BT_FramebufferResolution,
BT_Scale,
BT_GamepadKeyBinding,
BT_GamepadAxisBinding,
@@ -197,17 +198,27 @@ static OptionBinding* bind_stroption(ConfigIndex cfgentry) {
// BT_Resolution: super-special binding type for the resolution setting
static void bind_resolution_update(OptionBinding *bind) {
- bool fullscreen = config_get_int(CONFIG_FULLSCREEN);
+ // FIXME This is brittle. The least we could do is to explicitly store whether the currently selected value represents a fullscreen index or windowed.
+
+ bool fullscreen = video_is_fullscreen();
uint nmodes = video_get_num_modes(fullscreen);
VideoMode cur = video_get_current_mode();
+ log_debug("Fullscreen: %i", fullscreen);
+ log_debug("Prev selected: %i", bind->selected);
+
bind->valrange_min = 0;
bind->valrange_max = nmodes - 1;
+ bind->selected = -1;
+
+ log_debug("nmodes = %i", nmodes);
for(int i = 0; i < nmodes; ++i) {
VideoMode m = video_get_mode(i, fullscreen);
+ log_debug("#%i %ix%i", i, m.width, m.height);
if(m.width == cur.width && m.height == cur.height) {
bind->selected = i;
+ log_debug("selected #%i", bind->selected);
}
}
}
@@ -220,6 +231,15 @@ static OptionBinding* bind_resolution(void) {
return bind;
}
+// BT_FramebufferResolution: not an actual setting (yet); just display effective resolution in pixels
+// This may be different from BT_Resolution in the high-DPI case
+// (BT_Resolution is a misnomer; it represents the window size in screen-space units)
+static OptionBinding* bind_fb_resolution(void) {
+ OptionBinding *bind = bind_new();
+ bind->type = BT_FramebufferResolution;
+ return bind;
+}
+
// BT_Scale: float values clamped to a range
static OptionBinding* bind_scale(int cfgentry, float smin, float smax, float step) {
OptionBinding *bind = bind_new();
@@ -390,12 +410,16 @@ static bool bind_resolution_dependence(void) {
return video_query_capability(VIDEO_CAP_CHANGE_RESOLUTION) == VIDEO_AVAILABLE;
}
+static bool bind_fb_resolution_dependence(void) {
+ return false;
+}
+
static bool bind_fullscreen_dependence(void) {
return video_query_capability(VIDEO_CAP_FULLSCREEN) == VIDEO_AVAILABLE;
}
static int bind_resolution_set(OptionBinding *b, int v) {
- bool fullscreen = config_get_int(CONFIG_FULLSCREEN);
+ bool fullscreen = video_is_fullscreen();
if(v >= 0) {
VideoMode m = video_get_mode(v, fullscreen);
config_set_int(CONFIG_VID_WIDTH, m.width);
@@ -432,7 +456,7 @@ static void destroy_options_menu(MenuData *m) {
if(bind->type == BT_Resolution && video_query_capability(VIDEO_CAP_CHANGE_RESOLUTION) == VIDEO_AVAILABLE) {
if(bind->selected != -1) {
- bool fullscreen = config_get_int(CONFIG_FULLSCREEN);
+ bool fullscreen = video_is_fullscreen();
VideoMode mode = video_get_mode(bind->selected, fullscreen);
config_set_int(CONFIG_VID_WIDTH, mode.width);
config_set_int(CONFIG_VID_HEIGHT, mode.height);
@@ -550,6 +574,11 @@ static MenuData* create_options_menu_video(MenuData *parent) {
); b->setter = bind_resolution_set;
b->dependence = bind_resolution_dependence;
+ add_menu_entry(m, "Renderer resolution", do_nothing,
+ b = bind_fb_resolution()
+ ); b->dependence = bind_fb_resolution_dependence;
+ b->pad++;
+
add_menu_entry(m, "Resizable window", do_nothing,
b = bind_option(CONFIG_VID_RESIZABLE, bind_common_onoff_get, bind_common_onoff_set)
); bind_onoff(b);
@@ -1179,21 +1208,33 @@ static void draw_options_menu(MenuData *menu) {
}
case BT_Resolution: {
- char tmp[16];
+ char tmp[32];
int w, h;
VideoMode m;
if(bind->selected == -1) {
m = video_get_current_mode();
} else {
- bool fullscreen = config_get_int(CONFIG_FULLSCREEN);
+ bool fullscreen = video_is_fullscreen();
m = video_get_mode(bind->selected, fullscreen);
}
w = m.width;
h = m.height;
- snprintf(tmp, 16, "%dx%d", w, h);
+ snprintf(tmp, sizeof(tmp), "%dx%d", w, h);
+ text_draw(tmp, &(TextParams) {
+ .pos = { origin, 20*i },
+ .align = ALIGN_RIGHT,
+ .color = &clr,
+ });
+ break;
+ }
+
+ case BT_FramebufferResolution: {
+ IntExtent fbsize = r_framebuffer_get_size(video_get_screen_framebuffer());
+ char tmp[32];
+ snprintf(tmp, sizeof(tmp), "%gx %dx%d", video_get_scaling_factor(), fbsize.w, fbsize.h);
text_draw(tmp, &(TextParams) {
.pos = { origin, 20*i },
.align = ALIGN_RIGHT,
diff --git a/src/taisei.manifest b/src/taisei.manifest
index 55499d0a..1d16440f 100644
--- a/src/taisei.manifest
+++ b/src/taisei.manifest
@@ -29,8 +29,8 @@
- PerMonitorV2
- true
+ PerMonitorV2,PerMonitor
+ True/PM
diff --git a/src/video.c b/src/video.c
index b39489bc..bd6057d2 100644
--- a/src/video.c
+++ b/src/video.c
@@ -25,19 +25,82 @@ static struct {
VideoModeArray fs_modes;
VideoModeArray win_modes;
SDL_Window *window;
+ VideoPostProcess *postprocess;
VideoMode intended;
VideoMode current;
VideoBackend backend;
+ double scaling_factor;
} video;
+VideoCapabilityState (*video_query_capability)(VideoCapability cap);
+
typedef struct ScreenshotTaskData {
char *dest_path;
Pixmap image;
} ScreenshotTaskData;
-static VideoPostProcess *v_postprocess;
+#define VIDEO_MIN_SIZE_FACTOR 0.8
+#define VIDEO_MIN_WIDTH (int)(SCREEN_W * VIDEO_MIN_SIZE_FACTOR)
+#define VIDEO_MIN_HEIGHT (int)(SCREEN_H * VIDEO_MIN_SIZE_FACTOR)
-VideoCapabilityState (*video_query_capability)(VideoCapability cap);
+/*
+ * BEGIN Conversion between screen-space and pixel-space coordinates (for high-DPI mode)
+ * TODO: figure out how to round these correctly
+ */
+
+attr_unused static inline int coords_val_screen_to_pixels(int screen_coord) {
+ return round(screen_coord * video.scaling_factor);
+}
+
+attr_unused static inline int coords_val_pixels_to_screen(int pixel_coord) {
+ return round(pixel_coord / video.scaling_factor);
+}
+
+attr_unused static inline IntOffset coords_ofs_screen_to_pixels(IntOffset screen_ofs) {
+ IntOffset pixel_ofs;
+ pixel_ofs.x = coords_val_screen_to_pixels(screen_ofs.x);
+ pixel_ofs.y = coords_val_screen_to_pixels(screen_ofs.y);
+ return pixel_ofs;
+}
+
+attr_unused static inline IntOffset coords_ofs_pixels_to_screen(IntOffset pixel_ofs) {
+ IntOffset screen_ofs;
+ screen_ofs.x = coords_val_pixels_to_screen(pixel_ofs.x);
+ screen_ofs.y = coords_val_pixels_to_screen(pixel_ofs.y);
+ return screen_ofs;
+}
+
+attr_unused static inline IntExtent coords_ext_screen_to_pixels(IntExtent screen_ext) {
+ IntExtent pixel_ext;
+ pixel_ext.w = coords_val_screen_to_pixels(screen_ext.w);
+ pixel_ext.h = coords_val_screen_to_pixels(screen_ext.h);
+ return pixel_ext;
+}
+
+attr_unused static inline IntExtent coords_ext_pixels_to_screen(IntExtent pixel_ext) {
+ IntExtent screen_ext;
+ screen_ext.w = coords_val_pixels_to_screen(pixel_ext.w);
+ screen_ext.h = coords_val_pixels_to_screen(pixel_ext.h);
+ return screen_ext;
+}
+
+attr_unused static inline IntRect coords_rect_screen_to_pixels(IntRect screen_rect) {
+ IntRect pixel_rect;
+ pixel_rect.extent = coords_ext_screen_to_pixels(screen_rect.extent);
+ pixel_rect.offset = coords_ofs_screen_to_pixels(screen_rect.offset);
+ return pixel_rect;
+}
+
+attr_unused static inline IntRect coords_rect_pixels_to_screen(IntRect pixel_rect) {
+ IntRect screen_rect;
+ screen_rect.extent = coords_ext_pixels_to_screen(pixel_rect.extent);
+ screen_rect.offset = coords_ofs_pixels_to_screen(pixel_rect.offset);
+ return screen_rect;
+}
+
+/*
+ * END Conversion between screen-space and pixel-space coordinates (for high-DPI mode)
+ */
static VideoCapabilityState video_query_capability_generic(VideoCapability cap) {
switch(cap) {
@@ -48,11 +111,7 @@ static VideoCapabilityState video_query_capability_generic(VideoCapability cap)
return video_is_fullscreen() ? VIDEO_CURRENTLY_UNAVAILABLE : VIDEO_AVAILABLE;
case VIDEO_CAP_CHANGE_RESOLUTION:
- if(video_is_fullscreen() && config_get_int(CONFIG_FULLSCREEN_DESKTOP)) {
- return VIDEO_CURRENTLY_UNAVAILABLE;
- } else {
- return VIDEO_AVAILABLE;
- }
+ return video_is_fullscreen() ? VIDEO_CURRENTLY_UNAVAILABLE : VIDEO_AVAILABLE;
case VIDEO_CAP_VSYNC_ADAPTIVE:
return VIDEO_AVAILABLE;
@@ -93,30 +152,56 @@ static VideoCapabilityState video_query_capability_webcanvas(VideoCapability cap
}
}
-static void video_add_mode_handler(VideoModeArray *mode_array, int width, int height, const char *mode_type) {
+static void video_add_mode(VideoModeArray *mode_array, IntExtent mode_screen, IntExtent min_screen, IntExtent max_screen, const char *mode_type) {
+ if(
+ (mode_screen.w > max_screen.w && max_screen.w > 0) ||
+ (mode_screen.h > max_screen.h && max_screen.h > 0)
+ ) {
+ log_debug("Mode %ix%i rejected: > %ix%i", mode_screen.w, mode_screen.h, max_screen.w, max_screen.h);
+ return;
+ }
+
+ if(
+ mode_screen.w < min_screen.w ||
+ mode_screen.h < min_screen.h
+ ) {
+ log_debug("Mode %ix%i rejected: < %ix%i", mode_screen.w, mode_screen.h, min_screen.w, min_screen.h);
+ return;
+ }
+
for(uint i = 0; i < mode_array->num_elements; ++i) {
VideoMode *m = mode_array->data + i;
- if(m->width == width && m->height == height) {
+ if(m->width == mode_screen.w && m->height == mode_screen.h) {
+ log_debug("Mode %ix%i rejected: already registered", mode_screen.w, mode_screen.h);
return;
}
}
- *dynarray_append(mode_array) = (VideoMode) { width, height };
- log_debug("Add %s mode: %ix%i", mode_type, width, height);
+ dynarray_append(mode_array)->as_int_extent = mode_screen;
+ log_debug("Add %s mode: %ix%i", mode_type, mode_screen.w, mode_screen.h);
}
-static void video_add_mode_fullscreen(int width, int height) {
- video_add_mode_handler(&video.fs_modes, width, height, "fullscreen");
+static void video_add_mode_dpi_aware(VideoModeArray *mode_array, IntExtent mode_pix, IntExtent min_screen, IntExtent max_screen, const char *mode_type) {
+ IntExtent mode_screen = coords_ext_pixels_to_screen(mode_pix);
+
+ // Yes, we add both. The pixel-space size is interpreted as screen-space.
+ // Anything too large to fit on the screen is rejected.
+ video_add_mode(mode_array, mode_screen, min_screen, max_screen, mode_type);
+ video_add_mode(mode_array, mode_pix, min_screen, max_screen, mode_type);
}
-static void video_add_mode_windowed(int width, int height) {
- video_add_mode_handler(&video.win_modes, width, height, "windowed");
+static void video_add_mode_fullscreen(IntExtent mode_pix, IntExtent min_screen, IntExtent max_screen) {
+ video_add_mode_dpi_aware(&video.fs_modes, mode_pix, min_screen, max_screen, "fullscreen");
+}
+
+static void video_add_mode_windowed(IntExtent mode_pix, IntExtent min_screen, IntExtent max_screen) {
+ video_add_mode_dpi_aware(&video.win_modes, mode_pix, min_screen, max_screen, "windowed");
}
static int video_compare_modes(const void *a, const void *b) {
- VideoMode *va = (VideoMode*)a;
- VideoMode *vb = (VideoMode*)b;
+ const VideoMode *va = a;
+ const VideoMode *vb = b;
return va->width * va->height - vb->width * vb->height;
}
@@ -138,6 +223,120 @@ static FloatExtent video_get_viewport_size_for_framebuffer(IntExtent framebuffer
return (FloatExtent) { w, h };
}
+static IntExtent round_viewport_size(FloatExtent vp) {
+ return (IntExtent) { round(vp.w), round(vp.h) };
+}
+
+static void video_update_mode_lists(void) {
+ video.fs_modes.num_elements = 0;
+ video.win_modes.num_elements = 0;
+
+ dynarray_ensure_capacity(&video.fs_modes, 16);
+ dynarray_ensure_capacity(&video.win_modes, 16);
+
+ bool fullscreen_available = false;
+ bool has_windowed_modes = (video_query_capability(VIDEO_CAP_FULLSCREEN) != VIDEO_ALWAYS_ENABLED);
+ FloatExtent largest_fullscreen_viewport = { 0, 0 };
+
+ IntExtent screenspace_min_size = { VIDEO_MIN_WIDTH, VIDEO_MIN_HEIGHT };
+ screenspace_min_size = coords_ext_pixels_to_screen(screenspace_min_size);
+
+ // Register all resolutions that are available in fullscreen and their corresponding windowed modes.
+ for(int s = 0; s < video_num_displays(); ++s) {
+ log_info("Found display #%i: %s", s, video_display_name(s));
+
+ SDL_DisplayMode desktop_mode;
+ IntExtent screenspace_max_size = { 0 };
+
+ if(SDL_GetDesktopDisplayMode(s, &desktop_mode)) {
+ log_sdl_error(LOG_WARN, "SDL_GetDesktopDisplayMode");
+ } else {
+ log_debug("Desktop mode: %ix%i@%iHz", desktop_mode.w, desktop_mode.h, desktop_mode.refresh_rate);
+ screenspace_max_size.w = desktop_mode.w;
+ screenspace_max_size.h = desktop_mode.h;
+ screenspace_max_size = coords_ext_pixels_to_screen(screenspace_max_size);
+ log_debug("Scaled screen-space bounds: %ix%i", screenspace_max_size.w, screenspace_max_size.h);
+ }
+
+ for(int i = 0; i < SDL_GetNumDisplayModes(s); ++i) {
+ SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 };
+
+ if(SDL_GetDisplayMode(s, i, &mode) != 0) {
+ log_sdl_error(LOG_WARN, "SDL_GetDisplayMode");
+ } else {
+ log_debug("Display mode #%i: %ix%i@%iHz", i, mode.w, mode.h, mode.refresh_rate);
+
+ video_add_mode_fullscreen((IntExtent) { mode.w, mode.h }, screenspace_min_size, screenspace_max_size);
+ fullscreen_available = true;
+
+ if(has_windowed_modes) {
+ FloatExtent vp = video_get_viewport_size_for_framebuffer((IntExtent) { mode.w, mode.h });
+ video_add_mode_windowed(round_viewport_size(vp), screenspace_min_size, screenspace_max_size);
+
+ // the ratio is always constant, so we need to check only 1 dimension
+ if(vp.w > largest_fullscreen_viewport.w) {
+ largest_fullscreen_viewport = vp;
+ }
+ }
+ }
+ }
+ }
+
+ if(has_windowed_modes) {
+ // Insert some more windowed modes derived from our "ideal" resolution.
+ // This is the resolution that the assets are optimized for.
+ float ideal_factor = 2;
+ FloatExtent ideal_resolution = { SCREEN_W * ideal_factor, SCREEN_H * ideal_factor };
+
+ if(largest_fullscreen_viewport.w == 0) {
+ // no way to determine the upper bound; guess it
+ largest_fullscreen_viewport = ideal_resolution;
+ }
+
+ IntExtent screenspace_max_size = coords_ext_pixels_to_screen(round_viewport_size(largest_fullscreen_viewport));
+
+ float scaling_factor = 0.5;
+ float scaling_factor_step = 0.2;
+
+ while(ideal_resolution.w * scaling_factor <= largest_fullscreen_viewport.w) {
+ FloatExtent vp = {
+ ideal_resolution.w * scaling_factor,
+ ideal_resolution.h * scaling_factor,
+ };
+ IntExtent pix_vp = round_viewport_size(vp);
+ video_add_mode_windowed(pix_vp, screenspace_min_size, screenspace_max_size);
+ scaling_factor += scaling_factor_step;
+ }
+
+ // Finally add the worst size we will tolerate, in case we haven't already.
+ video_add_mode_windowed((IntExtent) { VIDEO_MIN_WIDTH, VIDEO_MIN_HEIGHT }, screenspace_min_size, screenspace_max_size);
+ }
+
+ dynarray_compact(&video.fs_modes);
+ dynarray_compact(&video.win_modes);
+
+ dynarray_qsort(&video.fs_modes, video_compare_modes);
+ dynarray_qsort(&video.win_modes, video_compare_modes);
+
+ if(!fullscreen_available) {
+ log_warn("No available fullscreen modes");
+ config_set_int(CONFIG_FULLSCREEN, false);
+ }
+}
+
+static void video_update_scaling_factor(void) {
+ // NOTE: must query the main framebuffer explicitly here; postprocess buffers may have outdated information.
+ IntExtent main_fb = r_framebuffer_get_size(NULL);
+ assert(main_fb.w > 0);
+ double scaling_factor = (double)main_fb.w / video.current.width;
+
+ if(scaling_factor != video.scaling_factor) {
+ log_debug("Scaling factor updated: %f --> %f", video.scaling_factor, scaling_factor);
+ video.scaling_factor = scaling_factor;
+ video_update_mode_lists();
+ }
+}
+
void video_get_viewport_size(float *width, float *height) {
IntExtent fb = video_get_screen_framebuffer_size();
FloatExtent vp = video_get_viewport_size_for_framebuffer(fb);
@@ -178,45 +377,17 @@ static void video_update_vsync(void) {
}
}
-static uint32_t get_fullscreen_flag(void) {
- if(config_get_int(CONFIG_FULLSCREEN_DESKTOP)) {
- return SDL_WINDOW_FULLSCREEN_DESKTOP;
- } else {
- return SDL_WINDOW_FULLSCREEN;
- }
-}
-
-static void video_check_fullscreen_sanity(void) {
- SDL_DisplayMode mode;
-
- if(SDL_GetCurrentDisplayMode(SDL_GetWindowDisplayIndex(video.window), &mode)) {
- log_sdl_error(LOG_WARN, "SDL_GetCurrentDisplayMode");
- return;
- }
-
- if(video.current.width != mode.w || video.current.height != mode.h) {
- log_error("BUG: window is not actually fullscreen after modesetting. Video mode: %ix%i, window size: %ix%i",
- mode.w, mode.h, video.current.width, video.current.height);
- }
-}
-
static void video_update_mode_settings(void) {
SDL_ShowCursor(false);
video_update_vsync();
SDL_GetWindowSize(video.window, &video.current.width, &video.current.height);
video_set_viewport();
events_emit(TE_VIDEO_MODE_CHANGED, 0, NULL, NULL);
-
- if(video_is_fullscreen() && !config_get_int(CONFIG_FULLSCREEN_DESKTOP)) {
- video_check_fullscreen_sanity();
- }
}
-static const char* modeflagsstr(uint32_t flags) {
- if(WINFLAGS_IS_FAKE_FULLSCREEN(flags)) {
- return "fake fullscreen";
- } else if(WINFLAGS_IS_REAL_FULLSCREEN(flags)) {
- return "true fullscreen";
+static const char *modeflagsstr(uint32_t flags) {
+ if(flags & SDL_WINDOW_FULLSCREEN_DESKTOP) {
+ return "fullscreen";
} else if(flags & SDL_WINDOW_RESIZABLE) {
return "windowed, resizable";
} else {
@@ -241,7 +412,8 @@ static void video_new_window_internal(uint display, uint w, uint h, uint32_t fla
if(video.window) {
SDL_ShowWindow(video.window);
- SDL_SetWindowMinimumSize(video.window, SCREEN_W / 4, SCREEN_H / 4);
+ IntExtent min_size = coords_ext_pixels_to_screen((IntExtent) { VIDEO_MIN_WIDTH, VIDEO_MIN_HEIGHT });
+ SDL_SetWindowMinimumSize(video.window, min_size.w, min_size.h);
video_update_mode_settings();
return;
}
@@ -255,10 +427,10 @@ static void video_new_window_internal(uint display, uint w, uint h, uint32_t fla
}
static void video_new_window(uint display, uint w, uint h, bool fs, bool resizable) {
- uint32_t flags = 0;
+ uint32_t flags = SDL_WINDOW_ALLOW_HIGHDPI;
if(fs) {
- flags |= get_fullscreen_flag();
+ flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
} else if(resizable && video.backend != VIDEO_BACKEND_EMSCRIPTEN) {
flags |= SDL_WINDOW_RESIZABLE;
}
@@ -278,28 +450,8 @@ static void video_new_window(uint display, uint w, uint h, bool fs, bool resizab
SDL_RaiseWindow(video.window);
}
-static bool video_set_display_mode(uint display, uint w, uint h) {
- SDL_DisplayMode closest, target = { .w = w, .h = h };
-
- if(!SDL_GetClosestDisplayMode(display, &target, &closest)) {
- log_error("No available display modes for %ix%i on display %i", w, h, display);
- return false;
- }
-
- if(closest.w != w || closest.h != h) {
- log_warn("Can't use %ix%i, closest available is %ix%i", w, h, closest.w, closest.h);
- }
-
- if(SDL_SetWindowDisplayMode(video.window, &closest)) {
- log_error("Failed to set display mode for %ix%i on display %i: %s", closest.w, closest.h, display, SDL_GetError());
- return false;
- }
-
- return true;
-}
-
static void video_set_fullscreen_internal(bool fullscreen) {
- uint32_t flags = fullscreen ? get_fullscreen_flag() : 0;
+ uint32_t flags = fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0;
events_pause_keyrepeat();
if(SDL_SetWindowFullscreen(video.window, flags) < 0) {
@@ -333,9 +485,7 @@ void video_set_mode(uint display, uint w, uint h, bool fs, bool resizable) {
}
if(size_changed) {
- if(fs && !config_get_int(CONFIG_FULLSCREEN_DESKTOP)) {
- video_set_display_mode(display, w, h);
- } else if(video.backend == VIDEO_BACKEND_X11) {
+ if(video.backend == VIDEO_BACKEND_X11) {
// XXX: I would like to use SDL_SetWindowSize for size changes, but apparently it's impossible to reliably detect
// when it fails to actually resize the window. For example, a tiling WM (awesome) may be getting in its way
// and we'd never know. SDL_GL_GetDrawableSize/SDL_GetWindowSize aren't helping as of SDL 2.0.5.
@@ -387,7 +537,7 @@ void video_set_display(uint idx) {
);
}
-static void* video_screenshot_task(void *arg) {
+static void *video_screenshot_task(void *arg) {
ScreenshotTaskData *tdata = arg;
pixmap_convert_inplace_realloc(&tdata->image, PIXMAP_FORMAT_RGB8);
@@ -499,7 +649,7 @@ bool video_is_resizable(void) {
}
bool video_is_fullscreen(void) {
- return WINFLAGS_IS_FULLSCREEN(SDL_GetWindowFlags(video.window));
+ return SDL_GetWindowFlags(video.window) & SDL_WINDOW_FULLSCREEN_DESKTOP;
}
static void video_init_sdl(void) {
@@ -676,8 +826,6 @@ uint video_current_display(void) {
}
void video_init(void) {
- bool fullscreen_available = false;
-
video_init_sdl();
const char *driver = SDL_GetCurrentVideoDriver();
@@ -707,72 +855,10 @@ void video_init(void) {
video.backend = VIDEO_BACKEND_OTHER;
}
+ video.scaling_factor = 0;
+
r_init();
- dynarray_ensure_capacity(&video.fs_modes, 16);
- dynarray_ensure_capacity(&video.win_modes, 16);
-
- bool has_windowed_modes = (video_query_capability(VIDEO_CAP_FULLSCREEN) != VIDEO_ALWAYS_ENABLED);
- FloatExtent largest_fullscreen_viewport = { 0, 0 };
-
- // Register all resolutions that are available in fullscreen and their corresponding windowed modes.
- for(int s = 0; s < video_num_displays(); ++s) {
- log_info("Found display #%i: %s", s, video_display_name(s));
- for(int i = 0; i < SDL_GetNumDisplayModes(s); ++i) {
- SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 };
-
- if(SDL_GetDisplayMode(s, i, &mode) != 0) {
- log_sdl_error(LOG_WARN, "SDL_GetDisplayMode");
- } else {
- video_add_mode_fullscreen(mode.w, mode.h);
- fullscreen_available = true;
-
- if(has_windowed_modes) {
- FloatExtent vp = video_get_viewport_size_for_framebuffer((IntExtent) { mode.w, mode.h });
- video_add_mode_windowed(vp.w, vp.h);
-
- // the ratio is always constant, so we need to check only 1 dimension
- if(vp.w > largest_fullscreen_viewport.w) {
- largest_fullscreen_viewport = vp;
- }
- }
- }
- }
- }
-
- if(!fullscreen_available) {
- log_warn("No available fullscreen modes");
- config_set_int(CONFIG_FULLSCREEN, false);
- }
-
- if(has_windowed_modes) {
- // Insert some more windowed modes derived from our "ideal" resolution.
- // This is the resolution that the assets are optimized for.
- FloatExtent ideal_resolution = { SCREEN_W * 2, SCREEN_H * 2 };
-
- if(largest_fullscreen_viewport.w == 0) {
- // no way to determine the upper bound; guess it
- largest_fullscreen_viewport = ideal_resolution;
- }
-
- float scaling_factor = 0.5;
- float scaling_factor_step = 0.2;
-
- while(ideal_resolution.w * scaling_factor <= largest_fullscreen_viewport.w) {
- uint w = ideal_resolution.w * scaling_factor;
- uint h = ideal_resolution.h * scaling_factor;
- video_add_mode_windowed(w, h);
- scaling_factor += scaling_factor_step;
- }
- }
-
- dynarray_compact(&video.fs_modes);
- dynarray_compact(&video.win_modes);
-
- // sort it, mainly for the options menu
- dynarray_qsort(&video.fs_modes, video_compare_modes);
- dynarray_qsort(&video.win_modes, video_compare_modes);
-
video_set_mode(
config_get_int(CONFIG_VID_DISPLAY),
config_get_int(CONFIG_VID_WIDTH),
@@ -781,6 +867,8 @@ void video_init(void) {
config_get_int(CONFIG_VID_RESIZABLE)
);
+ video_update_scaling_factor();
+
events_register_handler(&(EventHandler) {
.proc = video_handle_window_event,
.priority = EPRIO_SYSTEM,
@@ -798,12 +886,12 @@ void video_init(void) {
void video_post_init(void) {
fbmgr_init();
- v_postprocess = video_postprocess_init();
+ video.postprocess = video_postprocess_init();
r_framebuffer(video_get_screen_framebuffer());
}
void video_shutdown(void) {
- video_postprocess_shutdown(v_postprocess);
+ video_postprocess_shutdown(video.postprocess);
fbmgr_shutdown();
events_unregister_handler(video_handle_window_event);
events_unregister_handler(video_handle_config_event);
@@ -815,11 +903,11 @@ void video_shutdown(void) {
}
Framebuffer *video_get_screen_framebuffer(void) {
- return video_postprocess_get_framebuffer(v_postprocess);
+ return video_postprocess_get_framebuffer(video.postprocess);
}
void video_swap_buffers(void) {
- Framebuffer *pp_fb = video_postprocess_render(v_postprocess);
+ Framebuffer *pp_fb = video_postprocess_render(video.postprocess);
if(pp_fb) {
r_flush_sprites();
@@ -866,3 +954,7 @@ uint video_get_num_modes(bool fullscreen) {
VideoMode video_get_current_mode(void) {
return video.current;
}
+
+double video_get_scaling_factor(void) {
+ return video.scaling_factor;
+}
diff --git a/src/video.h b/src/video.h
index a0b5fbbd..4410c6a1 100644
--- a/src/video.h
+++ b/src/video.h
@@ -11,17 +11,11 @@
#include "taisei.h"
-// FIXME: This is just for IntRect, which probably should be placed elsewhere.
#include "util/geometry.h"
-
#include "renderer/api.h"
#include
-#define WINFLAGS_IS_FULLSCREEN(f) ((f) & SDL_WINDOW_FULLSCREEN_DESKTOP)
-#define WINFLAGS_IS_FAKE_FULLSCREEN(f) (WINFLAGS_IS_FULLSCREEN(f) == SDL_WINDOW_FULLSCREEN_DESKTOP)
-#define WINFLAGS_IS_REAL_FULLSCREEN(f) (WINFLAGS_IS_FULLSCREEN(f) == SDL_WINDOW_FULLSCREEN)
-
#define WINDOW_TITLE "Taisei Project"
#define VIDEO_ASPECT_RATIO ((double)SCREEN_W/SCREEN_H)
@@ -33,9 +27,18 @@ enum {
#define SCREEN_SIZE { SCREEN_W, SCREEN_H }
-typedef struct VideoMode {
- int width;
- int height;
+typedef union VideoMode {
+ // NOTE: These really should be floats, since this represents abstract screen coordinates, not pixels.
+ // However, SDL's API expects integers everywhere, so it does not really make sense.
+
+ struct {
+ // TODO: get rid of this and just typedef to IntExtent?
+
+ int width;
+ int height;
+ };
+
+ IntExtent as_int_extent;
} VideoMode;
typedef enum VideoBackend {
@@ -82,5 +85,6 @@ VideoBackend video_get_backend(void);
VideoMode video_get_mode(uint idx, bool fullscreen);
uint video_get_num_modes(bool fullscreen);
VideoMode video_get_current_mode(void);
+double video_get_scaling_factor(void);
#endif // IGUARD_video_h