/*******************************************************************************
+
+  LEDA 3.5
+
+  _x_basic.c
+
+  This file is part of the LEDA research version (LEDA-R) that can be 
+  used free of charge in academic research and teaching. Any commercial
+  use of this software requires a license which is distributed by the
+  LEDA Software GmbH, Postfach 151101, 66041 Saarbruecken, FRG
+  (fax +49 681 31104).
+
+  Copyright (c) 1991-1997  by  Max-Planck-Institut fuer Informatik
+  Im Stadtwald, 66123 Saarbruecken, Germany     
+  All rights reserved.
+ 
*******************************************************************************/

 
//------------------------------------------------------------------------------
// basic graphics x_... functions for libW (declared in <LEDA/impl/x_basic.h>)
// implemented by X11 library functions
//
// S. Naeher (1995,1996,1997)
//------------------------------------------------------------------------------


#include <LEDA/impl/x_basic.h>
#include <LEDA/bitmaps/leda_icon.xpm>

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <unistd.h>


#if defined(sun) && !defined(__SVR4)
extern "C" int select(int,void*,void*,void*,void*);
#endif

#if defined(mips)
#include <bstring.h>
#endif

#if defined(_AIX)
#include <strings.h>
#include <sys/select.h>
#else
#include <sys/time.h>
#include <sys/times.h>
#endif



#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>


inline void SWAP(int& x1, int& x2)
{ int t = x1; x1 = x2; x2 = t; }


#define NUMBER_OF_COLORS 512 


#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

static char dot_mask[2] = { 2,8 };
static char dash_mask[2] = { 8,8 };

#if defined(__linux__)
static char* text_font_name = "-adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*";
static char* bold_font_name = "-adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*";
static char* fixed_font_name = "7x14";
#else
static char* text_font_name = "-adobe-helvetica-medium-r-*-*-14-*-*-*-*-*-*-*";
static char* bold_font_name = "lucidasans-bold-12";
static char* fixed_font_name = "9x15";
#endif

static char* text_font_name2 = "-adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*";
static char* bold_font_name2 = "-adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*";
static char* fixed_font_name2 = "9x15";


static Display  *display = NULL;
static int      screen;
static XEvent   event;
static Colormap color_map;

static XFontStruct *text_font;
static int text_char_width;

static XFontStruct *bold_font;
static int bold_char_width;

static XFontStruct *fixed_font;
static int fixed_char_width;

static int MAX_COLORS = 512;


//static int Read_Only_Colors = 0;

static int Read_Only_Colors = 1;


static unsigned long color_pix[NUMBER_OF_COLORS];
static char*         color_name[NUMBER_OF_COLORS];
static int           color_id[NUMBER_OF_COLORS];
static int           color_count= 0;

struct  x11_win
{
  Drawable     win;
  Drawable     win_save;
  Drawable     buf;
  int          buf_w;
  int          buf_h;

  XGCValues    gc_val;
  GC           gc;

  XFontStruct* font;
  int          char_width;

  int          LINEWIDTH;
  line_style   LINESTYLE;
  int          JOINSTYLE;
  drawing_mode MODE;
  text_mode    TEXTMODE;
  int          COLOR;

  int          mapped;

  void* inf;

  void (*redraw)(void*);
};

const int MAXWIN = 256;

static x11_win* wlist[MAXWIN];
static int wcount = 0;



//------------------------------------------------------------------------------
// auxiliary functions
//------------------------------------------------------------------------------

/*
static char* duplicate_string(const char* p)
{ char* q = new char[strlen(p)+1];
  strcpy(q,p);
  return q;
}
*/

static int get_char_width(XFontStruct* fp)
{ XCharStruct char_struct;
  int asc, desc, dir;
  XQueryTextExtents(display,fp->fid,"s",1, &dir,&asc,&desc,&char_struct);
  return char_struct.width;
}



//------------------------------------------------------------------------------
// display functions
//------------------------------------------------------------------------------

static int x_error_handler(Display* disp, XErrorEvent* err)
{ char msg[80];
  XGetErrorText(disp,err->error_code,msg,80);
  fprintf(stderr, "\n X Error: %s\n\n",msg);
  abort();
  return 0;
}

void x_open_display(void)
{ 
  if (display != NULL) return;

  if ((display = XOpenDisplay(0)) == NULL)	
  { fprintf(stderr, "Can\'t open display: \n");
    abort();
   }

  screen    = DefaultScreen(display);
  color_map = DefaultColormap(display,screen);


  if ((text_font = XLoadQueryFont(display,text_font_name)) == NULL)
  if ((text_font = XLoadQueryFont(display,text_font_name2)) == NULL)
  { fprintf(stderr,"Error: Cannot load text font");
    abort();
   }

  if ((bold_font = XLoadQueryFont(display,bold_font_name)) == NULL)
  if ((bold_font = XLoadQueryFont(display,bold_font_name2)) == NULL)
  { fprintf(stderr,"Error: Cannot load bold font");
    abort();
   }

  if ((fixed_font = XLoadQueryFont(display,fixed_font_name)) == NULL)
  if ((fixed_font = XLoadQueryFont(display,fixed_font_name2)) == NULL)
  { fprintf(stderr,"Error: Cannot load fixed font\n");
    abort();
   }


  if (DefaultDepth(display,screen) > 1)
  { unsigned long plane_masks;
    //while (MAX_COLORS > 0 && 
    //       !XAllocColorCells(display,color_map,False,&plane_masks,
    //                         0,color_pix, MAX_COLORS)) MAX_COLORS--;

    if (!Read_Only_Colors)
    {
     if (!XAllocColorCells(display,color_map,False,&plane_masks,0,color_pix,64))
        Read_Only_Colors = 1;
     else
        MAX_COLORS = 64;
     }
   }


  // 17 predefined colors from /usr/lib/X11/rgb.txt

  color_count = 0;
  x_new_color("white");          // 0: white
  x_new_color("black");          // 1: black
  x_new_color("red");            // 2: red
  x_new_color("green2");         // 3: green
  x_new_color("blue");           // 4: blue
  x_new_color("yellow");         // 5: yellow
  x_new_color("purple");         // 6: violet
  x_new_color("darkorange");     // 7: orange
  x_new_color("cyan");           // 8: cyan
  x_new_color("sienna");         // 9: brown
  x_new_color("magenta");        //10: pink 
  x_new_color("#0cb3a0");        //11: green2
  x_new_color("cornflowerblue"); //12: blue2
  x_new_color("grey86");         //13: grey1
  x_new_color("grey70");         //14: grey2
  x_new_color("grey35");         //15: grey3

  if (color_count < 16) {
    fprintf(stderr,"\nCould not allocate all 16 basic colors.\
                      Maybe another X client (netscape?) is\
                      using too many colors.\n\n");
  }

//x_new_color("ivory");          //16: ivory
  x_new_color("#ffffe0");        //16: ivory


  text_char_width = get_char_width(text_font);
  bold_char_width = get_char_width(bold_font);
  fixed_char_width = get_char_width(fixed_font);

  XSetErrorHandler(x_error_handler);

  // NULL window

  x11_win* wp = new x11_win;

  wp->win = RootWindow(display,screen);

  wp->font       = fixed_font;
  wp->char_width = fixed_char_width;
  wp->LINEWIDTH = 1;
  wp->LINESTYLE = solid;
  wp->JOINSTYLE = 1;
  wp->MODE      = src_mode;
  wp->TEXTMODE  = transparent;
  wp->COLOR     = black;
  wp->redraw    = 0;
  wp->inf       = 0;
  wp->mapped    = 0;

  wlist[0] = wp;
  wcount = 0;


}


