
/* x11lib.c
 * Useful for simple display models -- pixel by pixel plotting in a
 * single window. Useful for simple games, for the most part.
 */

/* NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
 * You should define "TIMER" if you wish event polling based on a
 *     timer. Otherwise, it will be assumed that you will call
 *     poll_events() occasionally, at your leisure or with your
 *     own timers.
 * NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
 */

/* NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
 * Some XServers are braindead -- if you have colourmap troubles, try
 * defining "BUSTEDXSERVER". It forces a 256 colourmap for the pixmaps,
 * which ought to be fine on any halfway useful XServer. The games are
 * only 16 colours anyway....
 * NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
 */

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <unistd.h>
#include <signal.h>

#include "unix.h"
#include "x11lib.h"

/* X terminal settings; remember, a NULL x11targetDisplay causes the
 * Xlib to look in users DISPLAY env-var, as god intended.
 */
char *x11_target_display = NULL;                    /* target display obj. */

/* internal X11 state; yeah, I know I use goofy variable names. They
 * came out of me at 2am when I was completely unaware of how hungarian
 * notation was done.
 */
static int x11_screen = 0;                          /* screen no. on disp */
static unsigned long x11_black_pixel,               /* X11 pixel-colours */
  x11_white_pixel;                                  /* X11 pixel-colours */
static Window x11_window_root, x11_window;          /* this and root window */
static GC x11_window_gc;                            /* GC for our window */
static unsigned int x11_colour_depth;               /* colour depth of disp */
static Colormap x11_colourmap;                      /* colour map for disp */
static XEvent x11_event;                            /* an event */
static KeySym x11_keysym;                           /* key moded in event */
static int x11_max_screenwidth;                     /* screen width */
static int x11_max_screenheight;                    /* screan height */
static Pixmap x11_icon;                             /* minimized icon */
Display *x11_display = NULL;                        /* display object */
int x11_current_pen = 0;                            /* current colour */
Pixmap x11_pixmap;                                  /* virtual window */
Pixmap x11_current_pixmap;                          /* drawing focus */
static XFontStruct *x11_fonts [ MAXFONTS ];         /* loaded fonts */

/* user configurable items */
int desired_window_width = 640;                     /* size to shoot for */
int desired_window_height = 480;                    /* size to shoot for */
void (*handleExpose)( void ) = NULL;                /* Expose event handler */
void (*handleKeyPress)( char, KeySym ) = NULL;      /* Expose event handler */
void (*handleKeyRelease)( char, KeySym ) = NULL;    /* Expose event handler */

/* X11 colour palettes for pixmap */
GC x11_colour_gcs [ 256 ];                          /* map colours->GCs */
static GC x11_black_gc;                             /* always a black GC */
static GC x11_white_gc;                             /* always a white GC */

/* drawing optimizations */
XSegment x11_vectors [ MAXVECTORS ];                /* pending draws */
XPoint   x11_points  [ MAXPIXELS ];                 /* pending draws */
int x11_pending_count = 0;                          /* no. pending draws */

#define x11_icon_width 16
#define x11_icon_height 16
static unsigned char x11_icon_data[] = {
  0x00, 0x00, 0x80, 0x00, 0x84, 0x10, 0xac, 0x1a, 0xbc, 0x1e, 0xac, 0x1a,
  0xac, 0x1a, 0x8c, 0x18, 0x8c, 0x18, 0xcc, 0x19, 0x04, 0x10, 0x04, 0x10,
  0x04, 0x10, 0x04, 0x10, 0x04, 0x10, 0x00, 0x00
};

