/************************************************************************/
/*			KEGS: Apple //gs Emulator			*/
/*			Copyright 2002 by Kent Dickey			*/
/*									*/
/*		This code is covered by the GNU GPL			*/
/*									*/
/*	The KEGS web page is kegs.sourceforge.net			*/
/*	You may contact the author at: kadickey@alumni.princeton.edu	*/
/************************************************************************/

const char rcsid_xdriver_c[] = "@(#)$KmKId: xdriver.c,v 1.187 2004-11-15 16:24:19-05 kentd Exp $";

# if !defined(__CYGWIN__) && !defined(__POWERPC__)
/* No shared memory on Cygwin */
# define X_SHARED_MEM
#endif /* CYGWIN */

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <time.h>
#include <stdlib.h>
#include <signal.h>

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

int XShmQueryExtension(Display *display);
void _XInitImageFuncPtrs(XImage *xim);

#include "defc.h"
#include "protos_xdriver.h"

#define FONT_NAME_STATUS	"8x13"

extern int Verbose;

extern int g_warp_pointer;
extern int g_screen_depth;
extern int g_force_depth;
int g_screen_mdepth = 0;

extern int _Xdebug;

extern int g_send_sound_to_file;

extern int g_quit_sim_now;

int	g_has_focus = 0;
int	g_auto_repeat_on = -1;
int	g_x_shift_control_state = 0;


Display *g_display = 0;
Visual	*g_vis = 0;
Window g_a2_win;
GC g_a2_winGC;
XFontStruct *g_text_FontSt;
Colormap g_a2_colormap = 0;
Colormap g_default_colormap = 0;
int	g_needs_cmap = 0;

extern word32 g_red_mask;
extern word32 g_green_mask;
extern word32 g_blue_mask;
extern int g_red_left_shift;
extern int g_green_left_shift;
extern int g_blue_left_shift;
extern int g_red_right_shift;
extern int g_green_right_shift;
extern int g_blue_right_shift;

#ifdef X_SHARED_MEM
int g_use_shmem = 1;
#else
int g_use_shmem = 0;
#endif

extern Kimage g_mainwin_kimage;

extern int Max_color_size;

XColor g_xcolor_a2vid_array[256];

extern word32 g_palette_8to1624[256];
extern word32 g_a2palette_8to1624[256];

int	g_alt_left_up = 1;
int	g_alt_right_up = 1;

extern word32 g_full_refresh_needed;

extern int g_border_sides_refresh_needed;
extern int g_border_special_refresh_needed;
extern int g_status_refresh_needed;

extern int g_lores_colors[];
extern int g_cur_a2_stat;

extern int g_a2vid_palette;

extern int g_installed_full_superhires_colormap;

extern int g_screen_redraw_skip_amt;

extern word32 g_a2_screen_buffer_changed;

extern char *g_status_ptrs[MAX_STATUS_LINES];

Cursor	g_cursor;
Pixmap	g_cursor_shape;
Pixmap	g_cursor_mask;

XColor	g_xcolor_black = { 0, 0x0000, 0x0000, 0x0000, DoRed|DoGreen|DoBlue, 0 };
XColor	g_xcolor_white = { 0, 0xffff, 0xffff, 0xffff, DoRed|DoGreen|DoBlue, 0 };

int g_depth_attempt_list[] = { 16, 24, 15, 8 };


#define X_EVENT_LIST_ALL_WIN						\
	(ExposureMask | ButtonPressMask | ButtonReleaseMask |		\
	 OwnerGrabButtonMask | KeyPressMask | KeyReleaseMask |		\
	 KeymapStateMask | ColormapChangeMask | FocusChangeMask)

#define X_BASE_WIN_EVENT_LIST						\
	(X_EVENT_LIST_ALL_WIN | PointerMotionMask | ButtonMotionMask)

#define X_A2_WIN_EVENT_LIST						\
	(X_BASE_WIN_EVENT_LIST)

int	g_num_a2_keycodes = 0;

