cx11gdm

How do I capture minimize and maximize events in XWindows?


I would like to determine if my XWindow is minimized or maximized. My example program is:

/*
 * Study for multiple windows.
 */
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void prtxevtt(int type)

{

    switch (type) {

        case 2:  fprintf(stderr, "KeyPress"); break;
        case 3:  fprintf(stderr, "KeyRelease"); break;
        case 4:  fprintf(stderr, "ButtonPress"); break;
        case 5:  fprintf(stderr, "ButtonRelease"); break;
        case 6:  fprintf(stderr, "MotionNotify"); break;
        case 7:  fprintf(stderr, "EnterNotify"); break;
        case 8:  fprintf(stderr, "LeaveNotify"); break;
        case 9:  fprintf(stderr, "FocusIn"); break;
        case 10: fprintf(stderr, "FocusOut"); break;
        case 11: fprintf(stderr, "KeymapNotify"); break;
        case 12: fprintf(stderr, "Expose"); break;
        case 13: fprintf(stderr, "GraphicsExpose"); break;
        case 14: fprintf(stderr, "NoExpose"); break;
        case 15: fprintf(stderr, "VisibilityNotify"); break;
        case 16: fprintf(stderr, "CreateNotify"); break;
        case 17: fprintf(stderr, "DestroyNotify"); break;
        case 18: fprintf(stderr, "UnmapNotify"); break;
        case 19: fprintf(stderr, "MapNotify"); break;
        case 20: fprintf(stderr, "MapRequest"); break;
        case 21: fprintf(stderr, "ReparentNotify"); break;
        case 22: fprintf(stderr, "ConfigureNotify"); break;
        case 23: fprintf(stderr, "ConfigureRequest"); break;
        case 24: fprintf(stderr, "GravityNotify"); break;
        case 25: fprintf(stderr, "ResizeRequest"); break;
        case 26: fprintf(stderr, "CirculateNotify"); break;
        case 27: fprintf(stderr, "CirculateRequest"); break;
        case 28: fprintf(stderr, "PropertyNotify"); break;
        case 29: fprintf(stderr, "SelectionClear"); break;
        case 30: fprintf(stderr, "SelectionRequest"); break;
        case 31: fprintf(stderr, "SelectionNotify"); break;
        case 32: fprintf(stderr, "ColormapNotify"); break;
        case 33: fprintf(stderr, "ClientMessage"); break;
        case 34: fprintf(stderr, "MappingNotify"); break;
        case 35: fprintf(stderr, "GenericEvent"); break;
        default: fprintf(stderr, "???"); break;

    }

}

int main(void) {

    Window       w;
    GC           gracxt;
    XEvent       e;
    const char*  msg = "Hello, window";
    int          s;
    XFontStruct* font;
    Display*     d;
    int          front = 1;
    Atom         fullscreen;
    int          status;
    Atom         prop;
    Atom         type;
    int          format;
    unsigned long length;
    unsigned long after;
    unsigned char* dp;
 
    d = XOpenDisplay(NULL);
    if (d == NULL) {

        fprintf(stderr, "Cannot open display\n");
        exit(1);

    }
 
    s = DefaultScreen(d);

    w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 1000, 1000, 5,
                            BlackPixel(d, s), WhitePixel(d, s));
    XSelectInput(d, w, ExposureMask|KeyPressMask|PointerMotionMask|
                       StructureNotifyMask|PropertyChangeMask);
    XMapWindow(d, w);
    gracxt = XCreateGC(d, w, 0, NULL);

    font = XLoadQueryFont(d,
        "-bitstream-courier 10 pitch-bold-r-normal--0-0-200-200-m-0-iso8859-1");
    if (!font) {

        fprintf(stderr, "*** No font ***\n");
        exit(1);

    }
    XSetFont(d, gracxt, font->fid);

    fullscreen = XInternAtom(d, "_NET_WM_STATE_FULLSCREEN", 1);
    printf("Fullscreen atom: %ld\n", fullscreen);

    while (1) {

        XNextEvent(d, &e);
    //printf("XWindow event: "); prtxevtt(e.type); printf("\n"); fflush(stdout);
    if (e.type == Expose) XDrawString(d, e.xany.window, gracxt, 10, 50, msg, strlen(msg));
    else if (e.type == ConfigureNotify) {

        printf("ConfigureNotify: x: %d y: %d w: %d h: %d\n",
        e.xconfigure.x, e.xconfigure.y, e.xconfigure.width, e.xconfigure.height);

    } else if (e.type == PropertyNotify) {

        //printf("PropertyNotify: name: %s value: %ld\n", XGetAtomName(d, e.xproperty.atom),
        //       e.xproperty.atom);
        if (!strcmp(XGetAtomName(d, e.xproperty.atom), "_NET_WM_STATE")) {

            status = XGetWindowProperty(d, w, e.xproperty.atom,
                                        0L, 1L, 0,
                                        AnyPropertyType, &type, &format,
                                        &length, &after, &dp);
            if (status == Success && dp && length) {

                prop = ((Atom*)dp)[0];

                printf("Property string: %s value: %ld\n", XGetAtomName(d, prop), prop);

            }

        }

    }

    XCloseDisplay(d);

    return 0;

}

