/**
 * TrueReality - Display/local/x11/x11.c
 * Copyright (C) 1998 Niki W. Waibel
 *
 * This program is free software; you can redistribute it and/
 * or modify it under the terms of the GNU General Public Li-
 * cence as published by the Free Software Foundation; either
 * version 2 of the Licence, or any later version.
 *
 * This program is distributed in the hope that it will be use-
 * ful, but WITHOUT ANY WARRANTY; without even the implied war-
 * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public Licence for more details.
 *
 * You should have received a copy of the GNU General Public
 * Licence along with this program; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
 * USA.
 *
 * Information about me (the author):
 *   Niki W. Waibel, Reichenau 20, 6890 Lustenau, Austria - EUROPE
 *   niki.waibel@gmx.net
**/





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

#include <signal.h>
#include <unistd.h>

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

#include "../../../config.h"
#include "../../../parser_extern.h"
#include "../../../n64/type_sizes.h"
#include "../../../n64/memory_extern.h"
#include "../../../dispatch.h"
#include "x11.h"

#ifdef DEBUG
#  include "../../../debug.h"
#endif

#ifdef SHM
#  include <sys/ipc.h>
#  include <sys/shm.h>
#  include <X11/extensions/XShm.h>
#endif

#ifdef FPS
#ifdef LINUX   /* FPS - I don't know if this works on other os than linux - please mail */
#   include <sys/time.h>
#   include <unistd.h>
#endif
#endif




static Display            *dsp;
static Screen             *scr;
static Window             win;
static XWindowAttributes  win_attrib;
static XImage             *img;
static GC                 gc;
static unsigned long      white, black;
static XColor             color;
static XColor             color6bit[64];
//static XColor             color8bit[256];   /* by Niki W. Waibel */
static Colormap           colormap;

static char               title[] = "TrueReality-" VERSION " by Niki W. Waibel and the help of many others";
static WORD               width  =   0;   /* initial VI_WIDTH_REG (0x4400008) value */
static WORD               height =   0;   /* width*3/4 */
static int                WIDTH  = 640;
static int                HEIGHT = 480;
#undef DEPTH
static int                DEPTH;

static void               *XBuf  = NULL;
static WORD               flags  = 0;

static XKeyboardState     xkeyboardstate;

#ifdef SHM
   static XShmSegmentInfo XBuf_SHMInfo;
#endif








static  int     InitDisplay();
static  int     DestroyDisplay();

static  void    PrintText(char *text, int x, int y);

#ifdef DEBUG_DISPLAY
        static  void PrintDisplayMode(WORD flags);
#endif

static  void    PutImage(int x, int y, int w, int h);
        
static  void    AllocDisplayMem();
static  void    FreeDisplayMem();










/*******************************************************************************
*                                                                              *
* int OpenVisual()                                                             *
* int CloseVisual()                                                            *
*                                                                              *
********************************************************************************
*                                                                              *
* These routines are used by the emulator to open and to close a visual.       *
*                                                                              *
* Both return a negative int on error and zero on success.                     *
*                                                                              *
*******************************************************************************/





int OpenVisual()
{
        if(InitDisplay() < 0)
                return(-1);

        prefs.display = LOCAL_DISPLAY;

        return(0);

} /* int OpenVisual() */





int CloseVisual()
{
        DestroyDisplay();

        prefs.display = NO_DISPLAY;

        return(0);

} /* int CloseVisual() */










/*******************************************************************************
*                                                                              *
* static int InitDisplay()                                                     *
*                                                                              *
********************************************************************************
*                                                                              *
* This routine is used to initialize the visual.                               *
* It should print serious information of the current status.                   *
* It must NOT be called by the emulator. It's called from 'int OpenVisual()'.  *
* The name is not standard but it should be used!                              *
*                                                                              *
* The routine returns a negative int on error and zero on success.             *
*                                                                              *
*******************************************************************************/





