/**
 * Beryl splash plugin
 *
 * splash.c
 *
 * Copyright : (C) 2006 by Dennis Kasprzyk
 * E-mail    : onestone@beryl-project.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.
 *
 **/

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xrender.h>
#include <beryl.h>
#include <beryl_ipcs.h>

#define GET_SPLASH_DISPLAY(d)                                  \
    ((SplashDisplay *) (d)->privates[displayPrivateIndex].ptr)

#define SPLASH_DISPLAY(d)                      \
    SplashDisplay *sd = GET_SPLASH_DISPLAY (d)

#define GET_SPLASH_SCREEN(s, sd)                                   \
    ((SplashScreen *) (s)->privates[(sd)->screenPrivateIndex].ptr)

#define SPLASH_SCREEN(s)                                                      \
    SplashScreen *ss = GET_SPLASH_SCREEN (s, GET_SPLASH_DISPLAY (s->display))

#define SPLASH_DISPLAY_OPTION_INITIATE                       0
#define SPLASH_DISPLAY_OPTION_LOGO                           1
#define SPLASH_DISPLAY_OPTION_BACKGROUND                     2
#define SPLASH_DISPLAY_OPTION_SATURATION                     3
#define SPLASH_DISPLAY_OPTION_BRIGHTNESS                     4
#define SPLASH_DISPLAY_OPTION_FADE_TIME                      5
#define SPLASH_DISPLAY_OPTION_DISPLAY_TIME                   6
#define SPLASH_DISPLAY_OPTION_DISABLE_UPDATE                 7
#define SPLASH_DISPLAY_OPTION_NUM                            8

#define SPLASH_DISPLAY_OPTION_LOGO_DEFAULT                   IMAGEDIR "/splash_logo.png"
#define SPLASH_DISPLAY_OPTION_BACKGROUND_DEFAULT             IMAGEDIR "/splash_background.png"
#define SPLASH_DISPLAY_OPTION_SATURATION_DEFAULT             50.0
#define SPLASH_DISPLAY_OPTION_BRIGHTNESS_DEFAULT             50.0
#define SPLASH_DISPLAY_OPTION_FADE_TIME_DEFAULT              1.0
#define SPLASH_DISPLAY_OPTION_DISPLAY_TIME_DEFAULT           2.0
#define SPLASH_DISPLAY_OPTION_DISABLE_UPDATE_DEFAULT         TRUE

#define SPLASH_DISPLAY_OPTION_INITIATE_KEY                   "F11"
#define SPLASH_DISPLAY_OPTION_INITIATE_MOD                   CompSuperMask

#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))

static int displayPrivateIndex = 0;

// our texture container
typedef struct _SplashTexture
{
	int width;
	int height;
	unsigned int handle;
} SplashTexture;

typedef struct _SplashDisplay
{
	int screenPrivateIndex;

	CompOption opt[SPLASH_DISPLAY_OPTION_NUM];
} SplashDisplay;

#define MESH_W 16
#define MESH_H 16

typedef struct _SplashScreen
{
	PreparePaintScreenProc preparePaintScreen;
	DonePaintScreenProc donePaintScreen;
	PaintScreenProc paintScreen;

	int fade_in;
	int fade_out;
	int time;

	SplashTexture background;
	CompTexture back_img, logo_img;
	unsigned int backSize[2], logoSize[2];
	Bool hasInit, hasLogo, hasBack;

	float mesh[MESH_W][MESH_H][2];
	float mMove;

	Bool initiate;

	Bool active;
	Bool doUpdate;

	int ipcs_active_atom;

} SplashScreen;

static void genTexture(SplashTexture * tex, int width, int height)
{
	//Enable texturing
	glEnable(GL_TEXTURE_RECTANGLE_ARB);
	//Generate the OpenGL textures
	if (!tex->handle)
		glGenTextures(1, &tex->handle);
	//Write important info into the structure
	tex->width = width;
	tex->height = height;
	//Bind the texture
	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex->handle);
	//Load the parameters
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
					GL_LINEAR);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
					GL_LINEAR);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP);

	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, width, height, 0,
				 GL_RGB, GL_UNSIGNED_BYTE, NULL);
	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
	glDisable(GL_TEXTURE_RECTANGLE_ARB);

}