int init_graphics_engine ( void ) {

  /* connect to the display */
  /* printf ( "Opening X11 display...\n" ); */

  if ( unix_option ( "display" ) ) {
    x11_target_display = unix_option_value ( "display" );
  }

  x11_display = XOpenDisplay ( x11_target_display );

  if ( ! x11_display ) {
    return ( 0 ); /* couldn't init display */
  }

  /* identify the screen on the display */
  x11_screen = DefaultScreen ( x11_display );

  /* its sometimes handy to have the root window selected */
  x11_window_root = RootWindow ( x11_display, x11_screen );

  /* identify basic pixel-colours for black and white */
  x11_black_pixel = BlackPixel ( x11_display, x11_screen );
  x11_white_pixel = WhitePixel ( x11_display, x11_screen );

  /* some extra attributes we can figure out */
  x11_max_screenwidth =
    WidthOfScreen ( ScreenOfDisplay ( x11_display, x11_screen ) );
  x11_max_screenheight =
    HeightOfScreen ( ScreenOfDisplay ( x11_display, x11_screen ) );

  /* create or inherit a colourmap
   */
  x11_colour_depth = DefaultDepth ( x11_display, x11_screen );

#ifdef BUSTEDXSERVER
  x11_colour_depth = 8;
#endif

  /* private colourmap not supported yet */
  x11_colourmap = DefaultColormap ( x11_display, x11_screen );

  return ( 1 );
}

int open_graphics_engine ( char *title ) {

  /* Already have the display and preferred screen number;
   * its time to open a top-level window (open a window,
   * provide an icon, set non-resizable flags, etc.)
   */

  /* create the window
   */
  {
    XSetWindowAttributes attributes;
    unsigned long attribute_mask;
    Visual *visual = CopyFromParent;   /* inherit */
	 
    /* attribute values for above attributes */
    attributes.background_pixel = x11_black_pixel;
    attributes.border_pixel = x11_white_pixel;

    /* which attributes to set */
    attributes.event_mask = ButtonPressMask | KeyPressMask |
      KeyReleaseMask  | ExposureMask |
      StructureNotifyMask;
    attribute_mask = CWBackPixel | CWBorderPixel | CWEventMask;

    if ( unix_option ( "root" ) ) {  /* display on the root window */
      x11_window = x11_window_root;

    } else { /* create a new window */
      x11_window = XCreateWindow ( x11_display,           /* display */
				   x11_window_root,       /* parent */
				   10, 10,                /* default loc */
				   desired_window_width,  /* window width */
				   desired_window_height, /* window height */
				   2,                     /* border width */
				   CopyFromParent,
				   InputOutput,
				   visual,
				   attribute_mask, &attributes );
    }

  } /* create window scope */
  
  if ( x11_window == (Window) None ) {
    fprintf ( stderr, "Couldn't open window.\n" );
    exit ( 1 );
  }

  /* create a GC for drawing on the window */
  x11_window_gc = X11MakeGC ( x11_display, x11_window,
			     x11_black_pixel, x11_white_pixel );

  /* set "normal hints" for the window
   */

  /* set window name */
  if ( ! unix_option ( "root" ) ) {
    XClassHint *classhints = XAllocClassHint();

    if ( classhints ) {
      classhints -> res_name = title;
      classhints -> res_class = title;
      XSetClassHint ( x11_display, x11_window, classhints );
      XFree ( classhints );
    }

    XStoreName ( x11_display, x11_window, title );
    XSetIconName ( x11_display, x11_window, title );

  } /* window name hints scope */

  /* set size and location hints */
  if ( ! unix_option ( "root" ) ) {
    XSizeHints *sizehints = XAllocSizeHints();

    if ( sizehints ) {
      sizehints -> min_width = desired_window_width;    /* user resize limit */
      sizehints -> min_height = desired_window_height;  /* user resize limit */
      sizehints -> max_width = desired_window_width;    /* user resize limit */
      sizehints -> max_height = desired_window_height;  /* user resize limit */
      sizehints -> base_width = desired_window_width;   /* preferred size */
      sizehints -> base_height = desired_window_height; /* preferred size */

      /* future: ought to set an aspect ratio requirement as well */
      sizehints -> flags = PMinSize | PMaxSize | PBaseSize;

      XSetWMNormalHints ( x11_display, x11_window, sizehints );
      XFree ( sizehints );

    } /* if sizehints allowed on this terminal */

  } /* size and location hints scope */

  {
    XWMHints *wmhints = XAllocWMHints();

    if ( ! wmhints ) {
      fprintf ( stderr, "Couldn't allocate window manager hints!\n" );
      exit ( 0 );
    }

    x11_icon = XCreateBitmapFromData ( x11_display, x11_window,
				      x11_icon_data,
				      x11_icon_width, x11_icon_height );

    wmhints -> initial_state = NormalState;
    wmhints -> input = True;
    wmhints -> icon_pixmap = x11_icon;
    wmhints -> icon_mask = x11_icon;
    wmhints -> flags = StateHint | InputHint | IconPixmapHint | IconMaskHint;

    XSetWMHints ( x11_display, x11_window, wmhints );
    XFree ( wmhints );

  } /* WMHints */

  /* create the default starting pixmap */
  x11_pixmap = create_pixmap();

  if ( x11_pixmap == (Pixmap) None ) {
    fprintf ( stderr, "Couldn't create pixmap!\n" );
    exit ( 1 );
  }

  /* make it the default for initial drawing operations */
  x11_current_pixmap = x11_pixmap;

  /* HACK: create the always-black-GC (eraser) */
  x11_black_gc = X11MakeGC ( x11_display, x11_pixmap,
			    x11_black_pixel, x11_black_pixel );
  x11_white_gc = X11MakeGC ( x11_display, x11_pixmap,
			    x11_white_pixel, x11_white_pixel );

  /* Create the colours for the text and other non-image
   * related graphics.
   */
  x11_colour_gcs [ COLOUR_BLACK ] = x11_black_gc;
  set_palette_entry ( COLOUR_SALMON, 250, 128, 114 );
  x11_colour_gcs [ COLOUR_WHITE ] = x11_white_gc;

  /* display the window
   */
  /* printf ( "Mapping window onto display...\n" ); */

  if ( ! unix_option ( "root" ) ) {
    XMapRaised ( x11_display, x11_window );
    XMapSubwindows ( x11_display, x11_window );
  }

  /* realize all these commands */
  XFlush ( x11_display );

  return ( 1 );
}

