/**********************************************/
/* Reusable graphics and display routines     */
/* Evert Glebbeek 2002, 2003                  */
/* eglebbk@phys.uva.nl                        */
/**********************************************/
#include <allegro.h>
#include <string.h>
#include <stdio.h>
#ifdef USE_JPEG
   #include "gfxsup/jpeg/jpeg.h"
#endif
#ifdef USE_GIF
   #include "gfxsup/gif/load_gif.h"
#endif
#include "global.h"
#include "gfx.h"

/* Maxiumum number of dirty-rectangles */
#define MAX_DRECT      2000

/* Data for rendering a bitmap */
typedef struct RENDERSTACK {
   BITMAP *bmp;
   int x, y;
   int w, h;
   int c1, c2, c3, c4;
} RENDERSTACK;

int black;
int grey;
int darkgrey;
int white;
int red;
int yellow;
int blue;
int green;
int bright_red;
int bright_blue;
int darkgreen;


/* Screen regions */
static BITMAP *arena = NULL;
static BITMAP *interface = NULL;
static int arena_x = 0;
static int arena_y = 0;
static int arena_w = 0;
static int arena_h = 0;
static int interface_x = 0;
static int interface_y = 0;
static int interface_w = 0;
static int interface_h = 0;

/* Virtual screen pages for various graphic update modes */
/*  taken from the Allegro demo */
/* Use a 'dirty-tile' system, just is in SGT. This should work reasonably
   well, as long as things like light-source rendering and charecter movement
   are handled properly. (Linked lists!) */
/* For now, we'll only use double buffering */
static BITMAP *page1 = NULL;
static BITMAP *page2 = NULL;
static BITMAP *page3 = NULL;
static BITMAP *scrnbuf = NULL;
static BITMAP *vscreen = NULL;
static int drect_count = 0;
static DRECT *drect_stack = NULL;
static int current_page = 0;

/* Datastructure holding data that is to be rendered */
static RENDERSTACK *renstk = NULL;
static int n_renjobs = 0;

/* FPS variables */
static volatile unsigned int fps = 0;
static volatile unsigned int frame_count = 0;
static int _fps_counter_locked = FALSE;

/* Function templates */
int (*makecolour) (int r, int g, int b) = makecol;

/***************************/
/* FPS timer functions     */
/***************************/
static void fps_proc(void)
{
   fps = frame_count;
   frame_count = 0;
}

END_OF_STATIC_FUNCTION(fps_proc);

int get_fps(void)
{
   return fps;
}

void inc_frame_counter(void)
{
   frame_count++;
}

void install_fps_counter(void)
{
   if (!_fps_counter_locked) {
      LOCK_FUNCTION(fps_proc);
      LOCK_VARIABLE(frame_count);
      LOCK_VARIABLE(fps);
      _fps_counter_locked = TRUE;
   }
   install_int(fps_proc, 1000);
}

void uninstall_fps_counter(void)
{
   remove_int(fps_proc);
}

/***************************/
/* Bitmap rendering        */
/***************************/
/* Push a render job onto the stack */
void push_render_job(BITMAP *bmp, int x, int y, int w, int h, int c1, int c2,
                     int c3, int c4)
{
   renstk = realloc(renstk, (n_renjobs + 1) * sizeof(RENDERSTACK));
   renstk[n_renjobs].bmp = bmp;
   renstk[n_renjobs].x = x;
   renstk[n_renjobs].y = y;
   renstk[n_renjobs].w = w;
   renstk[n_renjobs].h = h;
   renstk[n_renjobs].c1 = c1;
   renstk[n_renjobs].c2 = c2;
   renstk[n_renjobs].c3 = c3;
   renstk[n_renjobs].c4 = c4;
   n_renjobs++;
}

