 /* 
  * UAE - The Un*x Amiga Emulator
  * 
  * X interface
  * 
  * FIXME: The #ifdef density is getting a little high here...
  * Copyright 1995, 1996 Bernd Schmidt
  * Copyright 1996 Ed Hanway, Andre Beck, Samuel Devulder, Bruno Coste
  */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/cursorfont.h>

#ifndef DONT_WANT_SHM
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/extensions/XShm.h>
#endif

#include "config.h"
#include "amiga.h"
#include "options.h"
#include "memory.h"
#include "custom.h"
#include "newcpu.h"
#include "xwin.h"
#include "keyboard.h"
#include "keybuf.h"
#include "gui.h"
#ifdef LINUX_SVGALIB
#error Compiling xwin.c, but LINUX_SVGALIB set. Re-edit "config.h".
#endif

static Display *display;
static int screen;
static Window rootwin, mywin;

static GC whitegc,blackgc;
static XColor black,white;
static Colormap cmap;

static XImage *img;
static Visual *vis;
static XVisualInfo visualInfo;
static int bitdepth;
#ifndef DONT_WANT_SHM
static XShmSegmentInfo shminfo;
#endif
static Cursor blankCursor, xhairCursor;
static bool cursorOn;
 
static char pixel_buffer[3200];
#ifdef LOW_BANDWIDTH
static char oldxlinebuffer[2*(313-29)][3200];
#endif

static char *xlinestart;
char *xlinebuffer;
long int xcolors[4096];

 /* Keyboard and mouse */

static bool keystate[256];

bool buttonstate[3];
int lastmx, lastmy;
bool newmousecounters;
static bool inwindow;
const long int eventmask = (KeyPressMask|KeyReleaseMask|ButtonPressMask
			    |ButtonReleaseMask|PointerMotionMask
			    |FocusChangeMask|EnterWindowMask
			    |LeaveWindowMask);

static int vscale;
static bool next_line_double;
static int next_scanline = 0;

static int scrfirstline, scrlastline;
static int prev_scrfirstline, prev_scrlastline;
static xcolnr scrbgcol;

#ifndef INLINE_DRAWPIXEL
void (*DrawPixel)(int, xcolnr);

static void DrawPixel16(int x, xcolnr col)
{
    *(short *)xlinebuffer = col;
    xlinebuffer += 2;
}

static void DrawPixel32(int x, xcolnr col)
{
    *(long *)xlinebuffer = col;
    xlinebuffer += 4;
}

static void DrawPixel8(int x, xcolnr col)
{
    *(char *)xlinebuffer = col;
    xlinebuffer += 1;
}

static void DrawPixelGeneric(int x, xcolnr col)
{
#ifdef DONT_WANT_SHM
    XPutPixel(img, x, 0, col);
#else
    XPutPixel(img, x, next_scanline, col);
    if (next_line_double) {
	XPutPixel(img, x, next_scanline+1, col);
    }
#endif
}
#endif /* INLINE_DRAWPIXEL */

void prepare_line (int y, bool need_double)
{
    next_line_double = need_double;
    next_scanline = y;
    
#ifdef DONT_WANT_SHM
    xlinebuffer = pixel_buffer;
#else
    xlinestart = xlinebuffer = img->data + y * img->bytes_per_line;
#endif
}

void flush_line(void)
{
    int xs = 0, xe = 795;
    int len;
    
    if (scrfirstline == -1)
	scrfirstline = next_scanline;
    scrlastline = next_scanline + 1 + (next_line_double != 0);
    {    
#ifdef LOW_BANDWIDTH
#ifdef X_16BIT_SCREEN
	short *newp = (short *)pixel_buffer, *oldp = (short *)oldxlinebuffer[next_scanline];
	int byte_scale = 2;
#endif
#ifdef X_8BIT_SCREEN
	char *newp = (char *)pixel_buffer, *oldp = (char *)oldxlinebuffer[next_scanline];
	int byte_scale = 1;
#endif
	while (xs < xe && newp[xs] == oldp[xs])
	    xs++;
	while (xs < xe && newp[xe] == oldp[xe])
	    xe--;
	if (xe == xs && newp[xe] == oldp[xe])
	    return;
	
	memcpy (oldp + xs, newp + xs, byte_scale * (xe - xs + 1));
#endif /* LOW_BANDWIDTH */
    }
    len = xe - xs + 1;
    
#ifdef DONT_WANT_SHM
    XPutImage(display, mywin, blackgc, img, xs, 0, xs, next_scanline, len, 1);
    if (next_line_double)
    	XPutImage(display, mywin, blackgc, img, xs, 0, xs, next_scanline+1, len, 1);
#else
    if (use_fast_draw && next_line_double) {	
    	memcpy (xlinestart + img->bytes_per_line, xlinestart, img->bytes_per_line);
	next_scanline++; /* For flush_screen(). */
    }
#endif
}

