/* Copyright (C) 1992 Aladdin Enterprises.  All rights reserved.
   Distributed by Free Software Foundation, Inc.

This file is part of Ghostscript.

Ghostscript is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
to anyone for the consequences of using it or for whether it serves any
particular purpose or works at all, unless he says so in writing.  Refer
to the Ghostscript General Public License for full details.

Everyone is granted permission to copy, modify and redistribute
Ghostscript, but only under the conditions described in the Ghostscript
General Public License.  A copy of this license is supposed to have been
given to you along with Ghostscript so you can know your rights and
responsibilities.  It should be in a file named COPYING.  Among other
things, the copyright notice and this notice must be preserved on all
copies.  */

/*
 * gdevlinux.c
 *
 * This is a driver for 386 PCs using VGALIB for graphics on the console 
 * display.
 *
 * Written by Sigfrid Lundberg, siglun@euler.teorekol.lu.se.
 *
 * Support for all the SVGAlib video modes, MAGSTEP, XO, YO, and additions
 * to the gs.1 man page by Patrick Volkerding, volkerdi@mhd1.moorhead.msus.edu.
 *
 */

#include "gx.h"
#include "gxdevice.h"
#include "gserrors.h"

#include <errno.h>
#include <vga.h>

extern char svgalib_detected;
extern char video_resolution[25];
extern float size_multiplier; 
extern float adjust_xo;
extern float adjust_yo;
extern char xo_units;
extern char yo_units;

int screen_width, screen_height;
int xorigin, yorigin;

typedef struct gx_device_linux {
    gx_device_common;
    int fd;                    /* window file descriptor */
    uchar *screen;             /* pointer to screen image */
    ushort line_size;          /* size of single screen line in bytes */
    ulong screen_size;         /* size of screen image in bytes */
    int screen_width, screen_height; /* size of screen used */
    int page;                  /* page number */
#ifdef LINUX_PERF
    char *no_output, *no_fill, *no_copy;
#endif
} gx_device_linux;
#define linuxdev ((gx_device_linux *)dev)

#define XDPI   60              /* to get a more-or-less square aspect ratio */
#define YDPI   60

#ifndef A4 /*Letter size*/
#define YSIZE (20.0 * YDPI / 2.5))
#define XSIZE (8.5 / 11)*YSIZE /* 8.5 x 11 inch page, by default */
#else  /* A4 paper */
#define XSIZE 8.27
#define YSIZE 11.69 
#endif

dev_proc_open_device(linux_open);
dev_proc_close_device(linux_close);
dev_proc_draw_line(linux_draw_line);
dev_proc_fill_rectangle(linux_fill_rectangle);
dev_proc_tile_rectangle(linux_tile_rectangle);
dev_proc_map_color_rgb(linux_map_color_rgb);
dev_proc_map_rgb_color(linux_map_rgb_color);
dev_proc_copy_mono(linux_copy_mono);
dev_proc_copy_color(linux_copy_color);



private gx_device_procs linux_procs = {
    linux_open,
    gx_default_get_initial_matrix,
    gx_default_sync_output,
    gx_default_output_page,
    linux_close,
    linux_map_rgb_color,
    linux_map_color_rgb,
    linux_fill_rectangle,
    linux_tile_rectangle,
    linux_copy_mono,
    linux_copy_color,
    linux_draw_line,
    gx_default_get_bits,
    gx_default_get_props,
    gx_default_put_props
};

gx_device_linux gs_linux_device = {
    sizeof(gx_device_linux),
    &linux_procs,
    "linux",
    0,0,
    1,1,
    no_margins,
    dci_black_and_white,
    0
};