static void splashPreparePaintScreen(CompScreen * s, int ms)
{
	SPLASH_SCREEN(s);
	SPLASH_DISPLAY(s->display);

	Bool lastShot = FALSE;


	ss->fade_in -= ms;
	if (ss->fade_in < 0)
	{
		ss->time += ss->fade_in;
		ss->fade_in = 0;
		if (ss->time < 0)
		{
			if (ss->fade_out > 0 && ss->fade_out <= ms)
				lastShot = TRUE;
			ss->fade_out += ss->time;
			ss->time = 0;
			if (ss->fade_out < 0)
				ss->fade_out = 0;
		}
	}

	if (ss->initiate)
	{
		ss->fade_in = ss->fade_out =
				sd->opt[SPLASH_DISPLAY_OPTION_FADE_TIME].value.f * 1000.0;
		ss->time =
				sd->opt[SPLASH_DISPLAY_OPTION_DISPLAY_TIME].value.f * 1000.0;
		ss->initiate = FALSE;
	}

	if (ss->fade_in || ss->fade_out || ss->time || lastShot)
	{
		ss->active = TRUE;
		ss->mMove += ms / 500.0;
		if (!ss->hasInit)
		{
			ss->hasInit = TRUE;
			ss->doUpdate = TRUE;
			ss->mMove = 0.0;
			genTexture(&ss->background, s->width, s->height);
			ss->hasBack =
					readImageToTexture(s, &ss->back_img,
									   sd->
									   opt
									   [SPLASH_DISPLAY_OPTION_BACKGROUND].
									   value.s, &ss->backSize[0],
									   &ss->backSize[1]);
			ss->hasLogo =
					readImageToTexture(s, &ss->logo_img,
									   sd->
									   opt
									   [SPLASH_DISPLAY_OPTION_LOGO].
									   value.s, &ss->logoSize[0],
									   &ss->logoSize[1]);
			if (!ss->hasBack)
			{
				ss->hasBack =
						readImageToTexture(s, &ss->back_img,
										   SPLASH_DISPLAY_OPTION_BACKGROUND_DEFAULT,
										   &ss->backSize[0],
										   &ss->backSize[1]);
				if (ss->hasBack)
				{
					fprintf(stderr,
							"[SPLASH]: Could not load splash background image \"%s\" using default!\n",
							sd->
							opt[SPLASH_DISPLAY_OPTION_BACKGROUND].value.s);
				}
			}
			if (!ss->hasLogo)
			{
				ss->hasLogo =
						readImageToTexture(s, &ss->logo_img,
										   SPLASH_DISPLAY_OPTION_LOGO_DEFAULT,
										   &ss->logoSize[0],
										   &ss->logoSize[1]);
				if (ss->hasLogo)
				{
					fprintf(stderr,
							"[SPLASH]: Could not load splash logo image \"%s\" using default!\n",
							sd->opt[SPLASH_DISPLAY_OPTION_LOGO].value.s);
				}
			}
			if (!ss->hasBack)
				fprintf(stderr,
						"[SPLASH]: Could not load splash background image \"%s\" !\n",
						sd->opt[SPLASH_DISPLAY_OPTION_BACKGROUND].value.s);
			if (!ss->hasLogo)
				fprintf(stderr,
						"[SPLASH]: Could not load splash logo image \"%s\" !\n",
						sd->opt[SPLASH_DISPLAY_OPTION_LOGO].value.s);
		}
	}
	else
	{
		ss->active = FALSE;
		ss->doUpdate = TRUE;
		if (ss->hasInit)
		{
			ss->hasInit = FALSE;
			glDeleteTextures(1, &ss->background.handle);
			ss->background.handle = 0;
			if (ss->hasBack)
			{
				glDeleteTextures(1, &ss->back_img.name);
				ss->back_img.name = 0;
				ss->hasBack = FALSE;
			}
			if (ss->hasLogo)
			{
				glDeleteTextures(1, &ss->logo_img.name);
				ss->logo_img.name = 0;
				ss->hasLogo = FALSE;
			}
		}
	}

	if (ss->active)
	{
		IPCS_SetBool(IPCS_OBJECT(s), ss->ipcs_active_atom, TRUE);
	}
	else
	{
		IPCS_SetBool(IPCS_OBJECT(s), ss->ipcs_active_atom, FALSE);
	}

	UNWRAP(ss, s, preparePaintScreen);
	(*s->preparePaintScreen) (s, ms);
	WRAP(ss, s, preparePaintScreen, splashPreparePaintScreen);

}