int a2_key_to_xsym[][3] = {
	{ 0x35,	XK_Escape,	0 },
	{ 0x7a,	XK_F1,	0 },
	{ 0x78,	XK_F2,	0 },
	{ 0x63,	XK_F3,	0 },
	{ 0x76,	XK_F4,	0 },
	{ 0x60,	XK_F5,	0 },
	{ 0x61,	XK_F6,	0 },
	{ 0x62,	XK_F7,	0 },
	{ 0x64,	XK_F8,	0 },
	{ 0x65,	XK_F9,	0 },
	{ 0x6d,	XK_F10,	0 },
	{ 0x67,	XK_F11,	0 },
	{ 0x6f,	XK_F12,	0 },
	{ 0x69,	XK_F13,	0 },
	{ 0x6b,	XK_F14,	0 },
	{ 0x71,	XK_F15,	0 },
	{ 0x7f, XK_Pause, XK_Break },
	{ 0x32,	'`', '~' },		/* Key number 18? */
	{ 0x12,	'1', '!' },
	{ 0x13,	'2', '@' },
	{ 0x14,	'3', '#' },
	{ 0x15,	'4', '$' },
	{ 0x17,	'5', '%' },
	{ 0x16,	'6', '^' },
	{ 0x1a,	'7', '&' },
	{ 0x1c,	'8', '*' },
	{ 0x19,	'9', '(' },
	{ 0x1d,	'0', ')' },
	{ 0x1b,	'-', '_' },
	{ 0x18,	'=', '+' },
	{ 0x33,	XK_BackSpace, 0 },
	{ 0x72,	XK_Insert, XK_Help },	/* Help? */
/*	{ 0x73,	XK_Home, 0 },		alias XK_Home to be XK_KP_Equal! */
	{ 0x74,	XK_Page_Up, 0 },
	{ 0x47,	XK_Num_Lock, XK_Clear },	/* Clear */
	{ 0x51,	XK_KP_Equal, XK_Home },		/* Note XK_Home alias! */
	{ 0x4b,	XK_KP_Divide, 0 },
	{ 0x43,	XK_KP_Multiply, 0 },

	{ 0x30,	XK_Tab, 0 },
	{ 0x0c,	'q', 'Q' },
	{ 0x0d,	'w', 'W' },
	{ 0x0e,	'e', 'E' },
	{ 0x0f,	'r', 'R' },
	{ 0x11,	't', 'T' },
	{ 0x10,	'y', 'Y' },
	{ 0x20,	'u', 'U' },
	{ 0x22,	'i', 'I' },
	{ 0x1f,	'o', 'O' },
	{ 0x23,	'p', 'P' },
	{ 0x21,	'[', '{' },
	{ 0x1e,	']', '}' },
	{ 0x2a,	0x5c, '|' },	/* backslash, bar */
	{ 0x75,	XK_Delete, 0 },
	{ 0x77,	XK_End, 0 },
	{ 0x79,	XK_Page_Down, 0 },
	{ 0x59,	XK_KP_7, XK_KP_Home },
	{ 0x5b,	XK_KP_8, XK_KP_Up },
	{ 0x5c,	XK_KP_9, XK_KP_Page_Up },
	{ 0x4e,	XK_KP_Subtract, 0 },

	{ 0x39,	XK_Caps_Lock, 0 },
	{ 0x00,	'a', 'A' },
	{ 0x01,	's', 'S' },
	{ 0x02,	'd', 'D' },
	{ 0x03,	'f', 'F' },
	{ 0x05,	'g', 'G' },
	{ 0x04,	'h', 'H' },
	{ 0x26,	'j', 'J' },
	{ 0x28,	'k', 'K' },
	{ 0x25,	'l', 'L' },
	{ 0x29,	';', ':' },
	{ 0x27,	0x27, '"' },	/* single quote */
	{ 0x24,	XK_Return, 0 },
	{ 0x56,	XK_KP_4, XK_KP_Left },
	{ 0x57,	XK_KP_5, 0 },
	{ 0x58,	XK_KP_6, XK_KP_Right },
	{ 0x45,	XK_KP_Add, 0 },

	{ 0x38,	XK_Shift_L, XK_Shift_R },
	{ 0x06,	'z', 'Z' },
	{ 0x07,	'x', 'X' },
	{ 0x08,	'c', 'C' },
	{ 0x09,	'v', 'V' },
	{ 0x0b,	'b', 'B' },
	{ 0x2d,	'n', 'N' },
	{ 0x2e,	'm', 'M' },
	{ 0x2b,	',', '<' },
	{ 0x2f,	'.', '>' },
	{ 0x2c,	'/', '?' },
	{ 0x3e,	XK_Up, 0 },
	{ 0x53,	XK_KP_1, XK_KP_End },
	{ 0x54,	XK_KP_2, XK_KP_Down },
	{ 0x55,	XK_KP_3, XK_KP_Page_Down },

	{ 0x36,	XK_Control_L, XK_Control_R },
	{ 0x3a,	XK_Print, XK_Sys_Req },		/* Option */
	{ 0x37,	XK_Scroll_Lock, 0 },		/* Command */
	{ 0x31,	' ', 0 },
	{ 0x3b,	XK_Left, 0 },
	{ 0x3d,	XK_Down, 0 },
	{ 0x3c,	XK_Right, 0 },
	{ 0x52,	XK_KP_0, XK_KP_Insert },
	{ 0x41,	XK_KP_Decimal, XK_KP_Separator },
	{ 0x4c,	XK_KP_Enter, 0 },
	{ -1, -1, -1 }
};

int
main(int argc, char **argv)
{
	return kegsmain(argc, argv);
}

void
x_dialog_create_kegs_conf(const char *str)
{
	/* do nothing -- not implemented yet */
	return;
}

int
x_show_alert(int is_fatal, const char *str)
{
	/* Not implemented yet */
	adb_all_keys_up();

	clear_fatal_logs();
	return 0;
}


#define MAKE_2(val)	( (val << 8) + val)

void
x_update_color(int col_num, int red, int green, int blue, word32 rgb)
{
	XColor *xcol;

	xcol = &(g_xcolor_a2vid_array[col_num]);
	xcol->red = MAKE_2(red);
	xcol->green = MAKE_2(green);
	xcol->blue = MAKE_2(blue);
	xcol->flags = DoRed | DoGreen | DoBlue;
}

void
x_update_physical_colormap()
{
	if(g_needs_cmap) {
		XStoreColors(g_display, g_a2_colormap,
				&g_xcolor_a2vid_array[0], Max_color_size);
	}
}