int
linux_open(gx_device *dev)
{
  if (!strcmp(video_resolution,"320x200x16") || !strcmp(video_resolution,"1")) {
    vga_setmode(1);
  } else if (!strcmp(video_resolution,"640x200x16") || !strcmp(video_resolution,"2")) {
    vga_setmode(2);
  } else if (!strcmp(video_resolution,"640x350x16") || !strcmp(video_resolution,"3")) {
    vga_setmode(3);
  } else if (!strcmp(video_resolution,"640x480x16") || !strcmp(video_resolution,"4")) {
    vga_setmode(4);
  } else if (!strcmp(video_resolution,"320x200x256") || !strcmp(video_resolution,"5")) {
    vga_setmode(5);
  } else if (!strcmp(video_resolution,"320x240x256") || !strcmp(video_resolution,"6")) {
    vga_setmode(6);
  } else if (!strcmp(video_resolution,"320x400x256") || !strcmp(video_resolution,"7")) {
    vga_setmode(7);
  } else if (!strcmp(video_resolution,"360x480x256") || !strcmp(video_resolution,"8")) {
    vga_setmode(8);
  } else if (!strcmp(video_resolution,"640x480x2") || !strcmp(video_resolution,"9")) {
    vga_setmode(9);
  } else if (!strcmp(video_resolution,"640x480x256") || !strcmp(video_resolution,"10")) {
    vga_setmode(10);
  } else if (!strcmp(video_resolution,"800x600x256") || !strcmp(video_resolution, "11")) {
    vga_setmode(11);
  } else if (!strcmp(video_resolution,"1024x768x256") || !strcmp(video_resolution,"12")) {
    vga_setmode(12);
  } else if (!strcmp(video_resolution,"320x200x32K") || !strcmp(video_resolution,"13")) {
    vga_setmode(13);
  } else if (!strcmp(video_resolution,"320x200x64K") || !strcmp(video_resolution,"14")) {
    vga_setmode(14);
  } else if (!strcmp(video_resolution,"320x200x16M") || !strcmp(video_resolution,"15")) {
    vga_setmode(15);
  } else if (!strcmp(video_resolution,"640x480x32K") || !strcmp(video_resolution,"16")) {
    vga_setmode(16);
  } else if (!strcmp(video_resolution,"640x480x64K") || !strcmp(video_resolution,"17")) {
    vga_setmode(17);
  } else if (!strcmp(video_resolution,"640x480x16M") || !strcmp(video_resolution,"18")) {
    vga_setmode(18);
  } else if (!strcmp(video_resolution,"800x600x32K") || !strcmp(video_resolution,"19")) {
    vga_setmode(19);
  } else if (!strcmp(video_resolution,"800x600x64K") || !strcmp(video_resolution,"20")) {
    vga_setmode(20);
  } else if (!strcmp(video_resolution,"800x600x16M") || !strcmp(video_resolution,"21")) {
    vga_setmode(21);
  } else if (!strcmp(video_resolution,"1024x768x32K") || !strcmp(video_resolution,"22")) {
    vga_setmode(22);
  } else if (!strcmp(video_resolution,"1024x768x64K") || !strcmp(video_resolution,"23")) {
    vga_setmode(23);
  } else if (!strcmp(video_resolution,"1024x768x16M") || !strcmp(video_resolution,"24")) {
    vga_setmode(24);
  } else if (!strcmp(video_resolution,"1280x1024x256") || !strcmp(video_resolution,"25")) {
    vga_setmode(25);
  } else if (!strcmp(video_resolution,"1280x1024x32K") || !strcmp(video_resolution,"26")) {
    vga_setmode(26);
  } else if (!strcmp(video_resolution,"1280x1024x64K") || !strcmp(video_resolution,"27")) {
    vga_setmode(27);
  } else if (!strcmp(video_resolution,"1280x1024x16M") || !strcmp(video_resolution,"28")) {
    vga_setmode(28);
  } else if (!strcmp(video_resolution,"720x350x16") || !strcmp(video_resolution,"29")) {
    vga_setmode(29);
  } else if (!strcmp(video_resolution,"720x480x16") || !strcmp(video_resolution,"30")) {
    vga_setmode(30);
  } else {
    fprintf(stdout,"Sorry, but %s is not a recognized SVGAlib mode.\n\n",video_resolution);
    fprintf(stdout,"These are the recognized video modes, (and mode numbers):\n");
    fprintf(stdout," 320x200x16 (1), 640x200x16 (2), 640x350x16 (3), 640x480x16 (4), \n");
    fprintf(stdout," 320x200x256 (5), 320x240x256 (6), 320x400x256 (7), 360x480x256 (8), \n");
    fprintf(stdout," 640x480x2 (9), 640x480x256 (10), 800x600x256 (11), 1024x768x256 (12), \n");
    fprintf(stdout," 320x200x32K (13), 320x200x64K (14), 320x200x16M (15), 640x480x32K (16), \n");
    fprintf(stdout," 640x480x64K (17), 640x480x16M (18), 800x600x32K (19), 800x600x64K (20), \n");
    fprintf(stdout," 800x600x16M (21), 1024x768x32K (22), 1024x768x64K (23), 1024x768x16M (24), \n");
    fprintf(stdout," 1280x1024x256 (25), 1280x1024x32K (26), 1280x1024x64K (27), \n");
    fprintf(stdout," 1280x1024x16M (28), 720x350x16 (29), 720x480x16 (30), \n");
    fprintf(stdout,"\n");
    fprintf(stdout,"In addition to -r<resolution>, these flags also work with -sDEVICE=linux:\n");
    fprintf(stdout,"\n");
    fprintf(stdout,"  -dMAGSTEP=<value> : Change the image size. Values 0.0 < value < 1.0 shrink\n");
    fprintf(stdout,"       the image size, while values > 1.0 expand it. The default value is 1.0.\n");
    fprintf(stdout,"\n");
    fprintf(stdout,"  -dXO=<value>[units], -dYO=<value>[units] : Specify a new origin. The upper-\n");
    fprintf(stdout,"       left hand corner of the image is considered to be (0,0). You may move\n");
    fprintf(stdout,"       the origin down and/or right using these flags. The default unit of \n");
    fprintf(stdout,"       measurement is inches (in), but you may also use centimeters (cm), or\n");
    fprintf(stdout,"       percent (%%) of distance from the origin (0%% - 100%%).\n");
    exit(1);
  }
  fprintf(stdout,"\nSelected SVGAlib resolution: %s\n",video_resolution);
  vga_clear();
  if ( dev->width == 0 )
    dev->width = vga_getxdim() * size_multiplier; /* + 1; */
    screen_width = vga_getxdim();
  if ( dev->height == 0 )
    dev->height = vga_getydim() * size_multiplier; /* + 1; */
    screen_height = vga_getydim();

  /*vgalib provides no facilities for finding out aspect ratios*/
  if ( dev->y_pixels_per_inch == 1 ) {
    dev->y_pixels_per_inch = dev->height / 11.0;
    dev->x_pixels_per_inch = dev->y_pixels_per_inch;
    if (xo_units == 'c') adjust_xo /= 2.58;
    if (yo_units == 'c') adjust_yo /= 2.58;
    if (xo_units == '%') {
      if (adjust_xo < 0) adjust_xo = 0;
      if (adjust_xo > 100) adjust_xo = 100;
      adjust_xo = 8.5 * (adjust_xo/100);
    }
    if (yo_units == '%') {
      if (adjust_yo < 0) adjust_yo = 0;
      if (adjust_yo > 100) adjust_yo = 100;
      adjust_yo = 11 * (adjust_yo/100);
    }
    xorigin = dev->x_pixels_per_inch * adjust_xo;
    yorigin = dev->y_pixels_per_inch * adjust_yo;
  }

  /* Find out if the device supports color */
  /* (default initialization is monochrome). */
  /* We only recognize 16-color devices right now. */
  if ( vga_getcolors() > 1 ) {
   
    int index,one,rgb[3];

    static gx_device_color_info linux_16color = dci_color(4, 2, 3);
    dev->color_info = linux_16color;  

    for(index=0;index<gx_max_color_value;index++) {
      one = (index & 8 ? gx_max_color_value : gx_max_color_value / 3);
      rgb[0] = (index & 4 ? one : 0);
      rgb[1] = (index & 2 ? one : 0);
      rgb[2] = (index & 1 ? one : 0);
      vga_setpalette((int)index, rgb[0], rgb[1], rgb[2]);
    }
  }

  return 0;
}

