/*
 * 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