/****************************************************************************
    Copyright (C) 1987-2005 by Jeffery P. Hansen

    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.
****************************************************************************/
#include <stdlib.h>
#include <assert.h>
#define BEAK_MODES
#include "tkgate.h"

#define DEBUG_CURSORS 0

#define DEFAULT_CURS	-1
#define SMALL_CURS	0
#define BIG_CURS	1
#define STREK_CURS	2
#define TREK_CURS	3
#define BAT_CURS	4

typedef struct {
  int			code;		/* code number for cursor */
  int			pmap;		/* pixmap cursor is on */
  iconDimensions	icon;		/* icon dimensions for cursor */
} CursorDef;

typedef struct {
  int			pm_code;
  char			*pm_name;
  char			*pm_file;
#if 0
  unsigned char		*bits;
  int			width;
  int			height;
#endif
} CursorPMDef;

#if 0
static CursorPMDef cursorPMs[] = {
  {SMALL_CURS,	"smallcurs",	smallcurs_bits,smallcurs_width,smallcurs_height},
  {BIG_CURS,	"bigcurs",	bigcurs_bits,bigcurs_width,bigcurs_height},
#ifdef BEAK_MODES
  {STREK_CURS,	"strekcurs",	strek_bits,strek_width,strek_height},
  {TREK_CURS,	"trekcurs",	trek_bits,trek_width,trek_height},
  {BAT_CURS,	"batcurs",	bat_bits,bat_width,bat_height},
#endif
};
#endif
static CursorPMDef cursorPMs[] = {
  {SMALL_CURS,	"smallcurs",	"smallcurs.b"},
  {BIG_CURS,	"bigcurs",	"bigcurs.b"},
#ifdef BEAK_MODES
  {STREK_CURS,	"strekcurs",	"strek.b"},
  {TREK_CURS,	"trekcurs",	"trek.b"},
  {BAT_CURS,	"batcurs",	"bat.b"},
#endif
};
static int numPMs = sizeof(cursorPMs)/sizeof(cursorPMs[0]);

static CursorDef bigCursors[] = {
  {ARROWCURSOR,DEFAULT_CURS,	{0,0,0,0,0,0}},
  {OPENCUTTERS,BIG_CURS,	{0,0,22,26,3,3}},
  {CLOSEDCUTTERS,BIG_CURS,	{23,0,22,23,3,3}},
  {IRON,BIG_CURS,		{0,26,31,26,0,25}},
  {INVERTUPCURSOR,BIG_CURS,	{32,24,30,24,2,15}},
  {INVERTDNCURSOR,BIG_CURS,	{63,19,31,24,3,15}},
  {DELETEUPCURSOR,BIG_CURS,	{0,54,25,21,13,20}},
  {DELETEDNCURSOR,BIG_CURS,	{49,60,16,16,8,15}},
  {PENCIL,BIG_CURS,		{26,49,21,21,0,21}},
  {DOWNARROW,SMALL_CURS,	{0,51,16,16,8,16}},
  {UPARROW,SMALL_CURS,		{17,51,16,16,8,16}},
  {TRIARROW,SMALL_CURS,		{34,51,16,16,8,16}},
  {CHANGEDIRCURSOR,SMALL_CURS,	{51,51,16,16,8,16}},
  {REPCURSOR,BIG_CURS,		{50,47,24,12,10,6}},
  {SIZECURSOR,BIG_CURS,		{46,0,26,19,0,10}},
  {WRENCHCURSOR,SMALL_CURS,	{17,17,16,16,3,3}},
  {TEXTCURSOR,SMALL_CURS,	{68,0,16,16,8,8}},
  {ARROW0,SMALL_CURS,		{68,17,16,16,8,8}},
  {ARROW90,SMALL_CURS,		{68,51,16,16,8,8}},
  {ARROW180,SMALL_CURS,		{51,34,16,16,8,8}},
  {ARROW270,SMALL_CURS,		{68,34,16,16,8,8}},
#ifdef BEAK_MODES
  {BATCURSOR1,BAT_CURS,		{0,0,36,24,18,12}},
  {BATCURSOR2,BAT_CURS,		{37,0,25,24,13,12}},
  {BATCURSOR3,BAT_CURS,		{63,0,18,24,10,12}},
  {TREKDELETEUP,TREK_CURS,	{0,35,77,34,3,29}},
  {TREKDELETEDN,TREK_CURS,	{0,0,77,34,3,29}},
#endif
  {-1}
};

