shithub: puzzles

ref: cec6a8217fa2ae90a72226959e215b663c88bc41
dir: /plan9.c/

View raw version
#include <npe.h>
#include <draw.h>
#include <event.h>

#undef PI
#include "puzzles.h"

#ifdef COMBINED
#error Plan 9 should not be COMBINED
#endif

struct frontend {
	Image *image;
	midend *me;
	Image *background;
	Image **colors;
	int ncolors;
	Point ZP;
	int topbarh;
};

frontend *fe;

void
frontend_default_colour(frontend *fe, float *output)
{
	output[0] = .9;
	output[1] = .9;
	output[2] = .9;
}

void
get_random_seed(void **randseed, int *randseedsize)
{
	long *t = malloc(sizeof(long));
	assert(t);
	time(t);
	*randseed = (void*)t;
	*randseedsize = sizeof(long);
}

void
deactivate_timer(frontend *fe)
{
}

void activate_timer(frontend *fe)
{
}

void fatal(const char *fmt, ...)
{
    va_list ap;

    fprint(2, "fatal error: ");

    va_start(ap, fmt);
    vfprint(2, fmt, ap);
    va_end(ap);

    fprint(2, "\n");
    sysfatal("error");
}

#ifdef DEBUGGING
void debug_printf(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    vfprint(1, fmt, ap);
    va_end(ap);
}
#endif

void
usage(void)
{
	fprint(2, "usage: %s [-ho] [--options]\n", argv0);
	exits(nil);
}

static void p9_draw_text(void *handle, int x, int y, int fonttype, int fontsize, int align, int color, const char *text)
{
	frontend *fe = (frontend*)handle;
	string(fe->image, addpt(fe->ZP, Pt(x, y)), fe->colors[color], ZP, font, text);
}

static void
p9_draw_rect(void *handle, int x, int y, int w, int h, int color)
{
	frontend *fe = (frontend*)handle;
	draw(fe->image, rectaddpt(Rect(x, y, x+w, y+h), fe->ZP), fe->colors[color], nil, ZP);
}

static void
p9_draw_line(void *handle, int x1, int y1, int x2, int y2, int color)
{
	frontend *fe = (frontend*)handle;
	line(fe->image, addpt(fe->ZP, Pt(x1, y1)), addpt(fe->ZP, Pt(x2, y2)), Endsquare, Endsquare, 1, fe->colors[color], ZP);
}

static void
p9_draw_thick_line(void *handle, float thickness, float x1, float y1, float x2, float y2, int color)
{
	frontend *fe = (frontend*)handle;
	line(fe->image, addpt(fe->ZP, Pt(x1, y1)), addpt(fe->ZP, Pt(x2, y2)), Endsquare, Endsquare, thickness, fe->colors[color], ZP);
}

static void
p9_draw_poly(void *handle, const int *coords, int npoints, int fillcolor, int outlinecolor)
{
	Point *points;
	frontend *fe = (frontend*)handle;
	
	points = malloc(npoints * sizeof(Point));
	for (int i = 0; i < npoints; i++) {
		points[i].x = coords[i*2+0] + fe->ZP.x;
		points[i].y = coords[i*2+1] + fe->ZP.y;
	}
	
	if (fillcolor > 0)
		fillpoly(fe->image, points, npoints, 0, fe->colors[fillcolor], fe->ZP);
	if (outlinecolor > 0)
		poly(fe->image, points, npoints, Endsquare, Endsquare, 1, fe->colors[outlinecolor], fe->ZP);
	
	free(points);
}

static void
p9_draw_circle(void *handle, int cx, int cy, int radius, int fillcolor, int outlinecolor)
{
	frontend *fe = (frontend*)handle;
	Point c = addpt(fe->ZP, Pt(cx, cy));
	fillellipse(fe->image, c, radius, radius, fe->colors[fillcolor], ZP);
	ellipse(fe->image, c, radius, radius, 0, fe->colors[outlinecolor], ZP);
}

static void
p9_draw_update(void *handle, int x, int y, int w, int h)
{
}

static void
p9_clip(void *handle, int x, int y, int w, int h)
{
}

static void
p9_unclip(void *handle)
{
}

static void
p9_start_draw(void *handle)
{
}

