~ubuntu-branches/ubuntu/vivid/marco/vivid

« back to all changes in this revision

Viewing changes to src/core/place.c

  • Committer: Package Import Robot
  • Author(s): Mike Gabriel
  • Date: 2014-01-21 19:52:39 UTC
  • Revision ID: package-import@ubuntu.com-20140121195239-c4k1v8umdw1u4n29
Tags: upstream-1.6.2
ImportĀ upstreamĀ versionĀ 1.6.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 
2
 
 
3
/* Marco window placement */
 
4
 
 
5
/* 
 
6
 * Copyright (C) 2001 Havoc Pennington
 
7
 * Copyright (C) 2002, 2003 Red Hat, Inc.
 
8
 * Copyright (C) 2003 Rob Adams
 
9
 * Copyright (C) 2005 Elijah Newren
 
10
 * 
 
11
 * This program is free software; you can redistribute it and/or
 
12
 * modify it under the terms of the GNU General Public License as
 
13
 * published by the Free Software Foundation; either version 2 of the
 
14
 * License, or (at your option) any later version.
 
15
 *
 
16
 * This program is distributed in the hope that it will be useful, but
 
17
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
19
 * General Public License for more details.
 
20
 * 
 
21
 * You should have received a copy of the GNU General Public License
 
22
 * along with this program; if not, write to the Free Software
 
23
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 
24
 * 02110-1301, USA.
 
25
 */
 
26
 
 
27
#include <config.h>
 
28
 
 
29
#include "place.h"
 
30
#include "workspace.h"
 
31
#include "prefs.h"
 
32
#include <gdk/gdk.h>
 
33
#include <math.h>
 
34
#include <stdlib.h>
 
35
 
 
36
typedef enum
 
37
{
 
38
  META_LEFT,
 
39
  META_RIGHT,
 
40
  META_TOP,
 
41
  META_BOTTOM
 
42
} MetaWindowDirection;
 
43
 
 
44
static gint
 
45
northwestcmp (gconstpointer a, gconstpointer b)
 
46
{
 
47
  MetaWindow *aw = (gpointer) a;
 
48
  MetaWindow *bw = (gpointer) b;
 
49
  int from_origin_a;
 
50
  int from_origin_b;
 
51
  int ax, ay, bx, by;
 
52
 
 
53
  /* we're interested in the frame position for cascading,
 
54
   * not meta_window_get_position()
 
55
   */
 
56
  if (aw->frame)
 
57
    {
 
58
      ax = aw->frame->rect.x;
 
59
      ay = aw->frame->rect.y;
 
60
    }
 
61
  else
 
62
    {
 
63
      ax = aw->rect.x;
 
64
      ay = aw->rect.y;
 
65
    }
 
66
 
 
67
  if (bw->frame)
 
68
    {
 
69
      bx = bw->frame->rect.x;
 
70
      by = bw->frame->rect.y;
 
71
    }
 
72
  else
 
73
    {
 
74
      bx = bw->rect.x;
 
75
      by = bw->rect.y;
 
76
    }
 
77
  
 
78
  /* probably there's a fast good-enough-guess we could use here. */
 
79
  from_origin_a = sqrt (ax * ax + ay * ay);
 
80
  from_origin_b = sqrt (bx * bx + by * by);
 
81
    
 
82
  if (from_origin_a < from_origin_b)
 
83
    return -1;
 
84
  else if (from_origin_a > from_origin_b)
 
85
    return 1;
 
86
  else
 
87
    return 0;
 
88
}
 
89
 
 
90
static void
 
91
find_next_cascade (MetaWindow *window,
 
92
                   MetaFrameGeometry *fgeom,
 
93
                   /* visible windows on relevant workspaces */
 
94
                   GList      *windows,
 
95
                   int         x,
 
96
                   int         y,
 
97
                   int        *new_x,
 
98
                   int        *new_y)
 