void do_render_job(void)
{
   BITMAP *bmp;
   int c;
   int i;

   if ((!n_renjobs) || (!renstk))
      return;

   for (c = 0; c < n_renjobs; c++) {
      /* Create a copy of our original bitmap area */
      bmp = create_bitmap_ex(bitmap_color_depth(renstk[c].bmp),
                             renstk[c].w, renstk[c].h);
      blit(renstk[c].bmp, bmp,
           renstk[c].x, renstk[c].y, 0, 0, renstk[c].w, renstk[c].h);
      /* Now put back a rendered copy */
      switch (settings.flags & (LIT_PLAIN + LIT_GOURAUD)) {
         case LIT_PLAIN:
            i = 255 - (renstk[c].c1 + renstk[c].c2 + renstk[c].c3 +
                       renstk[c].c4) / 4;
            if (i == 255) {
               rectfill(renstk[c].bmp, renstk[c].x, renstk[c].y,
                        renstk[c].x + renstk[c].w - 1,
                        renstk[c].y + renstk[c].h - 1, black);
            } else {
               draw_lit_sprite(renstk[c].bmp, bmp,
                               renstk[c].x, renstk[c].y, i);
            }
            break;
         case LIT_GOURAUD + LIT_PLAIN:   // Huh? Should this be possible?
         case LIT_GOURAUD:{
               if ((renstk[c].c1 == renstk[c].c2) &&
                   (renstk[c].c2 == renstk[c].c3) &&
                   (renstk[c].c3 == renstk[c].c4)) {
                  if (renstk[c].c1 == 255) {
                     /* No need to do anything */
                  } else if (renstk[c].c1 == 0) {
                     rectfill(renstk[c].bmp, renstk[c].x, renstk[c].y,
                              renstk[c].x + renstk[c].w - 1,
                              renstk[c].y + renstk[c].h - 1, black);
                  } else {
                     draw_lit_sprite(renstk[c].bmp, bmp,
                                     renstk[c].x, renstk[c].y,
                                     255 - renstk[c].c1);
                  }
               } else {
                  draw_gouraud_sprite(renstk[c].bmp, bmp,
                                      renstk[c].x, renstk[c].y,
                                      renstk[c].c1, renstk[c].c2,
                                      renstk[c].c3, renstk[c].c4);
               }
               break;
            }
      }
      destroy_bitmap(bmp);
   }
   free(renstk);
   renstk = NULL;
   n_renjobs = 0;
}

/* Dirty rectangel routines */
/* qsort() callback for sorting the dirty rectangle list */
static int rect_cmp(const void *p1, const void *p2)
{
   DRECT *r1 = (DRECT *)p1;
   DRECT *r2 = (DRECT *)p2;

   return (r1->y - r2->y);
}

/*
static int rect_area_cmp(const void *p1, const void *p2)
{
   DRECT *r1 = (DRECT *)p1;
   DRECT *r2 = (DRECT *)p2;

   return (r1->w*r1->h - r2->w*r2->h);
}
*/

/* Adds a dirty rectangle to the list if possible. Initializes the list
   if needed */
static void store_dirty(int x, int y, int w, int h)
{
   drect_stack[drect_count].x = x;
   drect_stack[drect_count].y = y;
   drect_stack[drect_count].w = w;
   drect_stack[drect_count].h = h;
   drect_count++;
}

