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