99
{
 
100
  GList *tmp;
 
101
  GList *sorted;
 
102
  int cascade_x, cascade_y;
 
103
  int x_threshold, y_threshold;
 
104
  int window_width, window_height;
 
105
  int cascade_stage;
 
106
  MetaRectangle work_area;
 
107
  const MetaXineramaScreenInfo* current;
 
108
  
 
109
  sorted = g_list_copy (windows);
 
110
  sorted = g_list_sort (sorted, northwestcmp);
 
111
 
 
112
  /* This is a "fuzzy" cascade algorithm. 
 
113
   * For each window in the list, we find where we'd cascade a
 
114
   * new window after it. If a window is already nearly at that
 
115
   * position, we move on.
 
116
   */
 
117
  
 
118
  /* arbitrary-ish threshold, honors user attempts to
 
119
   * manually cascade.
 
120
   */
 
121
#define CASCADE_FUZZ 15
 
122
  if (fgeom)
 
123
    {
 
124
      x_threshold = MAX (fgeom->left_width, CASCADE_FUZZ);
 
125
      y_threshold = MAX (fgeom->top_height, CASCADE_FUZZ);
 
126
    }
 
127
  else
 
128
    {
 
129
      x_threshold = CASCADE_FUZZ;
 
130
      y_threshold = CASCADE_FUZZ;
 
131
    }
 
132
  
 
133
  /* Find furthest-SE origin of all workspaces.
 
134
   * cascade_x, cascade_y are the target position
 
135
   * of NW corner of window frame.
 
136
   */
 
137
 
 
138
  current = meta_screen_get_current_xinerama (window->screen);
 
139
  meta_window_get_work_area_for_xinerama (window, current->number, &work_area);
 
140
 
 
141
  cascade_x = MAX (0, work_area.x);
 
142
  cascade_y = MAX (0, work_area.y);
 
143
  
 
144
  /* Find first cascade position that's not used. */
 
145
  
 
146
  window_width = window->frame ? window->frame->rect.width : window->rect.width;
 
147
  window_height = window->frame ? window->frame->rect.height : window->rect.height;
 
148
  
 
149
  cascade_stage = 0;
 
150
  tmp = sorted;
 
151
  while (tmp != NULL)
 
152
    {
 
153
      MetaWindow *w;
 
154
      int wx, wy;
 
155
      
 
156
      w = tmp->data;
 
157
 
 
158
      /* we want frame position, not window position */
 
159
      if (w->frame)
 
160
        {
 
161
          wx = w->frame->rect.x;
 
162
          wy = w->frame->rect.y;
 
163
        }
 
164
      else
 
165
        {
 
166
          wx = w->rect.x;
 
167
          wy = w->rect.y;
 
168
        }
 
169
      
 
170
      if (ABS (wx - cascade_x) < x_threshold &&
 
171
          ABS (wy - cascade_y) < y_threshold)
 
172
        {
 
173
          /* This window is "in the way", move to next cascade
 
174
           * point. The new window frame should go at the origin
 
175
           * of the client window we're stacking above.
 
176
           */
 
177
          meta_window_get_position (w, &wx, &wy);
 
178
          cascade_x = wx;
 
179
          cascade_y = wy;
 
180
          
 
181
          /* If we go off the screen, start over with a new cascade */
 
182
          if (((cascade_x + window_width) >
 
183
               (work_area.x + work_area.width)) ||
 
184
              ((cascade_y + window_height) >
 
185
               (work_area.y + work_area.height)))
 
186
            {
 
187
              cascade_x = MAX (0, work_area.x);
 
188
              cascade_y = MAX (0, work_area.y);
 
189
              
 
190
#define CASCADE_INTERVAL 50 /* space between top-left corners of cascades */
 
191
              cascade_stage += 1;
 
192
              cascade_x += CASCADE_INTERVAL * cascade_stage;
 
193
              
 
194
              /* start over with a new cascade translated to the right, unless
 
195
               * we are out of space
 
196
               */
 
197
              if ((cascade_x + window_width) <
 
198
                  (work_area.x + work_area.width))
 
199
                {
 
200
                  tmp = sorted;
 
201
                  continue;
 
202
                }
 
203
              else
 
204
                {
 
205
                  /* All out of space, this cascade_x won't work */
 
206
                  cascade_x = MAX (0, work_area.x);
 
207
                  break;
 
208
                }
 
209
            }
 
210
        }
 
211
      else
 
212
        {
 
213
          /* Keep searching for a further-down-the-diagonal window. */
 
214
        }
 
215
        
 
216
      tmp = tmp->next;
 
217
    }
 
218
 
 
219
  /* cascade_x and cascade_y will match the last window in the list
 
220
   * that was "in the way" (in the approximate cascade diagonal)
 
221
   */
 
222
  
 
223
  g_list_free (sorted);
 
224
 
 
225
  /* Convert coords to position of window, not position of frame. */
 
226
  if (fgeom == NULL)
 
227
    {
 
228
      *new_x = cascade_x;
 
229
      *new_y = cascade_y;
 
230
    }
 
231
  else
 
232
    {
 
233
      *new_x = cascade_x + fgeom->left_width;
 
234
      *new_y = cascade_y + fgeom->top_height;
 
235
    }
 
236
}
 
237
 
 
238
static void
 
239
find_most_freespace (MetaWindow *window,
 
240
                     MetaFrameGeometry *fgeom,
 
241
                     /* visible windows on relevant workspaces */
 
242
                     MetaWindow *focus_window,
 
243
                     int         x,
 
244
                     int         y,
 
245
                     int        *new_x,
 
246
                     int        *new_y)
 