void drect_add(int x, int y, int w, int h)
{
   if (!(settings.flags & DIRTY_RECTANGLE))
      return;

   if (drect_count > -1 && drect_count < MAX_DRECT) {
      int c;

      /* Check for overlap or touching of existing rectangles */
      for (c = 0; c < drect_count; c++) {
         if (rect_overlap(drect_stack[c].x, drect_stack[c].y,
                          drect_stack[c].w, drect_stack[c].h,
                          x - 1, y - 1, w + 1, h + 1)) {
            /* Is one of the rectangles contained within the other? */
            if (rect_contains
                (drect_stack[c].x, drect_stack[c].y, drect_stack[c].w,
                 drect_stack[c].h, x, y, w, h)
                || rect_contains(x, y, w, h, drect_stack[c].x,
                                 drect_stack[c].y, drect_stack[c].w,
                                 drect_stack[c].h)) {
               // The one is contained within the other
               // Only store the biggest one
               if ((w * h) > (drect_stack[c].w * drect_stack[c].h)) {
                  drect_stack[c].x = x;
                  drect_stack[c].y = y;
                  drect_stack[c].h = h;
                  drect_stack[c].w = w;
               }
               /* Done */
               return;
            } else if (!rect_overlap(drect_stack[c].x, drect_stack[c].y,
                                     drect_stack[c].w, drect_stack[c].h,
                                     x, y, w, h)) {
               // Check for complete touching of borders or partial
               if (((w == drect_stack[c].w) || (h == drect_stack[c].h)) && (
                                                                              // top
                                                                              ((x == drect_stack[c].x) && (y + h + 1 == drect_stack[c].y)) ||
                                                                              // bottom
                                                                              ((x == drect_stack[c].x) && (y == drect_stack[c].y + drect_stack[c].h + 1)) ||
                                                                              // left
                                                                              ((x + w + 1 == drect_stack[c].x) && (y == drect_stack[c].y)) ||
                                                                              //right
                                                                              ((x == drect_stack[c].x + drect_stack[c].w + 1) && (y == drect_stack[c].y))
                   )) {
                  if ((x == drect_stack[c].x) &&
                      (y + h + 1 == drect_stack[c].y)) {
                     /* Top */
                     drect_stack[c].y = y;
                     drect_stack[c].h += h + 1;
                  } else if ((x == drect_stack[c].x) &&
                             (y == drect_stack[c].y + drect_stack[c].h + 1)) {
                     /* Bottom */
                     /* Extend bottom side of rectangle further */
                     drect_stack[c].h += h + 1;
                  } else if ((x + w + 1 == drect_stack[c].x) &&
                             (y == drect_stack[c].y)) {
                     /* Left hand side */
                     /* Increase width and change start-location of
                        rectangle */
                     drect_stack[c].w += w + 1;
                     drect_stack[c].x = x;
                  } else if ((x == drect_stack[c].x + drect_stack[c].w + 1) &&
                             (y == drect_stack[c].y)) {
                     /* Right hand side */
                     /* Extend right side of rectangle further */
                     drect_stack[c].w += w + 1;
                  }
                  return;
               } else {
                  /* Only update partial touches if they are in the width,
                     not height (because of retrace issues) */
                  if ((x + w + 1 == drect_stack[c].x) &&
                      (y == drect_stack[c].y)) {
                     /* touch on the left side. */
                     /* Split into three or two rectangles. */

                     /* Do it the dumb way for now, that is, just add */
                     store_dirty(x, y, w, h);
                     return;
                  } else if ((x == drect_stack[c].x + drect_stack[c].w + 1) &&
                             (y == drect_stack[c].y)) {
                     /* touch on the right side. */
                     /* Split into three or two rectangles. */

                     /* Do it the dumb way for now, that is, just add */
                     store_dirty(x, y, w, h);
                     return;
                  } else {
                     /* Store rectangle */
                     store_dirty(x, y, w, h);
                     return;
                  }
               }
            } else {
               // Check for complete overlap of one side or partial
               if (((h == drect_stack[c].h && y == drect_stack[c].y) ||
                    (w == drect_stack[c].w && x == drect_stack[c].x)) &&
                   rect_overlap(drect_stack[c].x, drect_stack[c].y,
                                drect_stack[c].w, drect_stack[c].h,
                                x, y, w, h)) {
                  /* Merge the two rectangles */
                  if (x == drect_stack[c].x) {
                     if (y > drect_stack[c].y) {
                        drect_stack[c].h += h + (drect_stack[c].y+drect_stack[c].h-y);
                     } else {
                        drect_stack[c].h += h + (y+h-drect_stack[c].y);
                        drect_stack[c].y = y;
                     }
                     //drect_stack[c].y = MIN(drect_stack[c].y, y);
                  } else {
                     if (x > drect_stack[c].x) {
                        drect_stack[c].w += w + (drect_stack[c].x+drect_stack[c].w-x);
                     } else {
                        drect_stack[c].w += w + (x+w-drect_stack[c].x);
                        drect_stack[c].x = x;
                     }
                     //drect_stack[c].x = MIN(drect_stack[c].x, x);
                  }
                  return;
               }
            }
         }
      }
      if (c == drect_count) {   /* Add new rectangle */
         store_dirty(x, y, w, h);
      }
      /* Sort according to size */
      //qsort(drect_stack, drect_count, sizeof(DRECT), rect_area_cmp);
   } else {
      drect_count = -1;
   }
}

/* Mark a rectangle - actually, this is just an external hook to drect_add */
/* This merges rectangles if it can */
void mark_rect(int x, int y, int w, int h)
{
   drect_add(x, y, w, h);
}

/* Mark a rectangle - actually, this is just an external hook to drect_add */
/* This NEVER merges rectangles */
void store_rect(int x, int y, int w, int h)
{
   store_dirty(x, y, w, h);
}

