/*
 * text.c - adds text image support to beryl.
 * Copyright: (C) 2006 Patrick Niklaus
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 */

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include <cairo-xlib-xrender.h>
#include <pango/pango.h>
#include <pango/pangocairo.h>

#include <beryl.h>

#include "beryl-text.h"

#define TEXT_DISPLAY_OPTION_NUM		0

static int displayPrivateIndex;

typedef struct _TextDisplay
{
	CompOption opt[TEXT_DISPLAY_OPTION_NUM];

	FileToImageProc fileToImage;
} TextDisplay;


#define GET_TEXT_DISPLAY(d)				    \
    ((TextDisplay *) (d)->privates[displayPrivateIndex].ptr)

#define TEXT_DISPLAY(d)			 \
    TextDisplay *td = GET_TEXT_DISPLAY (d)

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


static Bool textFileToImage(CompDisplay *d,
			   const char *path,
			   const char *name,
			   int *width, int *height, int *stride, void **data)
{
	Bool status = FALSE;

	if (path && name &&	strcmp(path, TEXT_ID) == 0)
	{
		cairo_t *cr;
		cairo_surface_t *surface;
		PangoLayout *layout;
		Pixmap pixmap;
		XRenderPictFormat *format;
		CompTextAttrib *text_attrib = (CompTextAttrib*) name; // get it through the name
		int w,h;

		Display *display = d->display;
		Screen *screen = ScreenOfDisplay(display, text_attrib->screen->screenNum);

		if (!screen) {
			printf("ERROR: Couldn't get screen for %d...\n", 
					text_attrib->screen->screenNum);
			return FALSE;
		}

		format = XRenderFindStandardFormat(display, PictStandardARGB32);
		if (!format) {
			printf("ERROR: Couldn't get format...\n");
			return FALSE;
		}

		pixmap = XCreatePixmap(display, text_attrib->screen->root, 1, 1, 32);
		if (!pixmap) {
			printf("ERROR: Couldn't create pixmap...\n");
			return FALSE;
		}

		surface = cairo_xlib_surface_create_with_xrender_format(
						display, pixmap, screen, format, 1, 1);

		if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
			printf("ERROR: Couldn't create surface...\n");
			XFreePixmap(display, pixmap);
			return FALSE;
		}

		cr = cairo_create(surface);
		if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) {
			printf("ERROR: Couldn't create cairo context...\n");
			XFreePixmap(display, pixmap);
			return FALSE;
		}

		// init pango
		layout = pango_cairo_create_layout(cr);
		if (!layout) {
			printf("ERROR: Couldn't create pango layout...\n");
			XFreePixmap(display, pixmap);
			return FALSE;
		}

		PangoFontDescription *font = pango_font_description_new();


		pango_font_description_set_family(font, text_attrib->family);
		pango_font_description_set_absolute_size(font, text_attrib->size * PANGO_SCALE);

		pango_font_description_set_style(font, PANGO_STYLE_NORMAL);

		if (text_attrib->style & TEXT_STYLE_BOLD)
			pango_font_description_set_weight(font, PANGO_WEIGHT_BOLD);

		if (text_attrib->style & TEXT_STYLE_ITALIC)
			pango_font_description_set_style(font, PANGO_STYLE_ITALIC);

		pango_layout_set_font_description(layout, font);

		if (text_attrib->ellipsize)
			pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);

		pango_layout_set_text(layout, text_attrib->text, -1);

		pango_layout_get_pixel_size(layout, &w, &h);

		w = MIN(text_attrib->maxwidth, w);
		h = MIN(text_attrib->maxheight, h);
		
		// update the size of the pango layout
		pango_layout_set_width(layout, w*PANGO_SCALE);

		cairo_surface_destroy(surface);
		cairo_destroy(cr);
		XFreePixmap(display, pixmap);


		pixmap = XCreatePixmap(display, text_attrib->screen->root, w, h, 32);

		surface = cairo_xlib_surface_create_with_xrender_format(display, pixmap,
				  screen, format, w, h);

		if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
			printf("ERROR: Couldn't create surface...\n");
			XFreePixmap(display, pixmap);
			return FALSE;
		}

		cr = cairo_create(surface);
		if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) {
			printf("ERROR: Couldn't create cairo context...\n");
			XFreePixmap(display, pixmap);
			return FALSE;
		}

		pango_cairo_update_layout(cr,layout);

		cairo_save(cr);
		cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
		cairo_paint(cr);
		cairo_restore(cr);

		cairo_set_operator(cr, CAIRO_OPERATOR_OVER);

		cairo_set_source_rgba(cr,
							  text_attrib->color[0] / 65535.0,
							  text_attrib->color[1] / 65535.0,
							  text_attrib->color[2] / 65535.0,
							  text_attrib->color[3] / 65535.0);
		pango_cairo_show_layout(cr, layout);

		g_object_unref(layout);
		cairo_surface_destroy(surface);
		cairo_destroy(cr);
		pango_font_description_free(font);

		*width = w;
		*height = h;
		*data = (void *)pixmap;

		return TRUE;
	}

	TEXT_DISPLAY(d);

	UNWRAP(td, d, fileToImage);
	status = (*d->fileToImage) (d, path, name, width, height, stride, data);
	WRAP(td, d, fileToImage, textFileToImage);

	return status;
}

static void textDisplayInitOptions(TextDisplay *td)
{
	//CompOption *o;
}

static CompOption *textGetDisplayOptions(CompDisplay *display, int *count)
{
	if (display)
	{
		TEXT_DISPLAY(display);

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

		textDisplayInitOptions(td);
		*count = NUM_OPTIONS(td);
		return td->opt;
	}
}

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

	TEXT_DISPLAY(display);

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

	switch (index)
	{
	default:
		break;
	}

	return FALSE;
}

static Bool textInitDisplay(CompPlugin *p, CompDisplay *d)
{
	TextDisplay *td;
	CompScreen *s;

	td = malloc(sizeof(TextDisplay));
	if (!td)
		return FALSE;

	WRAP(td, d, fileToImage, textFileToImage);

	d->privates[displayPrivateIndex].ptr = td;

	textDisplayInitOptions(td);

	for (s = d->screens; s; s = s->next)
		updateDefaultIcon(s);

	return TRUE;
}

static void textFiniDisplay(CompPlugin *p, CompDisplay *d)
{
	CompScreen *s;

	TEXT_DISPLAY(d);

	UNWRAP(td, d, fileToImage);

	for (s = d->screens; s; s = s->next)
		updateDefaultIcon(s);

	free(td);
}


static Bool textInit(CompPlugin *p)
{
	displayPrivateIndex = allocateDisplayPrivateIndex();
	if (displayPrivateIndex < 0)
		return FALSE;

	return TRUE;
}

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

CompPluginFeature textFeatures[]={
	{"TextToPixmap"}
};

CompPluginVTable textVTable = {
	"text",
	N_("Text"),
	N_("Render text to texture plugin"),
	textInit,
	textFini,
	textInitDisplay,
	textFiniDisplay,
	0,
	0,
	0,
	0,
	textGetDisplayOptions,
	textSetDisplayOption,
	0,
	0,
	0,
	0,
	textFeatures,
	sizeof(textFeatures)/sizeof(textFeatures[0]),
	BERYL_ABI_INFO,
	"beryl-plugins-extra",
	"imageformat",
	0,
	0,
	True,
};

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


syntax highlighted by Code2HTML, v. 0.9.1