void flush_screen (xcolnr bgcol)
{
    int i, ystart, ystop;
    if (scrfirstline == -1) 
	return;
    
    if (scrbgcol != bgcol) {
	prev_scrfirstline = 0;
	prev_scrlastline = vscale*(313-29);
    }
    scrbgcol = bgcol;
    if (prev_scrfirstline < scrfirstline || prev_scrlastline > scrlastline) {
	char *oldxlb = xlinebuffer;
	xlinebuffer = pixel_buffer;
	for (i = 0; i < 796; i++)
	    DrawPixel(i, bgcol);
	xlinebuffer = oldxlb;
    }
#ifdef DONT_WANT_SHM
    for (i = prev_scrfirstline; i < scrfirstline; i++)
	XPutImage(display, mywin, blackgc, img, 0, 0, 0, i, 796, 1);
    for (i = scrlastline; i < prev_scrlastline; i++)
	XPutImage(display, mywin, blackgc, img, 0, 0, 0, i, 796, 1);
#else
    for (i = prev_scrfirstline; i < scrfirstline; i++)
	memcpy (img->data + i*img->bytes_per_line, pixel_buffer,
		img->bytes_per_line);
    for (i = scrlastline; i < prev_scrlastline; i++)
	memcpy (img->data + i*img->bytes_per_line, pixel_buffer,
		img->bytes_per_line);
#endif
    ystart = scrfirstline < prev_scrfirstline ? scrfirstline : prev_scrfirstline;
    ystop = scrlastline > prev_scrlastline ? scrlastline : prev_scrlastline;
#ifndef DONT_WANT_SHM
    XShmPutImage(display, mywin, blackgc, img, 0, ystart, 0, ystart, 796,
		 ystop-ystart, 0);
    XSync(display, 0);
#endif
    prev_scrfirstline = scrfirstline;
    prev_scrlastline = scrlastline;
    
    scrfirstline = scrlastline = -1;
}

#ifndef DONT_WANT_SHM
static void initShm(int depth)
{
    int scale = dont_want_aspect ? 1 : 2;
    img = XShmCreateImage(display, vis, depth,
			  ZPixmap, 0, &shminfo, 800, (313-29) * scale);
    
    shminfo.shmid = shmget(IPC_PRIVATE, 512*1024*scale, IPC_CREAT|0777);
    shminfo.shmaddr = img->data = (char *)shmat(shminfo.shmid, 0, 0);
    shminfo.readOnly = False;
    /* let the xserver attach */
    XShmAttach(display, &shminfo);
    /* NOW ! */
    XSync(display,0);
    /* now deleting means making it temporary */
    shmctl(shminfo.shmid, IPC_RMID, 0);
    
    xlinestart = xlinebuffer = img->data;
}
#endif

static __inline__ int bitsInMask(unsigned long mask)
{
    /* count bits in mask */
    int n = 0;
    while(mask) {
	n += mask&1;
	mask >>= 1;
    }
    return n;
}

static __inline__ int maskShift(unsigned long mask)
{
    /* determine how far mask is shifted */
    int n = 0;
    while(!(mask&1)) {
	n++;
	mask >>= 1;
    }
    return n;
}

static __inline__ unsigned long doMask(int p, int bits, int shift)
{
    /* p is a value from 0 to 15 (Amiga color value)
     * scale to 0..255, shift to align msb with mask, and apply mask */

    unsigned long val = p * 0x11111111UL;
    val >>= (32 - bits);
    val <<= shift;

    return val;
}

