DX9 switching from full screen to window give wrong client area - c++

Having a bit of a problem in my dx9 application.
When I switch back from Full screen to windowed mode using the code below the client area is not the right size, it's smaller.
The AdjustWindowRect function is doing its task correctly but SetWindowPos does not set the right window size. Perhaps I'm missing something. Any ideas .
if (d3dpp.Windowed && resChoice == resolutionchoice) return false; // already in window mode and same resolution
//MessageBox(NULL, L"switching to window", L"ERROR", MB_OK);
resChoice = resolutionchoice;
screenWidth = resolutionwidth[resChoice];
screenHeight = resolutionheight[resChoice];
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; // set the back buffer format to 32-bit
d3dpp.BackBufferWidth = screenWidth; // set the width of the buffer
d3dpp.BackBufferHeight = screenHeight; // set the height of the buffer
d3dpp.Windowed = true;
SetWindowLong(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW); // WS_CAPTION | WS_SYSMENU | WS_VISIBLE | WS_MINIMIZEBOX );
// need to call SetWindowPos as well
string values;
RECT r = { 0,0,screenWidth,screenHeight };
AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, false);
values = std::to_string(r.left);
OutputDebugStringA("Adjust area = ");
OutputDebugStringA(values.c_str()); OutputDebugStringA(",");
values = std::to_string(r.top);
OutputDebugStringA(values.c_str()); OutputDebugStringA(",");
values = std::to_string(r.right);
OutputDebugStringA(values.c_str()); OutputDebugStringA(",");
values = std::to_string(r.bottom);
OutputDebugStringA(values.c_str()); OutputDebugStringA("\n");
SetWindowPos(hWnd, HWND_TOP, r.left, r.top, r.right - r.left , r.bottom - r.top, SWP_NOZORDER | SWP_SHOWWINDOW | SWP_FRAMECHANGED);//
//screenWidth = SCREEN_WIDTH;
//screenHeight = SCREEN_HEIGHT;
//windowXscale = 1;
//windowYscale = 1;
GetClientRect(hWnd, &r);
values = std::to_string(r.left);
OutputDebugStringA("Client area = ");
OutputDebugStringA(values.c_str()); OutputDebugStringA(",");
values = std::to_string(r.top);
OutputDebugStringA(values.c_str()); OutputDebugStringA(",");
values = std::to_string(r.right);
OutputDebugStringA(values.c_str()); OutputDebugStringA(",");
values = std::to_string(r.bottom);
OutputDebugStringA(values.c_str()); OutputDebugStringA("\n");
}

After some more searching discovered that if you use SetWindowPos and attempt to make a window that is bigger than the desktop or when returning from Full screen in this case, windows will send a message and the window size will be adjusted automatically to make it no bigger than the current desktop size.
Adding SWP_NOSENDCHANGING flag in the SetWindowPos api call stops this happening and the client size will be what you wanted.

Related

SetWindowPos() cross-process DPI aware

