 /* 
  * UAE - The Un*x Amiga Emulator
  * 
  * SVGAlib interface.
  * 
  * (c) 1995 Bernd Schmidt
  */

#include "sysconfig.h"
#include "sysdeps.h"

#include <assert.h>
#include <ctype.h>
#include <signal.h>
#include <vga.h>
#include <vgamouse.h>
#include <vgakeyboard.h>

#include "config.h"
#include "options.h"
#include "memory.h"
#include "custom.h"
#include "newcpu.h"
#include "keyboard.h"
#include "xwin.h"
#include "keybuf.h"
#include "gui.h"

#define SCODE_CURSORBLOCKUP	103	/* Cursor key block. */
#define SCODE_CURSORBLOCKLEFT 105
#define SCODE_CURSORBLOCKRIGHT 106
#define SCODE_CURSORBLOCKDOWN 108

#define SCODE_INSERT 110
#define SCODE_HOME 102
#define SCODE_PGUP 104
#define SCODE_DELETE 111
#define SCODE_END 107
#define SCODE_PGDN 109

#define SCODE_KEYPAD0	82
#define SCODE_KEYPAD1	79
#define SCODE_KEYPAD2	80
#define SCODE_KEYPAD3	81
#define SCODE_KEYPAD4	75
#define SCODE_KEYPAD5	76
#define SCODE_KEYPAD6	77
#define SCODE_KEYPAD7	71
#define SCODE_KEYPAD8	72
#define SCODE_KEYPAD9	73
#define SCODE_KEYPADENTER	96
#define SCODE_KEYPADPLUS	78
#define SCODE_KEYPADMINUS	74

#define SCODE_Q		16
#define SCODE_W		17
#define SCODE_E		18
#define SCODE_R		19
#define SCODE_T		20
#define SCODE_Y		21
#define SCODE_U		22
#define SCODE_I		23
#define SCODE_O		24
#define SCODE_P		25

#define SCODE_A		30
#define SCODE_S		31
#define SCODE_D		32
#define SCODE_F		33
#define SCODE_G		34
#define SCODE_H		35
#define SCODE_J		36
#define SCODE_K		37
#define SCODE_L		38

#define SCODE_Z		44
#define SCODE_X		45
#define SCODE_C		46
#define SCODE_V		47
#define SCODE_B		48
#define SCODE_N		49
#define SCODE_M		50

#define SCODE_ESCAPE		1
#define SCODE_ENTER		28
#define SCODE_RIGHTCONTROL	97
#define SCODE_CONTROL	97
#define SCODE_RIGHTALT	100
#define SCODE_LEFTCONTROL	29
#define SCODE_LEFTALT	56
#define SCODE_SPACE		57

#define SCODE_F1		59
#define SCODE_F2		60
#define SCODE_F3		61
#define SCODE_F4		62
#define SCODE_F5		63
#define SCODE_F6		64
#define SCODE_F7		65
#define SCODE_F8		66
#define SCODE_F9		67
#define SCODE_F10		68

#define SCODE_0 11
#define SCODE_1 2
#define SCODE_2 3
#define SCODE_3 4
#define SCODE_4 5 
#define SCODE_5 6
#define SCODE_6 7 
#define SCODE_7 8
#define SCODE_8 9
#define SCODE_9 10

#define SCODE_LEFTSHIFT 42
#define SCODE_RIGHTSHIFT 54
#define SCODE_TAB 15

#define SCODE_F11 87
#define SCODE_F12 88
#define SCODE_NEXT 81
#define SCODE_PRIOR 73
#define SCODE_BS 14
/*
#define SCODE_asciicircum 1
*/
#define SCODE_bracketleft 26
#define SCODE_bracketright 27
#define SCODE_comma 51
#define SCODE_period 52
#define SCODE_slash 53
#define SCODE_semicolon 39
#define SCODE_grave 40
#define SCODE_minus 12
#define SCODE_equal 13
#define SCODE_numbersign 43
#define SCODE_ltgt 86
#define SCODE_scrolllock 70

#define SCODE_LWIN95 125
#define SCODE_RWIN95 126
#define SCODE_MWIN95 127

void setup_brkhandler(void)
{
}

static int vsize, bitdepth, bit_unit, using_linear;
static vga_modeinfo modeinfo;
static char *linear_mem = NULL;
static int need_dither;
static int x_size = 0, vidmode_linewidth;    

static UBYTE dither_buf[1000]; /* I hate having to think about array bounds */

xcolnr xcolors[4096];

struct vidbuf_description gfxvidinfo;

static int x_size_table[MAX_SCREEN_MODES+1] = { 320, 320, 320, 640, 800 };

