Allow moving fullscreen windows between monitors
[openbox.git] / openbox / place_overlap.c
blob2857b8949aba2831036525767218fd37a5ee16f1
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.
19 #include "config.h"
20 #include "geom.h"
21 #include "place_overlap.h"
22 #include "obt/bsearch.h"
24 #include <glib.h>
25 #include <stdlib.h>
27 static void make_grid(const Rect* client_rects,
28 int n_client_rects,
29 const Rect* monitor,
30 int* x_edges,
31 int* y_edges,
32 int max_edges);
34 static int best_direction(const Point* grid_point,
35 const Rect* client_rects,
36 int n_client_rects,
37 const Rect* monitor,
38 const Size* req_size,
39 Point* best_top_left);
41 static int total_overlap(const Rect* client_rects,
42 int n_client_rects,
43 const Rect* proposed_rect);
45 static void center_in_field(Point* grid_point,
46 const Size* req_size,
47 const Rect *monitor,
48 const Rect* client_rects,
49 int n_client_rects,
50 const int* x_edges,
51 const int* y_edges,
52 int max_edges);
54 /* Choose the placement on a grid with least overlap */
56 void place_overlap_find_least_placement(const Rect* client_rects,
57 int n_client_rects,
58 const Rect *monitor,
59 const Size* req_size,
60 Point* result)
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);
70 int i;
71 for (i = 0; i < max_edges; ++i) {
72 if (x_edges[i] == G_MAXINT)
73 break;
74 int j;
75 for (j = 0; j < max_edges; ++j) {
76 if (y_edges[j] == G_MAXINT)
77 break;
78 Point grid_point = {.x = x_edges[i], .y = y_edges[j]};
79 Point best_top_left;
80 int this_overlap =
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;
87 if (overlap == 0)
88 break;
90 if (overlap == 0)
91 break;
93 if (config_place_center && overlap == 0) {
94 center_in_field(result,
95 req_size,
96 monitor,
97 client_rects,
98 n_client_rects,
99 x_edges,
100 y_edges,
101 max_edges);
105 static int compare_ints(const void* a,
106 const void* b)
108 const int* ia = (const int*)a;
109 const int* ib = (const int*)b;
110 return *ia - *ib;
113 static void uniquify(int* edges,
114 int n_edges)
116 int i = 0;
117 int j = 0;
119 while (j < n_edges) {
120 int last = edges[j++];
121 edges[i++] = last;
122 while (j < n_edges && edges[j] == last)
123 ++j;
125 /* fill the rest with nonsense */
126 for (; i < n_edges; ++i)
127 edges[i] = G_MAXINT;
130 static void make_grid(const Rect* client_rects,
131 int n_client_rects,
132 const Rect* monitor,
133 int* x_edges,
134 int* y_edges,
135 int max_edges)
137 int i;
138 int n_edges = 0;
139 for (i = 0; i < n_client_rects; ++i) {
140 if (!RECT_INTERSECTS_RECT(client_rects[i], *monitor))
141 continue;
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,
160 int n_client_rects,
161 const Rect* proposed_rect)
163 int overlap = 0;
164 int i;
165 for (i = 0; i < n_client_rects; ++i) {
166 if (!RECT_INTERSECTS_RECT(*proposed_rect, client_rects[i]))
167 continue;
168 Rect rtemp;
169 RECT_SET_INTERSECTION(rtemp, *proposed_rect, client_rects[i]);
170 /* somewhat penalize #rects, overlapping an additional client is
171 * only better if it saves ~75x75 pixels. This is so that we don't
172 * overlap a bunch of windows just because there's a small gap
173 * between them. */
174 overlap += RECT_AREA(rtemp) + 6000;
176 return overlap;
179 static int find_first_grid_position_greater_or_equal(int search_value,
180 const int* edges,
181 int max_edges)
183 g_assert(max_edges >= 2);
184 g_assert(search_value >= edges[0]);
185 g_assert(search_value <= edges[max_edges - 1]);
187 BSEARCH_SETUP();
188 BSEARCH(int, edges, 0, max_edges, search_value);
190 if (BSEARCH_FOUND())
191 return BSEARCH_AT();
193 g_assert(BSEARCH_FOUND_NEAREST_SMALLER());
194 /* Get the nearest larger instead. */
195 return BSEARCH_AT() + 1;
198 static void expand_width(Rect* r, int by)
200 r->width += by;
203 static void expand_height(Rect* r, int by)
205 r->height += by;
208 typedef void ((*ExpandByMethod)(Rect*, int));
210 /* This structure packs most of the parametars for expand_field() in
211 order to save pushing the same parameters twice. */
212 typedef struct _ExpandInfo {
213 const Point* top_left;
214 int orig_width;
215 int orig_height;
216 const Rect* monitor;
217 const Rect* client_rects;
218 int n_client_rects;
219 int max_edges;
220 } ExpandInfo;
222 static int expand_field(int orig_edge_index,
223 const int* edges,
224 ExpandByMethod expand_by,
225 const ExpandInfo* i)
227 Rect field;
228 RECT_SET(field,
229 i->top_left->x,
230 i->top_left->y,
231 i->orig_width,
232 i->orig_height);
233 int edge_index = orig_edge_index;
234 while (edge_index < i->max_edges - 1) {
235 int next_edge_index = edge_index + 1;
236 (*expand_by)(&field, edges[next_edge_index] - edges[edge_index]);
237 int overlap = total_overlap(i->client_rects, i->n_client_rects, &field);
238 if (overlap != 0 || !RECT_CONTAINS_RECT(*(i->monitor), field))
239 break;
240 edge_index = next_edge_index;
242 return edge_index;
245 /* The algortihm used for centering a rectangle in a grid field: First
246 find the smallest rectangle of grid lines that enclose the given
247 rectangle. By definition, there is no overlap with any of the other
248 windows if the given rectangle is centered within this minimal
249 rectangle. Then, try extending the minimal rectangle in either
250 direction (x and y) by picking successively further grid lines for
251 the opposite edge. If the minimal rectangle can be extended in *one*
252 direction (x or y) but *not* the other, extend it as far as possible.
253 Otherwise, just use the minimal one. */
255 static void center_in_field(Point* top_left,
256 const Size* req_size,
257 const Rect *monitor,
258 const Rect* client_rects,
259 int n_client_rects,
260 const int* x_edges,
261 const int* y_edges,
262 int max_edges)
264 /* Find minimal rectangle. */
265 int orig_right_edge_index =
266 find_first_grid_position_greater_or_equal(
267 top_left->x + req_size->width, x_edges, max_edges);
268 int orig_bottom_edge_index =
269 find_first_grid_position_greater_or_equal(
270 top_left->y + req_size->height, y_edges, max_edges);
271 ExpandInfo i = {
272 .top_left = top_left,
273 .orig_width = x_edges[orig_right_edge_index] - top_left->x,
274 .orig_height = y_edges[orig_bottom_edge_index] - top_left->y,
275 .monitor = monitor,
276 .client_rects = client_rects,
277 .n_client_rects = n_client_rects,
278 .max_edges = max_edges};
279 /* Try extending width. */
280 int right_edge_index =
281 expand_field(orig_right_edge_index, x_edges, expand_width, &i);
282 /* Try extending height. */
283 int bottom_edge_index =
284 expand_field(orig_bottom_edge_index, y_edges, expand_height, &i);
286 int final_width = x_edges[orig_right_edge_index] - top_left->x;
287 int final_height = y_edges[orig_bottom_edge_index] - top_left->y;
288 if (right_edge_index == orig_right_edge_index &&
289 bottom_edge_index != orig_bottom_edge_index)
290 final_height = y_edges[bottom_edge_index] - top_left->y;
291 else if (right_edge_index != orig_right_edge_index &&
292 bottom_edge_index == orig_bottom_edge_index)
293 final_width = x_edges[right_edge_index] - top_left->x;
295 /* Now center the given rectangle within the field */
296 top_left->x += (final_width - req_size->width) / 2;
297 top_left->y += (final_height - req_size->height) / 2;
300 /* Given a list of Rect RECTS, a Point PT and a Size size, determine the
301 direction from PT which results in the least total overlap with RECTS
302 if a rectangle is placed in that direction. Return the top/left
303 Point of such rectangle and the resulting overlap amount. Only
304 consider placements within BOUNDS. */
306 #define NUM_DIRECTIONS 4
308 static int best_direction(const Point* grid_point,
309 const Rect* client_rects,
310 int n_client_rects,
311 const Rect* monitor,
312 const Size* req_size,
313 Point* best_top_left)
315 static const Size directions[NUM_DIRECTIONS] = {
316 {0, 0}, {0, -1}, {-1, 0}, {-1, -1}
318 int overlap = G_MAXINT;
319 int i;
320 for (i = 0; i < NUM_DIRECTIONS; ++i) {
321 Point pt = {
322 .x = grid_point->x + (req_size->width * directions[i].width),
323 .y = grid_point->y + (req_size->height * directions[i].height)
325 Rect r;
326 RECT_SET(r, pt.x, pt.y, req_size->width, req_size->height);
327 if (!RECT_CONTAINS_RECT(*monitor, r))
328 continue;
329 int this_overlap = total_overlap(client_rects, n_client_rects, &r);
330 if (this_overlap < overlap) {
331 overlap = this_overlap;
332 *best_top_left = pt;
334 if (overlap == 0)
335 break;
337 return overlap;