/*
   File: display.hh

   By: Alex Theo de Jong, NIST
       with help from Reinhard Baier (Fokus/GMD, Germany)
   
   Created: February 1996

   Description:
   Multi-Threaded display class (NOT for IRIX NATIVE, see notes). Display class 
   handles display of raw video frames under X11. Synchronization is provided by
   use of the "sync" object. The display is threaded in order to increase 
   performance on Multi-processor machines such as the SUN Sparc 10 MP. 

   Notes:
1  Since process switching is expensive and SGI Irix 5.3 does not support
   Multi-Threading, I don't thread when using SGI Irix. This would in fact
   drop performance. On SUN Solaris either with an MP machine or without, the
   performance is better when using Multi-threading.
2  I tried using the SOLARIS graphical libraries (XIL) but have not been very
   successful in improving performance with these libraries (Performce was measured
   on a SUN Ultra Sparc Created 3D). It turns out that the dither function 
   provided by XIL is slower then software dithering and X display. Please
   drop me a line if you can help me out with this :-)  A nice feature us the
   scaling option. It resizes the image when the users resizes the window. 
   Performance drops unfortunately.
   Use the -DSOLARIS_SDK_XIL flag to compile with the XIL library. Note that
   the full Solaris Software Development Kit needs to be installed.
   Using 24 bit TrueColor can be switched on by using the -D24BITSCOLORS.
   This only works for MPEG 1 sequences (behaviour is undefined otherwise.
*/

#ifndef __display_hh
#define __display_hh

#ifdef __GNUG__
#pragma interface
#endif

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

#ifdef SOLARIS_SDK_XIL
#undef NULL  // to avoid compiler warning
#include <xil/xil.h>
#else
#ifdef SH_MEM
#include <X11/extensions/XShm.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#endif // SH_MEM
#endif // SOLARIS_SDK_XIL



static int gXErrorFlag;


class DisplayX11 {
  Synchronization* sync;     // Synchronization object for Audio/Video sync
#ifdef USE_OPENPTC
  Console *con;
  Format *form;
#endif

  // MT stuff
  unsigned char** source;    // in order to thread display
  MutexLock display_lock;    // synchronization stuff
  Condition display_cond;
  athr_t thread_id;
  int terminated;
 
  unsigned char *clp;  // clipping table (created at inistantiation)
  unsigned char *dithered_image, *dithered_image2;
  unsigned char ytab[16*(256+16)];
  unsigned char uvtab[256*269+270];

  // X11 related variables
  XVisualInfo vinfo;
  XEvent event;
  Display *display;
  Window window;
  int horizontal_size, vertical_size;
  GC gc;
  unsigned char pixel[256];
  int bpp;
  int rgb_mode;
#ifdef SOLARIS_SDK_XIL
  int bands;
  XilSystemState State;
  int resized;
  float horizontal_factor, vertical_factor;
  XilMemoryStorage xilstorage1, xilstorage2;
  XilImage ximage, ximage2, resized_image, displayimage;
#else
  XImage *ximage, *ximage2;
#ifdef SH_MEM
  int shmem_flag;
  XShmSegmentInfo shminfo1, shminfo2;
  int CompletionType;
  int xwidth;
#endif // SH_MEM
#endif // SOLARIS_SDK_XIL
 protected:
  static void* dither_thread(DisplayX11* d);
  static void* display_thread(DisplayX11* d);
#ifdef SOLARIS_SDK_XIL
  int dither_image_rgb24(unsigned char* src[], unsigned char* dithered_image);
  void display_image(XilImage ximage, unsigned char *dithered_image);
#else
#ifdef LINUX
  int dither_image_rgb16(unsigned char* src[], unsigned char* dithered_image);
#endif
  void display_image(XImage *ximage, unsigned char *dithered_image);
#endif
  void dither_image(unsigned char *src[]);

  void ditherframe(unsigned char *src[]);
  void dithertop(unsigned char *src[], unsigned char *dst);
  void ditherbot(unsigned char *src[], unsigned char *dst);
  void ditherframe444(unsigned char *src[]);
  void dithertop444(unsigned char *src[], unsigned char *dst);
  void ditherbot444(unsigned char *src[], unsigned char *dst);

  static int HandleXError(Display*, XErrorEvent*){ gXErrorFlag=1; return 0; }
  void InstallXErrorHandler(){ XSetErrorHandler(DisplayX11::HandleXError); XFlush(display); }
  void DeInstallXErrorHandler(){ XSetErrorHandler(NULL); XFlush(display); }

  void exit_display(void);

#ifdef SOLARIS_SDK_XIL
  int resize();
  Xil_unsigned8* getImage1Data(){
    xil_export(ximage);
    xil_get_memory_storage(ximage, &xilstorage1);
    return xilstorage1.byte.data;
  }
  Xil_unsigned8* getImage2Data(){
    xil_export(ximage2);
    xil_get_memory_storage(ximage2, &xilstorage2);
    return xilstorage2.byte.data;
  }
#endif // SOLARIS_SDK_XIL
 public:
  DisplayX11(const char* title, Synchronization* s=0);
  ~DisplayX11();
  unsigned char* getClpTable() const { return clp; }
  int init(int h_size, int v_size);
  void display_second_field(void);
  void dither(unsigned char *src[]);
};


inline void DisplayX11::dither(unsigned char *src[]){
  display_lock.lock();
  while (source) display_cond.wait(&display_lock);
  source=src;
  display_lock.unlock();
#if defined(IRIX) || defined(SOLARIS_SDK_XIL) || defined(LINUX)
  dither_thread(this);
#else
  if (athr_create((void*(*)(void*))dither_thread, this, &thread_id)<0){
    error("could not create display thread");
    athr_exit(0);
  }
#endif
}

inline void DisplayX11::display_second_field(){
#if defined(IRIX) || defined(SOLARIS_SDK_XIL) || defined(LINUX)
  display_thread(this);
#else
  if (athr_create((void*(*)(void*))display_thread, this, &thread_id)<0){
    error("could not create display thread");
    athr_exit(0);
  }
#endif
}

#endif // __display_hh