void x_close_display()
{ int i;
  if (display) 
  { XCloseDisplay(display); 
    for(i=0; i < color_count; i++) delete color_name[i];
    for(i=0; i <= wcount; i++) 
        if (wlist[i]) delete wlist[i];
    display = 0; 
   } 
}


int x_display_width(void)
{ x_open_display();
  return DisplayWidth(display,screen);  
 }

int x_display_height(void)
{ x_open_display();
  return DisplayHeight(display,screen); 
 }

int x_display_depth(void)
{ x_open_display();
  return DefaultDepth(display,screen); 
 }

int x_display_bits_saved(void)
{ return XDoesBackingStore(XScreenOfDisplay(display,screen)); }
 

void x_flush_display(void)
{ XFlush(display); }



//------------------------------------------------------------------------------
// colors
//------------------------------------------------------------------------------

void x_set_palette(int i, int r, int g, int b)   
{ 
  x_open_display();

  if (Read_Only_Colors)
  { XColor xcolor;
    xcolor.red   = (unsigned short)r*256;
    xcolor.green = (unsigned short)g*256;
    xcolor.blue  = (unsigned short)b*256;
    XAllocColor(display,color_map,&xcolor);
    color_pix[i] = xcolor.pixel;
   }
  else
  { XColor color;
    color.pixel = color_pix[i];
    XQueryColor(display,color_map,&color);
    color.red   = (unsigned short)r*256;
    color.green = (unsigned short)g*256;
    color.blue  = (unsigned short)b*256;
    XStoreColor(display,color_map,&color);
   }
}


void x_get_palette(int i, int* r, int* g, int* b)   
{ x_open_display();
  XColor xcolor;
  xcolor.pixel = color_pix[i];
  XQueryColor(display,color_map,&xcolor);
  *r = xcolor.red/256;
  *g = xcolor.green/256;
  *b = xcolor.blue/256; 
 }


int x_new_color(int r, int g, int b)
{ 
  x_open_display();

  if (r < 0) r = 0;
  if (g < 0) g = 0;
  if (b < 0) b = 0;

  if (r > 255) r = 255;
  if (g > 255) g = 255;
  if (b > 255) b = 255;

  int col_id = r;

  col_id = (col_id << 8) + g;
  col_id = (col_id << 8) + b;


  for (int i = 0; i < color_count; i++)
     if (color_id[i] == col_id) return i;  // has been allocated before

  if (color_count >= MAX_COLORS) 
  { fprintf(stderr,"Too many colors (only %d available)\n",MAX_COLORS);
    abort();
   }

  int failed = 0;

  if (DefaultDepth(display,screen) == 1) // monochrome display
    if (r == 255 && g == 255 &&  b == 255)
        color_pix[color_count] = WhitePixel(display,screen);
     else
        color_pix[color_count] = BlackPixel(display,screen);
  else
     { XColor xcolor;
       xcolor.red   = (unsigned short)r*256;
       xcolor.green = (unsigned short)g*256;
       xcolor.blue  = (unsigned short)b*256;
       if (Read_Only_Colors)
         { if (XAllocColor(display,color_map,&xcolor))
              color_pix[color_count] = xcolor.pixel;
           else
            { color_pix[color_count] = white;
              failed = 1;
             }
          }
       else
         { xcolor.pixel = color_pix[color_count];
           xcolor.flags = DoRed | DoGreen | DoBlue;
           XStoreColor(display,color_map,&xcolor);
          }
 
      }

  color_id[color_count] = col_id;
  color_count++;

  return (failed) ? -1 : color_count;
}



int x_new_color(const char* name)
{ XColor xcolor;
  XParseColor(display, color_map, name, &xcolor);
  int r = xcolor.red/256;
  int g = xcolor.green/256;
  int b = xcolor.blue/256;
  return x_new_color(r,g,b);
}



static Pixmap xpm_pixmap(Window win, char** xpm, int& width, int& height) 
{
  int colors;
  int depth;
  sscanf(*xpm,"%d %d %d",&width,&height,&colors, &depth);

  XGCValues  gc_val;
  gc_val.background = color_pix[0];
  gc_val.foreground = color_pix[0];
  gc_val.function  = GXcopy; 
  GC gc = XCreateGC(display,RootWindow(display,screen),
                    GCBackground | GCForeground | GCFunction,
                    &gc_val);

  Pixmap pm = XCreatePixmap(display,win,
                            width, height, DefaultDepth(display,screen));
 
  XFillRectangle(display,pm,gc,0,0,width,height);


 int color_table[128];
 int i; 
 for(i=0; i<128; i++) color_table[i] = -1;

 for(i=0; i<colors; i++)
 { xpm++;
   char c;
   int  x;
   sscanf(*xpm,"%c c #%x",&c,&x);
   int b = x % 256; x = x/256;
   int g = x % 256; x = x/256;
   int r = x % 256;
   int col = x_new_color(r,g,b);
   if (col == -1)
      color_table[c] = (r+g+b > 384) ? white : black;
   else
      color_table[c] = col;
 }

 char** picture = new char*[height];
 for(i=0; i<height; i++) picture[i] = *++xpm;

 int N = width*height;
 XPoint* points = new XPoint[N];

 for(int c=0; c<128; c++)
 { int col = color_table[c];
   if (col == -1) continue;

   gc_val.foreground = color_pix[col];
   XChangeGC(display,gc,GCForeground,&gc_val);

   int n = 0;
   for(int y=0; y<height; y++)
   { char* line = picture[y];
     for(int x=0; x<width; x++)
       if (line[x] == c)
       { points[n].x = x;
         points[n].y = y;
         n++;
        }
    }
   XDrawPoints(display,pm,gc,points,n,CoordModeOrigin);
 }

 delete[] points;

 return pm;
}



static Window xpm_icon_window(char** xpm) 
{
  Window win = XCreateSimpleWindow(display,RootWindow(display,screen),
                                           0, 0, 1, 1, 0,
                                           BlackPixel(display,screen),
                                           BlackPixel(display,screen));

 int width, height;
 Pixmap pm = xpm_pixmap(win,xpm, width, height);
 XResizeWindow(display,win,width,height);
 XSetWindowBackgroundPixmap(display,win,pm);
 XFreePixmap(display,pm);
 return win;
}


 
  

//------------------------------------------------------------------------------
// windows
//------------------------------------------------------------------------------