static int init_colors(void)
{
#ifdef INLINE_DRAWPIXEL

#ifdef X_8BIT_SCREEN
    if (visualInfo.depth != 8) {	
    	fprintf(stderr, "Unsupported bit depth (%d)\n", visualInfo.depth);
	return 0;
    }
#endif

#ifdef X_16BIT_SCREEN
    if (visualInfo.depth != 12 && visualInfo.depth != 16) {
    	fprintf(stderr, "Unsupported bit depth (%d)\n", visualInfo.depth);
	return 0;
    }
#endif

#else /* not INLINE_DRAWPIXEL */
    DrawPixel = DrawPixelGeneric;

    if(use_fast_draw) {
	/* this is probably a lot faster but makes nonportable assumptions
	 * about image format */
	
	switch(visualInfo.depth) {
	 case 12:
	    DrawPixel = DrawPixel16;
	    break;
	    
	 case 24:
	    DrawPixel = DrawPixel32;
	    break;

	 case 16:
	    DrawPixel = DrawPixel16;
	    break;

	 case 8:
	    DrawPixel = DrawPixel8;
	    break;

	 default:
	    fprintf(stderr, "Unsupported bit depth (%d)\n", visualInfo.depth);
	    return 0;
	}
    }
#endif /* not INLINE_DRAWPIXEL */
    
#ifdef __cplusplus
    switch(visualInfo.c_class) 
#else
    switch(visualInfo.class) 
#endif
    {
     case TrueColor: 
	{	    
	    int red_bits = bitsInMask(visualInfo.red_mask);
	    int green_bits = bitsInMask(visualInfo.green_mask);
	    int blue_bits = bitsInMask(visualInfo.blue_mask);
	    int red_shift = maskShift(visualInfo.red_mask);
	    int green_shift = maskShift(visualInfo.green_mask);
	    int blue_shift = maskShift(visualInfo.blue_mask);
	    
	    int i;
	    for(i = 0; i < 4096; i++) {
		int r = i >> 8;
		int g = (i >> 4) & 0xF;
		int b = i & 0xF;
		xcolors[i] = (doMask(r, red_bits, red_shift) 
			      | doMask(g, green_bits, green_shift) 
			      | doMask(b, blue_bits, blue_shift));
	    }
	}
	break;

     case GrayScale:
     case PseudoColor:
	printf("Allocating lots of colors...\n");
	{
	    /* This is kind of kludgy...
	     * Try to allocate as many different colors as possible. */
#if 0
	    int col;
	    int step = 16;
	    int allocated[4096];
	    memset(allocated,0,sizeof allocated);
	    while ((step/=2) > 0) {
		int r, g, b;
		for(r=0; r<16; r += step) {
		    for(g=0; g<16; g += step) {
			for(b=0; b<16; b += step) {
			    int cnr = (r << 8) + (g << 4) + b;
			    if (!allocated[cnr]) {
				XColor col;
				char str[10];
				sprintf(str, "rgb:%x/%x/%x", r,g,b);
				XParseColor(display,cmap,str,&col);
				if (XAllocColor(display,cmap,&col)) {
				    allocated[cnr] = true;
				    xcolors[cnr] = col.pixel;
				} 
			    }
			}
		    }
		}
	    }

	    for(col = 0; col < 4096; col++) {
		int cnr = col;
		if (!allocated[cnr]) {
		    int r = cnr >> 8;
		    int g = (cnr >> 4) & 0xF;
		    int b = cnr & 0xF;
		    int maxd = 4096,best = 0;
		    int c2;
		    for(c2 = 0; c2 < 4096; c2++)
		    	if (allocated[c2]) {
			    int r2 = c2 >> 8;
			    int g2 = (c2 >> 4) & 0xF;
			    int b2 = c2 & 0xF;
			    int dist = abs(r2-r)*2 + abs(g2-g)*3 + abs(b2-b);
			    if (dist < maxd) {
				maxd = dist;
				best = c2;
			    }
			}
		    cnr = best;
		}
		xcolors[col] = xcolors[cnr];
		
	    }
#else
	    /* I think this code makes colors look much nicer than the code
	     * above. FIXME: surely there is One True Way to do this kind
	     * of thing? */
	    int step = 5, mode = 0, count = 0, col;
	    int allocated[4096];
	    memset(allocated,0,sizeof allocated);
	    
	    do {
		int r, g, b, rs, bs, gs;
		bs = step / (mode & 4 ? 2 : 1);
		rs = step / (mode & 2 ? 2 : 1);
		gs = step / (mode & 1 ? 2 : 1);
		for(b = 0; b < 16; b += bs) {
		    for(r = 0; r < 16; r += rs) {
			for(g = 0; g < 16; g += gs) {
			    int cnr = (r << 8) + (g << 4) + b;
			    if (!allocated[cnr]) {
				XColor col;
				char str[10];
				sprintf(str, "rgb:%x/%x/%x", r,g,b);
				XParseColor(display,cmap,str,&col);
				if (XAllocColor(display,cmap,&col)) {
				    allocated[cnr] = true;
				    xcolors[cnr] = col.pixel;
				} 
			    }
			}
		    }
		}
		if (step > 1)
		    step++;
		step /= 2;
	    } while (step);
	    
	    finished:
	    for(col = 0; col < 4096; col++) {
		int cnr = col;
		if (!allocated[cnr]) {
		    int r = cnr >> 8;
		    int g = (cnr >> 4) & 0xF;
		    int b = cnr & 0xF;
		    int maxd = 4096,best = 0;
		    int c2;
		    for(c2 = 0; c2 < 4096; c2++)
			if (allocated[c2]) {
			    int r2 = c2 >> 8;
			    int g2 = (c2 >> 4) & 0xF;
			    int b2 = c2 & 0xF;
			    int rd = (r2-r)*r*2;
			    int bd = (b2-b)*b*2;
			    int gd = (g2-g)*g*2;
			    int dist = abs(rd) + abs(gd) + abs(bd) + abs(rd-gd)/3 + abs(rd-bd)/3 + abs(gd-bd)/3;
			    if (dist < maxd) {
				maxd = dist;
				best = c2;
			    }
			}
		    cnr = best;
		}
		xcolors[col] = xcolors[cnr];
	    }
#endif
	}
	break;
	
     default:
#ifdef __cplusplus
	fprintf(stderr, "Unsupported class (%d)\n", visualInfo.c_class);
#else
	fprintf(stderr, "Unsupported class (%d)\n", visualInfo.class);
#endif
	return 0;
    }
    return 1;
}