static int vga_mode_table[MAX_SCREEN_MODES+1][MAX_COLOR_MODES+1] =
 { { G320x200x256, G320x200x32K, G320x200x64K, G320x200x256, G320x200x16, G320x200x16M32 },
   { G320x240x256, -1, -1, G320x240x256, -1, -1 },
   { G320x400x256, -1, -1, G320x400x256, -1, -1 },
   { G640x480x256, G640x480x32K, G640x480x64K, G640x480x256, G640x480x16, G640x480x16M32 },
   { G800x600x256, G800x600x32K, G800x600x64K, G800x600x256, G800x600x16, G800x600x16M32 } };

static int mode_bitdepth[MAX_COLOR_MODES+1][3] =
  { { 8, 8, 0 }, { 15, 16, 0 }, { 16, 16, 0 }, { 8, 8, 1 }, { 4, 8, 1 }, { 24, 32, 0 } };

void flush_line(int y)
{
    int target_y = y;

    if (screen_res < 2)
	target_y -= 8;
    if (linear_mem == NULL) {
	char *addr = gfxvidinfo.bufmem + y*gfxvidinfo.rowbytes;
	if (target_y < modeinfo.height && target_y >= 0) {
	    if (need_dither) {
		DitherLine(dither_buf, (UWORD *)addr, 0, y, x_size, bit_unit);
		addr = dither_buf;
	    } 
	    vga_drawscanline(target_y, addr);
	}
    } else {
	if (need_dither && target_y >= 0) {
	    char *addr = gfxvidinfo.bufmem + y*gfxvidinfo.rowbytes;
	    DitherLine(linear_mem + vidmode_linewidth * target_y, (UWORD *)addr, 0, y, x_size, bit_unit);
	}
    }
}

void flush_block(int a, int b)
{
    abort();
}

void flush_screen(int a, int b)
{
}

void calc_adjustment(void)
{
    switch (screen_res) {
     case 0: case 1: case 2: /* LoRes, 320xfoo */
	gfxvidinfo.x_adjust = prev_max_diwstop - 320;
	break;

     case 3: /* 640xbar */
	gfxvidinfo.x_adjust = prev_max_diwstop - 640;
	break;
     default:
	gfxvidinfo.x_adjust = 0;
	break;
    }
}

static int colors_allocated;

static int get_color(int r, int g, int b, xcolnr *cnp)
{
    if (colors_allocated == 256)
	return -1;
    *cnp = colors_allocated;
    vga_setpalette(colors_allocated, doMask(r, 6, 0), doMask(g, 6, 0), doMask(b, 6, 0));
    colors_allocated++;
    return 1;
}

static void init_colors(void)
{
    if (need_dither) {
	setup_dither(bitdepth, get_color);
    } else {
	int rw = 5, gw = 5, bw = 5;
	colors_allocated = 0;
	if (color_mode == 2) gw = 6;

	switch (gfxvidinfo.pixbytes) {
	 case 4:
	    alloc_colors64k(8, 8, 8, 16, 8, 0);
	    break;
	 case 2:
	    alloc_colors64k(rw, gw, bw, gw+bw, bw, 0);
	    break;
	 case 1:
	    alloc_colors256(get_color);
	    break;
	 default:
	    abort();
	}
    }
}

int buttonstate[3] = { 0, 0, 0 };
int lastmx, lastmy;
int newmousecounters = 0;

static int keystate[256];

