godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
88.84k stars 20.15k forks source link

Can not click the anchor toolbar and other popup toolbar in wslg #82204

Open Cliear opened 11 months ago

Cliear commented 11 months ago

Godot version

4.*

System information

WSLg Version: 1.0.51

Issue description

https://github.com/godotengine/godot/assets/48273990/fa64749e-33c3-4481-bd16-d9b75bef3d31

https://github.com/godotengine/godot/assets/48273990/0151233d-3635-4614-aced-17d25b2eceaa

Steps to reproduce

No matter what wsl distribution is used, as long as godot4 is run through wslg, no popup toorbar can be clicked.

Minimal reproduction project

N/A

Cliear commented 2 months ago

Here are some issues:

  1. Some sources mention that the window manager might create a shadow window for the X11 window we create to add decoreations, as described in the "Xlib Programming Manual for Version 11, Rel.5, Vol.1: xlib programming manual WSLg does it this way. See the following simple example:
    
    #include <X11/Xlib.h>

int main(int argc, char const argv[]) { Display display = XOpenDisplay(NULL); int x = 500; int y = 200; int width = 800; int height = 600; unsigned long border_width = 0; unsigned long border_color = WhitePixel(display, DefaultScreen(display)); unsigned long background = WhitePixel(display, DefaultScreen(display)); Window window = XCreateSimpleWindow(display, DefaultRootWindow(display), x, y, width, height, border_width, border_color, background);

XSelectInput(display, window, ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask);

XMapWindow(display, window);
XFlush(display);

XEvent report;
for (;;) {
    XNextEvent(display, &report);
}

return 0;

}

