/*
 *	Graphics support routines for T3X programs under Unix/X11.
 *	Copyright (C) 1997,1998 Nils M. Holm
 *	See the file LICENSE for conditions of use.
 */

#ifdef GRAPHICS_EXT

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

#include "gfx.h"


Display		*Dsp;
Window		Win;
GC		Ctx, Ctx_set, Ctx_clear, Ctx_invert;
int		Reshaped;
int		Fx, Fy;
int		Xn, Yn;
int		M_shift, M_control;


void adjust(width, height) {
	Fx = XLIM * 2 / width;
	Fy = YLIM * 2 / height;
}


int g_reshaped() {
	int	r = Reshaped;

	Reshaped = 0;
	return(r);
}


int _g_init() {
	XEvent		e;
	XGCValues	v;
	int		i = -1;
	char		*dpyname;
	int		scrn;
	XSizeHints	*wm_size;
	XWMHints	*wm_hints;
	XClassHint	*wm_class;
	XTextProperty	wname;
	char		*title = "TXX";

	Xn = 600;
	Yn = 450;
	wm_size = XAllocSizeHints();
	wm_hints = XAllocWMHints();
	wm_class = XAllocClassHint();
	if (!(wm_size && wm_hints && wm_class)) g_error("Not enough memory");
	dpyname = getenv("DISPLAY");
	Dsp = XOpenDisplay(dpyname);
	scrn = DefaultScreen(Dsp);
	if (!Dsp) g_error("Could not open display");
	Win = XCreateSimpleWindow(Dsp, RootWindow(Dsp, scrn), 10, 10,
		Xn, Yn, 0, BlackPixel(Dsp, scrn), WhitePixel(Dsp, scrn));
	if (!Dsp) g_error("Could not create window");
	XSelectInput(Dsp, Win, KeyPressMask | KeyReleaseMask | ButtonPressMask
		| ButtonReleaseMask | ExposureMask | PointerMotionMask);
	v.plane_mask = AllPlanes;
	v.foreground = 0;
	v.background = -1;
	v.function = GXset;
	Ctx_set = XCreateGC(Dsp, Win, GCPlaneMask | GCForeground
		| GCBackground | GCFunction, &v);
	v.function = GXclear;
	Ctx_clear = XCreateGC(Dsp, Win, GCPlaneMask | GCForeground
		| GCBackground | GCFunction, &v);
	v.function = GXinvert;
	Ctx_invert = XCreateGC(Dsp, Win, GCPlaneMask | GCForeground
		| GCBackground | GCFunction, &v);
	XStringListToTextProperty(&title, 1, &wname);
	wm_hints->initial_state = NormalState;
	wm_hints->flags = StateHint;
	wm_class->res_name = title;
	wm_class->res_class = title;
	XSetWMProperties(Dsp, Win, &wname, &wname, &title, 1, wm_size,
		wm_hints, wm_class);
	XMapWindow(Dsp, Win);
	do  {
		XNextEvent(Dsp, &e);
	} while (e.type != Expose);
	adjust(Xn, Yn);
	Reshaped = 0;
	M_shift = M_control = 0;
	return(0);
}


void e_init() {
}


void g_end() {
	XFreeGC(Dsp, Ctx_set);
	XFreeGC(Dsp, Ctx_clear);
	XFreeGC(Dsp, Ctx_invert);
	XDestroyWindow(Dsp, Win);
	XFlush(Dsp);
	XCloseDisplay(Dsp);
}


void g_sync() {
	XFlush(Dsp);
}


void flags(f) {
	switch(f) {
	case 0:	Ctx = Ctx_set; break;
	case 1:	Ctx = Ctx_clear; break;
	case 2:	Ctx = Ctx_clear; break;
	case 3:	Ctx = Ctx_invert; break;
	default:g_error("Bad flags in graphics request");
	}
}


void g_point(x, y, f)
int	x, y, f;
{
	x = (x + XLIM) / Fx;
	y = (y + YLIM) / Fy;
	flags(f);
	XDrawPoint(Dsp, Win, Ctx, x, y);
}


void g_line(x, y, dx, dy, f) {
	x = (x + XLIM) / Fx;
	y = (y + YLIM) / Fy;
	dx = (dx + XLIM) / Fx;
	dy = (dy + YLIM) / Fy;
	flags(f);
	XDrawLine(Dsp, Win, Ctx, x, y, dx, dy);
}


