/** Set _NET_WORKAREA. */ void SetWorkarea(void) { BoundingBox box; unsigned long *array; unsigned int count; int x; count = 4 * settings.desktopCount * sizeof(unsigned long); array = (unsigned long*)AllocateStack(count); box.x = 0; box.y = 0; box.width = rootWidth; box.height = rootHeight; SubtractTrayBounds(GetTrays(), &box, LAYER_NORMAL); SubtractStrutBounds(&box, NULL); for(x = 0; x < settings.desktopCount; x++) { array[x * 4 + 0] = box.x; array[x * 4 + 1] = box.y; array[x * 4 + 2] = box.width; array[x * 4 + 3] = box.height; } JXChangeProperty(display, rootWindow, atoms[ATOM_NET_WORKAREA], XA_CARDINAL, 32, PropModeReplace, (unsigned char*)array, settings.desktopCount * 4); ReleaseStack(array); }
/** Constrain the position of a client. */ void ConstrainPosition(ClientNode *np) { BoundingBox box; int north, south, east, west; /* Get the bounds for placement. */ box.x = 0; box.y = 0; box.width = rootWidth; box.height = rootHeight; SubtractTrayBounds(GetTrays(), &box, np->state.layer); SubtractStrutBounds(&box, np); /* Fix the position. */ GetBorderSize(&np->state, &north, &south, &east, &west); if(np->x + np->width + east + west > box.x + box.width) { np->x = box.x + box.width - np->width - east; } if(np->y + np->height + north + south > box.y + box.height) { np->y = box.y + box.height - np->height - south; } if(np->x < box.x + west) { np->x = box.x + west; } if(np->y < box.y + north) { np->y = box.y + north; } }
/** 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); } } }
/** Startup key bindings. */ void StartupKeys(void) { XModifierKeymap *modmap; KeyNode *np; TrayType *tp; int x; /* Get the keys that we don't care about (num lock, etc). */ modmap = JXGetModifierMapping(display); for(x = 0; x < sizeof(lockMods) / sizeof(lockMods[0]); x++) { lockMods[x].mask = GetModifierMask(modmap, lockMods[x].symbol); lockMask |= lockMods[x].mask; } JXFreeModifiermap(modmap); /* Look up and grab the keys. */ for(np = bindings; np; np = np->next) { /* Determine the key code. */ if(!np->code) { np->code = JXKeysymToKeycode(display, np->symbol); } /* Grab the key if needed. */ if(ShouldGrab(np->key)) { /* Grab on the root. */ GrabKey(np, rootWindow); /* Grab on the trays. */ for(tp = GetTrays(); tp; tp = tp->next) { GrabKey(np, tp->window); } } } }
/** Shutdown key bindings. */ void ShutdownKeys(void) { ClientNode *np; TrayType *tp; unsigned int layer; /* Ungrab keys on client windows. */ for(layer = 0; layer < LAYER_COUNT; layer++) { for(np = nodes[layer]; np; np = np->next) { JXUngrabKey(display, AnyKey, AnyModifier, np->window); } } /* Ungrab keys on trays, only really needed if we are restarting. */ for(tp = GetTrays(); tp; tp = tp->next) { JXUngrabKey(display, AnyKey, AnyModifier, tp->window); } /* Ungrab keys on the root. */ JXUngrabKey(display, AnyKey, AnyModifier, rootWindow); }
/** Snap to window borders. */ void DoSnapBorder(ClientNode *np) { const ClientNode *tp; const TrayType *tray; RectangleType client, other; RectangleType left = { 0 }; RectangleType right = { 0 }; RectangleType top = { 0 }; RectangleType bottom = { 0 }; int layer; int north, south, east, west; GetClientRectangle(np, &client); GetBorderSize(&np->state, &north, &south, &east, &west); other.valid = 1; /* Work from the bottom of the window stack to the top. */ for(layer = 0; layer < LAYER_COUNT; layer++) { /* Check tray windows. */ for(tray = GetTrays(); tray; tray = tray->next) { if(tray->hidden) { continue; } other.left = tray->x; other.right = tray->x + tray->width; other.top = tray->y; other.bottom = tray->y + tray->height; left.valid = CheckLeftValid(&client, &other, &left); right.valid = CheckRightValid(&client, &other, &right); top.valid = CheckTopValid(&client, &other, &top); bottom.valid = CheckBottomValid(&client, &other, &bottom); if(CheckOverlapTopBottom(&client, &other)) { if(abs(client.left - other.right) <= settings.snapDistance) { left = other; } if(abs(client.right - other.left) <= settings.snapDistance) { right = other; } } if(CheckOverlapLeftRight(&client, &other)) { if(abs(client.top - other.bottom) <= settings.snapDistance) { top = other; } if(abs(client.bottom - other.top) <= settings.snapDistance) { bottom = other; } } } /* Check client windows. */ for(tp = nodeTail[layer]; tp; tp = tp->prev) { if(tp == np || !ShouldSnap(tp)) { continue; } GetClientRectangle(tp, &other); /* Check if this border invalidates any previous value. */ left.valid = CheckLeftValid(&client, &other, &left); right.valid = CheckRightValid(&client, &other, &right); top.valid = CheckTopValid(&client, &other, &top); bottom.valid = CheckBottomValid(&client, &other, &bottom); /* Compute the new snap values. */ if(CheckOverlapTopBottom(&client, &other)) { if(abs(client.left - other.right) <= settings.snapDistance) { left = other; } if(abs(client.right - other.left) <= settings.snapDistance) { right = other; } } if(CheckOverlapLeftRight(&client, &other)) { if(abs(client.top - other.bottom) <= settings.snapDistance) { top = other; } if(abs(client.bottom - other.top) <= settings.snapDistance) { bottom = other; } } } } if(right.valid) { np->x = right.left - np->width - west; } if(left.valid) { np->x = left.right + east; } if(bottom.valid) { np->y = bottom.top - south; if(!(np->state.status & STAT_SHADED)) { np->y -= np->height; } } if(top.valid) { np->y = top.bottom + north; } }
/** Restack the clients according the way we want them. */ void RestackClients(void) { TrayType *tp; ClientNode *np; unsigned int layer, index; int trayCount; Window *stack; Window fw; if(JUNLIKELY(shouldExit)) { return; } /* Allocate memory for restacking. */ trayCount = GetTrayCount(); stack = AllocateStack((clientCount + trayCount) * sizeof(Window)); /* Prepare the stacking array. */ fw = None; index = 0; if(activeClient && (activeClient->state.status & STAT_FULLSCREEN)) { fw = activeClient->window; for(np = nodes[activeClient->state.layer]; np; np = np->next) { if(np->owner == fw) { if(np->parent != None) { stack[index] = np->parent; } else { stack[index] = np->window; } index += 1; } } if(activeClient->parent != None) { stack[index] = activeClient->parent; } else { stack[index] = activeClient->window; } index += 1; } layer = LAST_LAYER; for(;;) { for(np = nodes[layer]; np; np = np->next) { if( (np->state.status & (STAT_MAPPED | STAT_SHADED)) && !(np->state.status & STAT_HIDDEN)) { if(fw != None && (np->window == fw || np->owner == fw)) { continue; } if(np->parent != None) { stack[index] = np->parent; } else { stack[index] = np->window; } index += 1; } } for(tp = GetTrays(); tp; tp = tp->next) { if(layer == tp->layer) { stack[index] = tp->window; index += 1; } } if(layer == FIRST_LAYER) { break; } layer -= 1; } JXRestackWindows(display, stack, index); ReleaseStack(stack); UpdateNetClientList(); RequirePagerUpdate(); }
/** Place a maximized client on the screen. */ void PlaceMaximizedClient(ClientNode *np, MaxFlags flags) { BoundingBox box; const ScreenType *sp; int north, south, east, west; np->oldx = np->x; np->oldy = np->y; np->oldWidth = np->width; np->oldHeight = np->height; GetBorderSize(&np->state, &north, &south, &east, &west); sp = GetCurrentScreen(np->x + (east + west + np->width) / 2, np->y + (north + south + np->height) / 2); GetScreenBounds(sp, &box); if(!(flags & (MAX_HORIZ | MAX_LEFT | MAX_RIGHT))) { box.x = np->x - west; box.width = np->width + east + west; } if(!(flags & (MAX_VERT | MAX_TOP | MAX_BOTTOM))) { box.y = np->y - north; box.height = np->height + north + south; } SubtractTrayBounds(GetTrays(), &box, np->state.layer); SubtractStrutBounds(&box, np); if(box.width > np->maxWidth) { box.width = np->maxWidth; } if(box.height > np->maxHeight) { box.height = np->maxHeight; } if(np->sizeFlags & PAspect) { if(box.width * np->aspect.miny < box.height * np->aspect.minx) { box.height = (box.width * np->aspect.miny) / np->aspect.minx; } if(box.width * np->aspect.maxy > box.height * np->aspect.maxx) { box.width = (box.height * np->aspect.maxx) / np->aspect.maxy; } } /* Remove window outlines. */ if(flags & (MAX_VERT | MAX_TOP)) { north = Max(0, north - 1); } if(flags & (MAX_VERT | MAX_BOTTOM)) { south = Max(0, south - 1); } if(flags & (MAX_HORIZ | MAX_LEFT)) { west = Max(0, west - 1); } if(flags & (MAX_HORIZ | MAX_RIGHT)) { east = Max(0, east - 1); } /* If maximizing horizontally, update width. */ if(flags & MAX_HORIZ) { np->x = box.x; np->width = box.width; if(!(np->state.status & STAT_IIGNORE)) { np->width -= ((box.width - np->baseWidth) % np->xinc); } } else if(flags & MAX_LEFT) { np->x = box.x; np->width = box.width / 2 - west; if(!(np->state.status & STAT_IIGNORE)) { np->width -= ((box.width - np->baseWidth) % np->xinc); } } else if(flags & MAX_RIGHT) { np->x = box.x + box.width / 2 + west; np->width = box.width / 2 - east; if(!(np->state.status & STAT_IIGNORE)) { np->width -= ((box.width - np->baseWidth) % np->xinc); } } /* If maximizing vertically, update height. */ if(flags & MAX_VERT) { np->y = box.y + north; np->height = box.height - north; if(!(np->state.status & STAT_IIGNORE)) { np->height -= ((box.height - np->baseHeight) % np->yinc); } } else if(flags & MAX_TOP) { np->y = box.y + north; np->height = box.height / 2 - north - south; if(!(np->state.status & STAT_IIGNORE)) { np->height -= ((box.height - np->baseHeight) % np->yinc); } } else if(flags & MAX_BOTTOM) { np->y = box.y + box.height / 2 + north; np->height = box.height / 2 - north - south; if(!(np->state.status & STAT_IIGNORE)) { np->height -= ((box.height - np->baseHeight) % np->yinc); } } np->state.maxFlags = flags; }
/** Constrain the size of the client. */ char ConstrainSize(ClientNode *np) { BoundingBox box; const ScreenType *sp; int north, south, east, west; const int oldWidth = np->width; const int oldHeight = np->height; /* First we make sure the window isn't larger than the program allows. * We do this here to avoid moving the window below. */ np->width = Min(np->width, np->maxWidth); np->height = Min(np->height, np->maxHeight); /* Constrain the width if necessary. */ sp = GetCurrentScreen(np->x, np->y); GetScreenBounds(sp, &box); SubtractTrayBounds(GetTrays(), &box, np->state.layer); SubtractStrutBounds(&box, np); GetBorderSize(&np->state, &north, &south, &east, &west); if(np->width + east + west > sp->width) { box.x += west; box.width -= east + west; if(box.width > np->maxWidth) { box.width = np->maxWidth; } if(box.width > np->width) { box.width = np->width; } np->x = box.x; np->width = box.width - (box.width % np->xinc); } /* Constrain the height if necessary. */ if(np->height + north + south > sp->height) { box.y += north; box.height -= north + south; if(box.height > np->maxHeight) { box.height = np->maxHeight; } if(box.height > np->height) { box.height = np->height; } np->y = box.y; np->height = box.height - (box.height % np->yinc); } /* If the program has a minimum constraint, we apply that here. * Note that this could cause the window to overlap something. */ np->width = Max(np->width, np->minWidth); np->height = Max(np->height, np->minHeight); /* Fix the aspect ratio. */ if(np->sizeFlags & PAspect) { if(np->width * np->aspect.miny < np->height * np->aspect.minx) { np->height = (np->width * np->aspect.miny) / np->aspect.minx; } if(np->width * np->aspect.maxy > np->height * np->aspect.maxx) { np->width = (np->height * np->aspect.maxx) / np->aspect.maxy; } } if(np->width != oldWidth || np->height != oldHeight) { return 1; } else { return 0; } }