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