void g_box(x, y, dx, dy, f) {
	int	t;

	x = (x + XLIM) / Fx;
	y = (y + YLIM) / Fy;
	dx = (dx + XLIM) / Fx;
	dy = (dy + YLIM) / Fy;
	if (x > dx) { t = x; x = dx; dx = t; }
	if (y > dy) { t = y; y = dy; dy = t; }
	flags(f);
	XFillRectangle(Dsp, Win, Ctx, x, y, dx-x, dy-y);
}


void g_clear(m) {
	g_box(-XLIM, -YLIM, XLIM, YLIM, m);
}


void g_wait(msec) {
	usleep(msec*1000);
}


int mapkey(k)
KeySym	k;
{
	switch (k) {
	case XK_Begin:
	case XK_Home:	return(327);
	case XK_End:	return(335);
	case XK_Left:	return(331);
	case XK_Right:	return(333);
	case XK_Up:	return(328);
	case XK_Down:	return(336);
	case XK_Prior:	return(329);
	case XK_Next:	return(337);
	case XK_Insert:	return(338);
	case XK_Delete:	return(339);
	case XK_F1:	return(315);
	case XK_F2:	return(316);
	case XK_F3:	return(317);
	case XK_F4:	return(318);
	case XK_F5:	return(319);
	case XK_F6:	return(320);
	case XK_F7:	return(321);
	case XK_F8:	return(322);
	case XK_F9:	return(323);
	case XK_F10:	return(324);
	}
	if (IsModifierKey(k)) return -1;
	if (k & 0xFF00) return(k&0x7f);
	return(k);
}


int g_event(e, block)
struct event	*e;
int		block;
{
	static int	mx = 0, my = 0;
	static int	buttons = 0;
	XEvent		xe;
	int		r;
	KeySym		k, k2;
	int		n;
	Drawable	d;

	XFlush(Dsp);
	e->x = mx;
	e->y = my;
	e->buttons = buttons;
	e->key = -1;
	if (!block && !XPending(Dsp)) return(0);
	r = 0;
	while (1) {
		XNextEvent(Dsp, &xe);
		switch (xe.type) {
		case MotionNotify:
			e->x = xe.xmotion.x;
			e->y = xe.xmotion.y;
			break;

		case ButtonPress:
			e->buttons = xe.xbutton.button == Button1? 1:
				xe.xbutton.button == Button2? 2:
				xe.xbutton.button == Button3? 4: 0;
			e->x = xe.xbutton.x;
			e->y = xe.xbutton.y;
			r = -1;
			break;

		case ButtonRelease:
			e->buttons = 0;
			e->x = xe.xbutton.x;
			e->y = xe.xbutton.y;
			r = -1;
			break;

		case KeyRelease:
			k = XKeycodeToKeysym(Dsp, xe.xkey.keycode, 0);
			switch (k) {
			case XK_Shift_L:
			case XK_Shift_R:
				M_shift = 0; continue;

			case XK_Control_L:
			case XK_Control_R:
				M_control = 0; continue;

			default:
				e->x = xe.xkey.x;
				e->y = xe.xkey.y;
			}
			continue;

		case KeyPress:
			k = XKeycodeToKeysym(Dsp, xe.xkey.keycode, 0);
			switch (k) {
			case XK_Shift_L:
			case XK_Shift_R:
				M_shift = 1; continue;

			case XK_Control_L:
			case XK_Control_R:
				M_control = 1; continue;

			default:
				if (M_control) {
					if (islower(k))
						k -= '`';
					else
						k -= '@';
				}
				else if (M_shift)
					k = XKeycodeToKeysym(Dsp,
						xe.xkey.keycode, 1);
				e->key = mapkey(k);
				e->x = xe.xkey.x;
				e->y = xe.xkey.y;
				r = -1;
			}
			break;

		case Expose:
			XGetGeometry(Dsp, Win, &d, &n, &n, &Xn, &Yn, &n, &n);
			adjust(Xn, Yn);
			Reshaped = 1;
			e->x = xe.xexpose.x;
			e->y = xe.xexpose.y;
			while (XCheckTypedEvent(Dsp, Expose, &xe))
				;
			break;
		}
		if (XPending(Dsp)) continue;
		if (!block || r || Reshaped) break;
	}
	mx = e->x * Fx - XLIM;
	my = e->y * Fy - YLIM;
	e->x = mx;
	e->y = my;
	buttons = e->buttons;
	return(r);
}

#endif