void
show_xcolor_array()
{
	int i;

	for(i = 0; i < 256; i++) {
		printf("%02x: %08x\n", i, g_palette_8to1624[i]);
			
#if 0
		printf("%02x: %04x %04x %04x, %02x %x\n",
			i, xcolor_array[i].red, xcolor_array[i].green,
			xcolor_array[i].blue, (word32)xcolor_array[i].pixel,
			xcolor_array[i].flags);
#endif
	}
}


int
my_error_handler(Display *display, XErrorEvent *ev)
{
	char msg[1024];
	XGetErrorText(display, ev->error_code, msg, 1000);
	printf("X Error code %s\n", msg);
	fflush(stdout);

	return 0;
}

void
xdriver_end()
{

	printf("xdriver_end\n");
	if(g_display) {
		x_auto_repeat_on(1);
		XFlush(g_display);
	}
}

void
show_colormap(char *str, Colormap cmap, int index1, int index2, int index3)
{
	XColor	xcol;
	int	i;
	int	pix;

	printf("Show colormap: %08x = %s, cmap cells: %d,%d,%d\n",
			(int)cmap, str, index1, index2, index3);
	for(i = 0; i < index1 + index2 + index3; i++) {
		pix = i;
		if(i >= index1) {
			pix = (i-index1)*index1;
			if(i >= (index1 + index2)) {
				pix = (i - index1 - index2)*index2*index1;
			}
		}
		if(i == 0 && index1 < 250) {
			pix = 0x842;
		}
		xcol.pixel = pix;
		XQueryColor(g_display, cmap, &xcol);
		printf("Cell %03x: pix: %03x, R:%04x, G:%04x, B:%04x\n",
			i, (int)xcol.pixel, xcol.red, xcol.green, xcol.blue);
	}
}

void
x_badpipe(int signum)
{
	/* restore normal sigpipe handling */
	signal(SIGPIPE, SIG_DFL);

	/* attempt to xset r */
	system("xset r");
	my_exit(5);
}