int x_create_window(void* inf, int width, int height, int bg_col,
                    const char* header, const char* /*label*/, int parent,
                    void (*func)(void*))
{
  x11_win* wp = new x11_win;

  XSetWindowAttributes attrib;
  attrib.override_redirect = True;
  attrib.backing_store = Always;

  wp->win= XCreateWindow(display, wlist[parent]->win, 0, 0,
                         width,height,1,DefaultDepth(display,screen),
                         InputOutput,DefaultVisual(display,screen), 
                         CWBackingStore, &attrib);


  XSelectInput(display,wp->win, EnterWindowMask | LeaveWindowMask    |
                                KeyPressMask    | PointerMotionMask  | 
                                ButtonPressMask | ButtonReleaseMask  |
                                ExposureMask    | StructureNotifyMask ); 
  
  if (DefaultDepth(display,screen) == 1) 
    XSetWindowBackground(display,wp->win, WhitePixel(display, screen)); 
  else
    XSetWindowBackground(display,wp->win,color_pix[bg_col]);
 
  XStoreName(display,wp->win,header);
  XSetIconName(display,wp->win,"LEDA-R 3.5");
  //XSetIconName(display,wp->win,label);

/*
  Window icon_window = XCreateSimpleWindow(display,
                                           RootWindow(display,screen),
                                           0,0,
                                           leda_icon_width,
                                           leda_icon_height,
                                           0,
                                           BlackPixel(display,screen),
                                           BlackPixel(display,screen));

  Pixmap icon_pixmap = XCreatePixmapFromBitmapData(display, icon_window, 
                                            (char*)leda_icon_bits,
                                            leda_icon_width, leda_icon_height,
                                            //BlackPixel(display,screen),
                                            //WhitePixel(display,screen),
                                            color_pix[blue],
                                            color_pix[yellow],
                                            DefaultDepth(display,screen));

  XSetWindowBackgroundPixmap(display,icon_window,icon_pixmap);
*/


  Window icon_window = xpm_icon_window(leda_icon);


  XWMHints wm_hints;
  wm_hints.flags = StateHint | IconWindowHint | InputHint;
  wm_hints.icon_window = icon_window;
  wm_hints.initial_state = NormalState;
  wm_hints.input = True;
  XSetWMProperties(display,wp->win,0,0,0,0,0,&wm_hints,0);


  wp->gc_val.background = color_pix[0];
  wp->gc_val.foreground = color_pix[1];
  wp->gc_val.function  = GXcopy; 
  wp->gc_val.line_style = LineSolid;
  wp->gc_val.cap_style = CapButt;
  wp->gc_val.join_style = JoinMiter;
  wp->gc_val.line_width = 1;
  wp->gc_val.font = text_font->fid;

  wp->gc = XCreateGC(display,RootWindow(display,screen),
                    GCBackground | GCForeground |GCFunction |
                    GCLineStyle  | GCLineWidth  |GCFont,
                    &(wp->gc_val));

  wp->char_width = text_char_width;
  wp->font = text_font;
  wp->LINEWIDTH = 1;
  wp->JOINSTYLE = 1;
  wp->LINESTYLE = solid;
  wp->MODE      = src_mode;
  wp->COLOR     = black;
  wp->TEXTMODE  = transparent;
  wp->redraw    = func;
  wp->mapped    = 0;
  wp->inf       = inf;
  wp->buf       = 0;
  wp->buf_w     = 0;
  wp->buf_h     = 0;
  wp->win_save  = 0;

  XDrawLine(display,wp->win,wp->gc,0,0,width-1,0);

  int i = 1;
  while (i <= wcount)
  { if (wlist[i] == 0) break;
    i++;
   }
  if (i > wcount) wcount = i;

  wlist[i] = wp;

  return i;
}


void x_resize_window(int w, int width, int height) 
{ x11_win* wp = wlist[w];
  XResizeWindow(display,wp->win,width,height);
}


int x_window_opened(int w) 
{ return wlist[w] && wlist[w]->mapped; }


void x_open_window(int w, int xpos, int ypos, int width, int height, int pw)
{
  x11_win* wp = wlist[w];

  if (wp->mapped) return;

  if (pw > 0) 
    XReparentWindow(display,wp->win,wlist[pw]->win,xpos,ypos);
  
  if (pw < 0)
  { XSetWindowAttributes attrib;
    attrib.override_redirect = True;
    XChangeWindowAttributes(display,wp->win, CWOverrideRedirect,&attrib);
   }


  XMoveResizeWindow(display,wp->win,xpos,ypos,width,height);

  XSizeHints size_hints;
  size_hints.flags = PPosition;
  size_hints.x = xpos;
  size_hints.y = ypos;
  
  XSetWMProperties(display,wp->win,0,0,0,0,&size_hints,0,0);

  XMapRaised(display,wp->win);

  wp->mapped = 1;


  XEvent e;
  do XNextEvent(display, &e);
  while (e.type != MapNotify);

  while (XCheckMaskEvent(display, 
                         KeyPressMask    | // PointerMotionMask  | 
                         ButtonPressMask | ButtonReleaseMask  |
                         ExposureMask    | StructureNotifyMask, &event)); 
}


void x_close_window(int w)
{ wlist[w]->mapped = 0;
  XUnmapWindow(display,wlist[w]->win);
 }


void x_destroy_window(int w)
{ XDestroyWindow(display,wlist[w]->win); 
  delete wlist[w];
  wlist[w] = 0;
  if (w == wcount) wcount--;
 }
 
void x_clear_window(int w)
{ //x11_win* wp = wlist[w];
  //XClearWindow(display,wp->win);
  int save_col = x_set_color(w,0);
  drawing_mode save_mode = x_set_mode(w,src_mode);
  x_box(w,0,0,x_window_width(w),x_window_height(w));
  x_set_color(w,black);
  x_rect(w,0,0,x_window_width(w)-1,x_window_height(w)-1);
  x_set_color(w,save_col);
  x_set_mode(w,save_mode);
}


void* x_window_inf(int w) { return wlist[w]->inf; }

int x_window_height(int w)
{ x11_win* wp = wlist[w];
  Window win1;
  int xpos,ypos;
  unsigned width,height,bw,dep;
  XGetGeometry(display,wp->win,&win1,&xpos,&ypos,&width,&height,&bw,&dep);
  return height;
 }

int x_window_width(int w)
{ x11_win* wp = wlist[w];
  Window win1;
  int xpos,ypos;
  unsigned width,height,bw,dep;
  XGetGeometry(display,wp->win,&win1,&xpos,&ypos,&width,&height,&bw,&dep);
  return width;
 }


void x_window_position(int w, int *x, int *y)
{ x11_win* wp = wlist[w];
  XSizeHints size_hints;
  long flags = PPosition;
  XGetWMNormalHints(display,wp->win,&size_hints,&flags);
  *x = size_hints.x;
  *y = size_hints.y;
 }






//------------------------------------------------------------------------------
// drawing functions
//------------------------------------------------------------------------------

static void adjust_line(int s, int& x1, int& y1, int& x2, int& y2)
{ int dx = x2 - x1;
  int dy = y2 - y1;
  if (dx == 0 && dy == 0) return;

  int xoff = s;
  int yoff = s;

  if (dx < 0) { dx = -dx; xoff = -s; }
  if (dy < 0) { dy = -dy; yoff = -s; }

  if ( dx >= dy) x2 += xoff;
  if ( dx <= dy) y2 += yoff;
 }



