/*
 * X11_Window.C - X11 window driver.
 *
 * Copyright (C) 1992, Christoph Streit (streit@iam.unibe.ch)
 *                     University of Berne, Switzerland
 * All rights reserved.
 *
 * This software may be freely copied, modified, and redistributed
 * provided that this copyright notice is preserved on all copies.
 *
 * You may not distribute this software, in whole or in part, as part of
 * any commercial product without the express consent of the authors.
 *
 * There is no warranty or other guarantee of fitness of this software
 * for any purpose.  It is provided solely "as is".
 *
 */

#include "X11_Window.h"
#include "Error.h"

#include "X11_Window_icon"

//___________________________________________________________ X11_Window

X11_Window::X11_Window()
: display_name(NULL), segments(NULL), RequestSize(10), segmentsInBuffer(0)
{}

void X11_Window::open(int width, int height, const rcString& windowName)
{
  BaseWindow::open(width, height, windowName);
  
  if ((display = XOpenDisplay(display_name)) == NULL) 
    Error(ERR_PANIC, rcString("cannot connect to X server ") 
	             + XDisplayName(display_name));

  /*
   * Buffering of XSegments is done to achieve better preformance.
   * Check maximal request size of display server.
   */
  RequestSize = (XMaxRequestSize(display) < 10000) 
                 ? XMaxRequestSize(display) : 10000;
  segments = new XSegment[RequestSize];

  int screen = DefaultScreen(display);
  win = XCreateSimpleWindow(display, RootWindow(display, screen),
			    0, 0, resX, resY, 1, 
			    BlackPixel(display, screen), 
			    WhitePixel(display, screen));

  Pixmap iconPixmap = XCreateBitmapFromData(display, win,
					    X11_Window_icon_bits,
					    X11_Window_icon_width,
					    X11_Window_icon_height);

  gc = XCreateGC(display, win, 0, NULL);
  XSetForeground(display, gc, BlackPixel(display, screen));

  XSizeHints hints;
  hints.flags = PSize | PMinSize | PMaxSize;
  hints.width = resX; hints.height = resY;
  hints.min_width = resX; hints.min_height = resY;
  hints.max_width = resX; hints.max_height = resY;
  XSetStandardProperties(display, win,
			 (char*) windowName.chars(), (char*) windowName.chars(), 
			 iconPixmap, 0, 0, &hints);

  XSelectInput(display, win, ExposureMask | KeyPressMask);
  XMapWindow(display, win);

  /*
   * Wait for window to come up.
   */
  XEvent report;
  while(1) {
    XNextEvent(display, &report);
    if (report.type == Expose)
      if (report.xexpose.count == 0)
	break;
  }

  XSetWindowAttributes attributes;
  attributes.backing_store = Always;
  XChangeWindowAttributes(display, win, CWBackingStore, &attributes);
}

void X11_Window::close()
{
  BaseWindow::close();
  XFreeGC(display, gc);
  XCloseDisplay(display);
  delete [] segments;
}

void X11_Window::clear()
{
  BaseWindow::clear();
  XClearWindow(display, win);
}

char X11_Window::waitForKey()
{
  XEvent event;
  const int bufsize = 20;
  char buffer[bufsize];
  KeySym keysym;
  XComposeStatus stat;

  while(1) {
    XNextEvent(display, &event);
    if (event.type == KeyPress &&
	XLookupString(&event.xkey, buffer, bufsize, &keysym, &stat) == 1)
      return buffer[0];
  }
}

void X11_Window::writeText(const rcString& msg, int x, int y)
{
  XDrawString(display, win, gc, x, y, msg.chars(), msg.length());
  XFlush(display);
}

/*
 * Buffer line segments. If we have request size XSegments,
 * bring the hole buffer to screen.
 */

void X11_Window::drawLine(int x1, int y1, int x2, int y2)
{
  if (segmentsInBuffer < RequestSize) {
    segments[segmentsInBuffer].x1 = x1;
    segments[segmentsInBuffer].y1 = resY-y1;
    segments[segmentsInBuffer].x2 = x2;
    segments[segmentsInBuffer].y2 = resY-y2;
    segmentsInBuffer++;
  }
  else {
    XDrawSegments(display, win, gc, segments, segmentsInBuffer);
    segmentsInBuffer = 0;
  }
}

/*
 * Flush segment buffer.
 */

void X11_Window::flush()
{
  BaseWindow::flush();
  if (segmentsInBuffer > 0) {
    XDrawSegments(display, win, gc, segments, segmentsInBuffer);
    segmentsInBuffer = 0;
  }

  XFlush(display);
}