static int InitDisplay()
{
        XSetWindowAttributes    set_win_attrib;
        XEvent                  event;
        int                     i;

#ifdef SHM
        struct shmid_ds         s;
#endif



        printf("Initializing Unix/X display: (most stuff by \"Guenther Sohler\")\n");
   


        printf("Opening display ..."); fflush(stdout);

        dsp = XOpenDisplay(NULL);
        if(!dsp)
        {
                puts(" FAILED\n"); fflush(stdout);
                return(-1);
        }
        
        scr     = DefaultScreenOfDisplay(dsp);
        
        white   = WhitePixelOfScreen(scr);
        black   = BlackPixelOfScreen(scr);
        
        
        printf(" OK\n"
               "Creating window ..."); fflush(stdout);
               
        win = XCreateSimpleWindow(dsp, RootWindowOfScreen(scr), 0, 0, WIDTH, HEIGHT, 0, white, black);
        if(!win)
        {               
                printf(" FAILED\n"); fflush(stdout);
                return(-1);
        }



/* all of the DEPTH stuff is from "Guenther Sohler" <guenther.sohler@newlogic.at> */

        printf(" OK\n"
               "Checking depth of window ..."); fflush(stdout);

        XGetWindowAttributes(dsp, win, &win_attrib);
        DEPTH = win_attrib.depth;

        printf(" %02d bit ... OK\n", DEPTH); fflush(stdout);

        if(DEPTH == 8)
        {
                fputs("With 8 bit we've to allocate 64 colors ...", stdout); fflush(stdout);
                colormap = XCreateColormap(dsp, win, win_attrib.visual, AllocNone);
                XSetWindowColormap(dsp, win, colormap);

                for(i=0; i<64; i++)
                {
                        color.blue  = (i & 0x03) << 14;
                        color.green = (i & 0x0C) << 12;
                        color.red   = (i & 0x30) << 10;
                        if( !XAllocColor(dsp, colormap, &color) )
                        {
                                puts(" FAILED\n"); fflush(stdout);
                                return(-1);
                        }
                        color6bit[i] = color;

                } /* for(i=0; i<64; i++) */
                
                puts(" OK"); fflush(stdout);

        } /* if(DEPTH == 8) */



        {
                XSizeHints      Hints;
                XWMHints        WMHints;



                Hints.flags       = PSize | PMinSize | PMaxSize;
                Hints.min_width   = 32;
                Hints.max_width   = 1280;
                Hints.base_width  = WIDTH;
                Hints.min_height  = 24;
                Hints.max_height  = 1024;
                Hints.base_height = HEIGHT;
                WMHints.input     = True;
                WMHints.flags     = InputHint;

                XSetWMHints(dsp, win, &WMHints);
                XSetWMNormalHints(dsp, win, &Hints);
                XStoreName(dsp, win, title);
        }



/* the following 3 lines are from myself (niki.waibel@gmx.net) */
        set_win_attrib.backing_store = WhenMapped;
        XChangeWindowAttributes(dsp, win, CWBackingStore, &set_win_attrib);
        /* now it looks much nicer when popping up the window ;) */



        /* input events */

        XSelectInput(dsp, win, ButtonPressMask | FocusChangeMask | ExposureMask | KeyPressMask | KeyReleaseMask);



        /* display window */

        printf("Mapping window ..."); fflush(stdout);

        XMapRaised(dsp, win);

        printf(" OK\n"
               "Clearing window ..."); fflush(stdout);

        XClearWindow(dsp, win);

        /* catch first event to display window */
        XWindowEvent(dsp, win, ExposureMask, &event);



        printf(" OK\n"
               "Creating graphics context ..."); fflush(stdout);

        gc = XCreateGC(dsp, win, 0, 0);



#ifdef SHM   /* this code was first coded by me (Niki W. Waibel) */
             /* then improved by Guenther Sohler (thanks a lot, Gue!) */
        if(prefs.UseSHM)
        {
                printf(" OK\n"
                       "--- SHM stuff ---\n"
                       "Creating image in shared memory ..." ); fflush(stdout);
      
                img = XShmCreateImage(dsp, DefaultVisualOfScreen(scr), DEPTH, ZPixmap, NULL, &XBuf_SHMInfo, WIDTH, HEIGHT);
                if(!img)
                { 
                        puts(" FAILED\n"); fflush(stdout);
                        return(-1);
                }
      
                printf(" OK\n"
                       "Allocating display memory buffer as shared memory ..."); fflush(stdout);
      
                XBuf_SHMInfo.shmid = shmget(IPC_PRIVATE, img->height * img->bytes_per_line, IPC_CREAT | 0777);
      
                if(XBuf_SHMInfo.shmid < 0)
                {
                        puts(" FAILED\n"); fflush(stdout);
                        return(-1);
                }
      
                printf(" OK\n"
                       "Attaching shared memory (shmat) ..."); fflush(stdout);

                XBuf = img->data = XBuf_SHMInfo.shmaddr = shmat(XBuf_SHMInfo.shmid, 0, 0);
                if(!XBuf)
                {
                        puts(" FAILED (shmat)\n"); fflush(stdout);
                        return(-1);
                }

                XBuf_SHMInfo.readOnly = False;
      
                printf(" OK\n"                     
                       "Attaching shared memory (XShmAttach) ..."); fflush(stdout);
      
                if(!XShmAttach(dsp, &XBuf_SHMInfo))
                {
                        puts(" FAILED\n"); fflush(stdout);
                        return(-1); 
                }         

                shmctl(XBuf_SHMInfo.shmid, IPC_RMID, &s);   /* if someone kills me shm should be freed */
             
                puts(" OK\n"
                     "--- end of SHM stuff ---"); fflush(stdout);
        }
        else
#endif
        {
                printf(" OK\n"
                       "Creating image ..." ); fflush(stdout);
      
                img = XCreateImage(dsp, DefaultVisualOfScreen(scr), DEPTH, ZPixmap, 0, XBuf, WIDTH, HEIGHT, DEPTH, 0);
                if(!img)
                { 
                        puts(" FAILED\n"); fflush(stdout);
                        return(-1);
                }
      
                printf(" OK\n"
                       "Allocating display memory buffer ..."); fflush(stdout);

                XBuf = img->data = malloc(img->height * img->bytes_per_line);
                if(!XBuf)
                {
                        puts(" FAILED\n"); fflush(stdout);
                        return(-1);
                }
                puts(" OK"); fflush(stdout);

        }


        printf("-------------------------------------------------------------------------------\n"); fflush(stdout);

        RefreshVisual();

        return(0);
   
} /* static int InitDisplay() */










