Search code examples
c++x11xlibxorg

X11 in C++ - Cropping happens whenever ResizeRedirectMask is set when the window is resized


Story

I've made a simple drawing program displaying graphics using X11/Xlib for a Linux system.

It has a menu and an editor to make drawings.

It needs to know the size of the window, so, at first, I used XGetWindowAttributes()
but, I found it too slow for my taste.

Since I executed the function each frames, my program was slow.

Then, I tried to run the function less often, something like every 4 frames but it is caused a flickering.

I wanted an event that could be generated each time the window's size changed to run the function only on size change.

Original fix:

I've found ResizeRequest which does what I want and outputs the window's size as bonus.
So I didn't needed XGetWindowAttributes() any more. This speeded up the program a lot.
It works well when resizing down the window.

New Issue:

I've found that when the windows is resized bigger than the original size, every objects drawn outside of original range gets cropped as shown in the image.

Cropped Image

We can see the grey borders that should be all around the window but are cropped all in black. It's visible especially for the top right colour palette which has 16 colours but 4 colours are half-cropped and 8 aren't visible at all here.


What I would like to do:

I would like to know how to fix the cropping issue by maybe solving one of the following questions:

  1. If I must approve resizing when the event is sent, how can I do?
  2. If I must mess with buffers, how can I do?

Tracks:

My program has a menu that still uses XGetWindowAttributes(), and the editor (shown in the image) uses the ResizeRequest event, which is faster than XGetWindowAttributes().
As expected, the cropping happens in the editor and not in the menu.
But I noticed that I can change the window's size without cropping if I change it in the menu then in the editor, and it works!

I then found that the cropping happens whenever ResizeRedirectMask is set in the XSelectInput() function.

As quoted from Xlib Programming Manual at the 10.11.4 ResizeRequest Events page.

Any attempts to change the size by other clients are then redirected.

I guess, that means it won't affect windows size (which is true as I tested with XGetWindowAttributes() in parallel). So, then I must approve the resizing but how?

As quoted from the answer of X11 window resizing stutters.

I would clear the front buffer and await resize to complete.

How can I do that?


Sample Code:

I've made a short program that display random colored squares everywhere, even outside of the window. The program runs if you copy it.

There is a #if statement that you can change to see the difference between expected (false) and reality (true)

#include <X11/Xlib.h>
#include <cstdint>
#include <unistd.h>

#if true //Make it true to set ResizeRedirectMask and false to disable.
    #define Masks KeyPressMask|ResizeRedirectMask
#else
    #define Masks KeyPressMask
#endif

int main(){
    uint32_t RDM=0;

    uint16_t i=0;

    bool RunLoop=true;

    Display* XDisplay=XOpenDisplay(0);//Create a display
    Window XWindow=XCreateSimpleWindow(XDisplay,DefaultRootWindow(XDisplay),0,0,480,360,0,0,0);//Create a Window
    XMapWindow(XDisplay,XWindow);//Make the Window visible
    GC XGraphicCTX=XCreateGC(XDisplay,XWindow,0,0);//Create a Graphics Context

    //v Wait for a MapNotify XEvent for next commands
    XSelectInput(XDisplay,XWindow,StructureNotifyMask);
    while(1){
        XEvent e;
        XNextEvent(XDisplay,&e);
        if(e.type==MapNotify)break;
    }
    XSelectInput(XDisplay,XWindow,Masks); //Here is part of the magic error
    while(RunLoop){
        while(XPending(XDisplay)){//Get key changes
            XEvent Event;
            XNextEvent(XDisplay,&Event);
            if(Event.type==KeyPress){
                RunLoop=false;
            }
        }

        for(i=0;i<4096;i++){
            RDM=(RDM+1841)*9245;    //Not perfect but good enough
            XSetForeground(XDisplay,XGraphicCTX,RDM&0xFFFFFF);
            
            RDM=(RDM+1841)*9245;    //Not perfect but good enough
            XFillRectangle(XDisplay,XWindow,XGraphicCTX,RDM&0xFFFF,(RDM>>16),64,64);
        }

        XFlush(XDisplay);

        usleep(16667); //AHHH there is 666!
    }

    return 0;
}

The result is as follows when resized: Unexpected result of New Program

As we can see, cropping happens whenever ResizeRedirectMask is set, you don't even need to respond to the event to make it happening!
Toggling the #if statement to false gives the desired behaviour since it would disable ResizeRedirectMask.
That particular program doesn't care of the window's size, most of my programs need to know the size or it fails.


Some details:

I used to get the size of the window using XGetWindowAttributes() which a bit slow, I still use it in the menu as it works good enough.
That's why in the editor, I use the ResizeRequest event.

The ConfigureNotify event seems to works greatly but it flickers way more than by using XGetWindowAttributes().
Why does it flickers?
It flickers because a ConfigureNotify is sent after the resize completed, it means that it as generally sent one frame after the resize.
This is linked to X11 Window Resizing Stutters.
At least, it doesn't wastes a lot of performances.
I'm looking for a way to fix the flickering as it could be easier.

Note that the question completely changed because the issue is different than originally. But still in link with the original issue.


Solution

  • ResizeRedirectMask is irrelevant for your purposes. You never want it unless you are writing a window manager. You need StructureNotifyMask. You should virtually always select this mask (maybe unless your window never gets resized).

    If you do not select StructureNotifyMask, your window never gets actual resize events and does not get a chance to respond to them.

    When you get a ConfigureNotify event, which will only happen if you select StructureNotifyMask, it will contain the new actual geometry, so your window can update itself.