/*--------------------------------*-C-*---------------------------------*
 * File:	graphics.c
 *----------------------------------------------------------------------*
 * Copyright (C) 1997,1998 mj olesen <olesen@me.queensu.ca>
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*
 * Originally written:
 *     1994      Rob Nation <nation@rocket.sanders.lockheed.com>
 * Modifications: 
 *     1997      Raul Garcia Garcia <rgg@tid.es>
 *     1997,1998 mj olesen <olesen@me.QueensU.CA>
 *----------------------------------------------------------------------*/

#ifndef lint
static const char rcsid[] = "$Id: graphics.c,v 1.2 2004/11/16 19:47:58 sasha Exp $";
#endif

#include "rxvt.h"		/* NECESSARY */
#include <X11/cursorfont.h>

/* commands:
 * 'C' = Clear
 * 'F' = Fill
 * 'G' = Geometry
 * 'L' = Line
 * 'P' = Points
 * 'T' = Text
 * 'W' = Window
 */

#ifndef GRX_SCALE
# define GRX_SCALE	10000
#endif

#ifdef RXVT_GRAPHICS
static int      graphics_up = 0;
static grwin_t *gr_root = NULL;
#endif

/*----------------------------------------------------------------------*
 * local functions
 */
/* ARGSUSED */
/* PROTO */
void
Gr_NewWindow(int nargs, int args[])
{
#ifdef RXVT_GRAPHICS
    int             x, y;
    unsigned int    w, h;
    Window          win;
    grwin_t        *grwin;
    Cursor          cursor;

    if (nargs != 4) {
	print_error("NewWindow: 4 args needed, got %d\n", nargs);
	return;
    }
    x = args[0] * TermWin.width / GRX_SCALE + TermWin_internalBorder;
    y = args[1] * TermWin.height / GRX_SCALE + TermWin_internalBorder;
    w = args[2] * TermWin.width / GRX_SCALE;
    h = args[3] * TermWin.height / GRX_SCALE;
    win = XCreateSimpleWindow(Xdisplay, TermWin.vt,
			      x, y, w, h,
			      0,
			      PixColors[Color_fg],
			      PixColors[Color_bg]);

    cursor = XCreateFontCursor(Xdisplay, XC_crosshair);
    XDefineCursor(Xdisplay, win, cursor);
    XMapWindow(Xdisplay, win);
    XSelectInput(Xdisplay, win, ExposureMask);

    grwin = (grwin_t *) MALLOC(sizeof(grwin_t));
    grwin->win = win;
    grwin->x = x;
    grwin->y = y;
    grwin->w = w;
    grwin->h = h;
    grwin->screen = 0;
    grwin->prev = NULL;
    grwin->next = gr_root;
    if (grwin->next)
	grwin->next->prev = grwin;
    gr_root = grwin;
    grwin->graphics = NULL;
    graphics_up++;

    tt_printf("\033W%ld\n", (long)grwin->win);
#endif
}

/* ARGSUSED */
/* PROTO */
void
Gr_ClearWindow(grwin_t * grwin)
{
#ifdef RXVT_GRAPHICS
    grcmd_t        *cmd, *next;

    for (cmd = grwin->graphics; cmd != NULL; cmd = next) {
	next = cmd->next;
	free(cmd->coords);
	if (cmd->text != NULL)
	    free(cmd->text);
	free(cmd);
    }
    grwin->graphics = NULL;
    XClearWindow(Xdisplay, grwin->win);
#endif
}

/*
 * arg [0] = x
 * arg [1] = y
 * arg [2] = alignment
 * arg [3] = strlen (text)
 */