/*******************************************************************************
*                                                                              *
* static int DestroyDisplay()                                                  *
*                                                                              *
********************************************************************************
*                                                                              *
* This routine is used to destroy the visual.                                  *
* It should print serious information of the current status.                   *
* It must NOT be called by the emulator. It's called from 'int CloseVisual()'. *
* The name is not standard but it should be used!                              *
*                                                                              *
* The routine returns a negative int on error and zero on success.             *
*                                                                              *
*******************************************************************************/





static int DestroyDisplay()
{
        printf("-------------------------------------------------------------------------------\n");

        printf("Destroying Unix/X display:\n");

#ifdef SHM
        if(prefs.UseSHM)
        {
                printf("Detaching shared memory ..."); fflush(stdout);

                XShmDetach(dsp, &XBuf_SHMInfo);
                shmdt(XBuf_SHMInfo.shmaddr);
                free(img);
        }
        else
#endif SHM
        {
                printf("Destroying image ..."); fflush(stdout);
                
                XDestroyImage(img);
        }

        printf(" OK\n"
               "Freeing graphics context ..."); fflush(stdout);

        XFreeGC(dsp, gc);

        printf(" OK\n"
               "Unmapping Window ..."); fflush(stdout);

        XUnmapWindow(dsp, win);

        printf(" OK\n"
          "Destroying Window ..."); fflush(stdout);

        XDestroyWindow(dsp, win);
   
        printf(" OK\n"
          "Closing display ..."); fflush(stdout);

        if(xkeyboardstate.global_auto_repeat == AutoRepeatModeOn)
                XAutoRepeatOn(dsp);
   
        XCloseDisplay(dsp);
        
        printf(" OK\n");
   


        printf("-------------------------------------------------------------------------------\n"); fflush(stdout);

        return(0);

} /* static int DestroyDisplay() */









static void PrintText(char *text, int x, int y)
{
        XSetForeground(dsp, gc, black);
        XDrawString(dsp, win, gc, x+1, y+1, text, strlen(text));
        
        XSetForeground(dsp, gc, white);
        XDrawString(dsp, win, gc, x, y, text, strlen(text));
   
} /* static void PrintText(char *text, int x, int y) */





#ifdef DEBUG_DISPLAY

static void PrintDisplayMode(WORD flags)
{
        printf("Displaymode:\n");
   

   
        switch(flags & 0x0003)
        {
            case 0:
                printf("   blank (no data, no sync)\n");
                break;

            case 1:
                printf("   reserved\n");
                break;

            case 2:
                printf("   5/5/5/3 (\"16\" bit)\n");
                break;
         
            case 3:
                printf("   8/8/8/8 (32 bit)\n");
                break;
         
        } /* switch(flags & 0x0003) */
   

   
        if(flags & 0x0004)
                printf("   gamma_dither_enable\n");
        if(flags & 0x0008)
                printf("   gamma_enable\n");
        if(flags & 0x0010)
                printf("   divot_enable\n");
        if(flags & 0x0040)
                printf("   serrate\n");
   

   
        printf("   anti-alias (aa) mode\n");
   
        switch((flags & 0x0300) >> 8)
        {
            case 0:
                printf("      aa & resamp (always fetch extra lines)\n");
                break;

            case 1:
                printf("      aa & resamp (fetch extra lines if needed)\n");
                break;

            case 2:
                printf("      resamp only (treat as all fully covered)\n");
                break;
         
            case 3:
                printf("      neither (replicate pixels, no interpolate)\n");
                break;
         
        } /* switch((flags & 0x0300) >> 8) */
   
} /* static void PrintDisplayMode(WORD flags) */

