简体   繁体   中英

C - Xlib - BadWindow Error using XGetWindowProperty for window title

I want to get a list of all open windows' titles using Xlib in C. I am running Ubuntu 12.04. I am using the following code to accomplish this:

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <stdlib.h>

Window *list(Display *disp, unsigned long *len)
{
    Atom prop = XInternAtom(disp, "_NET_CLIENT_LIST", False), type;
    int form;
    unsigned long remain;
    unsigned char *list;

    XGetWindowProperty(disp, XDefaultRootWindow(disp), prop, 0, 1024, False, XA_WINDOW,
                            &type, &form, &len, &remain, &list);
    return (Window *)list;
}
char *name(Display *disp, Window window)
{
    Atom prop = XInternAtom(disp, "WM_NAME", False), type;
    int form;
    unsigned long remain, len;
    unsigned char *list;

    XGetWindowProperty(disp, window, prop, 0, 1024, False, AnyPropertyType,
                            &type, &form, &len, &remain, &list);
    return (char*)list;
}
int main(int argc, char *argv[])
{
    Display *disp;
    Window *wlist;
    unsigned long len;
    char *wname;

    disp = XOpenDisplay(NULL);

    wlist = (Window*)list(disp, &len);

    int i;
    for(i = 0; i < (int)len; i++){
            if(wlist[i] != 0){
                    wname = name(disp, wlist[i]);
                    printf("%d: %s\n", i, wname);
                    free(wname);
            }
    }
return 0;
}

Now the problem that I'm having is that this goes through most windows and then gives me a BadWindow error:

0: DNDCollectionWindow
1: launcher 
2: Desktop
3: panel
4: Dash
5: Hud
6: Switcher
7: Update Manager
8: Terminal
9: Ask a Question - Stack Overflow - Mozilla Firefox
X Error of failed request:  BadWindow (invalid Window parameter)
  Major opcode of failed request:  20 (X_GetProperty)
  Resource id in failed request:  0x41
  Serial number of failed request:  22
  Current serial number in output stream:  22

So I'm wondering if anyone knows what is causing this/how to fix it?

As far as I can tell the list function is returning some windows that I can't retrieve the name of, but I'm not sure.

Thanks in advance!

As per my comments, as the code is listed in the question, I get the compiler warning:

In function 'list': 14:29: warning: passing argument 10 of 'XGetWindowProperty' from incompatible pointer type [enabled by default]

  &type, &form, &len, &remain, &list); ^ 

In file included ...: /usr/include/X11/Xlib.h:2688:12: note: expected 'long unsigned int ' but argument is of type 'long unsigned int * '

Which was fixed by removing the address-of operator from the 10th parameter, changing &len to len , as it's being passed to list() as unsigned long *len .

NOTE: in the name() function, as it's declared as unsigned long len , the address-of operator is necessary.

Therefore, I've started with the following code which compiled without warnings:

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <stdlib.h>

Window *list(Display *disp, unsigned long *len)
{
    Atom prop = XInternAtom(disp, "_NET_CLIENT_LIST", False), type;
    int form;
    unsigned long remain;
    unsigned char *list;

    XGetWindowProperty(disp, XDefaultRootWindow(disp), prop, 0, 1024, False, XA_WINDOW,
                            &type, &form, len, &remain, &list);
    return (Window *)list;
}
char *name(Display *disp, Window window)
{
    Atom prop = XInternAtom(disp, "WM_NAME", False), type;
    int form;
    unsigned long remain, len;
    unsigned char *list;

    XGetWindowProperty(disp, window, prop, 0, 1024, False, AnyPropertyType,
                            &type, &form, &len, &remain, &list);
    return (char*)list;
}
int main(int argc, char *argv[])
{
    Display *disp;
    Window *wlist;
    unsigned long len;
    char *wname;

    disp = XOpenDisplay(NULL);

    wlist = (Window*)list(disp, &len);

    int i;
    for(i = 0; i < (int)len; i++){
            if(wlist[i] != 0){
                    wname = name(disp, wlist[i]);
                    printf("%d: %s\n", i, wname);
                    free(wname);
            }
    }
return 0;
}

Initially I was not getting the BadWindow error, so I inserted a sleep( 3 ) on line 38, just prior to the for loop to give me enough time to close a window in an attempt to replicate the behaviour.

Sure enough, this reproduced the error: BadWindow (invalid Window parameter) .


Scanning the code it originally appeared that the if( wlist[i]==0 ) should kick out invalid window handles, but this is not actually the case. Additionally, inserting a if( !window ) test into the name() function itself was equally futile.

However, the function XSetErrorHandler may be of some use, and I've included your code, revised, to show usage:

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <stdlib.h>

int catcher( Display *disp, XErrorEvent *xe )
{
        printf( "Something had happened, bruh.\n" );
        return 0;
}

Window *list(Display *disp, unsigned long *len)
{
    Atom prop = XInternAtom(disp, "_NET_CLIENT_LIST", False), type;
    int form;
    unsigned long remain;
    unsigned char *list;

    XGetWindowProperty(disp, XDefaultRootWindow(disp), prop, 0, 1024, False, XA_WINDOW,
                            &type, &form, len, &remain, &list);
    return (Window *)list;
}
char *name(Display *disp, Window window)
{
    Atom prop = XInternAtom(disp, "WM_NAME", False), type;
    int form;
    unsigned long remain, len;
    unsigned char *list;

    XGetWindowProperty(disp, window, prop, 0, 1024, False, AnyPropertyType,
                            &type, &form, &len, &remain, &list);
    return (char*)list;
}
int main(int argc, char *argv[])
{
    Display *disp;
    Window *wlist;
    unsigned long len;
    char *wname;

    disp = XOpenDisplay(NULL);

    wlist = (Window*)list(disp, &len);

    sleep( 3 ); // <-- inserted to give me time to close an open window

    XSetErrorHandler( catcher ); // <-- inserted to set error handler

    int i;
    for(i = 0; i < (int)len; i++){
    //        if(wlist[i] != 0){    // <-- apparently futile?
                    wname = name(disp, wlist[i]);
                    printf("%d: %s\n", i, wname);
                    free(wname);
    //        }
    }

    XSetErrorHandler( NULL ); // <-- restore the default error handler
    return 0;
}

I've simply created a small function int catcher( Display*, XErrorEvent * ) to catch the errors, avoiding runtime termination.

In case you have more coding to follow, I've included a second call to XErrorHandler() , passing NULL to restore the default handler.


A few other notes, first tested this code by killing the last window I created, but that wasn't quite enough to determine if it would proceed after receiving the error. So I did a second test wherein I killed windows that came before the end of the list, and have verified success.


A few final notes:

Obviously the error handler is over-simplified. When the error is caught, the message is displayed, and the program continues to run. However, the window item is still printed, but is reflected as (null) ...

EG:

7: neo – Dolphin
8: neo – Dolphin
Something had happened, bruh.
9: (null)
10: neo – Dolphin

Hopefully this can get you started... I'll leave the fun parts, such as detecting which error 'had happened', and adjusting the numbering/display of the list up to you ; )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM