#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <inttypes.h>
#include <xcb/xcb.h>
#include <xcb/xproto.h>
#include <xcb/xcb_atom.h>
#ifndef HAVE_STRNLEN
#include "strnlen.h"
#endif
#ifndef PRIx32
#define PRIx32 "x"
#endif
#ifndef PRIu32
#define PRIu32 "u"
#endif
static char *ProgramName;
static xcb_atom_t WM_STATE;
static void lookat (xcb_connection_t *dpy, xcb_window_t root, int verbose, int maxcmdlen);
static void print_client_properties (xcb_connection_t *dpy, xcb_window_t w,
int verbose, int maxcmdlen );
static void print_text_field (xcb_connection_t *dpy, char *s, xcb_get_property_reply_t *tp );
static int print_quoted_word (char *s, int maxlen);
static void unknown (xcb_connection_t *dpy, xcb_atom_t actual_type, int actual_format );
typedef int Bool;
#define False (0)
#define True (!False)
static void
usage(void)
{
fprintf (stderr,
"usage: %s [-display dpy] [-m len] [-[a][l]]\n", ProgramName);
exit (1);
}
typedef void (*queue_func)(void *closure);
typedef struct queue_blob {
queue_func func;
void *closure;
struct queue_blob *next;
} queue_blob;
static queue_blob *head = NULL;
static queue_blob **tail = &head;
static void enqueue(queue_func func, void *closure)
{
queue_blob *blob = malloc(sizeof(*blob));
if (!blob)
return;
blob->func = func;
blob->closure = closure;
blob->next = NULL;
*tail = blob;
tail = &blob->next;
}
static void run_queue(void)
{
while (head) {
queue_blob *blob = head;
blob->func(blob->closure);
head = blob->next;
free(blob);
}
tail = &head;
}
typedef struct {
xcb_connection_t *c;
xcb_intern_atom_cookie_t cookie;
xcb_atom_t *atom;
} atom_state;
static void atom_done(void *closure)
{
xcb_intern_atom_reply_t *reply;
atom_state *as = closure;
reply = xcb_intern_atom_reply(as->c, as->cookie, NULL);
if (!reply)
goto done;
*(as->atom) = reply->atom;
free(reply);
done:
free(as);
}
static void init_atoms(xcb_connection_t *c)
{
atom_state *as;
as = malloc(sizeof(*as));
as->c = c;
as->atom = &WM_STATE;
as->cookie = xcb_intern_atom(c, 0, strlen("WM_STATE"), "WM_STATE");
enqueue(atom_done, as);
}
int
main(int argc, char *argv[])
{
int i;
char *displayname = NULL;
Bool all_screens = False;
Bool verbose = False;
xcb_connection_t *dpy;
const xcb_setup_t *setup;
int screen_number = 0;
int maxcmdlen = 10000;
ProgramName = argv[0];
for (i = 1; i < argc; i++) {
char *arg = argv[i];
if (arg[0] == '-') {
char *cp;
switch (arg[1]) {
case 'd':
if (++i >= argc) usage ();
displayname = argv[i];
continue;
case 'm':
if (++i >= argc) usage ();
maxcmdlen = atoi (argv[i]);
continue;
}
for (cp = &arg[1]; *cp; cp++) {
switch (*cp) {
case 'a':
all_screens = True;
continue;
case 'l':
verbose = True;
continue;
default:
usage ();
}
}
} else {
usage ();
}
}
dpy = xcb_connect(displayname, &screen_number);
if (xcb_connection_has_error(dpy)) {
char *name = displayname;
if (!name)
name = getenv("DISPLAY");
if (!name)
name = "";
fprintf (stderr, "%s: unable to open display \"%s\"\r\n",
ProgramName, name);
exit (1);
}
init_atoms(dpy);
setup = xcb_get_setup(dpy);
if (all_screens) {
xcb_screen_iterator_t screen;
screen = xcb_setup_roots_iterator(setup);
do {
lookat(dpy, screen.data->root, verbose, maxcmdlen);
xcb_screen_next(&screen);
} while (screen.rem);
} else {
xcb_screen_iterator_t screen;
screen = xcb_setup_roots_iterator(setup);
for (i = 0; i < screen_number; i++)
xcb_screen_next(&screen);
lookat (dpy, screen.data->root, verbose, maxcmdlen);
}
run_queue();
xcb_disconnect(dpy);
exit (0);
}
typedef struct {
xcb_connection_t *c;
xcb_get_property_cookie_t *prop_cookie;
xcb_query_tree_cookie_t *tree_cookie;
xcb_window_t *win;
xcb_window_t orig_win;
int list_length;
int verbose;
int maxcmdlen;
} child_wm_state;
static void child_info(void *closure)
{
child_wm_state *cs = closure;
xcb_window_t orig = cs->orig_win;
xcb_connection_t *c = cs->c;
int verbose = cs->verbose;
int maxcmdlen = cs->maxcmdlen;
int i, j;
int child_count, num_rep;
xcb_query_tree_reply_t **reply;
for (i = 0; i < cs->list_length; i++) {
xcb_get_property_reply_t *reply;
reply = xcb_get_property_reply(c, cs->prop_cookie[i], NULL);
if (reply) {
if (reply->type) {
print_client_properties(c, cs->win[i], cs->verbose, cs->maxcmdlen);
free(reply);
for (j = i+1; j < cs->list_length; j++) {
reply = xcb_get_property_reply(c, cs->prop_cookie[j], NULL);
if (reply)
free(reply);
}
for (j = 0; j < cs->list_length; j++) {
xcb_query_tree_reply_t *rep;
rep = xcb_query_tree_reply(c, cs->tree_cookie[j], NULL);
if (rep)
free(rep);
}
goto done;
}
free(reply);
}
}
num_rep = 0;
reply = malloc(sizeof(*reply) * cs->list_length);
if (!reply)
goto done;
for (i = 0; i < cs->list_length; i++) {
reply[num_rep] = xcb_query_tree_reply(c, cs->tree_cookie[i], NULL);
if (reply[num_rep])
num_rep++;
}
child_count = 0;
for (i = 0; i < num_rep; i++)
child_count += reply[i]->children_len;
if (!child_count) {
print_client_properties(c, cs->orig_win, cs->verbose, cs->maxcmdlen);
goto reply_done;
}
cs = malloc(sizeof(*cs) + child_count * (sizeof(*cs->prop_cookie) + sizeof(*cs->tree_cookie) + sizeof(*cs->win)));
if (!cs)
goto reply_done;
cs->c = c;
cs->verbose = verbose;
cs->maxcmdlen = maxcmdlen;
cs->orig_win = orig;
cs->prop_cookie = (void *)&cs[1];
cs->tree_cookie = (void *)&cs->prop_cookie[child_count];
cs->win = (void *)&cs->tree_cookie[child_count];
cs->list_length = child_count;
child_count = 0;
for (i = 0; i < num_rep; i++) {
xcb_window_t *child = xcb_query_tree_children(reply[i]);
for (j = 0; j < reply[i]->children_len; j++) {
cs->win[child_count] = child[j];
cs->prop_cookie[child_count] = xcb_get_property(c, 0, child[j],
WM_STATE, XCB_GET_PROPERTY_TYPE_ANY,
0, 0);
cs->tree_cookie[child_count++] = xcb_query_tree(c, child[j]);
}
}
enqueue(child_info, cs);
reply_done:
for (i = 0; i < num_rep; i++)
free(reply[i]);
free(reply);
done:
free(closure);
}
typedef struct {
xcb_connection_t *c;
xcb_query_tree_cookie_t cookie;
int verbose;
int maxcmdlen;
} root_list_state;
static void root_list(void *closure)
{
int i;
xcb_window_t *child;
xcb_query_tree_reply_t *reply;
root_list_state *rl = closure;
reply = xcb_query_tree_reply(rl->c, rl->cookie, NULL);
if (!reply)
goto done;
child = xcb_query_tree_children(reply);
for (i = 0; i < reply->children_len; i++) {
child_wm_state *cs = malloc(sizeof(*cs) + sizeof(*cs->prop_cookie) + sizeof(*cs->tree_cookie) + sizeof(*cs->win));
if (!cs)
goto done;
cs->c = rl->c;
cs->verbose = rl->verbose;
cs->maxcmdlen = rl->maxcmdlen;
cs->prop_cookie = (void *)&cs[1];
cs->tree_cookie = (void *)&cs->prop_cookie[1];
cs->win = (void *)&cs->tree_cookie[1];
cs->orig_win = child[i];
cs->win[0] = child[i];
cs->prop_cookie[0] = xcb_get_property(rl->c, 0, child[i],
WM_STATE, XCB_GET_PROPERTY_TYPE_ANY,
0, 0);
cs->tree_cookie[0] = xcb_query_tree(rl->c, child[i]);
cs->list_length = 1;
enqueue(child_info, cs);
}
free(reply);
done:
free(rl);
}
static void
lookat(xcb_connection_t *dpy, xcb_window_t root, int verbose, int maxcmdlen)
{
root_list_state *rl = malloc(sizeof(*rl));
if (!rl)
return;
rl->c = dpy;
rl->cookie = xcb_query_tree(dpy, root);
rl->verbose = verbose;
rl->maxcmdlen = maxcmdlen;
enqueue(root_list, rl);
}
static char *Nil = "(nil)";
typedef struct {
xcb_connection_t *c;
xcb_get_property_cookie_t client_machine;
xcb_get_property_cookie_t command;
xcb_get_property_cookie_t name;
xcb_get_property_cookie_t icon_name;
xcb_get_property_cookie_t wm_class;
xcb_window_t w;
int verbose;
int maxcmdlen;
} client_state;
static void
show_client_properties(void *closure)
{
client_state *cs = closure;
xcb_get_property_reply_t *client_machine;
xcb_get_property_reply_t *command;
xcb_get_property_reply_t *name;
xcb_get_property_reply_t *icon_name;
xcb_get_property_reply_t *wm_class;
char *argv;
int charsleft = cs->maxcmdlen;
int i;
client_machine = xcb_get_property_reply(cs->c, cs->client_machine, NULL);
command = xcb_get_property_reply(cs->c, cs->command, NULL);
if (cs->verbose) {
name = xcb_get_property_reply(cs->c, cs->name, NULL);
icon_name = xcb_get_property_reply(cs->c, cs->icon_name, NULL);
wm_class = xcb_get_property_reply(cs->c, cs->wm_class, NULL);
}
if (!command || !command->type)
goto done;
if (cs->verbose) {
printf ("Window 0x%" PRIx32 ":\n", cs->w);
print_text_field (cs->c, " Machine: ", client_machine);
if (name && name->type)
print_text_field (cs->c, " Name: ", name);
} else {
print_text_field (cs->c, NULL, client_machine);
putchar (' ');
putchar (' ');
}
if (cs->verbose)
if (icon_name && icon_name->type)
print_text_field (cs->c, " Icon Name: ", icon_name);
if (cs->verbose)
printf (" Command: ");
argv = xcb_get_property_value(command);
for (i = 0; i < command->value_len && charsleft > 0; ) {
charsleft -= print_quoted_word (argv + i, charsleft);
i += strnlen(argv + i, command->value_len - i) + 1;
if (i < command->value_len && charsleft > 0) {
putchar (' ');
charsleft--;
}
}
putchar ('\n');
if (cs->verbose) {
if (wm_class && wm_class->type) {
char *res_name, *res_class;
int name_len, class_len;
res_name = xcb_get_property_value(wm_class);
name_len = strnlen(res_name, wm_class->value_len) + 1;
class_len = wm_class->value_len - name_len;
if (class_len > 0) {
res_class = res_name + name_len;
} else {
res_class = Nil;
class_len = strlen(res_class);
}
printf (" Instance/Class: %.*s/%.*s",
name_len, res_name,
class_len, res_class);
putchar ('\n');
}
}
done:
if (client_machine)
free(client_machine);
if (command)
free(command);
if (cs->verbose) {
if (name)
free(name);
if (icon_name)
free(icon_name);
if (wm_class)
free(wm_class);
}
free(cs);
}
static void
print_client_properties(xcb_connection_t *dpy, xcb_window_t w, int verbose, int maxcmdlen)
{
client_state *cs = malloc(sizeof(*cs));
if (!cs)
return;
cs->c = dpy;
cs->w = w;
cs->verbose = verbose;
cs->maxcmdlen = maxcmdlen;
cs->client_machine = xcb_get_property(dpy, 0, w,
WM_CLIENT_MACHINE, XCB_GET_PROPERTY_TYPE_ANY,
0, 1000000L);
cs->command = xcb_get_property(dpy, 0, w,
WM_COMMAND, XCB_GET_PROPERTY_TYPE_ANY,
0, 1000000L);
if (verbose) {
cs->name = xcb_get_property(dpy, 0, w,
WM_NAME, XCB_GET_PROPERTY_TYPE_ANY,
0, 1000000L);
cs->icon_name = xcb_get_property(dpy, 0, w,
WM_ICON_NAME, XCB_GET_PROPERTY_TYPE_ANY,
0, 1000000L);
cs->wm_class = xcb_get_property(dpy, 0, w,
WM_CLASS, STRING,
0, 1000000L);
}
enqueue(show_client_properties, cs);
}
static void
print_text_field(xcb_connection_t *dpy, char *s, xcb_get_property_reply_t *tp)
{
if (tp->type == XCB_NONE || tp->format == 0) {
printf ("''");
return;
}
if (s) printf ("%s", s);
if (tp->type == STRING && tp->format == 8) {
printf ("%.*s", (int)tp->value_len, (char *)xcb_get_property_value(tp));
} else {
unknown (dpy, tp->type, tp->format);
}
if (s) putchar ('\n');
}
static int
print_quoted_word(char *s,
int maxlen)
{
register char *cp;
Bool need_quote = False, in_quote = False;
char quote_char = '\'', other_quote = '"';
int charsprinted = 0;
for (cp = s; *cp; cp++) {
if (! ((isascii(*cp) && isalnum(*cp)) ||
(*cp == '-' || *cp == '_' || *cp == '.' || *cp == '+' ||
*cp == '/' || *cp == '=' || *cp == ':' || *cp == ','))) {
need_quote = True;
break;
}
}
in_quote = need_quote;
if (need_quote) {
putchar (quote_char);
charsprinted++; maxlen--;
}
for (cp = s; *cp && maxlen>0; cp++) {
if (*cp == quote_char) {
if (in_quote) {
putchar (quote_char);
charsprinted++; maxlen--;
}
putchar (other_quote);
charsprinted++; maxlen--;
{
char tmp = other_quote;
other_quote = quote_char; quote_char = tmp;
}
in_quote = True;
}
putchar (*cp);
charsprinted++; maxlen--;
}
if (in_quote && maxlen>0) {
putchar (quote_char);
charsprinted++; maxlen--;
}
return charsprinted;
}
static void
unknown(xcb_connection_t *dpy, xcb_atom_t actual_type, int actual_format)
{
printf ("<unknown type ");
if (actual_type == XCB_NONE)
printf ("None");
else {
xcb_get_atom_name_reply_t *atom =
xcb_get_atom_name_reply(dpy,
xcb_get_atom_name(dpy, actual_type), NULL);
if (atom) {
printf("%.*s", xcb_get_atom_name_name_length(atom),
xcb_get_atom_name_name(atom));
free(atom);
} else
fputs (Nil, stdout);
}
printf (" (%" PRIu32 ") or format %d>", actual_type, actual_format);
}