]>
Commit | Line | Data |
---|---|---|
4c1bd65b MG |
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 |