
/*

  This is the Interface for sprite01.H which supports it for X11

  Written by Jens Thiele in June 1996

*/

#include"scrX11.H"

// Klassenname !
const char *sprite_linear_nontransparent_screentype::headerstring="sprite_linear_nontransparent_screentype";

sprite_linear_nontransparent_screentype
::sprite_linear_nontransparent_screentype(int32 _x,int32 _y,int32 _sx,int32 _sy,
					  int32 _mx,int32 _my,
					  const char *_palette=NULL, const char *_scrname=NULL,
					  const char *_dpyname=NULL)
{
  buffer=new strstreambuf;
  ausgabe=new ostream(buffer);
  eingabe=new istream(buffer);

    dpy=XOpenDisplay(_dpyname);
    if (!dpy) 
	fehler("scrX11.H: Can't open display / can't connect to server" );

    scr = DefaultScreen(dpy);
    depth=DefaultDepth(dpy,scr);
    cout <<"Screen Depth is "<<depth<<"Bits.\n";
    cout <<"Display Cells (number of Entries in the Standard Colormap): "<<DisplayCells(dpy,scr)<<endl;
    win = XCreateSimpleWindow( dpy, RootWindow( dpy, scr ),
			       _x,_y,_sx,_sy,2,0,0);
    XMapRaised (dpy,win);    // Painting aktive

    XSetWindowAttributes setattr;
    //    setattr.event_mask=ButtonPressMask|ButtonReleaseMask|ButtonMotionMask;
    window_eventmask=ButtonPressMask|ResizeRedirectMask|ExposureMask;//|StructureNotifyMask; destroy ??
    setattr.event_mask=window_eventmask;
    //    setattr.override_redirect=1;
    //    XChangeWindowAttributes(dpy,win,CWEventMask|CWOverrideRedirect,&setattr);
    XChangeWindowAttributes(dpy,win,CWEventMask,&setattr);
    sizeptr=XAllocSizeHints();
    sizeptr->min_width=_sx;
    sizeptr->min_height=_sy;
    sizeptr->base_width=_sx;
    sizeptr->base_height=_sy;
    sizeptr->max_width=_sx;
    sizeptr->max_height=_sy;
    sizeptr->width_inc=4;
    sizeptr->height_inc=4;
    sizeptr->flags=PMinSize|PBaseSize|PMaxSize|PResizeInc;
    XSetWMNormalHints(dpy,win,sizeptr);
    sx=_sx;
    sy=_sy;
    mx=_mx;
    my=_my;
    
    new_sprite_buffer(_sx,_sy);

    // Soll eine Palette geladen werden ?
    cmapaktive=0;
    if (_palette!=NULL)
	{
	    scrpal=new palettetype(_palette);
	    showpal(scrpal);
	}
    else
      {
	cerr << "WARNING: sprite_linear...screentype: screen without palette\n";
	scrpal=NULL;
	if (depth==8)
	  {
	    // todo
	    //	    for (int a=0;a<256;a++)
	    //	      remap8[a]=a;
	  }
	else if (depth==24)
	  {
	    for (int a=0;a<256;a++)
	      remap32[a]=a;
	  }
      }
    sync=1;
    if (_scrname)
      XStoreName(dpy,win,_scrname);
}