void
dev_video_init()
{
	int	tmp_array[0x80];
	XGCValues new_gc;
	XSetWindowAttributes win_attr;
	XSizeHints my_winSizeHints;
	XClassHint my_winClassHint;
	XTextProperty my_winText;
	XVisualInfo *visualList;
	char	**font_ptr;
	char	cursor_data;
	word32	create_win_list;
	int	depth;
	int	len;
	int	cmap_alloc_amt;
	int	cnt;
	int	font_height;
	int	base_height;
	int	screen_num;
	char	*myTextString[1];
	word32	lores_col;
	int	ret;
	int	i;
	int	keycode;

	printf("Preparing X Windows graphics system\n");
	ret = 0;

	signal(SIGPIPE, x_badpipe);

	g_num_a2_keycodes = 0;
	for(i = 0; i <= 0x7f; i++) {
		tmp_array[i] = 0;
	}
	for(i = 0; i < 0x7f; i++) {
		keycode = a2_key_to_xsym[i][0];
		if(keycode < 0) {
			g_num_a2_keycodes = i;
			break;
		} else if(keycode > 0x7f) {
			printf("a2_key_to_xsym[%d] = %02x!\n", i, keycode);
				exit(2);
		} else {
			if(tmp_array[keycode]) {
				printf("a2_key_to_x[%d] = %02x used by %d\n",
					i, keycode, tmp_array[keycode] - 1);
			}
			tmp_array[keycode] = i + 1;
		}
	}

#if 0
	printf("Setting _Xdebug = 1, makes X synchronous\n");
	_Xdebug = 1;
#endif

	g_display = XOpenDisplay(NULL);
	if(g_display == NULL) {
		fprintf(stderr, "Can't open display\n");
		exit(1);
	}

	vid_printf("Just opened display = %p\n", g_display);
	fflush(stdout);

	screen_num = DefaultScreen(g_display);

	len = sizeof(g_depth_attempt_list)/sizeof(int);
	if(g_force_depth > 0) {
		/* Only use the requested user depth */
		len = 1;
		g_depth_attempt_list[0] = g_force_depth;
	}
	g_vis = 0;
	for(i = 0; i < len; i++) {
		depth = g_depth_attempt_list[i];

		g_vis = x_try_find_visual(depth, screen_num,
			&visualList);
		if(g_vis != 0) {
			break;
		}
	}
	if(g_vis == 0) {
		fprintf(stderr, "Couldn't find any visuals at any depth!\n");
		exit(2);
	}

	g_default_colormap = XDefaultColormap(g_display, screen_num);
	if(!g_default_colormap) {
		printf("g_default_colormap == 0!\n");
		exit(4);
	}

	g_a2_colormap = -1;
	cmap_alloc_amt = AllocNone;
	if(g_needs_cmap) {
		cmap_alloc_amt = AllocAll;
	}
	g_a2_colormap = XCreateColormap(g_display,
			RootWindow(g_display,screen_num), g_vis,
					cmap_alloc_amt);

	vid_printf("g_a2_colormap: %08x, main: %08x\n",
			(word32)g_a2_colormap, (word32)g_default_colormap);

	if(g_needs_cmap && g_a2_colormap == g_default_colormap) {
		printf("A2_colormap = default colormap!\n");
		exit(4);
	}

	/* and define cursor */
	cursor_data = 0;
	g_cursor_shape = XCreatePixmapFromBitmapData(g_display,
		RootWindow(g_display,screen_num), &cursor_data, 1, 1, 1, 0, 1);
	g_cursor_mask = XCreatePixmapFromBitmapData(g_display,
		RootWindow(g_display,screen_num), &cursor_data, 1, 1, 1, 0, 1);

	g_cursor = XCreatePixmapCursor(g_display, g_cursor_shape,
			g_cursor_mask, &g_xcolor_black, &g_xcolor_white, 0, 0);

	XFreePixmap(g_display, g_cursor_shape);
	XFreePixmap(g_display, g_cursor_mask);

	XFlush(g_display);

	win_attr.event_mask = X_A2_WIN_EVENT_LIST;
	win_attr.colormap = g_a2_colormap;
	win_attr.backing_store = WhenMapped;
	win_attr.border_pixel = 1;
	win_attr.background_pixel = 0;
	if(g_warp_pointer) {
		win_attr.cursor = g_cursor;
	} else {
		win_attr.cursor = None;
	}

	vid_printf("About to a2_win, depth: %d\n", g_screen_depth);
	fflush(stdout);

	create_win_list = CWEventMask | CWBackingStore | CWCursor;
	create_win_list |= CWColormap | CWBorderPixel | CWBackPixel;

	base_height = X_A2_WINDOW_HEIGHT + (MAX_STATUS_LINES * 13);

	g_a2_win = XCreateWindow(g_display, RootWindow(g_display, screen_num),
		0, 0, BASE_WINDOW_WIDTH, base_height,
		0, g_screen_depth, InputOutput, g_vis,
		create_win_list, &win_attr);

	XSetWindowColormap(g_display, g_a2_win, g_a2_colormap);

	XFlush(g_display);

/* Check for XShm */
#ifdef X_SHARED_MEM
	if(g_use_shmem) {
		ret = XShmQueryExtension(g_display);
		if(ret == 0) {
			printf("XShmQueryExt ret: %d\n", ret);
			printf("not using shared memory\n");
			g_use_shmem = 0;
		} else {
			printf("Will use shared memory for X\n");
		}
	}
#endif

	video_get_kimages();
	if(g_screen_depth != 8) {
		video_get_kimage(&g_mainwin_kimage, 0, g_screen_depth,
				g_screen_mdepth);
	}

	if(!g_use_shmem) {
		if(g_screen_redraw_skip_amt < 0) {
			g_screen_redraw_skip_amt = 3;
		}
		printf("Not using shared memory, setting skip_amt = %d\n",
			g_screen_redraw_skip_amt);
	}

	/* Done with visualList now */
	XFree(visualList);

	for(i = 0; i < 256; i++) {
		g_xcolor_a2vid_array[i].pixel = i;
		lores_col = g_lores_colors[i & 0xf];
		video_update_color_raw(i, lores_col);
		g_a2palette_8to1624[i] = g_palette_8to1624[i];
	}

	x_update_physical_colormap();

	g_installed_full_superhires_colormap = !g_needs_cmap;
	
	myTextString[0] = "KEGS";

	XStringListToTextProperty(myTextString, 1, &my_winText);

	my_winSizeHints.flags = PSize | PMinSize | PMaxSize;
	my_winSizeHints.width = BASE_WINDOW_WIDTH;
	my_winSizeHints.height = base_height;
	my_winSizeHints.min_width = BASE_WINDOW_WIDTH;
	my_winSizeHints.min_height = base_height;
	my_winSizeHints.max_width = BASE_WINDOW_WIDTH;
	my_winSizeHints.max_height = base_height;
	my_winClassHint.res_name = "KEGS";
	my_winClassHint.res_class = "KEGS";

	XSetWMProperties(g_display, g_a2_win, &my_winText, &my_winText, 0,
		0, &my_winSizeHints, 0, &my_winClassHint);
	XMapRaised(g_display, g_a2_win);

	XSync(g_display, False);

	g_a2_winGC = XCreateGC(g_display, g_a2_win, 0, (XGCValues *) 0);
	font_ptr = XListFonts(g_display, FONT_NAME_STATUS, 4, &cnt);

	vid_printf("act_cnt of fonts: %d\n", cnt);
	for(i = 0; i < cnt; i++) {
		vid_printf("Font %d: %s\n", i, font_ptr[i]);
	}
	fflush(stdout);
	g_text_FontSt = XLoadQueryFont(g_display, FONT_NAME_STATUS);
	vid_printf("font # returned: %08x\n", (word32)(g_text_FontSt->fid));
	font_height = g_text_FontSt->ascent + g_text_FontSt->descent;
	vid_printf("font_height: %d\n", font_height);

	vid_printf("widest width: %d\n", g_text_FontSt->max_bounds.width);

	new_gc.font = g_text_FontSt->fid;
	new_gc.fill_style = FillSolid;
	XChangeGC(g_display, g_a2_winGC, GCFillStyle | GCFont, &new_gc);

	/* XSync(g_display, False); */
#if 0
/* MkLinux for Powermac depth 15 has bugs--this was to try to debug them */
	if(g_screen_depth == 15) {
		/* draw phony screen */
		ptr16 = (word16 *)dint_main_win;
		for(i = 0; i < 320*400; i++) {
			ptr16[i] = 0;
		}
		for(i = 0; i < 400; i++) {
			for(j = 0; j < 640; j++) {
				sh = (j / 20) & 0x1f;
				val = sh;
				val = val;
				*ptr16++ = val;
			}
		}
		XPutImage(g_display, g_a2_win, g_a2_winGC, xint_main_win,
			0, 0,
			BASE_MARGIN_LEFT, BASE_MARGIN_TOP,
			640, 400);
		XFlush(g_display);
	}
#endif


	XFlush(g_display);
	fflush(stdout);
}