static int scancode2amiga(int scancode)
{
    switch(scancode) {
     case SCODE_A: return AK_A;
     case SCODE_B: return AK_B;
     case SCODE_C: return AK_C;
     case SCODE_D: return AK_D;
     case SCODE_E: return AK_E;
     case SCODE_F: return AK_F;
     case SCODE_G: return AK_G;
     case SCODE_H: return AK_H;
     case SCODE_I: return AK_I;
     case SCODE_J: return AK_J;
     case SCODE_K: return AK_K;
     case SCODE_L: return AK_L;
     case SCODE_M: return AK_M;
     case SCODE_N: return AK_N;
     case SCODE_O: return AK_O;
     case SCODE_P: return AK_P;
     case SCODE_Q: return AK_Q;
     case SCODE_R: return AK_R;
     case SCODE_S: return AK_S;
     case SCODE_T: return AK_T;
     case SCODE_U: return AK_U;
     case SCODE_V: return AK_V;
     case SCODE_W: return AK_W;
     case SCODE_X: return AK_X;
     case SCODE_Y: return AK_Y;
     case SCODE_Z: return AK_Z;
	
     case SCODE_0: return AK_0;
     case SCODE_1: return AK_1;
     case SCODE_2: return AK_2;
     case SCODE_3: return AK_3;
     case SCODE_4: return AK_4;
     case SCODE_5: return AK_5;
     case SCODE_6: return AK_6;
     case SCODE_7: return AK_7;
     case SCODE_8: return AK_8;
     case SCODE_9: return AK_9;
	
     case SCODE_KEYPAD0: return AK_NP0;
     case SCODE_KEYPAD1: return AK_NP1;
     case SCODE_KEYPAD2: return AK_NP2;
     case SCODE_KEYPAD3: return AK_NP3;
     case SCODE_KEYPAD4: return AK_NP4;
     case SCODE_KEYPAD5: return AK_NP5;
     case SCODE_KEYPAD6: return AK_NP6;
     case SCODE_KEYPAD7: return AK_NP7;
     case SCODE_KEYPAD8: return AK_NP8;
     case SCODE_KEYPAD9: return AK_NP9;
	
     case SCODE_F1: return AK_F1;
     case SCODE_F2: return AK_F2;
     case SCODE_F3: return AK_F3;
     case SCODE_F4: return AK_F4;
     case SCODE_F5: return AK_F5;
     case SCODE_F6: return AK_F6;
     case SCODE_F7: return AK_F7;
     case SCODE_F8: return AK_F8;
     case SCODE_F9: return AK_F9;
     case SCODE_F10: return AK_F10;
	
     case SCODE_BS: return AK_BS;
     case SCODE_LEFTCONTROL: return AK_CTRL;
     case SCODE_RIGHTCONTROL: return AK_CTRL;
     case SCODE_TAB: return AK_TAB;
     case SCODE_LEFTALT: return AK_LALT;
     case SCODE_RIGHTALT: return AK_RALT;
     case SCODE_ENTER: return AK_RET;
     case SCODE_SPACE: return AK_SPC;
     case SCODE_LEFTSHIFT: return AK_LSH;
     case SCODE_RIGHTSHIFT: return AK_RSH;
     case SCODE_ESCAPE: return AK_ESC;
	
     case SCODE_INSERT:
     case SCODE_END:	
     case SCODE_HOME: break;

     case SCODE_DELETE: return AK_DEL;
     case SCODE_CURSORBLOCKUP: return AK_UP;
     case SCODE_CURSORBLOCKDOWN: return AK_DN;
     case SCODE_CURSORBLOCKLEFT: return AK_LF;
     case SCODE_CURSORBLOCKRIGHT: return AK_RT;
	
     case SCODE_F11: return AK_BACKSLASH;
/*
     case SCODE_asciicircum: return AK_00;
 */
     case SCODE_bracketleft: return AK_LBRACKET;
     case SCODE_bracketright: return AK_RBRACKET;
     case SCODE_comma: return AK_COMMA;
     case SCODE_period: return AK_PERIOD;
     case SCODE_slash: return AK_SLASH;
     case SCODE_semicolon: return AK_SEMICOLON;
     case SCODE_grave: return AK_QUOTE;
     case SCODE_minus: return AK_MINUS;
     case SCODE_equal: return AK_EQUAL;
	
	/* This one turns off screen updates. */
     case SCODE_scrolllock: return AK_inhibit;

     case SCODE_PGUP: case SCODE_RWIN95: return AK_RAMI;
     case SCODE_PGDN: case SCODE_LWIN95: return AK_LAMI; 
	
/*#ifdef KBD_LANG_DE*/
     case SCODE_numbersign: return AK_NUMBERSIGN;
     case SCODE_ltgt: return AK_LTGT;
/*#endif*/
    }
    return -1;
}

static void my_kbd_handler(int scancode, int newstate)
{
    int akey = scancode2amiga(scancode);
    
    assert(scancode >= 0 && scancode < 0x100);
    if (scancode == SCODE_F12) {
    	regs.spcflags |= SPCFLAG_BRK;
	quit_program = 1;
    }
    if (keystate[scancode] == newstate)
    	return;
    keystate[scancode] = newstate;

    if (akey == -1) {
    	return;
    }

    if (newstate == KEY_EVENTPRESS) {
	if (akey == AK_inhibit)
	    inhibit_frame ^= 1;
	else
	    record_key (akey << 1);
    } else
    	record_key ((akey << 1) | 1);
    
    /* "Affengriff" */
    if(keystate[AK_CTRL] && keystate[AK_LAMI] && keystate[AK_RAMI])
    	m68k_reset();
}