int
linux_close(gx_device *dev)
{
  vga_getch();
  vga_setmode(TEXT);
  return 0;
}

gx_color_index
linux_map_rgb_color(gx_device *dev, gx_color_value red, gx_color_value green,
                   gx_color_value blue)
{
  int index;

  index=((red > gx_max_color_value / 4 ? 4 : 0) +
        (green > gx_max_color_value / 4 ? 2 : 0) +
        (blue > gx_max_color_value / 4 ? 1 : 0) +
        (red > gx_max_color_value / 4 * 3 ||
         green > gx_max_color_value / 4 * 3 ? 8 : 0));

  return (gx_color_index)index;
}


/* I actually don't understand what I'm doing -- the only thing I want to
achieve are palettes that emulates BGI */
int
linux_map_color_rgb(gx_device *dev, gx_color_index index, 
                   unsigned short rgb[3])
{  
  int red, green, blue;
  gx_color_value one = 
    (index & 8 ? gx_max_color_value : gx_max_color_value / 3);
  rgb[0] = (index & 4 ? one : 0);
  rgb[1] = (index & 2 ? one : 0);
  rgb[2] = (index & 1 ? one : 0);

  return 0;
}

void
adj_vga_drawpixel(int x, int y)
{
  y -= yorigin;
  x -= xorigin;
  if (x < 0 || x >= screen_width || y < 0 || y >= screen_height) return;
  vga_drawpixel(x,y);
}