static void splashDonePaintScreen(CompScreen * s)
{
	SPLASH_SCREEN(s);

	if (ss->fade_in || ss->fade_out || ss->time)
		damageScreen(s);

	UNWRAP(ss, s, donePaintScreen);
	(*s->donePaintScreen) (s);
	WRAP(ss, s, donePaintScreen, splashDonePaintScreen);
}


static Bool
splashPaintScreen(CompScreen * s, const ScreenPaintAttrib * sa,
				  Region region, int output, unsigned int mask)
{

	SPLASH_SCREEN(s);
	SPLASH_DISPLAY(s->display);

	Bool status = TRUE;
	XRectangle outputRect;

	UNWRAP(ss, s, paintScreen);
	status = (*s->paintScreen) (s, sa, region, output, mask);
	WRAP(ss, s, paintScreen, splashPaintScreen);

	screenGetOutputDevRect(s, output, &outputRect);
	int s_x = outputRect.x;
	int s_y = outputRect.y;
	int s_width = outputRect.width;
	int s_height = outputRect.height;

	if (!ss->active)
		return status;

	glPushMatrix();
	glLoadIdentity();
	prepareXCoords(s, output, -DEFAULT_Z_CAMERA);

	float alpha =
			(1.0 -
			 (ss->fade_in /
			  (sd->opt[SPLASH_DISPLAY_OPTION_FADE_TIME].value.f *
			   1000.0))) * (ss->fade_out /
							(sd->opt[SPLASH_DISPLAY_OPTION_FADE_TIME].
							 value.f * 1000.0));
	float darken =
			1.0 - (sd->opt[SPLASH_DISPLAY_OPTION_BRIGHTNESS].value.f / 100.0);
	float sat =
			1.0 - (sd->opt[SPLASH_DISPLAY_OPTION_SATURATION].value.f / 100.0);
	if (sat == 1.0)
		sat = 0.9999;

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	if (sat < 1.0 && s->canDoSaturated && ss->doUpdate)
	{
		glEnable(GL_TEXTURE_RECTANGLE_ARB);
		glBindTexture(GL_TEXTURE_RECTANGLE_ARB, ss->background.handle);
		glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, s_x,
							s->height - s_height - s_y, s_x,
							s->height - s_height - s_y, s_width, s_height);

		GLfloat constant[4];

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_PRIMARY_COLOR);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);

		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);

		glColor4f(1.0f, 1.0f, 1.0f, 0.5f);

		s->activeTexture(GL_TEXTURE1_ARB);

		glEnable(GL_TEXTURE_RECTANGLE_ARB);
		glBindTexture(GL_TEXTURE_RECTANGLE_ARB, ss->background.handle);

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);

		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);

		glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
		glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT);
		glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);

		constant[3] =
				(sd->opt[SPLASH_DISPLAY_OPTION_DISABLE_UPDATE].value.
				 b) ? sat : alpha * sat;

		constant[0] = 0.5f + 0.5f * RED_SATURATION_WEIGHT;
		constant[1] = 0.5f + 0.5f * GREEN_SATURATION_WEIGHT;
		constant[2] = 0.5f + 0.5f * BLUE_SATURATION_WEIGHT;

		glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant);

		glBegin(GL_QUADS);
		glTexCoord2f(s_x, s->height - s_y);
		glVertex2f(s_x, s_y);
		glTexCoord2f(s_x, s->height - s_height - s_y);
		glVertex2f(s_x, s_y + s_height);
		glTexCoord2f(s_x + s_width, s->height - s_height - s_y);
		glVertex2f(s_x + s_width, s_y + s_height);
		glTexCoord2f(s_x + s_width, s->height - s_y);
		glVertex2f(s_x + s_width, s_y);
		glEnd();

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
		glDisable(GL_TEXTURE_RECTANGLE_ARB);
		s->activeTexture(GL_TEXTURE0_ARB);
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
		glDisable(GL_TEXTURE_RECTANGLE_ARB);
	}

	if (darken > 0 && ss->doUpdate)
	{
		glColor4f(0, 0, 0,
				  (sd->opt[SPLASH_DISPLAY_OPTION_DISABLE_UPDATE].
				   value.b) ? darken : alpha * darken);
		glBegin(GL_QUADS);
		glVertex2f(s_x, s_y);
		glVertex2f(s_x, s_y + s_height);
		glVertex2f(s_x + s_width, s_y + s_height);
		glVertex2f(s_x + s_width, s_y);
		glEnd();
	}

	if (sd->opt[SPLASH_DISPLAY_OPTION_DISABLE_UPDATE].value.b)
	{

		// save background
		if (ss->doUpdate)
		{
			if (output + 1 == s->nOutputDev)
				ss->doUpdate = FALSE;
			glEnable(GL_TEXTURE_RECTANGLE_ARB);
			glBindTexture(GL_TEXTURE_RECTANGLE_ARB, ss->background.handle);
			glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0,
								s_x,
								s->height - s_height - s_y,
								s_x,
								s->height - s_height - s_y,
								s_width, s_height);
			glDisable(GL_TEXTURE_RECTANGLE_ARB);
			glPopMatrix();
			glDisable(GL_BLEND);
			glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
			UNWRAP(ss, s, paintScreen);
			status = (*s->paintScreen) (s, sa, region, output, mask);
			WRAP(ss, s, paintScreen, splashPaintScreen);
			glPushMatrix();
			glLoadIdentity();
			glTranslatef(-0.5f, -0.5f, -DEFAULT_Z_CAMERA);
			glScalef(1.0f / s->width, -1.0f / s->height, 1.0f);
			glTranslatef(0.0f, -s->height, 0.0f);
			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		}

		glEnable(GL_TEXTURE_RECTANGLE_ARB);
		glBindTexture(GL_TEXTURE_RECTANGLE_ARB, ss->background.handle);
		glColor4f(1.0, 1.0, 1.0, alpha);
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glBegin(GL_QUADS);
		glTexCoord2f(s_x, s->height - s_y);
		glVertex2f(s_x, s_y);
		glTexCoord2f(s_x, s->height - s_height - s_y);
		glVertex2f(s_x, s_y + s_height);
		glTexCoord2f(s_x + s_width, s->height - s_height - s_y);
		glVertex2f(s_x + s_width, s_y + s_height);
		glTexCoord2f(s_x + s_width, s->height - s_y);
		glVertex2f(s_x + s_width, s_y);
		glEnd();

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
		glDisable(GL_TEXTURE_RECTANGLE_ARB);

	}

	glColor4f(1.0, 1.0, 1.0, alpha);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	if (ss->hasBack)
	{
		int x, y, head;

		for (x = 0; x < MESH_W; x++)
		{
			for (y = 0; y < MESH_H; y++)
			{
				ss->mesh[x][y][0] =
						(x / (MESH_W - 1.0)) +
						(0.02 * sin((y / (MESH_H - 1.0) * 8) + ss->mMove));
				ss->mesh[x][y][1] =
						(y / (MESH_H - 1.0)) +
						(0.02 * sin((ss->mesh[x][y][0] * 8) + ss->mMove));;
			}
		}

		enableTexture(s, &ss->back_img, COMP_TEXTURE_FILTER_GOOD);

		if (s->nOutputDev > 1)
		{
			XRectangle headOutputRect;

			head = screenGetCurrentOutputDev(s);
			screenGetOutputDevRect(s, head, &headOutputRect);
			x = (headOutputRect.width - ss->backSize[0]) / 2;
			y = (headOutputRect.height - ss->backSize[1]) / 2;

			x += headOutputRect.x;
			y += headOutputRect.y;
		}
		else
		{
			x = (s->width - ss->backSize[0]) / 2;
			y = (s->height - ss->backSize[1]) / 2;
		}
		CompMatrix mat = ss->back_img.matrix;

		glTranslatef(x, y, 0);

		float cx1, cx2, cy1, cy2;

		glBegin(GL_QUADS);
		for (x = 0; x < MESH_W - 1; x++)
		{
			for (y = 0; y < MESH_H - 1; y++)
			{
				cx1 = (x / (MESH_W - 1.0)) * ss->backSize[0];
				cx2 = ((x + 1) / (MESH_W - 1.0)) * ss->backSize[0];
				cy1 = (y / (MESH_H - 1.0)) * ss->backSize[1];
				cy2 = ((y + 1) / (MESH_H - 1.0)) * ss->backSize[1];

				glTexCoord2f(COMP_TEX_COORD_X(&mat, cx1),
							 COMP_TEX_COORD_Y(&mat, cy1));
				glVertex2f(ss->mesh[x][y][0] *
						   ss->backSize[0],
						   ss->mesh[x][y][1] * ss->backSize[1]);
				glTexCoord2f(COMP_TEX_COORD_X(&mat, cx1),
							 COMP_TEX_COORD_Y(&mat, cy2));
				glVertex2f(ss->mesh[x][y + 1][0] *
						   ss->backSize[0],
						   ss->mesh[x][y + 1][1] * ss->backSize[1]);
				glTexCoord2f(COMP_TEX_COORD_X(&mat, cx2),
							 COMP_TEX_COORD_Y(&mat, cy2));
				glVertex2f(ss->mesh[x + 1][y + 1][0] *
						   ss->backSize[0],
						   ss->mesh[x + 1][y + 1][1] * ss->backSize[1]);
				glTexCoord2f(COMP_TEX_COORD_X(&mat, cx2),
							 COMP_TEX_COORD_Y(&mat, cy1));
				glVertex2f(ss->mesh[x + 1][y][0] *
						   ss->backSize[0],
						   ss->mesh[x + 1][y][1] * ss->backSize[1]);
			}
		}
		glEnd();

		if (s->nOutputDev > 1)
		{
			XRectangle headOutputRect;

			head = screenGetCurrentOutputDev(s);
			screenGetOutputDevRect(s, head, &headOutputRect);
			x = (headOutputRect.width - ss->backSize[0]) / 2;
			y = (headOutputRect.height - ss->backSize[1]) / 2;

			x += headOutputRect.x;
			y += headOutputRect.y;
		}
		else
		{
			x = (s->width - ss->backSize[0]) / 2;
			y = (s->height - ss->backSize[1]) / 2;
		}
		glTranslatef(-x, -y, 0);

		disableTexture(s, &ss->back_img);
	}

	if (ss->hasLogo)
	{
		enableTexture(s, &ss->logo_img, COMP_TEXTURE_FILTER_GOOD);
		int x, y, head;

		if (s->nOutputDev > 1)
		{
			XRectangle headOutputRect;

			head = screenGetCurrentOutputDev(s);
			screenGetOutputDevRect(s, head, &headOutputRect);
			x = (headOutputRect.width - ss->logoSize[0]) / 2;
			y = (headOutputRect.height - ss->logoSize[1]) / 2;

			x += headOutputRect.x;
			y += headOutputRect.y;
		}
		else
		{
			x = (s->width - ss->logoSize[0]) / 2;
			y = (s->height - ss->logoSize[1]) / 2;
		}

		CompMatrix mat = ss->logo_img.matrix;

		glTranslatef(x, y, 0);

		glBegin(GL_QUADS);
		glTexCoord2f(COMP_TEX_COORD_X(&mat, 0), COMP_TEX_COORD_Y(&mat, 0));
		glVertex2f(0, 0);
		glTexCoord2f(COMP_TEX_COORD_X(&mat, 0),
					 COMP_TEX_COORD_Y(&mat, ss->logoSize[1]));
		glVertex2f(0, ss->logoSize[1]);
		glTexCoord2f(COMP_TEX_COORD_X(&mat, ss->logoSize[0]),
					 COMP_TEX_COORD_Y(&mat, ss->logoSize[1]));
		glVertex2f(ss->logoSize[0], ss->logoSize[1]);
		glTexCoord2f(COMP_TEX_COORD_X(&mat, ss->logoSize[0]),
					 COMP_TEX_COORD_Y(&mat, 0));
		glVertex2f(ss->logoSize[0], 0);
		glEnd();

		glTranslatef(-x, -y, 0);

		disableTexture(s, &ss->logo_img);
	}

	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	glDisable(GL_BLEND);
	glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
	glPopMatrix();
	return status;
}