Visual *
x_try_find_visual(int depth, int screen_num, XVisualInfo **visual_list_ptr)
{
	XVisualInfo *visualList;
	XVisualInfo *v_chosen;
	XVisualInfo vTemplate;
	int	visualsMatched;
	int	mdepth;
	int	needs_cmap;
	int	visual_chosen;
	int	match8, match24;
	int	i;

	vTemplate.screen = screen_num;
	vTemplate.depth = depth;

	visualList = XGetVisualInfo(g_display,
		(VisualScreenMask | VisualDepthMask),
		&vTemplate, &visualsMatched);

	vid_printf("visuals matched: %d\n", visualsMatched);
	if(visualsMatched == 0) {
		return (Visual *)0;
	}

	visual_chosen = -1;
	needs_cmap = 0;
	for(i = 0; i < visualsMatched; i++) {
		printf("Visual %d\n", i);
		printf("	id: %08x, screen: %d, depth: %d, class: %d\n",
			(word32)visualList[i].visualid,
			visualList[i].screen,
			visualList[i].depth,
			visualList[i].class);
		printf("	red: %08lx, green: %08lx, blue: %08lx\n",
			visualList[i].red_mask,
			visualList[i].green_mask,
			visualList[i].blue_mask);
		printf("	cmap size: %d, bits_per_rgb: %d\n",
			visualList[i].colormap_size,
			visualList[i].bits_per_rgb);
		match8 = (visualList[i].class == PseudoColor);
		match24 = (visualList[i].class == TrueColor);
		if((depth == 8) && match8) {
			visual_chosen = i;
			Max_color_size = visualList[i].colormap_size;
			needs_cmap = 1;
			break;
		}
		if((depth != 8) && match24) {
			visual_chosen = i;
			Max_color_size = -1;
			needs_cmap = 0;
			break;
		}
	}

	if(visual_chosen < 0) {
		printf("Couldn't find any good visuals at depth %d!\n",
			depth);
		return (Visual *)0;
	}

	printf("Chose visual: %d, max_colors: %d\n", visual_chosen,
		Max_color_size);

	v_chosen = &(visualList[visual_chosen]);
	x_set_mask_and_shift(v_chosen->red_mask, &g_red_mask,
				&g_red_left_shift, &g_red_right_shift);
	x_set_mask_and_shift(v_chosen->green_mask, &g_green_mask,
				&g_green_left_shift, &g_green_right_shift);
	x_set_mask_and_shift(v_chosen->blue_mask, &g_blue_mask,
				&g_blue_left_shift, &g_blue_right_shift);

	g_screen_depth = depth;
	mdepth = depth;
	if(depth > 8) {
		mdepth = 16;
	}
	if(depth > 16) {
		mdepth = 32;
	}
	g_screen_mdepth = mdepth;
	g_needs_cmap = needs_cmap;
	*visual_list_ptr = visualList;

	return v_chosen->visual;
}

void
x_set_mask_and_shift(word32 x_mask, word32 *mask_ptr, int *shift_left_ptr,
		int *shift_right_ptr)
{
	int	shift;
	int	i;

	/* Shift until we find first set bit in mask, then remember mask,shift*/

	shift = 0;
	for(i = 0; i < 32; i++) {
		if(x_mask & 1) {
			/* we're done! */
			break;
		}
		x_mask = x_mask >> 1;
		shift++;
	}
	*mask_ptr = x_mask;
	*shift_left_ptr = shift;
	/* Now, calculate shift_right_ptr */
	shift = 0;
	x_mask |= 1;		// make sure at least one bit is set
	while(x_mask < 0x80) {
		shift++;
		x_mask = x_mask << 1;
	}

	*shift_right_ptr = shift;
	return;

}

int g_xshm_error = 0;

int
xhandle_shm_error(Display *display, XErrorEvent *event)
{
	g_xshm_error = 1;
	return 0;
}

void
x_get_kimage(Kimage *kimage_ptr) {
	if(g_use_shmem) {
		g_use_shmem = get_shm(kimage_ptr);
	}
	if(!g_use_shmem) {
		get_ximage(kimage_ptr);
	}
}

