/*--------------------------------*-C-*---------------------------------*
 * File:	graphics.c
 *
 * This module is all new by Rob Nation
 * <nation@rocket.sanders.lockheed.com>
 *
 * Modifications by mj olesen <olesen@me.QueensU.CA>
 * and Raul Garcia Garcia <rgg@tid.es>
 *----------------------------------------------------------------------*/
#include "rxvt.h"

#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <X11/Xlib.h>
#include <X11/cursorfont.h>

#include "command.h"		/* for tty_printf() */
#include "debug.h"
#include "graphics.h"
#include "xsetup.h"
#include "screen.h"

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

#ifndef GRX_SCALE
# define GRX_SCALE	10000
#endif

/*----------------------------------------------------------------------*
 * extern functions referenced
 */
extern int scr_get_fgcolor (void);

/*----------------------------------------------------------------------*
 * extern variables referenced
 */
/*----------------------------------------------------------------------*
 * extern variables declared here
 */
int graphics_up = 0;

#ifdef RXVT_GRAPHICS
/*----------------------------------------------------------------------*
 * local variables
 */
typedef struct grcmd_t {
   char cmd;
   int color;
   int ncoords;
   int *coords;
   unsigned char *text;
   struct grcmd_t *next;
} grcmd_t;

typedef struct grwin_t {
   Window win;
   int x, y, w, h;
   int screen;
   grcmd_t *graphics;
   struct grwin_t *prev, *next;
} grwin_t;

static grwin_t *gr_root = NULL;

/*----------------------------------------------------------------------*
 * local functions
 */

/*----------------------------------------------------------------------*/
static void
Gr_NewWindow (int nargs, long *args)
{
   int x, y, 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;

   if (w < 0 || h < 0)
     return;

   win = XCreateSimpleWindow (Xdisplay, TermWin.vt,
			      x, y, w, h,
			      0,
			      pixel_colors [COLORFG],
			      pixel_colors [COLORBG]);

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

   grwin = MALLOC (sizeof(grwin_t),"grwin");
   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++;

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

static void
Gr_ClearWindow (grwin_t *grwin)
{
   grcmd_t *grcom, *next;

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

/*
 * arg [0] = x
 * arg [1] = y
 * arg [2] = alignment
 * arg [3] = strlen (text)
 */
static void
Gr_Text (grwin_t *grwin, grcmd_t *data)
{
   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);

   XDrawString (Xdisplay, grwin->win, TermWin.gc,
		x, y,
		data->text, data->coords [3]);
}

static void
Gr_Geometry (grwin_t *grwin, grcmd_t *data)
{
   if (grwin)
     tty_printf ("\033G%ld %d %d %d %d %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 */
     tty_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);
}

static void
Gr_DestroyWindow (grwin_t *grwin)
{
   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--;
}

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

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

	gcv.foreground = pixel_colors [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);
	       }
	     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 != COLORFG)
     {
	XGCValues gcv;

	gcv.foreground = pixel_colors [COLORFG];
	XChangeGC (Xdisplay, TermWin.gc, GCForeground, &gcv);
     }
}

static void
Gr_Redraw (grwin_t *grwin)
{
   grcmd_t *cmd;
   for (cmd = grwin->graphics; cmd != NULL; cmd = cmd->next)
     Gr_Dispatch (grwin, cmd);
}
#endif

/*----------------------------------------------------------------------*
 * end of static functions
 */

#ifdef RXVT_GRAPHICS
void
Gr_ButtonReport (int but, int x, int y)
{
   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;
   tty_printf ("\033%c%ld;%d;%d;\n", but, (long) grwin->win, x, y);
}

void
Gr_do_graphics (char cmd, int nargs, long *args, unsigned char *text)
{
   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 = MALLOC (sizeof(grcmd_t), "grcmd_t");
   newcmd->ncoords = nargs;
   newcmd->coords = MALLOC (newcmd->ncoords * sizeof(long),"coords");
   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);
}

void
Gr_scroll (int count)
{
   static int prev_start = 0;
   grwin_t *grwin, *next;

   if ((count == 0) && (prev_start == TermWin.hist_start))
     {
	return;
     }

   prev_start = TermWin.hist_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.hist_start * TermWin.fheight));
     }
}

void
Gr_ClearScreen (void)
{
   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);
	  }
     }
}

void
Gr_ChangeScreen (void)
{
   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;
	       }
	  }
     }
}

void
Gr_expose (Window win)
{
   grwin_t *grwin;

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

void
Gr_Resize (int w, int h)
{
   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.hist_start * TermWin.fheight));
	  }
	Gr_Redraw (grwin);
     }
}

void
Gr_reset (void)
{
   grwin_t *grwin, *next;

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

   graphics_up = 0;
}
#endif	/* RXVT_GRAPHICS */
/*----------------------- end-of-file (C source) -----------------------*/