If I maximize the window I get:

ConfigureNotifyXWindow event: ConfigureNotify: x: 2 y: 76 w: 4976 h: 2752 ExposeXWindow event:

Which does not really tell me that the user maximized it, just that it got bigger. It doesn't match the screen size, which of course is true, because it does not include the title and menu bars for the desktop.

When hitting minimize, I don't get any indication in the program at all.

In the document: "Client to Window Manager Communication"

"4.2.5. Iconify and Deiconify A top-level window that is not Withdrawn will be in the Normal state if it is mapped and in the Iconic state if it is unmapped. This will be true even if the window has been reparented; the window manager will unmap the window as well as its parent when switching to the Iconic state. The client can elect to be notified of these state changes by selecting for StructureNotify events on the top-level window. It will receive a UnmapNotify event when it goes Iconic and a MapNotify event when it goes Normal."

I am not seeing the unmap/mapnotify behavior described. This event should be enabled with StructureNotifyMask.

The working machine is Ubuntu 20.04 with GDM3.

Thanks,

Scott Franco San Jose, CA

With the PropertyNotify event I get:

PropertyNotifyXWindow event: 
PropertyNotify: WM_STATE
PropertyNotifyXWindow event: 
PropertyNotify: _NET_WM_STATE
PropertyNotifyXWindow event: 
PropertyNotify: _GTK_EDGE_CONSTRAINTS

On minimize/iconify. Still working on getting the data for the atom.

Second try (using new code above):

PropertyNotifyXWindow event: 
PropertyNotify: name: _NET_WM_STATE value: 326
Atom Property Value: a
PropertyNotifyXWindow event: 
PropertyNotify: name: _GTK_EDGE_CONSTRAINTS value: 402
Atom Property Value: �
PropertyNotifyXWindow event: 
PropertyNotify: name: _NET_WM_STATE value: 326
Atom Property Value: a

I got the XGetWindowProperty() example elsewhere. I am not sure what the window manager is trying to tell me here. Is the data yet another atom? (A number?).

I found some further information from the utility xev:

FocusIn event, serial 37, synthetic NO, window 0x4a00001,
    mode NotifyNormal, detail NotifyNonlinear

KeymapNotify event, serial 37, synthetic NO, window 0x0,
    keys:  70  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   
           0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   

PropertyNotify event, serial 37, synthetic NO, window 0x4a00001,
    atom 0x146 (_NET_WM_STATE), time 4645675, state PropertyNewValue

PropertyNotify event, serial 37, synthetic NO, window 0x4a00001,
    atom 0x192 (_GTK_EDGE_CONSTRAINTS), time 4645675, state PropertyNewValue

PropertyNotify event, serial 37, synthetic NO, window 0x4a00001,
    atom 0x146 (_NET_WM_STATE), time 4645679, state PropertyNewValue
...

On maximize of the test window, and minimize gives similar trace. So it says "state PropertyNewValue", and I am looking for _NET_WM_STATE_FULLSCREEN. Please how do I find what that new property value is?

Epilog:

I modified the program above as suggested. I commented out non-essential prints. Now I get:

Property string: _NET_WM_STATE_MAXIMIZED_HORZ value: 333 ConfigureNotify: x: 2 y: 76 w: 4976 h: 2752 ConfigureNotify: x: 20 y: 90 w: 1000 h: 1000 Property string: _NET_WM_STATE_FOCUSED value: 353 Property string: _NET_WM_STATE_HIDDEN value: 330 Property string: _NET_WM_STATE_HIDDEN value: 330 Property string: _NET_WM_STATE_FOCUSED value: 353

The _NET_WM_STATE_MAXIMIZED_HORZ was received on maximizing the window. The _NET_WM_STATE_FOCUSED state was received when the window was returned to normal (from minimized or maximized), and the _NET_WM_STATE_HIDDEN was from being iconified/minimized.

From dumpster diving on the internet, I find:

_NET_WM_STATE_MAXIMIZED_HORZ or _NET_WM_STATE_MAXIMIZED_VERT

Mean maximized.

_NET_WM_STATE_HIDDEN

Seems to mean iconified, but the description "indicate that a window would not be visible on the screen if its desktop/viewport were active and its coordinates were within the screen bounds", yee ha.

_NET_WM_STATE_FOCUSED

"indicates whether the window's decorations are drawn in an active state"

It SEEMS to meant the same thing as "has keyboard focus". Since you click the window to minimize/maximize the window, guess it could be an alias for this.

In any case, thanks for all the help, I sufficient information now.

Scott Franco San Jose, CA

PS.