int
get_shm(Kimage *kimage_ptr)
{
#ifdef X_SHARED_MEM
	XShmSegmentInfo *seginfo;
	XImage *xim;
	int	(*old_x_handler)(Display *, XErrorEvent *);
	int	width;
	int	height;
	int	depth;

	width = kimage_ptr->width_req;
	height = kimage_ptr->height;
	depth = kimage_ptr->depth;

	seginfo = (XShmSegmentInfo *)malloc(sizeof(XShmSegmentInfo));
	xim = XShmCreateImage(g_display, g_vis, depth, ZPixmap,
		(char *)0, seginfo, width, height);

	/* check mdepth! */
	if(xim->bits_per_pixel != kimage_ptr->mdepth) {
		printf("get_shm bits_per_pix: %d != %d\n",
				xim->bits_per_pixel, g_screen_mdepth);
	}

	vid_printf("xim: %p\n", xim);
	kimage_ptr->dev_handle = xim;
	kimage_ptr->dev_handle2 = seginfo;
	if(xim == 0) {
		return 0;
	}

	/* It worked, we got it */
	seginfo->shmid = shmget(IPC_PRIVATE, xim->bytes_per_line * xim->height,
		IPC_CREAT | 0777);
	vid_printf("seginfo->shmid = %d\n", seginfo->shmid);
	if(seginfo->shmid < 0) {
		XDestroyImage(xim);
		return 0;
	}

	/* Still working */
	seginfo->shmaddr = (char *)shmat(seginfo->shmid, 0, 0);
	vid_printf("seginfo->shmaddr: %p\n", seginfo->shmaddr);
	if(seginfo->shmaddr == ((char *) -1)) {
		XDestroyImage(xim);
		return 0;
	}

	/* Still working */
	xim->data = seginfo->shmaddr;
	seginfo->readOnly = False;

	/* XShmAttach will trigger X error if server is remote, so catch it */
	g_xshm_error = 0;
	old_x_handler = XSetErrorHandler(xhandle_shm_error);

	XShmAttach(g_display, seginfo);
	XSync(g_display, False);


	vid_printf("about to RMID the shmid\n");
	shmctl(seginfo->shmid, IPC_RMID, 0);

	XFlush(g_display);
	XSetErrorHandler(old_x_handler);

	if(g_xshm_error) {
		XDestroyImage(xim);
		/* We could release the shared mem segment, but by doing the */
		/* RMID, it will go away when we die now, so just leave it */
		printf("Not using shared memory\n");
		return 0;
	}

	kimage_ptr->data_ptr = (byte *)xim->data;
	vid_printf("Sharing memory. xim: %p, xim->data: %p\n", xim, xim->data);

	return 1;
#else
	return 0;		/* No shared memory */
#endif	/* X_SHARED_MEM */
}

void
get_ximage(Kimage *kimage_ptr)
{
	XImage	*xim;
	byte	*ptr;
	int	width;
	int	height;
	int	depth;
	int	mdepth;

	width = kimage_ptr->width_req;
	height = kimage_ptr->height;
	depth = kimage_ptr->depth;
	mdepth = kimage_ptr->mdepth;

	ptr = (byte *)malloc((width * height * mdepth) >> 3);

	vid_printf("ptr: %p\n", ptr);

	if(ptr == 0) {
		printf("malloc for data failed, mdepth: %d\n", mdepth);
		exit(2);
	}

	kimage_ptr->data_ptr = ptr;

	xim = XCreateImage(g_display, g_vis, depth, ZPixmap, 0,
		(char *)ptr, width, height, 8, 0);

#ifdef KEGS_LITTLE_ENDIAN
	xim->byte_order = LSBFirst;
#else
	xim->byte_order = MSBFirst;
#endif
	_XInitImageFuncPtrs(xim);	/* adjust to new byte order */

	/* check mdepth! */
	if(xim->bits_per_pixel != mdepth) {
		printf("shm_ximage bits_per_pix: %d != %d\n",
					xim->bits_per_pixel, mdepth);
	}

	vid_printf("xim: %p\n", xim);

	kimage_ptr->dev_handle = xim;

	return;
}


void
x_redraw_status_lines()
{
	char	*buf;
	int	line;
	int	height;
	int	margin;
	word32	white, black;

	height = g_text_FontSt->ascent + g_text_FontSt->descent;
	margin = g_text_FontSt->ascent;

	white = (g_a2vid_palette << 4) + 0xf;
	black = (g_a2vid_palette << 4) + 0x0;
	if(g_screen_depth != 8) {
		white = (2 << (g_screen_depth - 1)) - 1;
		black = 0;
	}
	XSetForeground(g_display, g_a2_winGC, white);
	XSetBackground(g_display, g_a2_winGC, black);

	for(line = 0; line < MAX_STATUS_LINES; line++) {
		buf = g_status_ptrs[line];
		if(buf == 0) {
			/* skip it */
			continue;
		}
		XDrawImageString(g_display, g_a2_win, g_a2_winGC, 0,
			X_A2_WINDOW_HEIGHT + height*line + margin,
			buf, strlen(buf));
	}

	XFlush(g_display);
}



void
x_push_kimage(Kimage *kimage_ptr, int destx, int desty, int srcx, int srcy,
	int width, int height)
{
	XImage	*xim;

	xim = (XImage *)kimage_ptr->dev_handle;

#ifdef X_SHARED_MEM
	if(g_use_shmem) {
		XShmPutImage(g_display, g_a2_win, g_a2_winGC, xim,
			srcx, srcy, destx, desty, width, height, False);
	}
#endif
	if(!g_use_shmem) {
		XPutImage(g_display, g_a2_win, g_a2_winGC, xim,
			srcx, srcy, destx, desty, width, height);
	}
}