247
{
 
248
  MetaWindowDirection side;
 
249
  int max_area;
 
250
  int max_width, max_height, left, right, top, bottom;
 
251
  int left_space, right_space, top_space, bottom_space;
 
252
  int frame_size_left, frame_size_top;
 
253
  MetaRectangle work_area;
 
254
  MetaRectangle avoid;
 
255
  MetaRectangle outer;
 
256
 
 
257
  frame_size_left = fgeom ? fgeom->left_width : 0;
 
258
  frame_size_top  = fgeom ? fgeom->top_height : 0;
 
259
 
 
260
  meta_window_get_work_area_current_xinerama (focus_window, &work_area);
 
261
  meta_window_get_outer_rect (focus_window, &avoid);
 
262
  meta_window_get_outer_rect (window, &outer);
 
263
 
 
264
  /* Find the areas of choosing the various sides of the focus window */
 
265
  max_width  = MIN (avoid.width, outer.width);
 
266
  max_height = MIN (avoid.height, outer.height);
 
267
  left_space   = avoid.x - work_area.x;
 
268
  right_space  = work_area.width - (avoid.x + avoid.width - work_area.x);
 
269
  top_space    = avoid.y - work_area.y;
 
270
  bottom_space = work_area.height - (avoid.y + avoid.height - work_area.y);
 
271
  left   = MIN (left_space,   outer.width);
 
272
  right  = MIN (right_space,  outer.width);
 
273
  top    = MIN (top_space,    outer.height);
 
274
  bottom = MIN (bottom_space, outer.height);
 
275
 
 
276
  /* Find out which side of the focus_window can show the most of the window */
 
277
  side = META_LEFT;
 
278
  max_area = left*max_height;
 
279
  if (right*max_height > max_area)
 
280
    {
 
281
      side = META_RIGHT;
 
282
      max_area = right*max_height;
 
283
    }
 
284
  if (top*max_width > max_area)
 
285
    {
 
286
      side = META_TOP;
 
287
      max_area = top*max_width;
 
288
    }
 
289
  if (bottom*max_width > max_area)
 
290
    {
 
291
      side = META_BOTTOM;
 
292
      max_area = bottom*max_width;
 
293
    }
 
294
 
 
295
  /* Give up if there's no where to put it (i.e. focus window is maximized) */
 
296
  if (max_area == 0)
 
297
    return;
 
298
 
 
299
  /* Place the window on the relevant side; if the whole window fits,
 
300
   * make it adjacent to the focus window; if not, make sure the
 
301
   * window doesn't go off the edge of the screen.
 
302
   */
 
303
  switch (side)
 
304
    {
 
305
    case META_LEFT:
 
306
      *new_y = avoid.y + frame_size_top;
 
307
      if (left_space > outer.width)
 
308
        *new_x = avoid.x - outer.width + frame_size_left;
 
309
      else
 
310
        *new_x = work_area.x + frame_size_left;
 
311
      break;
 
312
    case META_RIGHT:
 
313
      *new_y = avoid.y + frame_size_top;
 
314
      if (right_space > outer.width)
 
315
        *new_x = avoid.x + avoid.width + frame_size_left;
 
316
      else
 
317
        *new_x = work_area.x + work_area.width - outer.width + frame_size_left;
 
318
      break;
 
319
    case META_TOP:
 
320
      *new_x = avoid.x + frame_size_left;
 
321
      if (top_space > outer.height)
 
322
        *new_y = avoid.y - outer.height + frame_size_top;
 
323
      else
 
324
        *new_y = work_area.y + frame_size_top;
 
325
      break;
 
326
    case META_BOTTOM:
 
327
      *new_x = avoid.x + frame_size_left;
 
328
      if (bottom_space > outer.height)
 
329
        *new_y = avoid.y + avoid.height + frame_size_top;
 
330
      else
 
331
        *new_y = work_area.y + work_area.height - outer.height + frame_size_top;
 
332
      break;
 
333
    }
 
334
}
 
335
 
 
336
static void
 
337
avoid_being_obscured_as_second_modal_dialog (MetaWindow *window,
 
338
                                             MetaFrameGeometry *fgeom,
 
339
                                             int        *x,
 
340
                                             int        *y)
 
