/* Modification of basicwin program from Xlib programming manual */

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>

#include <stdio.h>

#include "bitmaps/basic_bitmap"

#define XA_BITMAPDEPTH 1   /* Bitmaps have depth 1 */

/* External functions */

extern char *strsave();

/* Global variables */

Display *display;   /* Physical device information */
int screen;         /* Which screen on the device */

/* Used to determine if window is too small to be used */

#define SMALL 1
#define OK 0

/* Get the graphics context */

void get_GC(win, gc, font_info)
  Window win;
  GC *gc;
  XFontStruct *font_info;
{
  unsigned long valuemask = 0;
  XGCValues values;
  unsigned int line_width = 6;
  int line_style = LineOnOffDash;
  int cap_style = CapRound;
  int join_style = JoinRound;
  int dash_offset = 0;
  static char dash_list[] = {
    12, 24    };
  int list_length = 2;

/* Create default graphics context */

  *gc = XCreateGC(display, win, valuemask, &values);

/* Specify font */

  XSetFont(display, *gc, font_info->fid);
  
/* Specify black foreground since default may be white on white */

  XSetForeground(display, *gc, BlackPixel(display, screen));

/* Set line attributes */

  XSetLineAttributes(display, *gc, line_width, line_style, cap_style,
		     join_style);

/* Set dashes to be line_width in length */

  XSetDashes(display, *gc, dash_offset, dash_list, list_length);
}

/* Load in a font */

void load_font(font_info)
  XFontStruct **font_info;
{
  char *fontname = "9x15";

/* Check out that font */

  if ((*font_info = XLoadQueryFont(display, fontname)) == NULL)
    {
      (void) fprintf(stderr, "Cannot open %s font\n", fontname);
      exit (-1);
    }
}

/* Place the text in the window - nicely centered, of course */

void draw_text(win, gc, font_info, win_width, win_height)
  Window win;
  GC gc;
  XFontStruct *font_info;
  unsigned int win_width, win_height;
{
  int y = 20;   /* Offset from corner of the window */
  char *string[4];
  int i, len[4];
  int width[3];
  char cd[3][50];  /* Window height, width, depth */
  int font_height;
  int initial_y_offset, x_offset;

/* Init. string values */

  string[0] = strsave("Radical Window Action!");
  string[1] = strsave("To terminate program: Press any key");
  string[2] = strsave("or button while in this window");
  string[3] = strsave("Screen Dimensions:");

/* Calculate the lengths of the strings - XTextWidth and XDrawString need
 * them.
 */

  for (i=0; i<4; i++)
    len[i] = strlen(string[i]);

/* Get string widths for centering */

  for (i=0; i<3; i++)
    width[i] = XTextWidth(font_info, string[i], len[i]);

/* Output text, centered on each line */

  XDrawString(display, win, gc, (win_width-width[0])/2, y, string[0], len[0]);
  XDrawString(display, win, gc, (win_width-width[1])/2,
	      (int)(win_height - 35), string[1], len[1]);
  XDrawString(display, win, gc, (win_width-width[2])/2,
	      (int)(win_height - 15), string[2], len[2]);

/* Copy numbers into string variables */

  (void) sprintf(cd[0], " Height: %d pixels",
		 DisplayHeight(display, screen));
  (void) sprintf(cd[1], " Width: %d pixels",
		 DisplayWidth(display, screen));
  (void) sprintf(cd[2], " Depth: %d plane(s)",
		 DefaultDepth(display, screen));

/* Reuse the length variables */

  for (i=0; i<3; i++)
    len[i] = strlen(cd[i]);

  font_height = font_info->max_bounds.ascent +
                font_info->max_bounds.descent;

  initial_y_offset = win_height/2 - font_height -
                     font_info->max_bounds.descent;
  x_offset = (int) win_width/4;
  
/* Draw the strings */

  XDrawString(display, win, gc, x_offset, (int)initial_y_offset, string[3],
	      len[3]);
  for (i=1; i<=3; i++)
    XDrawString(display, win, gc, x_offset, (int)initial_y_offset +
		(i * font_height), cd[i-1], len[i-1]);
}

void draw_graphics(win, gc, window_width, window_height)
  Window win;
  GC gc;
  unsigned int window_width, window_height;
{
  int x, y;
  unsigned int width, height;

  height = window_height/2;
  width = 3 * window_width/4;
  x = window_width/2 - width/2;    /* Center */
  y = window_height/2 - height/2;
  XDrawRectangle(display, win, gc, x, y, width, height);
}

