/* Skippy - Seduces Kids Into Perversion
 *
 * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
 *
 * 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 "skippy.h"

static Imlib_Color_Modifier
create_modifier(MainWin *mw, dlist *config, const char *item, const char *d_bright, const char *d_tint, const char *d_opacity)
{
	const char *tmp;
	XColor exact_color, screen_color;
	int i, alpha_v;
	DATA8 red[256], green[256], blue[256], alpha[256];
	Imlib_Color_Modifier modifier = imlib_create_color_modifier();
	
	imlib_context_set_color_modifier(modifier);
	
	tmp = config_get(config, item, "brightness", d_bright);
	imlib_modify_color_modifier_brightness(strtod(tmp, NULL));
	
	imlib_get_color_modifier_tables(red, green, blue, alpha);
	
	tmp = config_get(config, item, "tint", d_tint);
	if(XLookupColor(mw->dpy, mw->colormap, tmp, &exact_color, &screen_color) != 0)
	{
		
		double red_f = (double)exact_color.red / 65536.0,
		       green_f = (double)exact_color.green / 65536.0,
		       blue_f = (double)exact_color.blue / 65536.0;
		
		for(i = 0; i < 256; i++)
		{
			red[i] = (double)red[i] * red_f;
			green[i] = (double)green[i] * green_f;
			blue[i] = (double)blue[i] * blue_f;
		}
	} else
		fprintf(stderr, "WARNING: Couldn't look up tint color'%s'.\n", tmp);
	
	tmp = config_get(config, item, "opacity", d_opacity);
	alpha_v = strtol(tmp, 0, 10);
	if(alpha_v != 255)
	{
		for(i = 0; i < 256; i++)
			alpha[i] = alpha_v;
	}
	
	imlib_set_color_modifier_tables(red, green, blue, alpha);
	
	imlib_context_set_color_modifier(0);
	
	return modifier;
}

MainWin *
mainwin_create(Display *dpy, dlist *config)
{
	const char *tmp;
	XColor screen_color, exact_color;
	XSetWindowAttributes wattr;
	XWindowAttributes rootattr;
	unsigned long valuemask = CWEventMask;
#ifdef XINERAMA
	int event_base, error_base;
#endif /* XINERAMA */
	
	MainWin *mw = (MainWin *)malloc(sizeof(MainWin));
	
	mw->screen = DefaultScreen(dpy);
	mw->visual = imlib_get_best_visual(dpy, mw->screen, &mw->depth);
	mw->colormap = DefaultColormap(dpy, mw->screen);
	mw->root = RootWindow(dpy, mw->screen);
	mw->background = 0;
	mw->bg_pixmap = None;
#ifdef XINERAMA
	mw->xin_info = mw->xin_active = 0;
	mw->xin_screens = 0;
#endif /* XINERAMA */
	mw->x = mw->y = 0;
	
	mw->no_free_color = 0;
	mw->pressed = mw->focus = 0;
	mw->tooltip = 0;
	mw->cod = 0;
	mw->cm_normal = mw->cm_highlight = 0;
	mw->key_up = XKeysymToKeycode(dpy, XK_Up);
	mw->key_down = XKeysymToKeycode(dpy, XK_Down);
	mw->key_left = XKeysymToKeycode(dpy, XK_Left);
	mw->key_right = XKeysymToKeycode(dpy, XK_Right);
	mw->key_enter = XKeysymToKeycode(dpy, XK_Return);
	mw->key_space = XKeysymToKeycode(dpy, XK_space);
	mw->key_escape = XKeysymToKeycode(dpy, XK_Escape);
	mw->key_q = XKeysymToKeycode(dpy, XK_q);
	
	XGetWindowAttributes(dpy, mw->root, &rootattr);
	mw->width = rootattr.width;
	mw->height = rootattr.height;
	
	mw->dpy = dpy;
	
	valuemask |= CWBackPixel;
	wattr.background_pixel = BlackPixel(dpy, mw->screen);
	
	wattr.event_mask = VisibilityChangeMask |
	                   ButtonReleaseMask;
	
	mw->window = XCreateWindow(dpy, mw->root, 0, 0, mw->width, mw->height, 0,
	                           CopyFromParent, InputOutput, CopyFromParent,
	                           valuemask, &wattr);
	if(mw->window == None) {
		free(mw);
		return 0;
	}

#ifdef XINERAMA
# ifdef DEBUG
	fprintf(stderr, "--> checking for Xinerama extension... ");