I am creating a program that moves/resizes windows from another process with SetWindowPos(). My own program is PROCESS_PER_MONITOR_DPI_AWARE. The other programs could be anything from PROCESS_DPI_UNAWARE, PROCESS_SYSTEM_DPI_AWARE or PROCESS_PER_MONITOR_DPI_AWARE.
Because my own program is PROCESS_PER_MONITOR_DPI_AWARE, the coordinates I pass to SetWindowPos() are in physical coordinates. What I now want to do is resize the client area to a specific size in logical coordinates.
What I have tried to do is
Get the DPI of the monitor where the window is placed as screenDPI.
Get the DPI of the target window as windowDPI.
Get scaleFactor as screenDPI / windowDPI.
Scale the desired client area size by scaleFactor
Calculated the extra size for the window frame by subtracting the current client rect size from the window rect size.
This works for the most part, but when I am using two screens with different display scaling, then
the calculation of the window frame size is off if I move the window from one screen to the next.
this fails for an application that uses PROCESS_SYSTEM_DPI_AWARE, when the window is located on the secondary screen (which uses 96dpi compared to the primary screen with 120dpi). This has nothing to do with the window frame size and I am not yet sure why exactly it fails, but the target x and y coordinates are scaled up so that the window is moved offscreen.
what happens if, because of the resize, the center of the window changes the screen? Then the screenDPI will no longer be correct, right? How would I handle that case?
I know that there is also the function AdjustWindowRectExForDpi, but somehow I can't get it to work properly. What is the dpi value I am supposed to pass to it? The dpi of the target screen, the dpi of the target window or the dpi of my own program? Additionally, this function is only available from Windows 10 onwards, so how would I handle it on an older Windows client?
I would appreciate some help with this. Thanks!
What is the dpi value I am supposed to pass to it? The dpi of the target screen, the dpi of the target window or the dpi of my own program?
The DPI of the window you need to move from one screen to the next.
code sample:
#include <Windows.h>
LRESULT CALLBACK startup_window_procedure(HWND window, UINT message, WPARAM w_param, LPARAM l_param)
{
switch (message)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
case WM_DPICHANGED:
{
// Resize the window
RECT* new_rect = reinterpret_cast<RECT*>(l_param);
if (!SetWindowPos(window, nullptr, new_rect->left, new_rect->top, new_rect->right - new_rect->left, new_rect->bottom - new_rect->top, SWP_NOZORDER | SWP_NOACTIVATE))
{
return 1;
}
return 0;
}
}
return DefWindowProcW(window, message, w_param, l_param);
}
int CALLBACK wWinMain(HINSTANCE instance, HINSTANCE prev_instance, PWSTR cmd_line, int cmd_show)
{
constexpr auto window_class_name = L"example_dialog";
constexpr auto window_style = WS_OVERLAPPEDWINDOW;
// Enable per-monitor DPI-awareness version 2
if (!SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2))
{
return 1;
}
// Create the window
WNDCLASSEXW window_class;
window_class.cbSize = sizeof(window_class);
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.lpfnWndProc = startup_window_procedure;
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
window_class.hInstance = instance;
window_class.hIcon = nullptr;
window_class.hCursor = nullptr;
window_class.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
window_class.lpszMenuName = nullptr;
window_class.lpszClassName = window_class_name;
window_class.hIconSm = nullptr;
if (!RegisterClassExW(&window_class))
{
return 1;
}
HWND window = CreateWindowExW(0, window_class_name, L"Example window", window_style, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, nullptr, nullptr, instance, nullptr);
if (!window)
{
return 1;
}
UINT dpi = GetDpiForWindow(window);
float scaling_factor = static_cast<float>(dpi) / 96;
// Actually set the appropriate window size
RECT scale;
scale.left = 0;
scale.top = 0;
scale.right = static_cast<LONG>(300 * scaling_factor);
scale.bottom = static_cast<LONG>(150 * scaling_factor);
if (!AdjustWindowRectExForDpi(&scale, window_style, false, 0, dpi))
{
return 1;
}
if (!SetWindowPos(window, nullptr, 0, 0, scale.right - scale.left, scale.bottom - scale.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE))
{
return 1;
}
ShowWindow(window, SW_SHOWNORMAL);
// Message loop
MSG message;
int result;
while ((result = GetMessageW(&message, nullptr, 0, 0)) != 0)
{
if (result == -1)
{
return 1;
}
else
{
TranslateMessage(&message);
DispatchMessageW(&message);
}
}
return static_cast<int>(message.wParam);
}
The windows can move from one screen to the next and recalculate window size successfully.

Move window event on X11 window

