Skip to content

[GEN][ZH] Center Game App Window on startup and resolution change #541

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
May 16, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,13 @@ void GameEngine::init( int argc, char *argv[] )
#endif/////////////////////////////////////////////////////////////////////////////////////////////


// TheSuperHackers @bugfix helmutbuhler 04/14/2025
// Pump messages during startup to avoid "(Not responding)" in the window title
// on slower computers and in debug build.
// This also ensures that the window is correctly positioned, because Windows
// apparently ignores the SetWindowPos call when the window is not responding.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment says "apparently". What does that mean? Does it ignore the SetWindowPos or not? If it factually ignores the SetWindowPos, then there should be no word of doubt in the comment.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, Windows behaves as if it ignores the call. Does it actually ignore it, or does the GhostWindow implementation have bugs that ignore that call, or is it some other bug? I don't know, I don't have the code. All I know is that Windows appears to ignore the call, so I think the comment is accurate.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok if we do not know what Windows really does, then how about we just describe the observed issue? This way there will be no speculation.

Example: "Windows behaves as if the call to SetWindowPos never occurred".

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok if we do not know what Windows really does, then how about we just describe the observed issue? This way there will be no speculation.

Example: "Windows behaves as if the call to SetWindowPos never occurred".

I think the mistake causing this issue is inside the d3d8 wrapper setRenderDevice function. Even when passed the argument to resize, inside it's using flags for no resize and no reposition

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xezon In my opinion the comment is fine as it is and means the same as your suggestion. If you feel the comment needs to be changed, can you do it?

@aliendroid1 No, the flags are correct.

serviceWindowsOS();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can this problem be reproduced? I tried this on my machine with Debug configuration and I cannot get the Window to be non responding during boot with and without this change.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, are you perhaps having a debugger attached? The buggy behavior will only occur if there is no debugger attached.
You can also try to insert a Sleep(5000); call instead of serviceWindowsOS(); When I do this, even the Release build shows buggy behavior.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot confirm that this avoids "(Not Responding)" in the window title. I tested this with

serviceWindowsOS();
Sleep(10000);
serviceWindowsOS();

But it does fix the window positioning as you say.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, of course it shows the "(Not Responding)" if you put a Sleep in there :)
The original issue arises when you use Debug build without optimizations and without a debugger attached. In that case the startup is so slow that this "(Not Responding)" shenanigans arises without a Sleep call.

In case you are interested. MS has a little bit of documentation about this feature:
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-ishungappwindow?redirectedfrom=MSDN
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-disableprocesswindowsghosting
This undocumented function might also be of interest: HungWindowFromGhostWindow



#if defined(_DEBUG) || defined(_INTERNAL)
// If we're in Debug or Internal, load the Debug info as well.
Expand Down
140 changes: 78 additions & 62 deletions GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
//#define CREATE_DX8_MULTI_THREADED
//#define CREATE_DX8_FPU_PRESERVE
#define WW3D_DEVTYPE D3DDEVTYPE_HAL

#undef WINVER
#define WINVER 0x0500
#include "dx8wrapper.h"
#include "dx8webbrowser.h"
#include "dx8fvf.h"
Expand Down Expand Up @@ -242,6 +243,26 @@ void Non_Fatal_Log_DX8_ErrorCode(unsigned res,const char * file,int line)
}
}

// TheSuperHackers @info helmutbuhler 04/14/2025
// Helper function that moves x and y such that the inner rect fits into the outer rect.
// If inner already is in outer, this does nothing.
void MoveRectIntoOtherRect(const RECT& inner, const RECT& outer, int* x, int* y)
{
int dx = 0;
if (inner.right > outer.right)
dx = outer.right-inner.right;
if (inner.left < outer.left)
dx = outer.left-inner.left;

int dy = 0;
if (inner.bottom > outer.bottom)
dy = outer.bottom-inner.bottom;
if (inner.top < outer.top)
dy = outer.top-inner.top;

*x += dx;
*y += dy;
}


bool DX8Wrapper::Init(void * hwnd, bool lite)
Expand Down Expand Up @@ -885,6 +906,60 @@ void DX8Wrapper::Get_Format_Name(unsigned int format, StringClass *tex_format)
}
}