void TooSmall(win, gc, font_info)
  Window win;
  GC gc;
  XFontStruct *font_info;
{
  char *string1 = "Too Small";
  int x_offset, y_offset;

  y_offset = font_info->max_bounds.ascent + 2;
  x_offset = 2;
  
/* Output text, centered on each line */

  XDrawString(display, win, gc, x_offset, y_offset, string1, strlen(string1));
}


/* Main routine - initialization and event loop */

void main(ac, av)
  int ac;
  char *av[];
{
  Window win;
  unsigned int width, height;        /* Window size */
  int x = 0, y = 0;                  /* Position within root window */
  int i;
  unsigned int border_width = 4;     /* border 4 pixels wide */
  unsigned int display_width,
               display_height;
  char *window_name = "Joe the Window";
  char *icon_name = "Pete the Icon";
  Pixmap icon_pixmap;
  XSizeHints size_hints;             /* For the window manager */
  XEvent report;                     /* Elts. in event queue */
  GC gc;                             /* Graphics context */
  XFontStruct *font_info;
  char *display_name = NULL;         /* Use the display on this machine */
  int window_size = 0;               /* OK or SMALL */

/* Check the command line for a display name */

  for (i=1; i<ac; i++)
    {
      if (strcmp(av[i], "-display") == 0)
	display_name = strsave(av[++i]);
    }
  
/* Connect to X server */

  if ( (display=XOpenDisplay(display_name)) == NULL)
    {
      (void) fprintf(stderr,
		     "%s cannot connect to X server %s\n", av[0],
		     XDisplayName(display_name));
      exit(-1);
    }

/* Determine the size of the screen */

  screen = DefaultScreen(display);   /* Get the screen number */

  display_width = DisplayWidth(display, screen);
  display_height = DisplayHeight(display, screen);

/* Size the window so that it has enough room for text */

  width = display_width/3;
  height = display_height/4;

/* Create an opaque window */

  win = XCreateSimpleWindow(display, RootWindow(display, screen),
			    x, y, width, height, border_width,
			    BlackPixel(display, screen),
			    WhitePixel(display, screen));

/* Create a pixmap of depth 1 (bitmap) for the icon */

  icon_pixmap = XCreateBitmapFromData(display, win, icon_bitmap_bits,
				      icon_bitmap_width, icon_bitmap_height);

/* Initialize size hint property for window manager */

  size_hints.flags = PPosition | PSize | PMinSize;
  size_hints.x = x;
  size_hints.y = y;
  size_hints.width = width;
  size_hints.height = height;
  size_hints.min_width = 350;
  size_hints.min_height = 250;

/* Set the standard properties for the window manager (always do this before
 * mapping the window).
 */

  XSetStandardProperties(display, win, window_name, icon_name, icon_pixmap,
			 av, ac, &size_hints);
  
/* Inform the server which events this window is interested in */

  XSelectInput(display, win, ExposureMask | KeyPressMask | ButtonPressMask |
	       StructureNotifyMask);

/* Set the font */

  load_font(&font_info);

/* Create a graphics context for text and drawing */

  get_GC(win, &gc, font_info);

/* Send the window to the display */

  XMapWindow(display, win);

/* Event loop */

  while (1)
    {
      XNextEvent(display, &report);  /* Get next event */
      switch (report.type)
	{
	case Expose:  /* Part or all of the window newly visible */
	  
/* Get rid of all of the other Expose events in the queue - they are 
 * contigious.
 */

	  while (XCheckTypedEvent(display, Expose, &report));
	  
/* Check window size, redraw contents */

	  if (window_size == SMALL)
	    TooSmall(win, gc, font_info);
	  else
	    {
	      draw_text(win, gc, font_info, width, height);
	      draw_graphics(win, gc, width, height);
	    }
	  break;
	  
	case ConfigureNotify:  /* Window has been resized */

/* Get new width and height */

	  width = report.xconfigure.width;
	  height = report.xconfigure.height;
	  if ((width < size_hints.min_width) ||
	      (height < size_hints.min_height))
	    window_size = SMALL;
	  else
	    window_size = OK;
	  break;

	case ButtonPress:  /* Mouse event */
	case KeyPress: /* Keyboard event */
	      
/* Exit gracefully */

	  XUnloadFont(display, font_info->fid);
	  XFreeGC(display, gc);
	  XCloseDisplay(display);
	  exit(1);

	default:  /* Throw away all other events */
	  break;
	} /* End switch */
    } /* End event loop (while) */
}
