
/* Copyright (c) Mark J. Kilgard, 1995, 1998. */

/* This program is freely distributable without licensing fees
   and is provided without guarantee or warrantee expressed or
   implied. This program is -not- in the public domain. */

#include "glutint.h"

#if !defined(_WIN32) && !defined(__OS2PM__)
#include <X11/Xatom.h>  /* For XA_CURSOR */
#include <X11/cursorfont.h>
#endif

typedef struct _CursorTable {
#if defined(_WIN32)
  char* glyph;
#else
  int glyph;
#endif
  Cursor cursor;
} CursorTable;
/* *INDENT-OFF* */

static CursorTable cursorTable[] = {
  {XC_arrow, None},              /* GLUT_CURSOR_RIGHT_ARROW */
  {XC_top_left_arrow, None},     /* GLUT_CURSOR_LEFT_ARROW */
  {XC_hand1, None},              /* GLUT_CURSOR_INFO */
  {XC_pirate, None},             /* GLUT_CURSOR_DESTROY */
  {XC_question_arrow, None},     /* GLUT_CURSOR_HELP */
  {XC_exchange, None},           /* GLUT_CURSOR_CYCLE */
  {XC_spraycan, None},           /* GLUT_CURSOR_SPRAY */
  {XC_watch, None},              /* GLUT_CURSOR_WAIT */
  {XC_xterm, None},              /* GLUT_CURSOR_TEXT */
  {XC_crosshair, None},                  /* GLUT_CURSOR_CROSSHAIR */
  {XC_sb_v_double_arrow, None},          /* GLUT_CURSOR_UP_DOWN */
  {XC_sb_h_double_arrow, None},          /* GLUT_CURSOR_LEFT_RIGHT */
  {XC_top_side, None},           /* GLUT_CURSOR_TOP_SIDE */
  {XC_bottom_side, None},        /* GLUT_CURSOR_BOTTOM_SIDE */
  {XC_left_side, None},                  /* GLUT_CURSOR_LEFT_SIDE */
  {XC_right_side, None},         /* GLUT_CURSOR_RIGHT_SIDE */
  {XC_top_left_corner, None},    /* GLUT_CURSOR_TOP_LEFT_CORNER */
  {XC_top_right_corner, None},   /* GLUT_CURSOR_TOP_RIGHT_CORNER */
  {XC_bottom_right_corner, None}, /* GLUT_CURSOR_BOTTOM_RIGHT_CORNER */
  {XC_bottom_left_corner, None},  /* GLUT_CURSOR_BOTTOM_LEFT_CORNER */
};
/* *INDENT-ON* */

#if !defined(_WIN32) && !defined(__OS2PM__)
static Cursor blankCursor = None;
static Cursor fullCrosshairCusor = None;

/* SGI X server's support a special property called the
   _SGI_CROSSHAIR_CURSOR that when installed as a window's
   cursor, becomes a full screen crosshair cursor.  SGI
   has special cursor generation hardware for this case. */
static Cursor
getFullCrosshairCursor(void)
{
  Cursor cursor;
  Atom crosshairAtom, actualType;
  int rc, actualFormat;
  unsigned long n, left;
  unsigned long *value;

  if (fullCrosshairCusor == None) {
    crosshairAtom = XInternAtom(__glutDisplay,
      "_SGI_CROSSHAIR_CURSOR", True);
    if (crosshairAtom != None) {
      value = 0;        /* Make compiler happy. */
      rc = XGetWindowProperty(__glutDisplay, __glutRoot,
        crosshairAtom, 0, 1, False, XA_CURSOR, &actualType,
        &actualFormat, &n, &left, (unsigned char **) &value);
      if (rc == Success && actualFormat == 32 && n >= 1) {
        cursor = value[0];
        XFree(value);
        return cursor;
      }
    }
  }
  return XCreateFontCursor(__glutDisplay, XC_crosshair);
}