void x_pixel(int w, int x, int y)
{ x11_win* wp = wlist[w];
  XDrawPoint(display,wp->win,wp->gc,x,y); 
 }


int x_get_pixel(int, int, int) { return 0; }



void x_point(int w, int x, int y)
{ x11_win* wp = wlist[w];
  XDrawPoint(display,wp->win,wp->gc,x,y); 
  XDrawPoint(display,wp->win,wp->gc,x-2,y-2); 
  XDrawPoint(display,wp->win,wp->gc,x-1,y-1); 
  XDrawPoint(display,wp->win,wp->gc,x+1,y+1); 
  XDrawPoint(display,wp->win,wp->gc,x+2,y+2); 
  XDrawPoint(display,wp->win,wp->gc,x-2,y+2); 
  XDrawPoint(display,wp->win,wp->gc,x-1,y+1); 
  XDrawPoint(display,wp->win,wp->gc,x+1,y-1); 
  XDrawPoint(display,wp->win,wp->gc,x+2,y-2); 
 }


void x_pixels(int w, int n, int *x, int *y)
{ x11_win* wp = wlist[w];
  XPoint* points = new XPoint[n];
  int i;
  for(i=0; i<n; i++)
  { points[i].x = (short)x[i];
    points[i].y = (short)y[i];
   }
  XDrawPoints(display,wp->win,wp->gc,points,n,CoordModeOrigin);
  delete[] points;
 }


void x_line(int w, int x1, int y1, int x2, int y2)
{ x11_win* wp = wlist[w];
 
/*
  if ((x1/(1<<15)) % 2) x1 = -x1;
  if ((x2/(1<<15)) % 2) x2 = -x2;
  if ((y1/(1<<15)) % 2) y1 = -y1;
  if ((y2/(1<<15)) % 2) y2 = -y2;
*/


  int jstyle = wp->JOINSTYLE;

/*
#if (XtVersion > 11004)
  if (x1 > x2 || (x1 == x2 && y1 > y2)) 
#else
  if (x1 < x2 || (x1 == x2 && y1 < y2)) 
#endif
*/

  if (x1 > x2 || (x1 == x2 && y1 > y2)) 
  { SWAP(x1,x2);
    SWAP(y1,y2);
    if (jstyle == 1) 
       jstyle = 2; 
    else 
       if (jstyle == 2) jstyle = 1; 
   }

  if ((jstyle & 1) == 0) adjust_line(-1,x2,y2,x1,y1);
  if ((jstyle & 2) == 1) adjust_line(+1,x1,y1,x2,y2);

  XDrawLine(display,wp->win,wp->gc,(short)x1,(short)y1,(short)x2,(short)y2); 
 }


void x_lines(int w, int n, int *x1, int *y1, int* x2, int* y2)
{ 
  x11_win* wp = wlist[w];
  XSegment* segs = new XSegment[n];

  for(int i=0; i<n; i++)
  { segs[i].x1 = x1[i];
    segs[i].y1 = y1[i];
    segs[i].x2 = x2[i];
    segs[i].y2 = y2[i];
  }

  XDrawSegments(display,wp->win,wp->gc,segs,n);
  delete[] segs;
}
 


void x_rect(int w, int x1, int y1, int x2, int y2)
{ x11_win* wp = wlist[w];
  if (x1 > x2) SWAP(x1,x2);
  if (y1 > y2) SWAP(y1,y2);
  XDrawRectangle(display,wp->win,wp->gc,x1,y1,x2-x1,y2-y1);
}
 
 
void x_box(int w, int x1, int y1, int x2, int y2)
{ x11_win* wp = wlist[w];
  if (x1 > x2) SWAP(x1,x2);
  if (y1 > y2) SWAP(y1,y2);
  XFillRectangle(display,wp->win,wp->gc,x1,y1,x2-x1+1,y2-y1+1);

}


/* too slow 
void x_arc(int w, int x0, int y0, int r1, int r2, double start, double angle)
{ x11_win* wp = wlist[w];
  int s = (int)(360*32*start/M_PI);
  int a = (int)(360*32*angle/M_PI);
  XDrawArc(display,wp->win,wp->gc,x0-r1,y0-r2,2*r1,2*r2,s,a);
}
*/


#define setarcpix(win,x,y)\
{ if (orient >= 0)\
   { if (ax*y >= ay*x && x*by >= y*bx)\
        XDrawPoint(display,wp->win,wp->gc,x0+x,y0+y); }\
  else  if (ax*y >= ay*x || x*by >= y*bx)\
          XDrawPoint(display,wp->win,wp->gc,x0+x,y0+y);\
 }
 

void x_arc(int w, int x0, int y0, int r0, int /* r1 */, double start, double angle)
{ x11_win* wp = wlist[w];

  double ax =  cos(start);
  double ay = -sin(start);
  double bx =  cos(start+angle);
  double by = -sin(start+angle);
  double orient = ax*by - ay*bx;

  for (int r = r0-wp->LINEWIDTH/2; r <= r0+wp->LINEWIDTH/2; r++)
  { int y = r;
    int x = 1;
    int e = 3 - 2*y;

    setarcpix(win, 0, r);
    setarcpix(win, 0,-r);
    setarcpix(win, r, 0);
    setarcpix(win,-r, 0);

    while (x < y)
    { setarcpix(win, x, y);
      setarcpix(win, x,-y);
      setarcpix(win,-x, y);
      setarcpix(win,-x,-y);
      setarcpix(win, y, x);
      setarcpix(win, y,-x);
      setarcpix(win,-y, x);
      setarcpix(win,-y,-x);
      x++;
      if (e>=0) { y--; e = e - 4*y; }
      e = e + 4*x + 2;
     }

    if (x == y)
    { setarcpix(win, x, y);
      setarcpix(win, x,-y);
      setarcpix(win,-x, y);
      setarcpix(win,-x,-y);
     }
  }
}






void x_ellipse(int w, int x0, int y0, int r1, int r2)
{ x11_win* wp = wlist[w];
  XDrawArc(display,wp->win,wp->gc,x0-r1,y0-r2,2*r1,2*r2,0,360*64); }


void x_fill_arc(int w, int x0, int y0, int r1, int r2, double start, double angle)
{ x11_win* wp = wlist[w];
  int s = (int)(360*32*start/M_PI);
  int a = (int)(360*32*angle/M_PI);
  XFillArc(display,wp->win,wp->gc,x0-r1,y0-r2,2*r1,2*r2,s,a);
}


/*
void x_circle(int w, int x0, int y0, int r)
{ x11_win* wp = wlist[w];
  XDrawArc(display,wp->win,wp->gc,x0-r,y0-r,2*r,2*r,0,360*64); }

void x_fill_circle(int w, int x0, int y0, int r)
{ x11_win* wp = wlist[w];
  XFillArc(display,wp->win,wp->gc,x0-r,y0-r,2*r,2*r,0,360*64); }
*/