void mark_commandmap(void)
{
   mark_rect(arena_x, arena_y, arena_w, arena_h);
}

void mark_interface(void)
{
   mark_rect(interface_x, interface_y, interface_w, interface_h);
}


/************************************/
/* Drawing functions and primitives */
/************************************/

/* Create a (speed-optimized) bitmap, suitable for masked blitting */
BITMAP *create_optimized_mask_bitmap(const int w, const int h)
{
   BITMAP *bmp = NULL;

   if (gfx_capabilities & GFX_HW_VRAM_BLIT_MASKED)
      bmp = create_video_bitmap(w, h);
   if (bmp)
      return bmp;
   if (gfx_capabilities & GFX_HW_SYS_TO_VRAM_BLIT_MASKED)
      bmp = create_system_bitmap(w, h);
   if (bmp)
      return bmp;
   return create_bitmap(w, h);
}

/* Create a (speed-optimized) bitmap, suitable for non-masked blitting */
BITMAP *create_optimized_bitmap(const int w, const int h)
{
   BITMAP *bmp = NULL;

   if (gfx_capabilities & GFX_HW_VRAM_BLIT) {
      bmp = create_video_bitmap(w, h);
   }
   if (bmp)
      return bmp;
   if (gfx_capabilities & GFX_HW_SYS_TO_VRAM_BLIT) {
      bmp = create_system_bitmap(w, h);
   }
   if (bmp)
      return bmp;
   return create_bitmap(w, h);
}

/* Outline a graphic in a specific colour */
/* Returns a new bitmap, or NULL on failure */
BITMAP *outline_bitmap(BITMAP *bmp, const int colour)
{
   BITMAP *work;
   int trc;
   int x;
   int y;
   int (*getpxl) (BITMAP *bmp, int x, int y);
   void (*putpxl) (BITMAP *bmp, int x, int y, int color);

   ASSERT(bmp);

   work = create_bitmap_ex(bitmap_color_depth(bmp), bmp->w, bmp->h);
   if (!work) {
      return NULL;
   }

   blit(bmp, work, 0, 0, 0, 0, bmp->w, bmp->h);

   /* Read transparent colour */
   trc = bitmap_mask_color(work);

   switch (bitmap_color_depth(work)) {
      case 8:
         getpxl = _getpixel;
         putpxl = _putpixel;
         break;
      case 15:
         getpxl = _getpixel15;
         putpxl = _putpixel15;
         break;
      case 16:
         getpxl = _getpixel16;
         putpxl = _putpixel16;
         break;
      case 24:
         getpxl = _getpixel24;
         putpxl = _putpixel24;
         break;
      case 32:
         getpxl = _getpixel32;
         putpxl = _putpixel32;
         break;
      default:
         getpxl = getpixel;
         putpxl = putpixel;
         break;
   }

   for (y = 0; y < work->h - 1; y++)
      for (x = 0; x < work->w - 1; x++) {
         if (getpxl(bmp, x, y) == trc &&
             ((getpxl(bmp, x + 1, y) != trc) ||
              (getpxl(bmp, x, y + 1) != trc))) {
            putpxl(work, x, y, colour);
         }
      }

   for (y = 1; y < work->h; y++)
      for (x = 1; x < work->w; x++) {
         if (getpxl(bmp, x, y) == trc &&
             ((getpxl(bmp, x - 1, y) != trc) ||
              (getpxl(bmp, x, y - 1) != trc))) {
            putpxl(work, x, y, colour);
         }
      }

   return work;
}

/* Accessing the screen bitmap. This is a wrapper that will also allow */
/*  drawing to the screen when the update method is not 'immediate' */
BITMAP *get_screen_bmp(void)
{
   return vscreen;
}

/* Will return a pointer to the screen currently being displayed; or rather,
    the actual screen instead of a wrapper. For displaying popup-boxes and
   such */
BITMAP *get_monitor_bmp(void)
{
   if (settings.flags & PAGE_FLIP) {
      if (current_page == 0) {
         return page1;
      } else {
         return page2;
      }
   } else if (settings.flags & TRIPLE_BUFFER) {
      switch (current_page) {
         case 0:
            return page1;
         case 1:
            return page2;
         default:
            return page3;
      }                         /* End of switch */
   } else {
      return screen;
   }
}