int shutdown_graphics_engine ( void ) {

#ifdef 0
  XFreeGC ( x11_display, x11_window_gc );
  /* XFreeGC ( x11_display, x11_pixmapGC ); */
  XDestroySubwindows ( x11_display, x11_window );
  XDestroyWindow ( x11_display, x11_window );
  XFlush ( x11_display );
  XCloseDisplay ( x11_display );
#endif

  return ( 1 );
}

void flush_display ( void ) {

  if ( unix_option ( "rawtext" ) && unix_option ( "nopics" ) ) {
    return; /* no graphics mode needed */
  }

#ifdef TIMER
  X11UninstallVirtualAlarm();
#endif

  XCopyArea ( x11_display, x11_pixmap,
	      x11_window, x11_window_gc,
	      0, 0,                         /* top left of virtual window */
	      desired_window_width,         /* for its full width */
	      desired_window_height,        /* and full height */
	      0, 0 );                       /* to top left of real window */

  XFlush ( x11_display );

#ifdef TIMER
  X11InstallVirtualAlarm();
#endif

}

/* takes RGB components in EIGHT BIT; yes, it converts them to
 * 16 bit for X11. But it takes them in EIGHT BIT! :)
 */
int set_palette_entry ( int entry, int red, int green, int blue ) {
  XColor colour;

#ifdef TIMER
  X11UninstallVirtualAlarm();
#endif

  /* RGB model for allocation */
  colour.flags = DoRed | DoGreen | DoBlue;

  /* XWindow colours have 16bit components */
  colour.red =   red   << 8;
  colour.green = green << 8;
  colour.blue =  blue  << 8;

  /* create the "pixel colour" from the RGB colour */
  if ( ! XAllocColor ( x11_display, x11_colourmap, &colour ) ) {
    fprintf ( stderr, "Couldn't allocate a colour pen!\n" );
    exit ( 1 );
  }

  /* free the existing GC so as not to be wasteful */
  if ( x11_colour_gcs [ entry ] ) {
    XFreeGC ( x11_display, x11_colour_gcs [ entry ] );
  }

  /* create a graphics context for the colour */
  x11_colour_gcs [ entry ] =
    X11MakeGC ( x11_display, x11_window, colour.pixel, x11_black_pixel );

#ifdef TIMER
  X11InstallVirtualAlarm();
#endif

  return ( 1 );
}