341
{
 
342
  /* We can't center this dialog if it was denied focus and it
 
343
   * overlaps with the focus window and this dialog is modal and this
 
344
   * dialog is in the same app as the focus window (*phew*...please
 
345
   * don't make me say that ten times fast). See bug 307875 comment 11
 
346
   * and 12 for details, but basically it means this is probably a
 
347
   * second modal dialog for some app while the focus window is the
 
348
   * first modal dialog.  We should probably make them simultaneously
 
349
   * visible in general, but it becomes mandatory to do so due to
 
350
   * buggy apps (e.g. those using gtk+ *sigh*) because in those cases
 
351
   * this second modal dialog also happens to be modal to the first
 
352
   * dialog in addition to the main window, while it has only let us
 
353
   * know about the modal-to-the-main-window part.
 
354
   */
 
355
 
 
356
  MetaWindow *focus_window;
 
357
  MetaRectangle overlap;
 
358
 
 
359
  focus_window = window->display->focus_window;
 
360
 
 
361
  if (window->denied_focus_and_not_transient &&
 
362
      window->wm_state_modal && /* FIXME: Maybe do this for all transients? */
 
363
      meta_window_same_application (window, focus_window) &&
 
364
      meta_rectangle_intersect (&window->rect,
 
365
                                &focus_window->rect,
 
366
                                &overlap))
 
367
    {
 
368
      find_most_freespace (window, fgeom, focus_window, *x, *y, x, y);
 
369
      meta_topic (META_DEBUG_PLACEMENT,
 
370
                  "Dialog window %s was denied focus but may be modal "
 
371
                  "to the focus window; had to move it to avoid the "
 
372
                  "focus window\n",
 
373
                  window->desc);
 
374
    }
 
375
}
 
376
 
 
377
static gboolean
 
378
rectangle_overlaps_some_window (MetaRectangle *rect,
 
379
                                GList         *windows)
 
380
{
 
381
  GList *tmp;
 
382
  MetaRectangle dest;
 
383
  
 
384
  tmp = windows;
 
385
  while (tmp != NULL)
 
386
    {
 
387
      MetaWindow *other = tmp->data;
 
388
      MetaRectangle other_rect;      
 
389
 
 
390
      switch (other->type)
 
391
        {
 
392
        case META_WINDOW_DOCK:
 
393
        case META_WINDOW_SPLASHSCREEN:
 
394
        case META_WINDOW_DESKTOP:
 
395
        case META_WINDOW_DIALOG:
 
396
        case META_WINDOW_MODAL_DIALOG:
 
397
          break;
 
398
 
 
399
        case META_WINDOW_NORMAL:
 
400
        case META_WINDOW_UTILITY:
 
401
        case META_WINDOW_TOOLBAR:
 
402
        case META_WINDOW_MENU:
 
403
          meta_window_get_outer_rect (other, &other_rect);
 
404
          
 
405
          if (meta_rectangle_intersect (rect, &other_rect, &dest))
 
406
            return TRUE;
 
407
          break;
 
408
        }
 
409
      
 
410
      tmp = tmp->next;
 
411
    }
 
412
 
 
413
  return FALSE;
 
414
}
 
415
 
 
416
static gint
 
417
leftmost_cmp (gconstpointer a, gconstpointer b)
 
418
{
 
419
  MetaWindow *aw = (gpointer) a;
 
420
  MetaWindow *bw = (gpointer) b;
 
421
  int ax, bx;
 
422
 
 
423
  /* we're interested in the frame position for cascading,
 
424
   * not meta_window_get_position()
 
425
   */
 
426
  if (aw->frame)
 
427
    ax = aw->frame->rect.x;
 
428
  else
 
429
    ax = aw->rect.x;
 
430
 
 
431
  if (bw->frame)
 
432
    bx = bw->frame->rect.x;
 
433
  else
 
434
    bx = bw->rect.x;
 
435
 
 
436
  if (ax < bx)
 
437
    return -1;
 
438
  else if (ax > bx)
 
439
    return 1;
 
440
  else
 
441
    return 0;
 
442
}
 
443
 
 
444
static gint
 
445
topmost_cmp (gconstpointer a, gconstpointer b)
 
446
{
 
447
  MetaWindow *aw = (gpointer) a;
 
448
  MetaWindow *bw = (gpointer) b;
 
449
  int ay, by;
 
450
 
 
451
  /* we're interested in the frame position for cascading,
 
452
   * not meta_window_get_position()
 
453
   */
 
454
  if (aw->frame)
 
455
    ay = aw->frame->rect.y;
 
456
  else
 
457
    ay = aw->rect.y;
 
458
 
 
459
  if (bw->frame)
 
460
    by = bw->frame->rect.y;
 
461
  else
 
462
    by = bw->rect.y;
 
463
 
 
464
  if (ay < by)
 
465
    return -1;
 
466
  else if (ay > by)
 
467
    return 1;
 
468
  else
 
469
    return 0;
 
470
}
 
471
 
 
472
static void
 
473
center_tile_rect_in_area (MetaRectangle *rect,
 
474
                          MetaRectangle *work_area)
 