static void
p9_end_draw(void *handle)
{
}

static void
p9_status_bar(void *handle, const char *text)
{
	frontend *fe = (frontend*)handle;
	draw(fe->image, Rect(fe->ZP.x+10, fe->image->r.max.y-20, fe->image->r.max.x, fe->image->r.max.y), fe->background, nil, ZP);
	string(fe->image, Pt(fe->ZP.x+10, fe->image->r.max.y-20), display->black, ZP, font, text);
}

static blitter*
p9_blitter_new(void *handle, int w, int h)
{
	return nil;
}

static void
p9_blitter_free(void *handle, blitter *bl)
{
}

static void
p9_blitter_save(void *handle, blitter *bl, int x, int y)
{
}

static void
p9_blitter_load(void *handle, blitter *bl, int x, int y)
{
}

static const drawing_api p9_drawing = {
	p9_draw_text,
	p9_draw_rect,
	p9_draw_line,
	p9_draw_poly,
	p9_draw_circle,
	p9_draw_update,
	p9_clip,
	p9_unclip,
	p9_start_draw,
	p9_end_draw,
	p9_status_bar,
	p9_blitter_new,
	p9_blitter_free,
	p9_blitter_save,
	p9_blitter_load,
	nil, nil, nil, nil, nil, nil, nil, nil, /* {begin,end}_{doc,page,puzzle}, line_width, line_dotted */
	nil, /* text_fallback */
#ifdef NO_THICK_LINE
	nil,
#else
	p9_draw_thick_line,
#endif
};

static int rgb2col(int r, int g, int b)
{
	return (r<<24) | (g<<16) | (b<<8) | 0xFF;
}

static frontend*
new_window(void)
{
	frontend *fe;
	
	fe = mallocz(sizeof(frontend), 1);
	if (!fe)
		sysfatal("error: out of memory!");
	
	fe->me = midend_new(fe, &thegame, &p9_drawing, fe);
	
	return fe;
}

void
initfe(frontend *fe)
{
	float *colors;
	int ncolors;
	int r, g, b;
	int col;
	float bgcol[3];
	
	fe->topbarh = 20;
	fe->image = screen;
	fe->ZP = screen->r.min;
	fe->ZP.y += fe->topbarh;
	
	frontend_default_colour(fe, bgcol);
	fe->background = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, rgb2col(bgcol[0]*255., bgcol[1]*255., bgcol[2]*255.));
	
	colors = midend_colours(fe->me, &ncolors);
	fe->colors = mallocz(ncolors * sizeof(Image*), 1);
	for (int i = 0; i < ncolors; i++) {
		r = colors[i*3+0] * 255.;
		g = colors[i*3+1] * 255.;
		b = colors[i*3+2] * 255.;
		fe->colors[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, rgb2col(r, g, b));
	}
	free(colors);
}

void
resize(void)
{
	int x, y;
	x = screen->r.max.x - screen->r.min.x;
	y = screen->r.max.y - screen->r.min.y;
	
	midend_size(fe->me, &x, &y, 1, 1.);
}

void
redrawui(void)
{
	Point to = fe->ZP;
	to.x = fe->image->r.max.x;
	line(fe->image, fe->ZP, to, Endsquare, Endsquare, 0, display->black, ZP);
}

void
eresized(int new)
{
	if (new && getwindow(display, Refnone) < 0) {
		sysfatal("can't reattach to window: %r");
	}
	
	fe->image = screen;
	fe->ZP = screen->r.min;
	fe->ZP.y += fe->topbarh;
	draw(screen, screen->r, fe->background, nil, ZP);
	resize();
	midend_redraw(fe->me);
	redrawui();
}

void
keyboardev(int c, Event *ev)
{
	switch (c) {
	case 'q':
	case 127: /* DEL */
		exits(nil);
		break;
	default:
		if (c >= 0 && midend_process_key(fe->me, 0, 0, c) == PKR_QUIT) {
			exits(nil);
		}
	}
}