#define SETPIX(x,y)\
 XDrawPoint(display,wp->win,wp->gc,x,y); 

void x_circle(int w, int x0,int y0,int r0)
{ x11_win* wp = wlist[w];

  for (int r = r0-wp->LINEWIDTH/2; r <= r0+wp->LINEWIDTH/2; r++)
  { int y = r;
    int x = 0;
    int e = 3-2*y;

    XDrawPoint(display,wp->win,wp->gc,x,y); 

    SETPIX(x0,y0+r);
    SETPIX(x0,y0-r);
    SETPIX(x0+r,y0);
    SETPIX(x0-r,y0);

    for (x=1;x<y;)
      { SETPIX(x0+x,y0+y);
        SETPIX(x0+x,y0-y);
        SETPIX(x0-x,y0+y);
        SETPIX(x0-x,y0-y);
        SETPIX(x0+y,y0+x);
        SETPIX(x0+y,y0-x);
        SETPIX(x0-y,y0+x);
        SETPIX(x0-y,y0-x);
        x++;
        if (e>=0) { y--; e = e - 4*y; }
        e = e + 4*x + 2;
       }

    if (x == y)
    { SETPIX(x0+x,y0+y);
      SETPIX(x0+x,y0-y);
      SETPIX(x0-x,y0+y);
      SETPIX(x0-x,y0-y);
     }
  }
}



#define HLINE(x,y)\
XDrawLine(display,wp->win,wp->gc,x0-(x),y0+(y),x0+(x)+1,y0+(y))

void x_fill_circle(int w, int x0, int y0, int r)
{ x11_win* wp = wlist[w];

  int y = 1;
  int x = r;
  int e = 3-2*r;

  HLINE(x,0);

  while (y <= x)
  { HLINE(x,+y);
    HLINE(x,-y);
    if (y < x && e >= 0)
    { HLINE(y,+x);
      HLINE(y,-x);
      x--;
      e = e - 4*x;
     }
    y++;
    e = e + 4*y + 2;
   }
}



void x_fill_ellipse(int w, int x0, int y0, int r1, int r2)
{ x11_win* wp = wlist[w];
  XFillArc(display,wp->win,wp->gc,x0-r1,y0-r2,2*r1,2*r2,0,360*64); }


void x_fill_polygon(int w, int n, int *xcoord, int *ycoord)
{ x11_win* wp = wlist[w];
  XPoint* edges = new XPoint[n];
  for(int i=0;i<n;i++) 
  { edges[i].x = (short) xcoord[i];
    edges[i].y = (short) ycoord[i];
   }

  XFillPolygon(display,wp->win,wp->gc,edges,n,Nonconvex,CoordModeOrigin);

  delete edges;
}


void x_polygon(int w, int n, int *xcoord, int *ycoord)
{ x11_win* wp = wlist[w];
  XPoint* P = new XPoint[n+1];
  for(int i=0;i<n;i++) 
  { P[i].x = (short) xcoord[i];
    P[i].y = (short) ycoord[i];
   }
  P[n] = P[0];

  XDrawLines(display,wp->win,wp->gc,P,n+1,CoordModeOrigin);

  delete P;
}




//------------------------------------------------------------------------------
// text
//------------------------------------------------------------------------------

void x_text(int w, int x, int y, const char* s, int l)
{ x11_win* wp = wlist[w];
  y += wp->font->ascent;
  if (unsigned(l) > strlen(s)) l = strlen(s);
  if (wp->TEXTMODE == transparent)
     XDrawString(display,wp->win,wp->gc,x,y,s,l);
  else
     XDrawImageString(display,wp->win,wp->gc,x,y,s,l);
}

void x_text(int w, int x, int y, const char* s)
{ x11_win* wp = wlist[w];
  y += wp->font->ascent;
  if (wp->TEXTMODE == transparent)
     XDrawString(display,wp->win,wp->gc,x,y,s,strlen(s));
  else
     XDrawImageString(display,wp->win,wp->gc,x,y,s,strlen(s));
}


void x_ctext(int w, int x, int y, const char* s)
{ x -= x_text_width(w,s)/2;
  y -= x_text_height(w,s)/2;
  x_text(w,x,y,s);
}


 
int x_text_width(int w,const char* s)
{ return XTextWidth(wlist[w]->font,s,strlen(s)); }

int x_text_width(int w,const char* s, int l)
{ return XTextWidth(wlist[w]->font,s,l); }
 
 
int x_text_height(int w, const char*)
{ XFontStruct* fp = wlist[w]->font;
  return fp->ascent+(2*fp->descent)/3; }




//------------------------------------------------------------------------------
// pixrects
//------------------------------------------------------------------------------


struct x_image {
 int w;
 int h;
 Pixmap P;
 char* buf;
};


char* x_create_pixrect(int w, int width, int height, char* data,
                       int fg_col, int bg_col) 
{ 
  x11_win* wp = wlist[w];
  x_image *im=new x_image;
  im->w=width;
  im->h=height;
  im->P=XCreatePixmapFromBitmapData(display,wp->win,data,width,height,
                                    color_pix[fg_col],
                                    color_pix[bg_col],
                                    DefaultDepth(display,screen));
  return (char*)im;
 }



char* x_create_pixrect(int w, int x1, int y1, int x2, int y2)
{ x11_win* wp = wlist[w];
  drawing_mode save = x_set_mode(w,src_mode);
  x_image* im = new x_image;
  if (x1 > x2) SWAP(x1,x2);
  if (y1 > y2) SWAP(y1,y2);

  im->w = x2-x1+1;
  im->h = y2-y1+1;
  im->P = XCreatePixmap(display,wp->win,im->w,im->h,
                         DefaultDepth(display,screen));
  XCopyArea(display,wp->win,im->P,wp->gc,x1,y1,im->w,im->h,0,0);
  x_set_mode(w,save);
  return (char*)im;
 }


char* x_get_buffer_pixrect(int w)
{ x11_win* wp = wlist[w];
  x_start_buffering(w);
  x_image* im = new x_image;
  im->w = x_window_width(w);
  im->h = x_window_height(w);
  im->P = wp->buf;  
  x_stop_buffering(w);
  wp->buf = 0;
  x_start_buffering(w);
  return (char*)im;
}


void x_insert_pixrect(int w, int x, int y, char* prect)
{ // (x,y) lower left corner !
  x11_win* wp = wlist[w];
  drawing_mode save = x_set_mode(w,src_mode);
  x_image* im = (x_image*)prect;
  XCopyArea(display,im->P,wp->win,wp->gc,0,0,im->w,im->h,x,y-im->h+1);
  x_set_mode(w,save);
 }


void x_insert_pixrect(int w, int x, int y, char* prect, int x0, int y0, int wi, int he)
{ x11_win* wp = wlist[w];
  drawing_mode save = x_set_mode(w,src_mode);
  x_image* im = (x_image*)prect;
  XCopyArea(display,im->P,wp->win,wp->gc,x0,y0,wi,he,x,y-he+1);
  x_set_mode(w,save);
 }


