1 /*
2 * Copyright (C) 2004-2020 Kim Woelders
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies of the Software, its documentation and marketing & publicity
13 * materials, and acknowledgment shall be given in the documentation, materials
14 * and software packages that this Software was used.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23 #include "E.h"
24 #include "desktops.h"
25 #include "eobj.h"
26 #include "ewins.h"
27
28 #define ENABLE_DEBUG_STACKING 1
29
30 #define EobjGetCwin(p) \
31 ((p->type == EOBJ_TYPE_EWIN) ? EwinGetClientXwin(((EWin*)(p))) : NoXID)
32
33 typedef struct _eobjlist EobjList;
34
35 struct _eobjlist {
36 const char *name;
37 int nalloc;
38 int nwins;
39 EObj **list;
40 char layered;
41 char type;
42 };
43
44 static int EobjListRaise(EobjList * ewl, EObj * eo, int test);
45 static int EobjListLower(EobjList * ewl, EObj * eo, int test);
46
47 #if ENABLE_DEBUG_STACKING
48 static void
EobjListShow(const char * txt,EobjList * ewl)49 EobjListShow(const char *txt, EobjList * ewl)
50 {
51 int i;
52 EObj *eo;
53
54 if (!EDebug(EDBUG_TYPE_STACKING))
55 return;
56
57 Eprintf("%s-%s:\n", ewl->name, txt);
58 for (i = 0; i < ewl->nwins; i++)
59 {
60 eo = ewl->list[i];
61 Eprintf(" %2d: %#10x %#10x %d %d %s\n", i, EobjGetXwin(eo),
62 EobjGetCwin(eo), eo->desk->num, eo->ilayer, EobjGetName(eo));
63 }
64 }
65 #else
66 #define EobjListShow(txt, ewl)
67 #endif
68
69 static int
EobjListGetIndex(EobjList * ewl,EObj * eo)70 EobjListGetIndex(EobjList * ewl, EObj * eo)
71 {
72 int i;
73
74 for (i = 0; i < ewl->nwins; i++)
75 if (ewl->list[i] == eo)
76 return i;
77
78 return -1;
79 }
80
81 static void
EobjListAdd(EobjList * ewl,EObj * eo,int ontop)82 EobjListAdd(EobjList * ewl, EObj * eo, int ontop)
83 {
84 int i;
85
86 /* Quit if already in list */
87 i = EobjListGetIndex(ewl, eo);
88 if (i >= 0)
89 return;
90
91 if (ewl->nwins >= ewl->nalloc)
92 {
93 ewl->nalloc += 16;
94 ewl->list = EREALLOC(EObj *, ewl->list, ewl->nalloc);
95 }
96
97 if (ewl->layered)
98 {
99 /* The simple way for now (add, raise/lower) */
100 if (ontop)
101 {
102 ewl->list[ewl->nwins] = eo;
103 ewl->nwins++;
104 EobjListRaise(ewl, eo, 0);
105 }
106 else
107 {
108 memmove(ewl->list + 1, ewl->list, ewl->nwins * sizeof(EObj *));
109 ewl->list[0] = eo;
110 ewl->nwins++;
111 EobjListLower(ewl, eo, 0);
112 }
113 if (eo->stacked == 0)
114 DeskSetDirtyStack(eo->desk, eo);
115 }
116 else
117 {
118 if (ontop)
119 {
120 memmove(ewl->list + 1, ewl->list, ewl->nwins * sizeof(EObj *));
121 ewl->list[0] = eo;
122 }
123 else
124 {
125 ewl->list[ewl->nwins] = eo;
126 }
127 ewl->nwins++;
128 }
129
130 EobjListShow("EobjListAdd", ewl);
131 }
132
133 static void
EobjListDel(EobjList * ewl,EObj * eo)134 EobjListDel(EobjList * ewl, EObj * eo)
135 {
136 int i, n;
137
138 /* Quit if not in list */
139 i = EobjListGetIndex(ewl, eo);
140 if (i < 0)
141 return;
142
143 ewl->nwins--;
144 n = ewl->nwins - i;
145 if (n > 0)
146 {
147 memmove(ewl->list + i, ewl->list + i + 1, n * sizeof(EObj *));
148 }
149 else if (ewl->nwins <= 0)
150 {
151 /* Enables autocleanup at shutdown, if ever implemented */
152 EFREE_NULL(ewl->list);
153 ewl->nalloc = 0;
154 }
155
156 EobjListShow("EobjListDel", ewl);
157 }
158
159 static int
EobjListLower(EobjList * ewl,EObj * eo,int test)160 EobjListLower(EobjList * ewl, EObj * eo, int test)
161 {
162 int i, j, n;
163
164 /* Quit if not in list */
165 i = EobjListGetIndex(ewl, eo);
166 if (i < 0)
167 return 0;
168
169 j = ewl->nwins - 1;
170 if (ewl->layered)
171 {
172 /* Take the layer into account */
173 for (; j >= 0; j--)
174 if (i != j && eo->ilayer <= ewl->list[j]->ilayer)
175 break;
176 if (j < i)
177 j++;
178 }
179
180 n = j - i;
181 if (test)
182 return n;
183
184 if (n > 0)
185 {
186 memmove(ewl->list + i, ewl->list + i + 1, n * sizeof(EObj *));
187 ewl->list[j] = eo;
188 if (ewl->layered && eo->stacked > 0)
189 DeskSetDirtyStack(eo->desk, eo);
190 }
191 else if (n < 0)
192 {
193 memmove(ewl->list + j + 1, ewl->list + j, -n * sizeof(EObj *));
194 ewl->list[j] = eo;
195 if (ewl->layered && eo->stacked > 0)
196 DeskSetDirtyStack(eo->desk, eo);
197 }
198
199 EobjListShow("EobjListLower", ewl);
200 return n;
201 }
202
203 static int
EobjListRaise(EobjList * ewl,EObj * eo,int test)204 EobjListRaise(EobjList * ewl, EObj * eo, int test)
205 {
206 int i, j, n;
207
208 /* Quit if not in list */
209 i = EobjListGetIndex(ewl, eo);
210 if (i < 0)
211 return 0;
212
213 j = 0;
214 if (ewl->layered)
215 {
216 /* Take the layer into account */
217 for (; j < ewl->nwins; j++)
218 if (j != i && eo->ilayer >= ewl->list[j]->ilayer)
219 break;
220 if (j > i)
221 j--;
222 }
223
224 n = j - i;
225 if (test)
226 return n;
227
228 if (n > 0)
229 {
230 memmove(ewl->list + i, ewl->list + i + 1, n * sizeof(EObj *));
231 ewl->list[j] = eo;
232 if (ewl->layered && eo->stacked > 0)
233 DeskSetDirtyStack(eo->desk, eo);
234 }
235 else if (n < 0)
236 {
237 memmove(ewl->list + j + 1, ewl->list + j, -n * sizeof(EObj *));
238 ewl->list[j] = eo;
239 if (ewl->layered && eo->stacked > 0)
240 DeskSetDirtyStack(eo->desk, eo);
241 }
242
243 EobjListShow("EobjListRaise", ewl);
244 return n;
245 }
246
247 static EObj *
EobjListFind(const EobjList * ewl,EX_Window win)248 EobjListFind(const EobjList * ewl, EX_Window win)
249 {
250 int i;
251
252 for (i = 0; i < ewl->nwins; i++)
253 if (EobjGetXwin(ewl->list[i]) == win)
254 return ewl->list[i];
255
256 return NULL;
257 }
258
259 #if 0
260 static int
261 EobjListTypeCount(const EobjList * ewl, int type)
262 {
263 int i, n;
264
265 for (i = n = 0; i < ewl->nwins; i++)
266 if (ewl->list[i]->type == type)
267 n++;
268
269 return n;
270 }
271 #endif
272
273 /*
274 * The global object/client lists
275 */
276 static EobjList EwinListStack = { "Stack", 0, 0, NULL, 1, 0 };
277 static EobjList EwinListFocus = { "Focus", 0, 0, NULL, 0, 1 };
278 static EobjList EwinListOrder = { "Order", 0, 0, NULL, 0, 2 };
279
280 static EObj *const *
EobjListGet(EobjList * ewl,int * num)281 EobjListGet(EobjList * ewl, int *num)
282 {
283 *num = ewl->nwins;
284 return ewl->list;
285 }
286
287 int
EobjListStackCheck(EObj * eo)288 EobjListStackCheck(EObj * eo)
289 {
290 return EobjListGetIndex(&EwinListStack, eo);
291 }
292
293 EObj *
EobjListStackFind(EX_Window win)294 EobjListStackFind(EX_Window win)
295 {
296 return EobjListFind(&EwinListStack, win);
297 }
298
299 EObj *const *
EobjListStackGet(int * num)300 EobjListStackGet(int *num)
301 {
302 return EobjListGet(&EwinListStack, num);
303 }
304
305 void
EobjListStackAdd(EObj * eo,int ontop)306 EobjListStackAdd(EObj * eo, int ontop)
307 {
308 EobjListAdd(&EwinListStack, eo, ontop);
309 }
310
311 void
EobjListStackDel(EObj * eo)312 EobjListStackDel(EObj * eo)
313 {
314 EobjListDel(&EwinListStack, eo);
315 }
316
317 int
EobjListStackRaise(EObj * eo,int test)318 EobjListStackRaise(EObj * eo, int test)
319 {
320 return EobjListRaise(&EwinListStack, eo, test);
321 }
322
323 int
EobjListStackLower(EObj * eo,int test)324 EobjListStackLower(EObj * eo, int test)
325 {
326 return EobjListLower(&EwinListStack, eo, test);
327 }
328
329 void
EobjListFocusAdd(EObj * eo,int ontop)330 EobjListFocusAdd(EObj * eo, int ontop)
331 {
332 EobjListAdd(&EwinListFocus, eo, ontop);
333 }
334
335 void
EobjListFocusDel(EObj * eo)336 EobjListFocusDel(EObj * eo)
337 {
338 EobjListDel(&EwinListFocus, eo);
339 }
340
341 int
EobjListFocusRaise(EObj * eo)342 EobjListFocusRaise(EObj * eo)
343 {
344 return EobjListRaise(&EwinListFocus, eo, 0);
345 }
346
347 EWin *const *
EwinListStackGet(int * num)348 EwinListStackGet(int *num)
349 {
350 static EWin **lst = NULL;
351 static int nalloc = 0;
352 const EobjList *ewl;
353 int i, j;
354 EObj *eo;
355
356 ewl = &EwinListStack;
357
358 for (i = j = 0; i < ewl->nwins; i++)
359 {
360 eo = ewl->list[i];
361 if (eo->type != EOBJ_TYPE_EWIN)
362 continue;
363
364 if (nalloc <= j)
365 {
366 nalloc += 16; /* 16 at the time */
367 lst = EREALLOC(EWin *, lst, nalloc);
368 }
369
370 lst[j++] = (EWin *) eo;
371 }
372
373 *num = j;
374 return lst;
375 }
376
377 EWin *const *
EwinListFocusGet(int * num)378 EwinListFocusGet(int *num)
379 {
380 return (EWin * const *)EobjListGet(&EwinListFocus, num);
381 }
382
383 EWin *const *
EwinListGetForDesk(int * num,Desk * dsk)384 EwinListGetForDesk(int *num, Desk * dsk)
385 {
386 static EWin **lst = NULL;
387 static int nalloc = 0;
388 const EobjList *ewl;
389 int i, j;
390 EObj *eo;
391
392 ewl = &EwinListStack;
393
394 for (i = j = 0; i < ewl->nwins; i++)
395 {
396 eo = ewl->list[i];
397 if (eo->type != EOBJ_TYPE_EWIN || eo->desk != dsk)
398 continue;
399
400 if (nalloc <= j)
401 {
402 nalloc += 16; /* 16 at the time */
403 lst = EREALLOC(EWin *, lst, nalloc);
404 }
405
406 lst[j++] = (EWin *) eo;
407 }
408
409 *num = j;
410 return lst;
411 }
412
413 EObj *const *
EobjListStackGetForDesk(int * num,Desk * dsk)414 EobjListStackGetForDesk(int *num, Desk * dsk)
415 {
416 static EObj **lst = NULL;
417 static int nalloc = 0;
418 const EobjList *ewl;
419 int i, j;
420 EObj *eo;
421
422 ewl = &EwinListStack;
423
424 /* Too many - who cares. */
425 if (nalloc < ewl->nwins)
426 {
427 nalloc = (ewl->nwins + 16) & ~0xf; /* 16 at the time */
428 lst = EREALLOC(EObj *, lst, nalloc);
429 }
430
431 for (i = j = 0; i < ewl->nwins; i++)
432 {
433 eo = ewl->list[i];
434 if (eo->desk != dsk)
435 continue;
436
437 lst[j++] = eo;
438 }
439
440 *num = j;
441 return lst;
442 }
443
444 #if 0 /* Unused */
445 EWin *
446 EwinListStackGetTop(void)
447 {
448 const EobjList *ewl;
449 int i;
450 EObj *eo;
451
452 ewl = &EwinListStack;
453
454 for (i = 0; i < ewl->nwins; i++)
455 {
456 eo = ewl->list[i];
457 if (eo->type == EOBJ_TYPE_EWIN)
458 return (EWin *) eo;
459 }
460
461 return NULL;
462 }
463 #endif
464
465 int
EwinListStackIsRaised(const EWin * ewin)466 EwinListStackIsRaised(const EWin * ewin)
467 {
468 const EobjList *ewl;
469 int i;
470 const EObj *eo, *eox;
471
472 ewl = &EwinListStack;
473 eox = EoObj(ewin);
474
475 for (i = 0; i < ewl->nwins; i++)
476 {
477 eo = ewl->list[i];
478 if (eo->type != EOBJ_TYPE_EWIN)
479 continue;
480 if (eo->desk != eox->desk)
481 continue;
482 if (eo->ilayer > eox->ilayer)
483 continue;
484 if (EwinGetTransientFor((EWin *) eo) == EwinGetClientXwin(ewin))
485 continue;
486 return eo == eox;
487 }
488
489 return 1; /* It should be impossible to get here */
490 }
491
492 void
EobjListOrderAdd(EObj * eo)493 EobjListOrderAdd(EObj * eo)
494 {
495 EobjListAdd(&EwinListOrder, eo, 0);
496 }
497
498 void
EobjListOrderDel(EObj * eo)499 EobjListOrderDel(EObj * eo)
500 {
501 EobjListDel(&EwinListOrder, eo);
502 }
503
504 EWin *const *
EwinListOrderGet(int * num)505 EwinListOrderGet(int *num)
506 {
507 return (EWin * const *)EobjListGet(&EwinListOrder, num);
508 }
509