void
printoptions(config_item *c)
{
	char *t;
	char *s = nil, *cnames[16], *ckws[16];
	int n = 0, m;
	config_item *cfg = midend_get_config(fe->me, CFG_PREFS, &t);
	
	print("Options:\n");
	while (cfg->type != C_END) {
		switch (cfg->type) {
		case C_STRING:
			s = cfg->u.string.sval;
			break;
		case C_BOOLEAN:
			s = cfg->u.boolean.bval ? "1" : "0";
			break;
		case C_CHOICES:
			print("      Choices:\n");
			n = getfields(cfg->u.choices.choicenames, cnames, 16, 1, ":");
			m = getfields(cfg->u.choices.choicekws, ckws, 16, 1, ":");
			assert(n == m && cfg->u.choices.selected < n);
			s = ckws[cfg->u.choices.selected];
			break;
		}
		print("--%s=%s\n      %s\n", cfg->kw, s, cfg->name);
		if (cfg->type == C_CHOICES) {
			print("      Choices:\n");
			for (int i = 0; i < n; i++) {
				print("      %s: %s\n", ckws[i], cnames[i]);
			}
		}
		cfg++;
	}
}

void
parseoption(config_item *cfg, char *keyval)
{
	char *arg[2];
	int n;
	
	n = getfields(keyval, arg, 2, 1, "=");
	if (n != 2)
		usage(); // exits
	
	while (cfg && cfg->type != C_END)
		if (strcmp(cfg->kw, arg[0]) != 0)
			cfg++;
	
	if (!cfg || cfg->type == C_END) {
		fprint(2, "no valid option\n");
		return;
	}
	
	print("%s : %s\n", cfg->kw, cfg->name);
	return;
	
	switch (cfg->type) {
	case C_STRING:
		cfg->u.string.sval = arg[1];
		print("is string");
		break;
	case C_BOOLEAN:
		n = atoi(arg[1]);
		print("is boolean");
		cfg->u.boolean.bval = n ? 1 : 0;
		break;
	case C_CHOICES:
		// TODO
		print("is choices");
		fprint(2, "not implemented yet!\n");
		break;
	case C_END:
	default:
		print("not found");
		break;
	}
}

void
hittopbar(int x, int buttons)
{
	if (buttons&1) {
		print("left mouse\n");
	} else
	if (buttons&2) {
		print("middle mouse\n");
	} else
	if (buttons&4) {
		print("right mouse\n");
	} else /* no button at all */
		return;
	print("pos: %d\n", x);
}

void
main(int argc, char **argv)
{
	Event ev;
	int e;
	int x, y;
	char *s;
	int doprintoptions = 0;
	char *wintitle;
	config_item *cfg;
	int changedprefs = 0;
	
	fe = new_window();
	
	wintitle = nil;
	cfg = midend_get_config(fe->me, CFG_SETTINGS, &wintitle);
	
	ARGBEGIN{
	case 'h':
		usage();
		break;
	case 'o':
		doprintoptions++;
		break;
	case '-':
		parseoption(cfg, ARGF());
		changedprefs++;
		break;
	}ARGEND;
	
	if (changedprefs) {	
		s = midend_set_config(fe->me, CFG_SETTINGS, cfg);
		if (s) {
			fprint(2, "error: %s\n", s);
			exits("error");
		}
	}
	
	if (doprintoptions) {
		printoptions(cfg);
		exits(nil);
	}
	
	if (initdraw(nil, nil, wintitle) < 0) {
		sysfatal("initdraw failed: %r");
	}
	
	initfe(fe);
	midend_new_game(fe->me);
	einit(Emouse|Ekeyboard);
	eresized(0);
	
	for (;;) {
		switch (event(&ev)) {
		case Emouse:
			x = ev.mouse.xy.x - fe->ZP.x;
			y = ev.mouse.xy.y - fe->ZP.y;
			if (y < fe->topbarh) {
				hittopbar(x, ev.mouse.buttons);
			}
			if (ev.mouse.buttons&1) {
				midend_process_key(fe->me, x, y, LEFT_BUTTON);
			} else
			if (ev.mouse.buttons&2) {
				midend_process_key(fe->me, x, y, MIDDLE_BUTTON);
			} else
			if (ev.mouse.buttons&4) {
				midend_process_key(fe->me, x, y, RIGHT_BUTTON);
			}
		case Ekeyboard:
			keyboardev(ev.kbdc, &ev);
		}
	}
}