/* Returns 0 on succes and non-zero on failure */
int set_graphics(void)
{
   int try_depths[] = {32, 24, 16, 15, 8};
   int colour_depth = settings.colour_depth;
   int gfx_driver = GFX_AUTODETECT;
   int res;
   int try;

#ifdef USE_JPEG
   register_bitmap_file_type("jpg", load_jpg, NULL);
#endif
#ifdef USE_GIF
   register_bitmap_file_type("gif", load_gif, NULL);
#endif

   if (settings.flags & WINDOWED_MODE) {
      gfx_driver = GFX_AUTODETECT_WINDOWED;

      /* override colour depth with desktop setting */
      if (desktop_color_depth()) {
         settings.colour_depth = colour_depth = desktop_color_depth();
      }
   } else {
      gfx_driver = GFX_AUTODETECT_FULLSCREEN;
   }
   
#ifndef ALLEGRO_VRAM_SINGLE_SURFACE
	settings.virtual_width = 0;
	settings.virtual_height = 0;
#endif
   try = 0;
   do {
      set_color_depth(colour_depth);
      res = set_gfx_mode(gfx_driver, settings.width, settings.height, settings.virtual_width, settings.virtual_height);
      /* Check for failures */
      if (res) {
         if (try==5 || colour_depth<settings.lowest_colour_depth) {
            if (gfx_driver != GFX_SAFE) {
               gfx_driver = GFX_SAFE;
               try = 0;
            } else {
               return -1;
            }
         }
         colour_depth = try_depths[try];
         try++;
      }
   } while (res);

   clear_bitmap(screen);
   text_mode(-1);
#ifndef ALLEGRO_DOS
   set_display_switch_mode(SWITCH_PAUSE);
#endif

   /* Report success */
   return 0;
}

BITMAP *get_commandmap_bmp(void)
{
   return arena;
}

BITMAP *get_interface_bmp(void)
{
   return interface;
}

/* Sets the commandmap screen region */
void set_commandmap(const int x, const int y, const int w, const int h)
{
   arena_x = x;
   arena_y = y;
   arena_w = w;
   arena_h = h;
}

int get_commandmap_width(void)
{
   return arena_w;
}

int get_commandmap_height(void)
{
   return arena_h;
}

int get_commandmap_x(void)
{
   return arena_x;
}

int get_commandmap_y(void)
{
   return arena_y;
}

/* Sets the interface screen region */
void set_interface(const int x, const int y, const int w, const int h)
{
   interface_x = x;
   interface_y = y;
   interface_w = w;
   interface_h = h;
}

#define DESTROY_BITMAP(bmp) {if ((bmp) && (bmp)!=screen) destroy_bitmap(bmp);}

