win32/vulkan: a borderless fullscreen toggle keeps its window and its drivers

Toggling fullscreen restarted everything. The log of one F press on
Windows with Vulkan: the video thread stopped and started, the device
and the shader chain rebuilt, the audio driver closed and reopened its
pin, the input and joypad drivers freed and started, the controller
detected and configured again, and the window destroyed and created.
For exclusive fullscreen the window has to go, since the display mode
changes under it. For borderless fullscreen - Windowed Fullscreen,
the default - the only thing that differs between the two states is
the window's style, position and size.

win32_window_set_fullscreen() changes those on the window that is
there: the style, the menu bar (which belongs to the windowed state),
and a position and size worked out the way a new window's are. Going
windowed, the window returns to where it stood before it went
fullscreen, to the saved position if positions are saved, or to the
middle of its monitor if it has never been windowed. The video driver
hears of it as it hears of any resize, through WM_SIZE, and rebuilds
its swapchain for the new size. Exclusive fullscreen is refused and
left to the restart.

It runs through the path that already exists for changing the video
mode: the toggle calls video_driver_set_video_mode(), the threaded
video wrapper carries that to the thread that owns the window, and
the Windows Vulkan context, finding a window already up, restyles it
instead of creating another. A context says it can do this with
GFX_CTX_FLAGS_FULLSCREEN_IN_PLACE; the toggle uses it when Windowed
Fullscreen is on and the context has the flag, and restarts the
drivers as before otherwise. Only the Windows Vulkan context has it
so far.

The size a window is asked for was worked out inside
video_driver_init_internal(); that is video_driver_window_size() now,
so the toggle asks for the size a restart would have.

samples/gfx/win32_window_proc drives it on a real window under Wine:
twenty toggles on one window, which must stay the same window, wear
the right style, be the monitor's size when fullscreen and the asked
size when windowed, and raise a resize each time; and exclusive
fullscreen must be refused. The stand-in video state gets a core
geometry for this - without one the window code limits a window to a
width too small to hold it - and the recorded trace of the old window
procedures is unchanged by that.

Compiled for Windows with mingw (win32_common.c, the Vulkan context
and driver, video_driver.c, retroarch.c). Not run on Windows, and the
Wine test has no GPU in it: what the Vulkan driver does when its
window changes size under it this way is the same code as a resize by
hand, but has not been seen with this.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-10-03 03:45:38 +02:00
1 parent 8aacd31c22
commit 35f2d183dc
11 files changed
+384 -110

No files matched your search

+98
View File
@@ -2138,6 +2138,104 @@ bool win32_set_video_mode(void *data,
return true;
}
/* Takes the window that is already there between windowed and
* borderless fullscreen: its style, position and size change, and it
* stays the same window.
*
* A fullscreen toggle used to mean a full driver restart - window,
* video device, shaders, audio, input drivers, controllers detected
* again - because the only way to a window of the other kind was to
* destroy this one and create another. For a borderless fullscreen
* window nothing but the window differs. The size it ends up with
* reaches the video driver as any resize does, through WM_SIZE.
*
* Not for exclusive fullscreen, which changes the display mode: that
* answers false and is left to the restart.
*
* Has to run on the thread that owns the window; a video driver's
* set_video_mode does. */
bool win32_window_set_fullscreen(unsigned dims, bool fullscreen)
{
DWORD style;
RECT mon_rect;
RECT rect;
MONITORINFOEX current_mon;
int x, y;
unsigned width = VIDEO_SCALE_W(dims);
unsigned height = VIDEO_SCALE_H(dims);
unsigned mon_id = 0;
HMONITOR hm_to_use = NULL;
HWND hwnd = main_window.hwnd;
settings_t *settings = config_get_ptr();
win32_common_state_t *g_win32 = (win32_common_state_t*)&win32_st;
/* where the window stood before it last went fullscreen */
static RECT windowed_rect;
static HWND windowed_rect_of;
if (!hwnd || !settings->bools.video_windowed_fullscreen)
return false;
/* fullscreen on the monitor the window is on, as a restart has it */
win32_monitor_last = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
win32_monitor_info(&current_mon, &hm_to_use, &mon_id);
mon_rect = current_mon.rcMonitor;
rect.left = 0;
rect.top = 0;
rect.right = 0;
rect.bottom = 0;
if (fullscreen && !(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_POPUP))
{
GetWindowRect(hwnd, &windowed_rect);
windowed_rect_of = hwnd;
}
win32_set_style(&current_mon, &hm_to_use, &width, &height,
fullscreen, true, &rect, &mon_rect, &style);
if (fullscreen)
{
/* the menu bar belongs to the windowed state */
HMENU menu = GetMenu(hwnd);
if (menu)
{
SetMenu(hwnd, NULL);
DestroyMenu(menu);
}
x = mon_rect.left;
y = mon_rect.top;
}
else if (g_win32->pos_set)
{
x = VIDEO_POS_X(g_win32->pos);
y = VIDEO_POS_Y(g_win32->pos);
}
else if (windowed_rect_of == hwnd)
{
x = windowed_rect.left;
y = windowed_rect.top;
}
else
{
/* never windowed yet: the middle of its monitor */
x = mon_rect.left + ((mon_rect.right - mon_rect.left) - (int)width) / 2;
y = mon_rect.top + ((mon_rect.bottom - mon_rect.top) - (int)height) / 2;
if (x < mon_rect.left)
x = mon_rect.left;
if (y < mon_rect.top)
y = mon_rect.top;
}
SetWindowLongPtr(hwnd, GWL_STYLE, style | WS_VISIBLE);
SetWindowPos(hwnd, HWND_TOP, x, y, width, height,
SWP_FRAMECHANGED | SWP_NOOWNERZORDER);
win32_set_window(&width, &height, fullscreen, true, &rect);
return true;
}
#endif
bool win32_get_client_rect(RECT* rect)
+5
View File
@@ -105,6 +105,11 @@ bool win32_set_video_mode(void *data,
unsigned dims,
bool fullscreen);
/* Takes the existing window between windowed and borderless fullscreen
* without destroying it. False if there is no window, or fullscreen is
* the exclusive kind. Call on the thread that owns the window. */
bool win32_window_set_fullscreen(unsigned dims, bool fullscreen);
bool win32_suspend_screensaver(void *data, bool enable);
void win32_show_cursor(void *data, bool state);
+9 -3
View File
@@ -7035,9 +7035,15 @@ static void vulkan_set_video_mode(void *data,
bool fullscreen)
{
vk_t *vk = (vk_t*)data;
if (vk->ctx_driver->set_video_mode)
vk->ctx_driver->set_video_mode(vk->ctx_data,
dims, fullscreen);
if ( vk->ctx_driver->set_video_mode
&& vk->ctx_driver->set_video_mode(vk->ctx_data,
dims, fullscreen))
{
if (fullscreen)
vk->flags |= VK_FLAG_FULLSCREEN;
else
vk->flags &= ~VK_FLAG_FULLSCREEN;
}
}
static void vulkan_set_viewport(void *data, unsigned dims,
+17
View File
@@ -218,6 +218,21 @@ static bool gfx_ctx_w_vk_set_video_mode(void *data,
unsigned dims,
bool fullscreen)
{
/* With a window already up this is a fullscreen toggle on it: the
* window is restyled where it stands, and the swapchain follows
* when the resize reaches check_window, as for any resize. Where
* that is refused - exclusive fullscreen - this goes on as it
* always has. */
if ( win32_get_window()
&& win32_window_set_fullscreen(dims, fullscreen))
{
if (fullscreen)
win32_vk.flags |= VK_DATA_FLAG_FULLSCREEN;
else
win32_vk.flags &= ~VK_DATA_FLAG_FULLSCREEN;
return true;
}
if (fullscreen)
win32_vk.flags |= VK_DATA_FLAG_FULLSCREEN;
else
@@ -294,6 +309,8 @@ static uint32_t gfx_ctx_w_vk_get_flags(void *data)
&win32_vk.context.supports_adaptive_vsync))
BIT32_SET(flags, GFX_CTX_FLAGS_ADAPTIVE_VSYNC);
BIT32_SET(flags, GFX_CTX_FLAGS_FULLSCREEN_IN_PLACE);
#if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS)
BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG);
#endif
+5 -1
View File
@@ -371,7 +371,11 @@ enum display_flags
/* Set by a context driver whose default framebuffer is 10-bit
* Rec.2020 PQ (HDR10, e.g. a KMS scanout with HDR metadata): the
* video driver encodes its frame to PQ instead of scRGB. */
GFX_CTX_FLAGS_HDR10_FRAMEBUFFER
GFX_CTX_FLAGS_HDR10_FRAMEBUFFER,
/* The window can go between windowed and borderless fullscreen where
* it stands, through set_video_mode, with the driver seeing only a
* resize: a fullscreen toggle need not restart the drivers. */
GFX_CTX_FLAGS_FULLSCREEN_IN_PLACE
};
enum shader_uniform_type
+134 -103
View File
@@ -5747,111 +5747,21 @@ const char* video_driver_get_gpu_api_version_string(void)
return video_st->gpu_api_version_string;
}
bool video_driver_init_internal(bool *video_is_threaded, bool verbosity_enabled)
/* The size the video driver asks its window for: the configured
* fullscreen size, or for a window the saved or custom size, else the
* core's geometry times the window scale, kept within the largest
* window allowed. Used when the driver starts and when a fullscreen
* toggle changes the window it already has. */
static void video_driver_window_size(settings_t *settings,
video_driver_state_t *video_st, bool fullscreen,
unsigned *out_width, unsigned *out_height)
{
video_info_t video;
unsigned max_dim, scale, width, height;
video_viewport_settings_t *custom_vp = NULL;
input_driver_t *tmp = NULL;
static uint16_t dummy_pixels[32] = {0};
runloop_state_t *runloop_st = runloop_state_get_ptr();
settings_t *settings = config_get_ptr();
struct retro_game_geometry *geom = &video_st->av_info.geometry;
unsigned int rotation = retroarch_get_rotation();
unsigned width = 0;
unsigned height = 0;
input_driver_state_t *input_st = input_state_get_ptr();
video_driver_state_t *video_st = &video_driver_st;
struct retro_game_geometry *geom = &video_st->av_info.geometry;
const enum retro_pixel_format
video_driver_pix_fmt = video_st->pix_fmt;
unsigned int rotation = retroarch_get_rotation();
#ifdef HAVE_VIDEO_FILTER
const char *path_softfilter_plugin = settings->paths.path_softfilter_plugin;
#endif
#ifdef HAVE_VIDEO_FILTER
/* Bound before any driver or wrapper exists: under threaded video
* the OSD fonts live on the video thread, and the font driver
* reaches ra-video state through this capture, not the getter. */
font_driver_bind_video_state(video_state_get_ptr());
/* Init video filter only when game is running */
if (( runloop_st->current_core.flags & RETRO_CORE_FLAG_GAME_LOADED)
&& (path_softfilter_plugin && *path_softfilter_plugin))
video_driver_init_filter(video_driver_pix_fmt, settings);
#endif
#if defined(HAVE_THREADS) && !defined(FRAME_CACHE_HAZARDS)
/* Lazily allocate the cached-frame tuple lock on first video
* driver init. Kept across video driver reinits (HDR toggle,
* fullscreen change) and for the rest of the process: the cache is
* still invalidated from retroarch_deinit_drivers() after the video
* driver itself is gone, so there is no point at which freeing
* this would be safe. */
if (!cached_frame_lock)
cached_frame_lock = slock_new();
#endif
max_dim = MAX(geom->max_width, geom->max_height);
scale = next_pow2(max_dim) / RARCH_SCALE_BASE;
scale = MAX(scale, 1);
#ifdef HAVE_VIDEO_FILTER
if (video_st->state_filter)
scale = video_st->state_scale;
#endif
strlcpy(aspectratio_lut[ASPECT_RATIO_CONFIG].name,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO_CONFIG),
sizeof(aspectratio_lut[ASPECT_RATIO_CONFIG].name));
strlcpy(aspectratio_lut[ASPECT_RATIO_CORE].name,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO_CORE_PROVIDED),
sizeof(aspectratio_lut[ASPECT_RATIO_CORE].name));
strlcpy(aspectratio_lut[ASPECT_RATIO_CUSTOM].name,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO_CUSTOM),
sizeof(aspectratio_lut[ASPECT_RATIO_CUSTOM].name));
strlcpy(aspectratio_lut[ASPECT_RATIO_FULL].name,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO_FULL),
sizeof(aspectratio_lut[ASPECT_RATIO_FULL].name));
/* Update core-dependent aspect ratio values. */
video_driver_set_viewport_square_pixel(geom, rotation);
video_driver_set_viewport_core();
video_driver_set_viewport_config(geom,
settings->floats.video_aspect_ratio,
settings->bools.video_aspect_ratio_auto);
/* Update CUSTOM viewport. */
custom_vp = &settings->video_vp_custom;
if (settings->uints.video_aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
{
float default_aspect = aspectratio_lut[ASPECT_RATIO_CORE].value;
aspectratio_lut[ASPECT_RATIO_CUSTOM].value =
(custom_vp->dims
&& VIDEO_SCALE_W(custom_vp->dims)
&& VIDEO_SCALE_H(custom_vp->dims))
? (float)VIDEO_SCALE_W(custom_vp->dims)
/ VIDEO_SCALE_H(custom_vp->dims) : default_aspect;
}
{
/* Guard against aspect ratio index possibly being out of bounds */
unsigned new_aspect_idx = settings->uints.video_aspect_ratio_idx;
if (new_aspect_idx > ASPECT_RATIO_END)
new_aspect_idx = settings->uints.video_aspect_ratio_idx = 0;
video_driver_aspect_ratio_put(&video_st->aspect_ratio_bits,
aspectratio_lut[new_aspect_idx].value);
}
/* Seed the viewport-parameter snapshot before the driver's init
* runs: drivers call video_driver_update_viewport() from inside
* init. This must come after the aspect ratio above is stored -
* published any earlier, the snapshot carries the previous (on a
* cold start, zero) aspect, and init-time viewports come out
* 0 pixels wide until the first frame republishes. */
video_driver_publish_vp_params();
if ( settings->bools.video_fullscreen
|| ((uint32_t)retro_atomic_load_relaxed_int(&video_st->flags) & VIDEO_FLAG_FORCE_FULLSCREEN))
if (fullscreen)
{
width = settings->uints.video_fullscreen_x;
height = settings->uints.video_fullscreen_y;
@@ -5988,6 +5898,127 @@ bool video_driver_init_internal(bool *video_is_threaded, bool verbosity_enabled)
}
}
*out_width = width;
*out_height = height;
}
unsigned video_driver_window_dims(bool fullscreen)
{
unsigned width = 0;
unsigned height = 0;
video_driver_window_size(config_get_ptr(), &video_driver_st,
fullscreen, &width, &height);
return VIDEO_SCALE_PACK(width, height);
}
bool video_driver_init_internal(bool *video_is_threaded, bool verbosity_enabled)
{
video_info_t video;
unsigned max_dim, scale, width, height;
video_viewport_settings_t *custom_vp = NULL;
input_driver_t *tmp = NULL;
static uint16_t dummy_pixels[32] = {0};
runloop_state_t *runloop_st = runloop_state_get_ptr();
settings_t *settings = config_get_ptr();
input_driver_state_t *input_st = input_state_get_ptr();
video_driver_state_t *video_st = &video_driver_st;
struct retro_game_geometry *geom = &video_st->av_info.geometry;
const enum retro_pixel_format
video_driver_pix_fmt = video_st->pix_fmt;
unsigned int rotation = retroarch_get_rotation();
#ifdef HAVE_VIDEO_FILTER
const char *path_softfilter_plugin = settings->paths.path_softfilter_plugin;
#endif
#ifdef HAVE_VIDEO_FILTER
/* Bound before any driver or wrapper exists: under threaded video
* the OSD fonts live on the video thread, and the font driver
* reaches ra-video state through this capture, not the getter. */
font_driver_bind_video_state(video_state_get_ptr());
/* Init video filter only when game is running */
if (( runloop_st->current_core.flags & RETRO_CORE_FLAG_GAME_LOADED)
&& (path_softfilter_plugin && *path_softfilter_plugin))
video_driver_init_filter(video_driver_pix_fmt, settings);
#endif
#if defined(HAVE_THREADS) && !defined(FRAME_CACHE_HAZARDS)
/* Lazily allocate the cached-frame tuple lock on first video
* driver init. Kept across video driver reinits (HDR toggle,
* fullscreen change) and for the rest of the process: the cache is
* still invalidated from retroarch_deinit_drivers() after the video
* driver itself is gone, so there is no point at which freeing
* this would be safe. */
if (!cached_frame_lock)
cached_frame_lock = slock_new();
#endif
max_dim = MAX(geom->max_width, geom->max_height);
scale = next_pow2(max_dim) / RARCH_SCALE_BASE;
scale = MAX(scale, 1);
#ifdef HAVE_VIDEO_FILTER
if (video_st->state_filter)
scale = video_st->state_scale;
#endif
strlcpy(aspectratio_lut[ASPECT_RATIO_CONFIG].name,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO_CONFIG),
sizeof(aspectratio_lut[ASPECT_RATIO_CONFIG].name));
strlcpy(aspectratio_lut[ASPECT_RATIO_CORE].name,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO_CORE_PROVIDED),
sizeof(aspectratio_lut[ASPECT_RATIO_CORE].name));
strlcpy(aspectratio_lut[ASPECT_RATIO_CUSTOM].name,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO_CUSTOM),
sizeof(aspectratio_lut[ASPECT_RATIO_CUSTOM].name));
strlcpy(aspectratio_lut[ASPECT_RATIO_FULL].name,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_ASPECT_RATIO_FULL),
sizeof(aspectratio_lut[ASPECT_RATIO_FULL].name));
/* Update core-dependent aspect ratio values. */
video_driver_set_viewport_square_pixel(geom, rotation);
video_driver_set_viewport_core();
video_driver_set_viewport_config(geom,
settings->floats.video_aspect_ratio,
settings->bools.video_aspect_ratio_auto);
/* Update CUSTOM viewport. */
custom_vp = &settings->video_vp_custom;
if (settings->uints.video_aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
{
float default_aspect = aspectratio_lut[ASPECT_RATIO_CORE].value;
aspectratio_lut[ASPECT_RATIO_CUSTOM].value =
(custom_vp->dims
&& VIDEO_SCALE_W(custom_vp->dims)
&& VIDEO_SCALE_H(custom_vp->dims))
? (float)VIDEO_SCALE_W(custom_vp->dims)
/ VIDEO_SCALE_H(custom_vp->dims) : default_aspect;
}
{
/* Guard against aspect ratio index possibly being out of bounds */
unsigned new_aspect_idx = settings->uints.video_aspect_ratio_idx;
if (new_aspect_idx > ASPECT_RATIO_END)
new_aspect_idx = settings->uints.video_aspect_ratio_idx = 0;
video_driver_aspect_ratio_put(&video_st->aspect_ratio_bits,
aspectratio_lut[new_aspect_idx].value);
}
/* Seed the viewport-parameter snapshot before the driver's init
* runs: drivers call video_driver_update_viewport() from inside
* init. This must come after the aspect ratio above is stored -
* published any earlier, the snapshot carries the previous (on a
* cold start, zero) aspect, and init-time viewports come out
* 0 pixels wide until the first frame republishes. */
video_driver_publish_vp_params();
video_driver_window_size(settings, video_st,
settings->bools.video_fullscreen
|| ((uint32_t)retro_atomic_load_relaxed_int(&video_st->flags) & VIDEO_FLAG_FORCE_FULLSCREEN),
&width, &height);
if (width && height)
RARCH_LOG("[Video] Set video size to: %ux%u.\n", width, height);
else
+5
View File
@@ -1681,6 +1681,11 @@ void video_driver_shader_deferred_tick(void);
bool video_driver_set_rotation(unsigned rotation);
/* The size, packed with VIDEO_SCALE_PACK, the video driver would ask
* its window for in the given state; what a fullscreen toggle on the
* existing window resizes it to. */
unsigned video_driver_window_dims(bool fullscreen);
bool video_driver_set_video_mode(unsigned dims, bool fullscreen);
/* Appends printf-formatted text at @len to a statistics buffer of
+16 -3
View File
@@ -5699,9 +5699,22 @@ bool command_event(enum event_command cmd, void *data)
if (ra_is_forced_fs)
video_driver_modify_disp_flags(0, VIDEO_FLAG_FORCE_FULLSCREEN);
/* If we go fullscreen we drop all drivers and
* reinitialize to be safe. */
command_event(CMD_EVENT_REINIT, NULL);
/* Where the window can change state in place - borderless
* fullscreen on a context that says so - that is all a
* toggle is: the window is restyled and the video driver
* sees a resize. Device, shaders, audio and input drivers
* stay as they are.
*
* Otherwise (exclusive fullscreen, which changes the
* display mode, or a context without the means) all
* drivers are dropped and reinitialised, as before. */
if ( !settings->bools.video_windowed_fullscreen
|| !video_driver_test_all_flags(
GFX_CTX_FLAGS_FULLSCREEN_IN_PLACE)
|| !video_driver_set_video_mode(
video_driver_window_dims(new_fullscreen_state),
new_fullscreen_state))
command_event(CMD_EVENT_REINIT, NULL);
if (video_fullscreen)
{
if ( video_st->poke
@@ -2693,3 +2693,7 @@
== 20 fullscreen toggles
windows made 20, inited 20, gone again 20
window objects left behind: no
== 20 fullscreen toggles on one window
same window 20, right style 20, right size 20, resize seen 20
exclusive fullscreen left to a restart: yes
destroyed: window gone
@@ -190,6 +190,10 @@ ui_companion_driver_t ui_companion_win32;
void stubs_init(void)
{
/* a core's geometry: the window limits its size to twenty times
* it, and with none the limit is too small to hold a window */
stub_video_st.av_info.geometry.base_width = 320;
stub_video_st.av_info.geometry.base_height = 240;
stub_application.process_events = stub_process_events;
ui_companion_win32.application = &stub_application;
ui_companion_win32.window = &stub_window;
@@ -289,6 +289,93 @@ int main(int argc, char **argv)
(objects_last > objects_first) ? "yes" : "no");
}
#ifndef W32T_OLD
/* The same toggles on one window: win32_window_set_fullscreen()
* takes the window between windowed and borderless fullscreen where
* it stands. It has to stay the same window, wear the right style,
* end up the right size, and tell the video driver through a
* resize. (The twelve old procedures had no such thing: these lines
* of the expected trace are this function's own.) */
{
WNDCLASSEX wndclass;
HWND hwnd;
MONITORINFO mon;
int cycle, same = 0, styled = 0, sized = 0, resized = 0;
const int cycles = 20;
bool refused;
strlcpy(settings->arrays.input_driver, "raw",
sizeof(settings->arrays.input_driver));
settings->bools.video_fullscreen = false;
trace_on = false;
win32_window_reset();
win32_monitor_init();
memset(&wndclass, 0, sizeof(wndclass));
wndclass.lpfnWndProc = proc_for(FAM_VK, IN_WINRAW);
if ( win32_window_init(&wndclass, true, NULL)
&& win32_set_video_mode(NULL, VIDEO_SCALE_PACK(640, 480), false))
{
hwnd = win32_get_window();
pump();
memset(&mon, 0, sizeof(mon));
mon.cbSize = sizeof(mon);
GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST), &mon);
for (cycle = 0; cycle < cycles; cycle++)
{
RECT client;
bool fullscreen = !(cycle & 1);
bool popup;
win32_window_reset();
settings->bools.video_fullscreen = fullscreen;
if (!win32_window_set_fullscreen(VIDEO_SCALE_PACK(640, 480), fullscreen))
break;
pump();
if (win32_get_window() == hwnd && IsWindow(hwnd))
same++;
popup = (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_POPUP) != 0;
if (popup == fullscreen && IsWindowVisible(hwnd))
styled++;
GetClientRect(hwnd, &client);
if (fullscreen
? ( client.right == mon.rcMonitor.right - mon.rcMonitor.left
&& client.bottom == mon.rcMonitor.bottom - mon.rcMonitor.top)
: (client.right == 640 && client.bottom == 480))
sized++;
if (win32_get_flags() & WIN32_CMN_FLAG_RESIZED)
resized++;
}
/* exclusive fullscreen changes the display mode: not done in
* place, left to the driver restart */
settings->bools.video_windowed_fullscreen = false;
refused = !win32_window_set_fullscreen(VIDEO_SCALE_PACK(640, 480), true);
settings->bools.video_windowed_fullscreen = true;
win32_monitor_from_window();
win32_destroy_window();
pump();
trace_on = true;
trace("== %d fullscreen toggles on one window\n", cycles);
trace(" same window %d, right style %d, right size %d, resize seen %d\n",
same, styled, sized, resized);
trace(" exclusive fullscreen left to a restart: %s\n",
refused ? "yes" : "no");
trace(" destroyed: window %s\n", IsWindow(hwnd) ? "still there" : "gone");
}
else
{
trace_on = true;
trace("== fullscreen toggles on one window: no window\n");
}
}
#endif
out = fopen(argc > 1 ? argv[1] : "trace.txt", "wb");
if (!out)
return 2;