![shadow](https://github.com/godotengine/godot/assets/48273990/9d3c6e4a-f269-4cc3-98cf-8df549e6c861)

There is an area outside the border that belongs to this window, but it cannot receive any events.
If you use the _MOTIF_WM_HINTS to remove decorations, this shadow window will still persist:

include <X11/Xlib.h>

include

int main(int argc, char const argv[]) { Display display = XOpenDisplay(NULL); int x = 500; int y = 200; int width = 800; int height = 600; unsigned long border_width = 0; unsigned long border_color = WhitePixel(display, DefaultScreen(display)); unsigned long background = WhitePixel(display, DefaultScreen(display)); Window window = XCreateSimpleWindow(display, DefaultRootWindow(display), x, y, width, height, border_width, border_color, background);

enum MWM_Flag {
    MWM_HINTS_FUNCTIONS     = 1 << 0,
    MWM_HINTS_DECORATIONS   = 1 << 1,
    MWM_HINTS_INPUT_MODE    = 1 << 2,
    MWM_HINTS_STATUS        = 1 << 3
};

enum MWM_Decor {
    MWM_DECOR_NOTHING   = 0,
    MWM_DECOR_ALL       = 1 << 0,
    MWM_DECOR_BORDER    = 1 << 1,
    MWM_DECOR_RESIZEH   = 1 << 2,
    MWM_DECOR_TITLE     = 1 << 3,
    MWM_DECOR_MENU      = 1 << 4,
    MWM_DECOR_MINIMIZE  = 1 << 5,
    MWM_DECOR_MAXIMIZE  = 1 << 6,
};

struct {
    uint32_t flags;
    uint32_t functions;
    uint32_t decorations;
    int32_t input_mode;
    uint32_t status;
} hints = {
    .flags = MWM_HINTS_DECORATIONS,
    .decorations = MWM_DECOR_NOTHING
};
Atom property = XInternAtom(display, "_MOTIF_WM_HINTS", True);
if (property != None) {
    XChangeProperty(display, window, property, property, 32, PropModeReplace, (unsigned char *)&hints, sizeof(hints) / 4);
}    

XSelectInput(display, window, ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask);

XMapWindow(display, window);
XFlush(display);

XEvent report;
for (;;) {
    XNextEvent(display, &report);
}

return 0;

}

![no_decoration_shadow](https://github.com/godotengine/godot/assets/48273990/e9d420e9-6483-4a8a-adaf-8be864d3380e)

2. WSLg has a bug, when a x11 window has the  type _NET_WM_WINDOW_TYPE_UTILITY, it appears to be covered by the shadow window, causing it to not receive any events:

include

include

include <X11/Xlib.h>

include <X11/Xatom.h>

int main(int argc, char const argv[]) { Display display = XOpenDisplay(NULL); int x = 500; int y = 200; int width = 800; int height = 600; unsigned long border_width = 0; unsigned long border_color = WhitePixel(display, DefaultScreen(display)); unsigned long background = WhitePixel(display, DefaultScreen(display)); Window window = XCreateSimpleWindow(display, DefaultRootWindow(display), x, y, width, height, border_width, border_color, background);

Atom type_atom = XInternAtom(display, "_NET_WM_WINDOW_TYPE_UTILITY", False);
Atom wt_atom = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
if (wt_atom != None && type_atom != None) {
    XChangeProperty(display, window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
}

XSelectInput(display, window, ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask);

XMapWindow(display, window);
XFlush(display);

XEvent report;
for (;;) {
    XNextEvent(display, &report);
    switch (report.type)
    {
    case Expose:
        puts("Expose");
        break;
    case ConfigureNotify:
        puts("ConfigureNotify");
        break;
    case MapNotify:
        puts("MapNotify");
        break;
    case ReparentNotify:
        puts("ReparentNotify");
        break;
    default:
        puts("Receive a event!");
        break;
    }
}

return 0;

}


![utility](https://github.com/godotengine/godot/assets/48273990/998d67bf-0355-411d-9732-b568cfb354c6)
This type must be set after waiting for a while following the use of XMapWinodw, and then it must be made effective by using XUnMapWindow followed by XMapWindow again.
Cliear commented 2 months ago

The first issue might cause bugs when godot queries the window's position and size in WSLg.

Cliear commented 2 months ago

When _NET_WM_WINDOW_TYPE_UTILITY is commented out, the bug seems to be fixed, and the popup window can receive events: https://github.com/Cliear/godot/blob/fab7c74eb46039fc39c129ac1d671fd0cba77b81/platform/linuxbsd/x11/display_server_x11.cpp#L5703-L5707 remove_utility_anchor remove_utility_color_tool It seems to be running corrctly, but it is indeed affected by the first issue: The shadow window is still there, and it covers part of other windows.The bug comes from the following code: https://github.com/godotengine/godot/blob/26d1577f3985363faab48a65e9a0d9eed0e26d86/platform/linuxbsd/x11/display_server_x11.cpp#L2365-L2379 https://github.com/godotengine/godot/blob/26d1577f3985363faab48a65e9a0d9eed0e26d86/platform/linuxbsd/x11/display_server_x11.cpp#L2185-L2199 If it's a borderless window, it shouldn't require decorations.However, in WSLg, even when using _MOTIF_WM_HINTS, it always seems to factor in the shadow window when calculating the border size and position. popup_window_shadow When making the folloing changes, the bug will be fixed: https://github.com/Cliear/godot/blob/dae7f4f3b6df321bef355cec71a41a6d2628ea0e/platform/linuxbsd/x11/display_server_x11.cpp#L2184-L2201 https://github.com/Cliear/godot/blob/dae7f4f3b6df321bef355cec71a41a6d2628ea0e/platform/linuxbsd/x11/display_server_x11.cpp#L2366-L2383 Since a borderless window should have no decorations, there's no check to determine if it's a borderless window.This is result: position_fix

Cliear commented 2 months ago

Here are a few solutions:

  1. Use _KDE_NET_WM_WINDOW_TYPE_OVERRIDE to remove the shadow window
  2. Remove _NET_WM_WINDOW_TYPE_UTILITY and modify window_get_position_with_decorations and window_get_size_with_decorations
  3. Use override_redirect to disable the window manager's features By the way, Godot runs well under Wayland on WSLg