ref: cec6a8217fa2ae90a72226959e215b663c88bc41
dir: /plan9.c/
#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);
}
}
}