int graphics_init(void)
{
    int i;
    char *display_name = 0;
    XSetWindowAttributes wattr;
    vscale = dont_want_aspect ? 1 : 2;

    display = XOpenDisplay(display_name);
    if (display == 0)  {
	fprintf(stderr, "Can't connect to X server %s\n", XDisplayName(display_name));
	return 0;
    }
    screen = XDefaultScreen(display);
    rootwin = XRootWindow(display,screen);

    /* try for a 12 bit visual first, then a 16 bit, then a 24 bit, then 8 bit */
    if (XMatchVisualInfo(display, screen, 12, TrueColor, &visualInfo)) {
    } else if (XMatchVisualInfo(display, screen, 16, TrueColor, &visualInfo)) {
    } else if (XMatchVisualInfo(display, screen, 24, TrueColor, &visualInfo)) {
    } else if (XMatchVisualInfo(display, screen, 8, PseudoColor, &visualInfo)) {
      /* for our HP boxes */
    } else if (XMatchVisualInfo(display, screen, 8, GrayScale, &visualInfo)) {
    } else {
	fprintf(stderr, "Can't obtain appropriate X visual\n");
	return 0;
    }

    vis = visualInfo.visual;
    bitdepth = visualInfo.depth;

    fprintf(stderr, "Using %d bit visual\n", bitdepth);

    cmap = XCreateColormap(display, rootwin, vis, AllocNone);
    XParseColor(display, cmap, "#000000", &black);
    if (!XAllocColor(display, cmap, &black))
	fprintf(stderr, "Whoops??\n");
    XParseColor(display, cmap, "#ffffff", &white);
    if (!XAllocColor(display, cmap, &white))
	fprintf(stderr, "Whoops??\n");

    wattr.event_mask = eventmask;
    wattr.background_pixel = black.pixel;
    wattr.backing_store = Always;
    wattr.backing_planes = bitdepth;
    wattr.border_pixmap = None;
    wattr.border_pixel = black.pixel;
    wattr.colormap = cmap;

    mywin = XCreateWindow(display,rootwin,0,0,796,vscale*(313-29),0,
			  bitdepth, InputOutput, vis,
			  CWEventMask|CWBackPixel|CWBorderPixel|CWBackingStore
			  |CWBackingPlanes|CWColormap,
			  &wattr);
    XMapWindow(display,mywin);
    XStoreName(display, mywin, "UAE");
    
    blankCursor = XCreatePixmapCursor(display,
				      XCreatePixmap(display, mywin, 1, 1, 1),
				      XCreatePixmap(display, mywin, 1, 1, 1), 
				      &black, &white, 0, 0);
    xhairCursor = XCreateFontCursor(display, XC_crosshair);

    whitegc = XCreateGC(display,mywin,0,0);
    blackgc = XCreateGC(display,mywin,0,0);
    
    XSetForeground(display,blackgc,black.pixel);
    XSetForeground(display,whitegc,white.pixel);

#ifdef DONT_WANT_SHM
    img = XCreateImage(display, vis, bitdepth, ZPixmap, 0, pixel_buffer, 800, 
		       1, 32, 0);
#else
    initShm (bitdepth);
#endif
    if (!init_colors())
	return 0;
    buttonstate[0] = buttonstate[1] = buttonstate[2] = false;
    for(i=0; i<256; i++) keystate[i] = false;
    
    scrfirstline = scrlastline = -1;
    prev_scrfirstline = 0;
    prev_scrlastline = vscale * (313-29);
    scrbgcol = xcolors[0];
    
    lastmx = lastmy = 0; newmousecounters = false; inwindow = false;
    
    XDefineCursor(display, mywin, xhairCursor);
    cursorOn = true;
    return 1;
}