static Bool splashInitScreen(CompPlugin * p, CompScreen * s)
{

	SPLASH_DISPLAY(s->display);

	SplashScreen *ss = (SplashScreen *) calloc(1, sizeof(SplashScreen));

	s->privates[sd->screenPrivateIndex].ptr = ss;

	WRAP(ss, s, paintScreen, splashPaintScreen);
	WRAP(ss, s, preparePaintScreen, splashPreparePaintScreen);
	WRAP(ss, s, donePaintScreen, splashDonePaintScreen);

	addScreenAction(s, &sd->opt[SPLASH_DISPLAY_OPTION_INITIATE].value.action);

	ss->initiate = FALSE;

	ss->ipcs_active_atom =
			IPCS_GetAtom(IPCS_OBJECT(s), IPCS_BOOL, "SPLASH_IS_ACTIVE", TRUE);

	return TRUE;
}


static void splashFiniScreen(CompPlugin * p, CompScreen * s)
{

	SPLASH_SCREEN(s);
	SPLASH_DISPLAY(s->display);

	//Restore the original function
	UNWRAP(ss, s, paintScreen);
	UNWRAP(ss, s, preparePaintScreen);
	UNWRAP(ss, s, donePaintScreen);

	removeScreenAction(s,
					   &sd->opt[SPLASH_DISPLAY_OPTION_INITIATE].value.action);

	//Free the pointer
	free(ss);
}

