/* 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 */ /**************************************************************************** * This module is all new * by Rob Nation * * This code does smart-placement initial window placement stuff * * Copyright 1994 Robert Nation. No restrictions are placed on this code, * as long as the copyright notice is preserved . No guarantees or * warrantees of any sort whatsoever are given or implied or anything. ****************************************************************************/ #include "config.h" #include #include "libs/fvwmlib.h" #include "libs/FScreen.h" #include "fvwm.h" #include "externs.h" #include "cursor.h" #include "functions.h" #include "bindings.h" #include "misc.h" #include "screen.h" #include "placement.h" #include "geometry.h" #include "update.h" #include "style.h" #include "borders.h" #include "move_resize.h" #include "virtual.h" #include "stack.h" #include "add_window.h" #ifndef MIN #define MIN(A,B) ((A)<(B)? (A):(B)) #endif #ifndef MAX #define MAX(A,B) ((A)>(B)? (A):(B)) #endif static int get_next_x( FvwmWindow *t, rectangle *screen_g, int x, int y, int pdeltax, int pdeltay, int use_percent); static int get_next_y( FvwmWindow *t, rectangle *screen_g, int y, int pdeltay, int use_percent); static float test_fit( FvwmWindow *t, rectangle *screen_g, int x11, int y11, float aoimin, int pdeltax, int pdeltay, int use_percent); static void CleverPlacement( FvwmWindow *t, rectangle *screen_g, int *x, int *y, int pdeltax, int pdeltay, int use_percent); static int SmartPlacement( FvwmWindow *t, rectangle *screen_g, int width, int height, int *x, int *y, int pdeltax, int pdeltay) { int PageLeft = screen_g->x - pdeltax; int PageTop = screen_g->y - pdeltay; int PageRight = PageLeft + screen_g->width; int PageBottom = PageTop + screen_g->height; int temp_h; int temp_w; int test_x = 0; int test_y = 0; int loc_ok = False; int tw = 0; int tx = 0; int ty = 0; int th = 0; FvwmWindow *test_window; int stickyx; int stickyy; Bool do_test; test_x = PageLeft; test_y = PageTop; temp_h = height; temp_w = width; for ( ; test_y + temp_h < PageBottom && !loc_ok; ) { test_x = PageLeft; while (test_x + temp_w < PageRight && !loc_ok) { loc_ok = True; test_window = Scr.FvwmRoot.next; for ( ; test_window != NULL && loc_ok; test_window = test_window->next) { if (t == test_window) continue; /* RBW - account for sticky windows... */ if (test_window->Desk == t->Desk || IS_STICKY(test_window)) { if (IS_STICKY(test_window)) { stickyx = pdeltax; stickyy = pdeltay; } else { stickyx = 0; stickyy = 0; } do_test = False; if(PLACEMENT_AVOID_ICON != 0 && IS_ICONIFIED(test_window) && !IS_ICON_UNMAPPED(test_window) && test_window->icon_w) { tw=test_window->icon_p_width; th=test_window->icon_p_height+ test_window->icon_g.height; tx = test_window->icon_g.x - stickyx; ty = test_window->icon_g.y - stickyy; do_test = True; } else if(!IS_ICONIFIED(test_window)) { tw = test_window->frame_g.width; th = test_window->frame_g.height; tx = test_window->frame_g.x - stickyx; ty = test_window->frame_g.y - stickyy; do_test = True; } if (do_test) { if (tx < test_x + width && test_x < tx + tw && ty < test_y + height && test_y < ty + th) { /* window overlaps, look for a different place */ loc_ok = False; test_x = tx + tw - 1; } } } /* if (test_window->Desk == t->Desk || IS_STICKY(test_window)) */ } /* for */ if (!loc_ok) test_x +=1; } /* while */ if (!loc_ok) test_y +=1; } /* while */ if (loc_ok == False) { return False; } *x = test_x; *y = test_y; return True; } /* CleverPlacement by Anthony Martin * This function will place a new window such that there is a minimum amount * of interference with other windows. If it can place a window without any * interference, fine. Otherwise, it places it so that the area of of * interference between the new window and the other windows is minimized */ static void CleverPlacement( FvwmWindow *t, rectangle *screen_g, int *x, int *y, int pdeltax, int pdeltay, int use_percent) { int test_x; int test_y; int xbest; int ybest; /* area of interference */ float aoi; float aoimin; int PageLeft = screen_g->x - pdeltax; int PageTop = screen_g->y - pdeltay; test_x = PageLeft; test_y = PageTop; aoi = test_fit( t, screen_g, test_x, test_y, -1, pdeltax, pdeltay, use_percent); aoimin = aoi; xbest = test_x; ybest = test_y; while (aoi != 0 && aoi != -1) { if (aoi > 0) { /* Windows interfere. Try next x. */ test_x = get_next_x( t, screen_g, test_x, test_y, pdeltax, pdeltay, use_percent); } else { /* Out of room in x direction. Try next y. Reset x.*/ test_x = PageLeft; test_y = get_next_y(t, screen_g, test_y, pdeltay, use_percent); } aoi = test_fit( t, screen_g, test_x, test_y, aoimin, pdeltax, pdeltay, use_percent); /* I've added +0.00001 because whith my machine the < test fail with * certain *equal* float numbers! */ if (aoi >= 0 && aoi + 0.00001 < aoimin) { xbest = test_x; ybest = test_y; aoimin = aoi; } } *x = xbest; *y = ybest; return; } static int get_next_x( FvwmWindow *t, rectangle *screen_g, int x, int y, int pdeltax, int pdeltay, int use_percent) { FvwmWindow *testw; int xnew; int xtest; int PageLeft = screen_g->x - pdeltax; int PageRight = PageLeft + screen_g->width; int minx_inc = screen_g->width / 20; int stickyx; int stickyy; /* Test window at far right of screen */ xnew = PageRight; xtest = PageRight - t->frame_g.width; if (xtest > x) xnew = MIN(xnew, xtest); /* Test the values of the right edges of every window */ for (testw = Scr.FvwmRoot.next ; testw != NULL ; testw = testw->next) { if (testw == t || (testw->Desk != t->Desk && !IS_STICKY(testw))) continue; if (IS_STICKY(testw)) { stickyx = pdeltax; stickyy = pdeltay; } else { stickyx = 0; stickyy = 0; } if (IS_ICONIFIED(testw)) { if (y < testw->icon_p_height + testw->icon_g.height + testw->icon_g.y - stickyy && testw->icon_g.y - stickyy < t->frame_g.height + y) { xtest = PageLeft + testw->icon_p_width + testw->icon_g.x - stickyx; if (xtest > x) xnew = MIN(xnew, xtest); xtest = PageLeft + testw->icon_g.x - stickyx - t->frame_g.width; if (xtest > x) xnew = MIN(xnew, xtest); } } else if (y < testw->frame_g.height + testw->frame_g.y - stickyy && testw->frame_g.y - stickyy < t->frame_g.height + y) { xtest = PageLeft + testw->frame_g.width + testw->frame_g.x - stickyx; if (xtest > x) xnew = MIN(xnew, xtest); xtest = PageLeft + testw->frame_g.x - stickyx - t->frame_g.width; if (xtest > x) xnew = MIN(xnew, xtest); } } if (use_percent) xnew = MIN(xnew, x + minx_inc); return xnew; } static int get_next_y( FvwmWindow *t, rectangle *screen_g, int y, int pdeltay, int use_percent) { FvwmWindow *testw; int ynew; int ytest; int PageBottom = screen_g->y + screen_g->height - pdeltay; int miny_inc = (screen_g->height / 20); int stickyy; /* Test window at far bottom of screen */ ynew = PageBottom; ytest = PageBottom - t->frame_g.height; if (ytest > y) ynew = MIN(ynew, ytest); /* Test the values of the bottom edge of every window */ for (testw = Scr.FvwmRoot.next ; testw != NULL ; testw = testw->next) { if (testw == t || (testw->Desk != t->Desk && !IS_STICKY(testw))) continue; if (IS_STICKY(testw)) { stickyy = pdeltay; } else { stickyy = 0; } if (IS_ICONIFIED(testw)) { ytest = testw->icon_p_height + testw->icon_g.height + testw->icon_g.y - stickyy; if (ytest > y) ynew = MIN(ynew, ytest); ytest = testw->icon_g.y - stickyy - t->frame_g.height; if (ytest > y) ynew = MIN(ynew, ytest); } else { ytest = testw->frame_g.height + testw->frame_g.y - stickyy; if (ytest > y) ynew = MIN(ynew, ytest); ytest = testw->frame_g.y - stickyy - t->frame_g.height; if (ytest > y) ynew = MIN(ynew, ytest); } } if (use_percent) ynew = MIN(ynew,y+miny_inc); return ynew; } static float test_fit( FvwmWindow *t, rectangle *screen_g, int x11, int y11, float aoimin, int pdeltax, int pdeltay, int use_percent) { FvwmWindow *testw; int x12; int x21; int x22; int y12; int y21; int y22; /* xleft, xright, ytop, ybottom */ int xl; int xr; int yt; int yb; /* area of interference */ float aoi = 0; float anew; float cover_factor = 1; float avoidance_factor; int PageRight = screen_g->x + screen_g->width - pdeltax; int PageBottom = screen_g->y + screen_g->height - pdeltay; int stickyx, stickyy; x12 = x11 + t->frame_g.width; y12 = y11 + t->frame_g.height; if (y12 > PageBottom) /* No room in y direction */ return -1; if (x12 > PageRight) /* No room in x direction */ return -2; for (testw = Scr.FvwmRoot.next ; testw != NULL ; testw = testw->next) { if (testw == t || (testw->Desk != t->Desk && !IS_STICKY(testw))) continue; if (IS_STICKY(testw)) { stickyx = pdeltax; stickyy = pdeltay; } else { stickyx = 0; stickyy = 0; } if (IS_ICONIFIED(testw)) { if (testw->icon_w == None || IS_ICON_UNMAPPED(testw)) continue; x21 = testw->icon_g.x - stickyx; y21 = testw->icon_g.y - stickyy; x22 = x21 + testw->icon_p_width; y22 = y21 + testw->icon_p_height + testw->icon_g.height; } else { x21 = testw->frame_g.x - stickyx; y21 = testw->frame_g.y - stickyy; x22 = x21 + testw->frame_g.width; y22 = y21 + testw->frame_g.height; } if (x11 < x22 && x12 > x21 && y11 < y22 && y12 > y21) { /* Windows interfere */ xl = MAX(x11, x21); xr = MIN(x12, x22); yt = MAX(y11, y21); yb = MIN(y12, y22); anew = (xr - xl) * (yb - yt); if (IS_ICONIFIED(testw)) avoidance_factor = PLACEMENT_AVOID_ICON; else if(compare_window_layers(testw, t) > 0) avoidance_factor = PLACEMENT_AVOID_ONTOP; else if(compare_window_layers(testw, t) < 0) avoidance_factor = PLACEMENT_AVOID_BELOW; else if(IS_STICKY(testw)) avoidance_factor = PLACEMENT_AVOID_STICKY; else avoidance_factor = 1.0; if (use_percent) { /* normalisation */ if ((x22 - x21) * (y22 - y21) != 0 && (x12 - x11) * (y12 - y11) != 0) { anew = 100 * MAX(anew / ((x22 - x21) * (y22 - y21)), anew / ((x12 - x11) * (y12 - y11))); if (anew >= 99) cover_factor = PLACEMENT_AVOID_COVER_99; else if (anew >= 95) cover_factor = PLACEMENT_AVOID_COVER_95; else if (anew >= 85) cover_factor = PLACEMENT_AVOID_COVER_85; else if (anew >= 75) cover_factor = PLACEMENT_AVOID_COVER_75; } if (avoidance_factor>1) avoidance_factor = avoidance_factor + (cover_factor > 1) ? cover_factor : 0; else avoidance_factor = cover_factor; } anew *= avoidance_factor; aoi += anew; if (aoi > aoimin && aoimin != -1) return aoi; } } return aoi; } /************************************************************************** * * Handles initial placement and sizing of a new window * * Return value: * * 0 = window lost * 1 = OK * 2 = OK, window must be resized too * **************************************************************************/ Bool PlaceWindow( FvwmWindow *tmp_win, style_flags *sflags, int Desk, int PageX, int PageY, int XineramaScreen, int mode) { FvwmWindow *t; int xl = -1; int yt; int DragWidth; int DragHeight; int px = 0; int py = 0; int pdeltax = 0; int pdeltay = 0; int PageLeft; int PageTop; int PageRight; int PageBottom; rectangle screen_g; Bool rc = False; struct { unsigned do_forbid_manual_placement : 1; unsigned do_honor_starts_on_page : 1; unsigned do_honor_starts_on_screen : 1; unsigned is_smartly_placed : 1; unsigned do_not_use_wm_placement : 1; } flags; extern Bool Restarting; extern Bool PPosOverride; memset(&flags, 0, sizeof(flags)); /* Select a desk to put the window on (in list of priority): * 1. Sticky Windows stay on the current desk. * 2. Windows specified with StartsOnDesk go where specified * 3. Put it on the desk it was on before the restart. * 4. Transients go on the same desk as their parents. * 5. Window groups stay together (if the KeepWindowGroupsOnDesk style is * used). */ /* * Let's get the StartsOnDesk/Page tests out of the way first. */ if (SUSE_START_ON_DESK(sflags) || SUSE_START_ON_SCREEN(sflags)) { flags.do_honor_starts_on_page = True; flags.do_honor_starts_on_screen = True; /* * Honor the flag unless... * it's a restart or recapture, and that option's disallowed... */ if (PPosOverride && (Restarting || (Scr.flags.windows_captured)) && !SRECAPTURE_HONORS_STARTS_ON_PAGE(sflags)) { flags.do_honor_starts_on_page = False; flags.do_honor_starts_on_screen = False; } /* * it's a cold start window capture, and that's disallowed... */ if (PPosOverride && (!Restarting && !(Scr.flags.windows_captured)) && !SCAPTURE_HONORS_STARTS_ON_PAGE(sflags)) { flags.do_honor_starts_on_page = False; flags.do_honor_starts_on_screen = False; } /* * it's ActivePlacement and SkipMapping, and that's disallowed. */ if (!PPosOverride && (DO_NOT_SHOW_ON_MAP(tmp_win) && (SPLACEMENT_MODE(sflags) == PLACE_MANUAL || SPLACEMENT_MODE(sflags) == PLACE_MANUAL_B || SPLACEMENT_MODE(sflags) == PLACE_TILEMANUAL) && !SMANUAL_PLACEMENT_HONORS_STARTS_ON_PAGE(sflags))) { flags.do_honor_starts_on_page = False; fvwm_msg(WARN, "PlaceWindow", "illegal style combination used: StartsOnPage/StartsOnDesk" " and SkipMapping don't work with ManualPlacement and" " TileManualPlacement. Putting window on current page," " please use an other placement style or" " ActivePlacementHonorsStartsOnPage."); } } /* if (SUSE_START_ON_DESK(sflags) || SUSE_START_ON_SCREEN(sflags))) */ /* get the screen coordinates to place window on */ if (SUSE_START_ON_SCREEN(sflags)) { if (flags.do_honor_starts_on_screen) { /* use screen from style */ FScreenGetScrRect( NULL, XineramaScreen, &screen_g.x, &screen_g.y, &screen_g.width, &screen_g.height); } else { /* use global screen */ FScreenGetScrRect( NULL, FSCREEN_GLOBAL, &screen_g.x, &screen_g.y, &screen_g.width, &screen_g.height); } } else { /* use current screen */ FScreenGetScrRect( NULL, FSCREEN_CURRENT, &screen_g.x, &screen_g.y, &screen_g.width, &screen_g.height); } PageLeft = screen_g.x - pdeltax; PageTop = screen_g.y - pdeltay; PageRight = PageLeft + screen_g.width; PageBottom = PageTop + screen_g.height; yt = PageTop; /* Don't alter the existing desk location during Capture/Recapture. */ if (!PPosOverride) { tmp_win->Desk = Scr.CurrentDesk; } if (SIS_STICKY(*sflags)) tmp_win->Desk = Scr.CurrentDesk; else if (SUSE_START_ON_DESK(sflags) && Desk && flags.do_honor_starts_on_page) tmp_win->Desk = (Desk > -1) ? Desk - 1 : Desk; else { if ((DO_USE_WINDOW_GROUP_HINT(tmp_win)) && (tmp_win->wmhints) && (tmp_win->wmhints->flags & WindowGroupHint)&& (tmp_win->wmhints->window_group != None) && (tmp_win->wmhints->window_group != Scr.Root)) { /* Try to find the group leader or another window in the group */ for (t = Scr.FvwmRoot.next; t != NULL; t = t->next) { if (t->w == tmp_win->wmhints->window_group) { /* found the group leader, break out */ tmp_win->Desk = t->Desk; break; } else if (t->wmhints && (t->wmhints->flags & WindowGroupHint) && (t->wmhints->window_group == tmp_win->wmhints->window_group)) { /* found a window from the same group, but keep looking for the group * leader */ tmp_win->Desk = t->Desk; } } } if ((IS_TRANSIENT(tmp_win))&&(tmp_win->transientfor!=None)&& (tmp_win->transientfor != Scr.Root)) { /* Try to find the parent's desktop */ for (t = Scr.FvwmRoot.next; t != NULL; t = t->next) { if(t->w == tmp_win->transientfor) tmp_win->Desk = t->Desk; } } { /* migo - I am not sure this block is ever needed */ Atom atype; int aformat; unsigned long nitems, bytes_remain; unsigned char *prop; if ( XGetWindowProperty( dpy, tmp_win->w, _XA_WM_DESKTOP, 0L, 1L, True, _XA_WM_DESKTOP, &atype, &aformat, &nitems, &bytes_remain, &prop) == Success) { if (prop != NULL) { tmp_win->Desk = *(unsigned long *)prop; XFree(prop); } } } } /* I think it would be good to switch to the selected desk * whenever a new window pops up, except during initialization */ /* RBW - 11/02/1998 -- I dont. */ if ((!PPosOverride)&&(!DO_NOT_SHOW_ON_MAP(tmp_win))) { goto_desk(tmp_win->Desk); } /* Don't move viewport if SkipMapping, or if recapturing the window, * adjust the coordinates later. Otherwise, just switch to the target * page - it's ever so much simpler. */ if (!SIS_STICKY(*sflags) && SUSE_START_ON_DESK(sflags)) { if (PageX && PageY) { px = PageX - 1; py = PageY - 1; px *= Scr.MyDisplayWidth; py *= Scr.MyDisplayHeight; if ( (!PPosOverride) && (!DO_NOT_SHOW_ON_MAP(tmp_win))) { MoveViewport(px,py,True); } else if (flags.do_honor_starts_on_page) { /* Save the delta from current page */ pdeltax = Scr.Vx - px; pdeltay = Scr.Vy - py; PageLeft -= pdeltax; PageRight -= pdeltax; PageTop -= pdeltay; PageBottom -= pdeltay; } } } /* Desk has been selected, now pick a location for the window. * * Windows use the position hint if these conditions are met: * * The program specified a USPosition hint and it is not overridden with * the No(Transient)USPosition style. * * OR * * The program specified a PPosition hint and it is not overridden with * the No(Transient)PPosition style. * * Windows without a position hint are placed using wm placement. * * If RandomPlacement was specified, then place the window in a * psuedo-random location */ { Bool override_ppos; Bool override_uspos; Bool has_ppos = False; Bool has_uspos = False; if (IS_TRANSIENT(tmp_win)) { override_ppos = SUSE_NO_TRANSIENT_PPOSITION(sflags); override_uspos = SUSE_NO_TRANSIENT_USPOSITION(sflags); } else { override_ppos = SUSE_NO_PPOSITION(sflags); override_uspos = SUSE_NO_USPOSITION(sflags); } if ((tmp_win->hints.flags & PPosition) && !override_ppos) { has_ppos = True; } if ((tmp_win->hints.flags & USPosition) && !override_uspos) { has_uspos = True; } if (mode == PLACE_AGAIN) { flags.do_not_use_wm_placement = False; } else if (has_ppos || has_uspos) { flags.do_not_use_wm_placement = True; } else if (PPosOverride) { flags.do_not_use_wm_placement = True; } else if (!flags.do_honor_starts_on_page && tmp_win->wmhints && (tmp_win->wmhints->flags & StateHint) && tmp_win->wmhints->initial_state == IconicState) { flags.do_forbid_manual_placement = True; } } if (!flags.do_not_use_wm_placement) { unsigned int placement_mode = SPLACEMENT_MODE(sflags); /* override if Manual placement happen */ SET_PLACED_BY_FVWM(tmp_win,True); if (flags.do_forbid_manual_placement) { switch (placement_mode) { case PLACE_MANUAL: case PLACE_MANUAL_B: placement_mode = PLACE_CASCADE; break; case PLACE_TILEMANUAL: placement_mode = PLACE_TILECASCADE; break; default: break; } } /* first, try various "smart" placement */ switch (placement_mode) { case PLACE_TILEMANUAL: flags.is_smartly_placed = SmartPlacement( tmp_win, &screen_g, tmp_win->frame_g.width, tmp_win->frame_g.height, &xl, &yt, pdeltax, pdeltay); if (flags.is_smartly_placed) break; /* fall through to manual placement */ case PLACE_MANUAL: case PLACE_MANUAL_B: /* either "smart" placement fail and the second choice is a manual placement (TileManual) or we have a manual placement in any case (Manual) */ xl = -1; yt = -1; if (GrabEm(CRS_POSITION, GRAB_NORMAL)) { int mx; int my; /* Grabbed the pointer - continue */ MyXGrabServer(dpy); if (XGetGeometry(dpy, tmp_win->w, &JunkRoot, &JunkX, &JunkY, (unsigned int *)&DragWidth, (unsigned int *)&DragHeight, &JunkBW, &JunkDepth) == 0) { MyXUngrabServer(dpy); UngrabEm(GRAB_NORMAL); return False; } SET_PLACED_BY_FVWM(tmp_win,False); MyXGrabKeyboard(dpy); DragWidth = tmp_win->frame_g.width; DragHeight = tmp_win->frame_g.height; XMapRaised(dpy, Scr.SizeWindow); FScreenGetScrRect(NULL, FSCREEN_GLOBAL, &mx, &my, NULL, NULL); if (moveLoop(tmp_win, mx, my, DragWidth, DragHeight, &xl, &yt, False)) { /* resize too */ rc = True; } else { /* ok */ rc = False; } XUnmapWindow(dpy, Scr.SizeWindow); MyXUngrabKeyboard(dpy); MyXUngrabServer(dpy); UngrabEm(GRAB_NORMAL); } else { /* couldn't grab the pointer - better do something */ XBell(dpy, 0); xl = 0; yt = 0; } if (flags.do_honor_starts_on_page) { xl -= pdeltax; yt -= pdeltay; } tmp_win->attr.y = yt; tmp_win->attr.x = xl; break; case PLACE_MINOVERLAPPERCENT: CleverPlacement(tmp_win, &screen_g, &xl, &yt, pdeltax, pdeltay, 1); flags.is_smartly_placed = True; break; case PLACE_TILECASCADE: flags.is_smartly_placed = SmartPlacement( tmp_win, &screen_g, tmp_win->frame_g.width, tmp_win->frame_g.height, &xl, &yt, pdeltax, pdeltay); if (flags.is_smartly_placed) break; /* fall through to cascade placement */ case PLACE_CASCADE: case PLACE_CASCADE_B: /* either "smart" placement fail and the second choice is "random" placement (TileCascade) or we have a "random" placement in any case (Cascade) or we have a crazy SPLACEMENT_MODE(sflags) value set with the old Style Dumb/Smart, Random/Active, Smart/SmartOff (i.e.: Dumb+Random+Smart or Dumb+Active+Smart) */ if ((Scr.randomx += tmp_win->title_g.height) > screen_g.width / 2) { Scr.randomx = tmp_win->title_g.height; } if ((Scr.randomy += 2 * tmp_win->title_g.height) > screen_g.height / 2) { Scr.randomy = 2 * tmp_win->title_g.height; } tmp_win->attr.x = Scr.randomx + PageLeft - pdeltax; tmp_win->attr.y = Scr.randomy + PageTop - pdeltay; /* try to keep the window on the screen */ tmp_win->frame_g.x = PageLeft + tmp_win->attr.x + tmp_win->old_bw + 10; tmp_win->frame_g.y = PageTop + tmp_win->attr.y + tmp_win->old_bw + 10; if (tmp_win->attr.x + tmp_win->frame_g.width >= PageRight) { tmp_win->attr.x = PageRight - tmp_win->attr.width - tmp_win->old_bw - 2*tmp_win->boundary_width; Scr.randomx = 0; } if (tmp_win->attr.y + tmp_win->frame_g.height >= PageBottom) { tmp_win->attr.y = PageBottom - tmp_win->attr.height - tmp_win->old_bw - tmp_win->title_g.height - 2*tmp_win->boundary_width; Scr.randomy = 0; } /* the left and top sides are more important in huge windows */ if (tmp_win->attr.x < PageLeft) { tmp_win->attr.x = PageLeft; } if (tmp_win->attr.y < PageTop) { tmp_win->attr.y = PageTop; } break; case PLACE_MINOVERLAP: CleverPlacement(tmp_win, &screen_g, &xl, &yt, pdeltax, pdeltay, 0); flags.is_smartly_placed = True; break; default: /* can't happen */ break; } if (flags.is_smartly_placed) { /* "smart" placement succed, we have done ... */ tmp_win->attr.x = xl; tmp_win->attr.y = yt; } } /* !flags.do_not_use_wm_placement */ else { if (!PPosOverride) SET_PLACED_BY_FVWM(tmp_win,False); /* the USPosition was specified, or the window is a transient, * or it starts iconic so place it automatically */ if (SUSE_START_ON_SCREEN(sflags) && flags.do_honor_starts_on_screen) { /* * If StartsOnScreen has been given for a window, translate its * USPosition so that it is relative to that particular screen. * If we don't do this, then a geometry would completely cancel * the effect of the StartsOnScreen style. * * So there are two ways to get a window to pop up on a particular * Xinerama screen. 1: The intuitive way giving a geometry hint * relative to the desired screen's 0,0 along with the appropriate * StartsOnScreen style (or *wmscreen resource), or 2: Do NOT * specify a Xinerama screen (or specify it to be 'g') and give * the geometry hint in terms of the global screen. */ FScreenTranslateCoordinates( NULL, XineramaScreen, NULL, FSCREEN_GLOBAL, &tmp_win->attr.x, &tmp_win->attr.y); } /* * If SkipMapping, and other legalities are observed, adjust for * StartsOnPage. */ if ( ( DO_NOT_SHOW_ON_MAP(tmp_win) && flags.do_honor_starts_on_page ) && ( (!(IS_TRANSIENT(tmp_win)) || SUSE_START_ON_PAGE_FOR_TRANSIENT(sflags)) #if 0 /* dv 08-Jul-2003: Do not use this. Instead, force the * window on the requested page even if the application * requested a different position. */ && ((SUSE_NO_PPOSITION(sflags)) || !(tmp_win->hints.flags & PPosition)) /* dv 08-Jul-2003: This condition is always true because we * already checked for flags.do_honor_starts_on_page above. */ /* RBW - allow StartsOnPage to go through, even if iconic. */ && ( ((!((tmp_win->wmhints)&& (tmp_win->wmhints->flags & StateHint)&& (tmp_win->wmhints->initial_state == IconicState))) || (flags.do_honor_starts_on_page)) ) #endif ) ) { /* * We're placing a SkipMapping window - either capturing one that's * previously been mapped, or overriding USPosition - so what we * have here is its actual untouched coordinates. In case it was * a StartsOnPage window, we have to 1) convert the existing x,y * offsets relative to the requested page (i.e., as though there * were only one page, no virtual desktop), then 2) readjust * relative to the current page. */ if (tmp_win->attr.x < 0) { tmp_win->attr.x = ((Scr.MyDisplayWidth + tmp_win->attr.x) % Scr.MyDisplayWidth); } else { tmp_win->attr.x = tmp_win->attr.x % Scr.MyDisplayWidth; } /* * Noticed a quirk here. With some apps (e.g., xman), we find the * placement has moved 1 pixel away from where we originally put it when * we come through here. Why is this happening? * Probably old_bw, try xclock -borderwidth 100 */ if (tmp_win->attr.y < 0) { tmp_win->attr.y = ((Scr.MyDisplayHeight + tmp_win->attr.y) % Scr.MyDisplayHeight); } else { tmp_win->attr.y = tmp_win->attr.y % Scr.MyDisplayHeight; } tmp_win->attr.x -= pdeltax; tmp_win->attr.y -= pdeltay; } /* put it where asked, mod title bar */ /* if the gravity is towards the top, move it by the title height */ { rectangle final_g; int gravx; int gravy; gravity_get_offsets(tmp_win->hints.win_gravity, &gravx, &gravy); final_g.x = tmp_win->attr.x + gravx * tmp_win->old_bw; final_g.y = tmp_win->attr.y + gravy * tmp_win->old_bw; /* Virtually all applications seem to share a common bug: they request * the top left pixel of their *border* as their origin instead of the * top left pixel of their client window, e.g. 'xterm -g +0+0' creates an * xterm that tries to map at (0 0) although its border (width 1) would * not be visible if it ran under plain X. It should have tried to map * at (1 1) instead. This clearly violates the ICCCM, but trying to * change this is like tilting at windmills. So we have to add the * border width here. */ final_g.x += tmp_win->old_bw; final_g.y += tmp_win->old_bw; final_g.width = 0; final_g.height = 0; if (mode == PLACE_INITIAL) { gravity_resize( tmp_win->hints.win_gravity, &final_g, 2 * tmp_win->boundary_width, 2 * tmp_win->boundary_width + tmp_win->title_g.height); } tmp_win->attr.x = final_g.x; tmp_win->attr.y = final_g.y; } } return rc; } void CMD_PlaceAgain(F_CMD_ARGS) { int x; int y; char *token; float noMovement[1] = {1.0}; float *ppctMovement = noMovement; window_style style; if (DeferExecution(eventp, &w, &tmp_win, &context, CRS_SELECT, ButtonRelease)) return; lookup_style(tmp_win, &style); PlaceWindow( tmp_win, &style.flags, SGET_START_DESK(style), SGET_START_PAGE_X(style), SGET_START_PAGE_Y(style), SGET_START_SCREEN(style), PLACE_AGAIN); x = tmp_win->attr.x; y = tmp_win->attr.y; /* Possibly animate the movement */ token = PeekToken(action, NULL); if(token && StrEquals("ANIM", token)) ppctMovement = NULL; AnimatedMoveFvwmWindow(tmp_win, tmp_win->frame, -1, -1, x, y, False, -1, ppctMovement); return; }