Update upstream source from tag 'upstream/3.1.1'
[xfishtank.git] / src / wmctrl.c
diff --git a/src/wmctrl.c b/src/wmctrl.c
new file mode 100644 (file)
index 0000000..80a452c
--- /dev/null
@@ -0,0 +1,382 @@
+/* -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;
+}
+
+
+
This page took 0.013557 seconds and 4 git commands to generate.