void
x_push_done()
{
	XFlush(g_display);
}


#define KEYBUFLEN	128

int g_num_check_input_calls = 0;
int g_check_input_flush_rate = 2;

int
x_update_mouse(int raw_x, int raw_y, int button_states, int buttons_valid)
{
	int	x, y;

	x = raw_x - BASE_MARGIN_LEFT;
	y = raw_y - BASE_MARGIN_TOP;

	if(g_warp_pointer && (x == A2_WINDOW_WIDTH/2) &&
			(y == A2_WINDOW_HEIGHT/2) && (buttons_valid == 0) ) {
		/* tell adb routs to recenter but ignore this motion */
		update_mouse(x, y, 0, -1);
		return 0;
	}
	return update_mouse(x, y, button_states, buttons_valid & 7);
}

void
check_input_events()
{
	XEvent	ev;
	int	len;
	int	motion;
	int	buttons;
	int	refresh_needed;

	g_num_check_input_calls--;
	if(g_num_check_input_calls < 0) {
		len = XPending(g_display);
		g_num_check_input_calls = g_check_input_flush_rate;
	} else {
		len = QLength(g_display);
	}

	motion = 0;
	refresh_needed = 0;
	while(len > 0) {
		XNextEvent(g_display, &ev);
		len--;
		if(len == 0) {
			/* Xaccel on linux only buffers one X event */
			/*  must look for more now */
			len = XPending(g_display);
		}
		switch(ev.type) {
		case FocusIn:
		case FocusOut:
			if(ev.xfocus.type == FocusOut) {
				/* Allow keyrepeat again! */
				vid_printf("Left window, auto repeat on\n");
				x_auto_repeat_on(0);
				g_has_focus = 0;
			} else if(ev.xfocus.type == FocusIn) {
				/* Allow keyrepeat again! */
				vid_printf("Enter window, auto repeat off\n");
				x_auto_repeat_off(0);
				g_has_focus = 1;
			}
			break;
		case EnterNotify:
		case LeaveNotify:
			/* These events are disabled now */
			printf("Enter/Leave event for winow %08x, sub: %08x\n",
				(word32)ev.xcrossing.window,
				(word32)ev.xcrossing.subwindow);
			printf("Enter/L mode: %08x, detail: %08x, type:%02x\n",
				ev.xcrossing.mode, ev.xcrossing.detail,
				ev.xcrossing.type);
			break;
		case ButtonPress:
			vid_printf("Got button press of button %d!\n",
						ev.xbutton.button);
			buttons = (1 << ev.xbutton.button) >> 1;
			motion |= x_update_mouse(ev.xbutton.x, ev.xbutton.y,
				buttons, buttons & 7);

			break;
		case ButtonRelease:
			buttons = (1 << ev.xbutton.button) >> 1;
			motion |= x_update_mouse(ev.xbutton.x, ev.xbutton.y, 0,
								buttons & 7);
			break;
		case MotionNotify:
			if(ev.xmotion.window != g_a2_win) {
				printf("Motion in window %08x unknown!\n",
					(word32)ev.xmotion.window);
			}
			motion |= x_update_mouse(ev.xmotion.x, ev.xmotion.y, 0,
								0);
			break;
		case Expose:
			refresh_needed = -1;
			break;
		case NoExpose:
			/* do nothing */
			break;
		case KeyPress:
		case KeyRelease:
			handle_keysym(&ev);
			break;
		case KeymapNotify:
			break;
		case ColormapNotify:
			vid_printf("ColormapNotify for %08x\n",
				(word32)(ev.xcolormap.window));
			vid_printf("colormap: %08x, new: %d, state: %d\n",
				(word32)ev.xcolormap.colormap,
				ev.xcolormap.new, ev.xcolormap.state);
			break;
		default:
			printf("X event 0x%08x is unknown!\n",
				ev.type);
			break;
		}
	}

	if(motion && g_warp_pointer) {
		XWarpPointer(g_display, None, g_a2_win, 0, 0, 0, 0,
			BASE_MARGIN_LEFT + (A2_WINDOW_WIDTH/2),
			BASE_MARGIN_TOP + (A2_WINDOW_HEIGHT/2));
	}

	if(refresh_needed) {
		printf("Full refresh needed\n");
		g_a2_screen_buffer_changed = -1;
		g_full_refresh_needed = -1;

		g_border_sides_refresh_needed = 1;
		g_border_special_refresh_needed = 1;
		g_status_refresh_needed = 1;

		/* x_refresh_ximage(); */
		/* redraw_border(); */
	}

}

void
x_hide_pointer(int do_hide)
{
	if(do_hide) {
		XDefineCursor(g_display, g_a2_win, g_cursor);
	} else {
		XDefineCursor(g_display, g_a2_win, None);
	}
}


