Skip to content
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

[Windows] Remove visible WINDOW_MODE_FULLSCREEN border by setting window region. #88852

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
1 change: 0 additions & 1 deletion doc/classes/DisplayServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2058,7 +2058,6 @@
<constant name="WINDOW_MODE_FULLSCREEN" value="3" enum="WindowMode">
Full screen mode with full multi-window support.
Full screen window covers the entire display area of a screen and has no decorations. The display's video mode is not changed.
[b]On Windows:[/b] Multi-window full-screen mode has a 1px border of the [member ProjectSettings.rendering/environment/defaults/default_clear_color] color.
[b]On macOS:[/b] A new desktop is used to display the running project.
[b]Note:[/b] Regardless of the platform, enabling full screen will change the window size to match the monitor's size. Therefore, make sure your project supports [url=$DOCS_URL/tutorials/rendering/multiple_resolutions.html]multiple resolutions[/url] when enabling full screen mode.
</constant>
Expand Down
154 changes: 80 additions & 74 deletions platform/windows/display_server_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "display_server_windows.h"

#include "os_windows.h"
#include "scene/main/window.h"
#include "wgl_detect_version.h"

#include "core/config/project_settings.h"
Expand Down Expand Up @@ -74,6 +75,8 @@
#define GetProcAddress (void *)GetProcAddress
#endif

int constexpr FS_TRANSP_BORDER = 2;

static String format_error_message(DWORD id) {
LPWSTR messageBuffer = nullptr;
size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
Expand Down Expand Up @@ -151,8 +154,11 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {

WindowData &wd = windows[window_id];

int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;

RECT clipRect;
GetClientRect(wd.hWnd, &clipRect);
clipRect.right -= off_x;
ClientToScreen(wd.hWnd, (POINT *)&clipRect.left);
ClientToScreen(wd.hWnd, (POINT *)&clipRect.right);
ClipCursor(&clipRect);
Expand Down Expand Up @@ -1769,23 +1775,36 @@ void DisplayServerWindows::window_set_mouse_passthrough(const Vector<Vector2> &p
void DisplayServerWindows::_update_window_mouse_passthrough(WindowID p_window) {
ERR_FAIL_COND(!windows.has(p_window));

if (windows[p_window].mpass || windows[p_window].mpath.size() == 0) {
SetWindowRgn(windows[p_window].hWnd, nullptr, FALSE);
const WindowData &wd = windows[p_window];
bool clip_pixel = (wd.multiwindow_fs || (wd.borderless && wd.maximized));
bool pass_set = (wd.mpath.size() > 0);
if (!clip_pixel && !pass_set) {
SetWindowRgn(wd.hWnd, nullptr, TRUE);
} else {
POINT *points = (POINT *)memalloc(sizeof(POINT) * windows[p_window].mpath.size());
for (int i = 0; i < windows[p_window].mpath.size(); i++) {
if (windows[p_window].borderless) {
points[i].x = windows[p_window].mpath[i].x;
points[i].y = windows[p_window].mpath[i].y;
} else {
points[i].x = windows[p_window].mpath[i].x + GetSystemMetrics(SM_CXSIZEFRAME);
points[i].y = windows[p_window].mpath[i].y + GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYCAPTION);
HRGN region = nullptr;
if (pass_set) {
Vector<POINT> points;
points.resize(wd.mpath.size());
POINT *points_ptr = points.ptrw();
for (int i = 0; i < wd.mpath.size(); i++) {
if (wd.borderless) {
points_ptr[i].x = wd.mpath[i].x;
points_ptr[i].y = wd.mpath[i].y;
} else {
points_ptr[i].x = wd.mpath[i].x + GetSystemMetrics(SM_CXSIZEFRAME);
points_ptr[i].y = wd.mpath[i].y + GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYCAPTION);
}
}
region = CreatePolygonRgn(points.ptr(), points.size(), ALTERNATE);
} else {
region = CreateRectRgn(0, 0, wd.width, wd.height);
}

HRGN region = CreatePolygonRgn(points, windows[p_window].mpath.size(), ALTERNATE);
SetWindowRgn(windows[p_window].hWnd, region, FALSE);
memfree(points);
if (clip_pixel) {
HRGN region_clip = CreateRectRgn(0, 0, wd.width, wd.height);
CombineRgn(region, region, region_clip, RGN_AND);
DeleteObject(region_clip);
}
SetWindowRgn(wd.hWnd, region, FALSE);
}
}

Expand All @@ -1812,14 +1831,16 @@ void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_wi
if (wd.fullscreen) {
Point2 pos = screen_get_position(p_screen) + _get_screens_origin();
Size2 size = screen_get_size(p_screen);
int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;

MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE);
MoveWindow(wd.hWnd, pos.x, pos.y, size.width + off_x, size.height, TRUE);
} else if (wd.maximized) {
Point2 pos = screen_get_position(p_screen) + _get_screens_origin();
Size2 size = screen_get_size(p_screen);
int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;

ShowWindow(wd.hWnd, SW_RESTORE);
MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE);
MoveWindow(wd.hWnd, pos.x, pos.y, size.width + off_x, size.height, TRUE);
ShowWindow(wd.hWnd, SW_MAXIMIZE);
} else {
Rect2i srect = screen_get_usable_rect(p_screen);
Expand Down Expand Up @@ -2048,7 +2069,8 @@ Size2i DisplayServerWindows::window_get_size(WindowID p_window) const {

RECT r;
if (GetClientRect(wd.hWnd, &r)) { // Retrieves area inside of window border, including decoration.
return Size2(r.right - r.left, r.bottom - r.top);
int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;
return Size2(r.right - r.left - off_x, r.bottom - r.top);
}
return Size2();
}
Expand All @@ -2061,7 +2083,8 @@ Size2i DisplayServerWindows::window_get_size_with_decorations(WindowID p_window)

RECT r;
if (GetWindowRect(wd.hWnd, &r)) { // Retrieves area inside of window border, including decoration.
return Size2(r.right - r.left, r.bottom - r.top);
int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;
return Size2(r.right - r.left - off_x, r.bottom - r.top);
}
return Size2();
}
Expand Down Expand Up @@ -2094,9 +2117,6 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_initiali
r_style |= WS_MAXIMIZEBOX;
}
}
if ((p_fullscreen && p_multiwindow_fs) || p_maximized_fs) {
r_style |= WS_BORDER; // Allows child windows to be displayed on top of full screen.
}
} else {
if (p_resizable) {
if (p_minimized) {
Expand Down Expand Up @@ -2150,7 +2170,8 @@ void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repain
if (p_repaint) {
RECT rect;
GetWindowRect(wd.hWnd, &rect);
MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;
MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left + off_x, rect.bottom - rect.top, TRUE);
}
}

Expand All @@ -2160,6 +2181,10 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];