I currently have 2 X11 windows that I want to keep in sync. One overlays a transparent graphic while the other one shows video. I currently have the one that overlays graphic to be always on top of the one of the video, but I am having trouble making sure both windows are at the same places when moved. I am looking for a window move event in the X11 documentation, but I can't seem to find one.
In addition, in my event handle loop, I have tried to get the location of one of my windows and to move the other window to that location. This has failed whenever XGetWindowAttributes is called it always returns x=0 and y=0 even though the window is moved.
Window win;
int nxvisuals = 0;
XVisualInfo visual_template;
XVisualInfo *visual_list;
XVisualInfo vinfo;
Visual *visual;
int depth;
Atom wm_state;
(void)wm_state;
x_display = XOpenDisplay ( NULL ); // open the standard display (the primary screen)
visual_template.screen = DefaultScreen(x_display);
visual_list = XGetVisualInfo(x_display, VisualScreenMask, &visual_template, &nxvisuals);
XMatchVisualInfo(x_display, XDefaultScreen(x_display), 32, TrueColor, &vinfo)
Window parent = XDefaultRootWindow(x_display);
XSync(x_display, True);
visual = vinfo.visual;
depth = vinfo.depth;
XSetWindowAttributes swa;
swa.event_mask = ExposureMask | PointerMotionMask | KeyPressMask;
swa.colormap = XCreateColormap(x_display, XDefaultRootWindow(x_display), visual, AllocNone);
swa.background_pixel = 0;
swa.border_pixel = 0;
win = XCreateWindow ( // create a window with the provided parameters
x_display, parent,
0, 0, 1024, 576, 0,
depth, InputOutput,
visual, CWEventMask | CWBackPixel | CWColormap | CWBorderPixel,
&swa );
XSync(x_display, True);
XSetWindowAttributes xattr;
xattr.override_redirect = False;
XChangeWindowAttributes ( x_display, win, CWOverrideRedirect, &xattr );
XWMHints hints;
hints.input = True;
hints.flags = InputHint;
XSetWMHints(x_display, win, &hints);
XSizeHints *size_hints = XAllocSizeHints();
size_hints->flags = PMinSize | PMaxSize | PSize;
size_hints->min_width = 1024;
size_hints->max_width = 1024;
size_hints->min_height = 576;
size_hints->max_height = 576;
XSetNormalHints(x_display, win, size_hints);
XSetWMSizeHints(x_display,win , size_hints, PSize | PMinSize | PMaxSize);
XMapWindow ( x_display , win ); // make the window visible on the screen
XStoreName ( x_display , win , "OpenGL" ); // give the window a name
/* Second window starts here */
int cnxvisuals = 0;
XVisualInfo cvisual_template;
XVisualInfo *cvisual_list;
XVisualInfo cvinfo;
Visual *cvisual;
int cdepth;
cvisual_template.screen = DefaultScreen(x_display);
cvisual_list = XGetVisualInfo(x_display, VisualScreenMask, &cvisual_template, &cnxvisuals);
XMatchVisualInfo(x_display, XDefaultScreen(x_display), 24, TrueColor, &cvinfo)
Window child = XDefaultRootWindow(x_display);
XSync(x_display, True);
cvisual = cvinfo.visual;
cdepth = cvinfo.depth;
XSetWindowAttributes cswa;
cswa.event_mask = PointerMotionMask | KeyPressMask;
cswa.colormap = XCreateColormap(x_display, XDefaultRootWindow(x_display), cvisual, AllocNone);
cswa.background_pixel = 0;
cswa.border_pixel = 0;
child = XCreateWindow ( // create a window with the provided parameters
x_display, parent,
0, 0, 1024, 576, 0,
cdepth, InputOutput,
cvisual, CWEventMask | CWBackPixel | CWColormap | CWBorderPixel,
&cswa );
XSync(x_display, True);
XSetWindowAttributes xcattr;
xcattr.override_redirect = False;
XChangeWindowAttributes ( x_display, child, CWOverrideRedirect, &xcattr );
XWMHints chints;
chints.input = True;
chints.flags = InputHint;
XSetWMHints(x_display, child, &chints);
XSetNormalHints(x_display, child, size_hints);
XSetWMSizeHints(x_display,child , size_hints, PSize | PMinSize | PMaxSize);
XMapWindow ( x_display , child ); // make the window visible on the screen
XStoreName ( x_display , child , "video" ); // give the window a name
XSelectInput(x_display, child, ExposureMask | FocusChangeMask);
int id = pthread_create(&x11loop, NULL,x11_handle_events,this);
Here is my handle events call
void* x11_handle_events(void *void_ptr)
{
Renderer* renderer = static_cast<Renderer*>(void_ptr);
renderer->stop = false;
XEvent event;
XWindowAttributes opengl_attrs;
while(!renderer->stop)
{
XNextEvent(renderer->x_display, &event);
switch(event.type)
{
case Expose:
if (event.xexpose.window == renderer->child)
{
XRaiseWindow(renderer->x_display, renderer->win);
}
break;
case FocusIn:
if (event.xfocus.window == renderer->child)
{
XRaiseWindow(renderer->x_display, renderer->win);
}
break;
}
// Make sure both windows are in the same location
XGetWindowAttributes(renderer->x_display, renderer->child, &opengl_attrs);
XMoveWindow(renderer->x_display, renderer->win, opengl_attrs.x, opengl_attrs.y);
}
pthread_exit(0);
return NULL;
}
The event you're looking for is ConfigureNotify
http://tronche.com/gui/x/xlib/events/window-state-change/configure.html
The X server can report ConfigureNotify events to clients wanting information about actual changes to a window's state, such as size, position, border, and stacking order. The X server generates this event type whenever one of the following configure window requests made by a client application actually completes:
snip
A window is moved by calling XMoveWindow().
The x and y members are set to the coordinates relative to the parent window's origin and indicate the position of the upper-left outside corner of the window. The width and height members are set to the inside size of the window, not including the border. The border_width member is set to the width of the window's border, in pixels.
The event mask is iirc StructureNotifyMask.
The window manager might disagree with your moving around though... but if it still doesn't work, leave a comment and we'll look deeper.

Win32 API Window Height Mismatch

I'm creating a window and I'm defining the height as 768:
RECT windowRect;
windowRect.left = (long)0;
windowRect.right = 1366;
windowRect.top = (long)0;
windowRect.bottom = 768;
...and the following styles:
dwExStyle = WS_EX_APPWINDOW | WS_EX_CLIENTEDGE;
dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU;
...and finally doing this:
AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);
//windowRect.left = -11;
//windowRect.right = 1377;
//windowRect.top = -38;
//windowRect.bottom = 779;
And then I'm creating another window trying to fill the whole screen, like this:
hTest = CreateWindow(L"STATIC", L"Testing", WS_CHILD | WS_VISIBLE | SS_BITMAP, 0, 0, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, hWnd, NULL, hInstance, 0);
With width of 1366 and height of 768. My computer's resolution is 1920 x 1080.
The width is perfectly aligned, exactly 1366.
The height is weird though, at 768 there's about a 10px margin below. If I use values up to 779, nothing changes, the same 10px margin in the bottom is still there. Then if I change to 780px, it's fully covered.
Why does this happen and what's the best way to deal with this? I'd like to have a window menu with exactly the same client height available.
EDIT: I've tried:
RECT r;
GetClientRect(hWnd, &r);
//r.right = 1366
//r.bottom = 768
I don't know why this weird area in bottom is showing, even though the numbers say it's fine...
It's the black border below (the white area is the new child window I'm creating):
Although it's in a bad quality (for size), there's exactly 1366 x 768 pixels of client area, which is what I requested in the CreateWindow function.
Creating a child window for this main window with 768 pixels of height causes the image above.
Adding a padding at top to check if the top 0 is actually 0 (and it isn't using the title border) shows that it is actually 0, that is, if I add any positionX to the child window I see the black background above it.

Render to Desktop

I want to be able to render a thing as if it was a wallpaper. I use Windows, and I prefer DirectX. I know that VLC can render the video has a wallpaper in DirectX mode, so it's possible.
So, a quick question, How could I set the rendertarget to render like if it was a wallpaper in Windows?
Here is some code which will get you a handle (HWND) to a window that can be used to draw over top of the windows Desktop. The main issue with how this works is that the desktop icons are still present but this will allow you to draw over top of them. If you want the icons to appear as normal (with your stuff behind them) you need to redraw them after you've drawn your stuff, or find a way to avoid drawing over them in the first place. This is fairly non-trivial and something I never completely solved.
This definitely works on XP and Windows 7 (with Areo) for getting something that normal GDI drawing can use. I've never tested it with DirectX but I suspect it would work if you used hMainWnd as your presentation window.
HWND hProgMan = NULL;
HWND hShell = NULL;
HWND hMainWnd = NULL;
unsigned int ScreenWidth = 0;
unsigned int ScreenHeight = 0;
int ScreenTop = 0;
int ScreenLeft = 0;
HRGN ValidRGN = NULL;
// ...
ScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
if ( ScreenWidth == 0 )
ScreenWidth = GetSystemMetrics( SM_CXSCREEN );
ScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
if ( ScreenHeight == 0 )
ScreenHeight = GetSystemMetrics(SM_CYSCREEN);
ScreenTop = GetSystemMetrics(SM_YVIRTUALSCREEN);
ScreenLeft = GetSystemMetrics(SM_XVIRTUALSCREEN);
ValidRGN = CreateRectRgn(0,0,ScreenWidth,ScreenHeight);
hProgMan = FindWindow("Progman", "Program Manager");
if(hProgMan != NULL)
{
hShell = FindWindowEx(hProgMan, 0, "SHELLDLL_DefView", NULL);
}
else
{
hProgMan = FindWindow("DesktopBackgroundClass", NULL);
if(hProgMan != NULL)
hShell = FindWindowEx(hProgMan, 0, "DeskFolder", NULL);
}
hMainWnd = CreateWindowEx( WS_EX_TRANSPARENT, "MyWindowClass", "Window Title", WS_CHILDWINDOW | WS_OVERLAPPED | WS_CLIPCHILDREN, 0,0,ScreenWidth,ScreenHeight, hShell,NULL,hInstance,NULL );
EnableWindow(hMainWnd,FALSE);
SetWindowPos(hMainWnd,HWND_BOTTOM,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);
... and then for drawing (using GDI), something like this...
HDC hDC = GetDC( hMainWnd );
SelectClipRgn(hDC,ValidRGN);
BitBlt( hDC, 0, 0, ScreenX, ScreenY, hBackBuffer, 0, 0, SRCCOPY );
ReleaseDC( hMainWnd, hDC );
... and update ValidRGN with the regions of the Desktop icons. Those can be found with a bit of work with the Desktop's listview control window. That is fairly complicated and maybe off topic for this question.

Is there a graceful way to handle toggling between fullscreen and windowed mode in a Windows OpenGL application?

I'm wondering if it's possible to toggle back and forth between fullscreen mode and windowed mode in an OpenGL window(I'm writing for Windows using C++ and win32), without destroying the OpenGL context, and thus having to reload assets(Textures, VBOs, etc) in the process?
This is undesirable because it introduces a delay in switching between fullscreen and windowed mode, potentially a long one, as well as making it easier to screw things up by forgetting to reinitialize something.
As a followup to that, are there certain visual effects that are broken by managing to do this?
I've done a fair bit of searching and reading for the past few days, and despite a lot of flaming of SDL and other frameworks for having the same problem(I'm not using them anyway, but...), the best I've managed to find is a possible lead on opening a 1x1 window in the background to retain the context while a secondary window is destroyed or created at whim. And that's seeming unreliable from the comments I found regarding it, and seems very kludgey regardless.
Is there a proper way to do this, or is the proper way the often-given-as-an-example method of destroying your window, and recreating it, including destroying your OpenGL context and recreating it?
Basically it's just resizing the window and specifying flags that the border is invisible.
SetWindowLongPtr(hWnd, GWL_STYLE,
WS_SYSMENU | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE);
MoveWindow(hWnd, 0, 0, width, height, TRUE);
to set it back:
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = width;
rect.bottom = height;
SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE);
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
MoveWindow(hWnd, 0, 0, rect.right-rect.left, rect.bottom-rect.top, TRUE);
or for a not-resizable window:
SetWindowLongPtr(hWnd, GWL_STYLE, WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE);
AdjustWindowRect(&rect, WS_CAPTION | WS_POPUPWINDOW, FALSE);
MoveWindow(hWnd, 0, 0, rect.right-rect.left, rect.bottom-rect.top, TRUE);
and then just resize your OpenGL viewport settings.
If you want to set the display mode too, use this:
// change display mode if destination mode is fullscreen
if (fullscreen) {
DEVMODE dm;
dm.dmSize = sizeof(DEVMODE);
dm.dmPelsWidth = width;
dm.dmPelsHeight = height;
dm.dmBitsPerPel = bitsPerPixel;
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
success = ChangeDisplaySettings(&dm, 0) == DISP_CHANGE_SUCCESSFUL;
}
// reset display mode if destination mode is windowed
if (!fullscreen)
success = ChangeDisplaySettings(0, 0) == DISP_CHANGE_SUCCESSFUL;
Here's the code I use, which uses SetWindowPos() rather than MoveWindow(), as discussed in the comments of the other answer.
void enter_fullscreen(application* App)
{
POINT Point = {0};
HMONITOR Monitor = MonitorFromPoint(Point, MONITOR_DEFAULTTONEAREST);
MONITORINFO MonitorInfo = { sizeof(MonitorInfo) };
if (GetMonitorInfo(Monitor, &MonitorInfo)) {
DWORD Style = WS_POPUP | WS_VISIBLE;
SetWindowLongPtr(App->Window, GWL_STYLE, Style);
SetWindowPos(App->Window, 0, MonitorInfo.rcMonitor.left, MonitorInfo.rcMonitor.top,
MonitorInfo.rcMonitor.right - MonitorInfo.rcMonitor.left, MonitorInfo.rcMonitor.bottom - MonitorInfo.rcMonitor.top,
SWP_FRAMECHANGED | SWP_SHOWWINDOW);
}
App->IsFullscreen = true;
}
void exit_fullscreen(application* App)
{
bool WasMaximized = App->IsMaximized;
DWORD Style = WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN;
if (WasMaximized) {
Style = Style | WS_MAXIMIZE;
}
ivec2 WindowPosition = WasMaximized ? App->WindowPosition : App->NormalWindowPosition;
ivec2 WindowSize = WasMaximized ? App->WindowSize : App->NormalWindowSize;
SetWindowLongPtr(App->Window, GWL_STYLE, Style);
SetWindowPos(App->Window, 0,
WindowPosition.X, WindowPosition.Y, WindowSize.X, WindowSize.Y,
SWP_FRAMECHANGED | SWP_SHOWWINDOW);
App->IsFullscreen = false;
}
I call it on F11, but also on WM_ACTIVATE. Otherwise the window would sometimes keep rendering on top on Windows 7, even if another application would receive all messages, including mouse and keyboard.