/* ARGSUSED */
/* PROTO */
void
Gr_Text(grwin_t * grwin, grcmd_t * data)
{
#ifdef RXVT_GRAPHICS
    int             x, y, align;

    if (data->ncoords < 4 || data->text == NULL || *(data->text) == '\0')
	return;

    x = data->coords[0] * grwin->w / GRX_SCALE;
    y = data->coords[1] * grwin->h / GRX_SCALE;
    align = data->coords[2];

    if (align & RIGHT_TEXT)
	x -= XTextWidth(TermWin.font, data->text, data->coords[3]);
    else if (align & HCENTER_TEXT)
	x -= (XTextWidth(TermWin.font, data->text, data->coords[3]) >> 1);

    if (align & TOP_TEXT)
	y += TermWin.font->ascent;
    else if (align & BOTTOM_TEXT)
	y -= TermWin.font->descent;

    if (align & VCENTER_TEXT)
	y -= TermWin.font->descent
	     + ((TermWin.font->ascent + TermWin.font->descent) >> 1);
    if (align & VCAPS_CENTER_TEXT)
	y += (TermWin.font->ascent >> 1);

    XPMClearArea(Xdisplay, grwin->win, x, y, Width2Pixel(data->coords[3]),
		 Height2Pixel(1), 0);
    XDrawString(Xdisplay, grwin->win, TermWin.gc, x, y,
		data->text, data->coords[3]);
#endif
}

/* ARGSUSED */
/* PROTO */
void
Gr_Geometry(grwin_t * grwin, grcmd_t * data)
{
#ifdef RXVT_GRAPHICS
    if (grwin)
	tt_printf("\033G%ld %d %d %u %u %d %d %ld %ld %d\n",
		  (long)grwin->win,
		  grwin->x, grwin->y, grwin->w, grwin->h,
		  TermWin.fwidth,
		  TermWin.fheight,
		  (long)GRX_SCALE * TermWin.fwidth / grwin->w,
		  (long)GRX_SCALE * TermWin.fheight / grwin->h,
		  Xdepth);
    else			/* rxvt terminal window size */
	tt_printf("\033G0 0 0 %d %d %d %d %ld %ld %d\n",
		  TermWin.width - 2 * TermWin_internalBorder,
		  TermWin.height - 2 * TermWin_internalBorder,
		  TermWin.fwidth,
		  TermWin.fheight,
		  (long)GRX_SCALE * TermWin.fwidth
		        / (TermWin.width - 2 * TermWin_internalBorder),
		  (long)GRX_SCALE * TermWin.fheight
		        / (TermWin.height - 2 * TermWin_internalBorder),
		  Xdepth);
#endif
}

/* ARGSUSED */
/* PROTO */
void
Gr_DestroyWindow(grwin_t * grwin)
{
#ifdef RXVT_GRAPHICS
    grcmd_t        *cmd, *next;

    if (grwin == NULL)
	return;

    for (cmd = grwin->graphics; cmd; cmd = next) {
	next = cmd->next;
	free(cmd->coords);
	if (cmd->text != NULL)
	    free(cmd->text);
	free(cmd);
    }

    XDestroyWindow(Xdisplay, grwin->win);
    if (grwin->next != NULL)
	grwin->next->prev = grwin->prev;
    if (grwin->prev != NULL)
	grwin->prev->next = grwin->next;
    else
	gr_root = grwin->next;
    free(grwin);

    graphics_up--;
#endif
}

/* ARGSUSED */
/* PROTO */
void
Gr_Dispatch(grwin_t * grwin, grcmd_t * data)
{
#ifdef RXVT_GRAPHICS
    int             i, n;
    union {
	XPoint          pt[NGRX_PTS / 2];
	XRectangle      rect[NGRX_PTS / 4];
    } xdata;

    if (data->color != Color_fg) {
	XGCValues       gcv;

	gcv.foreground = PixColors[data->color];
	XChangeGC(Xdisplay, TermWin.gc, GCForeground, &gcv);
    }
    if (grwin)
	switch (data->cmd) {
	case 'L':
	    if (data->ncoords > 3) {
		for (n = i = 0; i < data->ncoords; i += 2, n++) {
		    xdata.pt[n].x = data->coords[i] * grwin->w / GRX_SCALE;
		    xdata.pt[n].y = data->coords[i + 1] * grwin->h / GRX_SCALE;
		}
		XDrawLines(Xdisplay,
		       grwin->win, TermWin.gc, xdata.pt, n, CoordModeOrigin);
	    }
	    break;

	case 'P':
	    if (data->ncoords > 3) {
		for (n = i = 0; i < data->ncoords; i += 2, n++) {
		    xdata.pt[n].x = data->coords[i] * grwin->w / GRX_SCALE;
		    xdata.pt[n].y = data->coords[i + 1] * grwin->h / GRX_SCALE;
		}
		XDrawPoints(Xdisplay,
		       grwin->win, TermWin.gc, xdata.pt, n, CoordModeOrigin);
	    }
	    break;

	case 'F':
	    if (data->ncoords > 0) {
		for (n = i = 0; i < data->ncoords; i += 4, n++) {
		    xdata.rect[n].x = data->coords[i] * grwin->w / GRX_SCALE;
		    xdata.rect[n].y = data->coords[i + 1] * grwin->h
				      / GRX_SCALE;
		    xdata.rect[n].width = ((data->coords[i + 2]
					    - data->coords[i] + 1) *
					   grwin->w / GRX_SCALE);
		    xdata.rect[n].height = ((data->coords[i + 3]
					     - data->coords[i + 1] + 1) *
					    grwin->h / GRX_SCALE);
		    XPMClearArea(Xdisplay, grwin->win,
				 xdata.rect[n].x, xdata.rect[n].y,
				 xdata.rect[n].width, xdata.rect[n].height,
				 0);
		}
		XFillRectangles(Xdisplay, grwin->win, TermWin.gc, xdata.rect,
				n);
	    }
	    break;
	case 'T':
	    Gr_Text(grwin, data);
	    break;
	case 'C':
	    Gr_ClearWindow(grwin);
	    break;
	}
    if (data->color != Color_fg) {
	XGCValues       gcv;

	gcv.foreground = PixColors[Color_fg];
	XChangeGC(Xdisplay, TermWin.gc, GCForeground, &gcv);
    }
#endif
}