if (p_mode == WINDOW_MODE_MAXIMIZED && wd.borderless) {
p_mode = WINDOW_MODE_FULLSCREEN;
}

if (wd.fullscreen && p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
RECT rect;

Expand Down Expand Up @@ -2206,21 +2231,21 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
wd.minimized = true;
}

bool was_fs = wd.fullscreen;
if (p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
wd.multiwindow_fs = false;
_update_window_style(p_window, false);
} else {
} else if (p_mode == WINDOW_MODE_FULLSCREEN) {
wd.multiwindow_fs = true;
_update_window_style(p_window, false);
}
_update_window_style(p_window, false);

if ((p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) && !wd.fullscreen) {
if (wd.minimized || wd.maximized) {
ShowWindow(wd.hWnd, SW_RESTORE);
}
wd.was_maximized = wd.maximized;

if (wd.pre_fs_valid) {
if (wd.pre_fs_valid && !was_fs) {
GetWindowRect(wd.hWnd, &wd.pre_fs_rect);
}

Expand All @@ -2234,7 +2259,8 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)

_update_window_style(p_window, false);

MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE);
int off_x = (wd.multiwindow_fs || (!wd.fullscreen && wd.borderless && wd.maximized)) ? FS_TRANSP_BORDER : 0;
MoveWindow(wd.hWnd, pos.x, pos.y, size.width + off_x, size.height, TRUE);

// If the user has mouse trails enabled in windows, then sometimes the cursor disappears in fullscreen mode.
// Save number of trails so we can restore when exiting, then turn off mouse trails
Expand All @@ -2243,6 +2269,7 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
SystemParametersInfoA(SPI_SETMOUSETRAILS, 0, nullptr, 0);
}
}
_update_window_mouse_passthrough(p_window);
}

DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_window) const {
Expand Down Expand Up @@ -2288,8 +2315,8 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W
} break;
case WINDOW_FLAG_BORDERLESS: {
wd.borderless = p_enabled;
_update_window_style(p_window);
_update_window_mouse_passthrough(p_window);
_update_window_style(p_window);
ShowWindow(wd.hWnd, (wd.no_focus || wd.is_popup) ? SW_SHOWNOACTIVATE : SW_SHOW); // Show the window.
} break;
case WINDOW_FLAG_ALWAYS_ON_TOP: {
Expand Down Expand Up @@ -2330,7 +2357,6 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W
} break;
case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
wd.mpass = p_enabled;
_update_window_mouse_passthrough(p_window);
} break;
case WINDOW_FLAG_POPUP: {
ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window can't be popup.");
Expand Down Expand Up @@ -4001,30 +4027,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
SendMessageW(windows[window_id].hWnd, WM_PAINT, 0, 0);
}
} break;
case WM_NCPAINT: {
if (RenderingServer::get_singleton() && (windows[window_id].borderless || (windows[window_id].fullscreen && windows[window_id].multiwindow_fs))) {
Color color = RenderingServer::get_singleton()->get_default_clear_color();
HDC hdc = GetWindowDC(hWnd);
if (hdc) {
HPEN pen = CreatePen(PS_SOLID, 1, RGB(color.r * 255.f, color.g * 255.f, color.b * 255.f));
if (pen) {
HGDIOBJ prev_pen = SelectObject(hdc, pen);
HGDIOBJ prev_brush = SelectObject(hdc, GetStockObject(NULL_BRUSH));

RECT rc;
GetWindowRect(hWnd, &rc);
OffsetRect(&rc, -rc.left, -rc.top);
Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);

SelectObject(hdc, prev_pen);
SelectObject(hdc, prev_brush);
DeleteObject(pen);
}
ReleaseDC(hWnd, hdc);
}
return 0;
}
} break;
case WM_NCHITTEST: {
if (windows[window_id].mpass) {
return HTTRANSPARENT;
Expand Down Expand Up @@ -5012,24 +5014,26 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
} break;

case WM_WINDOWPOSCHANGED: {
WindowData &window = windows[window_id];

int off_x = (window.multiwindow_fs || (!window.fullscreen && window.borderless && IsZoomed(hWnd))) ? FS_TRANSP_BORDER : 0;
Rect2i window_client_rect;
Rect2i window_rect;
{
RECT rect;
GetClientRect(hWnd, &rect);
ClientToScreen(hWnd, (POINT *)&rect.left);
ClientToScreen(hWnd, (POINT *)&rect.right);
window_client_rect = Rect2i(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
window_client_rect = Rect2i(rect.left, rect.top, rect.right - rect.left - off_x, rect.bottom - rect.top);
window_client_rect.position -= _get_screens_origin();

RECT wrect;
GetWindowRect(hWnd, &wrect);
window_rect = Rect2i(wrect.left, wrect.top, wrect.right - wrect.left, wrect.bottom - wrect.top);
window_rect = Rect2i(wrect.left, wrect.top, wrect.right - wrect.left - off_x, wrect.bottom - wrect.top);
window_rect.position -= _get_screens_origin();
}

WINDOWPOS *window_pos_params = (WINDOWPOS *)lParam;
WindowData &window = windows[window_id];

bool rect_changed = false;
if (!(window_pos_params->flags & SWP_NOSIZE) || window_pos_params->flags & SWP_FRAMECHANGED) {
Expand Down Expand Up @@ -5098,6 +5102,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
RECT crect;
GetClientRect(window.hWnd, &crect);
crect.right -= off_x;
ClientToScreen(window.hWnd, (POINT *)&crect.left);
ClientToScreen(window.hWnd, (POINT *)&crect.right);
ClipCursor(&crect);
Expand Down Expand Up @@ -5549,23 +5554,25 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,

_get_window_style(window_id_counter == MAIN_WINDOW_ID, false, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MINIMIZED, p_mode == WINDOW_MODE_MAXIMIZED, false, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), dwStyle, dwExStyle);

int rq_screen = get_screen_from_rect(p_rect);
if (rq_screen < 0) {
rq_screen = get_primary_screen(); // Requested window rect is outside any screen bounds.
}

Rect2 screen_rect = Rect2(screen_get_position(rq_screen), screen_get_size(rq_screen));
Rect2i usable_rect = screen_get_usable_rect(rq_screen);
int off_x = (p_mode == WINDOW_MODE_FULLSCREEN || ((p_flags & WINDOW_FLAG_BORDERLESS_BIT) && p_mode == WINDOW_MODE_MAXIMIZED)) ? FS_TRANSP_BORDER : 0;

RECT WindowRect;

WindowRect.left = p_rect.position.x;
WindowRect.right = p_rect.position.x + p_rect.size.x;
WindowRect.right = p_rect.position.x + p_rect.size.x + off_x;
WindowRect.top = p_rect.position.y;
WindowRect.bottom = p_rect.position.y + p_rect.size.y;

int rq_screen = get_screen_from_rect(p_rect);
if (rq_screen < 0) {
rq_screen = get_primary_screen(); // Requested window rect is outside any screen bounds.
}

if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
Rect2i screen_rect = Rect2i(screen_get_position(rq_screen), screen_get_size(rq_screen));

WindowRect.left = screen_rect.position.x;
WindowRect.right = screen_rect.position.x + screen_rect.size.x;
WindowRect.right = screen_rect.position.x + screen_rect.size.x + off_x;
WindowRect.top = screen_rect.position.y;
WindowRect.bottom = screen_rect.position.y + screen_rect.size.y;
} else {
Expand All @@ -5576,7 +5583,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
}

WindowRect.left = wpos.x;
WindowRect.right = wpos.x + p_rect.size.x;
WindowRect.right = wpos.x + p_rect.size.x + off_x;
WindowRect.top = wpos.y;
WindowRect.bottom = wpos.y + p_rect.size.y;
}
Expand Down Expand Up @@ -5682,15 +5689,15 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
return INVALID_WINDOW_ID;
}

