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)
...
E.G.:
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 ; )