# endif /* DEBUG */
	if(XineramaQueryExtension(dpy, &event_base, &error_base))
	{
# ifdef DEBUG
	    fprintf(stderr, "yes\n--> checking if Xinerama is enabled... ");
# endif /* DEBUG */
	    if(XineramaIsActive(dpy))
	    {
# ifdef DEBUG
	        fprintf(stderr, "yes\n--> fetching Xinerama info... ");
# endif /* DEBUG */
	        mw->xin_info = XineramaQueryScreens(mw->dpy, &mw->xin_screens);
# ifdef DEBUG	        
		fprintf(stderr, "done (%i screens)\n", mw->xin_screens);
# endif /* DEBUG */
	    }
# ifdef DEBUG
	    else
	        fprintf(stderr, "no\n");
# endif /* DEBUG */
	}
# ifdef DEBUG
	else
	    fprintf(stderr, "no\n");
# endif /* DEBUG */
#endif /* XINERAMA */
	
	tmp = config_get(config, "normal", "border", "black");
	if(! XAllocNamedColor(mw->dpy, mw->colormap, tmp, &screen_color, &exact_color))
	{
		fprintf(stderr, "WARNING: Invalid color '%s', reverting to black.\n", tmp);
		BORDER_COLOR(mw) = BlackPixel(mw->dpy, mw->screen);
		mw->no_free_color = 1;
	} else
		BORDER_COLOR(mw) = screen_color.pixel;
	
	tmp = config_get(config, "highlight", "border", "#d0d0ff");
	if(! XAllocNamedColor(mw->dpy, mw->colormap, tmp, &screen_color, &exact_color))
	{
		fprintf(stderr, "WARNING: Invalid color '%s', reverting to white.\n", tmp);
		HIGHLIGHT_COLOR(mw) = WhitePixel(mw->dpy, mw->screen);
		mw->no_free_color |= 2;
	} else
		HIGHLIGHT_COLOR(mw) = screen_color.pixel;
	
	tmp = config_get(config, "general", "distance", "50");
	DISTANCE(mw) = MAX(1, strtol(tmp, 0, 10));
	
	if(! strcasecmp(config_get(config, "tooltip", "show", "true"), "true"))
		mw->tooltip = tooltip_create(mw, config);
	
	mw->cm_normal = create_modifier(mw, config, "normal", "0.0", "white", "200");
	mw->cm_highlight = create_modifier(mw, config, "highlight", "0.05", "#d0d0ff", "255");
	
	return mw;
}

static
void mainwin_update_bg_pixmap(MainWin *mw)
{
	Imlib_Image tmp;
	XSetWindowAttributes wattr;
	Pixmap dummy;
	
	if(mw->bg_pixmap != None)
		imlib_free_pixmap_and_mask(mw->bg_pixmap);
	
	imlib_context_set_image(mw->background);
	tmp = imlib_create_cropped_image(mw->x, mw->y, mw->width, mw->height);
	imlib_context_set_image(tmp);
	
	imlib_context_set_drawable(mw->window);
	imlib_render_pixmaps_for_whole_image(&mw->bg_pixmap, &dummy);
	
	wattr.background_pixmap = mw->bg_pixmap;
	XChangeWindowAttributes(mw->dpy, mw->window, CWBackPixmap, &wattr);
	XClearArea(mw->dpy, mw->window, 0, 0, 0, 0, False);
	
	imlib_free_image();
}

void
mainwin_update_background(MainWin *mw)
{
	Pixmap dummy = wm_get_root_pmap(mw->dpy);
	Window dummy_root;
	int x, y;
	unsigned int root_w, root_h, border_width, depth;
	
	XGetGeometry(mw->dpy, mw->root, &dummy_root, &x, &y, &root_w, &root_h, &border_width, &depth);
	
	if(mw->background != 0)
	{
		imlib_context_set_image(mw->background);
		imlib_free_image();
	}
	
	if(dummy != None)
	{
		unsigned int width, height;
		
		mw->background = imlib_create_image(root_w, root_h);
		imlib_context_set_image(mw->background);
		
		XGetGeometry(mw->dpy, dummy, &dummy_root, &x, &y, &width, &height, &border_width, &depth);
		imlib_context_set_drawable(dummy);
		imlib_copy_drawable_to_image(0, 0, 0, width, height, 0, 0, 1);
		
		for(x = 1; x < (int)ceil((double)root_w / width); ++x)
			imlib_image_copy_rect(0, 0, width, height, x * width, 0);
		for(y = 1; y < (int)ceil((double)root_h / height); ++y)
			imlib_image_copy_rect(0, 0, root_w, height, 0, y * height);
	}
	else
	{
		mw->background = imlib_create_image(root_w, root_h);
		imlib_context_set_image(mw->background);
		imlib_image_clear();
	}
	
	mainwin_update_bg_pixmap(mw);
	REDUCE(clientwin_render((ClientWin*)iter->data), mw->cod);
}