int graphics_init(void)
{
    int i;
    int vgamode;
    
    need_dither = 0;
    using_linear = 0;

    if (screen_res < 3)
	correct_aspect = 0;
    
    vgamode = vga_mode_table[screen_res][color_mode];
    if (vgamode == -1) {
	fprintf(stderr, "Sorry, this combination of color and video mode is not supported.\n");
	return 0;
    }
    
    bitdepth = mode_bitdepth[color_mode][0];
    bit_unit = mode_bitdepth[color_mode][1];
    need_dither = mode_bitdepth[color_mode][2];
    x_size = x_size_table[screen_res];
    
    vga_init();
    modeinfo = *vga_getmodeinfo (vgamode);
    
    vidmode_linewidth = modeinfo.linewidth;
    gfxvidinfo.pixbytes = modeinfo.bytesperpixel;
    if (!need_dither) {
	if (modeinfo.bytesperpixel == 0) {
	    printf("Got a bogus value from SVGAlib... %s.\n",
		   screen_res == 1 || screen_res == 2 ? "trying to fix it" : "giving up");
	    if (screen_res != 1 && screen_res != 2)
		return 0;
	    gfxvidinfo.pixbytes = 1;
	}
    } else {
	gfxvidinfo.pixbytes = 2;
    }
    
    if (vga_setmode(vgamode) < 0) {
	sleep(1);
	vga_setmode(TEXT);
	fprintf(stderr, "SVGAlib doesn't like my video mode. Giving up.\n");
	return 0;
    }

    if ((modeinfo.flags & CAPABLE_LINEAR) && !no_xhair) {
	if (vga_setlinearaddressing() != -1) {
	    linear_mem = (char *)vga_getgraphmem();
	    printf("Using linear addressing: %p.\n", linear_mem);
	    using_linear = !need_dither;
	}
    }
    
    vsize = correct_aspect ? 2*numscrlines : numscrlines;
    gfxvidinfo.maxblocklines = 0;

    if (using_linear) {
	gfxvidinfo.bufmem = linear_mem;
	gfxvidinfo.rowbytes = modeinfo.linewidth;
    } else {
	gfxvidinfo.rowbytes = x_size * gfxvidinfo.pixbytes;
	gfxvidinfo.bufmem = malloc(gfxvidinfo.rowbytes * vsize);
	memset(gfxvidinfo.bufmem, 0, gfxvidinfo.rowbytes * vsize);
    }
    
    gfxvidinfo.maxlinetoscr = x_size < 800 ? x_size : 0;
    gfxvidinfo.x_adjust = 0;
    gfxvidinfo.maxline = modeinfo.height;

    init_colors();
    vga_setmousesupport(1);
    mouse_init("/dev/mouse",vga_getmousetype(),10);
    if (keyboard_init() != 0)
    	abort();
    keyboard_seteventhandler(my_kbd_handler);
    keyboard_translatekeys(DONT_CATCH_CTRLC);
    
    buttonstate[0] = buttonstate[1] = buttonstate[2] = 0;
    for(i = 0; i < 256; i++) 
	keystate[i] = 0;

    lastmx = lastmy = 0;
    newmousecounters = 0;

    mouse_setxrange(-1000,1000);
    mouse_setyrange(-1000,1000);
    mouse_setposition(0,0);
    
    return 1;
}

void graphics_leave(void)
{
    sleep(1); /* Maybe this will fix the "screen full of garbage" problem */
    vga_setmode(TEXT);
    keyboard_close();
    dumpcustom();
}

void handle_events(void)
{
    int button = mouse_getbutton();
    
    keyboard_update();
    mouse_update();
    lastmx += mouse_getx();
    lastmy += mouse_gety();
    mouse_setposition(0,0);

    buttonstate[0] = button & 4;
    buttonstate[1] = button & 2;
    buttonstate[2] = button & 1;
}

int debuggable(void)
{
    return 0;
}

int needmousehack(void)
{
    return 0;
}

void LED(int on)
{
}

void target_specific_usage(void)
{
    printf("  -S n                     : Sound emulation accuracy (n = 0, 1, 2 or 3)\n"
	   "                             For sound emulation, n = 2 is recommended\n");
    printf("  -b n                     : Use n bits for sound output (8 or 16)\n");
    printf("  -R n                     : Use n Hz to output sound. Common values are\n"
	   "                             22050 Hz or 44100 Hz\n");
    printf("  -B n                     : Use a sound buffer of n bytes (use small\n"
	   "                             values on fast machines)\n");
    printf("  -x                       : Don't use SVGAlib linear framebuffer, even if available.\n");
    printf("  -p command               : Use command to pipe printer output to.\n");
    printf("  -I device                : Name of the used serial device (i.e. /dev/ttyS1\n");
}
