Screen-sized undecorated window incorrectly gets _NET_WM_STATE_FULLSCREEN and maximized states
To reproduce:
- Two-monitor setup is necessary, wasn't able to reproduce with just one monitor
- Correct the window size to correspond to your monitor's size (here, 3840x2160) in the reproducer program (see at the bottom):
window = XCreateSimpleWindow(display,
RootWindow(display, screen),
0, 0, 3840, 2160, 1, BlackPixel(display, screen), WhitePixel(display, screen));
- Compile the program (gcc a.c -lX11)
- Run the resulting ./a.out
- Observe the output and/or check the window's _NET_WM_STATE with
xprop
:
$ gcc a.c -lX11
$ ./a.out
Size: 3840x2160, border 0
Resizing...
Size: 3840x2160, border 0
Resizing...
_NET_WM_STATE_FULLSCREEN is set
The xprop output:
_NET_WM_STATE(ATOM) = _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_MAXIMIZED_VERT, _NET_WM_STATE_FULLSCREEN, _NET_WM_STATE_FOCUSED
As you can see, the window also gets maximized, even though it didn't make the request to become one.
This behavior has negative repercussions when a GUI applications wishes to implement "client-side" decorations and assumes responsibility for its own controls, resizing, title bar, etc. Once the window enters the fullscreen mode, it looses the ability to get resized.
The reproducer program:
cat a.c
#include <stdio.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
int main() {
Display *display;
Window window;
XEvent event;
int screen;
// Open a connection to the X server
display = XOpenDisplay(NULL);
if (display == NULL) {
fprintf(stderr, "Cannot open display\n");
return 1;
}
screen = DefaultScreen(display);
// Create a window
window = XCreateSimpleWindow(display,
RootWindow(display, screen),
0, 0, 3840, 2160, 1, BlackPixel(display, screen), WhitePixel(display, screen));
// Set MOTIF WM hints to make the window undecorated
Atom motif_hints = XInternAtom(display, "_MOTIF_WM_HINTS", False);
struct {
unsigned long flags;
unsigned long functions;
unsigned long decorations;
long input_mode;
unsigned long status;
} motif_hints_data = {
.flags = 2, // MWM_HINTS_DECORATIONS
.functions = 0,
.decorations = 0, // No decorations (undecorated)
.input_mode = 0,
.status = 0
};
XChangeProperty(display, window, motif_hints, motif_hints, 32, PropModeReplace, (unsigned char *)&motif_hints_data, sizeof(motif_hints_data) / sizeof(long));
// Set window properties
XStoreName(display, window, "Maximized Window");
XSelectInput(display, window, KeyPressMask | StructureNotifyMask);
XMapWindow(display, window);
Atom fullscreen_state = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False);
// Main event loop
while (1) {
XNextEvent(display, &event);
if (event.type == ConfigureNotify) {
XConfigureEvent configureEvent = event.xconfigure;
// Handle the configure event here
printf("Size: %dx%d, border %d \n", configureEvent.width, configureEvent.height, configureEvent.border_width);
printf("Resizing...\n");
XResizeWindow(display, window, configureEvent.width, configureEvent.height);
}
if (event.type == KeyPress) {
// Check if _NET_WM_STATE_FULLSCREEN property is set
Atom actual_type;
int actual_format;
unsigned long nitems, bytes_after;
Atom *properties = NULL;
if (XGetWindowProperty(display, window, XInternAtom(display, "_NET_WM_STATE", False), 0, (~0L), False,
XA_ATOM, &actual_type, &actual_format, &nitems, &bytes_after, (unsigned char**)&properties) == Success) {
for (unsigned long i = 0; i < nitems; ++i) {
if (properties[i] == fullscreen_state) {
printf("_NET_WM_STATE_FULLSCREEN is set\n");
break;
}
}
XFree(properties);
}
}
}
// Clean up and close the connection
XDestroyWindow(display, window);
XCloseDisplay(display);
return 0;
}
(originally reported as https://gitlab.freedesktop.org/xorg/xserver/-/issues/1587)