void sprite_linear_nontransparent_screentype::new_sprite_buffer(int32 _sx,int32 _sy)
{
  XResizeWindow(dpy,win,_sx,_sy);
  XMapRaised (dpy,win);    // Painting aktive
  XSync(dpy,0);
  XWindowAttributes winattr;
  XGetWindowAttributes(dpy,win,&winattr);

  // todo wieder machen und testen ob Pixmap wirklich so viel schneller als Image !!
  bild=XShmCreateImage(dpy,DefaultVisual(dpy, scr),winattr.depth,
		       ZPixmap,NULL,&xshminfo,_sx,_sy);
  
  //  xshminfo.shmid = shmget(IPC_PRIVATE,_sx*_sy,IPC_CREAT | 0777);   // Pixmap ver !!

  xshminfo.shmid = shmget(IPC_PRIVATE,bild->bytes_per_line*bild->height,IPC_CREAT | 0777); // Image ver !!


  if (xshminfo.shmid < 0) 
    fehler("scrX11.H: Shared Memory Error at Pos. 1");



  bild->data = xshminfo.shmaddr =(char *) shmat(xshminfo.shmid, 0, 0);  // Image only
  //  xshminfo.shmaddr =(char *) shmat(xshminfo.shmid, 0, 0); // Pixmap ver.
  if (xshminfo.shmaddr == ((char *) -1)) 
    fehler("Shared Memory Error at Pos. 2");
  xshminfo.readOnly = False;
  
  if (xshminfo.shmaddr<(char *)0)
    fehler("Shared Memory Error at Pos. 3");
  XSync(dpy,0);

  shmctl(xshminfo.shmid,IPC_RMID,0); 
  
  Status result = XShmAttach(dpy, &xshminfo);
  if (!result) 
    fehler("Shared Memory Error at Pos. 4 / Display is remote ?");
  XSync(dpy,0);

  // bild=XShmCreatePixmap(dpy,win,xshminfo.shmaddr,&xshminfo,_sx,_sy,depth);

  if (!bild)
    fehler("scrX11.H: Unable to create Pixmap/Image");

  cout <<"X-Server is running in ";
  if (depth==8)
    {
      cout <<"8-Bit mode\n";
      sprite=xshminfo.shmaddr;
    }
  else if (depth==24)
    {
      cout <<"24/32-Bit mode\n";
      // todo
      //      fatalfehler("Sorry ... 24(32) Bit mode doesn't work in the moment.\n"
      //		  "Please run your Server in 8Bit mode.");
      // Brauche ein 8 Bit sprite !!! und nur wenn ich den screen mappe wird in Shared Memory buffer
      // von 8 nach 24 bzw. 32 Bit konvertiert !
      sprite=new char[_sx*_sy+1];
      sprite32=(uint32 *)xshminfo.shmaddr;
    }
  else
    fatalfehler("sprite_linear_nontransparenttype::sprite_linear_nontransparenttype:\n"
		"X-Server must run in 8Bit or 24(32)Bit colormode");
  if (_sx!=sx)
    mx=mx*_sx/sx;
  if (_sy!=sy)
    my=my*_sy/sy;
  sx=_sx;
  sy=_sy;
  cout << "New Window Size: "<<sx<<","<<sy<<" Middle: "<<mx<<","<<my<<endl;


  // aktgc braucht man fuern XCopyArea
  XGCValues values;
  values.graphics_exposures = False;
  aktgc=XCreateGC(dpy,win,GCGraphicsExposures,&values);
  XSync(dpy,0);
}

sprite_linear_nontransparent_screentype
::~sprite_linear_nontransparent_screentype()
{
  if (scrpal)
    delete scrpal;
  delete sizeptr;
  delete_sprite_buffer();
  XDestroyWindow( dpy, win);
  XCloseDisplay( dpy );
  delete ausgabe;
  delete eingabe;
  delete buffer;
}

void sprite_linear_nontransparent_screentype::delete_sprite_buffer()
{
  XShmDetach(dpy, &xshminfo);
  XDestroyImage(bild);    
  //XFreePixmap(dpy,bild);
  shmdt(xshminfo.shmaddr);
  shmctl(xshminfo.shmid, IPC_RMID, 0);
  if (depth==24)
    delete(sprite);
  sprite=NULL;        // ~spritetype macht delete sprite (=> seg. fault!)
  XFreeGC(dpy,aktgc);
}