/* X11 forces you to create a blank cursor if you want
   to disable the cursor. */
static Cursor
makeBlankCursor(void)
{
  static char data[1] =
  {0};
  Cursor cursor;
  Pixmap blank;
  XColor dummy;

  blank = XCreateBitmapFromData(__glutDisplay, __glutRoot,
    data, 1, 1);
  if (blank == None)
    __glutFatalError("out of memory.");
  cursor = XCreatePixmapCursor(__glutDisplay, blank, blank,
    &dummy, &dummy, 0, 0);
  XFreePixmap(__glutDisplay, blank);

  return cursor;
}
#endif /* !_WIN32 && !__OS2PM__*/

/* Win32 and X11 use this same function to accomplish
   fairly different tasks.  X11 lets you just define the
   cursor for a window and the window system takes care
   of making sure that the window's cursor is installed
   when the mouse is in the window.  Win32 requires the
   application to handle a WM_SETCURSOR message to install
   the right cursor when windows are entered.  Think of
   the Win32 __glutSetCursor (called from __glutWindowProc)
   as "install cursor".  Think of the X11 __glutSetCursor
   (called from glutSetCursor) as "define cursor". */
void
__glutSetCursor(GLUTwindow *window)
{
  int cursor = window->cursor;
  Cursor xcursor = 0;

  if (cursor >= 0 &&
    cursor < sizeof(cursorTable) / sizeof(cursorTable[0])) {
    if (cursorTable[cursor].cursor == None) {
      cursorTable[cursor].cursor = XCreateFontCursor(__glutDisplay,
        cursorTable[cursor].glyph);
    }
    xcursor = cursorTable[cursor].cursor;
  } else {
    /* Special cases. */
    switch (cursor) {
    case GLUT_CURSOR_INHERIT:
#if defined(_WIN32)
      while (window->parent) {
        window = window->parent;
        if (window->cursor != GLUT_CURSOR_INHERIT) {
          __glutSetCursor(window);
          return;
        }
      }
      /* XXX Default to an arrow cursor.  Is this
         right or should we be letting the default
         window proc be installing some system cursor? */
      xcursor = cursorTable[0].cursor;
      if (xcursor == NULL) {
        xcursor =
          cursorTable[0].cursor =
          LoadCursor(NULL, cursorTable[0].glyph);
      }

#elif defined(__OS2PM__)
//todo
      xcursor = None;

#else
      xcursor = None;
#endif
      break;
    case GLUT_CURSOR_NONE:
#if defined(_WIN32) || defined(__OS2PM__)
      xcursor = NULL;
#else
      if (blankCursor == None) {
        blankCursor = makeBlankCursor();
      }
      xcursor = blankCursor;
#endif
      break;
    case GLUT_CURSOR_FULL_CROSSHAIR:
#if defined(_WIN32)
      xcursor = (HICON) IDC_CROSS;
#elif defined(__OS2PM__)
//todo
#else
      if (fullCrosshairCusor == None) {
        fullCrosshairCusor = getFullCrosshairCursor();
      }
      xcursor = fullCrosshairCusor;
#endif
      break;
    }
  }
  XDefineCursor(__glutDisplay,
    window->win, xcursor);
  XFlush(__glutDisplay);
}

/* CENTRY */
void GLUTAPIENTRY
glutSetCursor(int cursor)
{
#ifdef _WIN32
  POINT point;

  __glutCurrentWindow->cursor = cursor;
  /* Are we in the window right now?  If so,
     install the cursor. */
  GetCursorPos(&point);
  if (__glutCurrentWindow->win == WindowFromPoint(point)) {
    __glutSetCursor(__glutCurrentWindow);
  }
#elif defined(__OS2PM__)
//todo
#else
  __glutCurrentWindow->cursor = cursor;
  __glutSetCursor(__glutCurrentWindow);
#endif
}
/* ENDCENTRY */