475
{
 
476
  int fluff;
 
477
 
 
478
  /* The point here is to tile a window such that "extra"
 
479
   * space is equal on either side (i.e. so a full screen
 
480
   * of windows tiled this way would center the windows
 
481
   * as a group)
 
482
   */
 
483
 
 
484
  fluff = (work_area->width % (rect->width+1)) / 2;
 
485
  rect->x = work_area->x + fluff;
 
486
  fluff = (work_area->height % (rect->height+1)) / 3;
 
487
  rect->y = work_area->y + fluff;
 
488
}
 
489
 
 
490
static void
 
491
center_rect_in_area (MetaRectangle *rect,
 
492
                     MetaRectangle *work_area)
 
493
{
 
494
  rect->x = work_area->x + ((work_area->width - rect->width) / 2);
 
495
  rect->y = work_area->y + ((work_area->height - rect->height) / 2);
 
496
}
 
497
 
 
498
/* Find the leftmost, then topmost, empty area on the workspace
 
499
 * that can contain the new window.
 
500
 *
 
501
 * Cool feature to have: if we can't fit the current window size,
 
502
 * try shrinking the window (within geometry constraints). But
 
503
 * beware windows such as Emacs with no sane minimum size, we
 
504
 * don't want to create a 1x1 Emacs.
 
505
 */
 
506
static gboolean
 
507
find_first_fit (MetaWindow *window,
 
508
                MetaFrameGeometry *fgeom,
 
509
                /* visible windows on relevant workspaces */
 
510
                GList      *windows,
 
511
                int         xinerama,
 
512
                int         x,
 
513
                int         y,
 
514
                int        *new_x,
 
515
                int        *new_y)
 
516
{
 
517
  /* This algorithm is limited - it just brute-force tries
 
518
   * to fit the window in a small number of locations that are aligned
 
519
   * with existing windows. It tries to place the window on
 
520
   * the bottom of each existing window, and then to the right
 
521
   * of each existing window, aligned with the left/top of the
 
522
   * existing window in each of those cases.
 
523
   */  
 
524
  int retval;
 
525
  GList *below_sorted;
 
526
  GList *right_sorted;
 
527
  GList *tmp;
 
528
  MetaRectangle rect;
 
529
  MetaRectangle work_area;
 
530
  
 
531
  retval = FALSE;
 
532
 
 
533
  /* Below each window */
 
534
  below_sorted = g_list_copy (windows);
 
535
  below_sorted = g_list_sort (below_sorted, leftmost_cmp);
 
536
  below_sorted = g_list_sort (below_sorted, topmost_cmp);  
 
537
 
 
538
  /* To the right of each window */
 
539
  right_sorted = g_list_copy (windows);
 
540
  right_sorted = g_list_sort (right_sorted, topmost_cmp);
 
541
  right_sorted = g_list_sort (right_sorted, leftmost_cmp);
 
542
  
 
543
  rect.width = window->rect.width;
 
544
  rect.height = window->rect.height;
 
545
  
 
546
  if (fgeom)
 
547
    {
 
548
      rect.width += fgeom->left_width + fgeom->right_width;
 
549
      rect.height += fgeom->top_height + fgeom->bottom_height;
 
550
    }
 
551
 
 
552
#ifdef WITH_VERBOSE_MODE
 
553
    {
 
554
      char xinerama_location_string[RECT_LENGTH];
 
555
      meta_rectangle_to_string (&window->screen->xinerama_infos[xinerama].rect,
 
556
                                xinerama_location_string);
 
557
      meta_topic (META_DEBUG_XINERAMA,
 
558
                  "Natural xinerama is %s\n",
 
559
                  xinerama_location_string);
 
560
    }
 
561
#endif
 
562
 
 
563
    meta_window_get_work_area_for_xinerama (window, xinerama, &work_area);
 
564
 
 
565
    if (meta_prefs_get_center_new_windows ())
 
566
      center_rect_in_area (&rect, &work_area);
 
567
    else
 
568
      center_tile_rect_in_area (&rect, &work_area);
 
569
 
 
570
    if (meta_rectangle_contains_rect (&work_area, &rect) &&
 
571
        (meta_prefs_get_center_new_windows () ||
 
572
         !rectangle_overlaps_some_window (&rect, windows)))
 
573
      {
 
574
        *new_x = rect.x;
 
575
        *new_y = rect.y;
 
576
        if (fgeom)
 
577
          {
 
578
            *new_x += fgeom->left_width;
 
579
            *new_y += fgeom->top_height;
 
580
          }
 
581
    
 
582
        retval = TRUE;
 
583
       
 
584
        goto out;
 
585
      }
 
586
 
 
587
    /* try below each window */
 
588
    tmp = below_sorted;
 
589
    while (tmp != NULL)
 
590
      {
 
591
        MetaWindow *w = tmp->data;
 
592
        MetaRectangle outer_rect;
 
593
 
 
594
        meta_window_get_outer_rect (w, &outer_rect);
 
595
      
 
596
        rect.x = outer_rect.x;
 
597
        rect.y = outer_rect.y + outer_rect.height;
 
598
      
 
599
        if (meta_rectangle_contains_rect (&work_area, &rect) &&
 
600
            !rectangle_overlaps_some_window (&rect, below_sorted))
 
601
          {
 
602
            *new_x = rect.x;
 
603
            *new_y = rect.y;
 
604
            if (fgeom)
 
605
              {
 
606
                *new_x += fgeom->left_width;
 
607
                *new_y += fgeom->top_height;
 
608
              }
 
609
          
 
610
            retval = TRUE;
 
611
          
 
612
            goto out;
 
613
          }
 
614
 
 
615
        tmp = tmp->next;
 
616
      }
 
617
 
 
618
    /* try to the right of each window */
 
619
    tmp = right_sorted;
 
620
    while (tmp != NULL)
 
621
      {
 
622
        MetaWindow *w = tmp->data;
 
623
        MetaRectangle outer_rect;
 
624
   
 
625
        meta_window_get_outer_rect (w, &outer_rect);
 
626
     
 
627
        rect.x = outer_rect.x + outer_rect.width;
 
628
        rect.y = outer_rect.y;
 
629
   
 
630
        if (meta_rectangle_contains_rect (&work_area, &rect) &&
 
631
            !rectangle_overlaps_some_window (&rect, right_sorted))
 
632
          {
 
633
            *new_x = rect.x;
 
634
            *new_y = rect.y;
 
635
            if (fgeom)
 
636
              {
 
637
                *new_x += fgeom->left_width;
 
638
                *new_y += fgeom->top_height;
 
639
              }
 
640
        
 
641
            retval = TRUE;
 
642
       
 
643
            goto out;
 
644
          }
 
645
 
 
646
        tmp = tmp->next;
 
647
      }
 
648
      
 
649
 out:
 
650
 
 
651
  g_list_free (below_sorted);
 
652
  g_list_free (right_sorted);
 
653
  return retval;
 
654
}
 