void graphics_leave(void)
{
    XAutoRepeatOn(display);
}

/* Decode KeySyms. This function knows about all keys that are common 
 * between different keyboard languages. */
static int kc_decode (KeySym ks)
{
    switch (ks) {	
     case XK_A: case XK_a: return AK_A;
     case XK_B: case XK_b: return AK_B;
     case XK_C: case XK_c: return AK_C;
     case XK_D: case XK_d: return AK_D;
     case XK_E: case XK_e: return AK_E;
     case XK_F: case XK_f: return AK_F;
     case XK_G: case XK_g: return AK_G;
     case XK_H: case XK_h: return AK_H;
     case XK_I: case XK_i: return AK_I;
     case XK_J: case XK_j: return AK_J;
     case XK_K: case XK_k: return AK_K;
     case XK_L: case XK_l: return AK_L;
     case XK_M: case XK_m: return AK_M;
     case XK_N: case XK_n: return AK_N;
     case XK_O: case XK_o: return AK_O;
     case XK_P: case XK_p: return AK_P;
     case XK_Q: case XK_q: return AK_Q;
     case XK_R: case XK_r: return AK_R;
     case XK_S: case XK_s: return AK_S;
     case XK_T: case XK_t: return AK_T;
     case XK_U: case XK_u: return AK_U;
     case XK_V: case XK_v: return AK_V;
     case XK_W: case XK_w: return AK_W;
     case XK_X: case XK_x: return AK_X;
	
     case XK_0: return AK_0;
     case XK_1: return AK_1;
     case XK_2: return AK_2;
     case XK_3: return AK_3;
     case XK_4: return AK_4;
     case XK_5: return AK_5;
     case XK_6: return AK_6;
     case XK_7: return AK_7;
     case XK_8: return AK_8;
     case XK_9: return AK_9;
	
     case XK_KP_0: return AK_NP0;
     case XK_KP_1: return AK_NP1;
     case XK_KP_2: return AK_NP2;
     case XK_KP_3: return AK_NP3;
     case XK_KP_4: return AK_NP4;
     case XK_KP_5: return AK_NP5;
     case XK_KP_6: return AK_NP6;
     case XK_KP_7: return AK_NP7;
     case XK_KP_8: return AK_NP8;
     case XK_KP_9: return AK_NP9;

     case XK_KP_Divide: return AK_NPDIV;
     case XK_KP_Multiply: return AK_NPMUL;
     case XK_KP_Subtract: return AK_NPSUB;
     case XK_KP_Add: return AK_NPADD;
     case XK_KP_Decimal: return AK_NPDEL;
     case XK_KP_Enter: return AK_ENT;

     case XK_F1: return AK_F1;
     case XK_F2: return AK_F2;
     case XK_F3: return AK_F3;
     case XK_F4: return AK_F4;
     case XK_F5: return AK_F5;
     case XK_F6: return AK_F6;
     case XK_F7: return AK_F7;
     case XK_F8: return AK_F8;
     case XK_F9: return AK_F9;
     case XK_F10: return AK_F10;
	    
     case XK_BackSpace: return AK_BS;
     case XK_Delete: return AK_DEL;
     case XK_Control_L: case XK_Control_R: return AK_CTRL;
     case XK_Tab: return AK_TAB;
     case XK_Alt_L: return AK_LALT;
     case XK_Alt_R: return AK_RALT;
     case XK_Meta_R: case XK_Hyper_R:
     case XK_Meta_L: case XK_Hyper_L:
     case XK_Return: return AK_RET;
     case XK_space: return AK_SPC;
     case XK_Shift_L: return AK_LSH;
     case XK_Shift_R: return AK_RSH;
     case XK_Escape: return AK_ESC;

     case XK_Insert: return AK_BACKSLASH;
     case XK_End: return AK_HELP;
     case XK_Caps_Lock: return AK_CAPSLOCK;
	
     case XK_Up: return AK_UP;
     case XK_Down: return AK_DN;
     case XK_Left: return AK_LF;
     case XK_Right: return AK_RT;
	
     case XK_F11: return AK_BACKSLASH;
     case XK_F12: return AK_mousestuff;
#ifdef XK_F14
     case XK_F14:
#endif
     case XK_Scroll_Lock: return AK_inhibit;

#ifdef XK_Page_Up /* These are missing occasionally */
     case XK_Page_Up: return AK_RAMI;          /* PgUp mapped to right amiga */
     case XK_Page_Down: return AK_LAMI;        /* PgDn mapped to left amiga */
#endif
    }
    return -1;
}