rendering_context->window_set_size(id, real_client_rect.right - real_client_rect.left, real_client_rect.bottom - real_client_rect.top);
rendering_context->window_set_size(id, real_client_rect.right - real_client_rect.left - off_x, real_client_rect.bottom - real_client_rect.top);
rendering_context->window_set_vsync_mode(id, p_vsync_mode);
wd.context_created = true;
}
#endif

#ifdef GLES3_ENABLED
if (gl_manager_native) {
if (gl_manager_native->window_create(id, wd.hWnd, hInstance, real_client_rect.right - real_client_rect.left, real_client_rect.bottom - real_client_rect.top) != OK) {
if (gl_manager_native->window_create(id, wd.hWnd, hInstance, real_client_rect.right - real_client_rect.left - off_x, real_client_rect.bottom - real_client_rect.top) != OK) {
memdelete(gl_manager_native);
gl_manager_native = nullptr;
windows.erase(id);
Expand All @@ -5700,7 +5707,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
}

if (gl_manager_angle) {
if (gl_manager_angle->window_create(id, nullptr, wd.hWnd, real_client_rect.right - real_client_rect.left, real_client_rect.bottom - real_client_rect.top) != OK) {
if (gl_manager_angle->window_create(id, nullptr, wd.hWnd, real_client_rect.right - real_client_rect.left - off_x, real_client_rect.bottom - real_client_rect.top) != OK) {
memdelete(gl_manager_angle);
gl_manager_angle = nullptr;
windows.erase(id);
Expand Down Expand Up @@ -5795,21 +5802,20 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
ClientToScreen(wd.hWnd, (POINT *)&r.left);
ClientToScreen(wd.hWnd, (POINT *)&r.right);
wd.last_pos = Point2i(r.left, r.top) - _get_screens_origin();
wd.width = r.right - r.left;
wd.width = r.right - r.left - off_x;
wd.height = r.bottom - r.top;
} else {
wd.last_pos = p_rect.position;
wd.width = p_rect.size.width;
wd.height = p_rect.size.height;
}

wd.create_completed = true;
// Set size of maximized borderless window (by default it covers the entire screen).
if (p_mode == WINDOW_MODE_MAXIMIZED && (p_flags & WINDOW_FLAG_BORDERLESS_BIT)) {
Rect2i srect = screen_get_usable_rect(rq_screen);
SetWindowPos(wd.hWnd, HWND_TOP, srect.position.x, srect.position.y, srect.size.width, srect.size.height, SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(wd.hWnd, HWND_TOP, usable_rect.position.x - off_x, usable_rect.position.y, usable_rect.size.width + off_x, usable_rect.size.height, SWP_NOZORDER | SWP_NOACTIVATE);
}

wd.create_completed = true;
_update_window_mouse_passthrough(id);
window_id_counter++;
}

Expand Down
Loading