void x_insert_pixrect(int w, char* prect)
{ // inssert at (0,0)
  x11_win* wp = wlist[w];
  drawing_mode save = x_set_mode(w,src_mode);
  x_image* im = (x_image*)prect;
  XCopyArea(display,im->P,wp->win,wp->gc,0,0,im->w,im->h,0,0);
  x_set_mode(w,save);
 }


void x_delete_pixrect(char* prect)
{ x_image* im = (x_image*)prect;
  XFreePixmap(display,im->P);
  delete im;
 }


void x_copy_pixrect(int w, int x1, int y1, int x2, int y2, int x, int y)
{ char* im = x_create_pixrect(w,x1,y1,x2,y2); 
  x_insert_pixrect(w,x,y,im);
  x_delete_pixrect(im);
 }






char* x_create_bitmap(int, int width, int height, char* p)
{ x_image* im = new x_image;

  int bytes = (width+7)/8;
  int sz = height * bytes;

  char* bm = new char[sz];
  char* q  = bm;
  char* stop = p + sz; 
  while (p < stop) *q++ = *p++;

  im->w = width;
  im->h = height;
  im->buf = bm;

  return (char*)im;
}



static void x_bit_line(int w, int x, int y, char* line, int bytes)
{ x11_win* wp = wlist[w];
  XPoint* points = new XPoint[8*bytes];
  XPoint* stop   = points + 8*bytes;
  XPoint* p      = points;
  while (p < stop) (p++)->y = (short)y;
  p = points;
  for (int i=0; i<bytes; i++)
  { unsigned char c = line[i];
    if (c & 0x01) (p++)->x = x; x++;
    if (c & 0x02) (p++)->x = x; x++;
    if (c & 0x04) (p++)->x = x; x++;
    if (c & 0x08) (p++)->x = x; x++;
    if (c & 0x10) (p++)->x = x; x++;
    if (c & 0x20) (p++)->x = x; x++;
    if (c & 0x40) (p++)->x = x; x++;
    if (c & 0x80) (p++)->x = x; x++;
   }
  XDrawPoints(display,wp->win,wp->gc,points,p-points,CoordModeOrigin);
  delete[] points;
}


void x_insert_bitmap(int w, int x, int y, char* bmap)
{ x_image* im = (x_image*)bmap;
  char*     p = im->buf;
  int      he = im->h;
  int      wi = (im->w + 7)/8;
  for (int i =  y-he+1; i <= y; i++)
  { x_bit_line(w,x,i,p,wi);
    p += wi;
   }
}
  

void x_delete_bitmap(char* bmap) 
{ x_image* im = (x_image*)bmap;
  delete[] im->buf;
  delete im;
}


char* x_create_pixrect(int w, char** xpm) 
{ x11_win* wp = wlist[w];
  x_image* im = new x_image;
  im->P = xpm_pixmap(wp->win,xpm,im->w,im->h);
  return (char*)im;
}


void x_pixrect_dimensions(char* pr, int* w, int* h)
{ if (pr)
  { x_image* im = (x_image*)pr;
    *w = im->w;
    *h = im->h;
   }
 }


//------------------------------------------------------------------------------
// fonts
//------------------------------------------------------------------------------

int x_load_text_font(const char* font_name)
{ XFontStruct* fp = XLoadQueryFont(display,font_name);
  if (fp)  
  { text_font = fp;
    text_char_width = get_char_width(fp);
   }
  return (fp != NULL);
 }

int x_load_bold_font(const char* font_name)
{ XFontStruct* fp = XLoadQueryFont(display,font_name);
  if (fp)  
  { bold_font = fp;
    bold_char_width = get_char_width(fp);
   }
  return (fp != NULL);
 }


int x_load_fixed_font(const char* font_name)
{ XFontStruct* fp = XLoadQueryFont(display,font_name);
  if (fp)  
  { fixed_font = fp;
    fixed_char_width = get_char_width(fp);
   }
  return (fp != NULL);
 }


int x_set_font(int w, const char *fname)
{ x11_win* wp = wlist[w];
  XFontStruct* fp = XLoadQueryFont(display,fname);
  if (fp)
  { wp->font = fp;
    wp->char_width = get_char_width(fp);
    wp->gc_val.font = fp->fid;
    XChangeGC(display,wp->gc,GCFont,&(wp->gc_val));
   }
  return (fp != NULL);
 }


void x_set_text_font(int w)
{ x11_win* wp = wlist[w];
  if (wp->font != text_font)
  { wp->font = text_font;
    wp->gc_val.font = text_font->fid;
    XChangeGC(display,wp->gc,GCFont,&(wp->gc_val));
    wp->char_width = text_char_width;
  }
 }


void x_set_bold_font(int w)
{ x11_win* wp = wlist[w];
  if (wp->font != bold_font)
  { wp->font = bold_font;
    wp->gc_val.font = bold_font->fid;
    XChangeGC(display,wp->gc,GCFont,&(wp->gc_val));
    wp->char_width = bold_char_width;
   }
 }


void x_set_fixed_font(int w)
{ x11_win* wp = wlist[w];
  if (wp->font != fixed_font)
  { wp->font = fixed_font;
    wp->gc_val.font = fixed_font->fid;
    XChangeGC(display,wp->gc,GCFont,&(wp->gc_val));
    wp->char_width = fixed_char_width;
   }
 }




//------------------------------------------------------------------------------
// setting parameter
//------------------------------------------------------------------------------

void x_set_border_width(int w, int width)
{ x11_win* wp = wlist[w];
  XWindowChanges changes;
  changes.border_width = width;
  XConfigureWindow(display,wp->win,CWBorderWidth,&changes);
}

 
void x_set_header(int w, const char* s)
{ x11_win* wp = wlist[w];
  if (wp->win_save)
     XStoreName(display,wp->win_save,s); 
  else
     XStoreName(display,wp->win,s); 
}


void x_set_clip_rect(int w, int x0, int y0, int width, int height)
{ x11_win* wp = wlist[w];
  XRectangle rect;
  rect.x = 0;
  rect.y = 0;
  rect.width = width;
  rect.height = height;
  XSetClipRectangles(display,wp->gc,x0,y0,&rect,1,0);
}


int x_set_color(int w, int col)
{ x11_win* wp = wlist[w];
  int save = wp->COLOR;
  if (col != 0 && DefaultDepth(display,screen) == 1) col = 1; 
  wp->COLOR = col;
  wp->gc_val.foreground = color_pix[col];
  if (wp->MODE == xor_mode) wp->gc_val.foreground  ^= color_pix[white];
  XChangeGC(display,wp->gc,GCForeground,&(wp->gc_val));
  return save;
 }


drawing_mode x_set_mode(int w, drawing_mode m)
{ x11_win* wp = wlist[w];

  if (wp->MODE == m) return m;

  drawing_mode save = wp->MODE;

  wp->MODE = m;

  wp->gc_val.foreground = color_pix[wp->COLOR];

  switch (m)  {

   case 0 : wp->gc_val.function = GXcopy;
            break;
   case 1 : wp->gc_val.function = GXxor;
            break;
   case 2 : wp->gc_val.function = GXor;
            wp->gc_val.foreground ^= color_pix[white];
            break;
   default: break;
  }

  XChangeGC(display,wp->gc,GCFunction,&(wp->gc_val));

  return save;
}