int query_screen_width ( void ) {
  return ( WidthOfScreen ( ScreenOfDisplay ( x11_display, x11_screen ) ) );
}

int query_screen_height ( void ) {
  return ( HeightOfScreen ( ScreenOfDisplay ( x11_display, x11_screen ) ) );
}

void set_vector_draw_colour ( int entry ) {

  /* if the colour changes, be sure to draw out any pending
   * vectors before colour change is made.
   */

  if ( x11_current_pen != entry ) {
    draw_pending_vectors();
    x11_current_pen = entry;
  }

}

void begin_vector_draw ( void ) {

  /* future: Allocate an array of XPoints, all empty; the draw macro
   * could fill it up, and then the EndVector function could draw
   * all of them in one shot; the line macro would have to do a
   * draw run as well, if colour changed.
   */

#ifdef TIMER
  X11UninstallVirtualAlarm();
#endif

}

void draw_pending_vectors ( void ) {

  XDrawSegments ( x11_display, x11_current_pixmap,
		  x11_colour_gcs [ x11_current_pen ],
		  x11_vectors, x11_pending_count );

  x11_pending_count = 0;

}

void draw_vector ( int fromx, int fromy, int tox, int toy ) {

  if ( x11_pending_count == MAXVECTORS ) {
    draw_pending_vectors();
  }

  x11_vectors [ x11_pending_count ].x1 = fromx;
  x11_vectors [ x11_pending_count ].y1 = fromy;
  x11_vectors [ x11_pending_count ].x2 = tox;
  x11_vectors [ x11_pending_count ].y2 = toy;
  x11_pending_count++;

}

void end_vector_draw ( void ) {

  /* finish off any remaining draws */
  draw_pending_vectors();

  /* Commit the display operations (or the unforced draw
   * operations).
   */
  XFlush ( x11_display );

#ifdef TIMER
  X11InstallVirtualAlarm();
#endif

}

void set_pixel_draw_colour ( int entry ) {

  /* if the colour changes, be sure to draw out any pending
   * vectors before colour change is made.
   */

  if ( x11_current_pen != entry ) {
    draw_pending_pixels();
    x11_current_pen = entry;
  }

}

void begin_pixel_draw ( void ) {

  /* future: Allocate an array of XPoints, all empty; the draw macro
   * could fill it up, and then the EndVector function could draw
   * all of them in one shot; the line macro would have to do a
   * draw run as well, if colour changed.
   */

#ifdef TIMER
  X11UninstallVirtualAlarm();
#endif

}

void draw_pending_pixels ( void ) {

  XDrawPoints ( x11_display, x11_current_pixmap,
		x11_colour_gcs [ x11_current_pen ],
		x11_points, x11_pending_count,
		CoordModeOrigin );

  x11_pending_count = 0;

}

void draw_pixel ( int fromx, int fromy ) {

  if ( x11_pending_count == MAXPIXELS ) {
    draw_pending_pixels();
  }

  x11_points [ x11_pending_count ].x = fromx;
  x11_points [ x11_pending_count ].y = fromy;
  x11_pending_count++;

}