/* Readies double buffering and things like that for the first time */
/* Call this once at startup */
void prepare_screen(void)
{
   if (!arena_w)
      arena_w = SCREEN_W;
   if (!arena_h)
      arena_h = SCREEN_H;
   if (!interface_w)
      interface_w = SCREEN_W;
   if (!interface_h)
      interface_h = SCREEN_H;

   DESTROY_BITMAP(page1);
   DESTROY_BITMAP(page2);
   DESTROY_BITMAP(page3);
   DESTROY_BITMAP(scrnbuf);
   DESTROY_BITMAP(arena);
   DESTROY_BITMAP(interface);

   if ((settings.flags & DOUBLE_BUFFER) || (settings.flags & DIRTY_RECTANGLE)) {
      BITMAP *scrap = create_video_bitmap(SCREEN_W, SCREEN_H);
      /* Create a scrap bitmap so that Allegro doesn't put the next video */
      /*  bitmap over the screen */

      scrnbuf = create_optimized_bitmap(SCREEN_W, SCREEN_H);

      if (scrap)
         destroy_bitmap(scrap);
   }
   if (settings.flags & DOUBLE_BUFFER) {
   } else if (settings.flags & PAGE_FLIP) {
      /* set up page flipping bitmaps */
      page1 = screen;           //create_video_bitmap(SCREEN_W, SCREEN_H);
      page2 = create_video_bitmap(SCREEN_W, SCREEN_H);
      if ((!page1) || (!page2)) {
         set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
         allegro_message("Not enough video memory for page flipping\n");
         exit(EXIT_FAILURE);
      }
   } else if (settings.flags & IMMEDIATE) {
      vscreen = screen;
   } else if (settings.flags & DIRTY_RECTANGLE) {
      /* Initialize dirty-rectangle stack */
      drect_count = 0;
      drect_stack = realloc(drect_stack, MAX_DRECT * sizeof(DRECT));
   } else if (settings.flags & TRIPLE_BUFFER) {
      /* set up triple buffered bitmaps */
      if (!(gfx_capabilities & GFX_CAN_TRIPLE_BUFFER))
         enable_triple_buffer();

      if (!(gfx_capabilities & GFX_CAN_TRIPLE_BUFFER)) {
         set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
         allegro_message
            ("Sorry... your hardware doesn't seem to support triple buffering.\n"
             "You may try to use another screen update method, or try a\n"
             "different driver.\n");
         exit(EXIT_FAILURE);
      }

      page1 = screen;
      page2 = create_video_bitmap(SCREEN_W, SCREEN_H);
      page3 = create_video_bitmap(SCREEN_W, SCREEN_H);

      if ((!page1) || (!page2) || (!page3)) {
         set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
         allegro_message("Not enough video memory for triple buffering\n");
         exit(EXIT_FAILURE);
      }
      clear_bitmap(page1);
      clear_bitmap(page2);
      clear_bitmap(page3);
   } else {
      set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
      allegro_message
         ("Sorry, that graphics update mode is not yet implemented\n");
      exit(EXIT_FAILURE);
   }

   if (settings.flags & IMMEDIATE) {
      arena = create_sub_bitmap(screen, arena_x, arena_y, arena_w, arena_h);
      interface =
         create_sub_bitmap(screen, interface_x, interface_y, interface_w,
                           interface_h);
   } else if (settings.flags & DOUBLE_BUFFER) {
      arena = create_sub_bitmap(scrnbuf, arena_x, arena_y, arena_w, arena_h);
      interface =
         create_sub_bitmap(scrnbuf, interface_x, interface_y, interface_w,
                           interface_h);
   } else if (settings.flags & PAGE_FLIP) {
   } else if (settings.flags & DIRTY_RECTANGLE) {
      arena = create_sub_bitmap(scrnbuf, arena_x, arena_y, arena_w, arena_h);
      interface =
         create_sub_bitmap(scrnbuf, interface_x, interface_y, interface_w,
                           interface_h);
      vscreen = scrnbuf;
   }
   show_mouse(NULL);

   switch (bitmap_color_depth(screen)) {
      case 15:
         makecolour = makecol15;
         break;
      case 16:
         makecolour = makecol16;
         break;
      case 24:
         makecolour = makecol24;
         break;
      case 32:
         makecolour = makecol32;
         break;
      default:
         makecolour = makecol8;
   }

   /* Initialize symbolic colour names */
   black = makecolour(0, 0, 0);
   grey = makecolour(192, 192, 192);
   darkgrey = makecolour(64, 64, 64);
   white = makecolour(255, 255, 255);
   yellow = makecolour(255, 255, 64);
   red = makecolour(224, 0, 0);
   blue = makecolour(0, 0, 224);
   green = makecolour(0, 224, 0);
   bright_red = makecolour(240, 40, 40);
   bright_blue = makecolour(40, 40, 240);
   darkgreen = makecolour(0, 128, 0);

   clear_bitmap(screen);
}

/* Prepares to update the screen. Actions depend on the update method */
void prepare_screen_update(void)
{
   TRACE("Preparing screen update.\n");
   if (settings.flags & IMMEDIATE) {
      scare_mouse();
   } else if (settings.flags & DOUBLE_BUFFER) {
      vscreen = scrnbuf;
   } else if (settings.flags & TRIPLE_BUFFER) {
      if (current_page == 0) {
         vscreen = page2;
         current_page = 1;
      } else if (current_page == 1) {
         vscreen = page3;
         current_page = 2;
      } else {
         vscreen = page1;
         current_page = 0;
      }
      screen = vscreen;
      arena = create_sub_bitmap(vscreen, arena_x, arena_y, arena_w, arena_h);
      interface =
         create_sub_bitmap(vscreen, interface_x, interface_y, interface_w,
                           interface_h);
   } else if (settings.flags & PAGE_FLIP) {
      if (current_page == 0) {
         vscreen = page2;
         current_page = 1;
      } else {
         vscreen = page1;
         current_page = 0;
      }
      arena = create_sub_bitmap(vscreen, arena_x, arena_y, arena_w, arena_h);
      interface =
         create_sub_bitmap(vscreen, interface_x, interface_y, interface_w,
                           interface_h);
      screen = vscreen;
   } else if (settings.flags & DIRTY_RECTANGLE) {
      //drect_count = 0;
   } else {
   }
   return;
}