/* ARGSUSED */
/* PROTO */
void
Gr_Redraw(grwin_t * grwin)
{
#ifdef RXVT_GRAPHICS
    grcmd_t        *cmd;

    for (cmd = grwin->graphics; cmd != NULL; cmd = cmd->next)
	Gr_Dispatch(grwin, cmd);
#endif
}

/*----------------------------------------------------------------------*
 * end of static functions
 */
/* ARGSUSED */
/* PROTO */
void
Gr_ButtonReport(int but, int x, int y)
{
#ifdef RXVT_GRAPHICS
    grwin_t        *grwin;

    for (grwin = gr_root; grwin != NULL; grwin = grwin->next)
	if ((x > grwin->x) &&
	    (y > grwin->y) &&
	    (x < grwin->x + grwin->w) &&
	    (y < grwin->y + grwin->h))
	    break;

    if (grwin == NULL)
	return;

    x = GRX_SCALE * (x - grwin->x) / grwin->w;
    y = GRX_SCALE * (y - grwin->y) / grwin->h;
    tt_printf("\033%c%ld;%d;%d;\n", but, (long)grwin->win, x, y);
#endif
}

/* ARGSUSED */
/* PROTO */
void
Gr_do_graphics(int cmd, int nargs, int args[], unsigned char *text)
{
#ifdef RXVT_GRAPHICS
    static Window   last_id = None;
    long            win_id;
    grwin_t        *grwin;
    grcmd_t        *newcmd, *oldcmd;
    int             i;

    if (cmd == 'W') {
	Gr_NewWindow(nargs, args);
	return;
    }
    win_id = (nargs > 0) ? (Window) args[0] : None;

    if ((cmd == 'G') && (win_id == None)) {
	Gr_Geometry(NULL, NULL);
	return;
    }
    if ((win_id == None) && (last_id != None))
	win_id = last_id;

    if (win_id == None)
	return;

    grwin = gr_root;
    while ((grwin != NULL) && (grwin->win != win_id))
	grwin = grwin->next;

    if (grwin == NULL)
	return;

    if (cmd == 'G') {
	Gr_Geometry(grwin, NULL);
	return;
    }
    nargs--;
    args++;			/* skip over window id */

/* record this new command */
    newcmd = (grcmd_t *) MALLOC(sizeof(grcmd_t));
    newcmd->ncoords = nargs;
    newcmd->coords = (int *)MALLOC((newcmd->ncoords * sizeof(int)));

    newcmd->next = NULL;
    newcmd->cmd = cmd;
    newcmd->color = scr_get_fgcolor();
    newcmd->text = text;

    for (i = 0; i < newcmd->ncoords; i++)
	newcmd->coords[i] = args[i];

/*
 * If newcmd == fill, and rectangle is full window, drop all prior
 * commands.
 */
    if ((newcmd->cmd == 'F') && (grwin) && (grwin->graphics)) {
	for (i = 0; i < newcmd->ncoords; i += 4) {
	    if ((newcmd->coords[i] == 0) &&
		(newcmd->coords[i + 1] == 0) &&
		(newcmd->coords[i + 2] == GRX_SCALE) &&
		(newcmd->coords[i + 3] == GRX_SCALE)) {
	    /* drop previous commands */
		oldcmd = grwin->graphics;
		while (oldcmd->next != NULL) {
		    grcmd_t        *tmp = oldcmd;

		    oldcmd = oldcmd->next;
		    free(tmp);
		}
		grwin->graphics = NULL;
	    }
	}
    }
/* insert new command into command list */
    oldcmd = grwin->graphics;
    if (oldcmd == NULL)
	grwin->graphics = newcmd;
    else {
	while (oldcmd->next != NULL)
	    oldcmd = oldcmd->next;
	oldcmd->next = newcmd;
    }
    Gr_Dispatch(grwin, newcmd);
#endif
}