static Bool
splashSetDisplayOption(CompDisplay * display, char *name,
					   CompOptionValue * value)
{
	CompOption *o;
	int index;

	SPLASH_DISPLAY(display);

	o = compFindOption(sd->opt, NUM_OPTIONS(sd), name, &index);
	if (!o)
		return FALSE;

	switch (index)
	{
	case SPLASH_DISPLAY_OPTION_INITIATE:
		if (setDisplayAction(display, o, value))
			return TRUE;
		break;
	case SPLASH_DISPLAY_OPTION_SATURATION:
	case SPLASH_DISPLAY_OPTION_BRIGHTNESS:
	case SPLASH_DISPLAY_OPTION_FADE_TIME:
	case SPLASH_DISPLAY_OPTION_DISPLAY_TIME:
		if (compSetFloatOption(o, value))
			return TRUE;
		break;
	case SPLASH_DISPLAY_OPTION_LOGO:
	case SPLASH_DISPLAY_OPTION_BACKGROUND:
		if (compSetStringOption(o, value))
			return TRUE;
		break;
	case SPLASH_DISPLAY_OPTION_DISABLE_UPDATE:
		if (compSetBoolOption(o, value))
			return TRUE;
		break;
	default:
		break;
	}

	return FALSE;
}

static Bool
splashInitiate(CompDisplay * d, CompAction * ac, CompActionState state,
			   CompOption * option, int nOption)
{
	CompScreen *s;

	s = findScreenAtDisplay(d, getIntOptionNamed(option, nOption, "root", 0));
	if (s)
	{
		SPLASH_SCREEN(s);
		ss->initiate = TRUE;
		damageScreen(s);
	}
	return FALSE;
}

