// Find top-level windows whose WM_CLASS contains the given substring and
// print their absolute screen geometry. Recurses into WM frame windows.
//
// Usage: xfind <wm_class_substring>
// Compile: gcc -o xfind xfind.c -lX11

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static Display *d;
static Atom net_wm_name, utf8_string;

static char *get_net_wm_name(Window w) {
  Atom type; int fmt; unsigned long n, after; unsigned char *p = NULL;
  if (XGetWindowProperty(d, w, net_wm_name, 0, 1024, False, utf8_string,
                         &type, &fmt, &n, &after, &p) == Success && p) {
    return (char *)p;  // caller XFree()s
  }
  return NULL;
}

static void recurse(Window w, const char *needle) {
  XClassHint ch;
  if (XGetClassHint(d, w, &ch)) {
    int match =
        (ch.res_name && strstr(ch.res_name, needle)) ||
        (ch.res_class && strstr(ch.res_class, needle));
    if (match) {
      XWindowAttributes a;
      if (XGetWindowAttributes(d, w, &a) && a.map_state == IsViewable) {
        int rx, ry;
        Window child;
        XTranslateCoordinates(d, w, a.root, 0, 0, &rx, &ry, &child);
        char *name = get_net_wm_name(w);
        if (!name) XFetchName(d, w, &name);
        printf("WIN 0x%lx class=%s/%s geom=%dx%d+%d+%d name=%s\n", w,
               ch.res_name ? ch.res_name : "", ch.res_class ? ch.res_class : "",
               a.width, a.height, rx, ry, name ? name : "");
        if (name) XFree(name);
      }
    }
    if (ch.res_name) XFree(ch.res_name);
    if (ch.res_class) XFree(ch.res_class);
  }
  Window root, parent, *children;
  unsigned int n;
  if (XQueryTree(d, w, &root, &parent, &children, &n)) {
    for (unsigned i = 0; i < n; i++) recurse(children[i], needle);
    if (children) XFree(children);
  }
}

int main(int argc, char **argv) {
  if (argc < 2) { fprintf(stderr, "usage: xfind <class>\n"); return 1; }
  d = XOpenDisplay(NULL);
  if (!d) return 1;
  net_wm_name = XInternAtom(d, "_NET_WM_NAME", False);
  utf8_string = XInternAtom(d, "UTF8_STRING", False);
  recurse(DefaultRootWindow(d), argv[1]);
  XCloseDisplay(d);
  return 0;
}