void sprite_linear_nontransparent_screentype::resize(int32 _sx,int32 _sy)
{
  delete_sprite_buffer();
  XWindowChanges values;
  values.width=(_sx/4)*4;
  values.height=(_sy/4)*4;
  if (values.width<4) values.width=4;
  if (values.height<4) values.height=4;
  if (!XReconfigureWMWindow(dpy,win,scr,CWWidth|CWHeight,&values))
    fatalfehler("sprite_linear_nontransparent_screentype::resize: Fuck your Windowmanager !\n");
  XMapWindow(dpy,win);    // Painting aktive
  new_sprite_buffer(values.width,values.height);
}

void sprite_linear_nontransparent_screentype::map()
{
  if (depth==8)
    {
      // Kein Remap da Private Colormap !!!

      // Remappen !
      // So umstaendlich damit es schneller ist !
      // spaeter besser ! evtl. asm !! 
      /*      int32 aktbyte=sy*sx-1;
      uint8 *aktspritepos=sprite+aktbyte;
      do
	{
	  *aktspritepos=remap8[*aktspritepos];
	  aktbyte--;
	  aktspritepos--;
	}
      while(aktbyte);*/
    }
  else if (depth==24)
    {
      for (int32 aktbyte=0;aktbyte<sy*sx;aktbyte++)
	{
	  ulong wert=remap32[(uint8)sprite[aktbyte]];
	  sprite32[aktbyte]=wert;
	}
    }

  XShmPutImage(dpy, win, aktgc, bild, 0, 0, 0, 0, sx, sy, 0);
  //  XCopyArea (dpy,bild,win,aktgc,0,0,sx,sy,0,0);
  if (sync)
    XSync(dpy,0); // Until this Operation you can't be sure that it is painted
}


void sprite_linear_nontransparent_screentype::map_box(int32 _x,int32 _y,
						      int32 _sx, int32 _sy)
{
  _x+=mx;
  _y+=my;

  // todo verry ugly and slow 
  if (depth==24)
    {
      for (int32 akty=_y;akty<_y+_sy;akty++)
	{
	  for (int32 aktx=_x;aktx<_x+_sx;aktx++)
	    {
	      int32 aktbyte=akty*sx+aktx;
	      ulong wert=remap32[(uint8)sprite[aktbyte]];
	      sprite32[aktbyte]=wert;
	    }
	}
    }
  
  XShmPutImage(dpy, win, aktgc, bild, _x, _y, _x, _y, _sx, _sy, 0);
  if (sync)
   XSync(dpy,0); // Until this Operation you can't be sure that it is painted
}

// Wird auf true gesetzt wenn ein Fehler auftrat !
// Nicht unbedingt eine geniale loesung spaeter 
// Habe ich benutzt um die Palette des X-Servers ueberhaupt herausfinden zu koennen !!
/*
char erroroccured;
char errorallowed;
int handler(Display *, XErrorEvent *)
{
   if (!errorallowed)
    fatalfehler("Error occured at the wrong time!\n");
  erroroccured=1;
  return 0;
}
*/

void sprite_linear_nontransparent_screentype::showpal(palettetype *pal)
{
  // todo muss eigentlich immer freigegeben werden
  if (cmapaktive)
    {
      //      XUninstallColormap(dpy,cmap);
      XFreeColormap(dpy,cmap);
    }

  uint16 scale=uint16(-1)/pal->maxval;
  if (depth==8)
    {
      // Install Private Colormap
      cmap=XCreateColormap(dpy,win,DefaultVisual(dpy, scr),AllocAll);
      cmapaktive=1;
      XColor colors[256];
      for (int a=0;a<256;a++)
	{
	  colors[a].pixel=a;
	  colors[a].red=pal->palette[a].red*scale;
	  colors[a].green=pal->palette[a].green*scale;
	  colors[a].blue=pal->palette[a].blue*scale;
	  colors[a].flags=DoRed|DoGreen|DoBlue;
	}
      XStoreColors(dpy,cmap,colors,256);
      XSetWindowColormap(dpy, win, cmap);
      XInstallColormap(dpy,cmap);
    }
  else
    {
      // 32 Bit Farbtiefe Truecolor Remap Tabelle erstellen !
      // Meine Palette ausgeben
      //      printf("Meine Palette (ist zwar Truecolor aber ich benutze intern nur 8Bit !):\n");
      cmap=DefaultColormap(dpy, scr);
      XColor color;
      for (int akt=0;akt<256;akt++)
	{
	  color.red=pal->palette[akt].red*scale; 
	  color.green=pal->palette[akt].green*scale;
	  color.blue=pal->palette[akt].blue*scale;
	  color.pixel=akt;
	  
	  if (!XAllocColor(dpy,cmap,&color))
	    {
	      fatalfehler("sprite_linear_nontransparent_screentype::showpal:\n"
			  "Isn't possible !!");
	    }
	  remap32[akt]=color.pixel;
	}
    }
}