/* ARGSUSED */
/* PROTO */
void
Gr_scroll(int count)
{
#ifdef RXVT_GRAPHICS
    static short    prev_start = 0;
    grwin_t        *grwin, *next;

    if ((count == 0) && (prev_start == TermWin.view_start))
	return;

    prev_start = TermWin.view_start;

    for (grwin = gr_root; grwin != NULL; grwin = next) {
	next = grwin->next;
	grwin->y -= (count * TermWin.fheight);
	if ((grwin->y + grwin->h) < -(TermWin.saveLines * TermWin.fheight))
	    Gr_DestroyWindow(grwin);
	else
	    XMoveWindow(Xdisplay, grwin->win,
			grwin->x,
			grwin->y + (TermWin.view_start * TermWin.fheight));
    }
#endif
}

/* PROTO */
void
Gr_ClearScreen(void)
{
#ifdef RXVT_GRAPHICS
    grwin_t        *grwin, *next;

    for (grwin = gr_root; grwin != NULL; grwin = next) {
	next = grwin->next;
	if ((grwin->screen == 0) && (grwin->y + grwin->h > 0)) {
	    if (grwin->y >= 0)
		Gr_DestroyWindow(grwin);
	    else
		XResizeWindow(Xdisplay, grwin->win,
			      grwin->w, -grwin->y);
	}
    }
#endif
}

/* PROTO */
void
Gr_ChangeScreen(void)
{
#ifdef RXVT_GRAPHICS
    grwin_t        *grwin, *next;

    for (grwin = gr_root; grwin != NULL; grwin = next) {
	next = grwin->next;
	if (grwin->y + grwin->h > 0) {
	    if (grwin->screen == 1) {
		XMapWindow(Xdisplay, grwin->win);
		grwin->screen = 0;
	    } else {
		XUnmapWindow(Xdisplay, grwin->win);
		grwin->screen = 1;
	    }
	}
    }
#endif
}

/* ARGSUSED */
/* PROTO */
void
Gr_expose(Window win)
{
#ifdef RXVT_GRAPHICS
    grwin_t        *grwin;

    for (grwin = gr_root; grwin != NULL; grwin = grwin->next) {
	if (grwin->win == win) {
	    Gr_Redraw(grwin);
	    break;
	}
    }
#endif
}

/* ARGSUSED */
/* PROTO */
void
Gr_Resize(int w, int h)
{
#ifdef RXVT_GRAPHICS
    grwin_t        *grwin;

    for (grwin = gr_root; grwin != NULL; grwin = grwin->next) {
	if (TermWin.height != h) {
	    grwin->y += (TermWin.height - h);
	    XMoveWindow(Xdisplay, grwin->win,
			grwin->x,
			grwin->y + (TermWin.view_start * TermWin.fheight));
	}
	Gr_Redraw(grwin);
    }
#endif
}

/* PROTO */
void
Gr_reset(void)
{
#ifdef RXVT_GRAPHICS
    grwin_t        *grwin, *next;

    for (grwin = gr_root; grwin != NULL; grwin = next) {
	next = grwin->next;
	Gr_DestroyWindow(grwin);
    }

    graphics_up = 0;
#endif
}

/* PROTO */
int
Gr_Displayed(void)
{
#ifdef RXVT_GRAPHICS
    return graphics_up;
#else
    return 0;
#endif
}
/*----------------------- end-of-file (C source) -----------------------*/