/* Wrap things up after a screen update. Currently does nothing */
void screen_update_done(void)
{
   if (settings.flags & IMMEDIATE) {
      unscare_mouse();
   } else if (settings.flags & DOUBLE_BUFFER) {
      /* when double buffering, just copy the memory bitmap to the screen */
      scare_mouse();
      acquire_screen();
      blit(vscreen, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
      release_screen();
      unscare_mouse();
   } else if (settings.flags & DIRTY_RECTANGLE) {
      if (!drect_count)
         return;                // Nothing to do
      if (drect_count < 0) {    // Stack overflow
         scare_mouse();
         acquire_screen();
         blit(vscreen, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
         release_screen();
         unscare_mouse();
      } else {                  // Update of rectangles in the stack
         int n;

         /* First sort the list */
         if (!gfx_driver->linear)
            qsort(drect_stack, drect_count, sizeof(DRECT), rect_cmp);
         if (is_video_bitmap(vscreen))
            acquire_bitmap(vscreen);
         acquire_screen();
         for (n = 0; n < drect_count; n++) {
            /* Check for mouse overlap */
            scare_mouse_area(drect_stack[n].x, drect_stack[n].y,
                             drect_stack[n].w, drect_stack[n].h);

            blit(vscreen, screen, drect_stack[n].x, drect_stack[n].y,
                 drect_stack[n].x, drect_stack[n].y,
                 drect_stack[n].w, drect_stack[n].h);
            /* This is for debugging purposes */
            /*
               rect (screen, drect_stack[n].x, drect_stack[n].y,
               drect_stack[n].x+drect_stack[n].w, drect_stack[n].y+drect_stack[n].h,
               rand()%(1<<bitmap_color_depth(screen)));
               // */
            unscare_mouse();
         }
         release_screen();
         if (is_video_bitmap(vscreen))
            release_bitmap(vscreen);
      }
      drect_count = 0;
   } else if (settings.flags & TRIPLE_BUFFER) {
      /* make sure the last flip request has actually happened */
      do {
      } while (poll_scroll());

      /* post a request to display the page we just drew */
      show_mouse(vscreen);
      request_video_bitmap(vscreen);
      /* Clear the next screen page in line */
      if (current_page == 0) {
         clear_bitmap(page2);
      } else if (current_page == 1) {
         clear_bitmap(page3);
      } else {
         clear_bitmap(page1);
      }
      destroy_bitmap(arena);
      destroy_bitmap(interface);
   } else if (settings.flags & PAGE_FLIP) {
      /* for page flipping we scroll to display the image */

      show_mouse(vscreen);
      show_video_bitmap(vscreen);
      /* Clear the temporarily allocated bitmaps */
      destroy_bitmap(arena);
      destroy_bitmap(interface);
   }
   inc_frame_counter();
   /* Show FPS */
   /*
      text_mode (black);
      textprintf (get_monitor_bmp(), font, 0,0, white, "[fps %02d] ", get_fps());
      text_mode (-1);
   // */
   TRACE("Screen update completed.\n");
}

/***************************/
/* Miscelaneous functions  */
/***************************/

/* Do a screen shot */
void screenshot(void)
{
   BITMAP *bmp;
   PALETTE pal;
   char s[20] = "Screen00.pcx";
   int c;

   for (c = 1; (c <= 99) && exists(s); c++) {
      snprintf(s, 20, "Screen%02d.pcx", c);
   }

   get_palette(pal);
   //bmp = create_sub_bitmap(get_monitor_bmp(), 0, 0, SCREEN_W, SCREEN_H);
   bmp = create_bitmap(SCREEN_W, SCREEN_H);
   blit(get_monitor_bmp(), bmp, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
   save_bitmap(s, bmp, pal);
   destroy_bitmap(bmp);
}