void
handle_keysym(XEvent *xev_in)
{
	KeySym	keysym;
	word32	state;
	int	keycode;
	int	a2code;
	int	type;
	int	is_up;

	keycode = xev_in->xkey.keycode;
	type = xev_in->xkey.type;

	keysym = XLookupKeysym(&(xev_in->xkey), 0);

	state = xev_in->xkey.state;

	vid_printf("keycode: %d, type: %d, state:%d, sym: %08x\n",
		keycode, type, state, (word32)keysym);

	x_update_modifier_state(state);

	is_up = 0;
	if(type == KeyRelease) {
		is_up = 1;
	}

#if 0
	if(keysym == XK_Alt_L || keysym == XK_Meta_L) {
		g_alt_left_up = is_up;
	}

	if(keysym == XK_Alt_R || keysym == XK_Meta_R) {
		g_alt_right_up = is_up;
	}

	if(g_alt_left_up == 0 && g_alt_right_up == 0) {
		printf("Sending sound to file\n");
		g_send_sound_to_file = 1;
	} else {
		if(g_send_sound_to_file) {
			printf("Stopping sending sound to file\n");
			close_sound_file();
		}
		g_send_sound_to_file = 0;
	}
#endif

	/* first, do conversions */
	switch(keysym) {
	case XK_Alt_R:
	case XK_Meta_R:
	case XK_Super_R:
	case XK_Mode_switch:
	case XK_Cancel:
		keysym = XK_Print;		/* option */
		break;
	case XK_Alt_L:
	case XK_Meta_L:
	case XK_Super_L:
	case XK_Menu:
		keysym = XK_Scroll_Lock;	/* cmd */
		break;
	case 0x1000003:
		if(keycode == 0x3c) {
			/* enter key on Mac OS X laptop--make it option */
			keysym = XK_Print;
		}
		break;
	case NoSymbol:
		switch(keycode) {
		/* 94-95 are for my PC101 kbd + windows keys on HPUX */
		case 0x0095:
			/* left windows key = option */
			keysym = XK_Print;
			break;
		case 0x0096:
		case 0x0094:
			/* right windows key = cmd */
			keysym = XK_Scroll_Lock;
			break;
		/* 0072 is for cra@WPI.EDU who says it's Break under XFree86 */
		case 0x0072:
		/* 006e is break according to mic@research.nj.nec.com */
		case 0x006e:
			keysym = XK_Break;
			break;

		/* 0x0042, 0x0046, and 0x0048 are the windows keys according */
		/*  to Geoff Weiss on Solaris x86 */
		case 0x0042:
		case 0x0046:
			/* flying windows == open apple */
			keysym = XK_Scroll_Lock;
			break;
		case 0x0048:
		case 0x0076:		/* Windows menu key on Mac OS X */
			/* menu windows == option */
			keysym = XK_Print;
			break;
		}
	}

	a2code = x_keysym_to_a2code(keysym, is_up);
	if(a2code >= 0) {
		adb_physical_key_update(a2code, is_up);
	} else if(a2code != -2) {
		printf("Keysym: %04x of keycode: %02x unknown\n",
			(word32)keysym, keycode);
	}
}

int
x_keysym_to_a2code(int keysym, int is_up)
{
	int	i;

	if(keysym == 0) {
		return -1;
	}

	if((keysym == XK_Shift_L) || (keysym == XK_Shift_R)) {
		if(is_up) {
			g_x_shift_control_state &= ~ShiftMask;
		} else {
			g_x_shift_control_state |= ShiftMask;
		}
	}
	if(keysym == XK_Caps_Lock) {
		if(is_up) {
			g_x_shift_control_state &= ~LockMask;
		} else {
			g_x_shift_control_state |= LockMask;
		}
	}
	if((keysym == XK_Control_L) || (keysym == XK_Control_R)) {
		if(is_up) {
			g_x_shift_control_state &= ~ControlMask;
		} else {
			g_x_shift_control_state |= ControlMask;
		}
	}

	/* Look up Apple 2 keycode */
	for(i = g_num_a2_keycodes - 1; i >= 0; i--) {
		if((keysym == a2_key_to_xsym[i][1]) ||
					(keysym == a2_key_to_xsym[i][2])) {

			vid_printf("Found keysym:%04x = a[%d] = %04x or %04x\n",
				(int)keysym, i, a2_key_to_xsym[i][1],
				a2_key_to_xsym[i][2]);

			return a2_key_to_xsym[i][0];
		}
	}

	return -1;
}

void
x_update_modifier_state(int state)
{
	int	state_xor;
	int	is_up;

	state = state & (ControlMask | LockMask | ShiftMask);
	state_xor = g_x_shift_control_state ^ state;
	is_up = 0;
	if(state_xor & ControlMask) {
		is_up = ((state & ControlMask) == 0);
		adb_physical_key_update(0x36, is_up);
	}
	if(state_xor & LockMask) {
		is_up = ((state & LockMask) == 0);
		adb_physical_key_update(0x39, is_up);
	}
	if(state_xor & ShiftMask) {
		is_up = ((state & ShiftMask) == 0);
		adb_physical_key_update(0x38, is_up);
	}

	g_x_shift_control_state = state;
}

void
x_auto_repeat_on(int must)
{
	if((g_auto_repeat_on <= 0) || must) {
		g_auto_repeat_on = 1;
		XAutoRepeatOn(g_display);
		XFlush(g_display);
		adb_kbd_repeat_off();
	}
}

void
x_auto_repeat_off(int must)
{
	if((g_auto_repeat_on != 0) || must) {
		XAutoRepeatOff(g_display);
		XFlush(g_display);
		g_auto_repeat_on = 0;
		adb_kbd_repeat_off();
	}
}

void
x_full_screen(int do_full)
{
	return;
}