655
 
 
656
void
 
657
meta_window_place (MetaWindow        *window,
 
658
                   MetaFrameGeometry *fgeom,
 
659
                   int                x,
 
660
                   int                y,
 
661
                   int               *new_x,
 
662
                   int               *new_y)
 
663
{
 
664
  GList *windows;
 
665
  const MetaXineramaScreenInfo *xi;
 
666
 
 
667
  /* frame member variables should NEVER be used in here, only
 
668
   * MetaFrameGeometry. But remember fgeom == NULL
 
669
   * for undecorated windows. Also, this function should
 
670
   * NEVER have side effects other than computing the
 
671
   * placement coordinates.
 
672
   */
 
673
  
 
674
  meta_topic (META_DEBUG_PLACEMENT, "Placing window %s\n", window->desc);
 
675
 
 
676
  windows = NULL;
 
677
  
 
678
  switch (window->type)
 
679
    {
 
680
      /* Run placement algorithm on these. */
 
681
    case META_WINDOW_NORMAL:
 
682
    case META_WINDOW_DIALOG:
 
683
    case META_WINDOW_MODAL_DIALOG:
 
684
    case META_WINDOW_SPLASHSCREEN:
 
685
      break;
 
686
          
 
687
      /* Assume the app knows best how to place these, no placement
 
688
       * algorithm ever (other than "leave them as-is")
 
689
       */
 
690
    case META_WINDOW_DESKTOP:
 
691
    case META_WINDOW_DOCK:
 
692
    case META_WINDOW_TOOLBAR:
 
693
    case META_WINDOW_MENU:
 
694
    case META_WINDOW_UTILITY:
 
695
      goto done_no_constraints;
 
696
    }
 
697
  
 
698
  if (meta_prefs_get_disable_workarounds ())
 
699
    {
 
700
      switch (window->type)
 
701
        {
 
702
          /* Only accept USPosition on normal windows because the app is full
 
703
           * of shit claiming the user set -geometry for a dialog or dock
 
704
           */
 
705
        case META_WINDOW_NORMAL:
 
706
          if (window->size_hints.flags & USPosition)
 
707
            {
 
708
              /* don't constrain with placement algorithm */
 
709
              meta_topic (META_DEBUG_PLACEMENT,
 
710
                          "Honoring USPosition for %s instead of using placement algorithm\n", window->desc);
 
711
 
 
712
              goto done;
 
713
            }
 
714
          break;
 
715
 
 
716
          /* Ignore even USPosition on dialogs, splashscreen */
 
717
        case META_WINDOW_DIALOG:
 
718
        case META_WINDOW_MODAL_DIALOG:
 
719
        case META_WINDOW_SPLASHSCREEN:
 
720
          break;
 
721
          
 
722
          /* Assume the app knows best how to place these. */
 
723
        case META_WINDOW_DESKTOP:
 
724
        case META_WINDOW_DOCK:
 
725
        case META_WINDOW_TOOLBAR:
 
726
        case META_WINDOW_MENU:
 
727
        case META_WINDOW_UTILITY:
 
728
          if (window->size_hints.flags & PPosition)
 
729
            {
 
730
              meta_topic (META_DEBUG_PLACEMENT,
 
731
                          "Not placing non-normal non-dialog window with PPosition set\n");
 
732
              goto done_no_constraints;
 
733
            }
 
734
          break;
 
735
        }
 
736
    }
 
737
  else
 
738
    {
 
739
      /* workarounds enabled */
 
740
      
 
741
      if ((window->size_hints.flags & PPosition) ||
 
742
          (window->size_hints.flags & USPosition))
 
743
        {
 
744
          meta_topic (META_DEBUG_PLACEMENT,
 
745
                      "Not placing window with PPosition or USPosition set\n");
 
746
          avoid_being_obscured_as_second_modal_dialog (window, fgeom, &x, &y);
 
747
          goto done_no_constraints;
 
748
        }
 
749
    }
 
750
  
 
751
  if ((window->type == META_WINDOW_DIALOG ||
 
752
       window->type == META_WINDOW_MODAL_DIALOG) &&
 
753
      window->xtransient_for != None)
 
754
    {
 
755
      /* Center horizontally, at top of parent vertically */
 
756
 
 
757
      MetaWindow *parent;
 
758
          
 
759
      parent =
 
760
        meta_display_lookup_x_window (window->display,
 
761
                                      window->xtransient_for);
 
762
 
 
763
      if (parent)
 
764
        {
 
765
          int w;
 
766
 
 
767
          meta_window_get_position (parent, &x, &y);
 
768
          w = parent->rect.width;
 
769
 
 
770
          /* center of parent */
 
771
          x = x + w / 2;
 
772
          /* center of child over center of parent */
 
773
          x -= window->rect.width / 2;
 
774
 
 
775
          /* "visually" center window over parent, leaving twice as
 
776
           * much space below as on top.
 
777
           */
 
778
          y += (parent->rect.height - window->rect.height)/3;
 
779
 
 
780
          /* put top of child's frame, not top of child's client */
 
781
          if (fgeom)
 
782
            y += fgeom->top_height;
 
783
 
 
784
          meta_topic (META_DEBUG_PLACEMENT, "Centered window %s over transient parent\n",
 
785
                      window->desc);
 
786
          
 
787
          avoid_being_obscured_as_second_modal_dialog (window, fgeom, &x, &y);
 
788
 
 
789
          goto done;
 
790
        }
 
791
    }
 
792
  
 
793
  /* FIXME UTILITY with transient set should be stacked up
 
794
   * on the sides of the parent window or something.
 
795
   */
 
796
  
 
797
  if (window->type == META_WINDOW_DIALOG ||
 
798
      window->type == META_WINDOW_MODAL_DIALOG ||
 
799
      window->type == META_WINDOW_SPLASHSCREEN)
 
800
    {
 
801
      /* Center on current xinerama (i.e. on current monitor) */
 
802
      int w, h;
 
803
 
 
804
      /* Warning, this function is a round trip! */
 
805
      xi = meta_screen_get_current_xinerama (window->screen);
 
806
 
 
807
      w = xi->rect.width;
 
808
      h = xi->rect.height;
 
809
 
 
810
      x = (w - window->rect.width) / 2;
 
811
      y = (h - window->rect.height) / 2;
 
812
 
 
813
      x += xi->rect.x;
 
814
      y += xi->rect.y;
 
815
      
 
816
      meta_topic (META_DEBUG_PLACEMENT, "Centered window %s on screen %d xinerama %d\n",
 
817
                  window->desc, window->screen->number, xi->number);
 
818
 
 
819
      goto done_check_denied_focus;
 
820
    }
 
821
  
 
822
  /* Find windows that matter (not minimized, on same workspace
 
823
   * as placed window, may be shaded - if shaded we pretend it isn't
 
824
   * for placement purposes)
 
825
   */
 
826
  {
 
827
    GSList *all_windows;
 
828
    GSList *tmp;
 
829
    
 
830
    all_windows = meta_display_list_windows (window->display);
 
831
 
 
832
    tmp = all_windows;
 
833
    while (tmp != NULL)
 
834
      {
 
835
        MetaWindow *w = tmp->data;
 
836
 
 
837
        if (meta_window_showing_on_its_workspace (w) &&
 
838
            w != window && 
 
839
            (window->workspace == w->workspace ||
 
840
             window->on_all_workspaces || w->on_all_workspaces))
 
841
          windows = g_list_prepend (windows, w);
 
842
 
 
843
        tmp = tmp->next;
 
844
      }
 
845
 
 
846
    g_slist_free (all_windows);
 
847
  }
 
848
 
 
849
  /* Warning, this is a round trip! */
 
850
  xi = meta_screen_get_current_xinerama (window->screen);
 
851
  
 
852
  /* "Origin" placement algorithm */
 
853
  x = xi->rect.x;
 
854
  y = xi->rect.y;
 
855
 
 
856
  if (find_first_fit (window, fgeom, windows,
 
857
                      xi->number,
 
858
                      x, y, &x, &y))
 
859
    goto done_check_denied_focus;
 
860
 
 
861
  /* Maximize windows if they are too big for their work area (bit of
 
862
   * a hack here). Assume undecorated windows probably don't intend to
 
863
   * be maximized.  
 
864
   */
 
865
  if (window->has_maximize_func && window->decorated &&
 
866
      !window->fullscreen)
 
867
    {
 
868
      MetaRectangle workarea;
 
869
      MetaRectangle outer;
 
870
 
 
871
      meta_window_get_work_area_for_xinerama (window,
 
872
                                              xi->number,
 
873
                                              &workarea);      
 
874
      meta_window_get_outer_rect (window, &outer);
 
875
      
 
876
      /* If the window is bigger than the screen, then automaximize.  Do NOT
 
877
       * auto-maximize the directions independently.  See #419810.
 
878
       */
 
879
      if (outer.width >= workarea.width && outer.height >= workarea.height)
 
880
        {
 
881
          window->maximize_horizontally_after_placement = TRUE;
 
882
          window->maximize_vertically_after_placement = TRUE;
 
883
        }
 
884
    }
 
885
 
 
886
  /* If no placement has been done, revert to cascade to avoid 
 
887
   * fully overlapping window (e.g. starting multiple terminals)
 
888
   * */
 
889
  if (!meta_prefs_get_center_new_windows() && (x == xi->rect.x && y == xi->rect.y))
 
890
    find_next_cascade (window, fgeom, windows, x, y, &x, &y);
 
891
 
 
892
 done_check_denied_focus:
 
893
  /* If the window is being denied focus and isn't a transient of the
 
894
   * focus window, we do NOT want it to overlap with the focus window
 
895
   * if at all possible.  This is guaranteed to only be called if the
 
896
   * focus_window is non-NULL, and we try to avoid that window.
 
897
   */
 
898
  if (window->denied_focus_and_not_transient)
 
899
    {
 
900
      gboolean       found_fit;
 
901
      MetaWindow    *focus_window;
 
902
      MetaRectangle  overlap;
 
903
 
 
904
      focus_window = window->display->focus_window;
 
905
      g_assert (focus_window != NULL);
 
906
 
 
907
      /* No need to do anything if the window doesn't overlap at all */
 
908
      found_fit = !meta_rectangle_intersect (&window->rect,
 
909
                                             &focus_window->rect,
 
910
                                             &overlap);
 
911
 
 
912
      /* Try to do a first fit again, this time only taking into account the
 
913
       * focus window.
 
914
       */
 
915
      if (!meta_prefs_get_center_new_windows() && !found_fit)
 
916
        {
 
917
          GList *focus_window_list;
 
918
          focus_window_list = g_list_prepend (NULL, focus_window);
 
919
 
 
920
          /* Reset x and y ("origin" placement algorithm) */
 
921
          x = xi->rect.x;
 
922
          y = xi->rect.y;
 
923
 
 
924
          found_fit = find_first_fit (window, fgeom, focus_window_list,
 
925
                                      xi->number,
 
926
                                      x, y, &x, &y);
 
927
          g_list_free (focus_window_list);
 
928
        }
 
929
 
 
930
      /* If that still didn't work, just place it where we can see as much
 
931
       * as possible.
 
932
       */
 
933
      if (!found_fit)
 
934
        find_most_freespace (window, fgeom, focus_window, x, y, &x, &y);
 
935
    }
 
936
  
 
937
 done:
 
938
  g_list_free (windows);
 
939
  
 
940
 done_no_constraints:
 
941
 
 
942
  *new_x = x;
 
943
  *new_y = y;
 
944
}