ref: 3224333549a381765671e17c31043211ac6bd4c4
dir: /plan9.c/
#include <npe.h> #include <thread.h> #include <draw.h> #include <mouse.h> #include <keyboard.h> #include <control.h> #undef PI #include "puzzles.h" #ifdef COMBINED #error Plan 9 should not be COMBINED #endif //#define DIRECTDRAW //#define PROFILE struct frontend { Image *image; midend *me; Image *background; Image **colors; int ncolors; Point ZP; Controlset *cs; Channel *c; int showframe; int timeractive; }; struct blitter { Image *blimg; }; frontend *fe = nil; struct proftimes { long draw; } ptimes; enum { GAME = 0, SETTINGS = 1, }; void frontend_default_colour(frontend *, 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) { fe->timeractive = 0; } void activate_timer(frontend *fe) { fe->timeractive = 1; } 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); } char *showcmd = "c_game show"; static void p9_draw_text(void *handle, int x, int y, int fonttype, int fontsize, int align, int color, const char *text) { // todo: align, fontsize, fonttype frontend *fe = (frontend*)handle; #ifdef DIRECTDRAW string(screen, addpt(Pt(x, y), fe->ZP), fe->colors[color], ZP, font, text); #else string(fe->image, Pt(x, y), fe->colors[color], ZP, font, text); #endif } static void p9_draw_rect(void *handle, int x, int y, int w, int h, int color) { frontend *fe = (frontend*)handle; #ifdef DIRECTDRAW draw(screen, rectaddpt(Rect(x, y, x+w, y+h), fe->ZP), fe->colors[color], nil, ZP); #else draw(fe->image, Rect(x, y, x+w, y+h), fe->colors[color], nil, ZP); #endif } static void p9_draw_line(void *handle, int x1, int y1, int x2, int y2, int color) { frontend *fe = (frontend*)handle; #ifdef DIRECTDRAW line(screen, addpt(Pt(x1, y1), fe->ZP), addpt(Pt(x2, y2), fe->ZP), Endsquare, Endsquare, 0, fe->colors[color], ZP); #else line(fe->image, Pt(x1, y1), Pt(x2, y2), Endsquare, Endsquare, 0, fe->colors[color], ZP); #endif } static void p9_draw_thick_line(void *handle, float thickness, float x1, float y1, float x2, float y2, int color) { frontend *fe = (frontend*)handle; #ifdef DIRECTDRAW line(screen, addpt(Pt(x1, y1), fe->ZP), addpt(Pt(x2, y2), fe->ZP), Endsquare, Endsquare, thickness-1, fe->colors[color], ZP); #else line(fe->image, Pt(x1, y1), Pt(x2, y2), Endsquare, Endsquare, thickness-1, fe->colors[color], ZP); #endif } 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++) { #ifdef DIRECTDRAW points[i].x = coords[i*2+0] + fe->ZP.x; points[i].y = coords[i*2+1] + fe->ZP.y; #else points[i].x = coords[i*2+0]; points[i].y = coords[i*2+1]; #endif } #ifdef DIRECTDRAW if (fillcolor > 0) fillpoly(screen, points, npoints, 0, fe->colors[fillcolor], ZP); if (outlinecolor > 0) poly(screen, points, npoints, Endsquare, Endsquare, 1, fe->colors[outlinecolor], ZP); #else if (fillcolor > 0) fillpoly(fe->image, points, npoints, 0, fe->colors[fillcolor], ZP); if (outlinecolor > 0) poly(fe->image, points, npoints, Endsquare, Endsquare, 1, fe->colors[outlinecolor], ZP); #endif free(points); } static void p9_draw_circle(void *handle, int cx, int cy, int radius, int fillcolor, int outlinecolor) { frontend *fe = (frontend*)handle; #ifdef DIRECTDRAW Point c = addpt(Pt(cx, cy), fe->ZP); fillellipse(screen, c, radius, radius, fe->colors[fillcolor], ZP); ellipse(screen, c, radius, radius, 0, fe->colors[outlinecolor], ZP); #else Point c = Pt(cx, cy); fillellipse(fe->image, c, radius, radius, fe->colors[fillcolor], ZP); ellipse(fe->image, c, radius, radius, 0, fe->colors[outlinecolor], ZP); #endif } static void p9_draw_update(void *handle, int x, int y, int w, int h) { USED(handle, x, y, w, h); //frontend *fe = (frontend*)handle; //print("draw_update\n"); //chanprint(fe->cs->ctl, showcmd); } static void p9_clip(void *handle, int x, int y, int w, int h) { frontend *fe = (frontend*)handle; fe->image->clipr = Rect(x, y, x + w, y + h); } static void p9_unclip(void *handle) { frontend *fe = (frontend*)handle; fe->image->clipr = fe->image->r; } long drawtime; static void p9_start_draw(void *handle) { USED(handle); #ifdef PROFILE drawtime = times(nil); #endif } static void p9_end_draw(void *handle) { frontend *fe = (frontend*)handle; chanprint(fe->cs->ctl, showcmd); #ifdef PROFILE ptimes.draw = times(nil) - drawtime; #endif } static void p9_status_bar(void *handle, const char *text) { frontend *fe = (frontend*)handle; chanprint(fe->cs->ctl, "l_status value %q", text); } static blitter* p9_blitter_new(void *handle, int w, int h) { blitter *bl; USED(handle); bl = malloc(sizeof(blitter)); bl->blimg = allocimage(display, Rect(0, 0, w, h), screen->chan, 0, 0); return bl; } static void p9_blitter_free(void *handle, blitter *bl) { USED(handle); freeimage(bl->blimg); free(bl); } static void p9_blitter_save(void *handle, blitter *bl, int x, int y) { frontend *fe = (frontend*)handle; draw(bl->blimg, Rect(x, y, x + bl->blimg->r.max.x, y + bl->blimg->r.max.y), fe->image, nil, Pt(x, y)); // fix ZP if needed } static void p9_blitter_load(void *handle, blitter *bl, int x, int y) { frontend *fe = (frontend*)handle; draw(fe->image, Rect(x, y, x + bl->blimg->r.max.x, y + bl->blimg->r.max.y), bl->blimg, nil, Pt(x, 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 initui(Controlset *cs, Channel *c) { Control *b_game, *b_settings, *c_game, *c_settings, *stackmain; Point p; createrow(cs, "rowmain"); stackmain = createstack(cs, "stackmain"); chanprint(cs->ctl, "stackmain border 1"); controlwire(stackmain, "event", c); b_game = createtextbutton(cs, "b_game"); p = stringsize(font, "game"); chanprint(cs->ctl, "b_game border 1"); chanprint(cs->ctl, "b_game align center"); chanprint(cs->ctl, "b_game text game"); chanprint(cs->ctl, "b_game size %d %d 500 %d", p.x, p.y, p.y); c_game = createbox(cs, "c_game"); chanprint(cs->ctl, "c_game border 1"); #ifdef DIRECTDRAW chanprint(cs->ctl, "c_game image background"); #else chanprint(cs->ctl, "c_game image frame"); #endif controlwire(c_game, "event", c); controlwire(b_game, "event", c); b_settings = createtextbutton(cs, "b_settings"); p = stringsize(font, "settings"); chanprint(cs->ctl, "b_settings border 1"); chanprint(cs->ctl, "b_settings align center"); chanprint(cs->ctl, "b_settings text settings"); chanprint(cs->ctl, "b_settings size %d %d 500 %d", p.x, p.y, p.y); c_settings = createcolumn(cs, "c_settings"); controlwire(c_settings, "event", c); controlwire(b_settings, "event", c); createlabel(cs, "l_status"); chanprint(cs->ctl, "stackmain add c_game c_settings"); chanprint(cs->ctl, "rowmain add b_game\nrowmain add b_settings"); activate(b_game); activate(b_settings); activate(c_game); } void initfe(frontend *fe, Mousectl *mousectl) { float *colors; int ncolors; int r, g, b; float bgcol[3]; Channel *c, *d; fe->image = allocimage(display, screen->r, screen->chan, 0, 0); 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); c = chancreate(sizeof(Mouse), 0); d = chancreate(sizeof(Rune), 0); fe->cs = newcontrolset(screen, d, c, mousectl->resizec); fe->c = chancreate(sizeof(char*), 0); ctldeletequits = 1; namectlimage(fe->image, "frame"); namectlimage(display->black, "i_black"); namectlimage(display->white, "i_white"); namectlimage(fe->background, "background"); initui(fe->cs, fe->c); } int windowset = 0; Point resize(int *resizenop) { int x, y, fd; x = Dx(screen->r); y = Dy(screen->r) - 2*font->height; midend_size(fe->me, &x, &y, 1, 1.); // to test if (0 && !windowset) { fd = open("/dev/wctl", OWRITE); if (fd >= 0) { fprint(fd, "resize -dx %d -dy %d\n", x+2, y + 2*font->height); close(fd); windowset = 1; } } else windowset = 0; /* do not resize if we're waiting for the window resize */ *resizenop = windowset; return Pt(x, y); } void resizecontrolset(Controlset *cs) { Rectangle rmenu, rarea, sarea; int resizenop; Control *ctl; Point newsize; if (getwindow(display, Refnone) < 0) { sysfatal("resize failed: %r"); } rmenu = screen->r; rmenu.max.y = rmenu.min.y + font->height; rarea = screen->r; rarea.min.y = rmenu.min.y + font->height; rarea.max.y = rarea.max.y - font->height; sarea = screen->r; sarea.min.y = sarea.max.y - font->height; if (fe->image) { freeimage(fe->image); fe->image = nil; } newsize = resize(&resizenop); fe->image = allocimage(display, Rect(0, 0, newsize.x, newsize.y), screen->chan, 0, 0); if (0 && resizenop) return; draw(screen, screen->r, fe->background, nil, ZP); #ifndef DIRECTDRAW midend_force_redraw(fe->me); #endif chanprint(cs->ctl, "rowmain rect %R\nrowmain show", rmenu); chanprint(cs->ctl, "c_game rect %R\nc_settings rect %R", rarea, rarea); chanprint(cs->ctl, "stackmain rect %R\nstackmain show", rarea); chanprint(cs->ctl, "stackmain reveal %d", fe->showframe); chanprint(cs->ctl, "l_status rect %R\nl_status show", sarea); #ifdef DIRECTDRAW if (fe->showframe == GAME) midend_force_redraw(fe->me); #endif ctl = controlcalled("c_game"); fe->ZP = ctl->rect.min; } 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 showframe(int frame) { if (frame == GAME) midend_force_redraw(fe->me); fe->showframe = frame; chanprint(fe->cs->ctl, "stackmain reveal %d", frame); } int keyev(Rune k) { switch (k) { case 'q': case 127: return 1; /* return 1 to quit */ case 'n': midend_process_key(fe->me, 0, 0, UI_NEWGAME); chanprint(fe->cs->ctl, showcmd); break; default: if (midend_process_key(fe->me, 0, 0, k) == PKR_QUIT) return 1; } return 0; } void tick(float delta) { #ifdef PROFILE char msg[128]; #endif if (fe->timeractive) { midend_timer(fe->me, delta); } #ifdef PROFILE snprint(msg, 128, "draw: %ld", ptimes.draw); chanprint(fe->cs->ctl, "l_status value %q", msg); #endif } typedef struct Tickinfo Tickinfo; struct Tickinfo { long totaltime; long lasttick; float delta; }; void timerproc(void *v) { Channel *c; Tickinfo ti; long newtime; c = v; ti.totaltime = times(nil); for (;;) { sleep(20); newtime = times(nil); ti.delta = (newtime - ti.totaltime) / 1000.; ti.totaltime = newtime; send(c, &ti.delta); } } void processmouse(Mouse *m, int *lm) { int x, y, r; Control *c; if (fe->showframe != GAME) goto Ctrl; c = controlcalled("c_game"); if (!ptinrect(m->xy, c->rect)) goto Ctrl; x = m->xy.x - c->rect.min.x; y = m->xy.y - c->rect.min.y; r = -1; if ( ((*lm)&1) && !(m->buttons&1)) r = midend_process_key(fe->me, x, y, LEFT_RELEASE); if (!((*lm)&1) && (m->buttons&1)) r = midend_process_key(fe->me, x, y, LEFT_BUTTON); if ( ((*lm)&2) && !(m->buttons&2)) r = midend_process_key(fe->me, x, y, MIDDLE_RELEASE); if (!((*lm)&2) && (m->buttons&2)) r = midend_process_key(fe->me, x, y, MIDDLE_BUTTON); if ( ((*lm)&4) && !(m->buttons&4)) r = midend_process_key(fe->me, x, y, RIGHT_RELEASE); if (!((*lm)&4) && (m->buttons&2)) r = midend_process_key(fe->me, x, y, RIGHT_BUTTON); if (r >= 0) { chanprint(fe->cs->ctl, showcmd); } *lm = m->buttons; return; Ctrl: send(fe->cs->mousec, m); } void threadmain(int argc, char **argv) { int lastmouse; char *s, *args[6]; int doprintoptions = 0; char *wintitle; Channel *c; config_item *cfg; int changedprefs = 0; float delta; Mousectl *mousectl; Mouse m; Keyboardctl *keyboardctl; Rune rune; Alt a[] = { { nil, &s, CHANRCV }, { nil, &delta, CHANRCV }, { nil, &m, CHANRCV }, { nil, &rune, CHANRCV }, { nil, nil, CHANEND }, }; 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"); } mousectl = initmouse(nil, screen); keyboardctl = initkeyboard(nil); initcontrols(); initfe(fe, mousectl); midend_new_game(fe->me); resizecontrolset(fe->cs); a[0].c = fe->c; a[1].c = chancreate(sizeof(float), 0); a[2].c = mousectl->c; a[3].c = keyboardctl->c; proccreate(timerproc, a[1].c, 4096); for (;;) { switch (alt(a)) { case 0: /* libcontrol event channel */ tokenize(s, args, nelem(args)); if (strcmp(args[0], "c_settings:") == 0) { print("c_settings event: %s\n", args[1]); } else if (strcmp(args[0], "b_game:") == 0) { showframe(GAME); chanprint(fe->cs->ctl, "b_game value 0"); } else if (strcmp(args[0], "b_settings:") == 0) { showframe(SETTINGS); chanprint(fe->cs->ctl, "b_settings value 0"); } break; case 1: /* timer */ tick(delta); break; case 2: /* mouse */ processmouse(&m, &lastmouse); break; case 3: /* keyboard */ if (keyev(rune)) goto Out; break; } } Out: threadexitsall(nil); }