]> Dogcows Code - chaz/openbox/blob - openbox/place.c
Merge branch 'master' into chaz
[chaz/openbox] / openbox / place.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 place.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "client.h"
21 #include "group.h"
22 #include "screen.h"
23 #include "frame.h"
24 #include "focus.h"
25 #include "config.h"
26 #include "dock.h"
27 #include "debug.h"
28 #include "place_overlap.h"
29
30 static Rect *choose_pointer_monitor(ObClient *c)
31 {
32 return screen_area(c->desktop, screen_monitor_pointer(), NULL);
33 }
34
35 /* use the following priority lists for choose_monitor()
36
37 When a window is being placed in the FOREGROUND, use a monitor chosen in
38 the following order:
39 1. per-app settings
40 2. same monitor as parent
41 3. primary monitor if placement=PRIMARY
42 active monitor if placement=ACTIVE
43 pointer monitor if placement=MOUSE
44 4. primary monitor
45 5. other monitors where the window has group members on the same desktop
46 6. other monitors where the window has group members on other desktops
47 7. other monitors
48
49 When a window is being placed in the BACKGROUND, use a monitor chosen in the
50 following order:
51 1. per-app settings
52 2. same monitor as parent
53 3. other monitors where the window has group members on the same desktop
54 3a. primary monitor in this set
55 3b. other monitors in this set
56 4. other monitors where the window has group members on other desktops
57 4a. primary monitor in this set
58 4b. other monitors in this set
59 5. other monitors
60 5a. primary monitor in this set
61 5b. other monitors in this set
62 */
63
64 /*! One for each possible head, used to sort them in order of precedence. */
65 typedef struct {
66 guint monitor;
67 guint flags;
68 } ObPlaceHead;
69
70 /*! Flags for ObPlaceHead */
71 enum {
72 HEAD_PARENT = 1 << 0, /* parent's monitor */
73 HEAD_PLACED = 1 << 1, /* chosen monitor by placement */
74 HEAD_PRIMARY = 1 << 2, /* primary monitor */
75 HEAD_GROUP_DESK = 1 << 3, /* has a group member on the same desktop */
76 HEAD_GROUP = 1 << 4, /* has a group member on another desktop */
77 HEAD_PERAPP = 1 << 5, /* chosen by per-app settings */
78 };
79
80 gint cmp_foreground(const void *a, const void *b)
81 {
82 const ObPlaceHead *h1 = a;
83 const ObPlaceHead *h2 = b;
84 gint i = 0;
85
86 if (h1->monitor == h2->monitor) return 0;
87
88 if (h1->flags & HEAD_PERAPP) --i;
89 if (h2->flags & HEAD_PERAPP) ++i;
90 if (i) return i;
91
92 if (h1->flags & HEAD_PARENT) --i;
93 if (h2->flags & HEAD_PARENT) ++i;
94 if (i) return i;
95
96 if (h1->flags & HEAD_PLACED) --i;
97 if (h2->flags & HEAD_PLACED) ++i;
98 if (i) return i;
99
100 if (h1->flags & HEAD_PRIMARY) --i;
101 if (h2->flags & HEAD_PRIMARY) ++i;
102 if (i) return i;
103
104 if (h1->flags & HEAD_GROUP_DESK) --i;
105 if (h2->flags & HEAD_GROUP_DESK) ++i;
106 if (i) return i;
107
108 if (h1->flags & HEAD_GROUP) --i;
109 if (h2->flags & HEAD_GROUP) ++i;
110 if (i) return i;
111
112 return h1->monitor - h2->monitor;
113 }
114
115 gint cmp_background(const void *a, const void *b)
116 {
117 const ObPlaceHead *h1 = a;
118 const ObPlaceHead *h2 = b;
119 gint i = 0;
120
121 if (h1->monitor == h2->monitor) return 0;
122
123 if (h1->flags & HEAD_PERAPP) --i;
124 if (h2->flags & HEAD_PERAPP) ++i;
125 if (i) return i;
126
127 if (h1->flags & HEAD_PARENT) --i;
128 if (h2->flags & HEAD_PARENT) ++i;
129 if (i) return i;
130
131 if (h1->flags & HEAD_GROUP_DESK || h2->flags & HEAD_GROUP_DESK) {
132 if (h1->flags & HEAD_GROUP_DESK) --i;
133 if (h2->flags & HEAD_GROUP_DESK) ++i;
134 if (i) return i;
135 if (h1->flags & HEAD_PRIMARY) --i;
136 if (h2->flags & HEAD_PRIMARY) ++i;
137 if (i) return i;
138 }
139
140 if (h1->flags & HEAD_GROUP || h2->flags & HEAD_GROUP) {
141 if (h1->flags & HEAD_GROUP) --i;
142 if (h2->flags & HEAD_GROUP) ++i;
143 if (i) return i;
144 if (h1->flags & HEAD_PRIMARY) --i;
145 if (h2->flags & HEAD_PRIMARY) ++i;
146 if (i) return i;
147 }
148
149 if (h1->flags & HEAD_PRIMARY) --i;
150 if (h2->flags & HEAD_PRIMARY) ++i;
151 if (i) return i;
152
153 return h1->monitor - h2->monitor;
154 }
155
156 /*! Pick a monitor to place a window on. */
157 static Rect* choose_monitor(ObClient *c, gboolean client_to_be_foregrounded,
158 ObAppSettings *settings)
159 {
160 Rect *area;
161 ObPlaceHead *choice;
162 guint i;
163 ObClient *p;
164 GSList *it;
165
166 choice = g_new(ObPlaceHead, screen_num_monitors);
167 for (i = 0; i < screen_num_monitors; ++i) {
168 choice[i].monitor = i;
169 choice[i].flags = 0;
170 }
171
172 /* find monitors with group members */
173 if (c->group) {
174 for (it = c->group->members; it; it = g_slist_next(it)) {
175 ObClient *itc = it->data;
176 if (itc != c) {
177 guint m = client_monitor(itc);
178
179 if (m < screen_num_monitors) {
180 if (screen_compare_desktops(itc->desktop, c->desktop))
181 choice[m].flags |= HEAD_GROUP_DESK;
182 else
183 choice[m].flags |= HEAD_GROUP;
184 }
185 }
186 }
187 }
188
189 i = screen_monitor_primary(FALSE);
190 if (i < screen_num_monitors) {
191 choice[i].flags |= HEAD_PRIMARY;
192 if (config_place_monitor == OB_PLACE_MONITOR_PRIMARY)
193 choice[i].flags |= HEAD_PLACED;
194 if (settings &&
195 settings->monitor_type == OB_PLACE_MONITOR_PRIMARY)
196 choice[i].flags |= HEAD_PERAPP;
197 }
198
199 i = screen_monitor_active();
200 if (i < screen_num_monitors) {
201 if (config_place_monitor == OB_PLACE_MONITOR_ACTIVE)
202 choice[i].flags |= HEAD_PLACED;
203 if (settings &&
204 settings->monitor_type == OB_PLACE_MONITOR_ACTIVE)
205 choice[i].flags |= HEAD_PERAPP;
206 }
207
208 i = screen_monitor_pointer();
209 if (i < screen_num_monitors) {
210 if (config_place_monitor == OB_PLACE_MONITOR_MOUSE)
211 choice[i].flags |= HEAD_PLACED;
212 if (settings &&
213 settings->monitor_type == OB_PLACE_MONITOR_MOUSE)
214 choice[i].flags |= HEAD_PERAPP;
215 }
216
217 if (settings) {
218 i = settings->monitor - 1;
219 if (i < screen_num_monitors)
220 choice[i].flags |= HEAD_PERAPP;
221 }
222
223 /* direct parent takes highest precedence */
224 if ((p = client_direct_parent(c))) {
225 i = client_monitor(p);
226 if (i < screen_num_monitors)
227 choice[i].flags |= HEAD_PARENT;
228 }
229
230 qsort(choice, screen_num_monitors, sizeof(ObPlaceHead),
231 client_to_be_foregrounded ? cmp_foreground : cmp_background);
232
233 /* save the areas of the monitors in order of their being chosen */
234 for (i = 0; i < screen_num_monitors; ++i)
235 {
236 ob_debug("placement choice %d is monitor %d", i, choice[i].monitor);
237 if (choice[i].flags & HEAD_PARENT)
238 ob_debug(" - parent on monitor");
239 if (choice[i].flags & HEAD_PLACED)
240 ob_debug(" - placement choice");
241 if (choice[i].flags & HEAD_PRIMARY)
242 ob_debug(" - primary monitor");
243 if (choice[i].flags & HEAD_GROUP_DESK)
244 ob_debug(" - group on same desktop");
245 if (choice[i].flags & HEAD_GROUP)
246 ob_debug(" - group on other desktop");
247 }
248
249 area = screen_area(c->desktop, choice[0].monitor, NULL);
250
251 g_free(choice);
252
253 /* return the area for the chosen monitor */
254 return area;
255 }
256
257 static gboolean place_under_mouse(ObClient *client, gint *x, gint *y,
258 Size frame_size)
259 {
260 if (config_place_policy != OB_PLACE_POLICY_MOUSE)
261 return FALSE;
262
263 gint l, r, t, b;
264 gint px, py;
265 Rect *area;
266
267 ob_debug("placing under mouse");
268
269 if (!screen_pointer_pos(&px, &py))
270 return FALSE;
271 area = choose_pointer_monitor(client);
272
273 l = area->x;
274 t = area->y;
275 r = area->x + area->width - frame_size.width;
276 b = area->y + area->height - frame_size.height;
277
278 *x = px - frame_size.width / 2;
279 *x = MIN(MAX(*x, l), r);
280 *y = py - frame_size.height / 2;
281 *y = MIN(MAX(*y, t), b);
282
283 g_slice_free(Rect, area);
284
285 return TRUE;
286 }
287
288 static gboolean place_per_app_setting_position(ObClient *client, Rect *screen,
289 gint *x, gint *y,
290 ObAppSettings *settings,
291 Size frame_size)
292 {
293 if (!settings || !settings->pos_given)
294 return FALSE;
295
296 ob_debug("placing by per-app settings");
297
298 if (settings->position.x.center)
299 *x = screen->x + screen->width / 2 - client->area.width / 2;
300 else if (settings->position.x.opposite)
301 *x = screen->x + screen->width - frame_size.width -
302 settings->position.x.pos;
303 else
304 *x = screen->x + settings->position.x.pos;
305 if (settings->position.x.denom)
306 *x = (*x * screen->width) / settings->position.x.denom;
307
308 if (settings->position.y.center)
309 *y = screen->y + screen->height / 2 - client->area.height / 2;
310 else if (settings->position.y.opposite)
311 *y = screen->y + screen->height - frame_size.height -
312 settings->position.y.pos;
313 else
314 *y = screen->y + settings->position.y.pos;
315 if (settings->position.y.denom)
316 *y = (*y * screen->height) / settings->position.y.denom;
317
318 return TRUE;
319 }
320
321 static void place_per_app_setting_size(ObClient *client, Rect *screen,
322 gint *w, gint *h,
323 ObAppSettings *settings)
324 {
325 if (!settings)
326 return;
327
328 g_assert(settings->width_num >= 0);
329 g_assert(settings->width_denom >= 0);
330 g_assert(settings->height_num >= 0);
331 g_assert(settings->height_denom >= 0);
332
333 if (settings->width_num) {
334 ob_debug("setting width by per-app settings");
335 if (!settings->width_denom)
336 *w = settings->width_num;
337 else {
338 *w = screen->width * settings->width_num / settings->width_denom;
339 *w = MIN(*w, screen->width);
340 }
341 }
342
343 if (settings->height_num) {
344 ob_debug("setting height by per-app settings");
345 if (!settings->height_denom)
346 *h = settings->height_num;
347 else {
348 *h = screen->height * settings->height_num / settings->height_denom;
349 *h = MIN(*h, screen->height);
350 }
351 }
352 }
353
354 static gboolean place_transient_splash(ObClient *client, Rect *area,
355 gint *x, gint *y, Size frame_size)
356 {
357 if (client->type == OB_CLIENT_TYPE_DIALOG) {
358 GSList *it;
359 gboolean first = TRUE;
360 gint l, r, t, b;
361
362 ob_debug("placing dialog");
363
364 for (it = client->parents; it; it = g_slist_next(it)) {
365 ObClient *m = it->data;
366 if (!m->iconic) {
367 if (first) {
368 l = RECT_LEFT(m->frame->area);
369 t = RECT_TOP(m->frame->area);
370 r = RECT_RIGHT(m->frame->area);
371 b = RECT_BOTTOM(m->frame->area);
372 first = FALSE;
373 } else {
374 l = MIN(l, RECT_LEFT(m->frame->area));
375 t = MIN(t, RECT_TOP(m->frame->area));
376 r = MAX(r, RECT_RIGHT(m->frame->area));
377 b = MAX(b, RECT_BOTTOM(m->frame->area));
378 }
379 }
380 if (!first) {
381 *x = ((r + 1 - l) - frame_size.width) / 2 + l;
382 *y = ((b + 1 - t) - frame_size.height) / 2 + t;
383 return TRUE;
384 }
385 }
386 }
387
388 if (client->type == OB_CLIENT_TYPE_DIALOG ||
389 client->type == OB_CLIENT_TYPE_SPLASH)
390 {
391 ob_debug("placing dialog or splash");
392
393 *x = (area->width - frame_size.width) / 2 + area->x;
394 *y = (area->height - frame_size.height) / 2 + area->y;
395 return TRUE;
396 }
397
398 return FALSE;
399 }
400
401 static gboolean place_least_overlap(ObClient *c, Rect *head, int *x, int *y,
402 Size frame_size)
403 {
404 /* Assemble the list of windows that could overlap with @c in the user's
405 current view. */
406 GSList* potential_overlap_clients = NULL;
407 gint n_client_rects = config_dock_hide ? 0 : 1;
408
409 /* if we're "showing desktop", ignore all existing windows */
410 if (!screen_showing_desktop) {
411 GList* it;
412 for (it = client_list; it != NULL; it = g_list_next(it)) {
413 ObClient* maybe_client = (ObClient*)it->data;
414 if (maybe_client == c)
415 continue;
416 if (maybe_client->iconic)
417 continue;
418 if (!client_occupies_space(maybe_client))
419 continue;
420 if (c->desktop != DESKTOP_ALL) {
421 if (maybe_client->desktop != c->desktop &&
422 maybe_client->desktop != DESKTOP_ALL)
423 continue;
424 } else {
425 if (maybe_client->desktop != screen_desktop &&
426 maybe_client->desktop != DESKTOP_ALL)
427 continue;
428 }
429
430 potential_overlap_clients = g_slist_prepend(
431 potential_overlap_clients, maybe_client);
432 n_client_rects += 1;
433 }
434 }
435 Rect client_rects[n_client_rects];
436
437 GSList* it;
438 guint i = 0;
439 if (!config_dock_hide)
440 dock_get_area(&client_rects[i++]);
441 for (it = potential_overlap_clients; it != NULL; it = g_slist_next(it)) {
442 ObClient* potential_overlap_client = (ObClient*)it->data;
443 client_rects[i] = potential_overlap_client->frame->area;
444 i += 1;
445 }
446 g_slist_free(potential_overlap_clients);
447
448 Point result;
449 place_overlap_find_least_placement(client_rects, n_client_rects, head,
450 &frame_size, &result);
451 *x = result.x;
452 *y = result.y;
453
454 return TRUE;
455 }
456
457 static gboolean should_set_client_position(ObClient *client,
458 ObAppSettings *settings)
459 {
460 gboolean has_position = settings && settings->pos_given;
461 gboolean has_forced_position = has_position && settings->pos_force;
462
463 gboolean user_positioned = client->positioned & USPosition;
464 if (user_positioned && !has_forced_position)
465 return FALSE;
466
467 gboolean program_positioned = client->positioned & PPosition;
468 if (program_positioned && !has_position)
469 return FALSE;
470
471 return TRUE;
472 }
473
474 gboolean place_client(ObClient *client, gboolean client_to_be_foregrounded,
475 Rect* client_area, ObAppSettings *settings)
476 {
477 gboolean ret;
478 Rect *monitor_area;
479 int *x, *y, *w, *h;
480 Size frame_size;
481
482 monitor_area = choose_monitor(client, client_to_be_foregrounded, settings);
483
484 w = &client_area->width;
485 h = &client_area->height;
486 place_per_app_setting_size(client, monitor_area, w, h, settings);
487
488 if (!should_set_client_position(client, settings))
489 return FALSE;
490
491 x = &client_area->x;
492 y = &client_area->y;
493
494 SIZE_SET(frame_size,
495 *w + client->frame->size.left + client->frame->size.right,
496 *h + client->frame->size.top + client->frame->size.bottom);
497
498 ret =
499 place_per_app_setting_position(client, monitor_area, x, y, settings,
500 frame_size) ||
501 place_transient_splash(client, monitor_area, x, y, frame_size) ||
502 place_under_mouse(client, x, y, frame_size) ||
503 place_least_overlap(client, monitor_area, x, y, frame_size);
504 g_assert(ret);
505
506 g_slice_free(Rect, monitor_area);
507
508 /* get where the client should be */
509 frame_frame_gravity(client->frame, x, y);
510 return TRUE;
511 }
This page took 0.057008 seconds and 4 git commands to generate.