/* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002, 2003 Red Hat, Inc. * Copyright (C) 2003 Rob Adams * Copyright (C) 2005 Novell, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include #include #include #include #define PLACE_WORKAROUND_DEFAULT TRUE #define RECTANGLE_FROM_WINDOW(w) {w->attrib.x,w->attrib.y,w->attrib.width,w->attrib.height} #define GET_WINDOW_HEIGHT(w) (window->serverHeight + window->serverBorderWidth * 2) #define GET_WINDOW_WIDTH(w) (window->serverWidth + window->serverBorderWidth * 2) static int displayPrivateIndex; typedef struct _PlaceDisplay { int screenPrivateIndex; // HandleEventProc handleEvent; } PlaceDisplay; #define PLACE_SCREEN_OPTION_WORKAROUND 0 #define PLACE_SCREEN_OPTION_MODE 1 #define PLACE_SCREEN_OPTION_NUM 2 typedef struct _PlaceScreen { CompOption opt[PLACE_SCREEN_OPTION_NUM]; DamageWindowRectProc damageWindowRect; int placeMode; } PlaceScreen; typedef enum _PlaceMode { PlaceModeCascade = 0, PlaceModeCentered, PlaceModeRandom, PlaceModeIntelligent, } PlaceMode; char *placeModes[] = { N_("Cascade"), N_("Centered"), N_("Random"), N_("Intelligent") }; #define PLACE_MODE_DEFAULT PlaceModeIntelligent #define NUM_PLACE_MODES 4 #define GET_PLACE_DISPLAY(d) \ ((PlaceDisplay *) (d)->privates[displayPrivateIndex].ptr) #define PLACE_DISPLAY(d) \ PlaceDisplay *pd = GET_PLACE_DISPLAY (d) #define GET_PLACE_SCREEN(s, pd) \ ((PlaceScreen *) (s)->privates[(pd)->screenPrivateIndex].ptr) #define PLACE_SCREEN(s) \ PlaceScreen *ps = GET_PLACE_SCREEN (s, GET_PLACE_DISPLAY (s->display)) #define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption)) static Bool placeSetScreenOption(CompScreen * screen, char *name, CompOptionValue * value) { CompOption *o; int index; PLACE_SCREEN(screen); o = compFindOption(ps->opt, NUM_OPTIONS(ps), name, &index); if (!o) return FALSE; switch (index) { case PLACE_SCREEN_OPTION_WORKAROUND: if (compSetBoolOption(o, value)) return TRUE; break; case PLACE_SCREEN_OPTION_MODE: if (compSetStringOption(o, value)) { int i; for (i = 0; i < NUM_PLACE_MODES; i++) if (strcmp(placeModes[i], o->value.s) == 0) ps->placeMode = (PlaceMode) i; } default: break; } return FALSE; } static void placeScreenInitOptions(PlaceScreen * ps) { CompOption *o; int i; o = &ps->opt[PLACE_SCREEN_OPTION_WORKAROUND]; o->advanced = False; o->name = "workarounds"; o->group = N_("Misc. options"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Workarounds"); o->longDesc = N_("Window placement Workarounds."); o->type = CompOptionTypeBool; o->value.b = PLACE_WORKAROUND_DEFAULT; o = &ps->opt[PLACE_SCREEN_OPTION_MODE]; o->advanced = False; o->name = "place_mode"; o->group = N_("Misc. options"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Placement Mode"); o->longDesc = N_("Window Placement technique."); o->type = CompOptionTypeString; o->value.s = strdup(placeModes[PLACE_MODE_DEFAULT]); o->rest.s.string = placeModes; o->rest.s.nString = NUM_PLACE_MODES; for (i = 0; i < NUM_PLACE_MODES; i++) if (strcmp(placeModes[i], o->value.s) == 0) ps->placeMode = (PlaceMode) i; } static CompOption *placeGetScreenOptions(CompScreen * screen, int *count) { if (screen) { PLACE_SCREEN(screen); *count = NUM_OPTIONS(ps); return ps->opt; } else { PlaceScreen *ps = malloc(sizeof(PlaceScreen)); placeScreenInitOptions(ps); *count = NUM_OPTIONS(ps); return ps->opt; } } typedef enum { PlaceLeft, PlaceRight, PlaceTop, PlaceBottom } PlaceWindowDirection; static Bool rectangleIntersect(XRectangle * src1, XRectangle * src2, XRectangle * dest) { int dest_x, dest_y; int dest_w, dest_h; int return_val; g_return_val_if_fail(src1 != NULL, FALSE); g_return_val_if_fail(src2 != NULL, FALSE); g_return_val_if_fail(dest != NULL, FALSE); return_val = FALSE; dest_x = MAX(src1->x, src2->x); dest_y = MAX(src1->y, src2->y); dest_w = MIN(src1->x + src1->width, src2->x + src2->width) - dest_x; dest_h = MIN(src1->y + src1->height, src2->y + src2->height) - dest_y; if (dest_w > 0 && dest_h > 0) { dest->x = dest_x; dest->y = dest_y; dest->width = dest_w; dest->height = dest_h; return_val = TRUE; } else { dest->width = 0; dest->height = 0; } return return_val; } static gint northwestcmp(gconstpointer a, gconstpointer b) { CompWindow *aw = (gpointer) a; CompWindow *bw = (gpointer) b; int from_origin_a; int from_origin_b; int ax, ay, bx, by; ax = aw->attrib.x - aw->input.left; ay = aw->attrib.y - aw->input.top; bx = bw->attrib.x - bw->input.left; by = bw->attrib.y - bw->input.top; /* probably there's a fast good-enough-guess we could use here. */ from_origin_a = sqrt(ax * ax + ay * ay); from_origin_b = sqrt(bx * bx + by * by); if (from_origin_a < from_origin_b) return -1; else if (from_origin_a > from_origin_b) return 1; else return 0; } static void get_workarea_of_current_output_device(CompScreen * s, XRectangle * area) { int head; head = screenGetCurrentOutputDev(s); screenGetOutputDevWorkArea(s, head, area); } static void placeCentered(CompWindow * window, int *x, int *y) { *x = window->screen->workArea.x + (window->screen->workArea.width - GET_WINDOW_WIDTH(window)) / 2; *y = window->screen->workArea.y + (window->screen->workArea.height - GET_WINDOW_HEIGHT(window)) / 2; } static void placeRandom(CompWindow * window, int *x, int *y) { *x = rand() % (window->screen->workArea.width - window->screen->workArea.x); *y = rand() % (window->screen->workArea.height - window->screen->workArea.y); } static void find_next_cascade(CompWindow * window, GList * windows, int x, int y, int *new_x, int *new_y) { GList *tmp; GList *sorted; int cascade_x, cascade_y; int x_threshold, y_threshold; int window_width, window_height; int cascade_stage; XRectangle work_area; sorted = g_list_copy(windows); sorted = g_list_sort(sorted, northwestcmp); /* This is a "fuzzy" cascade algorithm. * For each window in the list, we find where we'd cascade a * new window after it. If a window is already nearly at that * position, we move on. */ /* arbitrary-ish threshold, honors user attempts to * manually cascade. */ #define CASCADE_FUZZ 15 x_threshold = MAX(window->input.left, CASCADE_FUZZ); y_threshold = MAX(window->input.top, CASCADE_FUZZ); /* Find furthest-SE origin of all workspaces. * cascade_x, cascade_y are the target position * of NW corner of window frame. */ get_workarea_of_current_output_device(window->screen, &work_area); cascade_x = MAX(0, work_area.x); cascade_y = MAX(0, work_area.y); /* Find first cascade position that's not used. */ window_width = GET_WINDOW_WIDTH(window) + window->input.left + window->input.right; window_height = GET_WINDOW_HEIGHT(window) + window->input.top + window->input.bottom; cascade_stage = 0; tmp = sorted; while (tmp != NULL) { CompWindow *w; int wx, wy; w = tmp->data; /* we want frame position, not window position */ wx = w->attrib.x - w->input.left; wy = w->attrib.y - w->input.top; if (ABS(wx - cascade_x) < x_threshold && ABS(wy - cascade_y) < y_threshold) { /* This window is "in the way", move to next cascade * point. The new window frame should go at the origin * of the client window we're stacking above. */ wx = w->attrib.x; wy = w->attrib.y; cascade_x = wx; cascade_y = wy; /* If we go off the screen, start over with a new cascade */ if (((cascade_x + window_width) > (work_area.x + work_area.width)) || ((cascade_y + window_height) > (work_area.y + work_area.height))) { cascade_x = MAX(0, work_area.x); cascade_y = MAX(0, work_area.y); #define CASCADE_INTERVAL 50 /* space between top-left corners of cascades */ cascade_stage += 1; cascade_x += CASCADE_INTERVAL * cascade_stage; /* start over with a new cascade translated to the right, * unless we are out of space */ if ((cascade_x + window_width) < (work_area.x + work_area.width)) { tmp = sorted; continue; } else { /* All out of space, this cascade_x won't work */ cascade_x = MAX(0, work_area.x); break; } } } else { /* Keep searching for a further-down-the-diagonal window. */ } tmp = tmp->next; } /* cascade_x and cascade_y will match the last window in the list * that was "in the way" (in the approximate cascade diagonal) */ g_list_free(sorted); /* Convert coords to position of window, not position of frame. */ *new_x = cascade_x + window->input.left; *new_y = cascade_y + window->input.top; } static void find_most_freespace(CompWindow * window, CompWindow * focus_window, int x, int y, int *new_x, int *new_y) { PlaceWindowDirection side; int max_area; int max_width, max_height, left, right, top, bottom; int left_space, right_space, top_space, bottom_space; int frame_size_left, frame_size_top; XRectangle work_area; XRectangle avoid; XRectangle outer; frame_size_left = window->input.left; frame_size_top = window->input.top; get_workarea_of_current_output_device(window->screen, &work_area); getOuterRectOfWindow(focus_window, &avoid); getOuterRectOfWindow(window, &outer); /* Find the areas of choosing the various sides of the focus window */ max_width = MIN(avoid.width, outer.width); max_height = MIN(avoid.height, outer.height); left_space = avoid.x - work_area.x; right_space = work_area.width - (avoid.x + avoid.width - work_area.x); top_space = avoid.y - work_area.y; bottom_space = work_area.height - (avoid.y + avoid.height - work_area.y); left = MIN(left_space, outer.width); right = MIN(right_space, outer.width); top = MIN(top_space, outer.height); bottom = MIN(bottom_space, outer.height); /* Find out which side of the focus_window can show the most of the * window */ side = PlaceLeft; max_area = left * max_height; if (right * max_height > max_area) { side = PlaceRight; max_area = right * max_height; } if (top * max_width > max_area) { side = PlaceTop; max_area = top * max_width; } if (bottom * max_width > max_area) { side = PlaceBottom; max_area = bottom * max_width; } /* Give up if there's no where to put it * (i.e. focus window is maximized) */ if (max_area == 0) return; /* Place the window on the relevant side; if the whole window fits, * make it adjacent to the focus window; if not, make sure the * window doesn't go off the edge of the screen. */ switch (side) { case PlaceLeft: *new_y = avoid.y + frame_size_top; if (left_space > outer.width) *new_x = avoid.x - outer.width + frame_size_left; else *new_x = work_area.x + frame_size_left; break; case PlaceRight: *new_y = avoid.y + frame_size_top; if (right_space > outer.width) *new_x = avoid.x + avoid.width + frame_size_left; else *new_x = work_area.x + work_area.width - outer.width + frame_size_left; break; case PlaceTop: *new_x = avoid.x + frame_size_left; if (top_space > outer.height) *new_y = avoid.y - outer.height + frame_size_top; else *new_y = work_area.y + frame_size_top; break; case PlaceBottom: *new_x = avoid.x + frame_size_left; if (bottom_space > outer.height) *new_y = avoid.y + avoid.height + frame_size_top; else *new_y = work_area.y + work_area.height - outer.height + frame_size_top; break; } } static void avoid_being_obscured_as_second_modal_dialog(CompWindow * window, int *x, int *y) { /* We can't center this dialog if it was denied focus and it * overlaps with the focus window and this dialog is modal and this * dialog is in the same app as the focus window (*phew*...please * don't make me say that ten times fast). See bug 307875 comment 11 * and 12 for details, but basically it means this is probably a * second modal dialog for some app while the focus window is the * first modal dialog. We should probably make them simultaneously * visible in general, but it becomes mandatory to do so due to * buggy apps (e.g. those using gtk+ *sigh*) because in those cases * this second modal dialog also happens to be modal to the first * dialog in addition to the main window, while it has only let us * know about the modal-to-the-main-window part. */ CompWindow *focus_window; focus_window = findWindowAtDisplay(window->screen->display, window->screen->display->activeWindow); } static gboolean rectangle_overlaps_some_window(XRectangle * rect, GList * windows) { GList *tmp; XRectangle dest; tmp = windows; while (tmp != NULL) { CompWindow *other = tmp->data; XRectangle other_rect; switch (other->type) { case CompWindowTypeSplashMask: case CompWindowTypeDesktopMask: case CompWindowTypeDialogMask: case CompWindowTypeModalDialogMask: case CompWindowTypeFullscreenMask: case CompWindowTypeUnknownMask: break; case CompWindowTypeNormalMask: case CompWindowTypeUtilMask: case CompWindowTypeToolbarMask: case CompWindowTypeMenuMask: getOuterRectOfWindow(other, &other_rect); if (rectangleIntersect(rect, &other_rect, &dest)) return TRUE; break; } tmp = tmp->next; } return FALSE; } static void placeIntelligent(CompWindow * w, int *x, int *y, GList * windows) { int tries = 0; XRectangle wRect = RECTANGLE_FROM_WINDOW(w); XRectangle sRect = { 0, 0, 0, 0 }; wRect.height += w->serverBorderWidth; int xi; int yi; CompScreen *screen = w->screen; int maxTries = ( ((wRect.height + wRect.width ) / 100) > 1 ? ((wRect.height + wRect.width) / 100) : 1 ); int wIter = wRect.width / maxTries; int hIter = wRect.height / maxTries; get_workarea_of_current_output_device(screen, &sRect); placeCentered(w, x, y); XRectangle cRect = { *x, *y, wRect.width, wRect.height }; if (!rectangle_overlaps_some_window(&cRect, windows)) { return; /*If it fits place it in the center */ } /* Recursing would be ineffecient, a goto really is the best solution */ thisisdisgusting: for (xi = sRect.x; xi < (sRect.width+sRect.x); xi += 10) { wRect.x = xi; for (yi = sRect.y; yi < (sRect.height+sRect.y); yi += 10) { wRect.y = yi; if (!rectangle_overlaps_some_window(&wRect, windows)) { if (! ((wRect.width > sRect.width / 2) && ((wRect.width + w->serverX > sRect.width) || (wRect.height + w->serverY > sRect.height)))) { *x = xi; } *y = yi; return; } } } /* No fit and big window? Center it, that tends to be what people expect */ if (tries == 0 && wRect.width > (sRect.width / 2)) { placeCentered(w, x, y); return; } /* Would the window fit if it was smaller ? Reduce width of the window rectangle and go through the check loop again */ if (tries < maxTries && wRect.width > 0 && wRect.height > 0) { tries++; wRect.width -= wIter; wRect.height -= hIter; goto thisisdisgusting; } /* Fallback, we shouldn't get here? */ placeCentered(w, x, y); } static gint leftmost_cmp(gconstpointer a, gconstpointer b) { CompWindow *aw = (gpointer) a; CompWindow *bw = (gpointer) b; int ax, bx; ax = aw->attrib.x - aw->input.left; bx = bw->attrib.x - bw->input.left; if (ax < bx) return -1; else if (ax > bx) return 1; else return 0; } static gint topmost_cmp(gconstpointer a, gconstpointer b) { CompWindow *aw = (gpointer) a; CompWindow *bw = (gpointer) b; int ay, by; ay = aw->attrib.y - aw->input.top; by = bw->attrib.y - bw->input.top; if (ay < by) return -1; else if (ay > by) return 1; else return 0; } static void center_tile_rect_in_area(XRectangle * rect, XRectangle * work_area) { int fluff; /* The point here is to tile a window such that "extra" * space is equal on either side (i.e. so a full screen * of windows tiled this way would center the windows * as a group) */ fluff = (work_area->width % (rect->width + 1)) / 2; rect->x = work_area->x + fluff; fluff = (work_area->height % (rect->height + 1)) / 3; rect->y = work_area->y + fluff; } static gboolean rect_fits_in_work_area(XRectangle * work_area, XRectangle * rect) { return ((rect->x >= work_area->x) && (rect->y >= work_area->y) && (rect->x + rect->width <= work_area->x + work_area->width) && (rect->y + rect->height <= work_area->y + work_area->height)); } /* Find the leftmost, then topmost, empty area on the workspace * that can contain the new window. * * Cool feature to have: if we can't fit the current window size, * try shrinking the window (within geometry constraints). But * beware windows such as Emacs with no sane minimum size, we * don't want to create a 1x1 Emacs. */ static gboolean find_first_fit(CompWindow * window, GList * windows, int x, int y, int *new_x, int *new_y) { /* This algorithm is limited - it just brute-force tries * to fit the window in a small number of locations that are aligned * with existing windows. It tries to place the window on * the bottom of each existing window, and then to the right * of each existing window, aligned with the left/top of the * existing window in each of those cases. */ int retval; GList *below_sorted; GList *right_sorted; GList *tmp; XRectangle rect; XRectangle work_area; retval = FALSE; /* Below each window */ below_sorted = g_list_copy(windows); below_sorted = g_list_sort(below_sorted, leftmost_cmp); below_sorted = g_list_sort(below_sorted, topmost_cmp); /* To the right of each window */ right_sorted = g_list_copy(windows); right_sorted = g_list_sort(right_sorted, topmost_cmp); right_sorted = g_list_sort(right_sorted, leftmost_cmp); getOuterRectOfWindow(window, &rect); get_workarea_of_current_output_device(window->screen, &work_area); work_area.x += (window->initialViewportX - window->screen->x) * window->screen->width; work_area.y += (window->initialViewportY - window->screen->y) * window->screen->height; center_tile_rect_in_area(&rect, &work_area); if (rect_fits_in_work_area(&work_area, &rect) && !rectangle_overlaps_some_window(&rect, windows)) { *new_x = rect.x + window->input.left; *new_y = rect.y + window->input.top; retval = TRUE; goto out; } /* try below each window */ tmp = below_sorted; while (tmp != NULL) { CompWindow *w = tmp->data; XRectangle outer_rect; getOuterRectOfWindow(w, &outer_rect); rect.x = outer_rect.x; rect.y = outer_rect.y + outer_rect.height; if (rect_fits_in_work_area(&work_area, &rect) && !rectangle_overlaps_some_window(&rect, below_sorted)) { *new_x = rect.x + window->input.left; *new_y = rect.y + window->input.top; retval = TRUE; goto out; } tmp = tmp->next; } /* try to the right of each window */ tmp = right_sorted; while (tmp != NULL) { CompWindow *w = tmp->data; XRectangle outer_rect; getOuterRectOfWindow(w, &outer_rect); rect.x = outer_rect.x + outer_rect.width; rect.y = outer_rect.y; if (rect_fits_in_work_area(&work_area, &rect) && !rectangle_overlaps_some_window(&rect, right_sorted)) { *new_x = rect.x + window->input.left; *new_y = rect.y + window->input.top; retval = TRUE; goto out; } tmp = tmp->next; } out: g_list_free(below_sorted); g_list_free(right_sorted); return retval; } static void placeWindow(CompWindow * window, int x, int y, int *new_x, int *new_y) { CompWindow *wi; GList *windows; XRectangle work_area; int x0 = (window->initialViewportX - window->screen->x) * window->screen->width; int y0 = (window->initialViewportY - window->screen->y) * window->screen->height; int window_width, window_height; PLACE_SCREEN(window->screen); window_width = GET_WINDOW_WIDTH(window); window_height = GET_WINDOW_HEIGHT(window); get_workarea_of_current_output_device(window->screen, &work_area); work_area.x += x0; work_area.y += y0; windows = NULL; switch (window->type) { case CompWindowTypeSplashMask: case CompWindowTypeDialogMask: case CompWindowTypeModalDialogMask: case CompWindowTypeNormalMask: /* Run placement algorithm on these. */ break; case CompWindowTypeDockMask: case CompWindowTypeDesktopMask: case CompWindowTypeUtilMask: case CompWindowTypeToolbarMask: case CompWindowTypeMenuMask: case CompWindowTypeFullscreenMask: case CompWindowTypeUnknownMask: /* Assume the app knows best how to place these, no placement * algorithm ever (other than "leave them as-is") */ goto done_no_constraints; break; } /* don't run placement algorithm on windows that can't be moved */ if (!(window->actions & CompWindowActionMoveMask)) { goto done_no_constraints; } if (window->type & CompWindowTypeFullscreenMask) { x = x0; y = y0; goto done_no_constraints; } if (window->state & (CompWindowStateMaximizedVertMask | CompWindowStateMaximizedHorzMask)) { if (window->screen->nOutputDev == 1) { if (window->state & CompWindowStateMaximizedVertMask) y = work_area.y + window->input.top; if (window->state & CompWindowStateMaximizedHorzMask) x = work_area.x + window->input.left; } else { int head = screenGetOutputDevForWindow(window); XRectangle workArea; screenGetOutputDevWorkArea(window->screen, head, &workArea); if (window->state & CompWindowStateMaximizedVertMask) y = workArea.y + window->input.top; if (window->state & CompWindowStateMaximizedHorzMask) x = workArea.x + window->input.left; } goto done; } if (ps->opt[PLACE_SCREEN_OPTION_WORKAROUND].value.b) { /* workarounds enabled */ if ((window->sizeHints.flags & PPosition) || (window->sizeHints.flags & USPosition)) { avoid_being_obscured_as_second_modal_dialog(window, &x, &y); goto done; } } else { switch (window->type) { case CompWindowTypeNormalMask: /* Only accept USPosition on normal windows because the app is full * of shit claiming the user set -geometry for a dialog or dock */ if (window->sizeHints.flags & USPosition) { /* don't constrain with placement algorithm */ goto done; } break; case CompWindowTypeSplashMask: case CompWindowTypeDialogMask: case CompWindowTypeModalDialogMask: /* Ignore even USPosition on dialogs, splashscreen */ break; case CompWindowTypeDockMask: case CompWindowTypeDesktopMask: case CompWindowTypeUtilMask: case CompWindowTypeToolbarMask: case CompWindowTypeMenuMask: case CompWindowTypeFullscreenMask: case CompWindowTypeUnknownMask: /* Assume the app knows best how to place these. */ if (window->sizeHints.flags & PPosition) { goto done_no_constraints; } break; } } if ((window->type == CompWindowTypeDialogMask || window->type == CompWindowTypeModalDialogMask) && window->transientFor != None) { /* Center horizontally, at top of parent vertically */ CompWindow *parent; parent = findWindowAtDisplay(window->screen->display, window->transientFor); if (parent) { int w; x = parent->attrib.x; y = parent->attrib.y; w = GET_WINDOW_WIDTH(parent); /* center of parent */ x = x + w / 2 - window_width / 2; /* "visually" center window over parent, leaving twice as * much space below as on top. */ y += (GET_WINDOW_HEIGHT(parent) - window_height) / 3 + window->input.top; /* clip to screen if parent is visible in current viewport */ if (parent->attrib.x < parent->screen->width && parent->attrib.x + parent->screen->width > 0) { XRectangle area; get_workarea_of_current_output_device(window->screen, &area); if (x + window_width > area.x + area.width) x = area.x + area.width - window_width; if (y + window_height > area.y + area.height) y = area.y + area.height - window_height; if (x < area.x) x = area.x; if (y < area.y) y = area.y; } avoid_being_obscured_as_second_modal_dialog(window, &x, &y); goto done_no_x_constraints; } } /* FIXME UTILITY with transient set should be stacked up * on the sides of the parent window or something. */ if (window->type == CompWindowTypeDialogMask || window->type == CompWindowTypeModalDialogMask || window->type == CompWindowTypeSplashMask) { /* Center on screen */ int head; XRectangle workArea; /* w = window->screen->width; h = window->screen->height; x = (w - window->width) / 2; y = (h - window->height) / 2; */ head = screenGetCurrentOutputDev(window->screen); screenGetOutputDevWorkArea(window->screen, head, &workArea); x = (workArea.width - window_width) / 2; y = (workArea.height - window_height) / 2; goto done_check_denied_focus; } /* Find windows that matter (not minimized, on same workspace * as placed window, may be shaded - if shaded we pretend it isn't * for placement purposes) */ for (wi = window->screen->windows; wi; wi = wi->next) { if (!wi->shaded && wi->attrib.map_state != IsViewable) continue; if (wi->attrib.x >= work_area.x + work_area.width || wi->attrib.x + GET_WINDOW_WIDTH(wi) <= work_area.x || wi->attrib.y >= work_area.y + work_area.height || wi->attrib.y + GET_WINDOW_HEIGHT(wi) <= work_area.y) continue; if (wi->attrib.override_redirect) continue; if (wi->state & CompWindowStateOffscreenMask) continue; if (wi->type & (CompWindowTypeDesktopMask | CompWindowTypeDockMask | CompWindowTypeFullscreenMask | CompWindowTypeUnknownMask)) continue; if (wi != window) windows = g_list_prepend(windows, wi); } /* "Origin" placement algorithm */ x = x0; y = y0; if (ps->placeMode == PlaceModeCascade) { if (find_first_fit(window, windows, x, y, &x, &y)) goto done_check_denied_focus; /* if the window wasn't placed at the origin of screen, * cascade it onto the current screen */ find_next_cascade(window, windows, x, y, &x, &y); } else if (ps->placeMode == PlaceModeCentered) { placeCentered(window, &x, &y); } else if (ps->placeMode == PlaceModeRandom) { placeRandom(window, &x, &y); } else { placeIntelligent(window, &x, &y, windows); } done_check_denied_focus: /* If the window is being denied focus and isn't a transient of the * focus window, we do NOT want it to overlap with the focus window * if at all possible. This is guaranteed to only be called if the * focus_window is non-NULL, and we try to avoid that window. */ if (0 /* window->denied_focus_and_not_transient */ ) { gboolean found_fit = FALSE; CompWindow *focus_window; focus_window = findWindowAtDisplay(window->screen->display, window->screen->display->activeWindow); if (focus_window) { XRectangle wr, fwr, overlap; getOuterRectOfWindow(window, &wr); getOuterRectOfWindow(focus_window, &fwr); /* No need to do anything if the window doesn't overlap at all */ found_fit = !rectangleIntersect(&wr, &fwr, &overlap); /* Try to do a first fit again, this time only taking into * account the focus window. */ if (!found_fit) { GList *focus_window_list; focus_window_list = g_list_prepend(NULL, focus_window); /* Reset x and y ("origin" placement algorithm) */ x = 0; y = 0; found_fit = find_first_fit(window, focus_window_list, x, y, &x, &y); g_list_free(focus_window_list); } } /* If that still didn't work, just place it where we can see as much * as possible. */ if (!found_fit) find_most_freespace(window, focus_window, x, y, &x, &y); } g_list_free(windows); done: if (((x + window_width + window->input.right < work_area.x + work_area.width) && (x + window_width + window->input.right > work_area.x)) || ((x - window->input.left > work_area.x) && (x - window->input.left < work_area.x + work_area.width))) { if (x + window_width + window->input.right > work_area.x + work_area.width) x = work_area.x + work_area.width - window_width - window->input.right; if (x - window->input.left < work_area.x) x = work_area.x + window->input.left; } else { //only use THIS code if the window is TOTALLY outside the viewport. while (x + window_width + window->input.right > work_area.x + work_area.width) x -= work_area.width; if (x - window->input.left < work_area.x) x = work_area.x + window->input.left; } done_no_x_constraints: if (window->screen->nOutputDev == 1) { if (y + window_height + window->input.bottom > work_area.y + work_area.height) y = work_area.y + work_area.height - window_height - window->input.bottom; if (y - window->input.top < work_area.y) y = work_area.y + window->input.top; } else { int head = screenGetOutputDevForWindow(window); XRectangle workArea; screenGetOutputDevWorkArea(window->screen, head, &workArea); if (y + window_height + window->input.bottom > workArea.y + workArea.height) y = workArea.y + workArea.height - window_height - window->input.bottom; if (y - window->input.top < workArea.y) y = workArea.y + window->input.top; } done_no_constraints: *new_x = x; *new_y = y; } static Bool placeDamageWindowRect(CompWindow * w, Bool initial, BoxPtr rect) { Bool status; PLACE_SCREEN(w->screen); UNWRAP(ps, w->screen, damageWindowRect); status = (*w->screen->damageWindowRect) (w, initial, rect); WRAP(ps, w->screen, damageWindowRect, placeDamageWindowRect); if (initial && !w->attrib.override_redirect && !w->placed) { int newX, newY; placeWindow(w, w->attrib.x, w->attrib.y, &newX, &newY); w->placed = TRUE; if (newX != w->attrib.x || newY != w->attrib.y) { moveWindow(w, newX - w->attrib.x, newY - w->attrib.y, FALSE, TRUE); syncWindowPosition(w); } } return status; } static Bool placeInitDisplay(CompPlugin * p, CompDisplay * d) { PlaceDisplay *pd; pd = malloc(sizeof(PlaceDisplay)); if (!pd) return FALSE; pd->screenPrivateIndex = allocateScreenPrivateIndex(d); if (pd->screenPrivateIndex < 0) { free(pd); return FALSE; } d->privates[displayPrivateIndex].ptr = pd; // WRAP (pd, d, handleEvent, placeHandleEvent); return TRUE; } static void placeFiniDisplay(CompPlugin * p, CompDisplay * d) { PLACE_DISPLAY(d); // UNWRAP (pd, d, handleEvent); freeScreenPrivateIndex(d, pd->screenPrivateIndex); free(pd); } static Bool placeInitScreen(CompPlugin * p, CompScreen * s) { PlaceScreen *ps; PLACE_DISPLAY(s->display); ps = malloc(sizeof(PlaceScreen)); if (!ps) return FALSE; placeScreenInitOptions(ps); // addScreenBinding (s, &ps->opt[PLACE_SCREEN_OPTION_CENTER_INITIATE].value.bind); WRAP(ps, s, damageWindowRect, placeDamageWindowRect); s->privates[pd->screenPrivateIndex].ptr = ps; return TRUE; } static void placeFiniScreen(CompPlugin * p, CompScreen * s) { PLACE_SCREEN(s); UNWRAP(ps, s, damageWindowRect); free(ps); } static Bool placeInit(CompPlugin * p) { displayPrivateIndex = allocateDisplayPrivateIndex(); if (displayPrivateIndex < 0) return FALSE; return TRUE; } static void placeFini(CompPlugin * p) { if (displayPrivateIndex >= 0) freeDisplayPrivateIndex(displayPrivateIndex); } static CompPluginVTable placeVTable = { "place", N_("Place Windows"), N_("Place windows at appropriate positions when mapped"), placeInit, placeFini, placeInitDisplay, placeFiniDisplay, placeInitScreen, placeFiniScreen, 0, /* InitWindow */ 0, /* FiniWindow */ 0, /* GetDisplayOptions */ 0, /* SetDisplayOption */ placeGetScreenOptions, placeSetScreenOption, 0, 0, 0, 0, BERYL_ABI_INFO, "beryl-plugins", "wm", 0, 0, True, }; CompPluginVTable *getCompPluginInfo(void) { return &placeVTable; }