1 /*      $Id$
2 
3         This program is free software; you can redistribute it and/or modify
4         it under the terms of the GNU General Public License as published by
5         the Free Software Foundation; either version 2, or (at your option)
6         any later version.
7 
8         This program is distributed in the hope that it will be useful,
9         but WITHOUT ANY WARRANTY; without even the implied warranty of
10         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11         GNU General Public License for more details.
12 
13         You should have received a copy of the GNU General Public License
14         along with this program; if not, write to the Free Software
15         Foundation, Inc., Inc., 51 Franklin Street, Fifth Floor, Boston,
16         MA 02110-1301, USA.
17 
18 
19         xfwm4    - (c) 2002-2011 Olivier Fourdan
20 
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include <X11/Xlib.h>
28 #include <X11/Xutil.h>
29 #include <glib.h>
30 #include <libxfce4util/libxfce4util.h>
31 
32 #include <common/xfwm-common.h>
33 
34 #include "screen.h"
35 #include "misc.h"
36 #include "client.h"
37 #include "placement.h"
38 #include "transients.h"
39 #include "workspaces.h"
40 #include "frame.h"
41 #include "netwm.h"
42 
43 #define MAX_VALID_STRUT(n) (n / 4) /* 25% of available space */
44 #define USE_CLIENT_STRUTS(c) (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_VISIBLE) && \
45                               FLAG_TEST (c->flags, CLIENT_FLAG_HAS_STRUT))
46 
47 /* Compute rectangle overlap area */
48 
49 static inline unsigned long
segment_overlap(int x0,int x1,int tx0,int tx1)50 segment_overlap (int x0, int x1, int tx0, int tx1)
51 {
52     if (tx0 > x0)
53     {
54         x0 = tx0;
55     }
56     if (tx1 < x1)
57     {
58         x1 = tx1;
59     }
60     if (x1 <= x0)
61     {
62         return 0;
63     }
64     return (x1 - x0);
65 }
66 
67 static inline unsigned long
overlap(int x0,int y0,int x1,int y1,int tx0,int ty0,int tx1,int ty1)68 overlap (int x0, int y0, int x1, int y1, int tx0, int ty0, int tx1, int ty1)
69 {
70     /* Compute overlapping box */
71     return (segment_overlap (x0, x1, tx0, tx1)
72             * segment_overlap (y0, y1, ty0, ty1));
73 }
74 
75 static void
set_rectangle(GdkRectangle * rect,gint x,gint y,gint width,gint height)76 set_rectangle (GdkRectangle * rect, gint x, gint y, gint width, gint height)
77 {
78     g_return_if_fail (rect != NULL);
79 
80     rect->x = x;
81     rect->y = y;
82     rect->width = width;
83     rect->height = height;
84 }
85 
86 gboolean
strutsToRectangles(Client * c,GdkRectangle * left,GdkRectangle * right,GdkRectangle * top,GdkRectangle * bottom)87 strutsToRectangles (Client *c,
88                     GdkRectangle *left,
89                     GdkRectangle *right,
90                     GdkRectangle *top,
91                     GdkRectangle *bottom)
92 {
93     ScreenInfo *screen_info;
94 
95     g_return_val_if_fail (c != NULL, FALSE);
96     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
97 
98     screen_info = c->screen_info;
99 
100     if (!USE_CLIENT_STRUTS (c))
101     {
102         return FALSE;
103     }
104 
105     if (left)
106     {
107         set_rectangle (left,
108                        0,
109                        c->struts[STRUTS_LEFT_START_Y],
110                        c->struts[STRUTS_LEFT],
111                        c->struts[STRUTS_LEFT_END_Y] - c->struts[STRUTS_LEFT_START_Y]);
112     }
113 
114     if (right)
115     {
116         set_rectangle (right,
117                        screen_info->width - c->struts[STRUTS_RIGHT],
118                        c->struts[STRUTS_RIGHT_START_Y],
119                        c->struts[STRUTS_RIGHT],
120                        c->struts[STRUTS_RIGHT_END_Y] - c->struts[STRUTS_RIGHT_START_Y]);
121     }
122 
123     if (top)
124     {
125         set_rectangle (top,
126                        c->struts[STRUTS_TOP_START_X],
127                        0,
128                        c->struts[STRUTS_TOP_END_X] - c->struts[STRUTS_TOP_START_X],
129                        c->struts[STRUTS_TOP]);
130     }
131 
132     if (bottom)
133     {
134         set_rectangle (bottom,
135                        c->struts[STRUTS_BOTTOM_START_X],
136                        screen_info->height - c->struts[STRUTS_BOTTOM],
137                        c->struts[STRUTS_BOTTOM_END_X] - c->struts[STRUTS_BOTTOM_START_X],
138                        c->struts[STRUTS_BOTTOM]);
139     }
140 
141     return TRUE;
142 }
143 
144 gboolean
checkValidStruts(GdkRectangle * struts,GdkRectangle * monitor,int side)145 checkValidStruts (GdkRectangle *struts,
146                   GdkRectangle *monitor,
147                   int side)
148 {
149     GdkRectangle intersect;
150 
151     if (gdk_rectangle_intersect (struts, monitor, &intersect))
152     {
153         switch (side)
154         {
155             case STRUTS_LEFT:
156             case STRUTS_RIGHT:
157                 return (intersect.width < MAX_VALID_STRUT(monitor->width));
158                 break;
159             case STRUTS_TOP:
160             case STRUTS_BOTTOM:
161                 return (intersect.height < MAX_VALID_STRUT(monitor->height));
162                 break;
163             default:
164                 TRACE ("unhandled strut side");
165                 break;
166         }
167     }
168     return TRUE;
169 }
170 
171 void
clientMaxSpace(ScreenInfo * screen_info,int * x,int * y,int * w,int * h)172 clientMaxSpace (ScreenInfo *screen_info, int *x, int *y, int *w, int *h)
173 {
174     Client *c;
175     guint i;
176     GdkRectangle top, left, right, bottom, area, initial, intersect;
177 
178     g_return_if_fail (x != NULL);
179     g_return_if_fail (y != NULL);
180     g_return_if_fail (w != NULL);
181     g_return_if_fail (h != NULL);
182 
183     set_rectangle (&area, *x, *y, *w, *h);
184     set_rectangle (&initial, *x, *y, *w, *h);
185 
186     for (c = screen_info->clients, i = 0; i < screen_info->client_count; c = c->next, i++)
187     {
188         if (!USE_CLIENT_STRUTS(c))
189         {
190             continue;
191         }
192 
193         if (strutsToRectangles (c, &left, &right, &top, &bottom))
194         {
195             /* Left */
196             if (checkValidStruts (&left, &initial, STRUTS_LEFT) &&
197                 gdk_rectangle_intersect (&left, &area, &intersect))
198             {
199                 *x = *x + intersect.width;
200                 *w = *w - intersect.width;
201                  set_rectangle (&area, *x, *y, *w, *h);
202             }
203 
204             /* Right */
205             if (checkValidStruts (&right, &initial, STRUTS_RIGHT) &&
206                 gdk_rectangle_intersect (&right, &area, &intersect))
207             {
208                 *w = *w - intersect.width;
209                 set_rectangle (&area, *x, *y, *w, *h);
210             }
211 
212             /* Top */
213             if (checkValidStruts (&top, &initial, STRUTS_TOP) &&
214                 gdk_rectangle_intersect (&top, &area, &intersect))
215             {
216                 *y = *y + intersect.height;
217                 *h = *h - intersect.height;
218                 set_rectangle (&area, *x, *y, *w, *h);
219             }
220 
221             /* Bottom */
222             if (checkValidStruts (&bottom, &initial, STRUTS_BOTTOM) &&
223                 gdk_rectangle_intersect (&bottom, &area, &intersect))
224             {
225                 *h = *h - intersect.height;
226                 set_rectangle (&area, *x, *y, *w, *h);
227             }
228         }
229     }
230 }
231 
232 /* clientConstrainPos() is used when moving windows
233    to ensure that the window stays accessible to the user
234 
235    Returns the position in which the window was constrained.
236     CLIENT_CONSTRAINED_TOP    = 1<<0
237     CLIENT_CONSTRAINED_BOTTOM = 1<<1
238     CLIENT_CONSTRAINED_LEFT   = 1<<2
239     CLIENT_CONSTRAINED_RIGHT  = 1<<3
240 
241  */
242 unsigned int
clientConstrainPos(Client * c,gboolean show_full)243 clientConstrainPos (Client * c, gboolean show_full)
244 {
245     Client *c2;
246     ScreenInfo *screen_info;
247     guint i;
248     gint cx, cy;
249     gint frame_top, frame_left;
250     gint title_visible;
251     gint screen_width, screen_height;
252     guint ret;
253     GdkRectangle top, left, right, bottom, win, monitor;
254     gint min_visible;
255 
256     g_return_val_if_fail (c != NULL, 0);
257 
258     TRACE ("client \"%s\" (0x%lx) %s", c->name, c->window,
259         show_full ? "(with show full)" : "(w/out show full)");
260 
261     screen_info = c->screen_info;
262 
263     /* We use a bunch of local vars to reduce the overhead of calling other functions all the time */
264     frame_top = frameExtentTop (c);
265     frame_left = frameExtentLeft (c);
266     set_rectangle (&win, frameExtentX (c), frameExtentY (c), frameExtentWidth (c), frameExtentHeight (c));
267 
268     title_visible = frame_top;
269     if (title_visible <= 0)
270     {
271         /* CSD window, use the title height from the theme */
272         title_visible = frameDecorationTop (screen_info);
273     }
274     min_visible = MAX (title_visible, CLIENT_MIN_VISIBLE);
275     ret = 0;
276 
277     cx = win.x + (win.width / 2);
278     cy = win.y + (win.height / 2);
279     myScreenFindMonitorAtPoint (screen_info, cx, cy, &monitor);
280 
281     screen_width = screen_info->width;
282     screen_height = screen_info->height;
283 
284     if (FLAG_TEST (c->flags, CLIENT_FLAG_FULLSCREEN))
285     {
286         TRACE ("ignoring constrained for client \"%s\" (0x%lx)", c->name,
287             c->window);
288         return 0;
289     }
290     if (show_full)
291     {
292         for (c2 = screen_info->clients, i = 0; i < screen_info->client_count; c2 = c2->next, i++)
293         {
294             if ((c2 == c) || !strutsToRectangles (c2, &left, &right, &top, &bottom))
295             {
296                 continue;
297             }
298 
299             /* right */
300             if (checkValidStruts (&right, &monitor, STRUTS_RIGHT) &&
301                 gdk_rectangle_intersect (&right, &win, NULL))
302             {
303                 c->x = screen_width - c2->struts[STRUTS_RIGHT] - win.width + frame_left;
304                 win.x = frameExtentX (c);
305                 ret |= CLIENT_CONSTRAINED_RIGHT;
306             }
307 
308             /* Bottom */
309             if (checkValidStruts (&bottom, &monitor, STRUTS_BOTTOM) &&
310                 gdk_rectangle_intersect (&bottom, &win, NULL))
311             {
312                 c->y = screen_height - c2->struts[STRUTS_BOTTOM] - win.height + frame_top;
313                 win.y = frameExtentY (c);
314                 ret |= CLIENT_CONSTRAINED_BOTTOM;
315             }
316         }
317 
318         if (win.x + win.width >= monitor.x + monitor.width)
319         {
320             c->x = monitor.x + monitor.width - win.width + frame_left;
321             win.x = frameExtentX (c);
322             ret |= CLIENT_CONSTRAINED_RIGHT;
323         }
324         if (win.x <= monitor.x)
325         {
326             c->x = monitor.x + frame_left;
327             win.x = frameExtentX (c);
328             ret |= CLIENT_CONSTRAINED_LEFT;
329         }
330         if (win.y + win.height >= monitor.y + monitor.height)
331         {
332             c->y = monitor.y + monitor.height - win.height + frame_top;
333             win.y = frameExtentY (c);
334             ret |= CLIENT_CONSTRAINED_BOTTOM;
335         }
336         if (win.y <= monitor.y)
337         {
338             c->y = monitor.y + frame_top;
339             win.y = frameExtentY (c);
340             ret |= CLIENT_CONSTRAINED_TOP;
341         }
342 
343         for (c2 = screen_info->clients, i = 0; i < screen_info->client_count; c2 = c2->next, i++)
344         {
345             if ((c2 == c) || !strutsToRectangles (c2, &left, &right, &top, &bottom))
346             {
347                 continue;
348             }
349 
350             /* Left */
351             if (checkValidStruts (&left, &monitor, STRUTS_LEFT) &&
352                 gdk_rectangle_intersect (&left, &win, NULL))
353             {
354                 c->x = c2->struts[STRUTS_LEFT] + frame_left;
355                 win.x = frameExtentX (c);
356                 ret |= CLIENT_CONSTRAINED_LEFT;
357             }
358 
359             /* Top */
360             if (checkValidStruts (&top, &monitor, STRUTS_TOP) &&
361                 gdk_rectangle_intersect (&top, &win, NULL))
362             {
363                 c->y = c2->struts[STRUTS_TOP] + frame_top;
364                 win.y = frameExtentY (c);
365                 ret |= CLIENT_CONSTRAINED_TOP;
366             }
367         }
368     }
369     else
370     {
371         if (win.x + win.width <= monitor.x + min_visible)
372         {
373             c->x = monitor.x + min_visible - win.width + frame_left;
374             win.x = frameExtentX (c);
375             ret |= CLIENT_CONSTRAINED_LEFT;
376         }
377         if (win.x + min_visible >= monitor.x + monitor.width)
378         {
379             c->x = monitor.x + monitor.width - min_visible + frame_left;
380             win.x = frameExtentX (c);
381             ret |= CLIENT_CONSTRAINED_RIGHT;
382         }
383         if (win.y + win.height <= monitor.y + min_visible)
384         {
385             c->y = monitor.y + min_visible - win.height + frame_top;
386             win.y = frameExtentY (c);
387             ret |= CLIENT_CONSTRAINED_TOP;
388         }
389         if (win.y + min_visible >= monitor.y + monitor.height)
390         {
391             c->y = monitor.y + monitor.height - min_visible + frame_top;
392             win.y = frameExtentY (c);
393             ret |= CLIENT_CONSTRAINED_BOTTOM;
394         }
395         if ((win.y <= monitor.y) && (win.y >= monitor.y - frame_top))
396         {
397             c->y = monitor.y + frame_top;
398             win.y = frameExtentY (c);
399             ret |= CLIENT_CONSTRAINED_TOP;
400         }
401 
402         /* Struts and other partial struts */
403         for (c2 = screen_info->clients, i = 0; i < screen_info->client_count; c2 = c2->next, i++)
404         {
405             if ((c2 == c) || !strutsToRectangles (c2, &left, &right, &top, &bottom))
406             {
407                 continue;
408             }
409 
410             /* Right */
411             if (checkValidStruts (&right, &monitor, STRUTS_RIGHT) &&
412                 gdk_rectangle_intersect (&right, &win, NULL))
413             {
414                 if (win.x >= screen_width - c2->struts[STRUTS_RIGHT] - min_visible)
415                 {
416                     c->x = screen_width - c2->struts[STRUTS_RIGHT] - min_visible + frame_left;
417                     win.x = frameExtentX (c);
418                     ret |= CLIENT_CONSTRAINED_RIGHT;
419                 }
420             }
421 
422             /* Left */
423             if (checkValidStruts (&left, &monitor, STRUTS_LEFT) &&
424                 gdk_rectangle_intersect (&left, &win, NULL))
425             {
426                 if (win.x + win.width <= c2->struts[STRUTS_LEFT] + min_visible)
427                 {
428                     c->x = c2->struts[STRUTS_LEFT] + min_visible - win.width + frame_left;
429                     win.x = frameExtentX (c);
430                     ret |= CLIENT_CONSTRAINED_LEFT;
431                 }
432             }
433 
434             /* Bottom */
435             if (checkValidStruts (&bottom, &monitor, STRUTS_BOTTOM) &&
436                 gdk_rectangle_intersect (&bottom, &win, NULL))
437             {
438                 if (win.y >= screen_height - c2->struts[STRUTS_BOTTOM] - min_visible)
439                 {
440                     c->y = screen_height - c2->struts[STRUTS_BOTTOM] - min_visible + frame_top;
441                     win.y = frameExtentY (c);
442                     ret |= CLIENT_CONSTRAINED_BOTTOM;
443                 }
444             }
445 
446             /* Top */
447             if (checkValidStruts (&top, &monitor, STRUTS_TOP) &&
448                 gdk_rectangle_intersect (&top, &win, NULL))
449             {
450                 if (segment_overlap (win.y, win.y + title_visible, 0, c2->struts[STRUTS_TOP]))
451                 {
452                     c->y = c2->struts[STRUTS_TOP] + frame_top;
453                     win.y = frameExtentY (c);
454                     ret |= CLIENT_CONSTRAINED_TOP;
455                 }
456                 if (win.y + win.height <= c2->struts[STRUTS_TOP] + min_visible)
457                 {
458                     c->y = c2->struts[STRUTS_TOP] + min_visible - win.height + frame_top;
459                     win.y = frameExtentY (c);
460                     ret |= CLIENT_CONSTRAINED_TOP;
461                 }
462             }
463         }
464     }
465     return ret;
466 }
467 
468 /* clientKeepVisible is used at initial mapping, to make sure
469    the window is visible on screen. It also does coordonate
470    translation in Xinerama to center window on physical screen
471    Not to be confused with clientConstrainPos()
472  */
473 static void
clientKeepVisible(Client * c,gint n_monitors,GdkRectangle * monitor_rect)474 clientKeepVisible (Client * c, gint n_monitors, GdkRectangle *monitor_rect)
475 {
476     gboolean centered;
477     int diff_x, diff_y;
478 
479     g_return_if_fail (c != NULL);
480     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
481 
482     centered = FALSE;
483     /* We only center dialogs */
484     if (c->type & (WINDOW_TYPE_DIALOG))
485     {
486         if ((c->size->x == 0) && (c->size->y == 0))
487         {
488             /* Dialogs that place themselves in (0,0) will be centered */
489             centered = TRUE;
490         }
491         else if ((n_monitors > 1) && (c->size->x > 0) && (c->size->y > 0))
492         {
493             /* Check if the window is centered on the whole screen */
494             diff_x = ABS(c->size->x - ((c->screen_info->width - c->size->width) / 2));
495             diff_y = ABS(c->size->y - ((c->screen_info->height - c->size->height) / 2));
496             centered = ((diff_x < 25) && (diff_y < 25));
497         }
498     }
499     if (centered)
500     {
501         /* We consider that the windows is centered on screen,
502          * Thus, will move it so its center on the current
503          * physical screen
504          */
505         c->x = monitor_rect->x + (monitor_rect->width - c->width) / 2;
506         c->y = monitor_rect->y + (monitor_rect->height - c->height) / 2;
507     }
508     clientConstrainPos (c, TRUE);
509 }
510 
511 static void
clientAutoMaximize(Client * c,int full_w,int full_h)512 clientAutoMaximize (Client * c, int full_w, int full_h)
513 {
514     if (FLAG_TEST (c->flags, CLIENT_FLAG_FULLSCREEN) ||
515         !FLAG_TEST (c->xfwm_flags, XFWM_FLAG_HAS_BORDER))
516     {
517         /*
518          * Fullscreen or undecorated windows should not be
519          * automatically maximized...
520          */
521         return;
522     }
523 
524     if (!FLAG_TEST (c->flags, CLIENT_FLAG_MAXIMIZED_HORIZ) &&
525         (frameExtentWidth (c) >= full_w))
526     {
527         DBG ("The application \"%s\" has requested a window width "
528              "(%u) equal or larger than the actual width available in the workspace (%u), "
529              "the window will be maximized horizontally.", c->name, frameExtentWidth (c), full_w);
530         FLAG_SET (c->flags, CLIENT_FLAG_MAXIMIZED_HORIZ | CLIENT_FLAG_RESTORE_SIZE_POS);
531     }
532 
533     if (!FLAG_TEST (c->flags, CLIENT_FLAG_MAXIMIZED_VERT) &&
534         (frameExtentHeight (c) >= full_h))
535     {
536         DBG ("The application \"%s\" has requested a window height "
537              "(%u) equal or larger than the actual height available in the workspace (%u), "
538              "the window will be maximized vertically.", c->name, frameExtentHeight (c), full_h);
539         FLAG_SET (c->flags, CLIENT_FLAG_MAXIMIZED_VERT | CLIENT_FLAG_RESTORE_SIZE_POS);
540     }
541 }
542 
543 static void
smartPlacement(Client * c,int full_x,int full_y,int full_w,int full_h)544 smartPlacement (Client * c, int full_x, int full_y, int full_w, int full_h)
545 {
546     Client *c2;
547     ScreenInfo *screen_info;
548     gfloat best_overlaps;
549     guint i;
550     gint test_x, test_y, xmax, ymax, best_x, best_y;
551     gint frame_height, frame_width, frame_left, frame_top;
552     gint c2_x, c2_y;
553     gint xmin, ymin;
554 
555     g_return_if_fail (c != NULL);
556 
557     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
558 
559     screen_info = c->screen_info;
560     frame_height = frameExtentHeight (c);
561     frame_width = frameExtentWidth (c);
562     frame_left = frameExtentLeft(c);
563     frame_top = frameExtentTop (c);
564 
565     /* max coordinates (bottom-right) */
566     xmax = full_x + full_w - c->width - frameExtentRight (c);
567     ymax = full_y + full_h - c->height - frameExtentBottom (c);
568 
569     /* min coordinates (top-left) */
570     xmin = full_x + frameExtentLeft (c);
571     ymin = full_y + frameExtentTop (c);
572 
573     /* start with worst-case position at top-left */
574     best_overlaps = G_MAXFLOAT;
575     best_x = xmin;
576     best_y = ymin;
577 
578     TRACE ("analyzing %i clients", screen_info->client_count);
579 
580     test_y = ymin;
581     do
582     {
583         gint next_test_y = G_MAXINT;
584         gboolean first_test_x = TRUE;
585 
586         TRACE ("testing y position %d", test_y);
587 
588         test_x = xmin;
589         do
590         {
591             gfloat count_overlaps = 0.0;
592             gint next_test_x = G_MAXINT;
593             gint c2_next_test_x;
594             gint c2_next_test_y;
595             gint c2_frame_height;
596             gint c2_frame_width;
597 
598             TRACE ("testing x position %d", test_x);
599 
600             for (c2 = screen_info->clients, i = 0; i < screen_info->client_count; c2 = c2->next, i++)
601             {
602                 if ((c2 != c) && (c2->type != WINDOW_DESKTOP)
603                     && (c->win_workspace == c2->win_workspace)
604                     && FLAG_TEST (c2->xfwm_flags, XFWM_FLAG_VISIBLE))
605                 {
606                     c2_x = frameExtentX (c2);
607                     c2_frame_width = frameExtentWidth (c2);
608                     if (c2_x >= full_x + full_w
609                         || c2_x + c2_frame_width < full_x)
610                     {
611                         /* skip clients on right-of or left-of monitor */
612                         continue;
613                     }
614 
615                     c2_y = frameExtentY (c2);
616                     c2_frame_height = frameExtentHeight (c2);
617                     if (c2_y >= full_y + full_h
618                         || c2_y + c2_frame_height < full_y)
619                     {
620                         /* skip clients on above-of or below-of monitor */
621                         continue;
622                     }
623 
624                     count_overlaps += overlap (test_x - frame_left,
625                                                test_y - frame_top,
626                                                test_x - frame_left + frame_width,
627                                                test_y - frame_top + frame_height,
628                                                c2_x,
629                                                c2_y,
630                                                c2_x + c2_frame_width,
631                                                c2_y + c2_frame_height);
632 
633                     /* find the next x boundy for the step */
634                     if (test_x > c2_x)
635                     {
636                         /* test location is beyond the x of the window,
637                          * take the window right corner as next target */
638                         c2_x += c2_frame_width;
639                     }
640                     c2_next_test_x = MIN (c2_x, xmax);
641                     if (c2_next_test_x < next_test_x
642                         && c2_next_test_x > test_x)
643                     {
644                         /* set new optimal next x step position */
645                         next_test_x = c2_next_test_x;
646                     }
647 
648                     if (first_test_x)
649                     {
650                         /* find the next y boundry step */
651                         if (test_y > c2_y)
652                         {
653                             /* test location is beyond the y of the window,
654                              * take the window bottom corner as next target */
655                             c2_y += c2_frame_height;
656                         }
657                         c2_next_test_y = MIN (c2_y, ymax);
658                         if (c2_next_test_y < next_test_y
659                             && c2_next_test_y > test_y)
660                         {
661                             /* set new optimal next y step position */
662                             next_test_y = c2_next_test_y;
663                         }
664                     }
665                 }
666             }
667 
668             /* don't look for the next y boundry this x row */
669             first_test_x = FALSE;
670 
671             if (count_overlaps < best_overlaps)
672             {
673                 /* found position with less overlap */
674                 best_x = test_x;
675                 best_y = test_y;
676                 best_overlaps = count_overlaps;
677 
678                 if (count_overlaps == 0.0f)
679                 {
680                     /* overlap is ideal, stop searching */
681                     TRACE ("found position without overlap");
682                     goto found_best;
683                 }
684             }
685 
686             if (G_LIKELY (next_test_x != G_MAXINT))
687             {
688                 test_x = MAX (next_test_x, next_test_x + frameExtentLeft (c));
689                 if (test_x > xmax)
690                 {
691                    /* always clamp on the monitor */
692                    test_x = xmax;
693                 }
694             }
695             else
696             {
697                 test_x++;
698             }
699         }
700         while (test_x <= xmax);
701 
702         if (G_LIKELY (next_test_y != G_MAXINT))
703         {
704             test_y = MAX (next_test_y, next_test_y + frameExtentTop (c));
705             if (test_y > ymax)
706             {
707                 /* always clamp on the monitor */
708                 test_y = ymax;
709             }
710         }
711         else
712         {
713             test_y++;
714         }
715     }
716     while (test_y <= ymax);
717 
718     found_best:
719 
720     TRACE ("overlaps %f at %d,%d (x,y)", best_overlaps, best_x, best_y);
721 
722     c->x = best_x;
723     c->y = best_y;
724 }
725 
726 static void
centerPlacement(Client * c,int full_x,int full_y,int full_w,int full_h)727 centerPlacement (Client * c, int full_x, int full_y, int full_w, int full_h)
728 {
729     g_return_if_fail (c != NULL);
730 
731     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
732 
733     c->x = MAX (full_x + frameExtentLeft(c) + (full_w - frameExtentWidth(c)) / 2, full_x + frameExtentLeft(c));
734     c->y = MAX (full_y + frameExtentTop(c) + (full_h - frameExtentHeight(c)) / 2, full_y + frameExtentTop(c));
735 }
736 
737 static void
mousePlacement(Client * c,int full_x,int full_y,int full_w,int full_h,int mx,int my)738 mousePlacement (Client * c, int full_x, int full_y, int full_w, int full_h, int mx, int my)
739 {
740     g_return_if_fail (c != NULL);
741 
742     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
743 
744     c->x = mx + frameExtentLeft(c) - frameExtentWidth(c) / 2;
745     c->y = my + frameExtentTop(c) - frameExtentHeight(c) / 2;
746 
747     c->x = MIN (c->x, full_x + full_w - frameExtentWidth(c) + frameExtentLeft(c));
748     c->y = MIN (c->y, full_y + full_h - frameExtentHeight(c) + frameExtentTop(c));
749 
750     c->x = MAX (c->x, full_x + frameExtentLeft(c));
751     c->y = MAX (c->y, full_y + frameExtentTop(c));
752 }
753 
754 void
clientInitPosition(Client * c)755 clientInitPosition (Client * c)
756 {
757     ScreenInfo *screen_info;
758     Client *c2;
759     GdkRectangle rect;
760     int full_x, full_y, full_w, full_h, msx, msy;
761     gint n_monitors;
762     gboolean place;
763     gboolean position;
764     gboolean is_transient;
765 
766     g_return_if_fail (c != NULL);
767 
768     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
769 
770     screen_info = c->screen_info;
771     msx = 0;
772     msy = 0;
773     position = (c->size->flags & (PPosition | USPosition));
774 
775     n_monitors = myScreenGetNumMonitors (c->screen_info);
776     getMouseXY (screen_info, &msx, &msy);
777     myScreenFindMonitorAtPoint (screen_info, msx, msy, &rect);
778     is_transient = clientIsTransient (c);
779 
780     if (position || is_transient || (c->type & (WINDOW_TYPE_DONT_PLACE | WINDOW_TYPE_DIALOG)))
781     {
782         if (!position && is_transient && (c2 = clientGetTransient (c)))
783         {
784             /* Center transient relative to their parent window */
785             c->x = c2->x + (c2->width - c->width) / 2;
786             c->y = c2->y + (c2->height - c->height) / 2;
787         }
788 
789         if (position && n_monitors > 1)
790         {
791             msx = frameExtentX (c) + (frameExtentWidth (c) / 2);
792             msy = frameExtentY (c) + (frameExtentHeight (c) / 2);
793             myScreenFindMonitorAtPoint (screen_info, msx, msy, &rect);
794         }
795 
796         if (CONSTRAINED_WINDOW (c))
797         {
798             clientKeepVisible (c, n_monitors, &rect);
799         }
800         place = FALSE;
801     }
802     else
803     {
804         place = TRUE;
805     }
806 
807     full_x = MAX (screen_info->params->xfwm_margins[STRUTS_LEFT], rect.x);
808     full_y = MAX (screen_info->params->xfwm_margins[STRUTS_TOP], rect.y);
809     full_w = MIN (screen_info->width - screen_info->params->xfwm_margins[STRUTS_RIGHT],
810                   rect.x + rect.width) - full_x;
811     full_h = MIN (screen_info->height - screen_info->params->xfwm_margins[STRUTS_BOTTOM],
812                   rect.y + rect.height) - full_y;
813 
814     /* Adjust size to the widest size available, not covering struts */
815     clientMaxSpace (screen_info, &full_x, &full_y, &full_w, &full_h);
816 
817     /*
818        If the windows is smaller than the given ratio of the available screen area,
819        or if the window is larger than the screen area or if the given ratio is higher
820        than 100% place the window at the center.
821        Otherwise, place the window "smartly", using the good old CPU consuming algorithm...
822      */
823     if (place)
824     {
825         if ((screen_info->params->placement_ratio >= 100) ||
826             (100 * frameExtentWidth(c) * frameExtentHeight(c)) < (screen_info->params->placement_ratio * full_w * full_h))
827         {
828             if (screen_info->params->placement_mode == PLACE_MOUSE)
829             {
830                 mousePlacement (c, full_x, full_y, full_w, full_h, msx, msy);
831             }
832             else
833             {
834                 centerPlacement (c, full_x, full_y, full_w, full_h);
835             }
836         }
837         else if ((frameExtentWidth(c) >= full_w) && (frameExtentHeight(c) >= full_h))
838         {
839             centerPlacement (c, full_x, full_y, full_w, full_h);
840         }
841         else
842         {
843             smartPlacement (c, full_x, full_y, full_w, full_h);
844         }
845     }
846 
847     if (c->type & WINDOW_REGULAR_FOCUSABLE)
848     {
849         clientAutoMaximize (c, full_w, full_h);
850     }
851 }
852 
853 void
clientFill(Client * c,int fill_type)854 clientFill (Client * c, int fill_type)
855 {
856     ScreenInfo *screen_info;
857     Client *east_neighbour;
858     Client *west_neighbour;
859     Client *north_neighbour;
860     Client *south_neighbour;
861     Client *c2;
862     GdkRectangle rect;
863     XWindowChanges wc;
864     unsigned short mask;
865     guint i;
866     gint cx, cy, full_x, full_y, full_w, full_h;
867     gint tmp_x, tmp_y, tmp_w, tmp_h;
868 
869     g_return_if_fail (c != NULL);
870 
871     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
872 
873     if (!CLIENT_CAN_FILL_WINDOW (c))
874     {
875         return;
876     }
877 
878     screen_info = c->screen_info;
879     mask = 0;
880     east_neighbour = NULL;
881     west_neighbour = NULL;
882     north_neighbour = NULL;
883     south_neighbour = NULL;
884 
885     for (c2 = screen_info->clients, i = 0; i < screen_info->client_count; c2 = c2->next, i++)
886     {
887 
888         /* Filter out all windows which are not visible, or not on the same layer
889          * as well as the client window itself
890          */
891         if ((c != c2) && FLAG_TEST (c2->xfwm_flags, XFWM_FLAG_VISIBLE) && (c2->win_layer == c->win_layer))
892         {
893             /* Fill horizontally */
894             if (fill_type & CLIENT_FILL_HORIZ)
895             {
896                 /*
897                  * check if the neigbour client (c2) is located
898                  * east or west of our client.
899                  */
900                 if (segment_overlap (frameExtentY(c), frameExtentY(c) + frameExtentHeight(c), frameExtentY(c2), frameExtentY(c2) + frameExtentHeight(c2)))
901                 {
902                     if ((frameExtentX(c2) + frameExtentWidth(c2)) <= frameExtentX(c))
903                     {
904                         if (west_neighbour)
905                         {
906                             /* Check if c2 is closer to the client
907                              * then the west neighbour already found
908                              */
909                             if ((frameExtentX(west_neighbour) + frameExtentWidth(west_neighbour)) < (frameExtentX(c2) + frameExtentWidth(c2)))
910                             {
911                                 west_neighbour = c2;
912                             }
913                         }
914                         else
915                         {
916                             west_neighbour = c2;
917                         }
918                     }
919                     if ((frameExtentX(c) + frameExtentWidth(c)) <= frameExtentX(c2))
920                     {
921                         /* Check if c2 is closer to the client
922                          * then the west neighbour already found
923                          */
924                         if (east_neighbour)
925                         {
926                             if (frameExtentX(c2) < frameExtentX(east_neighbour))
927                             {
928                                 east_neighbour = c2;
929                             }
930                         }
931                         else
932                         {
933                             east_neighbour = c2;
934                         }
935                     }
936                 }
937             }
938 
939             /* Fill vertically */
940             if (fill_type & CLIENT_FILL_VERT)
941             {
942                 /* check if the neigbour client (c2) is located
943                  * north or south of our client.
944                  */
945                 if (segment_overlap (frameExtentX(c), frameExtentX(c) + frameExtentWidth(c), frameExtentX(c2), frameExtentX(c2) + frameExtentWidth(c2)))
946                 {
947                     if ((frameExtentY(c2) + frameExtentHeight(c2)) <= frameExtentY(c))
948                     {
949                         if (north_neighbour)
950                         {
951                             /* Check if c2 is closer to the client
952                              * then the north neighbour already found
953                              */
954                             if ((frameExtentY(north_neighbour) + frameExtentHeight(north_neighbour)) < (frameExtentY(c2) + frameExtentHeight(c2)))
955                             {
956                                 north_neighbour = c2;
957                             }
958                         }
959                         else
960                         {
961                             north_neighbour = c2;
962                         }
963                     }
964                     if ((frameExtentY(c) + frameExtentHeight(c)) <= frameExtentY(c2))
965                     {
966                         if (south_neighbour)
967                         {
968                             /* Check if c2 is closer to the client
969                              * then the south neighbour already found
970                              */
971                             if (frameExtentY(c2) < frameExtentY(south_neighbour))
972                             {
973                                 south_neighbour = c2;
974                             }
975                         }
976                         else
977                         {
978                             south_neighbour = c2;
979                         }
980                     }
981                 }
982             }
983         }
984     }
985 
986     /* Compute the largest size available, based on struts, margins and Xinerama layout */
987     tmp_x = frameExtentX (c);
988     tmp_y = frameExtentY (c);
989     tmp_h = frameExtentHeight (c);
990     tmp_w = frameExtentWidth (c);
991 
992     cx = tmp_x + (tmp_w / 2);
993     cy = tmp_y + (tmp_h / 2);
994 
995     myScreenFindMonitorAtPoint (screen_info, cx, cy, &rect);
996 
997     full_x = MAX (screen_info->params->xfwm_margins[STRUTS_LEFT], rect.x);
998     full_y = MAX (screen_info->params->xfwm_margins[STRUTS_TOP], rect.y);
999     full_w = MIN (screen_info->width - screen_info->params->xfwm_margins[STRUTS_RIGHT],
1000                   rect.x + rect.width) - full_x;
1001     full_h = MIN (screen_info->height - screen_info->params->xfwm_margins[STRUTS_BOTTOM],
1002                   rect.y + rect.height) - full_y;
1003 
1004     if ((fill_type & CLIENT_FILL) == CLIENT_FILL)
1005     {
1006         mask = CWX | CWY | CWHeight | CWWidth;
1007         /* Adjust size to the largest size available, not covering struts */
1008         clientMaxSpace (screen_info, &full_x, &full_y, &full_w, &full_h);
1009     }
1010     else if (fill_type & CLIENT_FILL_VERT)
1011     {
1012         mask = CWY | CWHeight;
1013         /* Adjust size to the tallest size available, for the current horizontal position/width */
1014         clientMaxSpace (screen_info, &tmp_x, &full_y, &tmp_w, &full_h);
1015     }
1016     else if (fill_type & CLIENT_FILL_HORIZ)
1017     {
1018         mask = CWX | CWWidth;
1019         /* Adjust size to the widest size available, for the current vertical position/height */
1020         clientMaxSpace (screen_info, &full_x, &tmp_y, &full_w, &tmp_h);
1021     }
1022 
1023     /* If there are neighbours, resize to their borders.
1024      * If not, resize to the largest size available that you just have computed.
1025      */
1026 
1027     wc.x = full_x + frameExtentLeft(c);
1028     if (west_neighbour)
1029     {
1030         wc.x += MAX (frameExtentX(west_neighbour) + frameExtentWidth(west_neighbour) - full_x, 0);
1031     }
1032 
1033     wc.width = full_w - frameExtentRight(c) - (wc.x - full_x);
1034     if (east_neighbour)
1035     {
1036         wc.width -= MAX (full_w - (frameExtentX(east_neighbour) - full_x), 0);
1037     }
1038 
1039     wc.y = full_y + frameExtentTop(c);
1040     if (north_neighbour)
1041     {
1042         wc.y += MAX (frameExtentY(north_neighbour) + frameExtentHeight(north_neighbour) - full_y, 0);
1043     }
1044 
1045     wc.height = full_h - frameExtentBottom(c) - (wc.y - full_y);
1046     if (south_neighbour)
1047     {
1048         wc.height -= MAX (full_h - (frameExtentY(south_neighbour) - full_y), 0);
1049     }
1050 
1051     TRACE ("fill size request: (%d,%d) %dx%d", wc.x, wc.y, wc.width, wc.height);
1052     if (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_MANAGED))
1053     {
1054         clientConfigure(c, &wc, mask, NO_CFG_FLAG);
1055     }
1056 }
1057 
1058