static CursorDef smallCursors[] = {
  {ARROWCURSOR,DEFAULT_CURS,	{0,0,0,0,0,0}},
  {OPENCUTTERS,SMALL_CURS,	{0,0,16,16,1,1}},
  {CLOSEDCUTTERS,SMALL_CURS,	{17,0,16,16,1,1}},
  {IRON,SMALL_CURS,		{34,0,16,16,0,16}},
  {INVERTUPCURSOR,SMALL_CURS,	{51,0,16,16,2,6}},
  {INVERTDNCURSOR,SMALL_CURS,	{51,0,16,16,2,6}},
  {DELETEUPCURSOR,SMALL_CURS,	{34,17,16,16,8,16}},
  {DELETEDNCURSOR,SMALL_CURS,	{51,17,16,16,5,11}},
  {PENCIL,SMALL_CURS,		{0,34,16,16,0,16}},
  {DOWNARROW,SMALL_CURS,	{0,51,16,16,8,16}},
  {UPARROW,SMALL_CURS,		{17,51,16,16,8,16}},
  {TRIARROW,SMALL_CURS,		{34,51,16,16,8,16}},
  {CHANGEDIRCURSOR,SMALL_CURS,	{51,51,16,16,8,16}},
  {REPCURSOR,SMALL_CURS,	{17,34,16,9,8,5}},
  {SIZECURSOR,SMALL_CURS,	{34,34,16,9,0,4}},
  {WRENCHCURSOR,SMALL_CURS,	{17,17,16,16,3,3}},
  {TEXTCURSOR,SMALL_CURS,	{68,0,16,16,8,8}},
  {ARROW0,SMALL_CURS,		{68,17,16,16,8,8}},
  {ARROW90,SMALL_CURS,		{68,51,16,16,8,8}},
  {ARROW180,SMALL_CURS,		{51,34,16,16,8,8}},
  {ARROW270,SMALL_CURS,		{68,34,16,16,8,8}},
#ifdef BEAK_MODES
  {BATCURSOR1,BAT_CURS,		{0,24,16,16,0,0}},
  {BATCURSOR2,BAT_CURS,		{17,24,16,16,0,0}},
  {BATCURSOR3,BAT_CURS,		{34,24,16,16,0,0}},
  {TREKDELETEUP,STREK_CURS,	{0,0,16,16,0,7}},
  {TREKDELETEDN,STREK_CURS,	{17,0,16,16,0,7}},
#endif
  {-1}
};

static Cursor LastCursor;

NHash *GateCursors = 0;

void Cursor_register(int id,Cursor C)
{
  Cursor *pC = (Cursor*)ob_malloc(sizeof(Cursor),"Cursor");
  *pC = C;
  NHash_insert(GateCursors,id,pC);
}

Cursor Cursor_find(int id)
{
  Cursor *pC = (Cursor*)NHash_find(GateCursors,id);
  if (!pC) return None;
  return *pC;
}

void SetUpCursors()
{
  XColor FGC,BGC,FGCreal,BGCreal;
  int MaxW,MaxH;
  Cursor default_cursor = XCreateFontCursor(XGate.D,XC_top_left_arrow);
  CursorDef *CSet;
  int i;

  GateCursors = new_NHash();

  for (i = 0;i < numPMs;i++) {
    CursorPMDef *pmd = &cursorPMs[i];
    assert(pmd->pm_code == i);
#if 0
    Pixmap_register(pmd->pm_name,pmd->bits,pmd->width,pmd->height);
#endif
    Pixmap_registerFromFile(pmd->pm_name,pmd->pm_file);
  }

  XQueryBestCursor(XGate.D,XGate.root,32,32,&MaxW,&MaxH);
#if DEBUG_CURSORS
    printf("[Max cursor size is %dx%d]\n",MaxW,MaxH);
#endif
  if (MaxW >= 32 || MaxH >= 32) {
#if DEBUG_CURSORS
    printf("using bigCursors\n");
#endif
    CSet = bigCursors;
  } else {
    if (MaxW < 16 && MaxH < 16) {
      printf("[Warning: hardware cursors limited to "
	     "%dx%d (need at least 16x16).]\n",MaxW,MaxH);
    }
#if DEBUG_CURSORS
    printf("using smallCursors\n");
#endif
    CSet = smallCursors;
  }

  XAllocNamedColor(XGate.D,XGate.CM,"black",&FGCreal,&FGC);
  XAllocNamedColor(XGate.D,XGate.CM,"white",&BGCreal,&BGC);

  for (;CSet->code >= 0;CSet++) {
    Cursor C;

    if (CSet->pmap == DEFAULT_CURS || CSet->icon.w <= 0) {
      C = default_cursor;
#if DEBUG_CURSORS
      printf("curor %d: default\n",CSet->code);
#endif
    } else {
      Pixmap iconPM = Pixmap_find(cursorPMs[CSet->pmap].pm_name);
      Pixmap srcPM = XCreatePixmap(XGate.D,XGate.root,CSet->icon.w,CSet->icon.h,1);
      XCopyArea(XGate.D,iconPM,srcPM,XGate.bitGC,
		CSet->icon.x,CSet->icon.y,
		CSet->icon.w,CSet->icon.h,0,0);

      C = XCreatePixmapCursor(XGate.D,srcPM,srcPM,&FGCreal,&BGCreal,
			      CSet->icon.ox,CSet->icon.oy);
      XFreePixmap(XGate.D,srcPM);
#if DEBUG_CURSORS
      printf("curor %d: @(%d, %d) %dx%d = %x\n",CSet->code,
	     CSet->icon.x,CSet->icon.y,CSet->icon.w,CSet->icon.h,C);
#endif
    }
    Cursor_register(CSet->code,C);
  }
#if DEBUG_CURSORS
  printf("cursors done.\n");
#endif
}

void wm_SetCursor(int id)
{
  Cursor C = Cursor_find(id);

#if DEBUG_CURSORS > 1
  printf("wm_SetCursor(%d) = %x\n",id,C);
#endif
  XDefineCursor(XGate.D,XGate.W,C);
  LastCursor = C;
  XFlush(XGate.D);
}

void PushCursor(int id)
{
  Cursor C = Cursor_find(id);

#if DEBUG_CURSORS > 1
  printf("PushCursor(%d) = %x\n",id,C);
#endif

  XDefineCursor(XGate.D,XGate.W,C);
  XFlush(XGate.D);
}

void PopCursor()
{
#if DEBUG_CURSORS > 1
  printf("PopCursor() = %x\n",LastCursor);
#endif

  XDefineCursor(XGate.D,XGate.W,LastCursor);
  XFlush(XGate.D);
}