void
adj_vga_drawline(int x1, int y1, int x2, int y2 )
{
  y1 -= yorigin;
  y2 -= yorigin;
  x1 -= xorigin;
  x2 -= xorigin;
  if (x1 < 0 || x1 >= screen_width || y1 < 0 || y1 >= screen_height) return;
  if (x2 < 0 || x2 >= screen_width || y2 < 0 || y2 >= screen_height) return;
  vga_drawline(x1,y1,x2,y2);
}

int
linux_draw_line(gx_device *dev, int x0, int y0, int x1, int y1, 
               gx_color_index color)
{  
  if(!((x0==x1)&&(y0==y1))) {
    vga_setcolor((int)color);
    adj_vga_drawline(x0,y0,x1,y1);
  }
  return 0;
}

int
linux_tile_rectangle(gx_device *dev, const gx_bitmap *tile,
  int x, int y, int w, int h, gx_color_index czero, gx_color_index cone,
  int px, int py)
{
  if ( czero != gx_no_color_index && cone != gx_no_color_index ) {
    linux_fill_rectangle(dev, x, y, w, h, czero);
    czero = gx_no_color_index;
  }
  return gx_default_tile_rectangle(dev, tile, x, y, w, h, czero, cone, px, py);
}


int
linux_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
                      gx_color_index color)
{
  int i,j;
  fit_fill(dev, x, y, w, h);
  vga_setcolor((int)color);
  for(i=y;i<y+h;i++)
    for(j=x;j<x+w;j++)
      adj_vga_drawpixel(j,i);
  return 0;
}

int
linux_copy_mono(gx_device *dev,
  const byte *base, int sourcex, int raster, gx_bitmap_id id,
  int x, int y, int width, int height,
  gx_color_index zero, gx_color_index one)

{
  const byte *ptr_line = base + (sourcex >> 3);
  int left_bit = 0x80 >> (sourcex & 7);
  int dest_y = y, end_x = x + width;
  int invert = 0;
  int color;
  int i;

  fit_copy(dev, base, sourcex, raster, id, x, y, width, height);

  if ( zero == gx_no_color_index ) {
    if ( one == gx_no_color_index ) return 0;
    color = (int)one;
  } else {
    if ( one == gx_no_color_index ) {
      color = (int)zero;
      invert = -1;
    } else {   /* Pre-clear the rectangle to zero */
      vga_setcolor(zero);
      linux_fill_rectangle(dev,x,y,width,height,zero);
      color = (int)one;
    }
  }

  vga_setcolor(color);
  
  while( height-- ) { /* for each line */ 
    const byte *ptr_source = ptr_line;
    register int dest_x = x;
    register int bit = left_bit;
    while ( dest_x < end_x ) { /* for each bit in the line */
      if ( (*ptr_source ^ invert)  & bit ) 
      adj_vga_drawpixel(dest_x,dest_y);
      dest_x++;
      if ( (bit >>= 1) == 0 )
       bit = 0x80, ptr_source++;
    }
    dest_y++;
    ptr_line += raster;
  }  
  return 0;
}


/* Copy a color pixel map.  This is just like a bitmap, except that */
/* each pixel takes 4 bits instead of 1 when device driver has color. */
int
linux_copy_color(gx_device *dev,
  const byte *base, int sourcex, int raster, gx_bitmap_id id,
  int x, int y, int width, int height)
{

  fit_copy(dev, base, sourcex, raster, id, x, y, width, height);

  if ( gx_device_has_color(dev) ) { /* color device, four bits per pixel */
    const byte *line = base + (sourcex >> 1);
    int dest_y = y, end_x = x + width, i;

    if ( width <= 0 ) return 0;
    while ( height-- )  { /* for each line */
      const byte *source = line;
      register int dest_x = x;
      if ( sourcex & 1 ) {    /* odd nibble first */
       int color =  *source++ & 0xf;
       vga_setcolor(color);
       adj_vga_drawpixel(dest_x,dest_y);
       dest_x++;
      }
      /* Now do full bytes */
      while ( dest_x < end_x ) {
       int color = *source >> 4;
       vga_setcolor(color);
       adj_vga_drawpixel(dest_x,dest_y);
       dest_x++;
       if ( dest_x < end_x ) {
         color =  *source++ & 0xf;
         vga_setcolor(color);
         adj_vga_drawpixel(dest_x,dest_y);
         dest_x++;
       }
      }
      dest_y++;
      line += raster;
    }
  } else { /* monochrome device: one bit per pixel */
    /* bitmap is the same as bgi_copy_mono: one bit per pixel */
    linux_copy_mono(dev, base, sourcex, raster, id, x, y, width, height,
                 (gx_color_index)0, (gx_color_index)7);
    }
  return 0;
}