static int decode_us(KeySym ks)
{
    switch(ks) {	/* US specific */	
     case XK_Y: case XK_y: return AK_Y;
     case XK_Z: case XK_z: return AK_Z;
     case XK_bracketleft: return AK_LBRACKET;
     case XK_bracketright: return AK_RBRACKET;
     case XK_comma: return AK_COMMA;
     case XK_period: return AK_PERIOD;
     case XK_slash: return AK_SLASH;
     case XK_semicolon: return AK_SEMICOLON;
     case XK_minus: return AK_MINUS;
     case XK_equal: return AK_EQUAL;
	/* this doesn't work: */
     case XK_quoteright: return AK_QUOTE;
     case XK_quoteleft: return AK_BACKQUOTE;
    }

    return -1;
}

static int decode_de(KeySym ks)
{
    switch(ks) {
	/* DE specific */
     case XK_Y: case XK_y: return AK_Z;
     case XK_Z: case XK_z: return AK_Y;
     case XK_Odiaeresis: case XK_odiaeresis: return AK_SEMICOLON;
     case XK_Adiaeresis: case XK_adiaeresis: return AK_QUOTE;
     case XK_Udiaeresis: case XK_udiaeresis: return AK_LBRACKET;
     case XK_plus: case XK_asterisk: return AK_RBRACKET;
     case XK_comma: return AK_COMMA;
     case XK_period: return AK_PERIOD;
     case XK_less: case XK_greater: return AK_LTGT;
     case XK_numbersign: return AK_NUMBERSIGN;
     case XK_ssharp: return AK_MINUS;
     case XK_apostrophe: return AK_EQUAL;
     case XK_asciicircum: return AK_BACKQUOTE;
     case XK_minus: return AK_SLASH;
    }

    return -1;
}