static void splashDisplayInitOptions(SplashDisplay * sd)
{
	CompOption *o;

	o = &sd->opt[SPLASH_DISPLAY_OPTION_INITIATE];
	o->advanced = False;
	o->name = "initiate";
	o->group = N_("Binding");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Initiate Splash");
	o->longDesc = N_("Start Splash.");
	o->type = CompOptionTypeAction;
	o->value.action.initiate = splashInitiate;
	o->value.action.terminate = 0;
	o->value.action.bell = FALSE;
	o->value.action.edgeMask = 0;
	o->value.action.type = 0;
	//o->value.action.type |= CompBindingTypeKey;
	o->value.action.state = CompActionStateInitKey;
	o->value.action.key.modifiers = SPLASH_DISPLAY_OPTION_INITIATE_MOD;
	o->value.action.key.keysym =
			XStringToKeysym(SPLASH_DISPLAY_OPTION_INITIATE_KEY);

	o = &sd->opt[SPLASH_DISPLAY_OPTION_BACKGROUND];
	o->advanced = False;
	o->name = "background_file";
	o->group = N_("Images");
	o->subGroup = N_("");
	o->displayHints = "file;image;pngonly;";
	o->shortDesc = N_("Background File");
	o->longDesc = N_("Background image File.");
	o->type = CompOptionTypeString;
	o->value.s = strdup(SPLASH_DISPLAY_OPTION_BACKGROUND_DEFAULT);
	o->rest.s.string = 0;
	o->rest.s.nString = 0;

	o = &sd->opt[SPLASH_DISPLAY_OPTION_LOGO];
	o->advanced = False;
	o->name = "logo_file";
	o->group = N_("Images");
	o->subGroup = N_("");
	o->displayHints = "file;image;pngonly;";
	o->shortDesc = N_("Logo File");
	o->longDesc = N_("Logo image File.");
	o->type = CompOptionTypeString;
	o->value.s = strdup(SPLASH_DISPLAY_OPTION_LOGO_DEFAULT);
	o->rest.s.string = 0;
	o->rest.s.nString = 0;

	o = &sd->opt[SPLASH_DISPLAY_OPTION_FADE_TIME];
	o->advanced = False;
	o->name = "fade_time";
	o->group = N_("Appearance");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Fade Time");
	o->longDesc = N_("Fade In/Out Time.");
	o->type = CompOptionTypeFloat;
	o->value.f = SPLASH_DISPLAY_OPTION_FADE_TIME_DEFAULT;
	o->rest.f.min = 0.0;
	o->rest.f.max = 100.0;
	o->rest.f.precision = 0.1;

	o = &sd->opt[SPLASH_DISPLAY_OPTION_DISPLAY_TIME];
	o->advanced = False;
	o->name = "display_time";
	o->group = N_("Appearance");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Display Time");
	o->longDesc = N_("Display Time.");
	o->type = CompOptionTypeFloat;
	o->value.f = SPLASH_DISPLAY_OPTION_DISPLAY_TIME_DEFAULT;
	o->rest.f.min = 0.0;
	o->rest.f.max = 100.0;
	o->rest.f.precision = 0.1;

	o = &sd->opt[SPLASH_DISPLAY_OPTION_SATURATION];
	o->advanced = False;
	o->name = "saturation";
	o->group = N_("Appearance");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Saturation");
	o->longDesc = N_("Background Saturation.");
	o->type = CompOptionTypeFloat;
	o->value.f = SPLASH_DISPLAY_OPTION_SATURATION_DEFAULT;
	o->rest.f.min = 0.0;
	o->rest.f.max = 100.0;
	o->rest.f.precision = 0.1;

	o = &sd->opt[SPLASH_DISPLAY_OPTION_BRIGHTNESS];
	o->advanced = False;
	o->name = "brightness";
	o->group = N_("Appearance");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Brightness");
	o->longDesc = N_("Background Brightness.");
	o->type = CompOptionTypeFloat;
	o->value.f = SPLASH_DISPLAY_OPTION_BRIGHTNESS_DEFAULT;
	o->rest.f.min = 0.0;
	o->rest.f.max = 100.0;
	o->rest.f.precision = 0.1;

	o = &sd->opt[SPLASH_DISPLAY_OPTION_DISABLE_UPDATE];
	o->advanced = False;
	o->name = "disable_update";
	o->group = N_("Behaviour");
	o->subGroup = N_("");
	o->displayHints = "";
	o->shortDesc = N_("Disable Updates");
	o->longDesc = N_("Disable Updates of Background.");
	o->type = CompOptionTypeBool;
	o->value.b = SPLASH_DISPLAY_OPTION_DISABLE_UPDATE_DEFAULT;
}