void
mainwin_update(MainWin *mw)
{
#ifdef XINERAMA
	XineramaScreenInfo *iter;
	int i;
	Window dummy_w;
	int root_x, root_y, dummy_i;
	unsigned int dummy_u;
	
	if(! mw->xin_info || ! mw->xin_screens)
		return;
	
# ifdef DEBUG
	fprintf(stderr, "--> querying pointer... ");
# endif /* DEBUG */
	XQueryPointer(mw->dpy, mw->root, &dummy_w, &dummy_w, &root_x, &root_y, &dummy_i, &dummy_i, &dummy_u);
# ifdef DEBUG	
	fprintf(stderr, "+%i+%i\n", root_x, root_y);
	
	fprintf(stderr, "--> figuring out which screen we're on... ");
# endif /* DEBUG */
	iter = mw->xin_info;
	for(i = 0; i < mw->xin_screens; ++i)
	{
		if(root_x >= iter->x_org && root_x < iter->x_org + iter->width &&
		   root_y >= iter->y_org && root_y < iter->y_org + iter->height)
		{
# ifdef DEBUG
			fprintf(stderr, "screen %i %ix%i+%i+%i\n", iter->screen_number, iter->width, iter->height, iter->x_org, iter->y_org);
# endif /* DEBUG */
			break;
		}
		iter++;
	}
	if(i == mw->xin_screens)
	{
# ifdef DEBUG 
		fprintf(stderr, "unknown\n");
# endif /* DEBUG */
		return;
	}
	mw->x = iter->x_org;
	mw->y = iter->y_org;
	mw->width = iter->width;
	mw->height = iter->height;
	XMoveResizeWindow(mw->dpy, mw->window, iter->x_org, iter->y_org, mw->width, mw->height);
	mw->xin_active = iter;
#endif /* XINERAMA */
	
	mainwin_update_bg_pixmap(mw);
}

void
mainwin_map(MainWin *mw)
{
	wm_set_fullscreen(mw->dpy, mw->window, mw->x, mw->y, mw->width, mw->height);
	mw->pressed = 0;
	XMapWindow(mw->dpy, mw->window);
	XRaiseWindow(mw->dpy, mw->window);
}

void
mainwin_unmap(MainWin *mw)
{
	if(mw->tooltip)
		tooltip_unmap(mw->tooltip);
	XUnmapWindow(mw->dpy, mw->window);
}

void
mainwin_destroy(MainWin *mw)
{
	if(mw->tooltip)
		tooltip_destroy(mw->tooltip);
	
	if(! mw->no_free_color)
		XFreeColors(mw->dpy, mw->colormap, mw->pixels, 2, 0);
	else {
		if(! (mw->no_free_color & 1))
			XFreeColors(mw->dpy, mw->colormap, &BORDER_COLOR(mw), 1, 0);
		if(! (mw->no_free_color & 2))
			XFreeColors(mw->dpy, mw->colormap, &HIGHLIGHT_COLOR(mw), 1, 0);
	}
	
	if(mw->cm_highlight)
	{
		imlib_context_set_color_modifier(mw->cm_highlight);
		imlib_free_color_modifier();
	}
	
	if(mw->cm_normal)
	{
		imlib_context_set_color_modifier(mw->cm_normal);
		imlib_free_color_modifier();
	}
	
	if(mw->background)
	{
		imlib_context_set_image(mw->background);
		imlib_free_image();
	}
	
	if(mw->bg_pixmap != None)
		imlib_free_pixmap_and_mask(mw->bg_pixmap);
	
	XDestroyWindow(mw->dpy, mw->window);
	
#ifdef XINERAMA
	if(mw->xin_info)
		XFree(mw->xin_info);
#endif /* XINERAMA */
	
	free(mw);
}

int
mainwin_handle(MainWin *mw, XEvent *ev)
{
	switch(ev->type)
	{
	case KeyPress:
		if(ev->xkey.keycode == XKeysymToKeycode(mw->dpy, XK_q))
			return 2;
		break;
	case ButtonRelease:
		return 1;
		break;
	case VisibilityNotify:
		if(ev->xvisibility.state && mw->focus)
		{
			XSetInputFocus(mw->dpy, mw->focus->mini.window, RevertToParent, CurrentTime);
			mw->focus = 0;
		}
		break;
	default:
		;
	}
	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1