/* * BadWM - minimalistic window manager for the X Window System * Copyright (C) Robert Annessi * * 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/BadWM.h" #include #include #include #include #include "include/config.h" void make_new_client(Window w, ScreenInfo *s) { Client *c; XWindowAttributes attr; long dummy; XWMHints *hints; char *name; XGrabServer(dpy); c = (Client *)malloc(sizeof(Client)); if (!c) { fprintf(stderr, "out of memory in new_client; limping onward\n"); return; } /* We do this first of all as a test to see if the window actually * still exists by the time we've got to create a client structure * for it (sometimes they vanish too quickly or something, and lots * of pain ensues). */ initialising = w; XFetchName(dpy, w, &name); /* If 'initialising' is now set to None, that means doing the * XFetchName raised BadWindow - the window has been removed before * we got a chance to grab the server. */ if (initialising == None) { #ifdef DEBUG bad_debug(1,"make_new_client() : XError occurred for initialising window - aborting...\n"); #endif free(c); XSync(dpy, False); XUngrabServer(dpy); return; } initialising = None; c->next = head_client; /* adding to circular list */ if ( head_client) { c->fix_next = head_client->fix_next; head_client->fix_next = c; } else { c->fix_next = c; /* first one in list */ } head_client = c; /* initialise(c, w); */ c->screen = s; c->window = w; c->ignore_unmap = 0; /* Jon Perkin reported a crash wish an app called 'sunpci' which we * traced to getting divide-by-zeros because it sets PResizeInc * but then has increments as 0. So we check for 0s here and set them * to sensible defaults. */ c->size = XAllocSizeHints(); if (c->size->width_inc == 0) c->size->width_inc = 1; if (c->size->height_inc == 0) c->size->height_inc = 1; #ifdef DEBUG bad_debug(1,"XGetWMNormalHints()\n"); #endif XGetWMNormalHints(dpy, c->window, c->size, &dummy); XGetWindowAttributes(dpy, c->window, &attr); c->x = attr.x; c->y = attr.y; c->width = attr.width; c->height = attr.height; c->border = opt_bw; c->oldw = c->oldh = 0; if (name) { XFree(name); } c->vdesk = vdesk; if (attr.map_state == IsViewable) { c->ignore_unmap++; } else { init_position(c); if ((hints = XGetWMHints(dpy, w))) { if (hints->flags & StateHint) set_wm_state(c, hints->initial_state); XFree(hints); } } /* client is initialised */ change_gravity(c,opt_bw); reparent(c); #ifdef DEBUG if (wm_state(c) == IconicState) { bad_debug(1,"make_new_client() : client thinks it's iconised\n"); } else { if (wm_state(c) == NormalState) { bad_debug(1,"make_new_client() : client is in NormalState - good\n"); } else { if (wm_state(c) == WithdrawnState) { bad_debug(1,"make_new_client() : silly client! it's in WithdrawnState\n"); } else { bad_debug(1,"make_new_client() : don't know what state client is in\n"); } } } #endif XMapWindow(dpy, c->window); XMapRaised(dpy, c->parent); set_wm_state(c, NormalState); XSync(dpy, False); XUngrabServer(dpy); focus_client(c); } int intersect_area( Client *c1, Client *c2 ) { int c1x1=c1->x, c1y1=c1->y; int c1x2=c1->x+c1->width, c1y2=c1->y+c1->height; int c2x1=c2->x, c2y1=c2->y; int c2x2=c2->x+c2->width, c2y2=c2->y+c2->height; if (c1x2c2x2 || c1y1>c2y2) return 0; if (c2x2>c1x1 && c2y2>c1y1) return (c2x2-c1x1)*(c2y2-c1y1); else if (c1x2>c2x1 && c2y2>c1y1) return (c1x2-c2x1)*(c2y2-c1y1); else if (c2x2>c1x1 && c1y2>c2y1) return (c2x2-c1x1)*(c1y2-c2y1); else if (c1x2>c2x1 && c1y2>c2y1) return (c1x2-c2x1)*(c1y2-c2y1); exit(1); } int calc_overlap( Client *c ) { Client *cl; int dw = DisplayWidth(dpy,c->screen->screen); int dh = DisplayHeight(dpy,c->screen->screen); int val = 0; for (cl = head_client; cl; cl=cl->next) { if (cl!=c && cl->vdesk==c->vdesk) val += intersect_area(c,cl); } /* penalize outside-of-window positions hard */ if (c->x<0) val += 32*abs(c->x)*c->height; if (c->y<0) val += 32*abs(c->y)*c->width; if (c->x+c->width>dw) val += 32*(c->x+c->width-dw)*c->height; if (c->y+c->height>dh) val += 32*(c->y+c->height-dh)*c->width; return val; } void minoverlap_place_client( Client *c ) { #ifdef DEBUG bad_debug(1,"minoverlap_place_client()\n"); #endif int dw = DisplayWidth(dpy,c->screen->screen); int dh = DisplayHeight(dpy,c->screen->screen); int wi = dw/16; int hi = dh/16; int mlen = (wi>hi)?wi:hi; int failures; int xmin=0,ymin=0; int minval = INT_MAX; int val; /* Global placement strategy - the screen is divided into a 16x16 grid, and the window's top left corner is placed where it causes the least overlap with existing windows */ for (c->y=-hi; c->yy+=wi) for (c->x=-wi; c->xx+=hi) { val = calc_overlap(c); if (valx; ymin = c->y; } } /* Local placement strategy similar to the one suggested in the specifications. Choose random displacement vector (manhattan length = cellsize) and test, if the result is better than the current placement update current and continue. Stop after 5 failures. */ c->x = xmin; c->y = ymin; failures = 0; while (failures<5) { int dx,dy; dx = random()%mlen - mlen/2; dy = mlen-dx - mlen/2; c->x += dx; c->y += dy; val = calc_overlap(c); if (valx; ymin = c->y; } else failures++; } if (minval > c->width*c->height) { int x, y; #ifdef DEBUG bad_debug(1,"Placement strategy did not work -> centering window.\n"); #endif get_mouse_position(&x, &y, c->screen->root); xmin = (x * (dw - c->border - c->width)) / dw; ymin = (y * (dh - c->border - c->height)) / dh; } c->x = xmin; c->y = ymin; } void init_position(Client *c) { #ifdef DEBUG bad_debug(1,"init_position()\n"); #endif int xmax = DisplayWidth(dpy, c->screen->screen); int ymax = DisplayHeight(dpy, c->screen->screen); if (c->width < MINSIZE) c->width = MINSIZE; if (c->height < MINSIZE) c->height = MINSIZE; if (c->width > xmax) c->width = xmax; if (c->height > ymax) c->height = ymax; /* reposition if maximised horizontally or vertically */ if (c->x == 0 && c->width == xmax && c->y == 0 && c->height == ymax) { c->x = -c->border; c->y = -c->border; } else if (c->x < 0 || c->y < 0 || c->x > xmax || c->y > ymax) { c->x = c->y = c->border; } else { minoverlap_place_client(c); } } void reparent(Client *c) { XSetWindowAttributes p_attr; XSelectInput(dpy, c->window, EnterWindowMask | PropertyChangeMask); p_attr.override_redirect = True; p_attr.background_pixel = c->screen->bg.pixel; p_attr.event_mask = ChildMask | ButtonPressMask | ExposureMask | EnterWindowMask; c->parent = XCreateWindow(dpy, c->screen->root, c->x-c->border, c->y-c->border, c->width+(c->border*2), c->height + (c->border*2), 0, DefaultDepth(dpy, c->screen->screen), CopyFromParent, DefaultVisual(dpy, c->screen->screen), CWOverrideRedirect | CWBackPixel | CWEventMask, &p_attr); XDefineCursor(dpy,c->parent,arrow_curs); /* make same shape of new parent as original */ if (have_shape) { XShapeSelectInput(dpy, c->window, ShapeNotifyMask); set_shape(c); } XAddToSaveSet(dpy, c->window); XSetWindowBorderWidth(dpy, c->window, 0); XReparentWindow(dpy, c->window, c->parent, c->border, c->border); send_config(c); }