/*
* BadWM - minimalistic window manager for the X Window System
* Copyright (C) Robert Annessi <robert@annessi.at>
*
* 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#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 (c1x2<c2x1 || c1y2<c2y1 || c1x1>c2x2 || 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->y<dh; c->y+=wi)
for (c->x=-wi; c->x<dw; c->x+=hi)
{
val = calc_overlap(c);
if (val<minval)
{
minval = val;
xmin = c->x;
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 (val<minval)
{
minval = val;
xmin = c->x;
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);
}
syntax highlighted by Code2HTML, v. 0.9.1