text_mode x_set_text_mode(int w, text_mode tm) 
{ x11_win* wp = wlist[w];
  text_mode save = wp->TEXTMODE;
  wp->TEXTMODE = tm;  
  return save;
 }

int x_set_join_style(int w, int js) 
{ x11_win* wp = wlist[w];
  int save = wp->JOINSTYLE;
  wp->JOINSTYLE = js;  
  return save;
 }



int x_set_line_width(int w, int lw)
{ x11_win* wp = wlist[w];
  if (wp->LINEWIDTH == lw) return lw;
  int save = wp->LINEWIDTH;
  wp->LINEWIDTH = lw;
  wp->gc_val.line_width = lw;
  XChangeGC(display,wp->gc,GCLineWidth,&(wp->gc_val));
  return save;
}


line_style x_set_line_style(int w, line_style s)
{ x11_win* wp = wlist[w];
  if (wp->LINESTYLE == s) return s;
  line_style save = wp->LINESTYLE;

  wp->LINESTYLE = s;

  switch (s)  {

   case solid  : wp->gc_val.line_style = LineSolid;
                 break;
   case dashed : wp->gc_val.line_style = LineOnOffDash;
                 XSetDashes(display,wp->gc,0,dash_mask,2);
                 break;
   case dotted : wp->gc_val.line_style = LineOnOffDash;
                 XSetDashes(display,wp->gc,0,dot_mask,2);
                 break;
   }

  XChangeGC(display,wp->gc,GCLineStyle,&(wp->gc_val));
  return save;
}


int           x_get_color(int w)      { return wlist[w]->COLOR;     }
drawing_mode  x_get_mode(int w)       { return wlist[w]->MODE;      }
int           x_get_line_width(int w) { return wlist[w]->LINEWIDTH; }
line_style    x_get_line_style(int w) { return wlist[w]->LINESTYLE; }
text_mode     x_get_text_mode(int w)  { return wlist[w]->TEXTMODE;  }


//------------------------------------------------------------------------------
// event handling
//------------------------------------------------------------------------------

//static Window timer_event_win = 0;

static int handle_event(int *w,int *val,int *x,int *y,unsigned long *t)
{

/*
  if (event.xany.send_event == True) 
  { int i = wcount;
    wlist[0]->win = timer_event_win;
    while (wlist[i]->win != timer_event_win) i--;
    *w = i;
    *val = 0;
    return timer_event;
   }
*/


  KeySym keysym;
  XComposeStatus status;

  int  kind = no_event;
  char c;

  Window win = event.xany.window;


  wlist[0]->win = win; //stopper

  int i = wcount;
  while (wlist[i] == 0 || 
         (wlist[i]->win != win && wlist[i]->win_save != win)) i--;

  *w = i;
  *val = 0;

  wlist[0]->win = RootWindow(display,screen);


  switch (event.type) {


  case Expose: 
                kind = exposure_event;
                *x = event.xexpose.x;
                *y = event.xexpose.y;
                *val = event.xexpose.width;
                *t = event.xexpose.height;
                break;

  case ConfigureNotify: kind = configure_event;
                        *x = event.xconfigure.x;
                        *y = event.xconfigure.y;
                        if (wlist[i]->buf) x_create_buffer(i);
                        if (*x == 0 && *y == 0) kind = no_event;
                        break;

  case DestroyNotify: kind = destroy_event;
                      break;

  case ButtonPress: *val = event.xbutton.button;
                    *x = event.xbutton.x;
                    *y = event.xbutton.y;
                    *t = event.xbutton.time;
                    kind = button_press_event;
                    if (event.xbutton.state & Mod1Mask)    *val = 2; 
                    if (event.xbutton.state & ShiftMask)   *val |= 256; 
                    if (event.xbutton.state & ControlMask) *val |= 512;
                    //XUngrabPointer(display,CurrentTime);
                    x_ungrab_pointer();
                    break;

  case ButtonRelease: *val = event.xbutton.button;
                      *x = event.xbutton.x;
                      *y = event.xbutton.y;
                      *t = event.xbutton.time;
                      if (event.xbutton.state & Mod1Mask)    *val = 2; 
                      if (event.xbutton.state & ShiftMask)   *val |= 256; 
                      if (event.xbutton.state & ControlMask) *val |= 512;
                      kind = button_release_event;
                      break;

  case LeaveNotify:
  case EnterNotify:
  case MotionNotify: *x = event.xmotion.x;
                     *y = event.xmotion.y;
                     *t = event.xbutton.time;
                     kind = motion_event;
                     break;

  case KeyPress: *x = event.xkey.x;
                 *y = event.xkey.y;
                 *t = event.xkey.time;

                 c = 0;
                 XLookupString(&event.xkey,&c,1, &keysym, &status);

                 switch (keysym) {
                   case XK_BackSpace:  c = KEY_BACKSPACE;
                                       kind = key_press_event;
                                       break;
                   case XK_Return:     c = KEY_RETURN;
                                       kind = key_press_event;
                                       break;
                   case XK_Escape:     c = KEY_ESCAPE;
                                       kind = key_press_event;
                                       break;
                   case XK_Left:       c = KEY_LEFT;
                                       kind = key_press_event;
                                       break;
                   case XK_Right:      c = KEY_RIGHT;
                                       kind = key_press_event;
                                       break;
                   case XK_Up:         c = KEY_UP;
                                       kind = key_press_event;
                                       break;
                   case XK_Down:       c = KEY_DOWN;
                                       kind = key_press_event;
                                       break;
                   case XK_Home:       c = KEY_HOME;
                                       kind = key_press_event;
                                       break;
                   case XK_End:        c = KEY_END;
                                       kind = key_press_event;
                                       break;
/*
                   case XK_Page_Up:    c = KEY_PAGEUP;
                                       kind = key_press_event;
                                       break;
                   case XK_Page_Down:  c = KEY_PAGEDOWN;
                                       kind = key_press_event;
                                       break;
*/
                  }

                 if (c) kind = key_press_event;
                 *val = c;
                 break;

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

  }

  //printf("w= %d  k= %d  v= %d  x= %d  y= %d  t= %u\n", *w,event.type,*val,*x,*y,*t);

  return kind;
}


int x_check_next_event(int *w, int *val, int *x, int *y, unsigned long *t) 
{ // non-blocking 

  if (XCheckMaskEvent(display, 
                      EnterWindowMask | LeaveWindowMask    |
                      KeyPressMask    | PointerMotionMask  | 
                      ButtonPressMask | ButtonReleaseMask  |
                      ExposureMask    | StructureNotifyMask, &event) == 0) 
  { *w = 0;
    return no_event; 
   }

  return handle_event(w,val,x,y,t);
}


int x_get_next_event(int *w, int *val, int *x, int *y, unsigned long *t)
{ // blocking

  XNextEvent(display, &event);
  return handle_event(w,val,x,y,t);
}


