/* $id */
/*
 * 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 <stdio.h>
#include "include/config.h"

static int send_xmessage(Window w, Atom a, long x);

Client *find_client(Window w) {
	Client *c;

	/* maybe little enhanced -  but current is first often*/
	if ( current != NULL && (w == current->parent || w == current->window) ) 
		return current;

	for (c = head_client; c; c = c->next)
		if (w == c->parent || w == c->window)
			return c;
	return NULL;
}

static void __set_wm_state(const Window win, const Atom a, int state, long vis) {
	long data[2];

	data[0] = (long) state;
	data[1] = vis;

	XChangeProperty(dpy, win, a, a,
		32, PropModeReplace, (unsigned char *)data, 2);
}
void set_wm_state(Client *c, int state) {
	/* iconify window and set state */
	__set_wm_state(c->window, xa_wm_state, state, None);
}

static long * _g_wm_state(const Window win, const Atom where) {
	Atom real_type;
	int real_format;
	unsigned long n, extra;
	unsigned char *data;

	if ((XGetWindowProperty(dpy, win, where, 0L, 2L, False,
			AnyPropertyType, &real_type, &real_format, &n,
                        &extra, &data) == Success) && n) {
	                return (long *)data;
	}
	return NULL;
}
static int __wm_state(const Window win, const Atom where) {
	long *data, state = WithdrawnState;

	data = _g_wm_state(win, where);
	if (data) {
		state = *data;
		XFree(data);
	}
	return state;
}
int wm_state(Client *c) { return __wm_state(c->window, xa_wm_state); }

void send_config(Client *c) {
	XConfigureEvent ce;
	ce.type = ConfigureNotify;
	ce.event = c->window;
	ce.window = c->window;
	ce.x = c->x;
	ce.y = c->y;
	ce.width = c->width;
	ce.height = c->height;
	ce.border_width = 0;
	ce.above = None;
	ce.override_redirect = 0;
	XSendEvent(dpy, c->window, False, StructureNotifyMask, (XEvent *)&ce);
}

void remove_client(Client *c) {
	Client *p;
	#ifdef DEBUG
		bad_debug(1,"remove_client() : Removing...\n");
	#endif
	XGrabServer(dpy);
	XSetErrorHandler(ignore_xerror);

	if (!quitting) {
		#ifdef DEBUG
			bad_debug(1,"setting WithdrawnState\n");
		#endif
		set_wm_state(c, WithdrawnState);
		XRemoveFromSaveSet(dpy, c->window);
	}

	change_gravity(c,(opt_bw*-1));
	XSetWindowBorderWidth(dpy, c->window, opt_bw);
	XReparentWindow(dpy, c->window, c->screen->root, c->x, c->y);
	if (c->parent) XDestroyWindow(dpy, c->parent);

	if (head_client == c) head_client = c->next;
	else for (p = head_client; p && p->next; p = p->next)
		if (p->next == c) p->next = c->next;

	/* removing from fix_next list */
	for (p = head_client; p && p->next; p = p->next)
		if (p->fix_next == c) p->fix_next = c->fix_next;

	if (c->size) XFree(c->size);
	if (current == c)
		current = NULL; /* an enter event should set this up again */
	free(c);

	XSync(dpy, False);
	XSetErrorHandler(handle_xerror);
	XUngrabServer(dpy);

	if (!current) next();
}

void change_gravity(Client *c, int multiplier) {
	int dx = 0, dy = 0;
	int gravity = (c->size->flags & PWinGravity) ?
		c->size->win_gravity : NorthWestGravity;

	switch (gravity) {
		case NorthWestGravity:
		case SouthWestGravity:
		case NorthEastGravity:
		case StaticGravity:
			dx = c->border;
		case NorthGravity:
			dy = c->border; break;
	}

	c->x += multiplier * dx;
	c->y += multiplier * dy;
	#ifdef DEBUG
        	if (dx || dy) {
                	bad_debug(1,"window adjustment of %d,%d for ", multiplier * dx, multiplier * dy);
	        	switch (gravity) {
        	        	case NorthWestGravity: bad_debug(1,"NorthWestGravity\n"); break;
	                	case SouthWestGravity: bad_debug(1,"SouthWestGravity\n"); break;
		                case NorthEastGravity: bad_debug(1,"NorthEastGravity\n"); break;
        		        case NorthGravity: bad_debug(1,"NorthGravity\n"); break;
                		case StaticGravity: bad_debug(1,"StaticGravity\n"); break;
		                default: bad_debug(1,"unhandled gravity %d\n", gravity); break;
		        }
        	}
	#endif
}

void send_wm_delete(Client *c) {
	int i, n, found = 0;
	Atom *protocols;

	if (c) {
		if (XGetWMProtocols(dpy, c->window, &protocols, &n)) {
			for (i=0; i<n; i++) if (protocols[i] == xa_wm_delete) found++;
			XFree(protocols);
		}
		if (found) send_xmessage(c->window, xa_wm_protos, xa_wm_delete);
		else XKillClient(dpy, c->window);
	}
}

static int send_xmessage(Window w, Atom a, long x) {
	XEvent ev;

	ev.type = ClientMessage;
	ev.xclient.window = w;
	ev.xclient.message_type = a;
	ev.xclient.format = 32;
	ev.xclient.data.l[0] = x;
	ev.xclient.data.l[1] = CurrentTime;

	return XSendEvent(dpy, w, False, NoEventMask, &ev);
}

void set_shape(Client *c) {
	int n, order;
	XRectangle *rect;
	rect = XShapeGetRectangles(dpy, c->window, ShapeBounding, &n, &order);
	if (n > 1)
		XShapeCombineShape(dpy, c->parent, ShapeBounding, c->border, c->border, c->window, ShapeBounding, ShapeSet);
	XFree((void*)rect);
}

void client_update_current(Client *c, Client *newcurrent) {
	if (c) {
		if (c->vdesk == STICKY)
			XSetWindowBackground(dpy, c->parent, c == newcurrent ? c->screen->fc.pixel : c->screen->bg.pixel);
		else
			XSetWindowBackground(dpy, c->parent, c == newcurrent ? c->screen->fg.pixel : c->screen->bg.pixel);
		XClearWindow(dpy, c->parent);
	}
	current = newcurrent;
}

void focus_client( Client *c )
{
        int wdesk;
        wdesk = c->vdesk;
        if (wdesk != vdesk && wdesk != STICKY)
           return;
        client_update_current(current, c);
        client_update_current(c, current);
        XSetInputFocus(dpy, c->window, RevertToPointerRoot, CurrentTime);
        grab_button(c->parent, Mod1Mask, AnyButton);
}


syntax highlighted by Code2HTML, v. 0.9.1