void end_pixel_draw ( void ) {

  /* finish off any remaining draws */
  draw_pending_pixels();

  /* Commit the display operations (or the unforced draw
   * operations).
   */
  XFlush ( x11_display );

#ifdef TIMER
  X11InstallVirtualAlarm();
#endif

}

void fill_region ( int entry, int fromx, int fromy, int tox, int toy ) {

#ifdef TIMER
  X11UninstallVirtualAlarm();
#endif

  XFillRectangle ( x11_display, x11_current_pixmap,
		   x11_colour_gcs [ entry ],
		   fromx, fromy, tox - fromx, toy - fromy );

#ifdef TIMER
  X11InstallVirtualAlarm();
#endif

}

void init_keyboard_handler ( void ) {

  /* Install the keyboard mappings ..
   */

  /* install the virtual handler for X11 periodic events, such
   * as keyboard handling.
   */
#ifdef TIMER
  X11ResetVirtualAlarm();     /* first time */
  X11InstallVirtualAlarm();
#endif

  /* catch control-c, and turn key repeat back on!
   */
  signal ( SIGINT, handleUnixSigINT );

}

void restore_keyboard_handler ( void ) {

  /* turn key repeat back on, so we don't annoy the hell
   * out of the user :)
   */

  /* XAutoRepeatOn ( x11_display ); */

}

/* MakeDefaultGC() is a handle little routine for creating "stub" GC's
 * that we don't really want to draw with at this point.. they can be
 * later have their colour attributes set, and then used.
 */
GC X11MakeDefaultGC ( void ) {
  return ( X11MakeGC ( x11_display, x11_window, x11_black_pixel, x11_white_pixel ) );
}

/* MakeGC() is a handy little wrapper for the XCreateGC() function. It
 * simple creates a GC for the specified drawable and sets the foreground
 * and background colours. The new GC is returned.
 */
GC X11MakeGC ( Display *display, Drawable drawable,
	       unsigned long fore, unsigned long back )
{
  GC gc;
  XGCValues gcvalues;

#ifdef TIMER
  X11UninstallVirtualAlarm();
#endif

  gcvalues.foreground = fore;
  gcvalues.background = back;

  gc = XCreateGC ( display, drawable,
		   GCForeground | GCBackground,
		   &gcvalues );

  if ( ! gc ) {
    fprintf ( stderr, "Couldn't create graphic context!\n" );
    exit ( 1 );
  }

#ifdef TIMER
  X11InstallVirtualAlarm();
#endif

  return ( gc );
}

/* SetGC() changes the colours the specified GC makes use of.
 */
void X11SetGC ( Display *display, GC gc,
		unsigned long fore, unsigned long back )
{
  XSetForeground ( display, gc, fore );
  XSetBackground ( display, gc, back );
}

/* poll_events() is for the non-TIMER based event system, where the
 * library user wishes to control when event pollling can occur (or
 * they wish to use the timer, which then screws up the library...)
 */
