+/* -copyright-
+#-# Copyright © 2021 Eric Bina, Dave Black, TJ Phan,
+#-# Vincent Renardias, Willem Vermin
+#-#
+#-# 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 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.
+#-#
+*/
+/*
+ * This works with EWHM/NetWM compatible X Window managers,
+ * so enlightenment (for example) is a problem.
+ * In enlightenment there is no way to tell if a window is minimized,
+ * and on which workspace the focus is.
+ * There would be one advantage of enlightenment: you can tell easily
+ * if a window is on the screen (minimized or not) by looking at __E_WINDOW_MAPPED
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/shape.h>
+#include <string.h>
+#include <unistd.h>
+#include "wmctrl.h"
+//#include "windows.h"
+//#include "dsimple.h"
+#include "xfishtank.h"
+#include "debug.h"
+
+int GetWindows(WinInfo **windows, int *nwin)
+{
+ Atom type;
+ int format;
+ unsigned long b;
+ unsigned char *properties = NULL;
+ (*windows) = NULL;
+ static Display *getdisplay;;
+ Window *children;
+ long unsigned int nchildren;
+
+ static Atom atom_gtk_frame_extents;
+ static Atom atom_net_client_list;
+ static Atom atom_net_frame_extents;
+ static Atom atom_net_showing_desktop;
+ static Atom atom_net_wm_desktop;
+ static Atom atom_net_wm_state;
+ static Atom atom_net_wm_window_type;
+ static Atom atom_win_client_list;
+ static Atom atom_win_workspace;
+ static Atom atom_wm_state;
+
+ static Window Rootwindow;
+
+ static int firstcall = 1;
+ if(firstcall)
+ {
+ firstcall = 0;
+ getdisplay = Dpy;
+
+ atom_gtk_frame_extents = XInternAtom(getdisplay, "_GTK_FRAME_EXTENTS" ,False);
+ atom_net_client_list = XInternAtom(getdisplay, "_NET_CLIENT_LIST" ,False);
+ atom_net_frame_extents = XInternAtom(getdisplay, "_NET_FRAME_EXTENTS" ,False);
+ atom_net_showing_desktop = XInternAtom(getdisplay, "_NET_SHOWING_DESKTOP" ,False);
+ atom_net_wm_desktop = XInternAtom(getdisplay, "_NET_WM_DESKTOP" ,False);
+ atom_net_wm_state = XInternAtom(getdisplay, "_NET_WM_STATE" ,False);
+ atom_net_wm_window_type = XInternAtom(getdisplay, "_NET_WM_WINDOW_TYPE" ,False);
+ atom_win_client_list = XInternAtom(getdisplay, "_WIN_CLIENT_LIST" ,False);
+ atom_win_workspace = XInternAtom(getdisplay, "_WIN_WORKSPACE" ,False);
+ atom_wm_state = XInternAtom(getdisplay, "WM_STATE" ,False);
+
+ Rootwindow = DefaultRootWindow(getdisplay);
+ }
+
+ XGetWindowProperty(getdisplay, DefaultRootWindow(getdisplay), atom_net_client_list, 0, 1000000, False,
+ AnyPropertyType, &type, &format, &nchildren, &b, (unsigned char**)&children);
+ if(type == XA_WINDOW)
+ {
+ P("_NET_CLIENT_LIST succeeded\n");
+ }
+ else
+ {
+ P("No _NET_CLIENT_LIST, trying _WIN_CLIENT_LIST\n");
+ if(children)
+ {
+ XFree(children);
+ children = NULL;
+ }
+ XGetWindowProperty(getdisplay, DefaultRootWindow(getdisplay), atom_win_client_list, 0, 1000000, False,
+ AnyPropertyType, &type, &format, &nchildren, &b, (unsigned char**)&children);
+ if(type == XA_WINDOW)
+ {
+ P("_WIN_CLIENT_LIST succeeded\n");
+ }
+ }
+ if(type != XA_WINDOW)
+ {
+ P("No _WIN_CLIENT_LIST, trying XQueryTree\n");
+ if(children)
+ {
+ XFree(children);
+ children = NULL;
+ }
+ Window dummy;
+ unsigned int n;
+ XQueryTree(getdisplay,DefaultRootWindow(getdisplay),&dummy,&dummy,&children,&n);
+ nchildren = n;
+ }
+ P("----------------------------------------- nchildren: %ld\n",nchildren);
+ (*nwin) = nchildren;
+ (*windows) = NULL;
+ if(nchildren>0)
+ (*windows) = (WinInfo *)malloc(nchildren*sizeof(WinInfo));
+ WinInfo *w = (*windows);
+ int k = 0;
+
+ // and yet another check if window is hidden (needed e.g. in KDE/plasma after 'hide all windows')
+ int globalhidden = 0;
+ {
+ P("hidden3 %d %#lx\n",counter++,w->id);
+ if (atom_net_showing_desktop)
+ {
+ Atom type;
+ unsigned long nitems, b; int format;
+ unsigned char *properties = NULL;
+ P("hidden3 try _NET_SHOWING_DESKTOP\n");
+ XGetWindowProperty(getdisplay, Rootwindow, atom_net_showing_desktop, 0, (~0L), False,
+ AnyPropertyType, &type, &format, &nitems, &b, &properties);
+ if(format == 32 && nitems >=1)
+ {
+ if(*(long*) properties == 1)
+ globalhidden = 1;
+ P("hidden3 hidden:%d\n",globalhidden);
+ }
+ if(properties) XFree(properties);
+ }
+ }
+
+ unsigned long i;
+ for (i=0; i<nchildren; i++)
+ {
+ int x0,y0,xr,yr;
+ unsigned int depth;
+
+ w->id = children[i];
+
+ XWindowAttributes winattr;
+ XGetWindowAttributes(getdisplay, w->id, &winattr);
+
+ x0 = winattr.x;
+ y0 = winattr.y;
+ w->w = winattr.width;
+ w->h = winattr.height;
+ depth = winattr.depth;
+
+ P("%d %#lx %d %d %d %d %d\n",counter++,w->id,x0,y0,w->w,w->h,depth);
+ // if this window is showing nothing, we ignore it:
+ if (depth == 0)
+ continue;
+
+ Window child_return;
+ XTranslateCoordinates(getdisplay, w->id, Rootwindow, 0, 0, &xr, &yr, &child_return);
+ w->xa = xr - x0;
+ w->ya = yr - y0;
+ P("%d %#lx %d %d %d %d %d\n",counter++,w->id,w->xa,w->ya,w->w,w->h,depth);
+
+ XTranslateCoordinates(getdisplay, w->id, xfishtankWin, 0, 0, &(w->x), &(w->y), &child_return);
+
+ enum{NET,GTK};
+ Atom type; int format; unsigned long nitems,b; unsigned char *properties = NULL;
+ XGetWindowProperty(getdisplay, w->id, atom_net_wm_desktop, 0, 1, False,
+ AnyPropertyType, &type, &format, &nitems, &b, &properties);
+ if(type != XA_CARDINAL)
+ {
+ if(properties) XFree(properties);
+ properties = NULL;
+ XGetWindowProperty(getdisplay, w->id, atom_win_workspace, 0, 1, False,
+ AnyPropertyType, &type, &format, &nitems, &b, &properties);
+ }
+ if(properties)
+ {
+ w->ws = *(long*) properties;
+ if(properties) XFree(properties);
+ }
+ else
+ w->ws = 0;
+ // maybe this window is sticky:
+ w->sticky = 0;
+ properties = NULL;
+ nitems = 0;
+ XGetWindowProperty(getdisplay, w->id, atom_net_wm_state, 0, (~0L), False,
+ AnyPropertyType, &type, &format, &nitems, &b, &properties);
+ if (type == XA_ATOM)
+ {
+ int i;
+ for(i=0; (unsigned long)i<nitems; i++)
+ {
+ char *s = NULL;
+ s = XGetAtomName(getdisplay,((Atom*)properties)[i]);
+ if (!strcmp(s,"_NET_WM_STATE_STICKY"))
+ {
+ P("%#lx is sticky\n",w->id);
+ w->sticky = 1;
+ if(s) XFree(s);
+ break;
+ }
+ if(s) XFree(s);
+ }
+ }
+ // another sticky test, needed in KDE en LXDE:
+ if (w->ws == -1)
+ w->sticky = 1;
+ if(properties) XFree(properties);
+
+ // check if window is a "dock".
+ w->dock = 0;
+ properties = NULL;
+ nitems = 0;
+ XGetWindowProperty(getdisplay, w->id, atom_net_wm_window_type, 0, (~0L), False,
+ AnyPropertyType, &type, &format, &nitems, &b, &properties);
+ if(format == 32)
+ {
+ int i;
+ for(i=0; (unsigned long)i<nitems; i++)
+ {
+ char *s = NULL;
+ s = XGetAtomName(getdisplay,((Atom*)properties)[i]);
+ if (!strcmp(s,"_NET_WM_WINDOW_TYPE_DOCK"))
+ {
+ P("%#lx is dock %d\n",w->id, counter++);
+ w->dock = 1;
+ if(s) XFree(s);
+ break;
+ }
+ if(s) XFree(s);
+ }
+ }
+ if(properties) XFree(properties);
+
+ // check if window is hidden
+ w->hidden = globalhidden;
+ if(!w->hidden)
+ {
+ if (winattr.map_state != IsViewable)
+ {
+ P("map_state: %#lx %d\n",w->id,winattr.map_state);
+ w->hidden = 1;
+ }
+ }
+ // another check on hidden
+ if (!w->hidden)
+ {
+ properties = NULL;
+ nitems = 0;
+ XGetWindowProperty(getdisplay, w->id, atom_net_wm_state, 0, (~0L), False,
+ AnyPropertyType, &type, &format, &nitems, &b, &properties);
+ if(format == 32)
+ {
+ unsigned long i;
+ for (i=0; i<nitems; i++)
+ {
+ char *s = NULL;
+ s = XGetAtomName(getdisplay,((Atom*)properties)[i]);
+ if (!strcmp(s,"_NET_WM_STATE_HIDDEN"))
+ {
+ P("%#lx is hidden %d\n",w->id, counter++);
+ w->hidden = 1;
+ if(s) XFree(s);
+ break;
+ }
+ if(s) XFree(s);
+ }
+ }
+ if(properties) XFree(properties);
+ }
+
+ // yet another check if window is hidden:
+ if(!w->hidden)
+ {
+ P("hidden2 %#lx\n",w->id);
+ properties = NULL;
+ nitems = 0;
+ XGetWindowProperty(getdisplay, w->id, atom_wm_state, 0, (~0L), False,
+ AnyPropertyType, &type, &format, &nitems, &b, &properties);
+ if(format == 32 && nitems >=1)
+ {
+ // see https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1
+ // WithDrawnState: 0
+ // NormalState: 1
+ // IconicState: 3
+ if(*(long*) properties != NormalState)
+ w->hidden = 1;
+ }
+ if(properties) XFree(properties);
+ }
+
+ properties = NULL;
+ nitems = 0;
+
+ // first try to get adjustments for _GTK_FRAME_EXTENTS
+ if (atom_gtk_frame_extents)
+ XGetWindowProperty(getdisplay, w->id, atom_gtk_frame_extents, 0, 4, False,
+ AnyPropertyType, &type, &format, &nitems, &b, &properties);
+ int wintype = GTK;
+ // if not succesfull, try _NET_FRAME_EXTENTS
+ if (nitems != 4)
+ {
+ if(properties) XFree(properties);
+ properties = NULL;
+ P("trying net...\n");
+ XGetWindowProperty(getdisplay, w->id, atom_net_frame_extents, 0, 4, False,
+ AnyPropertyType, &type, &format, &nitems, &b, &properties);
+ wintype = NET;
+ }
+ P("nitems: %ld %ld %d\n",type,nitems,format);
+ if(nitems == 4 && format == 32 && type) // adjust x,y,w,h of window
+ {
+ long *r; // borderleft, borderright, top decoration, bottomdecoration
+ r = (long*)properties;
+ P("RRRR: %ld %ld %ld %ld\n",r[0],r[1],r[2],r[3]);
+ switch(wintype)
+ {
+ case NET:
+ P("NET\n");
+ w->x -= r[0];
+ w->y -= r[2];
+ w->w += r[0]+r[1];
+ w->h += r[2]+r[3];
+ break;
+ case GTK:
+ P("GTK\n");
+ w->x += r[0];
+ w->y += r[2];
+ w->w -= (r[0]+r[1]);
+ w->h -= (r[2]+r[3]);
+ break;
+ default:
+ I("Xfishtank encountered a serious problem, exiting ...\n");
+ exit(1);
+ break;
+ }
+ P("NET/GTK: %#lx %d %d %d %d %d\n", w->id,w->ws,w->x,w->y,w->w,w->h);
+ }
+ else
+ {
+ // this is a problem....
+ // In for example TWM, neither NET nor GTK is the case.
+ // Let us try this one:
+ w->x = x0;
+ w->y = y0;
+ P("%d %#lx %d %d\n",counter++,w->id,w->x,w->y);
+ }
+ if(properties)XFree(properties);
+ w++;
+ k++;
+ }
+ if(properties) XFree(properties);
+ (*nwin) = k;
+ //P("%d\n",counter++);printwindows(getdisplay,*windows,*nwin);
+ return 1;
+}
+
+
+