int x_get_next_event(int *w, int *val, int *x, int *y, unsigned long *t, int msec)
{
  int fd = ConnectionNumber(display);

  timeval polltime;
  fd_set rdset, wrset, xset;

  if (XPending(display) == 0) 
  { 
    polltime.tv_sec  = msec / 1000; 
    polltime.tv_usec = 1000 * (msec % 1000);

    FD_ZERO(&rdset);
    FD_SET(fd,&rdset);
    FD_ZERO(&wrset);
    FD_ZERO(&xset);
    FD_SET(fd,&xset);

#if defined(hpux)
    if (select(fd+1,(int*)&rdset,(int*)&wrset,(int*)&xset,&polltime) <= 0)
    { *w = 0;
      return no_event;
     }
#else
    if (select(fd+1,&rdset,&wrset,&xset,&polltime) <= 0)
    { *w = 0;
      return no_event;
     }
#endif

  }

  XNextEvent(display, &event);
  return handle_event(w,val,x,y,t);
}





void x_put_back_event(void)
{ XPutBackEvent(display,&event); }


int x_create_buffer(int w, int wi, int he)
{ x11_win* wp = wlist[w];
  if (wi != wp->buf_w || he != wp->buf_h)
  { if (wp->buf) XFreePixmap(display,wp->buf);
    wp->buf_w = wi;
    wp->buf_h = he;
    wp->buf = XCreatePixmap(display,wp->win,wi,he,DefaultDepth(display,screen));
    if (wp->win_save) wp->win = wp->buf;
   }
  return 1;
}


int x_create_buffer(int w)
{ x11_win* wp = wlist[w];
  int wi = x_window_width(w);
  int he = x_window_height(w);
  if (wp->buf == 0 || wi != wp->buf_w || he != wp->buf_h)
  { if (wp->buf) XFreePixmap(display,wp->buf);
    wp->buf_w = wi;
    wp->buf_h = he;
    wp->buf = XCreatePixmap(display,wp->win,wi,he,DefaultDepth(display,screen));
    if (wp->win_save) wp->win = wp->buf;
   }
  return 1;
}

void x_start_buffering(int w, int wi, int he) 
{ x11_win* wp = wlist[w];
  x_stop_buffering(w);
  x_delete_buffer(w);
  x_create_buffer(w,wi,he);
  wp->win_save = wp->win;
  wp->win = wp->buf;
} 



void x_start_buffering(int w) 
{ x11_win* wp = wlist[w];
  if (wp->win_save == 0)
  { if (wp->buf == 0) x_create_buffer(w);
    wp->win_save = wp->win;
    wp->win = wp->buf;
   }
} 


int x_test_buffer(int w)
{ x11_win* wp = wlist[w];
  return wp->win_save != 0;
 } 

void x_flush_buffer(int w, int x1, int y1, int x2, int y2, int xoff, int yoff) 
{ x11_win* wp = wlist[w];
  if (wp->buf == 0) return;

  Window win = wp->win_save;
  if (win == 0) win = wp->win;

  drawing_mode save = x_set_mode(w,src_mode);
  if (x1 > x2) SWAP(x1,x2);
  if (y1 > y2) SWAP(y1,y2);
  //int wi = x2-x1+1;
  //int he = y2-y1+1;
  int wi = x2-x1;
  int he = y2-y1;
  int x3 = x1+xoff;
  int y3 = y1+yoff;
  XCopyArea(display,wp->buf,win,wp->gc,x1,y1,wi,he,x3,y3);
  x_set_mode(w,save);
}

void x_flush_buffer(int w, int x1, int y1, int x2, int y2) 
{ x_flush_buffer(w,x1,y1,x2,y2,0,0); }


void x_stop_buffering(int w)
{ x11_win* wp = wlist[w];
  if (wp->win_save) wp->win  = wp->win_save;
  wp->win_save = 0;
} 

void x_delete_buffer(int w)  
{ x11_win* wp = wlist[w];
  x_stop_buffering(w);
  if (wp->buf) XFreePixmap(display,wp->buf);
  wp->buf = 0;
}



void x_start_timer(int, int ) {}
void x_stop_timer(int) {}

/*
static void timer_action(int)
{ XEvent event;
  event.type = ButtonPress;
  //XSendEvent(display,timer_event_win,True,ButtonPressMask,&event);
  XSendEvent(display,timer_event_win,False,ButtonPressMask,&event);
  signal(SIGALRM,timer_action);
 }

void x_start_timer(int w, int msec)
{ timer_event_win = wlist[w]->win;
  signal(SIGALRM,timer_action);
  itimerval it;
  bzero((char*)&it,sizeof(itimerval));
  it.it_interval.tv_usec = 1000*msec;
  it.it_value.tv_usec = 1000*msec;
  setitimer(ITIMER_REAL, &it, NULL);
}

void x_stop_timer(int w)
{ itimerval it;
  bzero((char*)&it,sizeof(itimerval));
  setitimer(ITIMER_REAL, &it, NULL);
}
*/



//------------------------------------------------------------------------------
// other functions
//------------------------------------------------------------------------------


void x_set_read_gc(int w)
{ XGCValues gc_val;
  gc_val.function = GXxor; 
  gc_val.foreground = BlackPixel(display,screen); 
  gc_val.line_style = LineSolid;
  gc_val.line_width = 1;
  XChangeGC(display,wlist[w]->gc,
            GCForeground|GCFunction|GCLineStyle|GCLineWidth,&gc_val);
  x_flush_display();
}

void x_reset_gc(int w)
{ x11_win* wp = wlist[w];
  XChangeGC(display,wp->gc,
            GCForeground|GCFunction|GCLineStyle|GCLineWidth,&(wp->gc_val));
  x_flush_display();
}



void x_grab_pointer(int w)
{ x11_win* wp = wlist[w];

  XGrabPointer(display,wp->win,False,ButtonPressMask|ButtonReleaseMask|
                                                     PointerMotionMask, 
               GrabModeAsync,GrabModeAsync,None,None,CurrentTime); 
 }


void x_ungrab_pointer()
{ XUngrabPointer(display,CurrentTime); }


void x_set_icon_pixmap(int w, char* prect)
{ x11_win* wp = wlist[w];
  XWMHints* wm_hints = XGetWMHints(display,wp->win);
  x_image* im = (x_image*)prect;
  XSetWindowBackgroundPixmap(display,wm_hints->icon_window,im->P);
}



void x_window_to_screen(int w, int* x, int* y)
{ Window src_win  = wlist[w]->win;
  Window dest_win = RootWindow(display,screen);
  Window child_win;
  int x1,y1;
  XTranslateCoordinates(display,src_win,dest_win,*x,*y,&x1,&y1,&child_win);
  *x = x1;
  *y = y1;
}


void x_screen_to_window(int w, int* x, int* y)
{ Window src_win  = RootWindow(display,screen);
  Window dest_win = wlist[w]->win;
  Window child_win;
  int x1,y1;
  XTranslateCoordinates(display,src_win,dest_win,*x,*y,&x1,&y1,&child_win);
  *x = x1;
  *y = y1;
}

