New upstream version 3.1.1
[xfishtank.git] / src / wmctrl.c
1 /* -copyright-
2 #-# Copyright © 2021 Eric Bina, Dave Black, TJ Phan,
3 #-# Vincent Renardias, Willem Vermin
4 #-#
5 #-# Permission is hereby granted, free of charge, to any person
6 #-# obtaining a copy of this software and associated documentation
7 #-# files (the “Software”), to deal in the Software without
8 #-# restriction, including without limitation the rights to use,
9 #-# copy, modify, merge, publish, distribute, sublicense, and/or
10 #-# sell copies of the Software, and to permit persons to whom
11 #-# the Software is furnished to do so, subject to the following
12 #-# conditions:
13 #-#
14 #-# The above copyright notice and this permission notice shall
15 #-# be included in all copies or substantial portions of the Software.
16 #-#
17 #-# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
18 #-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 #-# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 #-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 #-# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 #-# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 #-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 #-# OTHER DEALINGS IN THE SOFTWARE.
25 #-#
26 */
27 /*
28 * This works with EWHM/NetWM compatible X Window managers,
29 * so enlightenment (for example) is a problem.
30 * In enlightenment there is no way to tell if a window is minimized,
31 * and on which workspace the focus is.
32 * There would be one advantage of enlightenment: you can tell easily
33 * if a window is on the screen (minimized or not) by looking at __E_WINDOW_MAPPED
34 */
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <X11/Xlib.h>
39 #include <X11/Xatom.h>
40 #include <X11/extensions/shape.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include "wmctrl.h"
44 //#include "windows.h"
45 //#include "dsimple.h"
46 #include "xfishtank.h"
47 #include "debug.h"
48
49 int GetWindows(WinInfo **windows, int *nwin)
50 {
51 Atom type;
52 int format;
53 unsigned long b;
54 unsigned char *properties = NULL;
55 (*windows) = NULL;
56 static Display *getdisplay;;
57 Window *children;
58 long unsigned int nchildren;
59
60 static Atom atom_gtk_frame_extents;
61 static Atom atom_net_client_list;
62 static Atom atom_net_frame_extents;
63 static Atom atom_net_showing_desktop;
64 static Atom atom_net_wm_desktop;
65 static Atom atom_net_wm_state;
66 static Atom atom_net_wm_window_type;
67 static Atom atom_win_client_list;
68 static Atom atom_win_workspace;
69 static Atom atom_wm_state;
70
71 static Window Rootwindow;
72
73 static int firstcall = 1;
74 if(firstcall)
75 {
76 firstcall = 0;
77 getdisplay = Dpy;
78
79 atom_gtk_frame_extents = XInternAtom(getdisplay, "_GTK_FRAME_EXTENTS" ,False);
80 atom_net_client_list = XInternAtom(getdisplay, "_NET_CLIENT_LIST" ,False);
81 atom_net_frame_extents = XInternAtom(getdisplay, "_NET_FRAME_EXTENTS" ,False);
82 atom_net_showing_desktop = XInternAtom(getdisplay, "_NET_SHOWING_DESKTOP" ,False);
83 atom_net_wm_desktop = XInternAtom(getdisplay, "_NET_WM_DESKTOP" ,False);
84 atom_net_wm_state = XInternAtom(getdisplay, "_NET_WM_STATE" ,False);
85 atom_net_wm_window_type = XInternAtom(getdisplay, "_NET_WM_WINDOW_TYPE" ,False);
86 atom_win_client_list = XInternAtom(getdisplay, "_WIN_CLIENT_LIST" ,False);
87 atom_win_workspace = XInternAtom(getdisplay, "_WIN_WORKSPACE" ,False);
88 atom_wm_state = XInternAtom(getdisplay, "WM_STATE" ,False);
89
90 Rootwindow = DefaultRootWindow(getdisplay);
91 }
92
93 XGetWindowProperty(getdisplay, DefaultRootWindow(getdisplay), atom_net_client_list, 0, 1000000, False,
94 AnyPropertyType, &type, &format, &nchildren, &b, (unsigned char**)&children);
95 if(type == XA_WINDOW)
96 {
97 P("_NET_CLIENT_LIST succeeded\n");
98 }
99 else
100 {
101 P("No _NET_CLIENT_LIST, trying _WIN_CLIENT_LIST\n");
102 if(children)
103 {
104 XFree(children);
105 children = NULL;
106 }
107 XGetWindowProperty(getdisplay, DefaultRootWindow(getdisplay), atom_win_client_list, 0, 1000000, False,
108 AnyPropertyType, &type, &format, &nchildren, &b, (unsigned char**)&children);
109 if(type == XA_WINDOW)
110 {
111 P("_WIN_CLIENT_LIST succeeded\n");
112 }
113 }
114 if(type != XA_WINDOW)
115 {
116 P("No _WIN_CLIENT_LIST, trying XQueryTree\n");
117 if(children)
118 {
119 XFree(children);
120 children = NULL;
121 }
122 Window dummy;
123 unsigned int n;
124 XQueryTree(getdisplay,DefaultRootWindow(getdisplay),&dummy,&dummy,&children,&n);
125 nchildren = n;
126 }
127 P("----------------------------------------- nchildren: %ld\n",nchildren);
128 (*nwin) = nchildren;
129 (*windows) = NULL;
130 if(nchildren>0)
131 (*windows) = (WinInfo *)malloc(nchildren*sizeof(WinInfo));
132 WinInfo *w = (*windows);
133 int k = 0;
134
135 // and yet another check if window is hidden (needed e.g. in KDE/plasma after 'hide all windows')
136 int globalhidden = 0;
137 {
138 P("hidden3 %d %#lx\n",counter++,w->id);
139 if (atom_net_showing_desktop)
140 {
141 Atom type;
142 unsigned long nitems, b; int format;
143 unsigned char *properties = NULL;
144 P("hidden3 try _NET_SHOWING_DESKTOP\n");
145 XGetWindowProperty(getdisplay, Rootwindow, atom_net_showing_desktop, 0, (~0L), False,
146 AnyPropertyType, &type, &format, &nitems, &b, &properties);
147 if(format == 32 && nitems >=1)
148 {
149 if(*(long*) properties == 1)
150 globalhidden = 1;
151 P("hidden3 hidden:%d\n",globalhidden);
152 }
153 if(properties) XFree(properties);
154 }
155 }
156
157 unsigned long i;
158 for (i=0; i<nchildren; i++)
159 {
160 int x0,y0,xr,yr;
161 unsigned int depth;
162
163 w->id = children[i];
164
165 XWindowAttributes winattr;
166 XGetWindowAttributes(getdisplay, w->id, &winattr);
167
168 x0 = winattr.x;
169 y0 = winattr.y;
170 w->w = winattr.width;
171 w->h = winattr.height;
172 depth = winattr.depth;
173
174 P("%d %#lx %d %d %d %d %d\n",counter++,w->id,x0,y0,w->w,w->h,depth);
175 // if this window is showing nothing, we ignore it:
176 if (depth == 0)
177 continue;
178
179 Window child_return;
180 XTranslateCoordinates(getdisplay, w->id, Rootwindow, 0, 0, &xr, &yr, &child_return);
181 w->xa = xr - x0;
182 w->ya = yr - y0;
183 P("%d %#lx %d %d %d %d %d\n",counter++,w->id,w->xa,w->ya,w->w,w->h,depth);
184
185 XTranslateCoordinates(getdisplay, w->id, xfishtankWin, 0, 0, &(w->x), &(w->y), &child_return);
186
187 enum{NET,GTK};
188 Atom type; int format; unsigned long nitems,b; unsigned char *properties = NULL;
189 XGetWindowProperty(getdisplay, w->id, atom_net_wm_desktop, 0, 1, False,
190 AnyPropertyType, &type, &format, &nitems, &b, &properties);
191 if(type != XA_CARDINAL)
192 {
193 if(properties) XFree(properties);
194 properties = NULL;
195 XGetWindowProperty(getdisplay, w->id, atom_win_workspace, 0, 1, False,
196 AnyPropertyType, &type, &format, &nitems, &b, &properties);
197 }
198 if(properties)
199 {
200 w->ws = *(long*) properties;
201 if(properties) XFree(properties);
202 }
203 else
204 w->ws = 0;
205 // maybe this window is sticky:
206 w->sticky = 0;
207 properties = NULL;
208 nitems = 0;
209 XGetWindowProperty(getdisplay, w->id, atom_net_wm_state, 0, (~0L), False,
210 AnyPropertyType, &type, &format, &nitems, &b, &properties);
211 if (type == XA_ATOM)
212 {
213 int i;
214 for(i=0; (unsigned long)i<nitems; i++)
215 {
216 char *s = NULL;
217 s = XGetAtomName(getdisplay,((Atom*)properties)[i]);
218 if (!strcmp(s,"_NET_WM_STATE_STICKY"))
219 {
220 P("%#lx is sticky\n",w->id);
221 w->sticky = 1;
222 if(s) XFree(s);
223 break;
224 }
225 if(s) XFree(s);
226 }
227 }
228 // another sticky test, needed in KDE en LXDE:
229 if (w->ws == -1)
230 w->sticky = 1;
231 if(properties) XFree(properties);
232
233 // check if window is a "dock".
234 w->dock = 0;
235 properties = NULL;
236 nitems = 0;
237 XGetWindowProperty(getdisplay, w->id, atom_net_wm_window_type, 0, (~0L), False,
238 AnyPropertyType, &type, &format, &nitems, &b, &properties);
239 if(format == 32)
240 {
241 int i;
242 for(i=0; (unsigned long)i<nitems; i++)
243 {
244 char *s = NULL;
245 s = XGetAtomName(getdisplay,((Atom*)properties)[i]);
246 if (!strcmp(s,"_NET_WM_WINDOW_TYPE_DOCK"))
247 {
248 P("%#lx is dock %d\n",w->id, counter++);
249 w->dock = 1;
250 if(s) XFree(s);
251 break;
252 }
253 if(s) XFree(s);
254 }
255 }
256 if(properties) XFree(properties);
257
258 // check if window is hidden
259 w->hidden = globalhidden;
260 if(!w->hidden)
261 {
262 if (winattr.map_state != IsViewable)
263 {
264 P("map_state: %#lx %d\n",w->id,winattr.map_state);
265 w->hidden = 1;
266 }
267 }
268 // another check on hidden
269 if (!w->hidden)
270 {
271 properties = NULL;
272 nitems = 0;
273 XGetWindowProperty(getdisplay, w->id, atom_net_wm_state, 0, (~0L), False,
274 AnyPropertyType, &type, &format, &nitems, &b, &properties);
275 if(format == 32)
276 {
277 unsigned long i;
278 for (i=0; i<nitems; i++)
279 {
280 char *s = NULL;
281 s = XGetAtomName(getdisplay,((Atom*)properties)[i]);
282 if (!strcmp(s,"_NET_WM_STATE_HIDDEN"))
283 {
284 P("%#lx is hidden %d\n",w->id, counter++);
285 w->hidden = 1;
286 if(s) XFree(s);
287 break;
288 }
289 if(s) XFree(s);
290 }
291 }
292 if(properties) XFree(properties);
293 }
294
295 // yet another check if window is hidden:
296 if(!w->hidden)
297 {
298 P("hidden2 %#lx\n",w->id);
299 properties = NULL;
300 nitems = 0;
301 XGetWindowProperty(getdisplay, w->id, atom_wm_state, 0, (~0L), False,
302 AnyPropertyType, &type, &format, &nitems, &b, &properties);
303 if(format == 32 && nitems >=1)
304 {
305 // see https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1
306 // WithDrawnState: 0
307 // NormalState: 1
308 // IconicState: 3
309 if(*(long*) properties != NormalState)
310 w->hidden = 1;
311 }
312 if(properties) XFree(properties);
313 }
314
315 properties = NULL;
316 nitems = 0;
317
318 // first try to get adjustments for _GTK_FRAME_EXTENTS
319 if (atom_gtk_frame_extents)
320 XGetWindowProperty(getdisplay, w->id, atom_gtk_frame_extents, 0, 4, False,
321 AnyPropertyType, &type, &format, &nitems, &b, &properties);
322 int wintype = GTK;
323 // if not succesfull, try _NET_FRAME_EXTENTS
324 if (nitems != 4)
325 {
326 if(properties) XFree(properties);
327 properties = NULL;
328 P("trying net...\n");
329 XGetWindowProperty(getdisplay, w->id, atom_net_frame_extents, 0, 4, False,
330 AnyPropertyType, &type, &format, &nitems, &b, &properties);
331 wintype = NET;
332 }
333 P("nitems: %ld %ld %d\n",type,nitems,format);
334 if(nitems == 4 && format == 32 && type) // adjust x,y,w,h of window
335 {
336 long *r; // borderleft, borderright, top decoration, bottomdecoration
337 r = (long*)properties;
338 P("RRRR: %ld %ld %ld %ld\n",r[0],r[1],r[2],r[3]);
339 switch(wintype)
340 {
341 case NET:
342 P("NET\n");
343 w->x -= r[0];
344 w->y -= r[2];
345 w->w += r[0]+r[1];
346 w->h += r[2]+r[3];
347 break;
348 case GTK:
349 P("GTK\n");
350 w->x += r[0];
351 w->y += r[2];
352 w->w -= (r[0]+r[1]);
353 w->h -= (r[2]+r[3]);
354 break;
355 default:
356 I("Xfishtank encountered a serious problem, exiting ...\n");
357 exit(1);
358 break;
359 }
360 P("NET/GTK: %#lx %d %d %d %d %d\n", w->id,w->ws,w->x,w->y,w->w,w->h);
361 }
362 else
363 {
364 // this is a problem....
365 // In for example TWM, neither NET nor GTK is the case.
366 // Let us try this one:
367 w->x = x0;
368 w->y = y0;
369 P("%d %#lx %d %d\n",counter++,w->id,w->x,w->y);
370 }
371 if(properties)XFree(properties);
372 w++;
373 k++;
374 }
375 if(properties) XFree(properties);
376 (*nwin) = k;
377 //P("%d\n",counter++);printwindows(getdisplay,*windows,*nwin);
378 return 1;
379 }
380
381
382
This page took 0.035014 seconds and 4 git commands to generate.