void DX8Wrapper::Resize_And_Position_Window()
{
// Get the current dimensions of the 'render area' of the window
RECT rect = { 0 };
::GetClientRect (_Hwnd, &rect);

// Is the window the correct size for this resolution?
if ((rect.right-rect.left) != ResolutionWidth ||
(rect.bottom-rect.top) != ResolutionHeight) {

// Calculate what the main window's bounding rectangle should be to
// accomodate this resolution
rect.left = 0;
rect.top = 0;
rect.right = ResolutionWidth;
rect.bottom = ResolutionHeight;
DWORD dwstyle = ::GetWindowLong (_Hwnd, GWL_STYLE);
AdjustWindowRect (&rect, dwstyle, FALSE);
int width = rect.right-rect.left;
int height = rect.bottom-rect.top;

// Resize the window to fit this resolution
if (!IsWindowed)
::SetWindowPos(_Hwnd, HWND_TOPMOST, 0, 0, width, height, SWP_NOSIZE | SWP_NOMOVE);
else
{
// TheSuperHackers @feature helmutbuhler 04/14/2025
// Center the window in the workarea of the monitor it is on.
MONITORINFO mi = {sizeof(MONITORINFO)};
GetMonitorInfo(MonitorFromWindow(_Hwnd, MONITOR_DEFAULTTOPRIMARY), &mi);
int left = (mi.rcWork.left + mi.rcWork.right - width) / 2;
int top = (mi.rcWork.top + mi.rcWork.bottom - height) / 2;

// TheSuperHackers @feature helmutbuhler 04/14/2025
// In case part of the resulting client area is off monitor, move it.
// This can happen when the window is larger than the work area.
RECT rectClient;
rectClient.left = left - rect.left;
rectClient.top = top - rect.top;
rectClient.right = rectClient.left + ResolutionWidth;
rectClient.bottom = rectClient.top + ResolutionHeight;
MoveRectIntoOtherRect(rectClient, mi.rcMonitor, &left, &top);

::SetWindowPos (_Hwnd,
NULL,
left,
top,
width,
height,
SWP_NOZORDER);
}
}
}

bool DX8Wrapper::Set_Render_Device(int dev, int width, int height, int bits, int windowed,
bool resize_window,bool reset_device, bool restore_assets)
{
Expand Down Expand Up @@ -926,36 +1001,7 @@ bool DX8Wrapper::Set_Render_Device(int dev, int width, int height, int bits, int
// push the client area to be the size you really want.
// if ( resize_window && windowed ) {
if (resize_window) {

// Get the current dimensions of the 'render area' of the window
RECT rect = { 0 };
::GetClientRect (_Hwnd, &rect);

// Is the window the correct size for this resolution?
if ((rect.right-rect.left) != ResolutionWidth ||
(rect.bottom-rect.top) != ResolutionHeight) {

// Calculate what the main window's bounding rectangle should be to
// accomodate this resolution
rect.left = 0;
rect.top = 0;
rect.right = ResolutionWidth;
rect.bottom = ResolutionHeight;
DWORD dwstyle = ::GetWindowLong (_Hwnd, GWL_STYLE);
AdjustWindowRect (&rect, dwstyle, FALSE);

// Resize the window to fit this resolution
if (!windowed)
::SetWindowPos(_Hwnd, HWND_TOPMOST, 0, 0, rect.right-rect.left, rect.bottom-rect.top,SWP_NOSIZE |SWP_NOMOVE);
else
::SetWindowPos (_Hwnd,
NULL,
0,
0,
rect.right-rect.left,
rect.bottom-rect.top,
SWP_NOZORDER | SWP_NOMOVE);
}
Resize_And_Position_Window();
}
#endif
//must be either resetting existing device or creating a new one.
Expand Down Expand Up @@ -1204,37 +1250,7 @@ bool DX8Wrapper::Set_Device_Resolution(int width,int height,int bits,int windowe
}
if (resize_window)
{

// Get the current dimensions of the 'render area' of the window
RECT rect = { 0 };
::GetClientRect (_Hwnd, &rect);

// Is the window the correct size for this resolution?
if ((rect.right-rect.left) != ResolutionWidth ||
(rect.bottom-rect.top) != ResolutionHeight)
{

// Calculate what the main window's bounding rectangle should be to
// accomodate this resolution
rect.left = 0;
rect.top = 0;
rect.right = ResolutionWidth;
rect.bottom = ResolutionHeight;
DWORD dwstyle = ::GetWindowLong (_Hwnd, GWL_STYLE);
AdjustWindowRect (&rect, dwstyle, FALSE);

// Resize the window to fit this resolution
if (!windowed)
::SetWindowPos(_Hwnd, HWND_TOPMOST, 0, 0, rect.right-rect.left, rect.bottom-rect.top,SWP_NOSIZE |SWP_NOMOVE);
else
::SetWindowPos (_Hwnd,
NULL,
0,
0,
rect.right-rect.left,
rect.bottom-rect.top,
SWP_NOZORDER | SWP_NOMOVE);
}
Resize_And_Position_Window();
}
#pragma message("TODO: support changing windowed status and changing the bit depth")
return Reset_Device();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,7 @@ class DX8Wrapper
/*
** Internal functions
*/
static void Resize_And_Position_Window();
static bool Find_Color_And_Z_Mode(int resx,int resy,int bitdepth,D3DFORMAT * set_colorbuffer,D3DFORMAT * set_backbuffer, D3DFORMAT * set_zmode);
static bool Find_Color_Mode(D3DFORMAT colorbuffer, int resx, int resy, UINT *mode);
static bool Find_Z_Mode(D3DFORMAT colorbuffer,D3DFORMAT backbuffer, D3DFORMAT *zmode);
Expand Down