int sprite_linear_nontransparent_screentype::spritecheck()
{
  fatalfehler("\nsprite_linear_nontransparent_screentype::spritecheck():\n"
	      "Darf nicht aufgerufen werden, da ein check nicht möglich ist !");
#ifdef kickwarn
  return(-1);
#endif
}

XEvent *sprite_linear_nontransparent_screentype::get_event()
{
  XEvent *event=new XEvent;
  if (XCheckWindowEvent(dpy,win,window_eventmask,event))
    return event;
  delete event;
  return NULL;
}

// Wandle X-Event in einen Stream um !!!
istream *sprite_linear_nontransparent_screentype::get_input()
{
  XEvent *ev=get_event();
  if (ev)
    {
      strstream *ost=new strstream;
      if (ev->type==ResizeRequest)
	*ost << "ResizeRequest "<<ev->xresizerequest.width<<" "<<ev->xresizerequest.height<<endl;
      else if (ev->type==ButtonPress)
	*ost << "ButtonPress "<<ev->xbutton.x-mx<<" "<<ev->xbutton.y-my<<" "<<ev->xbutton.button<<endl;
      else if (ev->type==DestroyNotify)
	*ost << "Destroy"<<endl;
      else if (ev->type==Expose)
	*ost << "Expose"<<endl;
      else
	{
	  // No Operation
	  if (ev->type==CreateNotify)
	    cout <<"Window created\n"; // todo soll einfach nix machen :-)
	  else
	    cerr <<"WARNING: scrX11.C: get_input(): Got an unknown XEvent\n";
	  delete ev;
	  delete ost;
	  return NULL;
	}
      delete ev;
      return ost;
    }
  // else
  return NULL;
}

// Verarbeite den Input Stream !!
int sprite_linear_nontransparent_screentype::verarbeite_input(istream *ist)
{
  string evtype;
  streampos pos=ist->tellg();
  (*ist)>>evtype;
  if (evtype=="ResizeRequest")
    {
      int32 _sx,_sy;
      if ((*ist) >>_sx>>_sy)
	{
	  if ((_sx!=sx)||(_sy!=sy))
	    resize(_sx,_sy);
	  delete ist;
	  return 0;
	}
    } 
  else if (evtype=="Destroy")
    {
      cout <<"DDDDDEEEEEEEESSSSSSSTROY !!!\n";
      delete ist;
      return 0;
    }
  else if (evtype=="Expose")
    {
      verarbeite_expose_event();
      delete ist;
      return 0;
    }
  if (*ist)
    ist->seekg(pos);
  else
    fatalfehler("scrX11.C: verarbeite_input(..): Got a wrong Input Stream");
  return 1;
}

void sprite_linear_nontransparent_screentype::verarbeite_expose_event()
{
  map();
}

int sprite_linear_nontransparent_screentype::calc()
{
  istream *ist;
  while((ist=get_input()))
    {
      if (verarbeite_input(ist))
	{
	  cerr <<"scrX11.C: calc():\n"
	    "Got a unknown Event => Kill this event_managertype\n";
	  return 1;
	}
    }
  return 0;
}

istream &sprite_linear_nontransparent_screentype::get_output()
{
  return *eingabe;
}