void poll_events ( void ) {
  char buffer [ 10 ];          /* lame; I'll use a better func later */

  if ( unix_option ( "rawtext" ) && unix_option ( "nopics" ) ) {
    return; /* no graphics mode needed */
  }

  /* the alarm must be turned off while in the handler, else chaos
   * ensues within XWindows as one event is pulled off the queue
   * before the current one is finished being handled.
   */
#ifdef TIMER
  X11UninstallVirtualAlarm();
#endif

  /* are any X11 events pending? */
  while ( XPending ( x11_display ) ) {
  
    /* pick up pending event and move it into an event structure */
    XNextEvent ( x11_display, &x11_event );

    /* parse the events; consume those we don't care about, make use
     * of those we do. X typically returns a whole series of Expose
     * events, as a covering windows moves across, etc, therefore we
     * consume all Expose events besides the last one. Hell, we can
     * consume them all, since we redisplay continuously anyway :)
     */
    switch ( x11_event.type ) {

    case KeyPress:

      XLookupString ( (XKeyEvent*) &x11_event, buffer, 9, &x11_keysym, NULL );
      if ( handleKeyPress ) {
	handleKeyPress ( buffer [ 0 ], x11_keysym );
      }

      break;

    case KeyRelease:

      XLookupString ( (XKeyEvent*) &x11_event, buffer, 9, &x11_keysym, NULL );
      if ( handleKeyRelease ) {
	handleKeyRelease ( buffer [ 0 ], x11_keysym );
      }

      break;

    case ButtonPress:
      break;

    case ClientMessage:
      break;

    case PropertyNotify:
      break;

    case SelectionClear:
      break;

    case SelectionNotify:
      break;

    case SelectionRequest:
      break;

    case ConfigureNotify:
      break;

    case Expose:
      if ( ( handleExpose ) &&                 /* got expose handler? */
	   ( x11_event.xexpose.count == 0 ) )  /* consume all but last */
      {
	handleExpose();
      }
      break;

    case MappingNotify:
      XRefreshKeyboardMapping ( (XMappingEvent*) &x11_event );
      break;

    } /* switch on event type */

  } /* while X events are pending */

  /* reinstall the alarm, now that we're finished. */
#ifdef TIMER
  X11InstallVirtualAlarm();
#endif

}

void handleUnixVirtualAlarm ( int sigtype ) {
  poll_events();
}

static struct itimerval oldtimer;        /* so we don't forget to signal */
static struct itimerval stoptimer;       /* for disabling the timer */

void X11ResetVirtualAlarm ( void ) {

  oldtimer.it_interval.tv_sec = 0;       /* 0 seconds, but ... */
  oldtimer.it_interval.tv_usec = 500000;  /* x millieseconds */
  oldtimer.it_value.tv_sec = 0;
  oldtimer.it_value.tv_usec = 500000;

  stoptimer.it_interval.tv_sec = 0;      /* 0 seconds till next timer */
  stoptimer.it_interval.tv_usec = 0;     /* 0 uSeconds till next timer */
  stoptimer.it_value.tv_sec = 0;         /* 0 seconds till next timer */
  stoptimer.it_value.tv_usec = 0;        /* 0 uSeconds till next timer */

}

inline void X11InstallVirtualAlarm ( void ) {
  /* X11ResetVirtualAlarm(); */

  signal ( SIGVTALRM, handleUnixVirtualAlarm );
  setitimer ( ITIMER_VIRTUAL, &oldtimer, NULL );
}

inline void X11UninstallVirtualAlarm ( void ) {
  signal ( SIGVTALRM, SIG_IGN );      /* ignore any trailing signals */
  setitimer ( ITIMER_VIRTUAL, &stoptimer, &oldtimer );  /* disable */
}

void handleUnixSigINT ( int sig ) {

  XFlush ( x11_display );
  exit ( 0 );

}

/* font handling code
 */

/* retrieve_font()
 *
 * Brief:   Cause XServer to load a font into serverspace
 * Verbose: Request that the XServer load the named font.
 *
 * Entry:   Font slot to retrieve into, name of the font.
 * Exit:    boolean; success or failure.
 */
int retrieve_font ( int index, char *name ) {
  return ( ( x11_fonts [ index ] = XLoadQueryFont ( x11_display, name ) ) !=
	   NULL );
}

/* set_font()
 *
 * Brief:   Associate a font with a graphics context
 * Verbose: A graphics context can have previously loaded font
 *          selected into it. Any text drawn with this GC will use
 *          this font.
 *
 * Entry:   GC entry, font entry
 * Exit:    boolean; success or failure.
 */
int set_font ( int gcentry, int fontindex ) {
#ifdef TIMER
  X11UninstallVirtualAlarm();
#endif

  XSetFont ( x11_display,
	     x11_colour_gcs [ gcentry ],
	     x11_fonts [ fontindex ] -> fid );

  X11InstallVirtualAlarm();

  return ( 1 );
}

