/** Centered placement. */ void CenterClient(const BoundingBox *box, ClientNode *np) { np->x = box->x + (box->width / 2) - (np->width / 2); np->y = box->y + (box->height / 2) - (np->height / 2); ConstrainSize(np); ConstrainPosition(np); }
/** Cascade placement. */ void CascadeClient(const BoundingBox *box, ClientNode *np) { const ScreenType *sp; int north, south, east, west; int cascadeIndex; char overflow; GetBorderSize(&np->state, &north, &south, &east, &west); sp = GetMouseScreen(); cascadeIndex = sp->index * settings.desktopCount + currentDesktop; /* Set the cascaded location. */ np->x = box->x + west + cascadeOffsets[cascadeIndex]; np->y = box->y + north + cascadeOffsets[cascadeIndex]; cascadeOffsets[cascadeIndex] += settings.borderWidth + settings.titleHeight; /* Check for cascade overflow. */ overflow = 0; if(np->x + np->width - box->x > box->width) { overflow = 1; } else if(np->y + np->height - box->y > box->height) { overflow = 1; } if(overflow) { cascadeOffsets[cascadeIndex] = settings.borderWidth + settings.titleHeight; np->x = box->x + west + cascadeOffsets[cascadeIndex]; np->y = box->y + north + cascadeOffsets[cascadeIndex]; /* Check for client overflow and update cascade position. */ if(np->x + np->width - box->x > box->width) { np->x = box->x + west; } else if(np->y + np->height - box->y > box->height) { np->y = box->y + north; } else { cascadeOffsets[cascadeIndex] += settings.borderWidth + settings.titleHeight; } } ConstrainSize(np); ConstrainPosition(np); }
/** Place a client on the screen. */ void PlaceClient(ClientNode *np, char alreadyMapped) { BoundingBox box; const ScreenType *sp; Assert(np); if(alreadyMapped || (!(np->state.status & STAT_PIGNORE) && (np->sizeFlags & (PPosition | USPosition)))) { GravitateClient(np, 0); if(!alreadyMapped) { ConstrainSize(np); ConstrainPosition(np); } } else { sp = GetMouseScreen(); GetScreenBounds(sp, &box); SubtractTrayBounds(GetTrays(), &box, np->state.layer); SubtractStrutBounds(&box, np); /* If tiled is specified, first attempt to use tiled placement. */ if(np->state.status & STAT_TILED) { if(TileClient(&box, np)) { return; } } /* Either tiled placement failed or was not specified. */ if(np->state.status & STAT_CENTERED) { CenterClient(&box, np); } else { CascadeClient(&box, np); } } }
/** Process a configure request. */ void HandleConfigureRequest(const XConfigureRequestEvent *event) { XWindowChanges wc; ClientNode *np; if(HandleDockConfigureRequest(event)) { return; } np = FindClientByWindow(event->window); if(np) { int deltax, deltay; char changed = 0; char resized = 0; GetGravityDelta(np, np->gravity, &deltax, &deltay); if((event->value_mask & CWWidth) && (event->width != np->width)) { switch(np->gravity) { case EastGravity: case NorthEastGravity: case SouthEastGravity: /* Right side should not move. */ np->x -= event->width - np->width; break; case WestGravity: case NorthWestGravity: case SouthWestGravity: /* Left side should not move. */ break; case CenterGravity: /* Center of the window should not move. */ np->x -= (event->width - np->width) / 2; break; default: break; } np->width = event->width; changed = 1; resized = 1; } if((event->value_mask & CWHeight) && (event->height != np->height)) { switch(np->gravity) { case NorthGravity: case NorthEastGravity: case NorthWestGravity: /* Top should not move. */ break; case SouthGravity: case SouthEastGravity: case SouthWestGravity: /* Bottom should not move. */ np->y -= event->height - np->height; break; case CenterGravity: /* Center of the window should not move. */ np->y -= (event->height - np->height) / 2; break; default: break; } np->height = event->height; changed = 1; resized = 1; } if((event->value_mask & CWX) && (event->x - deltax != np->x)) { np->x = event->x - deltax; changed = 1; } if((event->value_mask & CWY) && (event->y - deltay != np->y)) { np->y = event->y - deltay; changed = 1; } /* Update stacking. */ if((event->value_mask & CWStackMode)) { Window above = None; if(event->value_mask & CWSibling) { above = event->above; } RestackClient(np, above, event->detail); } /* Return early if there's nothing to do. */ if(!changed) { return; } if(np->controller) { (np->controller)(0); } if(np->state.maxFlags) { MaximizeClient(np, MAX_NONE); } if(np->state.border & BORDER_CONSTRAIN) { resized = 1; } if(resized) { ConstrainSize(np); ConstrainPosition(np); ResetBorder(np); } else { int north, south, east, west; GetBorderSize(&np->state, &north, &south, &east, &west); if(np->parent != None) { JXMoveWindow(display, np->parent, np->x - west, np->y - north); } else { JXMoveWindow(display, np->window, np->x, np->y); } } SendConfigureEvent(np); RequirePagerUpdate(); } else { /* We don't know about this window, just let the configure through. */ wc.stack_mode = event->detail; wc.sibling = event->above; wc.border_width = event->border_width; wc.x = event->x; wc.y = event->y; wc.width = event->width; wc.height = event->height; JXConfigureWindow(display, event->window, event->value_mask, &wc); } }
/** Set a client's full screen state. */ void SetClientFullScreen(ClientNode *np, char fullScreen) { XEvent event; int north, south, east, west; BoundingBox box; const ScreenType *sp; Assert(np); /* Make sure there's something to do. */ if(!fullScreen == !(np->state.status & STAT_FULLSCREEN)) { return; } if(!(np->state.border & BORDER_FULLSCREEN)) { return; } if(np->state.status & STAT_SHADED) { UnshadeClient(np); } if(fullScreen) { np->state.status |= STAT_FULLSCREEN; if(!(np->state.maxFlags)) { np->oldx = np->x; np->oldy = np->y; np->oldWidth = np->width; np->oldHeight = np->height; } sp = GetCurrentScreen(np->x, np->y); GetScreenBounds(sp, &box); GetBorderSize(&np->state, &north, &south, &east, &west); box.x += west; box.y += north; box.width -= east + west; box.height -= north + south; np->x = box.x; np->y = box.y; np->width = box.width; np->height = box.height; ResetBorder(np); } else { np->state.status &= ~STAT_FULLSCREEN; np->x = np->oldx; np->y = np->oldy; np->width = np->oldWidth; np->height = np->oldHeight; ConstrainSize(np); ConstrainPosition(np); if(np->state.maxFlags != MAX_NONE) { PlaceMaximizedClient(np, np->state.maxFlags); } ResetBorder(np); event.type = MapRequest; event.xmaprequest.send_event = True; event.xmaprequest.display = display; event.xmaprequest.parent = np->parent; event.xmaprequest.window = np->window; JXSendEvent(display, rootWindow, False, SubstructureRedirectMask, &event); } WriteState(np); SendConfigureEvent(np); RequireRestack(); }
/** Attempt to place the client at the specified coordinates. */ char TryTileClient(const BoundingBox *box, ClientNode *np, int x, int y) { const ClientNode *tp; int layer; int north, south, east, west; int x1, x2, y1, y2; int ox1, ox2, oy1, oy2; /* Set the client position. */ GetBorderSize(&np->state, &north, &south, &east, &west); np->x = x + west; np->y = y + north; ConstrainSize(np); ConstrainPosition(np); /* Get the client boundaries. */ x1 = np->x - west; x2 = np->x + np->width + east; y1 = np->y - north; y2 = np->y + np->height + south; /* Loop over each client. */ for(layer = np->state.layer; layer < LAYER_COUNT; layer++) { for(tp = nodes[layer]; tp; tp = tp->next) { /* Skip clients that aren't visible. */ if(tp->state.desktop != currentDesktop) { if(!(tp->state.status & STAT_STICKY)) { continue; } } if(!(tp->state.status & STAT_MAPPED)) { continue; } if(tp == np) { continue; } /* Get the boundaries of the other client. */ GetBorderSize(&tp->state, &north, &south, &east, &west); ox1 = tp->x - west; ox2 = tp->x + tp->width + east; oy1 = tp->y - north; oy2 = tp->y + tp->height + south; /* Check for an overlap. */ if(x2 <= ox1 || x1 >= ox2) { continue; } if(y2 <= oy1 || y1 >= oy2) { continue; } return 0; } } /* No client overlaps this position. */ return 1; }