One small issue observed is that if you maximize a window, then minimize it (do not return to normal), then select the icon, you get _NET_WM_STATE_MAXIMIZED_HORZ for each event, not _NET_WM_STATE_HIDDEN as you would expect. No clue if this is a bug or what.

S.


Solution

  • So I have a final study program that does the required things I wanted, so I am posting this as an answer. Comments after.

     /*
     * Study for minimize/maximize/normalize.
     */
    #include <X11/Xlib.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(void) {
    
        Window         w;
        GC             gracxt;
        XEvent         e;
        const char*    msg = "Hello, window";
        int            s;
        Display*       d;
        Atom           cmaxhorz;
        Atom           cmaxvert;
        Atom           cfocused;
        Atom           chidden;
        int            maxhorz;
        int            maxvert;
        int            focused;
        int            hidden;
        int            status;
        Atom           prop;
        Atom           type;
        int            format;
        unsigned long  length;
        unsigned long  after;
        unsigned char* dp;
        int            winstate = 0; // 0 = normal, 1 = maximized, 2 = minimized
        int            lws;
     
        d = XOpenDisplay(NULL);
        if (d == NULL) {
    
            fprintf(stderr, "Cannot open display\n");
            exit(1);
    
        }
     
        s = DefaultScreen(d);
    
        w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 1000, 1000, 5,
                                BlackPixel(d, s), WhitePixel(d, s));
        XSelectInput(d, w, ExposureMask|KeyPressMask|PointerMotionMask|
                           StructureNotifyMask|PropertyChangeMask);
        XMapWindow(d, w);
        gracxt = XCreateGC(d, w, 0, NULL);
    
        cfocused = XInternAtom(d, "_NET_WM_STATE_FOCUSED", 1);
        cmaxhorz = XInternAtom(d, "_NET_WM_STATE_MAXIMIZED_HORZ", 1);
        cmaxvert = XInternAtom(d, "_NET_WM_STATE_MAXIMIZED_VERT", 1);
        chidden = XInternAtom(d, "_NET_WM_STATE_HIDDEN", 1);
    
        while (1) {
    
            XNextEvent(d, &e);
            if (e.type == Expose) XDrawString(d, e.xany.window, gracxt, 10, 50, msg, strlen(msg));
            else if (e.type == PropertyNotify) {
    
                if (!strcmp(XGetAtomName(d, e.xproperty.atom), "_NET_WM_STATE")) {
    
                    after = 1L;
                    focused = 0;
                    maxhorz = 0;
                    maxvert = 0;
                    hidden = 0;
                    do {
    
                        status = XGetWindowProperty(d, w, e.xproperty.atom,
                                                    0L, after, 0,
                                                    4/*XA_ATOM*/, &type, &format,
                                                    &length, &after, &dp);
                        if (status == Success && type == 4/*XA_ATOM*/ && dp && format == 32 && length) {
    
                            for (int i = 0; i < length; i++) {
    
                                prop = ((Atom*)dp)[i];
    
                                if (prop == cfocused) focused = 1;
                                if (prop == cmaxhorz) maxhorz = 1;
                                if (prop == cmaxvert) maxvert = 1;
                                if (prop == chidden) hidden = 1;
    
                            }
    
                        }
    
                    } while (after);
                    if (hidden) {
    
                        lws = winstate;
                        winstate = 2;
                        if (lws != winstate) printf("Minimized\n");
    
                    } else if (maxhorz || maxvert) {
    
                        lws = winstate;
                        winstate = 1;
                        if (lws != winstate) printf("Maximized\n");
    
                    } else if (focused) {
    
                        lws = winstate;
                        winstate = 0;
                        if (lws != winstate) printf("Normalized\n");
    
                    }
    
                }
    
            }
    
        }
    
        XCloseDisplay(d);
    
        return 0;
    
    }
    

    I was not able to find any good programs using XGetWindowProperty(). All of the ones out there missed the idea that you should iterate until "after" comes back 0. Others missed the fact that the first call can also contain data, even if followed by others.

    I am still mystified by why it is required to make multiple calls to XGetWindowProperty(). It can return an array of results, and there seems to be no rhyme or reason as to how it breaks up the atoms into groups.

    I don't know if it is actually required to get and store the numeric equivalents for the states of _NET_WM_STATE, assuming they change from implementation to implementation. I have assumed they do.

    I used xprop -spy, then click the window, to watch the properties and verify what my program should be seeing.

    The program shapes the events into what I consider the important states of a window, that is, maximized (occupying the whole screen), minimized (an icon) or normal window. Xwindows does not see it that way, and it separates horizontal and vertical maximizations, and a window can have more than one separate property. A minimized window can also be maximized, and you can see this by the fact a mimimized window, when selected, goes back to being maximized, not normalized. I am ok with that, and I think my three states of the window are a good paradigm.

    The sharp people out there will realize that the minimize/maximize/normalize thing comes from MS Windows, which, indeed, was my original implementation of my code.