#endif





void RefreshVisual()
{
        WORD    new_width;
        int     i, j, k, l;
        BYTE    *Imsrc1, *Imdst1, c;
        HWORD   *Imsrc2, *Imdst2, ui;
        WORD    *Imsrc4, *Imdst4, ul;

#ifdef FPS
#ifdef LINUX   /* FPS - I don't know if this works on other os than linux - please mail */
        static char             fps_text[40];
        int                     fps_update_sec = 1;   /* min update frequency in sec */
                                                      /* otherwise you might not read it properly */
        static long             fps_update = 0;   /* stores the next time for update */
        static int              fps_count = 0;   /* to make fps average */
        static struct timezone  fps_tz = { 0, DST_NONE};
        static float            fps_rate = 0.0;
        static struct timeval   fps_tv_old  = { 0, 0 };
        struct timeval          fps_tv;
        
#endif
#endif


   
        /* video flags */
   
        flags = ((WORD *)mem.vi_reg)[0];

#ifdef DEBUG_DISPLAY
        if(prefs.debug & DBG_DISP_REFRESH)
                puts("DBG_DISP_REFRESH: refresh");
        if(prefs.debug & DBG_DISP_MODE)
                PrintDisplayMode(flags);
#endif
   
   
   
        /* video width */
        /* if width in VI_WIDTH_REG (0x04400008) has changed set windows width */
   
        new_width = ((WORD *)mem.vi_reg)[2];
        if(new_width != width)
        {
                if(new_width < 320 || new_width > 640)
                        width = 640;
                else
                        width = new_width;

                height = (width * 3) >> 2;

                XResizeWindow(dsp, win, width, height);
        }


      
        /* display it! */

        if(new_width >= 320 && new_width <= 640)
        {
                if(flags & 0x0003)
                {
                        XGetWindowAttributes(dsp, win, &win_attrib);
                        if(WIDTH != win_attrib.width || HEIGHT != win_attrib.height)
                        {
                                WIDTH  = win_attrib.width;
                                HEIGHT = win_attrib.height;

                                /* free all display related memory */
                                FreeDisplayMem();
         

                                /* allocate a new portion of display related memory */
                                AllocDisplayMem();

                        } /* if(WIDTH != win_attrib.width || HEIGHT != win_attib.height) */


         
                        /* source video origin */
                        Imsrc4 = (WORD  *) (mem.rd_ram + ((WORD *)mem.vi_reg)[1]);
                        Imsrc2 = (HWORD *) Imsrc4;
                        Imsrc1 = (BYTE  *) Imsrc2;
         
                        /* destination video origin */
                        Imdst4 = (WORD  *) img->data;
                        Imdst2 = (HWORD *) Imdst4;
                        Imdst1 = (BYTE  *) Imdst2;

/* everything in the following switch comes from:  */
/* Guenther Sohler <guenther.sohler@newlogic.at>   */
/* HEY, GREAT WORK! (niki.waibel@gmx.net)          */
                        switch(DEPTH)
                        {
                            case 8:
                                switch(flags & 0x0003)
                                {
                                    case 0:
                                    case 1:
                                        break;
                                    
                                    case 2:   /* local: 2/2/2 (8bit - 6bit used)  n64: 5/5/5/3 (16bit) */
                                        j=0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
                                                                /* 
                                                                ** The following was modified by me
                                                                ** (Niki W. Waibel) to support correct colors
                                                                ** on big and little endians.
                                                                */
#ifdef ENDIAN_IS_LITTLE
                                                                ui = Imsrc2[(i*width+k)^0x01];
#endif
#ifdef ENDIAN_IS_BIG
                                                                ui = Imsrc2[i*width+k];
#endif
                                                                c  = color6bit[((ui & 0x30) >> 4) | ((ui & 0x600) >> 7) | ((ui & 0xc000) >> 10)].pixel;
                                                                for(; l*width<k*WIDTH; l++)
                                                                        Imdst1[j*img->bytes_per_line+l] = c;
                                                        }
                                                }
                                        break;
                                    
                                    case 3:   /* local: 2/2/2 (8bit - 6bit used)   n64: 8/8/8/8 (32bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
                                                                ul = Imsrc4[i*width+k];
                                                                c  = color6bit[((ul & 0xc0) >> 6) | ((ul & 0xc000) >> 12) | (( ul & 0xc00000) >> 18)].pixel;
                                                                for(; l*width<k*WIDTH;l++)
                                                                        Imdst1[j*img->bytes_per_line+l] = c;
                                                        }
                                                }
                                        break;

                                } /* switch(flags & 0x0003) */
                                break;
            
                            case 16:
                                switch(flags & 0x0003)
                                {
                                    case 0:
                                    case 1:
                                        break;

                                    case 2:   /* local: 5/6/5 (16bit)   n64: 5/5/5/3 (16bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
                                                                /* 
                                                                ** The following was modified by me
                                                                ** (Niki W. Waibel) to support correct colors
                                                                ** on big and little endians.
                                                                */
#ifdef ENDIAN_IS_LITTLE
                                                                ui = Imsrc2[(i*width+k)^0x01];
#endif
#ifdef ENDIAN_IS_BIG
                                                                ui = Imsrc2[i*width+k];
#endif
                                                                for(; l*width<k*WIDTH; l++)
                                                                        Imdst2[j*img->bytes_per_line/2+l] = ((ui >> 1) & 0x1f) + (ui & 0xffc0);
                                                        }
                                                }
                                        break;

                                    case 3:   /* local: 5/6/5 (16bit)   n64: 8/8/8/8 (32bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
                                                                ul = Imsrc4[i*width+k];
                                                                ui = ((ul & 0xF8) >> 3) | ((ul & 0xFC00) >> 5) | ((ul & 0xF80000) >> 8);
                                                                for(; l*width<k*WIDTH; l++)
                                                                        Imdst2[j*img->bytes_per_line/2+l] = ui;
                                                        }
                                                }
                                                break;

                                } /* switch(flags & 0x0003) */
                                break;

                        
                            /* 
                            ** The following 15/24/32bit stuff was hacked out by myself
                            ** (Niki W. Waibel) using the above (8/16bit) from
                            ** Guenther Sohler as a bases.
                            */
                            case 15:
                                switch(flags & 0x0003)
                                {
                                    case 0:
                                    case 1:
                                        break;

                                    case 2:   /* local: 5/5/5 (15bit)   n64: 5/5/5/3 (16bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
#ifdef ENDIAN_IS_LITTLE
                                                                ui = Imsrc2[(i*width+k)^0x01];
#endif
#ifdef ENDIAN_IS_BIG
                                                                ui = Imsrc2[i*width+k];
#endif
                                                                for(; l*width<k*WIDTH; l++)
                                                                        Imdst2[j*img->bytes_per_line/2+l] = ui >> 1;
                                                        }
                                                }
                                        break;

                                    case 3:   /* local: 5/5/5 (15bit)   n64: 8/8/8/8 (32bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
                                                                ul = Imsrc4[i*width+k];
                                                                ui = ((ul & 0xF8) >> 3) | ((ul & 0xFC00) >> 5) | ((ul & 0xF80000) >> 8);
                                                                for(; l*width<k*WIDTH; l++)
                                                                        Imdst2[j*img->bytes_per_line/2+l] = ui;
                                                        }
                                                }
                                                break;

                                } /* switch(flags & 0x0003) */
                                break;

                            case 24:
                                switch(flags & 0x0003)
                                {
                                    case 0:
                                    case 1:
                                        break;

                                    case 2:   /* local: 8/8/8 (24bit)   n64: 5/5/5/3 (16bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
#ifdef ENDIAN_IS_LITTLE
                                                                ui = Imsrc2[(i*width+k)^0x01];
#endif
#ifdef ENDIAN_IS_BIG
                                                                ui = Imsrc2[i*width+k];
#endif
                                                                for(; l*width<k*WIDTH; l++)
                                                                {
#ifdef ENDIAN_IS_LITTLE
                                                                        Imdst1[j*img->bytes_per_line+l*3+0] = (ui << 2) & 0xf8;
                                                                        Imdst1[j*img->bytes_per_line+l*3+2] = (ui >> 8) & 0xf8;
#endif
#ifdef ENDIAN_IS_BIG
                                                                        Imdst1[j*img->bytes_per_line+l*3+2] = (ui << 2) & 0xf8;
                                                                        Imdst1[j*img->bytes_per_line+l*3+0] = (ui >> 8) & 0xf8;
#endif
                                                                        Imdst1[j*img->bytes_per_line+l*3+1] = (ui >> 3) & 0xf8;
                                                                }
                                                        }
                                                }
                                        break;

                                    case 3:   /* local: 8/8/8 (24bit)   n64: 8/8/8/8 (32bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
                                                                ul = Imsrc4[i*width+k];
                                                                ui = ((ul & 0xF8) >> 3) | ((ul & 0xFC00) >> 5) | ((ul & 0xF80000) >> 8);
                                                                for(; l*width<k*WIDTH; l++)
                                                                        Imdst2[j*img->bytes_per_line/2+l] = ui;
                                                        }
                                                }
                                                break;

                                } /* switch(flags & 0x0003) */
                                break;

                            case 32:
                                switch(flags & 0x0003)
                                {
                                    case 0:
                                    case 1:
                                        break;

                                    case 2:   /* local: 8/8/8 (32bit)   n64: 5/5/5/3 (16bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
#ifdef ENDIAN_IS_LITTLE
                                                                ui = Imsrc2[(i*width+k)^0x01];
#endif
#ifdef ENDIAN_IS_BIG
                                                                ui = Imsrc2[i*width+k];
#endif
                                                                for(; l*width<k*WIDTH; l++)
                                                                {
#ifdef ENDIAN_IS_LITTLE
                                                                        Imdst1[j*img->bytes_per_line+l*4+0] = (ui << 2) & 0xf8;
                                                                        Imdst1[j*img->bytes_per_line+l*4+2] = (ui >> 8) & 0xf8;
#endif
#ifdef ENDIAN_IS_BIG
                                                                        Imdst1[j*img->bytes_per_line+l*4+2] = (ui << 2) & 0xf8;
                                                                        Imdst1[j*img->bytes_per_line+l*4+0] = (ui >> 8) & 0xf8;
#endif
                                                                        Imdst1[j*img->bytes_per_line+l*4+1] = (ui >> 3) & 0xf8;
                                                                }
                                                        }
                                                }
                                        break;

                                    case 3:   /* local: 8/8/8 (32bit)   n64: 8/8/8/8 (32bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
                                                                ul = Imsrc4[i*width+k];
                                                                ui = ((ul & 0xF8) >> 3) | ((ul & 0xFC00) >> 5) | ((ul & 0xF80000) >> 8);
                                                                for(; l*width<k*WIDTH; l++)
                                                                        Imdst2[j*img->bytes_per_line/2+l] = ui;
                                                        }
                                                }
                                                break;

                                } /* switch(flags & 0x0003) */
                                break;

                            default:
                                fprintf(stderr, "Display depth of %d bit is not supported! (yet ;) )\n", DEPTH);
                                
                        } /* switch(DEPTH) */
         
                        PutImage(0, 0, WIDTH, HEIGHT);
                }
                else
                {
                        PrintText("DISPLAY: blank (no data, no sync)", 20, 40);
                }
        }
        else
        {
                if(!(flags & 0x0003))
                        PrintText("DISPLAY: blank (no data, no sync)", 20, 40);
         
                PrintText("DISPLAY: wrong width (640 will be used!)", 20, 60);
        }



#ifdef FPS
#ifdef LINUX   /* FPS - I don't know if this works on other os than linux - please mail */

        gettimeofday(&fps_tv, &fps_tz);

        fps_rate = fps_count * fps_rate
                   + 1 / ( ((float)fps_tv.tv_sec     + (float)fps_tv.tv_usec     / 1000000)
                         - ((float)fps_tv_old.tv_sec + (float)fps_tv_old.tv_usec / 1000000) );

        fps_count++;

        fps_rate /= fps_count;
        
        if(fps_update <= fps_tv.tv_sec)
        {
                fps_update = fps_tv.tv_sec + fps_update_sec;
                fps_count  = 0;
                
                if(fps_rate > 99.9)
                        fps_rate = 99.99;
                
                sprintf(fps_text, "%05.2f", fps_rate);

                fps_rate = 0.0;
        }
        
        fps_tv_old.tv_sec = fps_tv.tv_sec;
        fps_tv_old.tv_usec = fps_tv.tv_usec;
        
        PrintText(fps_text, 01, 20);
#endif
#endif
   
        XFlush(dsp);

} /* void RefreshVisual() */





void GetVisualName(char *name, int *n)
{
   strncpy(name, DISPLAYNAME, (size_t) *n);
   
   *n = strlen(DISPLAYNAME);
   
} /* void GetVisualName() */





WORD CheckVisual()
{
        XEvent          event;
        KeySym          key;

        static HWORD    keystate = 0;
        static sBYTE    stick_x = 0;
        static sBYTE    stick_y = 0;




        /* FocusChange */

        if(XCheckWindowEvent(dsp, win, FocusChangeMask, &event))
        {
                if(event.type == FocusIn)
                {
                        puts("   ***   F O C U S   I N   ***");
                        
                        XGetKeyboardControl(dsp, &xkeyboardstate);

                        if(xkeyboardstate.global_auto_repeat == AutoRepeatModeOn)
                                XAutoRepeatOff(dsp);
                }
                else
                {
                        puts("   ***   F O C U S   O U T   ***");

                        if(xkeyboardstate.global_auto_repeat == AutoRepeatModeOn)
                                XAutoRepeatOn(dsp);
                }
        }



        /* ButtonPress / ButtonRelease */ 

        if(XCheckWindowEvent(dsp, win, ButtonPressMask | ButtonReleaseMask, &event))
        {
        }



        /* KeyPress and KeyReleases */

        if(XCheckWindowEvent(dsp, win, KeyPressMask | KeyReleaseMask, &event))
        {
                key = XLookupKeysym((XKeyEvent *)&event, 0);

                if(event.type == KeyPress)
                {
                        switch(key)
                        {
                            case XK_Escape:
                                dispatch(SHUTDOWN, 0);

                            case XK_F8:
                                RefreshVisual();
                                break;

                            case XK_q:           /* Start Button */
                                keystate |= 0x1000;
                                break;

                            case XK_w:                  /* A-Button */
                                keystate |= 0x8000;
                                break;

                            case XK_e:                  /* B-Button */
                                keystate |= 0x4000;
                                break;

                            case XK_Home:               /* C-Button UP */
                                keystate |= 0x0008;
                                break;

                            case XK_End:                /* C-Button DOWN */
                                keystate |= 0x0004;
                                break;

                            case XK_Delete:          /* C-Button LEFT */
                                keystate |= 0x0002;
                                break;

                            case XK_Next:          /* C-Button RIGHT */
                                keystate |= 0x0001;
                                break;

                            case XK_Up:              /* D-Button UP */
                                keystate |= 0x0800;
                                break;

                            case XK_Down:            /* D-Button DOWN */
                                keystate |= 0x0400;
                                break;

                            case XK_Left:            /* D-Button LEFT */
                                keystate |= 0x0200;
                                break;

                            case XK_Right:           /* D-Button RIGHT */
                                keystate |= 0x0100;
                                break;

                            case XK_a:          /* R-Button */
                                keystate |= 0x0010;
                                break;

                            case XK_s:            /* L-Button */
                                keystate |= 0x0020;
                                break;

                            case XK_d:                  /* Z-Button */
                                keystate |= 0x2000;
                                break;

                            case XK_k:   /* Stick Up */
                                if(stick_y < 120)
                                        stick_y += 0x08;
                                break;

                            case XK_j:   /* Stick Down */
                                if(stick_y > -120)
                                        stick_y -= 0x08;
                                break;

                            case XK_l:   /* Stick Right */
                                if(stick_x < 120)
                                        stick_x += 0x08;
                                break;

                            case XK_h:   /* Stick Left */
                                if(stick_x > -120)
                                        stick_x -= 0x08;
                                break;

                        } /* switch(key) */
                }
                else /* (event.type == KeyRelease) */
                {
                        switch(key)
                        {
                            case XK_q:           /* Start Button */
                                keystate &= ~0x1000;
                                break;

                            case XK_w:                  /* A-Button */
                                keystate &= ~0x8000;
                                break;

                            case XK_e:                  /* B-Button */
                                keystate &= ~0x4000;
                                break;

                            case XK_Home:               /* C-Button UP */
                                keystate &= ~0x0008;
                                break;

                            case XK_End:                /* C-Button DOWN */
                                keystate &= ~0x0004;
                                break;

                            case XK_Delete:          /* C-Button LEFT */
                                keystate &= ~0x0002;
                                break;

                            case XK_Next:          /* C-Button RIGHT */
                                keystate &= ~0x0001;
                                break;

                            case XK_Up:              /* D-Button UP */
                                keystate &= ~0x0800;
                                break;

                            case XK_Down:            /* D-Button DOWN */
                                keystate &= ~0x0400;
                                break;

                            case XK_Left:            /* D-Button LEFT */
                                keystate &= ~0x0200;
                                break;

                            case XK_Right:           /* D-Button RIGHT */
                                keystate &= ~0x0100;
                                break;

                            case XK_a:          /* R-Button */
                                keystate &= ~0x0010;
                                break;

                            case XK_s:            /* L-Button */
                                keystate &= ~0x0020;
                                break;

                            case XK_d:                  /* Z-Button */
                                keystate &= ~0x2000;
                                break;

                        } /* switch(key) */

                } /* (event.type == KeyRelease) */

        } /* if(XCheckWindowEvent(dsp, win, KeyPressMask | KeyReleaseMask, &event)) */



        /* Exposure */

        if(XCheckWindowEvent(dsp, win, ExposureMask, &event))
        {
                puts("   ***   E X P O S U R E   ***");
                RefreshVisual();
        }

        

#ifdef DEBUG_CONTROLLER
        if(prefs.debug & DBG_CONTR_BUTTONS)
                printf
                (
                        "Controller: "
                        "0x%04hx %+4d %+4d (0x%08lx) (0xBBBBXXYY) "
                        "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",

                        keystate, stick_x, stick_y,
                        (WORD)(keystate << 16) | ((WORD)(HWORD)(stick_x << 8)) | ((WORD)(BYTE)stick_y),
                        (keystate & 0x8000) ? "A "      : "",
                        (keystate & 0x4000) ? "B "      : "",
                        (keystate & 0x2000) ? "Z "      : "",
                        (keystate & 0x1000) ? "START "  : "",
                        (keystate & 0x0800) ? "Dup "    : "",
                        (keystate & 0x0400) ? "Ddown "  : "",
                        (keystate & 0x0200) ? "Dleft "  : "",
                        (keystate & 0x0100) ? "Dright " : "",
                        (keystate & 0x0080) ? ""        : "",
                        (keystate & 0x0040) ? ""        : "",
                        (keystate & 0x0020) ? "L "      : "",
                        (keystate & 0x0010) ? "R "      : "",
                        (keystate & 0x0008) ? "Cup "    : "",
                        (keystate & 0x0004) ? "Cdown "  : "",
                        (keystate & 0x0002) ? "Cleft "  : "",
                        (keystate & 0x0001) ? "Cright " : "" 
                );
#endif

        return(((WORD)(keystate << 16)) | ((WORD)(HWORD)(stick_x << 8)) | ((WORD)(BYTE)stick_y));

} /* WORD CheckVisual() */










static void PutImage(int X, int Y, int W, int H)
{
#ifdef SHM
   if(prefs.UseSHM)
   {
      XShmPutImage(dsp, win, gc, img, X, Y, (WIDTH-W)/2, (HEIGHT-H)/2, W, H, False);
   }
   else
#endif
      XPutImage(dsp, win, gc, img, X, Y, (WIDTH-W)/2, (HEIGHT-H)/2, W, H);
   
} /* static void PutImage(int X, int Y, int W, int H) */





static void FreeDisplayMem()
{
#ifdef SHM
        if(prefs.UseSHM)
        {
                XShmDetach(dsp, &XBuf_SHMInfo);
                shmdt(XBuf_SHMInfo.shmaddr);
                free(img);
        }
        else
#endif
        {
                XDestroyImage(img);
        }
        //fputs("FreeDisplayMem: ready\n", stderr); fflush(stderr);

} /* static void FreeDisplayMem() */





static void AllocDisplayMem()
{
#ifdef SHM
        struct shmid_ds s;
#endif



#ifdef SHM   /* this code was first coded by me (Niki W. Waibel) */
             /* then improved by Guenther Sohler (thanks a lot, Gue!) */
        if(prefs.UseSHM)
        {
                img = XShmCreateImage(dsp, DefaultVisualOfScreen(scr), DEPTH, ZPixmap, NULL, &XBuf_SHMInfo, WIDTH, HEIGHT);
                if(!img)
                { 
                        puts("FATAL DISPLAY ERROR: XCreateImage\n\n"); fflush(stdout);
                        exit(1);
                }
      
                XBuf_SHMInfo.shmid = shmget(IPC_PRIVATE, img->height * img->bytes_per_line, IPC_CREAT | 0777);
      
                if(XBuf_SHMInfo.shmid < 0)
                {
                        puts("FATAL DISPLAY ERROR: shmget\n\n"); fflush(stdout);
                        exit(1);
                }
      
                XBuf = img->data = XBuf_SHMInfo.shmaddr = shmat(XBuf_SHMInfo.shmid, 0, 0);
                if(!XBuf)
                {
                        puts("FATAL DISPLAY ERROR: shmat\n\n"); fflush(stdout);
                        exit(1);
                }

                XBuf_SHMInfo.readOnly = False;
      
                if(!XShmAttach(dsp, &XBuf_SHMInfo))
                {
                        puts("FATAL DISPLAY ERROR: XShmAttach\n\n"); fflush(stdout);
                        exit(1);
                }         

                shmctl(XBuf_SHMInfo.shmid, IPC_RMID, &s);   /* if someone kills me shm should be freed */
        }
        else
#endif
        {
                img = XCreateImage(dsp, DefaultVisualOfScreen(scr), DEPTH, ZPixmap, 0, XBuf, WIDTH, HEIGHT, DEPTH, 0);
                if(!img)
                { 
                        puts("FATAL DISPLAY ERROR: XCreateImage\n\n"); fflush(stdout);
                        exit(1);
                }

                XBuf = img->data = malloc(img->height * img->bytes_per_line);
                if(!XBuf)
                {
                        puts("FATAL DISPLAY ERROR: malloc\n\n"); fflush(stdout);
                        exit(1);
                }

        }

} /* static void AllocDisplayMem() */