static int keycode2amiga(XKeyEvent *event)
{
    KeySym ks;
    int as;
    int index = 0;
    
    do {
	ks = XLookupKeysym(event, index);
	as = kc_decode (ks);
	
	if (as == -1) {	    
	    switch(keyboard_lang) {
	     case KBD_LANG_US:
		as = decode_us(ks);
		break;
		
	     case KBD_LANG_DE:
		as = decode_de(ks);
		break;
		
	     default:
		as = -1;
		break;
	    }
	}
	if(-1 != as)
		return as;
	index++;
    } while (ks != NoSymbol);
    return -1;
}

static struct timeval lastMotionTime;

void handle_events(void)
{
    bool repeat;
    newmousecounters = false;
    gui_handle_events();
    
    do {
	XEvent event;
	if (!XCheckMaskEvent(display, eventmask, &event)) break;
	repeat = false;
	
	switch(event.type) {
	 case KeyPress: {		
	     int kc = keycode2amiga((XKeyEvent *)&event);
	     if (kc == -1) break;
	     switch (kc) {
	      case AK_mousestuff:
#if 0
	         if (keystate[AK_CTRL])
		     mousesetup();
		 else 
#endif
		     togglemouse();
		 break;

	      case AK_inhibit:
		 inhibit_frame ^= 1;
		 break;

	      default:
	     	 if (!keystate[kc]) {
		     keystate[kc] = true;
		     record_key (kc << 1);
		 }
		 break;
	     }
	     break;
	 }
	 case KeyRelease: {	     
	     int kc = keycode2amiga((XKeyEvent *)&event);
	     if (kc == -1) break;
	     keystate[kc] = false;
	     record_key ((kc << 1) | 1);
	     break;
	 }
	 case ButtonPress:
	    buttonstate[((XButtonEvent *)&event)->button-1] = true;
	    break;
	 case ButtonRelease:
	    buttonstate[((XButtonEvent *)&event)->button-1] = false;
	    break;
	 case EnterNotify:
	    newmousecounters = true;
	    lastmx = ((XCrossingEvent *)&event)->x;
	    lastmy = ((XCrossingEvent *)&event)->y;
	    repeat = true;
	    inwindow = true;
	    break;
	 case LeaveNotify:
	    inwindow = false;
	    repeat = true;
	    break;
	 case FocusIn:
	    XAutoRepeatOff(display);
	    repeat = true;
	    break;
	 case FocusOut:
	    XAutoRepeatOn(display);
	    repeat = true;
	    break;
	 case MotionNotify:
	    if (inwindow) {		
		lastmx = ((XMotionEvent *)&event)->x;
		lastmy = ((XMotionEvent *)&event)->y;
		if(!cursorOn) {
		    XDefineCursor(display, mywin, xhairCursor);
		    cursorOn = true;
		}
		gettimeofday(&lastMotionTime, NULL);
	    }
	    repeat = true;
	    break;
	}
    } while (repeat);
    
    if(cursorOn) {
	struct timeval now;
	int diff;
	gettimeofday(&now, NULL);
	diff = (now.tv_sec - lastMotionTime.tv_sec) * 1000000 + 
	    (now.tv_usec - lastMotionTime.tv_usec);
	if(diff > 1000000) {
	    XDefineCursor(display, mywin, blankCursor);
	    cursorOn = false;
	}
    }
	
    /* "Affengriff" */
    if(keystate[AK_CTRL] && keystate[AK_LAMI] && keystate[AK_RAMI])
    	MC68000_reset();
}

bool debuggable(void)
{
    return true;
}

bool needmousehack(void)
{
    return true;
}

void LED(int on)
{
	XKeyboardControl control;
	control.led = 1; /* implementation defined */
	control.led_mode = on ? LedModeOn : LedModeOff;
	XChangeKeyboardControl(display, KBLed | KBLedMode, &control);
}
