1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2020 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "E.h"
25 #include "desktops.h"
26 #include "ewins.h"
27 #include "groups.h"
28 #include "screen.h"
29 #include "slide.h"
30 
31 #define DEBUG_ARRANGE 0
32 
33 typedef struct {
34    void               *data;
35    int                 x, y, w, h;
36    int                 p;
37 } RectBox;
38 
39 typedef struct {
40    int                 x, y;
41    int                 p, q;
42 } RectInfo;
43 
44 static int
ArrangeAddToList(int * array,int current_size,int value)45 ArrangeAddToList(int *array, int current_size, int value)
46 {
47    int                 i, j;
48 
49    if (current_size >= 2 &&
50        (value <= array[0] || value >= array[current_size - 1]))
51       return current_size;
52 
53    for (i = 0; i < current_size; i++)
54      {
55 	if (value < array[i])
56 	  {
57 	     for (j = current_size; j > i; j--)
58 		array[j] = array[j - 1];
59 	     array[i] = value;
60 	     return current_size + 1;
61 	  }
62 	else if (value == array[i])
63 	   return current_size;
64      }
65    array[current_size] = value;
66    return current_size + 1;
67 }
68 
69 static void
ArrangeMakeXYArrays(int tx1,int tx2,int ty1,int ty2,int fitw,int fith,const RectBox * sorted,int num_sorted,int * xarray,int * nx,int * yarray,int * ny)70 ArrangeMakeXYArrays(int tx1, int tx2, int ty1, int ty2, int fitw, int fith,
71 		    const RectBox * sorted, int num_sorted,
72 		    int *xarray, int *nx, int *yarray, int *ny)
73 {
74    int                 j, x1, x2, y1, y2;
75    int                 xsize, ysize;
76 
77    xsize = 0;
78    ysize = 0;
79 
80    /* put all the sorted rects into the xy arrays */
81    xsize = ArrangeAddToList(xarray, xsize, tx1);
82    xsize = ArrangeAddToList(xarray, xsize, tx2);
83    xsize = ArrangeAddToList(xarray, xsize, tx2 - fitw);
84    ysize = ArrangeAddToList(yarray, ysize, ty1);
85    ysize = ArrangeAddToList(yarray, ysize, ty2);
86    ysize = ArrangeAddToList(yarray, ysize, ty2 - fith);
87 
88    for (j = 0; j < num_sorted; j++)
89      {
90 	x1 = sorted[j].x;
91 	x2 = x1 + sorted[j].w;
92 	xsize = ArrangeAddToList(xarray, xsize, x1);
93 	xsize = ArrangeAddToList(xarray, xsize, x2);
94 	xsize = ArrangeAddToList(xarray, xsize, x1 - fitw);
95 	xsize = ArrangeAddToList(xarray, xsize, x2 - fitw);
96 	y1 = sorted[j].y;
97 	y2 = y1 + sorted[j].h;
98 	ysize = ArrangeAddToList(yarray, ysize, y1);
99 	ysize = ArrangeAddToList(yarray, ysize, y2);
100 	ysize = ArrangeAddToList(yarray, ysize, y1 - fith);
101 	ysize = ArrangeAddToList(yarray, ysize, y2 - fith);
102      }
103 #if DEBUG_ARRANGE
104    for (j = 0; j < xsize; j++)
105       Eprintf("xarray[%d] = %d\n", j, xarray[j]);
106    for (j = 0; j < ysize; j++)
107       Eprintf("yarray[%d] = %d\n", j, yarray[j]);
108 #endif
109 
110    *nx = xsize;
111    *ny = ysize;
112 }
113 
114 #define Filled(x,y) (filled[(y * (xsize - 1)) + x])
115 
116 static void
ArrangeMakeFillLists(const RectBox * sorted,int num_sorted,int * xarray,int xsize,int * yarray,int ysize,unsigned char * filled)117 ArrangeMakeFillLists(const RectBox * sorted, int num_sorted,
118 		     int *xarray, int xsize, int *yarray, int ysize,
119 		     unsigned char *filled)
120 {
121    int                 j, x1, x2, y1, y2, k, y, x;
122 
123    /* fill the allocation array */
124    for (j = 0; j < (xsize - 1) * (ysize - 1); filled[j++] = 0)
125       ;
126    for (j = 0; j < num_sorted; j++)
127      {
128 	if (sorted[j].x + sorted[j].w <= xarray[0] ||
129 	    sorted[j].x >= xarray[xsize - 1])
130 	   continue;
131 	if (sorted[j].y + sorted[j].h <= yarray[0] ||
132 	    sorted[j].y >= yarray[ysize - 1])
133 	   continue;
134 	x1 = -1;
135 	x2 = -1;
136 	y1 = -1;
137 	y2 = -1;
138 	for (k = 0; k < xsize - 1; k++)
139 	   if (sorted[j].x <= xarray[k])
140 	     {
141 		x1 = x2 = k;
142 		break;
143 	     }
144 	for (k++; k < xsize - 1; k++)
145 	   if (sorted[j].x + sorted[j].w > xarray[k])
146 	      x2 = k;
147 	for (k = 0; k < ysize - 1; k++)
148 	   if (sorted[j].y <= yarray[k])
149 	     {
150 		y1 = y2 = k;
151 		break;
152 	     }
153 	for (k++; k < ysize - 1; k++)
154 	   if (sorted[j].y + sorted[j].h > yarray[k])
155 	      y2 = k;
156 #if DEBUG_ARRANGE
157 	Eprintf("Fill %4d,%4d %4dx%4d: (%2d)%4d->(%2d)%4d,(%2d)%4d->(%2d)%4d\n",
158 		sorted[j].x, sorted[j].y, sorted[j].w, sorted[j].h,
159 		x1, xarray[x1], x2, xarray[x2], y1, yarray[y1], y2, yarray[y2]);
160 #endif
161 	if ((x1 >= 0) && (x2 >= 0) && (y1 >= 0) && (y2 >= 0))
162 	  {
163 	     for (y = y1; y <= y2; y++)
164 	       {
165 		  for (x = x1; x <= x2; x++)
166 		    {
167 		       Filled(x, y) += sorted[j].p;
168 		    }
169 	       }
170 	  }
171      }
172 
173 #if DEBUG_ARRANGE
174    Eprintf("Filled[%2d,%2d] =\n", xsize, ysize);
175    for (k = 0; k < ysize - 1; k++)
176      {
177 	for (j = 0; j < xsize - 1; j++)
178 	   printf(" %2d", Filled(j, k));
179 	printf("\n");
180      }
181 #endif
182 }
183 
184 static void
ArrangeFindSpace(const int * xarray,int xsize,const int * yarray,int ysize,unsigned char * filled,RectInfo * spaces,int * ns,int wx,int wy,int ww,int wh)185 ArrangeFindSpace(const int *xarray, int xsize, const int *yarray, int ysize,
186 		 unsigned char *filled, RectInfo * spaces, int *ns,
187 		 int wx, int wy, int ww, int wh)
188 {
189    int                 i, j, w, h, fw, fh, z1, z2;
190    int                 cost, desk;
191    int                 num_spaces = *ns;
192 
193    if (wx < xarray[0] || (wx != xarray[0] && wx + ww > xarray[xsize - 1]))
194       return;
195    if (wy < yarray[0] || (wy != yarray[0] && wy + wh > yarray[ysize - 1]))
196       return;
197 
198    cost = desk = 0;
199    fh = wh;
200 #if DEBUG_ARRANGE > 1
201    Eprintf("Check-A %d,%d %dx%d\n", wx, wy, ww, wh);
202 #endif
203    for (j = 0; j < ysize - 1; j++)
204      {
205 	z2 = yarray[j + 1];
206 	if (z2 <= wy)
207 	   continue;
208 
209 	z1 = wy > yarray[j] ? wy : yarray[j];
210 	z2 = wy + wh < z2 ? wy + wh : z2;
211 	h = z2 - z1;
212 	fw = ww;
213 	for (i = 0; i < xsize - 1; i++)
214 	  {
215 	     z2 = xarray[i + 1];
216 	     if (z2 <= wx)
217 		continue;
218 
219 	     z1 = wx > xarray[i] ? wx : xarray[i];
220 	     z2 = wx + ww < z2 ? wx + ww : z2;
221 	     w = z2 - z1;
222 #if DEBUG_ARRANGE > 1
223 	     Eprintf("Add [%d,%d] %3dx%3d: %2d\n", i, j, w, h, Filled(i, j));
224 #endif
225 	     if (Filled(i, j) == 0)
226 		desk += w * h;
227 	     else
228 		cost += w * h * Filled(i, j);
229 	     fw -= w;
230 	     if (fw <= 0)
231 		break;
232 	  }
233 	fh -= h;
234 	if (fh <= 0)
235 	   break;
236      }
237 
238 #if DEBUG_ARRANGE
239    Eprintf("Check %4d,%4d %3dx%3d cost=%d desk=%d\n", wx, wy, ww, wh,
240 	   cost, desk);
241 #endif
242    spaces[num_spaces].x = wx;
243    spaces[num_spaces].y = wy;
244    spaces[num_spaces].p = cost;
245    spaces[num_spaces].q = desk;
246    num_spaces++;
247    *ns = num_spaces;
248 }
249 
250 static void
ArrangeFindSpaces(const int * xarray,int xsize,const int * yarray,int ysize,unsigned char * filled,RectInfo * spaces,int max_spaces,int * ns,RectBox * fit)251 ArrangeFindSpaces(const int *xarray, int xsize, const int *yarray, int ysize,
252 		  unsigned char *filled, RectInfo * spaces, int max_spaces,
253 		  int *ns, RectBox * fit)
254 {
255    int                 ix, iy, fx, fy, fw, fh;
256 
257    /* create list of all "spaces" */
258    *ns = 0;
259    fw = fit->w;
260    fh = fit->h;
261    for (iy = 0; iy < ysize; iy++)
262      {
263 	fy = yarray[iy];
264 
265 	for (ix = 0; ix < xsize; ix++)
266 	  {
267 	     fx = xarray[ix];
268 
269 	     ArrangeFindSpace(xarray, xsize, yarray, ysize, filled, spaces, ns,
270 			      fx, fy, fw, fh);
271 	     if (*ns >= max_spaces)
272 		goto done;
273 	  }
274      }
275 
276  done:
277    ;
278 }
279 
280 static void
ArrangeSwapList(RectBox * list,int a,int b)281 ArrangeSwapList(RectBox * list, int a, int b)
282 {
283    RectBox             bb;
284 
285    bb.data = list[a].data;
286    bb.x = list[a].x;
287    bb.y = list[a].y;
288    bb.w = list[a].w;
289    bb.h = list[a].h;
290    list[a].data = list[b].data;
291    list[a].x = list[b].x;
292    list[a].y = list[b].y;
293    list[a].w = list[b].w;
294    list[a].h = list[b].h;
295    list[b].data = bb.data;
296    list[b].x = bb.x;
297    list[b].y = bb.y;
298    list[b].w = bb.w;
299    list[b].h = bb.h;
300 }
301 
302 static void
ArrangeRects(const RectBox * fixed,int fixed_count,RectBox * floating,int floating_count,RectBox * sorted,int startx,int starty,int width,int height,int policy,char initial_window)303 ArrangeRects(const RectBox * fixed, int fixed_count, RectBox * floating,
304 	     int floating_count, RectBox * sorted, int startx, int starty,
305 	     int width, int height, int policy, char initial_window)
306 {
307    int                 num_sorted;
308    int                 tx1, ty1, tx2, ty2;
309    int                 xsize = 0, ysize = 0;
310    int                *xarray, *yarray;
311    int                 i, j, k;
312    unsigned char      *filled;
313    RectInfo           *spaces;
314    int                 num_spaces, alloc_spaces;
315    int                 sort;
316    int                 a1, a2;
317 
318    tx1 = startx;
319    ty1 = starty;
320    tx2 = startx + width;
321    ty2 = starty + height;
322    if (initial_window)
323      {
324 	int                 xx1, yy1, xx2, yy2;
325 
326 	ScreenGetAvailableAreaByPointer(&xx1, &yy1, &xx2, &yy2,
327 					Conf.place.ignore_struts);
328 	xx2 += xx1;
329 	yy2 += yy1;
330 	if (tx1 < xx1)
331 	   tx1 = xx1;
332 	if (tx2 > xx2)
333 	   tx2 = xx2;
334 	if (ty1 < yy1)
335 	   ty1 = yy1;
336 	if (ty2 > yy2)
337 	   ty2 = yy2;
338      }
339 #if DEBUG_ARRANGE
340    Eprintf("Target area %d,%d -> %d,%d\n", tx1, ty1, tx2, ty2);
341 #endif
342 
343    switch (policy)
344      {
345      case ARRANGE_VERBATIM:
346 	break;
347      case ARRANGE_BY_SIZE:
348 	sort = 0;
349 	while (!sort)
350 	  {
351 	     sort = 1;
352 	     for (i = 0; i < floating_count - 1; i++)
353 	       {
354 		  a1 = floating[i].w * floating[i].h;
355 		  a2 = floating[i + 1].w * floating[i + 1].h;
356 		  if (a2 > a1)
357 		    {
358 		       sort = 0;
359 		       ArrangeSwapList(floating, i, i + 1);
360 		    }
361 	       }
362 	  }
363 	break;
364      case ARRANGE_BY_POSITION:
365 	sort = 0;
366 	while (!sort)
367 	  {
368 	     sort = 1;
369 	     for (i = 0; i < floating_count - 1; i++)
370 	       {
371 		  a1 = floating[i].x + floating[i].y;
372 		  a2 = (floating[i + 1].x + (floating[i + 1].w >> 1)) +
373 		     (floating[i + 1].y + (floating[i + 1].h >> 1));
374 		  if (a2 < a1)
375 		    {
376 		       sort = 0;
377 		       ArrangeSwapList(floating, i, i + 1);
378 		    }
379 	       }
380 	  }
381 	break;
382      default:
383 	break;
384      }
385 
386    /* for every floating rect in order, "fit" it into the sorted list */
387    i = ((fixed_count + floating_count) * 4) + 2;
388    xarray = EMALLOC(int, i);
389    yarray = EMALLOC(int, i);
390 
391    filled = NULL;
392    spaces = NULL;
393    alloc_spaces = 0;
394 
395    if (!xarray || !yarray)
396       goto done;
397 
398    /* copy "fixed" rects into the sorted list */
399    if (fixed)
400       memcpy(sorted, fixed, fixed_count * sizeof(RectBox));
401    num_sorted = fixed_count;
402 
403    /* go through each floating rect in order and "fit" it in */
404    for (i = 0; i < floating_count; i++)
405      {
406 	ArrangeMakeXYArrays(tx1, tx2, ty1, ty2, floating[i].w, floating[i].h,
407 			    sorted, num_sorted, xarray, &xsize, yarray, &ysize);
408 	num_spaces = xsize * ysize;
409 	if (num_spaces <= 0)
410 	   continue;
411 
412 	if (alloc_spaces < num_spaces)
413 	  {
414 	     unsigned char      *ptr_f;
415 	     RectInfo           *ptr_s;
416 
417 	     ptr_f = EREALLOC(unsigned char, filled, num_spaces);
418 
419 	     if (ptr_f)
420 		filled = ptr_f;
421 	     ptr_s = EREALLOC(RectInfo, spaces, num_spaces);
422 	     if (ptr_s)
423 		spaces = ptr_s;
424 	     if (!ptr_f || !ptr_s)
425 		goto done;
426 	     alloc_spaces = num_spaces;
427 	  }
428 	ArrangeMakeFillLists(sorted, num_sorted,
429 			     xarray, xsize, yarray, ysize, filled);
430 
431 	/* create list of all "spaces" */
432 	ArrangeFindSpaces(xarray, xsize, yarray, ysize, filled,
433 			  spaces, alloc_spaces, &num_spaces, floating + i);
434 	if (num_spaces <= 0)
435 	   continue;
436 
437 	/* find the first space that fits */
438 	k = 0;
439 	sort = 0x7fffffff;	/* NB! Break at 0 == free space */
440 	for (j = 0; j < num_spaces; j++)
441 	  {
442 	     a1 = spaces[j].p - spaces[j].q * 4;
443 	     if (a1 >= sort)
444 		continue;
445 	     sort = a1;
446 	     k = j;
447 	     if (spaces[j].p == 0)
448 		break;
449 	  }
450 	if (spaces[k].q == 0 && Conf.place.center_if_desk_full)
451 	  {
452 	     sorted[num_sorted].x = (tx1 + tx2 - floating[i].w) / 2;
453 	     sorted[num_sorted].y = (ty1 + ty2 - floating[i].h) / 2;
454 	  }
455 	else
456 	  {
457 	     sorted[num_sorted].x = spaces[k].x;
458 	     sorted[num_sorted].y = spaces[k].y;
459 	  }
460 	sorted[num_sorted].data = floating[i].data;
461 	sorted[num_sorted].w = floating[i].w;
462 	sorted[num_sorted].h = floating[i].h;
463 	sorted[num_sorted].p = floating[i].p;
464 	num_sorted++;
465      }
466 
467 #if DEBUG_ARRANGE
468    for (i = 0; i < num_sorted; i++)
469       Eprintf("Sorted: x,y=%4d,%4d wxh=%3dx%3d p=%2d: %s\n",
470 	      sorted[i].x, sorted[i].y, sorted[i].w, sorted[i].h, sorted[i].p,
471 	      (sorted[i].data) ? EobjGetName((EObj *) sorted[i].data) : "?");
472 #endif
473 
474  done:
475    /* free up memory */
476    Efree(xarray);
477    Efree(yarray);
478    Efree(filled);
479    Efree(spaces);
480 }
481 
482 void
SnapEwin(EWin * ewin,int dx,int dy,int * new_dx,int * new_dy)483 SnapEwin(EWin * ewin, int dx, int dy, int *new_dx, int *new_dy)
484 {
485    EWin               *const *lst1;
486    EWin              **lst, **gwins;
487    int                 gnum, num, i, j, screen_snap_dist, odx, ody;
488    static char         last_res = 0;
489    int                 top_bound, bottom_bound, left_bound, right_bound, w, h;
490 
491    if (!ewin)
492       return;
493 
494    if (!Conf.snap.enable)
495      {
496 	*new_dx = dx;
497 	*new_dy = dy;
498 	return;
499      }
500 
501    ScreenGetGeometry(ewin->shape_x, ewin->shape_y,
502 		     &left_bound, &top_bound, &w, &h);
503    right_bound = left_bound + w;
504    bottom_bound = top_bound + h;
505    screen_snap_dist = Mode.constrained ? (w + h) : Conf.snap.screen_snap_dist;
506 
507    lst1 = EwinListOrderGet(&num);
508    if (!lst1)
509       return;
510 
511    lst = EMALLOC(EWin *, num);
512    if (!lst)
513       return;
514    memcpy(lst, lst1, num * sizeof(EWin *));
515 
516    gwins = ListWinGroupMembersForEwin(ewin, GROUP_ACTION_MOVE, Mode.nogroup
517 				      || Mode.move.swap, &gnum);
518    if (gwins)
519      {
520 	for (i = 0; i < gnum; i++)
521 	  {
522 	     for (j = 0; j < num; j++)
523 	       {
524 		  if ((lst[j] == gwins[i]) || (lst[j] == ewin))
525 		     lst[j] = NULL;
526 	       }
527 	  }
528 	Efree(gwins);
529      }
530 
531    odx = dx;
532    ody = dy;
533    if (dx < 0)
534      {
535 	if (IN_BELOW(ewin->shape_x + dx, left_bound, screen_snap_dist)
536 	    && (ewin->shape_x >= left_bound))
537 	  {
538 	     dx = left_bound - ewin->shape_x;
539 	  }
540 	else
541 	  {
542 	     for (i = 0; i < num; i++)
543 	       {
544 		  if (!lst[i])
545 		     continue;
546 
547 		  if ((EoGetDesk(ewin) == EoGetDesk(lst[i]) ||
548 		       EoIsSticky(lst[i])) && !(EoIsFloating(lst[i])) &&
549 		      !lst[i]->state.iconified && !lst[i]->props.ignorearrange)
550 		    {
551 		       if (IN_BELOW
552 			   (ewin->shape_x + dx,
553 			    EoGetX(lst[i]) + EoGetW(lst[i]) - 1,
554 			    Conf.snap.edge_snap_dist)
555 			   && SPANS_COMMON(ewin->shape_y, EoGetH(ewin),
556 					   EoGetY(lst[i]), EoGetH(lst[i]))
557 			   && (ewin->shape_x >=
558 			       (EoGetX(lst[i]) + EoGetW(lst[i]))))
559 			 {
560 			    dx =
561 			       (EoGetX(lst[i]) + EoGetW(lst[i])) -
562 			       ewin->shape_x;
563 			    break;
564 			 }
565 		    }
566 	       }
567 	  }
568 	if ((ewin->req_x - ewin->shape_x) > 0)
569 	   dx = 0;
570      }
571    else if (dx > 0)
572      {
573 	if (IN_ABOVE
574 	    (ewin->shape_x + EoGetW(ewin) + dx, right_bound, screen_snap_dist)
575 	    && ((ewin->shape_x + EoGetW(ewin)) <= right_bound))
576 	  {
577 	     dx = right_bound - (ewin->shape_x + EoGetW(ewin));
578 	  }
579 	else
580 	  {
581 	     for (i = 0; i < num; i++)
582 	       {
583 		  if (!lst[i])
584 		     continue;
585 
586 		  if ((EoGetDesk(ewin) == EoGetDesk(lst[i]) ||
587 		       EoIsSticky(lst[i])) && !(EoIsFloating(lst[i])) &&
588 		      !lst[i]->state.iconified && !lst[i]->props.ignorearrange)
589 		    {
590 		       if (IN_ABOVE
591 			   (ewin->shape_x + EoGetW(ewin) + dx - 1,
592 			    EoGetX(lst[i]), Conf.snap.edge_snap_dist)
593 			   && SPANS_COMMON(ewin->shape_y, EoGetH(ewin),
594 					   EoGetY(lst[i]), EoGetH(lst[i]))
595 			   && ((ewin->shape_x + EoGetW(ewin)) <=
596 			       EoGetX(lst[i])))
597 			 {
598 			    dx =
599 			       EoGetX(lst[i]) - (ewin->shape_x + EoGetW(ewin));
600 			    break;
601 			 }
602 		    }
603 	       }
604 	  }
605 	if ((ewin->req_x - ewin->shape_x) < 0)
606 	   dx = 0;
607      }
608 
609    if (dy < 0)
610      {
611 	if (IN_BELOW(ewin->shape_y + dy, top_bound, screen_snap_dist)
612 	    && (ewin->shape_y >= top_bound))
613 	  {
614 	     dy = top_bound - ewin->shape_y;
615 	  }
616 	else
617 	  {
618 	     for (i = 0; i < num; i++)
619 	       {
620 		  if (!lst[i])
621 		     continue;
622 
623 		  if ((EoGetDesk(ewin) == EoGetDesk(lst[i]) ||
624 		       EoIsSticky(lst[i])) && !(EoIsFloating(lst[i])) &&
625 		      !lst[i]->state.iconified && !lst[i]->props.ignorearrange)
626 		    {
627 		       if (IN_BELOW
628 			   (ewin->shape_y + dy,
629 			    EoGetY(lst[i]) + EoGetH(lst[i]) - 1,
630 			    Conf.snap.edge_snap_dist)
631 			   && SPANS_COMMON(ewin->shape_x, EoGetW(ewin),
632 					   EoGetX(lst[i]), EoGetW(lst[i]))
633 			   && (ewin->shape_y >=
634 			       (EoGetY(lst[i]) + EoGetH(lst[i]))))
635 			 {
636 			    dy =
637 			       (EoGetY(lst[i]) + EoGetH(lst[i])) -
638 			       ewin->shape_y;
639 			    break;
640 			 }
641 		    }
642 	       }
643 	  }
644 	if ((ewin->req_y - ewin->shape_y) > 0)
645 	   dy = 0;
646      }
647    else if (dy > 0)
648      {
649 	if (IN_ABOVE
650 	    (ewin->shape_y + EoGetH(ewin) + dy, bottom_bound,
651 	     screen_snap_dist)
652 	    && ((ewin->shape_y + EoGetH(ewin)) <= bottom_bound))
653 	  {
654 	     dy = bottom_bound - (ewin->shape_y + EoGetH(ewin));
655 	  }
656 	else
657 	  {
658 	     for (i = 0; i < num; i++)
659 	       {
660 		  if (!lst[i])
661 		     continue;
662 
663 		  if ((EoGetDesk(ewin) == EoGetDesk(lst[i]) ||
664 		       EoIsSticky(lst[i])) && !(EoIsFloating(lst[i])) &&
665 		      !lst[i]->state.iconified && !lst[i]->props.ignorearrange)
666 		    {
667 		       if (IN_ABOVE
668 			   (ewin->shape_y + EoGetH(ewin) + dy - 1,
669 			    EoGetY(lst[i]), Conf.snap.edge_snap_dist)
670 			   && SPANS_COMMON(ewin->shape_x, EoGetW(ewin),
671 					   EoGetX(lst[i]), EoGetW(lst[i]))
672 			   && ((ewin->shape_y + EoGetH(ewin)) <=
673 			       EoGetY(lst[i])))
674 			 {
675 			    dy =
676 			       EoGetY(lst[i]) - (ewin->shape_y + EoGetH(ewin));
677 			    break;
678 			 }
679 		    }
680 	       }
681 	  }
682 	if ((ewin->req_y - ewin->shape_y) < 0)
683 	   dy = 0;
684      }
685 
686    Efree(lst);
687 
688    if ((odx != dx) || (ody != dy))
689      {
690 	if (!last_res)
691 	  {
692 	     /* SoundPlay(SOUND_MOVE_RESIST); */
693 	     last_res = 1;
694 	  }
695      }
696    else
697      {
698 	last_res = 0;
699      }
700    *new_dx = dx;
701    *new_dy = dy;
702 }
703 
704 void
ArrangeEwin(EWin * ewin)705 ArrangeEwin(EWin * ewin)
706 {
707    int                 x, y;
708 
709    ArrangeEwinXY(ewin, &x, &y);
710    EwinMove(ewin, x, y, 0);
711 }
712 
713 void
ArrangeEwinCentered(EWin * ewin)714 ArrangeEwinCentered(EWin * ewin)
715 {
716    int                 x, y;
717 
718    ArrangeEwinCenteredXY(ewin, &x, &y);
719    EwinMove(ewin, x, y, 0);
720 }
721 
722 static void
ArrangeGetRectList(RectBox ** pfixed,int * nfixed,RectBox ** pfloating,int * nfloating,EWin * ewin)723 ArrangeGetRectList(RectBox ** pfixed, int *nfixed, RectBox ** pfloating,
724 		   int *nfloating, EWin * ewin)
725 {
726    RectBox            *rb, *fixed, *floating;
727    int                 x, y, w, h, i, nfix, nflt, num;
728    EObj               *const *lst, *eo;
729    Desk               *dsk;
730 
731    fixed = floating = NULL;
732    nfix = nflt = 0;
733 
734    lst = EobjListStackGet(&num);
735    if (!lst)
736       goto done;
737 
738    fixed = EMALLOC(RectBox, num);
739    if (!fixed)
740       goto done;
741 
742    dsk = (ewin) ? EoGetDesk(ewin) : DesksGetCurrent();
743 
744    for (i = 0; i < num; i++)
745      {
746 	rb = fixed + nfix;
747 	eo = lst[i];
748 
749 	if (!eo->shown)
750 	   continue;
751 
752 	if (eo->type == EOBJ_TYPE_EWIN)
753 	  {
754 	     EWin               *ew = (EWin *) eo;
755 
756 	     if (ew == ewin)
757 		continue;
758 	     if (eo->desk != dsk)
759 		continue;
760 
761 	     if (ew->props.ignorearrange || EoGetLayer(ew) == 0)
762 		continue;
763 
764 	     if (pfloating)
765 	       {
766 		  int                 ax, ay;
767 
768 		  DeskGetArea(EoGetDesk(ew), &ax, &ay);
769 
770 		  if (!EoIsSticky(ew) && !EoIsFloating(ew) &&
771 		      ew->area_x == ax && ew->area_y == ay)
772 		    {
773 		       floating = EREALLOC(RectBox, floating, nflt + 1);
774 		       rb = floating + nflt++;
775 		       rb->data = ew;
776 		       rb->x = EoGetX(ew);
777 		       rb->y = EoGetY(ew);
778 		       rb->w = EoGetW(ew);
779 		       rb->h = EoGetH(ew);
780 		       rb->p = EoGetLayer(ew);
781 #if DEBUG_ARRANGE
782 		       Eprintf("Add float: x,y=%4d,%4d wxh=%3dx%3d p=%2d: %s\n",
783 			       rb->x, rb->y, rb->w, rb->h, rb->p,
784 			       EobjGetName(eo));
785 #endif
786 		       continue;
787 		    }
788 	       }
789 
790 	     rb->data = ew;
791 
792 	     if (ew->props.never_use_area)
793 		rb->p = 100;
794 	     else
795 		rb->p = EoGetLayer(ew);
796 	  }
797 	else if (eo->type == EOBJ_TYPE_BUTTON)
798 	  {
799 	     if (!eo->sticky && eo->desk != dsk)
800 		continue;
801 
802 	     rb->data = NULL;
803 	     rb->p = (eo->sticky) ? 1 : 0;
804 	  }
805 	else
806 	  {
807 	     continue;
808 	  }
809 
810 	x = EobjGetX(eo);
811 	y = EobjGetY(eo);
812 	w = EobjGetW(eo);
813 	h = EobjGetH(eo);
814 
815 	if (x < 0)
816 	  {
817 	     w += x;
818 	     x = 0;
819 	  }
820 	if ((x + w) > WinGetW(VROOT))
821 	   w = WinGetW(VROOT) - x;
822 
823 	if (y < 0)
824 	  {
825 	     h += y;
826 	     y = 0;
827 	  }
828 	if ((y + h) > WinGetH(VROOT))
829 	   h = WinGetH(VROOT) - y;
830 
831 	if ((w <= 0) || (h <= 0))
832 	   continue;
833 
834 	rb->x = x;
835 	rb->y = y;
836 	rb->w = w;
837 	rb->h = h;
838 #if DEBUG_ARRANGE
839 	Eprintf("Add fixed: x,y=%4d,%4d wxh=%3dx%3d p=%2d: %s\n", rb->x, rb->y,
840 		rb->w, rb->h, rb->p, EobjGetName(eo));
841 #endif
842 
843 	nfix++;
844      }
845 
846  done:
847 #if DEBUG_ARRANGE
848    Eprintf("Fixed: %p/%d  Floating: %p/%d\n", fixed, nfix, floating, nflt);
849 #endif
850    *pfixed = fixed;
851    *nfixed = nfix;
852    if (pfloating)
853       *pfloating = floating;
854    if (nfloating)
855       *nfloating = nflt;
856 }
857 
858 void
ArrangeEwinXY(EWin * ewin,int * px,int * py)859 ArrangeEwinXY(EWin * ewin, int *px, int *py)
860 {
861    int                 i, num;
862    RectBox            *fixed, *ret, newrect;
863 
864    *px = *py = 0;
865 
866    if (!ewin)
867       return;
868 
869    EwinListGetAll(&num);
870    if (num <= 1)
871      {
872 	ArrangeEwinCenteredXY(ewin, px, py);
873 	return;
874      }
875 
876    fixed = NULL;
877    ArrangeGetRectList(&fixed, &num, NULL, NULL, ewin);
878 
879    newrect.data = ewin;
880    newrect.x = 0;
881    newrect.y = 0;
882    newrect.w = EoGetW(ewin);
883    newrect.h = EoGetH(ewin);
884    newrect.p = EoGetLayer(ewin);
885 
886    ret = ECALLOC(RectBox, num + 1);
887    if (!ret)
888       goto done;
889 
890    ArrangeRects(fixed, num, &newrect, 1, ret, 0, 0,
891 		WinGetW(VROOT), WinGetH(VROOT), ARRANGE_BY_SIZE, 1);
892 
893    for (i = 0; i < num + 1; i++)
894      {
895 	if (ret[i].data == ewin)
896 	  {
897 	     *px = ret[i].x;
898 	     *py = ret[i].y;
899 	     break;
900 	  }
901      }
902 
903  done:
904    Efree(ret);
905    Efree(fixed);
906 }
907 
908 void
ArrangeEwinCenteredXY(EWin * ewin,int * px,int * py)909 ArrangeEwinCenteredXY(EWin * ewin, int *px, int *py)
910 {
911    int                 x, y, w, h;
912 
913    ScreenGetAvailableAreaByPointer(&x, &y, &w, &h, Conf.place.ignore_struts);
914    *px = (w - EoGetW(ewin)) / 2 + x;
915    *py = (h - EoGetH(ewin)) / 2 + y;
916 }
917 
918 void
ArrangeEwinCenteredOn(EWin * ewin,int x,int y,int w,int h,int * px,int * py)919 ArrangeEwinCenteredOn(EWin * ewin, int x, int y, int w, int h, int *px, int *py)
920 {
921    int                 sx, sy, sw, sh;
922 
923    x += (w - EoGetW(ewin)) / 2;
924    y += (h - EoGetH(ewin)) / 2;
925 
926    ScreenGetAvailableArea(x, y, &sx, &sy, &sw, &sh, Conf.place.ignore_struts);
927 
928    /* keep it all on this screen if possible */
929    x = MIN(x, sx + sw - EoGetW(ewin));
930    y = MIN(y, sy + sh - EoGetH(ewin));
931    x = MAX(x, sx);
932    y = MAX(y, sy);
933 
934    *px = x;
935    *py = y;
936 }
937 
938 void
ArrangeEwins(const char * params)939 ArrangeEwins(const char *params)
940 {
941    const char         *type;
942    int                 method;
943    int                 i, nfix, nflt, num;
944    RectBox            *fixed, *ret, *floating;
945    EWin               *const *lst, *ewin;
946 
947    type = params;
948    method = ARRANGE_BY_SIZE;
949 
950    if (params)
951      {
952 	if (!strcmp("order", type))
953 	  {
954 	     method = ARRANGE_VERBATIM;
955 	  }
956 	else if (!strcmp("place", type))
957 	  {
958 	     method = ARRANGE_BY_POSITION;
959 	  }
960      }
961 
962    fixed = floating = ret = NULL;
963 
964    lst = EwinListGetAll(&num);
965    if (!lst)
966       goto done;
967 
968    ArrangeGetRectList(&fixed, &nfix, &floating, &nflt, NULL);
969 
970    ret = ECALLOC(RectBox, nflt + nfix);
971    if (!ret)
972       goto done;
973 
974    ArrangeRects(fixed, nfix, floating, nflt, ret, 0, 0,
975 		WinGetW(VROOT), WinGetH(VROOT), method, 1);
976 
977    for (i = nfix; i < nflt + nfix; i++)
978      {
979 	if (!ret[i].data)
980 	   continue;
981 
982 	ewin = (EWin *) ret[i].data;
983 	if ((EoGetX(ewin) == ret[i].x) && (EoGetY(ewin) == ret[i].y))
984 	   continue;
985 
986 	if (Conf.place.cleanupslide)
987 	   EwinSlideTo(ewin, EoGetX(ewin), EoGetY(ewin),
988 		       ret[i].x, ret[i].y, Conf.place.slidespeedcleanup,
989 		       Conf.place.slidemode, 0);
990 	else
991 	   EwinMove(ewin, ret[i].x, ret[i].y, 0);
992      }
993 
994  done:
995    Efree(fixed);
996    Efree(ret);
997    Efree(floating);
998 }
999