1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 overlap.c for the Openbox window manager
4 Copyright (c) 2011, 2013 Ian Zimmerman
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 See the COPYING file for a copy of the GNU General Public License.
21 #include "place_overlap.h"
22 #include "obt/bsearch.h"
27 static void make_grid(const Rect
* client_rects
,
34 static int best_direction(const Point
* grid_point
,
35 const Rect
* client_rects
,
39 Point
* best_top_left
);
41 static int total_overlap(const Rect
* client_rects
,
43 const Rect
* proposed_rect
);
45 static void center_in_field(Point
* grid_point
,
48 const Rect
* client_rects
,
54 /* Choose the placement on a grid with least overlap */
56 void place_overlap_find_least_placement(const Rect
* client_rects
,
62 POINT_SET(*result
, monitor
->x
, monitor
->y
);
63 int overlap
= G_MAXINT
;
64 int max_edges
= 2 * (n_client_rects
+ 1);
66 int x_edges
[max_edges
];
67 int y_edges
[max_edges
];
68 make_grid(client_rects
, n_client_rects
, monitor
,
69 x_edges
, y_edges
, max_edges
);
71 for (i
= 0; i
< max_edges
; ++i
) {
72 if (x_edges
[i
] == G_MAXINT
)
75 for (j
= 0; j
< max_edges
; ++j
) {
76 if (y_edges
[j
] == G_MAXINT
)
78 Point grid_point
= {.x
= x_edges
[i
], .y
= y_edges
[j
]};
81 best_direction(&grid_point
, client_rects
, n_client_rects
,
82 monitor
, req_size
, &best_top_left
);
83 if (this_overlap
< overlap
) {
84 overlap
= this_overlap
;
85 *result
= best_top_left
;
93 if (config_place_center
&& overlap
== 0) {
94 center_in_field(result
,
105 static int compare_ints(const void* a
,
108 const int* ia
= (const int*)a
;
109 const int* ib
= (const int*)b
;
113 static void uniquify(int* edges
,
119 while (j
< n_edges
) {
120 int last
= edges
[j
++];
122 while (j
< n_edges
&& edges
[j
] == last
)
125 /* fill the rest with nonsense */
126 for (; i
< n_edges
; ++i
)
130 static void make_grid(const Rect
* client_rects
,
139 for (i
= 0; i
< n_client_rects
; ++i
) {
140 if (!RECT_INTERSECTS_RECT(client_rects
[i
], *monitor
))
142 x_edges
[n_edges
] = client_rects
[i
].x
;
143 y_edges
[n_edges
++] = client_rects
[i
].y
;
144 x_edges
[n_edges
] = client_rects
[i
].x
+ client_rects
[i
].width
;
145 y_edges
[n_edges
++] = client_rects
[i
].y
+ client_rects
[i
].height
;
147 x_edges
[n_edges
] = monitor
->x
;
148 y_edges
[n_edges
++] = monitor
->y
;
149 x_edges
[n_edges
] = monitor
->x
+ monitor
->width
;
150 y_edges
[n_edges
++] = monitor
->y
+ monitor
->height
;
151 for (i
= n_edges
; i
< max_edges
; ++i
)
152 x_edges
[i
] = y_edges
[i
] = G_MAXINT
;
153 qsort(x_edges
, n_edges
, sizeof(int), compare_ints
);
154 uniquify(x_edges
, n_edges
);
155 qsort(y_edges
, n_edges
, sizeof(int), compare_ints
);
156 uniquify(y_edges
, n_edges
);
159 static int total_overlap(const Rect
* client_rects
,
161 const Rect
* proposed_rect
)
165 for (i
= 0; i
< n_client_rects
; ++i
) {
166 if (!RECT_INTERSECTS_RECT(*proposed_rect
, client_rects
[i
]))
169 RECT_SET_INTERSECTION(rtemp
, *proposed_rect
, client_rects
[i
]);
170 overlap
+= RECT_AREA(rtemp
);
175 static int find_first_grid_position_greater_or_equal(int search_value
,
179 g_assert(max_edges
>= 2);
180 g_assert(search_value
>= edges
[0]);
181 g_assert(search_value
<= edges
[max_edges
- 1]);
184 BSEARCH(int, edges
, 0, max_edges
, search_value
);
189 g_assert(BSEARCH_FOUND_NEAREST_SMALLER());
190 /* Get the nearest larger instead. */
191 return BSEARCH_AT() + 1;
194 static void expand_width(Rect
* r
, int by
)
199 static void expand_height(Rect
* r
, int by
)
204 typedef void ((*ExpandByMethod
)(Rect
*, int));
206 /* This structure packs most of the parametars for expand_field() in
207 order to save pushing the same parameters twice. */
208 typedef struct _ExpandInfo
{
209 const Point
* top_left
;
213 const Rect
* client_rects
;
218 static int expand_field(int orig_edge_index
,
220 ExpandByMethod expand_by
,
229 int edge_index
= orig_edge_index
;
230 while (edge_index
< i
->max_edges
- 1) {
231 int next_edge_index
= edge_index
+ 1;
232 (*expand_by
)(&field
, edges
[next_edge_index
] - edges
[edge_index
]);
233 int overlap
= total_overlap(i
->client_rects
, i
->n_client_rects
, &field
);
234 if (overlap
!= 0 || !RECT_CONTAINS_RECT(*(i
->monitor
), field
))
236 edge_index
= next_edge_index
;
241 /* The algortihm used for centering a rectangle in a grid field: First
242 find the smallest rectangle of grid lines that enclose the given
243 rectangle. By definition, there is no overlap with any of the other
244 windows if the given rectangle is centered within this minimal
245 rectangle. Then, try extending the minimal rectangle in either
246 direction (x and y) by picking successively further grid lines for
247 the opposite edge. If the minimal rectangle can be extended in *one*
248 direction (x or y) but *not* the other, extend it as far as possible.
249 Otherwise, just use the minimal one. */
251 static void center_in_field(Point
* top_left
,
252 const Size
* req_size
,
254 const Rect
* client_rects
,
260 /* Find minimal rectangle. */
261 int orig_right_edge_index
=
262 find_first_grid_position_greater_or_equal(
263 top_left
->x
+ req_size
->width
, x_edges
, max_edges
);
264 int orig_bottom_edge_index
=
265 find_first_grid_position_greater_or_equal(
266 top_left
->y
+ req_size
->height
, y_edges
, max_edges
);
268 .top_left
= top_left
,
269 .orig_width
= x_edges
[orig_right_edge_index
] - top_left
->x
,
270 .orig_height
= y_edges
[orig_bottom_edge_index
] - top_left
->y
,
272 .client_rects
= client_rects
,
273 .n_client_rects
= n_client_rects
,
274 .max_edges
= max_edges
};
275 /* Try extending width. */
276 int right_edge_index
=
277 expand_field(orig_right_edge_index
, x_edges
, expand_width
, &i
);
278 /* Try extending height. */
279 int bottom_edge_index
=
280 expand_field(orig_bottom_edge_index
, y_edges
, expand_height
, &i
);
282 int final_width
= x_edges
[orig_right_edge_index
] - top_left
->x
;
283 int final_height
= y_edges
[orig_bottom_edge_index
] - top_left
->y
;
284 if (right_edge_index
== orig_right_edge_index
&&
285 bottom_edge_index
!= orig_bottom_edge_index
)
286 final_height
= y_edges
[bottom_edge_index
] - top_left
->y
;
287 else if (right_edge_index
!= orig_right_edge_index
&&
288 bottom_edge_index
== orig_bottom_edge_index
)
289 final_width
= x_edges
[right_edge_index
] - top_left
->x
;
291 /* Now center the given rectangle within the field */
292 top_left
->x
+= (final_width
- req_size
->width
) / 2;
293 top_left
->y
+= (final_height
- req_size
->height
) / 2;
296 /* Given a list of Rect RECTS, a Point PT and a Size size, determine the
297 direction from PT which results in the least total overlap with RECTS
298 if a rectangle is placed in that direction. Return the top/left
299 Point of such rectangle and the resulting overlap amount. Only
300 consider placements within BOUNDS. */
302 #define NUM_DIRECTIONS 4
304 static int best_direction(const Point
* grid_point
,
305 const Rect
* client_rects
,
308 const Size
* req_size
,
309 Point
* best_top_left
)
311 static const Size directions
[NUM_DIRECTIONS
] = {
312 {0, 0}, {0, -1}, {-1, 0}, {-1, -1}
314 int overlap
= G_MAXINT
;
316 for (i
= 0; i
< NUM_DIRECTIONS
; ++i
) {
318 .x
= grid_point
->x
+ (req_size
->width
* directions
[i
].width
),
319 .y
= grid_point
->y
+ (req_size
->height
* directions
[i
].height
)
322 RECT_SET(r
, pt
.x
, pt
.y
, req_size
->width
, req_size
->height
);
323 if (!RECT_CONTAINS_RECT(*monitor
, r
))
325 int this_overlap
= total_overlap(client_rects
, n_client_rects
, &r
);
326 if (this_overlap
< overlap
) {
327 overlap
= this_overlap
;