diff options
author | Natanael Copa <ncopa@alpinelinux.org> | 2010-07-17 20:15:34 +0000 |
---|---|---|
committer | Natanael Copa <ncopa@alpinelinux.org> | 2010-07-17 20:15:34 +0000 |
commit | b1ab67635f5d706a1c45d673e37b90de2b77aa8b (patch) | |
tree | 97c3de1bb04f481d6b98b86617689504634d6db7 | |
parent | 60937c3d088fb999e9a95999e0420af7229ecef3 (diff) | |
download | aports-b1ab67635f5d706a1c45d673e37b90de2b77aa8b.tar.bz2 aports-b1ab67635f5d706a1c45d673e37b90de2b77aa8b.tar.xz |
testing/xwininfo: new aport
Window information utility for X
http://cgit.freedesktop.org/xorg/app/xwininfo/
-rw-r--r-- | testing/xwininfo/APKBUILD | 33 | ||||
-rw-r--r-- | testing/xwininfo/index.html?id=96f19bade9ce4940642d580f4c52e2bc0e3539ab | 2536 |
2 files changed, 2569 insertions, 0 deletions
diff --git a/testing/xwininfo/APKBUILD b/testing/xwininfo/APKBUILD new file mode 100644 index 000000000..4755ff9cd --- /dev/null +++ b/testing/xwininfo/APKBUILD @@ -0,0 +1,33 @@ +# Maintainer: Natanael Copa <ncopa@alpinelinux.org> +pkgname=xwininfo +pkgver=1.0.5 +pkgrel=0 +pkgdesc="Window information utility for X" +url="http://cgit.freedesktop.org/xorg/app/xwininfo/" +license="GPL" +depends= +makedepends="libx11-dev libxext-dev" +install= +subpackages="$pkgname-doc" +source="http://xorg.freedesktop.org/releases/individual/app/xwininfo-$pkgver.tar.bz2" + +_builddir="$srcdir"/$pkgname-$pkgver +prepare() { + cd "$_builddir" +} + +build() { + cd "$_builddir" + ./configure --prefix=/usr \ + --sysconfdir=/etc \ + --mandir=/usr/share/man \ + --infodir=/usr/share/info + make || return 1 +} + +package() { + cd "$_builddir" + make DESTDIR="$pkgdir" install +} + +md5sums="908f8bc3255f639effa9780fb1c19ea4 xwininfo-1.0.5.tar.bz2" diff --git a/testing/xwininfo/index.html?id=96f19bade9ce4940642d580f4c52e2bc0e3539ab b/testing/xwininfo/index.html?id=96f19bade9ce4940642d580f4c52e2bc0e3539ab new file mode 100644 index 000000000..35c007c4f --- /dev/null +++ b/testing/xwininfo/index.html?id=96f19bade9ce4940642d580f4c52e2bc0e3539ab @@ -0,0 +1,2536 @@ +From 96f19bade9ce4940642d580f4c52e2bc0e3539ab Mon Sep 17 00:00:00 2001 +From: Alan Coopersmith <alan.coopersmith@oracle.com> +Date: Sun, 13 Jun 2010 19:42:34 +0000 +Subject: Convert from Xlib to xcb + +Testing was done with a simple GNOME 2.28 session with a number of +applications open (gnome-terminal, VirtualBox, Firefox). + +Primary test case was xwininfo -root -all, which listed 114 children of +the root window. Output was identical to Xlib version (after applying +the fix to libxcb_icccm for always null-terminating wm_class properties). + +Over a local connection on the same machine: + +Xlib: 0.00u 0.01s 0:00.05 20.0% +xcb: 0.00u 0.00s 0:00.02 0.0% + +(i.e. barely measurable difference - I had more variation between + repeated runs of the command) + +Introducing latency by running over ssh -X from California to Beijing +and back: + +Xlib: 0.03u 0.02s 8:19.12 0.0% +xcb: 0.00u 0.00s 0:45.26 0.0% + +Memory size when exit() is called: + +Xlib: + Address Kbytes RSS Anon Locked Mode Mapped File +08043000 20 20 20 - rw--- [ stack ] +08400000 144 144 144 - rw--- [ heap ] +total Kb 8972 8640 316 - + +xcb: + Address Kbytes RSS Anon Locked Mode Mapped File +08045000 12 12 12 - rwx-- [ stack ] +0806C000 100 100 100 - rwx-- [ heap ] +total Kb 7980 7692 288 - + +Bytes sent & received (counted by proxying via xscope): + +Xlib: Client --> Server: 21380 bytes Client <-- Server: 54124 bytes +xcb: Client --> Server: 21114 bytes Client <-- Server: 53160 bytes + +(The Xlib code didn't save any replies, so re-requested a couple of things + when running with -all - I fixed that while porting to xcb, but the same + could be done with Xlib easily too.) + +Not yet handled: WM_NAME properties that need to be converted from another +character encoding. + +Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com> +Reviewed-by: James Cloos <cloos@jhcloos.com> +--- +diff --git a/COPYING b/COPYING +index 10b416d..687540f 100644 +--- a/COPYING ++++ b/COPYING +@@ -1,4 +1,4 @@ +-Copyright © 1999 Sun Microsystems, Inc. All rights reserved. ++Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), +diff --git a/clientwin.c b/clientwin.c +index cce35ad..fe6bd18 100644 +--- a/clientwin.c ++++ b/clientwin.c +@@ -19,47 +19,59 @@ + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ +-#include <X11/Xatom.h> +-#include <X11/Xlib.h> ++#include <xcb/xcb.h> ++#include <xcb/xproto.h> ++ ++#include <stdlib.h> ++#include <string.h> + + #include "clientwin.h" + +-static Atom atom_wm_state = None; ++static xcb_atom_t atom_wm_state = XCB_ATOM_NONE; ++typedef enum { False = 0, True } Bool; + + /* + * Check if window has given property + */ + static Bool +-Window_Has_Property(Display * dpy, Window win, Atom atom) ++Window_Has_Property(xcb_connection_t * dpy, xcb_window_t win, xcb_atom_t atom) + { +- Atom type_ret; +- int format_ret; +- unsigned char *prop_ret; +- unsigned long bytes_after, num_ret; +- +- type_ret = None; +- prop_ret = NULL; +- XGetWindowProperty(dpy, win, atom, 0, 0, False, AnyPropertyType, +- &type_ret, &format_ret, &num_ret, +- &bytes_after, &prop_ret); +- if (prop_ret) +- XFree(prop_ret); +- +- return (type_ret != None) ? True : False; ++ xcb_get_property_cookie_t prop_cookie; ++ xcb_get_property_reply_t *prop_reply; ++ ++ prop_cookie = xcb_get_property (dpy, False, win, atom, ++ XCB_GET_PROPERTY_TYPE_ANY, 0, 0); ++ ++ prop_reply = xcb_get_property_reply (dpy, prop_cookie, NULL); ++ ++ if (prop_reply) { ++ xcb_atom_t reply_type = prop_reply->type; ++ free (prop_reply); ++ if (reply_type != XCB_NONE) ++ return True; ++ } ++ ++ return False; + } + + /* + * Check if window is viewable + */ + static Bool +-Window_Is_Viewable(Display * dpy, Window win) ++Window_Is_Viewable(xcb_connection_t * dpy, xcb_window_t win) + { +- Bool ok; +- XWindowAttributes xwa; ++ Bool ok = False; ++ xcb_get_window_attributes_cookie_t attr_cookie; ++ xcb_get_window_attributes_reply_t *xwa; + +- XGetWindowAttributes(dpy, win, &xwa); ++ attr_cookie = xcb_get_window_attributes (dpy, win); ++ xwa = xcb_get_window_attributes_reply (dpy, attr_cookie, NULL); + +- ok = (xwa.class == InputOutput) && (xwa.map_state == IsViewable); ++ if (xwa) { ++ ok = (xwa->_class == XCB_WINDOW_CLASS_INPUT_OUTPUT) && ++ (xwa->map_state == XCB_MAP_STATE_VIEWABLE); ++ free (xwa); ++ } + + return ok; + } +@@ -70,24 +82,32 @@ Window_Is_Viewable(Display * dpy, Window win) + * Children are searched in top-down stacking order. + * The first matching window is returned, None if no match is found. + */ +-static Window +-Find_Client_In_Children(Display * dpy, Window win) ++static xcb_window_t ++Find_Client_In_Children(xcb_connection_t * dpy, xcb_window_t win) + { +- Window root, parent; +- Window *children; ++ xcb_query_tree_cookie_t qt_cookie; ++ xcb_query_tree_reply_t *tree; ++ xcb_window_t *children; + unsigned int n_children; + int i; + +- if (!XQueryTree(dpy, win, &root, &parent, &children, &n_children)) +- return None; +- if (!children) +- return None; ++ qt_cookie = xcb_query_tree (dpy, win); ++ tree = xcb_query_tree_reply (dpy, qt_cookie, NULL); ++ if (!tree) ++ return XCB_WINDOW_NONE; ++ n_children = xcb_query_tree_children_length (tree); ++ if (!n_children) { ++ free (tree); ++ return XCB_WINDOW_NONE; ++ } ++ children = xcb_query_tree_children (tree); + + /* Check each child for WM_STATE and other validity */ +- win = None; ++ win = XCB_WINDOW_NONE; + for (i = (int) n_children - 1; i >= 0; i--) { + if (!Window_Is_Viewable(dpy, children[i])) { +- children[i] = None; /* Don't bother descending into this one */ ++ /* Don't bother descending into this one */ ++ children[i] = XCB_WINDOW_NONE; + continue; + } + if (!Window_Has_Property(dpy, children[i], atom_wm_state)) +@@ -100,15 +120,15 @@ Find_Client_In_Children(Display * dpy, Window win) + + /* No children matched, now descend into each child */ + for (i = (int) n_children - 1; i >= 0; i--) { +- if (children[i] == None) ++ if (children[i] == XCB_WINDOW_NONE) + continue; + win = Find_Client_In_Children(dpy, children[i]); +- if (win != None) ++ if (win != XCB_WINDOW_NONE) + break; + } + + done: +- XFree(children); ++ free (tree); /* includes children */ + + return win; + } +@@ -116,49 +136,68 @@ Find_Client_In_Children(Display * dpy, Window win) + /* + * Find virtual roots (_NET_VIRTUAL_ROOTS) + */ +-static unsigned long * +-Find_Roots(Display * dpy, Window root, unsigned int *num) ++static xcb_window_t * ++Find_Roots(xcb_connection_t * dpy, xcb_window_t root, unsigned int *num) + { +- Atom type_ret; +- int format_ret; +- unsigned char *prop_ret; +- unsigned long bytes_after, num_ret; +- Atom atom; ++ xcb_atom_t atom = XCB_ATOM_NONE; ++ xcb_intern_atom_cookie_t atom_cookie; ++ xcb_intern_atom_reply_t *atom_reply; ++ ++ xcb_get_property_cookie_t prop_cookie; ++ xcb_get_property_reply_t *prop_reply; ++ ++ xcb_window_t *prop_ret = NULL; + + *num = 0; +- atom = XInternAtom(dpy, "_NET_VIRTUAL_ROOTS", False); ++ ++ atom_cookie = xcb_intern_atom (dpy, False, strlen("_NET_VIRTUAL_ROOTS"), ++ "_NET_VIRTUAL_ROOTS"); ++ atom_reply = xcb_intern_atom_reply (dpy, atom_cookie, NULL); ++ if (atom_reply) { ++ atom = atom_reply->atom; ++ free (atom_reply); ++ } + if (!atom) + return NULL; + +- type_ret = None; +- prop_ret = NULL; +- if (XGetWindowProperty(dpy, root, atom, 0, 0x7fffffff, False, +- XA_WINDOW, &type_ret, &format_ret, &num_ret, +- &bytes_after, &prop_ret) != Success) ++ prop_cookie = xcb_get_property (dpy, False, root, atom, XCB_ATOM_WINDOW, ++ 0, 0x7fffffff); ++ prop_reply = xcb_get_property_reply (dpy, prop_cookie, NULL); ++ if (!prop_reply) + return NULL; + +- if (prop_ret && type_ret == XA_WINDOW && format_ret == 32) { +- *num = num_ret; +- return ((unsigned long *) prop_ret); ++ if ((prop_reply->value_len > 0) && (prop_reply->type == XCB_ATOM_WINDOW) ++ && (prop_reply->format == 32)) { ++ int length = xcb_get_property_value_length (prop_reply); ++ prop_ret = malloc(length); ++ if (prop_ret) { ++ memcpy (prop_ret, xcb_get_property_value(prop_reply), length); ++ *num = prop_reply->value_len; ++ } + } +- if (prop_ret) +- XFree(prop_ret); ++ free (prop_reply); + +- return NULL; ++ return prop_ret; + } + + /* + * Find child window at pointer location + */ +-static Window +-Find_Child_At_Pointer(Display * dpy, Window win) ++static xcb_window_t ++Find_Child_At_Pointer(xcb_connection_t * dpy, xcb_window_t win) + { +- Window root_return, child_return; +- int dummyi; +- unsigned int dummyu; ++ xcb_window_t child_return = XCB_WINDOW_NONE; ++ ++ xcb_query_pointer_cookie_t qp_cookie; ++ xcb_query_pointer_reply_t *qp_reply; + +- XQueryPointer(dpy, win, &root_return, &child_return, +- &dummyi, &dummyi, &dummyi, &dummyi, &dummyu); ++ qp_cookie = xcb_query_pointer (dpy, win); ++ qp_reply = xcb_query_pointer_reply (dpy, qp_cookie, NULL); ++ ++ if (qp_reply) { ++ child_return = qp_reply->child; ++ free (qp_reply); ++ } + + return child_return; + } +@@ -175,12 +214,12 @@ Find_Child_At_Pointer(Display * dpy, Window win) + * This will of course work only if the virtual roots are children of the real + * root. + */ +-Window +-Find_Client(Display * dpy, Window root, Window subwin) ++xcb_window_t ++Find_Client(xcb_connection_t * dpy, xcb_window_t root, xcb_window_t subwin) + { +- unsigned long *roots; ++ xcb_window_t *roots; + unsigned int i, n_roots; +- Window win; ++ xcb_window_t win; + + /* Check if subwin is a virtual root */ + roots = Find_Roots(dpy, root, &n_roots); +@@ -188,16 +227,24 @@ Find_Client(Display * dpy, Window root, Window subwin) + if (subwin != roots[i]) + continue; + win = Find_Child_At_Pointer(dpy, subwin); +- if (win == None) ++ if (win == XCB_WINDOW_NONE) + return subwin; /* No child - Return virtual root. */ + subwin = win; + break; + } +- if (roots) +- XFree(roots); ++ free (roots); + +- if (atom_wm_state == None) { +- atom_wm_state = XInternAtom(dpy, "WM_STATE", False); ++ if (atom_wm_state == XCB_ATOM_NONE) { ++ xcb_intern_atom_cookie_t atom_cookie; ++ xcb_intern_atom_reply_t *atom_reply; ++ ++ atom_cookie = xcb_intern_atom (dpy, False, ++ strlen("WM_STATE"), "WM_STATE"); ++ atom_reply = xcb_intern_atom_reply (dpy, atom_cookie, NULL); ++ if (atom_reply) { ++ atom_wm_state = atom_reply->atom; ++ free (atom_reply); ++ } + if (!atom_wm_state) + return subwin; + } +@@ -208,7 +255,7 @@ Find_Client(Display * dpy, Window root, Window subwin) + + /* Attempt to find a client window in subwin's children */ + win = Find_Client_In_Children(dpy, subwin); +- if (win != None) ++ if (win != XCB_WINDOW_NONE) + return win; /* Found a client */ + + /* Did not find a client */ +diff --git a/clientwin.h b/clientwin.h +index 9fc59b5..05aa202 100644 +--- a/clientwin.h ++++ b/clientwin.h +@@ -22,8 +22,10 @@ + #ifndef _CLIENTWIN_H_ + #define _CLIENTWIN_H_ + +-#include <X11/Xlib.h> ++#include <xcb/xcb.h> ++#include <xcb/xproto.h> + +-extern Window Find_Client(Display * dpy, Window root, Window target_win); ++extern xcb_window_t Find_Client(xcb_connection_t * dpy, xcb_window_t root, ++ xcb_window_t target_win); + + #endif +diff --git a/configure.ac b/configure.ac +index 7ef640a..3337c6c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -41,8 +41,10 @@ XORG_DEFAULT_OPTIONS + AC_CHECK_FUNCS([strlcat]) + + # Checks for pkg-config packages +-PKG_CHECK_MODULES(XWININFO, xext x11 [xproto >= 7.0.17]) +-AC_SUBST(XWININFO_CFLAGS) +-AC_SUBST(XWININFO_LIBS) ++PKG_CHECK_MODULES(XWININFO, [xcb >= 1.6] xcb-icccm xcb-shape) ++# Even when using xcb, xproto is still required for Xfuncproto.h ++# and libX11 headers for cursorfont.h ++PKG_CHECK_MODULES(XLIB, x11 [xproto >= 7.0.17]) ++XWININFO_CFLAGS="${XWININFO_CFLAGS} ${XLIB_CFLAGS}" + + AC_OUTPUT([Makefile]) +diff --git a/dsimple.c b/dsimple.c +index 51df01f..d1accf7 100644 +--- a/dsimple.c ++++ b/dsimple.c +@@ -1,4 +1,26 @@ + /* ++ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ * DEALINGS IN THE SOFTWARE. ++ */ ++/* + + Copyright 1993, 1998 The Open Group + +@@ -26,19 +48,14 @@ from The Open Group. + + */ + +-#include <X11/Xos.h> +-#include <X11/Xlib.h> +-#include <X11/Xutil.h> ++#include <xcb/xcb.h> ++#include <xcb/xproto.h> ++#include <xcb/xcb_icccm.h> + #include <X11/cursorfont.h> + #include <stdio.h> + #include <stdlib.h> + #include <stdarg.h> +-/* +- * Other_stuff.h: Definitions of routines in other_stuff. +- * +- * Written by Mark Lillibridge. Last updated 7/1/87 +- */ +- ++#include <string.h> + #include "clientwin.h" + #include "dsimple.h" + +@@ -46,72 +63,33 @@ from The Open Group. + * Just_display: A group of routines designed to make the writing of simple + * X11 applications which open a display but do not open + * any windows much faster and easier. Unless a routine says +- * otherwise, it may be assumed to require program_name, dpy, +- * and screen already defined on entry. ++ * otherwise, it may be assumed to require program_name ++ * to be already defined on entry. + * + * Written by Mark Lillibridge. Last updated 7/1/87 + */ + + +-/* This stuff is defined in the calling program by just_display.h */ ++/* This stuff is defined in the calling program by dsimple.h */ + char *program_name = "unknown_program"; +-Display *dpy = NULL; +-int screen = 0; +- + + /* +- * Get_Display_Name (argc, argv) Look for -display, -d, or host:dpy (obselete) +- * If found, remove it from command line. Don't go past a lone -. ++ * Get_Display_Name (argc, argv) - return string representing display name ++ * that would be used given the specified argument (i.e. if it's NULL, check ++ * getenv("DISPLAY") - always returns a non-NULL pointer, though it may be ++ * an unwritable constant, so is safe to printf() on platforms that crash ++ * on NULL printf arguments. + */ +-char *Get_Display_Name ( +- int *pargc, /* MODIFIED */ +- char **argv) /* MODIFIED */ ++const char *Get_Display_Name (const char *display_name) + { +- int argc = *pargc; +- char **pargv = argv+1; +- char *displayname = NULL; +- int i; +- +- for (i = 1; i < argc; i++) { +- char *arg = argv[i]; +- +- if (!strcmp (arg, "-display") || !strcmp (arg, "-d")) { +- if (++i >= argc) usage (); ++ const char *name = display_name; + +- displayname = argv[i]; +- *pargc -= 2; +- continue; +- } +- if (!strcmp (arg,"-")) { +- while (i<argc) +- *pargv++ = argv[i++]; +- break; +- } +- *pargv++ = arg; ++ if (!name) { ++ name = getenv ("DISPLAY"); ++ if (!name) ++ name = ""; + } +- +- *pargv = NULL; +- return (displayname); +-} +- +- +- +-/* +- * Open_Display: Routine to open a display with correct error handling. +- * Does not require dpy or screen defined on entry. +- */ +-Display *Open_Display (char *display_name) +-{ +- Display *d; +- +- d = XOpenDisplay (display_name); +- if (d == NULL) { +- fprintf (stderr, "%s: unable to open display '%s'\n", +- program_name, XDisplayName (display_name)); +- exit (1); +- } +- +- return (d); ++ return (name); + } + + +@@ -120,154 +98,115 @@ Display *Open_Display (char *display_name) + * it calls Get_Display_Name) and then stores a + * pointer to it in dpy. The default screen + * for this display is then stored in screen. +- * Does not require dpy or screen defined. + */ + void Setup_Display_And_Screen ( +- int *argc, /* MODIFIED */ +- char **argv) /* MODIFIED */ ++ const char *display_name, ++ xcb_connection_t **dpy, /* MODIFIED */ ++ xcb_screen_t **screen) /* MODIFIED */ + { +- char *displayname = NULL; ++ int screen_number, i; + +- displayname = Get_Display_Name (argc, argv); +- dpy = Open_Display (displayname); +- screen = XDefaultScreen (dpy); +-} ++ /* Open Display */ ++ *dpy = xcb_connect (display_name, &screen_number); ++ if (xcb_connection_has_error (*dpy)) { ++ Fatal_Error ("unable to open display \"%s\"", ++ Get_Display_Name(display_name) ); ++ } + +-/* +- * Close_Display: Close display +- */ +-void Close_Display (void) +-{ +- if (dpy == NULL) +- return; ++ if (screen) { ++ /* find our screen */ ++ const xcb_setup_t *setup = xcb_get_setup(*dpy); ++ xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(setup); + +- XCloseDisplay (dpy); +- dpy = NULL; ++ for (i = 0; i < screen_number; i++) ++ xcb_screen_next(&screen_iter); ++ *screen = screen_iter.data; ++ } + } + +- + /* +- * Select_Window_Args: a rountine to provide a common interface for +- * applications that need to allow the user to select one +- * window on the screen for special consideration. +- * This routine implements the following command line +- * arguments: +- * +- * -root Selects the root window. +- * -id <id> Selects window with id <id>. <id> may +- * be either in decimal or hex. +- * -name <name> Selects the window with name <name>. +- * +- * Call as Select_Window_Args(&argc, argv) in main before +- * parsing any of your program's command line arguments. +- * Select_Window_Args will remove its arguments so that +- * your program does not have to worry about them. +- * The window returned is the window selected or 0 if +- * none of the above arguments was present. If 0 is +- * returned, Select_Window should probably be called after +- * all command line arguments, and other setup is done. +- * For examples of usage, see xwininfo, xwd, or xprop. ++ * xcb equivalent of XCreateFontCursor + */ +-Window Select_Window_Args ( +- int *rargc, +- char **argv) +-#define ARGC (*rargc) ++static xcb_cursor_t ++Create_Font_Cursor (xcb_connection_t *dpy, uint16_t glyph) + { +- int nargc = 1; +- int argc; +- char **nargv; +- Window w = 0; +- +- nargv = argv+1; argc = ARGC; +-#define OPTION argv[0] +-#define NXTOPTP ++argv, --argc>0 +-#define NXTOPT if (++argv, --argc==0) usage() +-#define COPYOPT nargv++[0]=OPTION, nargc++ +- +- while (NXTOPTP) { +- if (!strcmp (OPTION, "-")) { +- COPYOPT; +- while (NXTOPTP) +- COPYOPT; +- break; +- } +- if (!strcmp (OPTION, "-root")) { +- w = RootWindow (dpy, screen); +- continue; +- } +- if (!strcmp (OPTION, "-name")) { +- NXTOPT; +- w = Window_With_Name (dpy, RootWindow (dpy, screen), OPTION); +- if (!w) +- Fatal_Error ("No window with name %s exists!", OPTION); +- continue; +- } +- if (!strcmp (OPTION, "-id")) { +- NXTOPT; +- w = 0; +- sscanf (OPTION, "0x%lx", &w); +- if (!w) +- sscanf (OPTION, "%lu", &w); +- if (!w) +- Fatal_Error ("Invalid window id format: %s.", OPTION); +- continue; +- } +- COPYOPT; +- } +- ARGC = nargc; ++ static xcb_font_t cursor_font; ++ xcb_cursor_t cursor; + +- return (w); +-} ++ if (!cursor_font) { ++ cursor_font = xcb_generate_id (dpy); ++ xcb_open_font (dpy, cursor_font, strlen ("cursor"), "cursor"); ++ } + +-/* +- * Other_stuff: A group of routines which do common X11 tasks. +- * +- * Written by Mark Lillibridge. Last updated 7/1/87 +- */ ++ cursor = xcb_generate_id (dpy); ++ xcb_create_glyph_cursor (dpy, cursor, cursor_font, cursor_font, ++ glyph, glyph + 1, ++ 0, 0, 0, 0xffff, 0xffff, 0xffff); /* rgb, rgb */ + ++ return cursor; ++} + + /* + * Routine to let user select a window using the mouse + */ + +-Window Select_Window (Display *dpy, int descend) ++xcb_window_t Select_Window(xcb_connection_t *dpy, ++ const xcb_screen_t *screen, ++ int descend) + { +- int status; +- Cursor cursor; +- XEvent event; +- Window target_win = None, root = RootWindow (dpy,screen); ++ xcb_cursor_t cursor; ++ xcb_generic_event_t *event; ++ xcb_window_t target_win = XCB_WINDOW_NONE; ++ xcb_window_t root = screen->root; + int buttons = 0; ++ xcb_generic_error_t *err; ++ xcb_grab_pointer_cookie_t grab_cookie; ++ xcb_grab_pointer_reply_t *grab_reply; + + /* Make the target cursor */ +- cursor = XCreateFontCursor (dpy, XC_crosshair); ++ cursor = Create_Font_Cursor (dpy, XC_crosshair); + + /* Grab the pointer using target cursor, letting it room all over */ +- status = XGrabPointer (dpy, root, False, +- ButtonPressMask|ButtonReleaseMask, GrabModeSync, +- GrabModeAsync, root, cursor, CurrentTime); +- if (status != GrabSuccess) Fatal_Error ("Can't grab the mouse."); ++ grab_cookie = xcb_grab_pointer ++ (dpy, False, root, ++ XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE, ++ XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, ++ root, cursor, XCB_TIME_CURRENT_TIME); ++ grab_reply = xcb_grab_pointer_reply (dpy, grab_cookie, &err); ++ if (grab_reply->status != XCB_GRAB_STATUS_SUCCESS) ++ Fatal_Error ("Can't grab the mouse."); + + /* Let the user select a window... */ +- while ((target_win == None) || (buttons != 0)) { ++ while ((target_win == XCB_WINDOW_NONE) || (buttons != 0)) { + /* allow one more event */ +- XAllowEvents (dpy, SyncPointer, CurrentTime); +- XWindowEvent (dpy, root, ButtonPressMask|ButtonReleaseMask, &event); +- switch (event.type) { +- case ButtonPress: +- if (target_win == None) { +- target_win = event.xbutton.subwindow; /* window selected */ +- if (target_win == None) target_win = root; ++ xcb_allow_events (dpy, XCB_ALLOW_SYNC_POINTER, XCB_TIME_CURRENT_TIME); ++ xcb_flush (dpy); ++ event = xcb_wait_for_event (dpy); ++ switch (event->response_type & 0x7f) { ++ case XCB_BUTTON_PRESS: ++ { ++ xcb_button_press_event_t *bp = (xcb_button_press_event_t *)event; ++ ++ if (target_win == XCB_WINDOW_NONE) { ++ target_win = bp->child; /* window selected */ ++ if (target_win == XCB_WINDOW_NONE) ++ target_win = root; + } + buttons++; + break; +- case ButtonRelease: ++ } ++ case XCB_BUTTON_RELEASE: + if (buttons > 0) /* there may have been some down before we started */ + buttons--; + break; ++ default: ++ /* just discard all other events */ ++ break; + } ++ free (event); + } + +- XUngrabPointer (dpy, CurrentTime); /* Done with pointer */ ++ xcb_ungrab_pointer (dpy, XCB_TIME_CURRENT_TIME); /* Done with pointer */ + + if (!descend || (target_win == root)) + return (target_win); +@@ -285,36 +224,104 @@ Window Select_Window (Display *dpy, int descend) + * one found will be returned. Only top and its subwindows + * are looked at. Normally, top should be the RootWindow. + */ +-Window Window_With_Name ( +- Display *dpy, +- Window top, +- char *name) ++ ++struct wininfo_cookies { ++ xcb_get_property_cookie_t get_wm_name; ++ xcb_query_tree_cookie_t query_tree; ++}; ++ ++static xcb_window_t ++recursive_Window_With_Name ( ++ xcb_connection_t *dpy, ++ xcb_window_t window, ++ struct wininfo_cookies *cookies, ++ const char *name) + { +- Window *children, dummy; ++ xcb_window_t *children; + unsigned int nchildren; + int i; +- Window w=0; +- char *window_name; ++ xcb_window_t w = 0; ++ xcb_generic_error_t *err; ++ xcb_get_text_property_reply_t prop; ++ xcb_query_tree_reply_t *tree; ++ struct wininfo_cookies *child_cookies; ++ ++ if (xcb_get_wm_name_reply (dpy, cookies->get_wm_name, &prop, &err)) { ++ /* can't use strcmp, since prop.name is not null terminated */ ++ if (strncmp (prop.name, name, prop.name_len) == 0) { ++ w = window; ++ } ++ ++ xcb_get_text_property_reply_wipe (&prop); + +- if (XFetchName (dpy, top, &window_name) && !strcmp (window_name, name)) +- return (top); ++ if (w) ++ { ++ xcb_discard_reply (dpy, cookies->query_tree.sequence); ++ return w; ++ } ++ } else if (err) { ++ if (err->response_type == 0) ++ Print_X_Error (dpy, err); ++ return 0; ++ } + +- if (!XQueryTree (dpy, top, &dummy, &dummy, &children, &nchildren)) +- return (0); ++ tree = xcb_query_tree_reply (dpy, cookies->query_tree, &err); ++ if (!tree) { ++ if (err->response_type == 0) ++ Print_X_Error (dpy, err); ++ return 0; ++ } ++ ++ nchildren = xcb_query_tree_children_length (tree); ++ children = xcb_query_tree_children (tree); ++ child_cookies = calloc(nchildren, sizeof(struct wininfo_cookies)); ++ ++ if (child_cookies == NULL) ++ Fatal_Error("Failed to allocate memory in recursive_Window_With_Name"); ++ ++ for (i = 0; i < nchildren; i++) { ++ child_cookies[i].get_wm_name = xcb_get_wm_name (dpy, children[i]); ++ child_cookies[i].query_tree = xcb_query_tree (dpy, children[i]); ++ } ++ xcb_flush (dpy); + + for (i = 0; i < nchildren; i++) { +- w = Window_With_Name (dpy, children[i], name); ++ w = recursive_Window_With_Name (dpy, children[i], ++ &child_cookies[i], name); + if (w) + break; + } +- if (children) XFree ((char *)children); ++ ++ if (w) ++ { ++ /* clean up remaining replies */ ++ for (/* keep previous i */; i < nchildren; i++) { ++ xcb_discard_reply (dpy, child_cookies[i].get_wm_name.sequence); ++ xcb_discard_reply (dpy, child_cookies[i].query_tree.sequence); ++ } ++ } ++ ++ free (child_cookies); ++ free (tree); /* includes storage for children[] */ + return (w); + } + ++xcb_window_t ++Window_With_Name ( ++ xcb_connection_t *dpy, ++ xcb_window_t top, ++ const char *name) ++{ ++ struct wininfo_cookies cookies; ++ ++ cookies.get_wm_name = xcb_get_wm_name (dpy, top); ++ cookies.query_tree = xcb_query_tree (dpy, top); ++ return recursive_Window_With_Name(dpy, top, &cookies, name); ++} ++ + + /* + * Standard fatal error routine - call like printf +- * Does not require dpy or screen defined. + */ + void Fatal_Error (char *msg, ...) + { +@@ -326,6 +333,127 @@ void Fatal_Error (char *msg, ...) + vfprintf (stderr, msg, args); + va_end (args); + fprintf (stderr, "\n"); +- Close_Display (); + exit (EXIT_FAILURE); + } ++ ++/* ++ * Print X error information like the default Xlib error handler ++ */ ++void ++Print_X_Error ( ++ xcb_connection_t *dpy, ++ xcb_generic_error_t *err ++ ) ++{ ++ char buffer[256] = ""; ++ ++ if ((err == NULL) || (err->response_type != 0)) /* not an error */ ++ return; ++ ++ /* Todo: find a more user friendly way to show request/extension info */ ++ if (err->error_code >= 128) ++ { ++ fprintf (stderr, "X Extension Error: Error code %d\n", ++ err->error_code); ++ } ++ else ++ { ++ switch (err->error_code) ++ { ++ case XCB_REQUEST: ++ snprintf (buffer, sizeof(buffer), ": Bad Request"); ++ break; ++ ++ case XCB_VALUE: ++ snprintf (buffer, sizeof(buffer), ++ ": Bad Value: 0x%x", err->resource_id); ++ break; ++ ++ case XCB_WINDOW: ++ snprintf (buffer, sizeof(buffer), ++ ": Bad Window: 0x%x", err->resource_id); ++ break; ++ ++ case XCB_PIXMAP: ++ snprintf (buffer, sizeof(buffer), ++ ": Bad Pixmap: 0x%x", err->resource_id); ++ break; ++ ++ case XCB_ATOM: ++ snprintf (buffer, sizeof(buffer), ++ ": Bad Atom: 0x%x", err->resource_id); ++ break; ++ ++ case XCB_CURSOR: ++ snprintf (buffer, sizeof(buffer), ++ ": Bad Cursor: 0x%x", err->resource_id); ++ break; ++ ++ case XCB_FONT: ++ snprintf (buffer, sizeof(buffer), ++ ": Bad Font: 0x%x", err->resource_id); ++ break; ++ ++ case XCB_MATCH: ++ snprintf (buffer, sizeof(buffer), ": Bad Match"); ++ break; ++ ++ case XCB_DRAWABLE: ++ snprintf (buffer, sizeof(buffer), ++ ": Bad Drawable: 0x%x", err->resource_id); ++ break; ++ ++ case XCB_ACCESS: ++ snprintf (buffer, sizeof(buffer), ": Access Denied"); ++ break; ++ ++ case XCB_ALLOC: ++ snprintf (buffer, sizeof(buffer), ++ ": Server Memory Allocation Failure"); ++ break; ++ ++ case XCB_COLORMAP: ++ snprintf (buffer, sizeof(buffer), ++ ": Bad Color: 0x%x", err->resource_id); ++ break; ++ ++ case XCB_G_CONTEXT: ++ snprintf (buffer, sizeof(buffer), ++ ": Bad GC: 0x%x", err->resource_id); ++ break; ++ ++ case XCB_ID_CHOICE: ++ snprintf (buffer, sizeof(buffer), ++ ": Bad XID: 0x%x", err->resource_id); ++ break; ++ ++ case XCB_NAME: ++ snprintf (buffer, sizeof(buffer), ++ ": Bad Name"); ++ break; ++ ++ case XCB_LENGTH: ++ snprintf (buffer, sizeof(buffer), ++ ": Bad Request Length"); ++ break; ++ ++ case XCB_IMPLEMENTATION: ++ snprintf (buffer, sizeof(buffer), ++ ": Server Implementation Failure"); ++ break; ++ ++ default: ++ snprintf (buffer, sizeof(buffer), ": Unknown error"); ++ break; ++ } ++ fprintf (stderr, "X Error: %d%s\n", err->error_code, buffer); ++ } ++ ++ fprintf (stderr, " Request Major code: %d\n", err->major_code); ++ if (err->major_code >= 128) ++ { ++ fprintf (stderr, " Request Minor code: %d\n", err->minor_code); ++ } ++ ++ fprintf (stderr, " Request serial number: %d\n", err->full_sequence); ++} +diff --git a/dsimple.h b/dsimple.h +index b0d76a5..1a689e0 100644 +--- a/dsimple.h ++++ b/dsimple.h +@@ -27,55 +27,33 @@ from The Open Group. + */ + + /* +- * Just_display.h: This file contains the definitions needed to use the +- * functions in just_display.c. It also declares the global +- * variables dpy, screen, and program_name which are needed to +- * use just_display.c. ++ * dsimple.h: This file contains the definitions needed to use the ++ * functions in dsimple.c. It also declares the global ++ * variable program_name which is needed to use dsimple.c. + * +- * Written by Mark Lillibridge. Last updated 7/1/87 +- * +- * Send bugs, etc. to chariot@athena.mit.edu. ++ * Written by Mark Lillibridge for Xlib. Last updated 7/1/87 ++ * Ported to XCB over two decades later. + */ + + #include <X11/Xfuncproto.h> ++#include <xcb/xcb.h> ++#include <xcb/xproto.h> + +- /* Simple helper macros */ +-#ifndef MAX +-#define MAX(a,b) (((a)>(b))?(a):(b)) +-#endif /* MAX */ +-#ifndef MIN +-#define MIN(a,b) (((a)<(b))?(a):(b)) +-#endif /* MIN */ ++typedef enum { False = 0, True } Bool; + + /* Global variables used by routines in dsimple.c */ + + extern char *program_name; /* Name of this program */ +-extern Display *dpy; /* The current display */ +-extern int screen; /* The current screen */ +- +-#define INIT_NAME program_name=argv[0] /* use this in main to setup +- program_name */ + +- /* Declaritions for functions in dsimple.c */ ++ /* Declarations for functions in dsimple.c */ + +-char *Get_Display_Name(int *, char **); +-Display *Open_Display(char *); +-void Setup_Display_And_Screen(int *, char **); +-void Close_Display(void); +-Window Select_Window_Args(int *, char **); +-void usage(void); ++const char *Get_Display_Name (const char *displayname); ++void Setup_Display_And_Screen (const char *displayname, ++ xcb_connection_t **dpy, xcb_screen_t **screen); + +-#define X_USAGE "[host:display]" /* X arguments handled by +- Get_Display_Name */ +- +-/* +- * Other_stuff.h: Definitions of routines in other_stuff. +- * +- * Written by Mark Lillibridge. Last updated 7/1/87 +- * +- * Send bugs, etc. to chariot@athena.mit.edu. +- */ ++xcb_window_t Select_Window(xcb_connection_t *, const xcb_screen_t *, int); ++xcb_window_t Window_With_Name(xcb_connection_t *, xcb_window_t, const char *); + +-Window Select_Window(Display *, int); +-Window Window_With_Name(Display *, Window, char *); + void Fatal_Error(char *, ...) _X_NORETURN _X_ATTRIBUTE_PRINTF(1, 2); ++ ++void Print_X_Error (xcb_connection_t *, xcb_generic_error_t *); +diff --git a/xwininfo.c b/xwininfo.c +index 6b2f728..ea1de2d 100644 +--- a/xwininfo.c ++++ b/xwininfo.c +@@ -1,5 +1,5 @@ + /* +- * Copyright © 1999 Sun Microsystems, Inc. All rights reserved. ++ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), +@@ -64,14 +64,16 @@ of the copyright holder. + */ + + #include "config.h" +-#include <X11/Xlib.h> +-#include <X11/Xutil.h> +-#include <X11/Xatom.h> +-#include <X11/Xos.h> +-#include <X11/extensions/shape.h> +-#include <X11/Xlocale.h> ++ ++#include <xcb/xcb.h> ++#include <xcb/xproto.h> ++#include <xcb/xcb_icccm.h> ++#include <xcb/shape.h> ++ + #include <stdio.h> + #include <stdlib.h> ++#include <string.h> ++#include <locale.h> + + /* Include routines to handle parsing defaults */ + #include "dsimple.h" +@@ -81,29 +83,54 @@ typedef struct { + const char *name; + } binding; + +-static void scale_init (void); ++/* Information we keep track of for each window to allow prefetching/reusing */ ++struct wininfo { ++ xcb_window_t window; ++ ++ /* cookies for requests we've sent */ ++ xcb_get_geometry_cookie_t geometry_cookie; ++ xcb_get_property_cookie_t wm_name_cookie; ++ xcb_get_property_cookie_t wm_class_cookie; ++ xcb_translate_coordinates_cookie_t trans_coords_cookie; ++ xcb_query_tree_cookie_t tree_cookie; ++ xcb_get_window_attributes_cookie_t attr_cookie; ++ xcb_get_property_cookie_t normal_hints_cookie; ++ xcb_get_property_cookie_t hints_cookie; ++ xcb_get_property_cookie_t zoom_cookie; ++ ++ /* cached results from previous requests */ ++ xcb_get_geometry_reply_t * geometry; ++ xcb_get_window_attributes_reply_t * win_attributes; ++ xcb_size_hints_t * normal_hints; ++}; ++ ++static void scale_init (xcb_screen_t *scrn); + static char *nscale (int, int, int, char *, size_t); + static char *xscale (int); + static char *yscale (int); + static char *bscale (int); +-static int bad_window_handler (Display *, XErrorEvent *); + int main (int, char **); + static const char *LookupL (long, const binding *); + static const char *Lookup (int, const binding *); +-static void Display_Window_Id (Window, int); +-static void Display_Stats_Info (Window); +-static void Display_Bits_Info (Window); ++static void Display_Window_Id (struct wininfo *, Bool); ++static void Display_Stats_Info (struct wininfo *); ++static void Display_Bits_Info (struct wininfo *); + static void Display_Event_Mask (long); +-static void Display_Events_Info (Window); +-static void Display_Tree_Info (Window, int); +-static void display_tree_info_1 (Window, int, int); +-static void Display_Hints (XSizeHints *); +-static void Display_Size_Hints (Window); +-static void Display_Window_Shape (Window); +-static void Display_WM_Info (Window); ++static void Display_Events_Info (struct wininfo *); ++static void Display_Tree_Info (struct wininfo *, int); ++static void display_tree_info_1 (struct wininfo *, int, int); ++static void Display_Hints (xcb_size_hints_t *); ++static void Display_Size_Hints (struct wininfo *); ++static void Display_Window_Shape (xcb_window_t); ++static void Display_WM_Info (struct wininfo *); ++static void wininfo_wipe (struct wininfo *); + + static const char *window_id_format = "0x%lx"; + ++static xcb_connection_t *dpy; ++static xcb_screen_t *screen; ++static xcb_generic_error_t *err; ++ + #ifndef HAVE_STRLCAT + static size_t strlcat (char *dst, const char *src, size_t dstsize) + { +@@ -164,19 +191,18 @@ usage (void) + * + */ + +-#define getdsp(var,fn) var = fn (dpy, DefaultScreen (dpy)) + static int xp = 0, xmm = 0; + static int yp = 0, ymm = 0; + static int bp = 0, bmm = 0; + static int english = 0, metric = 0; + + static void +-scale_init (void) ++scale_init (xcb_screen_t *screen) + { +- getdsp (yp, DisplayHeight); +- getdsp (ymm, DisplayHeightMM); +- getdsp (xp, DisplayWidth); +- getdsp (xmm, DisplayWidthMM); ++ xp = screen->width_in_pixels; ++ yp = screen->height_in_pixels; ++ xmm = screen->width_in_millimeters; ++ ymm = screen->height_in_millimeters; + bp = xp + yp; + bmm = xmm + ymm; + } +@@ -250,9 +276,6 @@ static char xbuf[BUFSIZ]; + static char * + xscale (int x) + { +- if (!xp) { +- scale_init (); +- } + return (nscale (x, xp, xmm, xbuf, sizeof(xbuf))); + } + +@@ -260,9 +283,6 @@ static char ybuf[BUFSIZ]; + static char * + yscale (int y) + { +- if (!yp) { +- scale_init (); +- } + return (nscale (y, yp, ymm, ybuf, sizeof(ybuf))); + } + +@@ -270,53 +290,57 @@ static char bbuf[BUFSIZ]; + static char * + bscale (int b) + { +- if (!bp) { +- scale_init (); +- } + return (nscale (b, bp, bmm, bbuf, sizeof(bbuf))); + } + + /* end of pixel to inch, metric converter */ + +-/* This handler is enabled when we are checking +- to see if the -id the user specified is valid. */ +- +-/* ARGSUSED */ +-static int +-bad_window_handler (Display *disp, XErrorEvent *err) +-{ +- char badid[20]; +- +- snprintf (badid, sizeof(badid), window_id_format, err->resourceid); +- Fatal_Error ("No such window with id %s.", badid); +- exit (1); +- return 0; +-} +- +- + int + main (int argc, char **argv) + { + register int i; + int tree = 0, stats = 0, bits = 0, events = 0, wm = 0, size = 0, shape = 0; + int frame = 0, children = 0; +- Window window; ++ int use_root = 0; ++ xcb_window_t window = 0; ++ char *display_name = NULL; ++ const char *window_name = NULL; ++ struct wininfo wininfo; ++ struct wininfo *w = &wininfo; + +- INIT_NAME; ++ program_name = argv[0]; + + if (!setlocale (LC_ALL, "")) + fprintf (stderr, "%s: can not set locale properly\n", program_name); + +- /* Open display, handle command line arguments */ +- Setup_Display_And_Screen (&argc, argv); +- +- /* Get window selected on command line, if any */ +- window = Select_Window_Args (&argc, argv); ++ memset (w, 0, sizeof(struct wininfo)); + + /* Handle our command line arguments */ + for (i = 1; i < argc; i++) { + if (!strcmp (argv[i], "-help")) + usage (); ++ if (!strcmp (argv[i], "-display") || !strcmp (argv[i], "-d")) { ++ if (++i >= argc) ++ Fatal_Error("-display requires argument"); ++ display_name = argv[i]; ++ continue; ++ } ++ if (!strcmp (argv[i], "-root")) { ++ use_root = 1; ++ continue; ++ } ++ if (!strcmp (argv[i], "-id")) { ++ if (++i >= argc) ++ Fatal_Error("-id requires argument"); ++ window = strtoul(argv[i], NULL, 0); ++ continue; ++ } ++ if (!strcmp (argv[i], "-name")) { ++ if (++i >= argc) ++ Fatal_Error("-name requires argument"); ++ window_name = argv[i]; ++ continue; ++ } + if (!strcmp (argv[i], "-int")) { + window_id_format = "%ld"; + continue; +@@ -372,13 +396,26 @@ main (int argc, char **argv) + usage (); + } + ++ Setup_Display_And_Screen (display_name, &dpy, &screen); ++ ++ /* initialize scaling data */ ++ scale_init(screen); ++ ++ if (use_root) ++ window = screen->root; ++ else if (window_name) { ++ window = Window_With_Name (dpy, screen->root, window_name); ++ if (!window) ++ Fatal_Error ("No window with name \"%s\" exists!", window_name); ++ } ++ + /* If no window selected on command line, let user pick one the hard way */ + if (!window) { + printf ("\n" + "xwininfo: Please select the window about which you\n" + " would like information by clicking the\n" + " mouse in that window.\n"); +- window = Select_Window (dpy, !frame); ++ window = Select_Window (dpy, screen, !frame); + } + + /* +@@ -391,37 +428,102 @@ main (int argc, char **argv) + * make sure that the window is valid + */ + { +- Window root; +- int x, y; +- unsigned width, height, bw, depth; +- XErrorHandler old_handler; +- +- old_handler = XSetErrorHandler (bad_window_handler); +- XGetGeometry (dpy, window, &root, &x, &y, &width, &height, &bw, &depth); +- XSync (dpy, False); +- (void) XSetErrorHandler (old_handler); ++ xcb_get_geometry_cookie_t gg_cookie = ++ xcb_get_geometry (dpy, window); ++ ++ w->geometry = xcb_get_geometry_reply(dpy, gg_cookie, &err); ++ ++ if (!w->geometry) { ++ char badid[20]; ++ ++ if (err) ++ Print_X_Error (dpy, err); ++ ++ snprintf (badid, sizeof(badid), window_id_format, window); ++ Fatal_Error ("No such window with id %s.", badid); ++ } + } + ++ /* Send requests to prefetch data we'll need */ ++ w->window = window; ++ w->wm_name_cookie = xcb_get_wm_name (dpy, window); ++ if (children || tree) ++ w->tree_cookie = xcb_query_tree (dpy, window); ++ if (stats) { ++ w->trans_coords_cookie = ++ xcb_translate_coordinates (dpy, window, w->geometry->root, ++ -(w->geometry->border_width), ++ -(w->geometry->border_width)); ++ } ++ if (stats || bits || events) ++ w->attr_cookie = xcb_get_window_attributes (dpy, window); ++ if (stats || size) ++ w->normal_hints_cookie = xcb_get_wm_normal_hints (dpy, window); ++ if (wm) ++ w->hints_cookie = xcb_get_wm_hints(dpy, window); ++ if (size) ++ w->zoom_cookie = xcb_get_wm_size_hints (dpy, window, ++ XCB_ATOM_WM_ZOOM_HINTS); ++ xcb_flush (dpy); ++ + printf ("\nxwininfo: Window id: "); +- Display_Window_Id (window, True); ++ Display_Window_Id (w, True); + if (children || tree) +- Display_Tree_Info (window, tree); ++ Display_Tree_Info (w, tree); + if (stats) +- Display_Stats_Info (window); ++ Display_Stats_Info (w); + if (bits) +- Display_Bits_Info (window); ++ Display_Bits_Info (w); + if (events) +- Display_Events_Info (window); ++ Display_Events_Info (w); + if (wm) +- Display_WM_Info (window); ++ Display_WM_Info (w); + if (size) +- Display_Size_Hints (window); ++ Display_Size_Hints (w); + if (shape) + Display_Window_Shape (window); + printf ("\n"); ++ ++ wininfo_wipe (w); ++ xcb_disconnect (dpy); + exit (0); + } + ++/* Ensure win_attributes field is filled in */ ++static xcb_get_window_attributes_reply_t * ++fetch_win_attributes (struct wininfo *w) ++{ ++ if (!w->win_attributes) { ++ w->win_attributes = ++ xcb_get_window_attributes_reply (dpy, w->attr_cookie, &err); ++ ++ if (!w->win_attributes) { ++ Print_X_Error (dpy, err); ++ Fatal_Error ("Can't get window attributes."); ++ } ++ } ++ return w->win_attributes; ++} ++ ++/* Ensure normal_hints field is filled in */ ++static xcb_size_hints_t * ++fetch_normal_hints (struct wininfo *w, xcb_size_hints_t *hints_return) ++{ ++ xcb_size_hints_t hints; ++ ++ if (!w->normal_hints) { ++ if (xcb_get_wm_normal_hints_reply (dpy, w->normal_hints_cookie, ++ &hints, NULL)) { ++ w->normal_hints = malloc (sizeof(xcb_size_hints_t)); ++ if (w->normal_hints) ++ memcpy(w->normal_hints, &hints, sizeof(xcb_size_hints_t)); ++ } ++ } ++ if (hints_return && w->normal_hints) ++ memcpy(hints_return, w->normal_hints, sizeof(xcb_size_hints_t)); ++ return w->normal_hints; ++} ++ + + /* + * Lookup: lookup a code in a table. +@@ -458,41 +560,37 @@ Lookup (int code, const binding *table) + + /* + * Routine to display a window id in dec/hex with name if window has one ++ * ++ * Requires wininfo members initialized: window, wm_name_cookie + */ + + static void +-Display_Window_Id (Window window, Bool newline_wanted) ++Display_Window_Id (struct wininfo *w, Bool newline_wanted) + { +- XTextProperty tp; ++ xcb_get_text_property_reply_t prop; ++ uint8_t got_reply; + +- printf (window_id_format, window); /* print id # in hex/dec */ ++ printf (window_id_format, w->window); /* print id # in hex/dec */ + +- if (!window) { ++ if (!w->window) { + printf (" (none)"); + } else { +- if (window == RootWindow (dpy, screen)) { ++ if (w->window == screen->root) { + printf (" (the root window)"); + } +- if (!XGetWMName (dpy, window, &tp)) { /* Get window name if any */ ++ /* Get window name if any */ ++ got_reply = xcb_get_wm_name_reply (dpy, w->wm_name_cookie, ++ &prop, NULL); ++ if (!got_reply || prop.name_len == 0) { + printf (" (has no name)"); +- } else if (tp.nitems > 0) { ++ } else { + printf (" \""); +- { +- int count = 0, i, ret; +- char **list = NULL; +- ret = XmbTextPropertyToTextList (dpy, &tp, &list, &count); +- if ((ret == Success || ret > 0) && list != NULL){ +- for (i = 0; i < count; i++) +- printf ("%s", list[i]); +- XFreeStringList (list); +- } else { +- printf ("%s", tp.value); +- } +- } ++ /* XXX: need to handle encoding */ ++ printf ("%.*s", prop.name_len, prop.name); + printf ("\""); + } +- else +- printf (" (has no name)"); ++ if (got_reply) ++ xcb_get_text_property_reply_wipe (&prop); + } + + if (newline_wanted) +@@ -506,211 +604,244 @@ Display_Window_Id (Window window, Bool newline_wanted) + * Display Stats on window + */ + static const binding _window_classes[] = { +- { InputOutput, "InputOutput" }, +- { InputOnly, "InputOnly" }, ++ { XCB_WINDOW_CLASS_INPUT_OUTPUT, "InputOutput" }, ++ { XCB_WINDOW_CLASS_INPUT_ONLY, "InputOnly" }, + { 0, NULL } }; + + static const binding _map_states[] = { +- { IsUnmapped, "IsUnMapped" }, +- { IsUnviewable, "IsUnviewable" }, +- { IsViewable, "IsViewable" }, ++ { XCB_MAP_STATE_UNMAPPED, "IsUnMapped" }, ++ { XCB_MAP_STATE_UNVIEWABLE, "IsUnviewable" }, ++ { XCB_MAP_STATE_VIEWABLE, "IsViewable" }, + { 0, NULL } }; + + static const binding _backing_store_states[] = { +- { NotUseful, "NotUseful" }, +- { WhenMapped, "WhenMapped" }, +- { Always, "Always" }, ++ { XCB_BACKING_STORE_NOT_USEFUL, "NotUseful" }, ++ { XCB_BACKING_STORE_WHEN_MAPPED,"WhenMapped" }, ++ { XCB_BACKING_STORE_ALWAYS, "Always" }, + { 0, NULL } }; + + static const binding _bit_gravity_states[] = { +- { ForgetGravity, "ForgetGravity" }, +- { NorthWestGravity, "NorthWestGravity" }, +- { NorthGravity, "NorthGravity" }, +- { NorthEastGravity, "NorthEastGravity" }, +- { WestGravity, "WestGravity" }, +- { CenterGravity, "CenterGravity" }, +- { EastGravity, "EastGravity" }, +- { SouthWestGravity, "SouthWestGravity" }, +- { SouthGravity, "SouthGravity" }, +- { SouthEastGravity, "SouthEastGravity" }, +- { StaticGravity, "StaticGravity" }, ++ { XCB_GRAVITY_BIT_FORGET, "ForgetGravity" }, ++ { XCB_GRAVITY_NORTH_WEST, "NorthWestGravity" }, ++ { XCB_GRAVITY_NORTH, "NorthGravity" }, ++ { XCB_GRAVITY_NORTH_EAST, "NorthEastGravity" }, ++ { XCB_GRAVITY_WEST, "WestGravity" }, ++ { XCB_GRAVITY_CENTER, "CenterGravity" }, ++ { XCB_GRAVITY_EAST, "EastGravity" }, ++ { XCB_GRAVITY_SOUTH_WEST, "SouthWestGravity" }, ++ { XCB_GRAVITY_SOUTH, "SouthGravity" }, ++ { XCB_GRAVITY_SOUTH_EAST, "SouthEastGravity" }, ++ { XCB_GRAVITY_STATIC, "StaticGravity" }, + { 0, NULL }}; + + static const binding _window_gravity_states[] = { +- { UnmapGravity, "UnmapGravity" }, +- { NorthWestGravity, "NorthWestGravity" }, +- { NorthGravity, "NorthGravity" }, +- { NorthEastGravity, "NorthEastGravity" }, +- { WestGravity, "WestGravity" }, +- { CenterGravity, "CenterGravity" }, +- { EastGravity, "EastGravity" }, +- { SouthWestGravity, "SouthWestGravity" }, +- { SouthGravity, "SouthGravity" }, +- { SouthEastGravity, "SouthEastGravity" }, +- { StaticGravity, "StaticGravity" }, ++ { XCB_GRAVITY_WIN_UNMAP, "UnmapGravity" }, ++ { XCB_GRAVITY_NORTH_WEST, "NorthWestGravity" }, ++ { XCB_GRAVITY_NORTH, "NorthGravity" }, ++ { XCB_GRAVITY_NORTH_EAST, "NorthEastGravity" }, ++ { XCB_GRAVITY_WEST, "WestGravity" }, ++ { XCB_GRAVITY_CENTER, "CenterGravity" }, ++ { XCB_GRAVITY_EAST, "EastGravity" }, ++ { XCB_GRAVITY_SOUTH_WEST, "SouthWestGravity" }, ++ { XCB_GRAVITY_SOUTH, "SouthGravity" }, ++ { XCB_GRAVITY_SOUTH_EAST, "SouthEastGravity" }, ++ { XCB_GRAVITY_STATIC, "StaticGravity" }, + { 0, NULL }}; + + static const binding _visual_classes[] = { +- { StaticGray, "StaticGray" }, +- { GrayScale, "GrayScale" }, +- { StaticColor, "StaticColor" }, +- { PseudoColor, "PseudoColor" }, +- { TrueColor, "TrueColor" }, +- { DirectColor, "DirectColor" }, ++ { XCB_VISUAL_CLASS_STATIC_GRAY, "StaticGray" }, ++ { XCB_VISUAL_CLASS_GRAY_SCALE, "GrayScale" }, ++ { XCB_VISUAL_CLASS_STATIC_COLOR,"StaticColor" }, ++ { XCB_VISUAL_CLASS_PSEUDO_COLOR,"PseudoColor" }, ++ { XCB_VISUAL_CLASS_TRUE_COLOR, "TrueColor" }, ++ { XCB_VISUAL_CLASS_DIRECT_COLOR,"DirectColor" }, + { 0, NULL }}; + ++/* ++ * Requires wininfo members initialized: ++ * window, geometry, attr_cookie, trans_coords_cookie, normal_hints_cookie ++ */ + static void +-Display_Stats_Info (Window window) ++Display_Stats_Info (struct wininfo *w) + { +- XWindowAttributes win_attributes; +- XVisualInfo vistemplate, *vinfo; +- XSizeHints hints; +- int dw = DisplayWidth (dpy, screen), dh = DisplayHeight (dpy, screen); ++ xcb_translate_coordinates_reply_t *trans_coords; ++ xcb_get_window_attributes_reply_t *win_attributes; ++ xcb_size_hints_t hints; ++ ++ int dw = screen->width_in_pixels, dh = screen->height_in_pixels; + int rx, ry, xright, ybelow; + int showright = 0, showbelow = 0; +- Status status; +- Window wmframe; +- int junk; +- long longjunk; +- Window junkwin; +- +- if (!XGetWindowAttributes (dpy, window, &win_attributes)) +- Fatal_Error ("Can't get window attributes."); +- vistemplate.visualid = XVisualIDFromVisual (win_attributes.visual); +- vinfo = XGetVisualInfo (dpy, VisualIDMask, &vistemplate, &junk); +- +- (void) XTranslateCoordinates (dpy, window, win_attributes.root, +- -win_attributes.border_width, +- -win_attributes.border_width, +- &rx, &ry, &junkwin); +- +- xright = (dw - rx - win_attributes.border_width * 2 - +- win_attributes.width); +- ybelow = (dh - ry - win_attributes.border_width * 2 - +- win_attributes.height); ++ xcb_window_t wmframe, parent; ++ ++ trans_coords = ++ xcb_translate_coordinates_reply (dpy, w->trans_coords_cookie, NULL); ++ if (!trans_coords) ++ Fatal_Error ("Can't get translated coordinates."); ++ ++ rx = trans_coords->dst_x; ++ ry = trans_coords->dst_y; ++ free (trans_coords); ++ ++ xright = (dw - rx - w->geometry->border_width * 2 - ++ w->geometry->width); ++ ybelow = (dh - ry - w->geometry->border_width * 2 - ++ w->geometry->height); ++ + + printf ("\n"); + printf (" Absolute upper-left X: %s\n", xscale (rx)); + printf (" Absolute upper-left Y: %s\n", yscale (ry)); +- printf (" Relative upper-left X: %s\n", xscale (win_attributes.x)); +- printf (" Relative upper-left Y: %s\n", yscale (win_attributes.y)); +- printf (" Width: %s\n", xscale (win_attributes.width)); +- printf (" Height: %s\n", yscale (win_attributes.height)); +- printf (" Depth: %d\n", win_attributes.depth); +- printf (" Visual: 0x%lx\n", vinfo->visualid); +- printf (" Visual Class: %s\n", Lookup (vinfo->class, _visual_classes)); +- printf (" Border width: %s\n", bscale (win_attributes.border_width)); ++ printf (" Relative upper-left X: %s\n", xscale (w->geometry->x)); ++ printf (" Relative upper-left Y: %s\n", yscale (w->geometry->y)); ++ printf (" Width: %s\n", xscale (w->geometry->width)); ++ printf (" Height: %s\n", yscale (w->geometry->height)); ++ printf (" Depth: %d\n", w->geometry->depth); ++ ++ win_attributes = fetch_win_attributes (w); ++ ++ printf (" Visual: 0x%lx\n", (unsigned long) win_attributes->visual); ++ if (screen) ++ { ++ xcb_depth_iterator_t depth_iter; ++ xcb_visualtype_t *visual_type = NULL; ++ ++ depth_iter = xcb_screen_allowed_depths_iterator (screen); ++ for (; depth_iter.rem; xcb_depth_next (&depth_iter)) { ++ xcb_visualtype_iterator_t visual_iter; ++ ++ visual_iter = xcb_depth_visuals_iterator (depth_iter.data); ++ for (; visual_iter.rem; xcb_visualtype_next (&visual_iter)) { ++ if (screen->root_visual == visual_iter.data->visual_id) { ++ visual_type = visual_iter.data; ++ break; ++ } ++ } ++ } ++ if (visual_type) ++ printf (" Visual Class: %s\n", Lookup (visual_type->_class, ++ _visual_classes)); ++ } ++ ++ printf (" Border width: %s\n", bscale (w->geometry->border_width)); + printf (" Class: %s\n", +- Lookup (win_attributes.class, _window_classes)); ++ Lookup (win_attributes->_class, _window_classes)); + printf (" Colormap: 0x%lx (%sinstalled)\n", +- win_attributes.colormap, +- win_attributes.map_installed ? "" : "not "); ++ (unsigned long) win_attributes->colormap, ++ win_attributes->map_is_installed ? "" : "not "); + printf (" Bit Gravity State: %s\n", +- Lookup (win_attributes.bit_gravity, _bit_gravity_states)); ++ Lookup (win_attributes->bit_gravity, _bit_gravity_states)); + printf (" Window Gravity State: %s\n", +- Lookup (win_attributes.win_gravity, _window_gravity_states)); ++ Lookup (win_attributes->win_gravity, _window_gravity_states)); + printf (" Backing Store State: %s\n", +- Lookup (win_attributes.backing_store, _backing_store_states)); ++ Lookup (win_attributes->backing_store, _backing_store_states)); + printf (" Save Under State: %s\n", +- win_attributes.save_under ? "yes" : "no"); ++ win_attributes->save_under ? "yes" : "no"); + printf (" Map State: %s\n", +- Lookup (win_attributes.map_state, _map_states)); ++ Lookup (win_attributes->map_state, _map_states)); + printf (" Override Redirect State: %s\n", +- win_attributes.override_redirect ? "yes" : "no"); ++ win_attributes->override_redirect ? "yes" : "no"); + printf (" Corners: +%d+%d -%d+%d -%d-%d +%d-%d\n", + rx, ry, xright, ry, xright, ybelow, rx, ybelow); + +- XFree (vinfo); +- + /* + * compute geometry string that would recreate window + */ + printf (" -geometry "); + + /* compute size in appropriate units */ +- status = XGetWMNormalHints (dpy, window, &hints, &longjunk); +- if (status && hints.flags & PResizeInc && +- hints.width_inc != 0 && hints.height_inc != 0) { +- if (hints.flags & (PBaseSize|PMinSize)) { +- if (hints.flags & PBaseSize) { +- win_attributes.width -= hints.base_width; +- win_attributes.height -= hints.base_height; ++ if (!fetch_normal_hints (w, &hints)) ++ hints.flags = 0; ++ ++ if ((hints.flags & XCB_SIZE_HINT_P_RESIZE_INC) && ++ (hints.width_inc != 0) && (hints.height_inc != 0)) { ++ if (hints.flags & (XCB_SIZE_HINT_BASE_SIZE|XCB_SIZE_HINT_P_MIN_SIZE)) { ++ if (hints.flags & XCB_SIZE_HINT_BASE_SIZE) { ++ w->geometry->width -= hints.base_width; ++ w->geometry->height -= hints.base_height; + } else { + /* ICCCM says MinSize is default for BaseSize */ +- win_attributes.width -= hints.min_width; +- win_attributes.height -= hints.min_height; ++ w->geometry->width -= hints.min_width; ++ w->geometry->height -= hints.min_height; + } + } +- printf ("%dx%d", win_attributes.width/hints.width_inc, +- win_attributes.height/hints.height_inc); ++ printf ("%dx%d", w->geometry->width/hints.width_inc, ++ w->geometry->height/hints.height_inc); + } else +- printf ("%dx%d", win_attributes.width, win_attributes.height); ++ printf ("%dx%d", w->geometry->width, w->geometry->height); + +- if (!(hints.flags&PWinGravity)) +- hints.win_gravity = NorthWestGravity; /* per ICCCM */ ++ if (!(hints.flags & XCB_SIZE_HINT_P_WIN_GRAVITY)) ++ hints.win_gravity = XCB_GRAVITY_NORTH_WEST; /* per ICCCM */ + /* find our window manager frame, if any */ +- wmframe = window; +- while (True) { +- Window root, parent; +- Window *childlist; +- unsigned int ujunk; +- +- status = XQueryTree (dpy, wmframe, &root, &parent, &childlist, &ujunk); +- if (parent == root || !parent || !status) ++ for (wmframe = parent = w->window; parent != 0 ; wmframe = parent) { ++ xcb_query_tree_cookie_t qt_cookie; ++ xcb_query_tree_reply_t *tree; ++ ++ qt_cookie = xcb_query_tree (dpy, wmframe); ++ tree = xcb_query_tree_reply (dpy, qt_cookie, &err); ++ if (!tree) { ++ Print_X_Error (dpy, err); ++ Fatal_Error ("Can't query window tree."); ++ } ++ parent = tree->parent; ++ free (tree); ++ if (parent == w->geometry->root || !parent) + break; +- wmframe = parent; +- if (status && childlist) +- XFree ((char *)childlist); + } +- if (wmframe != window) { ++ if (wmframe != w->window) { + /* WM reparented, so find edges of the frame */ + /* Only works for ICCCM-compliant WMs, and then only if the + window has corner gravity. We would need to know the original width + of the window to correctly handle the other gravities. */ ++ xcb_get_geometry_cookie_t geom_cookie; ++ xcb_get_geometry_reply_t *frame_geometry; + +- XWindowAttributes frame_attr; ++ geom_cookie = xcb_get_geometry (dpy, wmframe); ++ frame_geometry = xcb_get_geometry_reply (dpy, geom_cookie, &err); + +- if (!XGetWindowAttributes (dpy, wmframe, &frame_attr)) +- Fatal_Error ("Can't get frame attributes."); ++ if (!frame_geometry) { ++ Print_X_Error (dpy, err); ++ Fatal_Error ("Can't get frame geometry."); ++ } + switch (hints.win_gravity) { +- case NorthWestGravity: case SouthWestGravity: +- case NorthEastGravity: case SouthEastGravity: +- case WestGravity: +- rx = frame_attr.x; ++ case XCB_GRAVITY_NORTH_WEST: case XCB_GRAVITY_SOUTH_WEST: ++ case XCB_GRAVITY_NORTH_EAST: case XCB_GRAVITY_SOUTH_EAST: ++ case XCB_GRAVITY_WEST: ++ rx = frame_geometry->x; + } + switch (hints.win_gravity) { +- case NorthWestGravity: case SouthWestGravity: +- case NorthEastGravity: case SouthEastGravity: +- case EastGravity: +- xright = dw - frame_attr.x - frame_attr.width - +- 2*frame_attr.border_width; ++ case XCB_GRAVITY_NORTH_WEST: case XCB_GRAVITY_SOUTH_WEST: ++ case XCB_GRAVITY_NORTH_EAST: case XCB_GRAVITY_SOUTH_EAST: ++ case XCB_GRAVITY_EAST: ++ xright = dw - frame_geometry->x - frame_geometry->width - ++ (2 * frame_geometry->border_width); + } + switch (hints.win_gravity) { +- case NorthWestGravity: case SouthWestGravity: +- case NorthEastGravity: case SouthEastGravity: +- case NorthGravity: +- ry = frame_attr.y; ++ case XCB_GRAVITY_NORTH_WEST: case XCB_GRAVITY_SOUTH_WEST: ++ case XCB_GRAVITY_NORTH_EAST: case XCB_GRAVITY_SOUTH_EAST: ++ case XCB_GRAVITY_NORTH: ++ ry = frame_geometry->y; + } + switch (hints.win_gravity) { +- case NorthWestGravity: case SouthWestGravity: +- case NorthEastGravity: case SouthEastGravity: +- case SouthGravity: +- ybelow = dh - frame_attr.y - frame_attr.height - +- 2*frame_attr.border_width; ++ case XCB_GRAVITY_NORTH_WEST: case XCB_GRAVITY_SOUTH_WEST: ++ case XCB_GRAVITY_NORTH_EAST: case XCB_GRAVITY_SOUTH_EAST: ++ case XCB_GRAVITY_SOUTH: ++ ybelow = dh - frame_geometry->y - frame_geometry->height - ++ (2 * frame_geometry->border_width); + } ++ free (frame_geometry); + } + /* If edge gravity, offer a corner on that edge (because the application + programmer cares about that edge), otherwise offer upper left unless + some other corner is close to an edge of the screen. + (For corner gravity, assume gravity was set by XWMGeometry. + For CenterGravity, it doesn't matter.) */ +- if (hints.win_gravity == EastGravity || ++ if (hints.win_gravity == XCB_GRAVITY_EAST || + (abs (xright) <= 100 && abs (xright) < abs (rx) +- && hints.win_gravity != WestGravity)) ++ && hints.win_gravity != XCB_GRAVITY_WEST)) + showright = 1; +- if (hints.win_gravity == SouthGravity || ++ if (hints.win_gravity == XCB_GRAVITY_SOUTH || + (abs (ybelow) <= 100 && abs (ybelow) < abs (ry) +- && hints.win_gravity != NorthGravity)) ++ && hints.win_gravity != XCB_GRAVITY_NORTH)) + showbelow = 1; + + if (showright) +@@ -729,24 +860,25 @@ Display_Stats_Info (Window window) + * Display bits info: + */ + static const binding _gravities[] = { +- { UnmapGravity, "UnMapGravity" }, /* WARNING: both of these have*/ +- { ForgetGravity, "ForgetGravity" }, /* the same value - see code */ +- { NorthWestGravity, "NorthWestGravity" }, +- { NorthGravity, "NorthGravity" }, +- { NorthEastGravity, "NorthEastGravity" }, +- { WestGravity, "WestGravity" }, +- { CenterGravity, "CenterGravity" }, +- { EastGravity, "EastGravity" }, +- { SouthWestGravity, "SouthWestGravity" }, +- { SouthGravity, "SouthGravity" }, +- { SouthEastGravity, "SouthEastGravity" }, +- { StaticGravity, "StaticGravity" }, ++ /* WARNING: the first two of these have the same value - see code */ ++ { XCB_GRAVITY_WIN_UNMAP, "UnMapGravity" }, ++ { XCB_GRAVITY_BIT_FORGET, "ForgetGravity" }, ++ { XCB_GRAVITY_NORTH_WEST, "NorthWestGravity" }, ++ { XCB_GRAVITY_NORTH, "NorthGravity" }, ++ { XCB_GRAVITY_NORTH_EAST, "NorthEastGravity" }, ++ { XCB_GRAVITY_WEST, "WestGravity" }, ++ { XCB_GRAVITY_CENTER, "CenterGravity" }, ++ { XCB_GRAVITY_EAST, "EastGravity" }, ++ { XCB_GRAVITY_SOUTH_WEST, "SouthWestGravity" }, ++ { XCB_GRAVITY_SOUTH, "SouthGravity" }, ++ { XCB_GRAVITY_SOUTH_EAST, "SouthEastGravity" }, ++ { XCB_GRAVITY_STATIC, "StaticGravity" }, + { 0, NULL } }; + + static const binding _backing_store_hint[] = { +- { NotUseful, "NotUseful" }, +- { WhenMapped, "WhenMapped" }, +- { Always, "Always" }, ++ { XCB_BACKING_STORE_NOT_USEFUL, "NotUseful" }, ++ { XCB_BACKING_STORE_WHEN_MAPPED,"WhenMapped" }, ++ { XCB_BACKING_STORE_ALWAYS, "Always" }, + { 0, NULL } }; + + static const binding _bool[] = { +@@ -754,26 +886,29 @@ static const binding _bool[] = { + { 1, "Yes" }, + { 0, NULL } }; + ++/* ++ * Requires wininfo members initialized: ++ * window, attr_cookie (or win_attributes) ++ */ + static void +-Display_Bits_Info (Window window) ++Display_Bits_Info (struct wininfo * w) + { +- XWindowAttributes win_attributes; +- +- if (!XGetWindowAttributes (dpy, window, &win_attributes)) +- Fatal_Error ("Can't get window attributes."); ++ xcb_get_window_attributes_reply_t *win_attributes ++ = fetch_win_attributes (w); + + printf ("\n"); + printf (" Bit gravity: %s\n", +- Lookup (win_attributes.bit_gravity, _gravities+1)); ++ Lookup (win_attributes->bit_gravity, _gravities+1)); + printf (" Window gravity: %s\n", +- Lookup (win_attributes.win_gravity, _gravities)); ++ Lookup (win_attributes->win_gravity, _gravities)); + printf (" Backing-store hint: %s\n", +- Lookup (win_attributes.backing_store, _backing_store_hint)); ++ Lookup (win_attributes->backing_store, _backing_store_hint)); + printf (" Backing-planes to be preserved: 0x%lx\n", +- win_attributes.backing_planes); +- printf (" Backing pixel: %ld\n", win_attributes.backing_pixel); ++ (unsigned long) win_attributes->backing_planes); ++ printf (" Backing pixel: %ld\n", ++ (unsigned long) win_attributes->backing_pixel); + printf (" Save-unders: %s\n", +- Lookup (win_attributes.save_under, _bool)); ++ Lookup (win_attributes->save_under, _bool)); + } + + +@@ -781,31 +916,31 @@ Display_Bits_Info (Window window) + * Routine to display all events in an event mask + */ + static const binding _event_mask_names[] = { +- { KeyPressMask, "KeyPress" }, +- { KeyReleaseMask, "KeyRelease" }, +- { ButtonPressMask, "ButtonPress" }, +- { ButtonReleaseMask, "ButtonRelease" }, +- { EnterWindowMask, "EnterWindow" }, +- { LeaveWindowMask, "LeaveWindow" }, +- { PointerMotionMask, "PointerMotion" }, +- { PointerMotionHintMask, "PointerMotionHint" }, +- { Button1MotionMask, "Button1Motion" }, +- { Button2MotionMask, "Button2Motion" }, +- { Button3MotionMask, "Button3Motion" }, +- { Button4MotionMask, "Button4Motion" }, +- { Button5MotionMask, "Button5Motion" }, +- { ButtonMotionMask, "ButtonMotion" }, +- { KeymapStateMask, "KeymapState" }, +- { ExposureMask, "Exposure" }, +- { VisibilityChangeMask, "VisibilityChange" }, +- { StructureNotifyMask, "StructureNotify" }, +- { ResizeRedirectMask, "ResizeRedirect" }, +- { SubstructureNotifyMask, "SubstructureNotify" }, +- { SubstructureRedirectMask, "SubstructureRedirect" }, +- { FocusChangeMask, "FocusChange" }, +- { PropertyChangeMask, "PropertyChange" }, +- { ColormapChangeMask, "ColormapChange" }, +- { OwnerGrabButtonMask, "OwnerGrabButton" }, ++ { XCB_EVENT_MASK_KEY_PRESS, "KeyPress" }, ++ { XCB_EVENT_MASK_KEY_RELEASE, "KeyRelease" }, ++ { XCB_EVENT_MASK_BUTTON_PRESS, "ButtonPress" }, ++ { XCB_EVENT_MASK_BUTTON_RELEASE, "ButtonRelease" }, ++ { XCB_EVENT_MASK_ENTER_WINDOW, "EnterWindow" }, ++ { XCB_EVENT_MASK_LEAVE_WINDOW, "LeaveWindow" }, ++ { XCB_EVENT_MASK_POINTER_MOTION, "PointerMotion" }, ++ { XCB_EVENT_MASK_POINTER_MOTION_HINT, "PointerMotionHint" }, ++ { XCB_EVENT_MASK_BUTTON_1_MOTION, "Button1Motion" }, ++ { XCB_EVENT_MASK_BUTTON_2_MOTION, "Button2Motion" }, ++ { XCB_EVENT_MASK_BUTTON_3_MOTION, "Button3Motion" }, ++ { XCB_EVENT_MASK_BUTTON_4_MOTION, "Button4Motion" }, ++ { XCB_EVENT_MASK_BUTTON_5_MOTION, "Button5Motion" }, ++ { XCB_EVENT_MASK_BUTTON_MOTION, "ButtonMotion" }, ++ { XCB_EVENT_MASK_KEYMAP_STATE, "KeymapState" }, ++ { XCB_EVENT_MASK_EXPOSURE, "Exposure" }, ++ { XCB_EVENT_MASK_VISIBILITY_CHANGE, "VisibilityChange" }, ++ { XCB_EVENT_MASK_STRUCTURE_NOTIFY, "StructureNotify" }, ++ { XCB_EVENT_MASK_RESIZE_REDIRECT, "ResizeRedirect" }, ++ { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, "SubstructureNotify" }, ++ { XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, "SubstructureRedirect" }, ++ { XCB_EVENT_MASK_FOCUS_CHANGE, "FocusChange" }, ++ { XCB_EVENT_MASK_PROPERTY_CHANGE, "PropertyChange" }, ++ { XCB_EVENT_MASK_COLOR_MAP_CHANGE, "ColormapChange" }, ++ { XCB_EVENT_MASK_OWNER_GRAB_BUTTON, "OwnerGrabButton" }, + { 0, NULL } }; + + static void +@@ -822,24 +957,25 @@ Display_Event_Mask (long mask) + + /* + * Display info on events ++ * ++ * Requires wininfo members initialized: ++ * window, attr_cookie (or win_attributes) + */ + static void +-Display_Events_Info (Window window) ++Display_Events_Info (struct wininfo *w) + { +- XWindowAttributes win_attributes; +- +- if (!XGetWindowAttributes (dpy, window, &win_attributes)) +- Fatal_Error ("Can't get window attributes."); ++ xcb_get_window_attributes_reply_t *win_attributes ++ = fetch_win_attributes (w); + + printf ("\n"); + printf (" Someone wants these events:\n"); +- Display_Event_Mask (win_attributes.all_event_masks); ++ Display_Event_Mask (win_attributes->all_event_masks); + + printf (" Do not propagate these events:\n"); +- Display_Event_Mask (win_attributes.do_not_propagate_mask); ++ Display_Event_Mask (win_attributes->do_not_propagate_mask); + + printf (" Override redirection?: %s\n", +- Lookup (win_attributes.override_redirect, _bool)); ++ Lookup (win_attributes->override_redirect, _bool)); + } + + +@@ -851,39 +987,48 @@ Display_Events_Info (Window window) + /* + * Display root, parent, and (recursively) children information + * recurse - true to show children information ++ * ++ * Requires wininfo members initialized: window, tree_cookie + */ + static void +-Display_Tree_Info (Window window, int recurse) ++Display_Tree_Info (struct wininfo *w, int recurse) + { +- display_tree_info_1 (window, recurse, 0); ++ display_tree_info_1 (w, recurse, 0); + } + + /* + * level - recursion level + */ + static void +-display_tree_info_1 (Window window, int recurse, int level) ++display_tree_info_1 (struct wininfo *w, int recurse, int level) + { + int i, j; +- int rel_x, rel_y, abs_x, abs_y; +- unsigned int width, height, border, depth; +- Window root_win, parent_win; + unsigned int num_children; +- Window *child_list; +- XClassHint classhint; ++ xcb_query_tree_reply_t *tree; + +- if (!XQueryTree (dpy, window, &root_win, &parent_win, &child_list, +- &num_children)) ++ tree = xcb_query_tree_reply (dpy, w->tree_cookie, &err); ++ if (!tree) { ++ Print_X_Error (dpy, err); + Fatal_Error ("Can't query window tree."); ++ } + + if (level == 0) { ++ struct wininfo rw, pw; ++ rw.window = tree->root; ++ rw.wm_name_cookie = xcb_get_wm_name (dpy, rw.window); ++ pw.window = tree->parent; ++ pw.wm_name_cookie = xcb_get_wm_name (dpy, pw.window); ++ xcb_flush (dpy); ++ + printf ("\n"); + printf (" Root window id: "); +- Display_Window_Id (root_win, True); ++ Display_Window_Id (&rw, True); + printf (" Parent window id: "); +- Display_Window_Id (parent_win, True); ++ Display_Window_Id (&pw, True); + } + ++ num_children = xcb_query_tree_children_length (tree); ++ + if (level == 0 || num_children > 0) { + printf (" "); + for (j = 0; j < level; j++) printf (" "); +@@ -891,42 +1036,90 @@ display_tree_info_1 (Window window, int recurse, int level) + num_children ? ":" : "."); + } + +- for (i = (int)num_children - 1; i >= 0; i--) { +- printf (" "); +- for (j = 0; j < level; j++) printf (" "); +- Display_Window_Id (child_list[i], False); +- printf (": ("); +- if (XGetClassHint (dpy, child_list[i], &classhint)) { +- if (classhint.res_name) { +- printf ("\"%s\" ", classhint.res_name); +- XFree (classhint.res_name); +- } else +- printf ("(none) "); +- if (classhint.res_class) { +- printf ("\"%s\") ", classhint.res_class); +- XFree (classhint.res_class); ++ if (num_children > 0) { ++ xcb_window_t *child_list = xcb_query_tree_children (tree); ++ struct wininfo *children ++ = calloc (num_children, sizeof(struct wininfo)); ++ ++ if (children == NULL) ++ Fatal_Error ("Failed to allocate memory in display_tree_info"); ++ ++ for (i = (int)num_children - 1; i >= 0; i--) { ++ struct wininfo *cw = &children[i]; ++ ++ cw->window = child_list[i]; ++ cw->wm_name_cookie = xcb_get_wm_name (dpy, child_list[i]); ++ cw->wm_class_cookie = xcb_get_wm_class (dpy, child_list[i]); ++ cw->geometry_cookie = xcb_get_geometry (dpy, child_list[i]); ++ cw->trans_coords_cookie = xcb_translate_coordinates ++ (dpy, child_list[i], tree->root, 0, 0); ++ if (recurse) ++ cw->tree_cookie = xcb_query_tree (dpy, child_list[i]); ++ } ++ xcb_flush (dpy); ++ ++ for (i = (int)num_children - 1; i >= 0; i--) { ++ struct wininfo *cw = &children[i]; ++ xcb_get_wm_class_reply_t classhint; ++ xcb_get_geometry_reply_t *geometry; ++ ++ printf (" "); ++ for (j = 0; j < level; j++) printf (" "); ++ Display_Window_Id (cw, False); ++ printf (": ("); ++ ++ if (xcb_get_wm_class_reply (dpy, cw->wm_class_cookie, ++ &classhint, NULL)) { ++ if (classhint.instance_name) ++ printf ("\"%s\" ", classhint.instance_name); ++ else ++ printf ("(none) "); ++ ++ if (classhint.class_name) ++ printf ("\"%s\") ", classhint.class_name); ++ else ++ printf ("(none)) "); ++ ++ xcb_get_wm_class_reply_wipe (&classhint); + } else +- printf ("(none)) "); +- } else +- printf (") "); +- +- if (XGetGeometry (dpy, child_list[i], &root_win, +- &rel_x, &rel_y, &width, &height, &border, &depth)) { +- Window child; +- +- printf (" %ux%u+%d+%d", width, height, rel_x, rel_y); +- if (XTranslateCoordinates (dpy, child_list[i], root_win, +- 0 ,0, &abs_x, &abs_y, &child)) { +- printf (" +%d+%d", abs_x - border, abs_y - border); ++ printf (") "); ++ ++ geometry = xcb_get_geometry_reply(dpy, cw->geometry_cookie, &err); ++ if (geometry) { ++ xcb_translate_coordinates_reply_t *trans_coords; ++ ++ printf (" %ux%u+%d+%d", geometry->width, geometry->height, ++ geometry->x, geometry->y); ++ ++ trans_coords = xcb_translate_coordinates_reply ++ (dpy, cw->trans_coords_cookie, &err); ++ ++ if (trans_coords) { ++ int16_t abs_x = (int16_t) trans_coords->dst_x; ++ int16_t abs_y = (int16_t) trans_coords->dst_y; ++ int border = geometry->border_width; ++ ++ printf (" +%d+%d", abs_x - border, abs_y - border); ++ free (trans_coords); ++ } else if (err) { ++ Print_X_Error (dpy, err); ++ } ++ ++ free (geometry); ++ } else if (err) { ++ Print_X_Error (dpy, err); + } +- } +- printf ("\n"); ++ printf ("\n"); + +- if (recurse) +- display_tree_info_1 (child_list[i], 1, level+1); ++ if (recurse) ++ display_tree_info_1 (cw, 1, level+1); ++ ++ wininfo_wipe (cw); ++ } ++ free (children); + } + +- if (child_list) XFree ((char *)child_list); ++ free (tree); /* includes storage for child_list[] */ + } + + +@@ -934,74 +1127,74 @@ display_tree_info_1 (Window window, int recurse, int level) + * Display a set of size hints + */ + static void +-Display_Hints (XSizeHints *hints) ++Display_Hints (xcb_size_hints_t *hints) + { + long flags; + + flags = hints->flags; + +- if (flags & USPosition) ++ if (flags & XCB_SIZE_HINT_US_POSITION) + printf (" User supplied location: %s, %s\n", + xscale (hints->x), yscale (hints->y)); + +- if (flags & PPosition) ++ if (flags & XCB_SIZE_HINT_P_POSITION) + printf (" Program supplied location: %s, %s\n", + xscale (hints->x), yscale (hints->y)); + +- if (flags & USSize) { ++ if (flags & XCB_SIZE_HINT_US_SIZE) { + printf (" User supplied size: %s by %s\n", + xscale (hints->width), yscale (hints->height)); + } + +- if (flags & PSize) ++ if (flags & XCB_SIZE_HINT_P_SIZE) + printf (" Program supplied size: %s by %s\n", + xscale (hints->width), yscale (hints->height)); + +- if (flags & PMinSize) ++ if (flags & XCB_SIZE_HINT_P_MIN_SIZE) + printf (" Program supplied minimum size: %s by %s\n", + xscale (hints->min_width), yscale (hints->min_height)); + +- if (flags & PMaxSize) ++ if (flags & XCB_SIZE_HINT_P_MAX_SIZE) + printf (" Program supplied maximum size: %s by %s\n", + xscale (hints->max_width), yscale (hints->max_height)); + +- if (flags & PBaseSize) { ++ if (flags & XCB_SIZE_HINT_BASE_SIZE) { + printf (" Program supplied base size: %s by %s\n", + xscale (hints->base_width), yscale (hints->base_height)); + } + +- if (flags & PResizeInc) { ++ if (flags & XCB_SIZE_HINT_P_RESIZE_INC) { + printf (" Program supplied x resize increment: %s\n", + xscale (hints->width_inc)); + printf (" Program supplied y resize increment: %s\n", + yscale (hints->height_inc)); + if (hints->width_inc != 0 && hints->height_inc != 0) { +- if (flags & USSize) ++ if (flags & XCB_SIZE_HINT_US_SIZE) + printf (" User supplied size in resize increments: %s by %s\n", + (xscale (hints->width / hints->width_inc)), + (yscale (hints->height / hints->height_inc))); +- if (flags & PSize) ++ if (flags & XCB_SIZE_HINT_P_SIZE) + printf (" Program supplied size in resize increments: %s by %s\n", + (xscale (hints->width / hints->width_inc)), + (yscale (hints->height / hints->height_inc))); +- if (flags & PMinSize) ++ if (flags & XCB_SIZE_HINT_P_MIN_SIZE) + printf (" Program supplied minimum size in resize increments: %s by %s\n", + xscale (hints->min_width / hints->width_inc), yscale (hints->min_height / hints->height_inc)); +- if (flags & PBaseSize) ++ if (flags & XCB_SIZE_HINT_BASE_SIZE) + printf (" Program supplied base size in resize increments: %s by %s\n", + (xscale (hints->base_width / hints->width_inc)), + (yscale (hints->base_height / hints->height_inc))); + } + } + +- if (flags & PAspect) { ++ if (flags & XCB_SIZE_HINT_P_ASPECT) { + printf (" Program supplied min aspect ratio: %s/%s\n", +- xscale (hints->min_aspect.x), yscale (hints->min_aspect.y)); ++ xscale (hints->min_aspect_num), yscale (hints->min_aspect_den)); + printf (" Program supplied max aspect ratio: %s/%s\n", +- xscale (hints->max_aspect.x), yscale (hints->max_aspect.y)); ++ xscale (hints->max_aspect_num), yscale (hints->max_aspect_den)); + } + +- if (flags & PWinGravity) { ++ if (flags & XCB_SIZE_HINT_P_WIN_GRAVITY) { + printf (" Program supplied window gravity: %s\n", + Lookup (hints->win_gravity, _gravities)); + } +@@ -1012,103 +1205,137 @@ Display_Hints (XSizeHints *hints) + * Display Size Hints info + */ + static void +-Display_Size_Hints (Window window) ++Display_Size_Hints (struct wininfo *w) + { +- XSizeHints *hints = XAllocSizeHints (); +- long supplied; ++ xcb_size_hints_t hints; + + printf ("\n"); +- if (!XGetWMNormalHints (dpy, window, hints, &supplied)) ++ if (!fetch_normal_hints (w, &hints)) + printf (" No normal window size hints defined\n"); + else { + printf (" Normal window size hints:\n"); +- hints->flags &= supplied; +- Display_Hints (hints); ++ Display_Hints (&hints); + } + +- if (!XGetWMSizeHints (dpy, window, hints, &supplied, XA_WM_ZOOM_HINTS)) ++ if (!xcb_get_wm_size_hints_reply (dpy, w->zoom_cookie, &hints, NULL)) + printf (" No zoom window size hints defined\n"); + else { + printf (" Zoom window size hints:\n"); +- hints->flags &= supplied; +- Display_Hints (hints); ++ Display_Hints (&hints); + } +- XFree ((char *)hints); + } + + + static void +-Display_Window_Shape (Window window) ++Display_Window_Shape (xcb_window_t window) + { +- Bool ws, bs; +- int xws, yws, xbs, ybs; +- unsigned int wws, hws, wbs, hbs; ++ const xcb_query_extension_reply_t *shape_query; ++ xcb_shape_query_extents_cookie_t extents_cookie; ++ xcb_shape_query_extents_reply_t *extents; + +- if (!XShapeQueryExtension (dpy, &bs, &ws)) ++ shape_query = xcb_get_extension_data (dpy, &xcb_shape_id); ++ if (!shape_query->present) + return; + + printf ("\n"); +- XShapeQueryExtents (dpy, window, &ws, &xws, &yws, &wws, &hws, +- &bs, &xbs, &ybs, &wbs, &hbs); +- if (!ws) ++ ++ extents_cookie = xcb_shape_query_extents (dpy, window); ++ extents = xcb_shape_query_extents_reply (dpy, extents_cookie, &err); ++ ++ if (!extents) { ++ if (err) ++ Print_X_Error (dpy, err); ++ else ++ { ++ printf (" No window shape defined\n"); ++ printf (" No border shape defined\n"); ++ } ++ return; ++ } ++ ++ if (!extents->bounding_shaped) + printf (" No window shape defined\n"); + else { + printf (" Window shape extents: %sx%s", +- xscale (wws), yscale (hws)); +- printf ("+%s+%s\n", xscale (xws), yscale (yws)); ++ xscale (extents->bounding_shape_extents_width), ++ yscale (extents->bounding_shape_extents_height)); ++ printf ("+%s+%s\n", ++ xscale (extents->bounding_shape_extents_x), ++ yscale (extents->bounding_shape_extents_y)); + } +- if (!bs) ++ if (!extents->clip_shaped) + printf (" No border shape defined\n"); + else { + printf (" Border shape extents: %sx%s", +- xscale (wbs), yscale (hbs)); +- printf ("+%s+%s\n", xscale (xbs), yscale (ybs)); ++ xscale (extents->clip_shape_extents_width), ++ yscale (extents->clip_shape_extents_height)); ++ printf ("+%s+%s\n", ++ xscale (extents->clip_shape_extents_x), ++ yscale (extents->clip_shape_extents_y)); + } ++ ++ free (extents); + } + + /* + * Display Window Manager Info ++ * ++ * Requires wininfo members initialized: ++ * window, hints_cookie + */ + static const binding _state_hints[] = { +- { DontCareState, "Don't Care State" }, +- { NormalState, "Normal State" }, +- { ZoomState, "Zoomed State" }, +- { IconicState, "Iconic State" }, +- { InactiveState, "Inactive State" }, ++ { XCB_WM_STATE_WITHDRAWN, "Withdrawn State" }, ++ { XCB_WM_STATE_NORMAL, "Normal State" }, ++ { XCB_WM_STATE_ICONIC, "Iconic State" }, ++/* xwininfo previously also reported the ZoomState & InactiveState, ++ but ICCCM declared those obsolete long ago */ + { 0, NULL } }; + + static void +-Display_WM_Info (Window window) ++Display_WM_Info (struct wininfo *w) + { +- XWMHints *wmhints; ++ xcb_wm_hints_t wmhints; + long flags; + +- wmhints = XGetWMHints (dpy, window); + printf ("\n"); +- if (!wmhints) { ++ if (!xcb_get_wm_hints_reply(dpy, w->hints_cookie, &wmhints, &err)) ++ { + printf (" No window manager hints defined\n"); ++ if (err) ++ Print_X_Error (dpy, err); + return; + } +- flags = wmhints->flags; ++ flags = wmhints.flags; + + printf (" Window manager hints:\n"); + +- if (flags & InputHint) ++ if (flags & XCB_WM_HINT_INPUT) + printf (" Client accepts input or input focus: %s\n", +- Lookup (wmhints->input, _bool)); ++ Lookup (wmhints.input, _bool)); ++ ++ if (flags & XCB_WM_HINT_ICON_WINDOW) { ++ struct wininfo iw; ++ iw.window = wmhints.icon_window; ++ iw.wm_name_cookie = xcb_get_wm_name (dpy, iw.window); + +- if (flags & IconWindowHint) { + printf (" Icon window id: "); +- Display_Window_Id (wmhints->icon_window, True); ++ Display_Window_Id (&iw, True); + } + +- if (flags & IconPositionHint) ++ if (flags & XCB_WM_HINT_ICON_POSITION) + printf (" Initial icon position: %s, %s\n", +- xscale (wmhints->icon_x), yscale (wmhints->icon_y)); ++ xscale (wmhints.icon_x), yscale (wmhints.icon_y)); + +- if (flags & StateHint) ++ if (flags & XCB_WM_HINT_STATE) + printf (" Initial state is %s\n", +- Lookup (wmhints->initial_state, _state_hints)); ++ Lookup (wmhints.initial_state, _state_hints)); ++} + +- XFree (wmhints); ++/* Frees all members of a wininfo struct, but not the struct itself */ ++static void ++wininfo_wipe (struct wininfo *w) ++{ ++ free (w->geometry); ++ free (w->win_attributes); ++ free (w->normal_hints); + } +-- +cgit v0.8.3-6-g21f6 |