Game window stuck partially offscreen
Game window stuck partially offscreen
I'm trying to solve this really old WinQuake problem. When the window is dragged partially offscreen, the only ways to bring it back are to change the vid_window_x and vid_window_y cvars and changing the resolution.
In the qbismSuper8 builds thread there's some talk about it, but I've tried the proposed solution, and moving the VID_CheckWindowXY call didn't fix it. I've also tried messing around with various things in VID_RememberWindowPos, VID_Update and MainWndProc.
Does anyone know why exactly this bug happens?
In the qbismSuper8 builds thread there's some talk about it, but I've tried the proposed solution, and moving the VID_CheckWindowXY call didn't fix it. I've also tried messing around with various things in VID_RememberWindowPos, VID_Update and MainWndProc.
Does anyone know why exactly this bug happens?
Re: Game window stuck partially offscreen
In build 88 there, the window was snapped back to screen center whenever it went off the edge. I think it worked but was annoying for it to go all the way back. In the current build it is supposed to come back just enough to get the whole window on screen. I recall it worked at the time but I see it's getting stuck again. I may have done something in build 109 that breaks it. Or could it be some windows or driver update?
BTW, I tried to browse super8 svn on sourceforge but their new interface is missing dozens of commits. But everything is there using
TortoiseSVN.
BTW, I tried to browse super8 svn on sourceforge but their new interface is missing dozens of commits. But everything is there using
TortoiseSVN.
Re: Game window stuck partially offscreen
I've figured it out:
The lines that are commented out are just so the function can be turned into a qboolean type and return a value if needed for refactoring the rest of the code. Also, the SetWindowPos line most likely shouldn't be there, even if the rest of the code is refactored, but I've left it in there and commented it out anyway.
This code also centers the window horizontally if it's wider than the screen, and vertically if taller. So, by setting vid_config_x and vid_config_y to the same dimensions of the desktop resolution, the game is essentially displayed in fullscreen without switching the desktop/taskbar/etc. away.
Code: Select all
void VID_CheckWindowXY (void)
{
// mankrip - begin
RECT rect;
// qboolean changed = false;
if (GetWindowRect (mainwindow, &rect))
{
if (rect.right - rect.left > GetSystemMetrics (SM_CXSCREEN)) // window is wider than the screen (may happen due to window borders)
{
// if ( (int)vid_window_x.value != (GetSystemMetrics (SM_CXSCREEN) - (rect.right - rect.left)) / 2)
{
Cvar_SetValue ("vid_window_x", (GetSystemMetrics (SM_CXSCREEN) - (rect.right - rect.left)) / 2);
// changed = true;
}
}
else if (rect.left < 0)
{
Cvar_SetValue ("vid_window_x", 0.0f);
// changed = true;
}
else if (rect.right > GetSystemMetrics (SM_CXSCREEN))
{
Cvar_SetValue ("vid_window_x", GetSystemMetrics (SM_CXSCREEN) - (rect.right - rect.left));
// changed = true;
}
if (rect.bottom - rect.top > GetSystemMetrics (SM_CYSCREEN)) // window is taller than the screen (may happen due to window borders)
{
// if ( (int)vid_window_y.value != (GetSystemMetrics (SM_CYSCREEN) - (rect.bottom - rect.top)) / 2)
{
Cvar_SetValue ("vid_window_y", (GetSystemMetrics (SM_CYSCREEN) - (rect.bottom - rect.top)) / 2);
// changed = true;
}
}
else if (rect.top < 0)
{
Cvar_SetValue ("vid_window_y", 0.0f);
// changed = true;
}
else if (rect.bottom > GetSystemMetrics (SM_CYSCREEN))
{
Cvar_SetValue ("vid_window_y", GetSystemMetrics (SM_CYSCREEN) - (rect.bottom - rect.top));
// changed = true;
}
// if (changed)
// SetWindowPos (mainwindow, NULL, (int) vid_window_x.value, (int) vid_window_y.value, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME);
}
// return changed;
// mankrip - end
}
This code also centers the window horizontally if it's wider than the screen, and vertically if taller. So, by setting vid_config_x and vid_config_y to the same dimensions of the desktop resolution, the game is essentially displayed in fullscreen without switching the desktop/taskbar/etc. away.
Re: Game window stuck partially offscreen
That looks like it should work, but it didn't for me. I'm going to double check to see if I've got the check is in the right place.
Here's the code from super8 build 109, which also doesn't work. But I'm pretty sure it used to work, strange. It's got a guess for the window border size because I don't know how to determine it.
Here's the code from super8 build 109, which also doesn't work. But I'm pretty sure it used to work, strange. It's got a guess for the window border size because I don't know how to determine it.
Code: Select all
void VID_CheckWindowXY (void)
{
if ((int) vid_window_x.value > GetSystemMetrics (SM_CXSCREEN) - dd_window_width -10) //qbism HACK - what is actual?
Cvar_SetValue ("vid_window_x", GetSystemMetrics (SM_CXSCREEN) - dd_window_width -15);
if ((int) vid_window_y.value > GetSystemMetrics (SM_CYSCREEN) - dd_window_height -35)
Cvar_SetValue ("vid_window_y", GetSystemMetrics (SM_CYSCREEN) - dd_window_height -40);
if ((int) vid_window_x.value < 0)
Cvar_SetValue ("vid_window_x", 0.0);
if ((int) vid_window_y.value < 0)
Cvar_SetValue ("vid_window_y", 0.0);
}
Re: Game window stuck partially offscreen
The above isn't quite the "right way" to center a window.
Use SPI_GETWORKAREA gets the actual usable desktop area for the primary monitor (the taskbar, etc. aren't in the resulting rectangle).
This ends being a bit more important than in the past as displays any more have less vertical space.
Code: Select all
RECT workarea;
iSystemParametersInfo (SPI_GETWORKAREA, 0, &workarea, 0)
This ends being a bit more important than in the past as displays any more have less vertical space.
The night is young. How else can I annoy the world before sunsrise? Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Re: Game window stuck partially offscreen
SPI_GETWORKAREA is good to know. Is there a way to retrieve the window size including borders from the API? If the ddraw area is 640x480, the total window is around 660x520.
Re: Game window stuck partially offscreen
It's not convenient, but yeah ... (SCROLL a little to see the important part)qbism wrote:SPI_GETWORKAREA is good to know. Is there a way to retrieve the window size including borders from the API? If the ddraw area is 640x480, the total window is around 660x520.
Code: Select all
WINDOWINFO windowinfo;
RECT twindowArea;
windowinfo.cbSize = sizeof (WINDOWINFO);
GetWindowRect (w32_plat.mainwindow, &twindowArea); // Total window area + borders
GetWindowInfo (w32_plat.mainwindow, &windowinfo); // Total client area. Take window area and subtract client area, obv
host.windowArea.top = twindowArea.top;
host.windowArea.bottom = twindowArea.bottom;
host.windowArea.left = twindowArea.left;
host.windowArea.right = twindowArea.right;
host.clientArea.top = windowinfo.rcClient.top;
host.clientArea.bottom = windowinfo.rcClient.bottom;
host.clientArea.left = windowinfo.rcClient.left;
host.clientArea.right = windowinfo.rcClient.right;
host.mouse_center_x = host.clientArea.left + (RECT_WIDTH(host.clientArea) >> 1);
host.mouse_center_y = host.clientArea.top + (RECT_HEIGHT(host.clientArea) >> 1);
host.borderAreaWidth = RECT_WIDTH(host.windowArea) - RECT_WIDTH(host.clientArea); /// <-------
host.borderAreaHeight = RECT_HEIGHT(host.windowArea) - RECT_HEIGHT(host.clientArea); /// <---------- here
#define RECT_WIDTH(x) (x.right - x.left)
#define RECT_HEIGHT(x) (x.bottom - x.top)
I believe the above code has the bonus of working around something where Vista and maybe Windows 7+ lies about something (a pixel or 2 off), I was probably poking through an MH engine to see that note somewhere.
The night is young. How else can I annoy the world before sunsrise? Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Re: Game window stuck partially offscreen
As Baker pointed out, my code does; it's in the (rect.right - rect.left) and (rect.bottom - rect.top) parts.qbism wrote:Is there a way to retrieve the window size including borders from the API? If the ddraw area is 640x480, the total window is around 660x520.
I'm aware of that, but in this specific case, this is not what I want; I want the window's contents to fill the whole screen, including the taskbar area (even if the taskbar is displayed in front of it).Baker wrote:The above isn't quite the "right way" to center a window.
[...]
Use SPI_GETWORKAREA gets the actual usable desktop area for the primary monitor (the taskbar, etc. aren't in the resulting rectangle).
Anyway, your new info helped me to improve it a bit. This new version will also take the title bar in consideration:
Code: Select all
void VID_CheckWindowXY (void)
{
// mankrip - begin
RECT rect;
if (GetWindowRect (mainwindow, &rect))
{
int
border
, titlebar
;
// Baker - begin
WINDOWINFO windowinfo;
windowinfo.cbSize = sizeof (WINDOWINFO);
GetWindowInfo (mainwindow, &windowinfo); // Total client area. Take window area and subtract client area, obv
// Baker - end
border = (rect.right - rect.left) - (windowinfo.rcClient.right - windowinfo.rcClient.left); // both borders; one on the left, and one on the right
titlebar = (rect.bottom - rect.top) - (windowinfo.rcClient.bottom - windowinfo.rcClient.top) - border; // title bar, without top and bottom borders
border /= 2; // convert to a single border
if (rect.right - rect.left > GetSystemMetrics (SM_CXSCREEN)) // window is wider than the screen (may happen due to window borders)
Cvar_SetValue ("vid_window_x", (float) (border * -1));
else if (rect.left < 0)
Cvar_SetValue ("vid_window_x", 0.0f);
else if (rect.right > GetSystemMetrics (SM_CXSCREEN))
Cvar_SetValue ("vid_window_x", (float) (GetSystemMetrics (SM_CXSCREEN) - (rect.right - rect.left)));
if (rect.bottom - rect.top > GetSystemMetrics (SM_CYSCREEN)) // window is taller than the screen (may happen due to window borders)
Cvar_SetValue ("vid_window_y", (float) (-1 * (border + titlebar)));
else if (rect.top < 0)
Cvar_SetValue ("vid_window_y", 0.0f);
else if (rect.bottom > GetSystemMetrics (SM_CYSCREEN))
Cvar_SetValue ("vid_window_y", (float) (GetSystemMetrics (SM_CYSCREEN) - (rect.bottom - rect.top)));
}
// mankrip - end
}
And here's my Vid_Update; it also has a couple more fixes:
Code: Select all
void VID_Update (vrect_t *rects)
{
vrect_t rect;
RECT trect;
if (!vid_palettized && palette_changed)
{
palette_changed = false;
rect.x = 0;
rect.y = 0;
rect.width = vid.width;
rect.height = vid.height;
rect.pnext = NULL;
rects = ▭
}
VID_CheckWindowXY (); //qbism- put the check here, and nowhere else
if (firstupdate)
{
if ((_vid_default_mode_win.value != vid_default) &&
(!startwindowed || (_vid_default_mode_win.value < MODE_FULLSCREEN_DEFAULT)))
{
firstupdate = 0;
if (COM_CheckParm ("-resetwinpos"))
{
Cvar_SetValue ("vid_window_x", 0.0);
Cvar_SetValue ("vid_window_y", 0.0);
}
if ((_vid_default_mode_win.value < 0) ||
(_vid_default_mode_win.value >= nummodes))
{
Cvar_SetValue ("_vid_default_mode_win", windowed_default);
}
Cvar_SetValue ("vid_mode", _vid_default_mode_win.value);
}
}
// We've drawn the frame; copy it to the screen
FlipScreen (rects);
if (vid_testingmode)
{
if (realtime >= vid_testendtime)
{
VID_SetMode (vid_realmode, vid_curpal);
vid_testingmode = 0;
}
}
else
{
if ((int) vid_mode.value != vid_realmode)
{
VID_SetMode ((int) vid_mode.value, vid_curpal);
Cvar_SetValue ("vid_mode", (float) vid_modenum);
// so if mode set fails, we don't keep on
// trying to set that mode
vid_realmode = vid_modenum;
}
// mankrip - begin
else if (vid_realmode == MODE_SETTABLE_WINDOW)
{
if (modelist[MODE_SETTABLE_WINDOW].width != (int) vid_config_x.value
|| modelist[MODE_SETTABLE_WINDOW].height != (int) vid_config_y.value
)
VID_SetMode (vid_realmode, vid_curpal);
}
// mankrip - end
}
// handle the mouse state when windowed if that's changed
if (modestate == MS_WINDOWED)
{
// mankrip - begin
// the window should be moved after VID_SetMode is run, not before
if (firstupdate)
{
GetWindowRect (mainwindow, &trect);
if ((trect.left != (int) vid_window_x.value)
|| (trect.top != (int) vid_window_y.value))
SetWindowPos (mainwindow, NULL, (int) vid_window_x.value, (int) vid_window_y.value, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME);
}
// mankrip - end
{
if (key_dest != key_menu && !cl.paused && !cls.demoplayback) // mankrip
{
IN_ActivateMouse ();
IN_HideMouse ();
}
else
{
IN_DeactivateMouse ();
IN_ShowMouse ();
}
}
}
}
Re: Game window stuck partially offscreen
This updated VID_CheckWindowXY is working great. Suggested change to Vid_Update. EDIT - this is a hack if window is getting stuck when partially off-screen.
Code: Select all
void VID_Update (vrect_t *rects)
{
vrect_t rect;
RECT trect;
if (!vid_palettized && palette_changed)
{
palette_changed = false;
rect.x = 0;
rect.y = 0;
rect.width = vid.width;
rect.height = vid.height;
rect.pnext = NULL;
rects = ▭
}
if (firstupdate)
{
if ((vid_default_mode_win.value != vid_default) &&
(!startwindowed || (vid_default_mode_win.value < MODE_FULLSCREEN_DEFAULT)))
{
firstupdate = 0;
if (COM_CheckParm ("-resetwinpos"))
{
Cvar_SetValue ("vid_window_x", 0.0);
Cvar_SetValue ("vid_window_y", 0.0);
}
if ((vid_default_mode_win.value < 0) ||
(vid_default_mode_win.value >= nummodes))
{
Cvar_SetValue ("vid_default_mode_win", windowed_default);
}
Cvar_SetValue ("vid_mode", vid_default_mode_win.value);
}
}
// handle the mouse state when windowed if that's changed
if (modestate == MS_WINDOWED)
{
VID_CheckWindowXY (); //qbism- put the check here, and nowhere else
GetWindowRect (mainwindow, &trect);
if ((trect.left != (int) vid_window_x.value)
|| (trect.top != (int) vid_window_y.value))
SetWindowPos (mainwindow, NULL, (int) vid_window_x.value, (int) vid_window_y.value, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME);
{
if (key_dest != key_menu && !cl.paused && !cls.demoplayback) // mankrip
{
IN_ActivateMouse ();
IN_HideMouse ();
}
else
{
IN_DeactivateMouse ();
IN_ShowMouse ();
}
}
}
// We've drawn the frame; copy it to the screen
FlipScreen (rects);
if (vid_testingmode)
{
if (realtime >= vid_testendtime)
{
VID_SetMode (vid_realmode, vid_curpal);
vid_testingmode = 0;
}
}
else
{
if ((int) vid_mode.value != vid_realmode)
{
VID_SetMode ((int) vid_mode.value, vid_curpal);
Cvar_SetValue ("vid_mode", (float) vid_modenum);
// so if mode set fails, we don't keep on
// trying to set that mode
vid_realmode = vid_modenum;
}
// mankrip - begin
else if (vid_realmode == MODE_SETTABLE_WINDOW)
{
if (modelist[MODE_SETTABLE_WINDOW].width != (int) vid_config_x.value
|| modelist[MODE_SETTABLE_WINDOW].height != (int) vid_config_y.value
)
VID_SetMode (vid_realmode, vid_curpal);
}
// mankrip - end
}
}
Last edited by qbism on Tue Dec 18, 2012 1:32 am, edited 1 time in total.
Re: Game window stuck partially offscreen
Code: Select all
// mankrip - begin
// the window should be moved after VID_SetMode is run, not before
Re: Game window stuck partially offscreen
A light just dawned. The change I proposed repositions the window every frame as a work-around for getting stuck. I assumed every windowed winquake was having the same problem. But I tried a vanilla mh ddraw vid_win.c build and it did not get stuck. [EDIT - oops, turned out to be GDI version not ddraw] So I'm going to take a step back to that and build back up...
Last edited by qbism on Tue Dec 18, 2012 3:36 am, edited 1 time in total.
Re: Game window stuck partially offscreen
...ACK, I take that back, I grabbed a GDI version of mh vid_win.c. At this point I'm going with the idea that it's a directdraw thing, keeping the hack, but moving it down below Vid_SetMode if possible.
The issue with GDI is that it is multiple-of-4 only and won't detect native resolutions that don't comply.
The issue with GDI is that it is multiple-of-4 only and won't detect native resolutions that don't comply.
Re: Game window stuck partially offscreen
Ouch; I didn't know about that. This means I'm going to have to implement the DirectDraw version later.qbism wrote:The issue with GDI is that it is multiple-of-4 only and won't detect native resolutions that don't comply.
I preferred the GDI-only version because it has less code to maintain, and also because I remember reading about DirectDraw being deprecated.