/* query_font_height()
 *
 * Brief:   Query pixel height of font
 * Verbose: Causes the previously loaded fonts ascender height to
 *          be added tp the descender height, and the result returned.
 *
 * Entry:   font entry
 * Exit:    pixel height of font
 */
int query_font_height ( int fontentry ) {
  return ( x11_fonts [ fontentry ] -> ascent +
	   x11_fonts [ fontentry ] -> descent );
}

/* query_font_width()
 *
 * Brief:   Query pixel width of font
 * Verbose: Since the requested font could be variable spaced or
 *          staticly spaced, no easy test can be done. If it were
 *          obviously static, one knows the width implicitly.
 *          For variable spaced fonts, a sample string must be
 *          provided, and its width is returned.
 *
 * Entry:   font entry, sample string
 * Exit:    pixel width of string
 */
int query_font_width ( int fontentry, char *buffer ) {
  return ( XTextWidth ( x11_fonts [ fontentry ],
			buffer,
			strlen ( buffer ) ) );
}

void begin_text_draw ( void ) {

  /* future: Allocate an array of XPoints, all empty; the draw macro
   * could fill it up, and then the EndVector function could draw
   * all of them in one shot; the line macro would have to do a
   * draw run as well, if colour changed.
   */

#ifdef TIMER
  X11UninstallVirtualAlarm();
#endif

}

/* draw_text()
 *
 * Brief:   Display text onto a drawable
 * Verbose: Display provided text in the face of a previously
 *          loaded font at the specified point. The font to draw
 *          in must have been previously set with set_font().
 *
 * Entry:   GC entry to use, (x,y) coord of baseline, string to write
 * Exit:    boolean; success or failure.
 */
int draw_text ( int gcentry, int x, int y, char *buffer ) {

  XDrawString ( x11_display, x11_current_pixmap,
		x11_colour_gcs [ gcentry ],
		x, y,
		buffer, strlen ( buffer ) );

  return ( 1 );
}

void end_text_draw ( void ) {

  /* Commit the display operations (or the unforced draw
   * operations).
   */
  XFlush ( x11_display );

#ifdef TIMER
  X11InstallVirtualAlarm();
#endif

}

Pixmap create_pixmap ( void ) {
  Pixmap ook;

#ifdef TIMER
  X11InstallVirtualAlarm();
#endif

  ook = XCreatePixmap ( x11_display, x11_window,
			desired_window_width,
			desired_window_height,
			x11_colour_depth );

#ifdef TIMER
  X11InstallVirtualAlarm();
#endif

  return ( ook );
}

int set_target_pixmap ( Pixmap pic ) {
  x11_current_pixmap = pic;
  return ( 1 );
}

/* realize_pixmap() copies the currently selected pixmap onto the
 * virtual display, where it may be finally displayed.
 */
int realize_pixmap ( int destx, int desty ) {

#ifdef TIMER
  X11UninstallVirtualAlarm();
#endif

  if ( unix_option ( "rawtext" ) && unix_option ( "nopics" ) ) {
    return ( 1 ); /* no graphics mode needed */
  }

  /* copy the virtual display onto the real display; this
   * ought to avoid most flicker by making the refresh
   * as atomic as the local X library can do it.
   */
  XCopyArea ( x11_display, x11_current_pixmap,
	      x11_pixmap, x11_window_gc,
	      0, 0,                         /* top left of virtual window */
	      desired_window_width,         /* for its full width */
	      desired_window_height,        /* and full height */
	      destx, desty );               /* to top left of real window */

  /* XFlush ( x11_display ); */            /* call flush_display() instead */

#ifdef TIMER
  X11InstallVirtualAlarm();
#endif

  return ( 1 );
}

Pixmap query_default_pixmap ( void ) {
  return ( x11_pixmap );
}
