Search code examples
windowfullscreenx11xlib

x11 XChangeProperty fails with BadValue


I am trying to create a fullscreen window with X11. I managed to get a basic window and event handling. According to this I have to use the property name "_MOTIF_WM_HINTS" in order to hide window decorations, along with the custom type Hints (wherever that may reside or is user created). Nevertheless I found a list of property names here but when I try using a simple one like "_WM_NAME" or "_NET_WM_NAME" I get a BadValue error.

I'm on Wayland currently but I do have installed xorg-xwayland.

Specificaly i get this error message:

X Error of failed request:  BadValue (integer parameter out of range for operation)
  Major opcode of failed request:  18 (X_ChangeProperty)
  Value in failed request:  0x8
  Serial number of failed request:  16
  Current serial number in output stream:  17

The code in question is below:

void fgd_window_toggle_fullscreen(fgd_window_t* win) 
{
    Display* display        = win->dsp; 
    Window   window         = win->window;
    int      screen_id      = win->screen_id;   
    fgd_window_info_t* info = &win->info; 
    
    info->is_fullscreen = (info->is_fullscreen == 1) ? 0 : 1;
    
    if (info->is_fullscreen) 
    {   
        Atom name_property = XInternAtom(display, "_NET_WM_NAME", False); 
        
        if (name_property == None) 
        {
            printf("Failed to create property.\n");
            return;
        }
        
        unsigned char some_text[21] = "My fullscreen window.";
        
        XChangeProperty(display, 
                        window, 
                        name_property, 
                        name_property,
                        PropModeReplace,
                        8,
                        some_text, 
                        10);
        
        int video_modes_count = 0; 
        XF86VidModeModeInfo** infos = NULL;
        
        XF86VidModeGetAllModeLines(display, 
                                                   screen_id, 
                                                   &video_modes_count,
                                                   &infos);
        printf("Number of video modes: %d\n", video_modes_count);
        for (int idx = 0; idx < video_modes_count; idx++) 
        {
            XF86VidModeModeInfo* current_info = infos[idx];
            
            printf("ModeInfo[%d] = (%d, %d)\n", idx, current_info->hdisplay,
                   current_info->vdisplay);
        }
        // Switch to another video mode so we can fullscreen the window.
        XF86VidModeModeInfo* video_mode = *infos;
        XF86VidModeSwitchToMode(display, screen_id, video_mode);
        // Move the window to top left corner.
        XF86VidModeSetViewPort(display, screen_id, 0, 0);
        // Resize the window to the coresponding dimensions.
        XResizeWindow(display, window,info->max_width, info->max_heigth);
        
        XFree(infos);
    }
    else
    {
        int width_center  = (info->max_width  >> 1) - (info->width  >> 1);
        int heigth_center = (info->max_heigth >> 1) - (info->heigth >> 1);
        
        XResizeWindow(display, window, info->width, info->heigth);
        XMoveWindow(display, window, width_center, heigth_center);
    }
    
}

Is this a code related problem or my display manager is interfering?


Solution

  • I found a solution! It seems that the client needs to call XSendEvent and it can't be done through XChangeProperty.

    According to this the first 4 bytes of the data union can be:

    _NET_WM_STATE_REMOVE        0    /* remove/unset property */
    _NET_WM_STATE_ADD           1    /* add/set property */
    _NET_WM_STATE_TOGGLE        2    /* toggle property  */
    
    void toggle_fullscreen(Display* dpy, Window win, b32* fullscreen)
    {
        
        XClientMessageEvent msg = {
            .type = ClientMessage,
            .display = dpy,
            .window = win,
            .message_type = XInternAtom(dpy, "_NET_WM_STATE", True),
            .format = 32,
            .data = { .l = {
                    *fullscreen,
                    XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", True),
                    None,
                    0,
                    1
                }}
        };
        
        XSendEvent(dpy, XRootWindow(dpy, XDefaultScreen(dpy)), False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*) &msg);
        
        *fullscreen = !(*fullscreen);
    }