static CompOption *splashGetDisplayOptions(CompDisplay * display, int *count)
{
	if (display)
	{
		SPLASH_DISPLAY(display);

		*count = NUM_OPTIONS(sd);
		return sd->opt;
	}
	else
	{
		SplashDisplay *sd = malloc(sizeof(SplashDisplay));

		splashDisplayInitOptions(sd);
		*count = NUM_OPTIONS(sd);
		return sd->opt;
	}
}



static Bool splashInitDisplay(CompPlugin * p, CompDisplay * d)
{
	//Generate a splash display
	SplashDisplay *sd = (SplashDisplay *) malloc(sizeof(SplashDisplay));

	//Allocate a private index
	sd->screenPrivateIndex = allocateScreenPrivateIndex(d);
	//Check if its valid
	if (sd->screenPrivateIndex < 0)
	{
		//Its invalid so free memory and return
		free(sd);
		return FALSE;
	}
	splashDisplayInitOptions(sd);
	d->privates[displayPrivateIndex].ptr = sd;
	return TRUE;
}

static void splashFiniDisplay(CompPlugin * p, CompDisplay * d)
{
	SPLASH_DISPLAY(d);
	//Free the private index
	freeScreenPrivateIndex(d, sd->screenPrivateIndex);
	//Free the pointer
	free(sd);
}



static Bool splashInit(CompPlugin * p)
{
	displayPrivateIndex = allocateDisplayPrivateIndex();

	if (displayPrivateIndex < 0)
		return FALSE;

	return TRUE;
}

static void splashFini(CompPlugin * p)
{
	if (displayPrivateIndex >= 0)
		freeDisplayPrivateIndex(displayPrivateIndex);
}

CompPluginDep splashDeps[] = {
	{CompPluginRuleAfterCategory, "imageloader"}
	,
	{CompPluginRuleEnd, ""}
};

CompPluginVTable splashVTable = {
	"splash",
	N_("Splash"),
	N_("A simple splash plugin"),
	splashInit,
	splashFini,
	splashInitDisplay,
	splashFiniDisplay,
	splashInitScreen,
	splashFiniScreen,
	0,
	0,
	splashGetDisplayOptions,
	splashSetDisplayOption,
	0,
	0,
	splashDeps,
	sizeof(splashDeps) / sizeof(splashDeps[0]),
	0,
	0,
	BERYL_ABI_INFO,
	"beryl-plugins",
	"misc",
	0,
	0,
	False,
};

CompPluginVTable *getCompPluginInfo(void)
{
	return &splashVTable;
}


syntax highlighted by Code2HTML, v. 0.9.1