From eb2ad6f1d0284c83b3cb5caf631c659441189d7d Mon Sep 17 00:00:00 2001 From: simon Date: Wed, 28 Apr 2004 12:07:15 +0000 Subject: [PATCH] Add a menu bar, in both Windows and GTK. In particular, game modules are now expected to provide a list of `presets' (game_params plus a name) which are selectable from the menu. This means I can play both Octahedron and Cube without recompiling in between :-) While I'm here, also enabled a Cygwin makefile, which Just Worked. git-svn-id: svn://svn.tartarus.org/sgt/puzzles@4158 cda61777-01e9-0310-a592-d414129be87e --- Recipe | 1 + cube.c | 64 ++++++++++++++++++++++++++++++----- gtk.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- midend.c | 42 ++++++++++++++++++++++- net.c | 59 ++++++++++++++++++++++++++++----- puzzles.h | 5 +++ windows.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 362 insertions(+), 23 deletions(-) diff --git a/Recipe b/Recipe index 31650a7..41962bb 100644 --- a/Recipe +++ b/Recipe @@ -10,6 +10,7 @@ !makefile gtk Makefile !makefile vc Makefile.vc +!makefile cygwin Makefile.cyg WINDOWS = windows user32.lib gdi32.lib COMMON = midend malloc diff --git a/cube.c b/cube.c index 8a19ae7..710845d 100644 --- a/cube.c +++ b/cube.c @@ -21,6 +21,7 @@ struct solid { int faces[MAXFACES * MAXORDER]; /* order*nfaces point indices */ float normals[MAXFACES * 3]; /* 3*npoints vector components */ float shear; /* isometric shear for nice drawing */ + float border; /* border required around arena */ }; static const struct solid tetrahedron = { @@ -41,7 +42,7 @@ static const struct solid tetrahedron = { 0.816496580928, -0.471404520791, 0.333333333334, 0.0, 0.0, -1.0, }, - 0.0 + 0.0, 0.3 }; static const struct solid cube = { @@ -57,7 +58,7 @@ static const struct solid cube = { { -1,0,0, 0,0,+1, +1,0,0, 0,0,-1, 0,-1,0, 0,+1,0 }, - 0.3 + 0.3, 0.5 }; static const struct solid octahedron = { @@ -84,7 +85,7 @@ static const struct solid octahedron = { 0.816496580928, -0.471404520791, -0.333333333334, 0.816496580928, 0.471404520791, 0.333333333334, }, - 0.0 + 0.0, 0.5 }; static const struct solid icosahedron = { @@ -132,7 +133,7 @@ static const struct solid icosahedron = { -0.57735026919, -0.333333333334, -0.745355992501, 0.57735026919, -0.333333333334, -0.745355992501, }, - 0.0 + 0.0, 0.8 }; enum { @@ -216,11 +217,58 @@ game_params *default_params(void) return ret; } +int game_fetch_preset(int i, char **name, game_params **params) +{ + game_params *ret = snew(game_params); + char *str; + + switch (i) { + case 0: + str = "Cube"; + ret->solid = CUBE; + ret->d1 = 4; + ret->d2 = 4; + break; + case 1: + str = "Tetrahedron"; + ret->solid = TETRAHEDRON; + ret->d1 = 2; + ret->d2 = 1; + break; + case 2: + str = "Octahedron"; + ret->solid = OCTAHEDRON; + ret->d1 = 2; + ret->d2 = 2; + break; + case 3: + str = "Icosahedron"; + ret->solid = ICOSAHEDRON; + ret->d1 = 3; + ret->d2 = 3; + break; + default: + sfree(ret); + return FALSE; + } + + *name = dupstr(str); + *params = ret; + return TRUE; +} + void free_params(game_params *params) { sfree(params); } +game_params *dup_params(game_params *params) +{ + game_params *ret = snew(game_params); + *ret = *params; /* structure copy */ + return ret; +} + static void enum_grid_squares(game_params *params, void (*callback)(void *, struct grid_square *), void *ctx) @@ -1083,8 +1131,8 @@ static struct bbox find_bbox(game_params *params) void game_size(game_params *params, int *x, int *y) { struct bbox bb = find_bbox(params); - *x = (bb.r - bb.l + 2) * GRID_SCALE; - *y = (bb.d - bb.u + 2) * GRID_SCALE; + *x = (bb.r - bb.l + 2*solids[params->solid]->border) * GRID_SCALE; + *y = (bb.d - bb.u + 2*solids[params->solid]->border) * GRID_SCALE; } float *game_colours(frontend *fe, game_state *state, int *ncolours) @@ -1110,8 +1158,8 @@ game_drawstate *game_new_drawstate(game_state *state) struct game_drawstate *ds = snew(struct game_drawstate); struct bbox bb = find_bbox(&state->params); - ds->ox = -(bb.l - 1) * GRID_SCALE; - ds->oy = -(bb.u - 1) * GRID_SCALE; + ds->ox = -(bb.l - state->solid->border) * GRID_SCALE; + ds->oy = -(bb.u - state->solid->border) * GRID_SCALE; return ds; } diff --git a/gtk.c b/gtk.c index b3b453f..102ed9c 100644 --- a/gtk.c +++ b/gtk.c @@ -206,6 +206,9 @@ static gint configure_area(GtkWidget *widget, frontend *fe = (frontend *)data; GdkGC *gc; + if (fe->pixmap) + gdk_pixmap_unref(fe->pixmap); + fe->pixmap = gdk_pixmap_new(widget->window, fe->w, fe->h, -1); gc = gdk_gc_new(fe->area->window); @@ -239,10 +242,56 @@ void activate_timer(frontend *fe) fe->timer_active = TRUE; } +static void menu_key_event(GtkMenuItem *menuitem, gpointer data) +{ + frontend *fe = (frontend *)data; + int key = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(menuitem), + "user-data")); + if (!midend_process_key(fe->me, 0, 0, key)) + gtk_widget_destroy(fe->window); +} + +static void menu_preset_event(GtkMenuItem *menuitem, gpointer data) +{ + frontend *fe = (frontend *)data; + game_params *params = + (game_params *)gtk_object_get_data(GTK_OBJECT(menuitem), "user-data"); + int x, y; + + midend_set_params(fe->me, params); + midend_new_game(fe->me, NULL); + midend_size(fe->me, &x, &y); + gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y); + fe->w = x; + fe->h = y; +} + +static GtkWidget *add_menu_item_with_key(frontend *fe, GtkContainer *cont, + char *text, int key) +{ + GtkWidget *menuitem = gtk_menu_item_new_with_label(text); + gtk_container_add(cont, menuitem); + gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", + GINT_TO_POINTER(key)); + gtk_signal_connect(GTK_OBJECT(menuitem), "activate", + GTK_SIGNAL_FUNC(menu_key_event), fe); + gtk_widget_show(menuitem); + return menuitem; +} + +static void add_menu_separator(GtkContainer *cont) +{ + GtkWidget *menuitem = gtk_menu_item_new(); + gtk_container_add(cont, menuitem); + gtk_widget_show(menuitem); +} + static frontend *new_window(void) { frontend *fe; - int x, y; + GtkBox *vbox; + GtkWidget *menubar, *menu, *menuitem; + int x, y, n; fe = snew(frontend); @@ -255,6 +304,55 @@ static frontend *new_window(void) #else gtk_window_set_policy(GTK_WINDOW(fe->window), FALSE, FALSE, TRUE); #endif + vbox = GTK_BOX(gtk_vbox_new(FALSE, 0)); + gtk_container_add(GTK_CONTAINER(fe->window), GTK_WIDGET(vbox)); + gtk_widget_show(GTK_WIDGET(vbox)); + + menubar = gtk_menu_bar_new(); + gtk_box_pack_start(vbox, menubar, FALSE, FALSE, 0); + gtk_widget_show(menubar); + + menuitem = gtk_menu_item_new_with_label("Game"); + gtk_container_add(GTK_CONTAINER(menubar), menuitem); + gtk_widget_show(menuitem); + + menu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu); + + add_menu_item_with_key(fe, GTK_CONTAINER(menu), "New", 'n'); + add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Restart", 'r'); + + if ((n = midend_num_presets(fe->me)) > 0) { + GtkWidget *submenu; + int i; + + menuitem = gtk_menu_item_new_with_label("Type"); + gtk_container_add(GTK_CONTAINER(menu), menuitem); + gtk_widget_show(menuitem); + + submenu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); + + for (i = 0; i < n; i++) { + char *name; + game_params *params; + + midend_fetch_preset(fe->me, i, &name, ¶ms); + + menuitem = gtk_menu_item_new_with_label(name); + gtk_container_add(GTK_CONTAINER(submenu), menuitem); + gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", params); + gtk_signal_connect(GTK_OBJECT(menuitem), "activate", + GTK_SIGNAL_FUNC(menu_preset_event), fe); + gtk_widget_show(menuitem); + } + } + + add_menu_separator(GTK_CONTAINER(menu)); + add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Undo", 'u'); + add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Redo", '\x12'); + add_menu_separator(GTK_CONTAINER(menu)); + add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Exit", 'q'); { int i, ncolours; @@ -288,7 +386,7 @@ static frontend *new_window(void) fe->w = x; fe->h = y; - gtk_container_add(GTK_CONTAINER(fe->window), fe->area); + gtk_box_pack_end(vbox, fe->area, FALSE, FALSE, 0); fe->pixmap = NULL; diff --git a/midend.c b/midend.c index d309068..cafc8ea 100644 --- a/midend.c +++ b/midend.c @@ -14,6 +14,11 @@ struct midend_data { frontend *frontend; char *seed; int nstates, statesize, statepos; + + game_params **presets; + char **preset_names; + int npresets, presetsize; + game_params *params; game_state **states; game_drawstate *drawstate; @@ -39,6 +44,9 @@ midend_data *midend_new(frontend *frontend) me->seed = NULL; me->drawstate = NULL; me->oldstate = NULL; + me->presets = NULL; + me->preset_names = NULL; + me->npresets = me->presetsize = 0; return me; } @@ -59,7 +67,7 @@ void midend_size(midend_data *me, int *x, int *y) void midend_set_params(midend_data *me, game_params *params) { free_params(me->params); - me->params = params; + me->params = dup_params(params); } void midend_new_game(midend_data *me, char *seed) @@ -227,3 +235,35 @@ float *midend_colours(midend_data *me, int *ncolours) return ret; } + +int midend_num_presets(midend_data *me) +{ + if (!me->npresets) { + char *name; + game_params *preset; + + while (game_fetch_preset(me->npresets, &name, &preset)) { + if (me->presetsize <= me->npresets) { + me->presetsize = me->npresets + 10; + me->presets = sresize(me->presets, me->presetsize, + game_params *); + me->preset_names = sresize(me->preset_names, me->presetsize, + char *); + } + + me->presets[me->npresets] = preset; + me->preset_names[me->npresets] = name; + me->npresets++; + } + } + + return me->npresets; +} + +void midend_fetch_preset(midend_data *me, int n, + char **name, game_params **params) +{ + assert(n >= 0 && n < me->npresets); + *name = me->preset_names[n]; + *params = me->presets[n]; +} diff --git a/net.c b/net.c index 6fd90aa..a295804 100644 --- a/net.c +++ b/net.c @@ -126,19 +126,60 @@ game_params *default_params(void) { game_params *ret = snew(game_params); - ret->width = 11; - ret->height = 11; - ret->wrapping = TRUE; - ret->barrier_probability = 0.1; + ret->width = 5; + ret->height = 5; + ret->wrapping = FALSE; + ret->barrier_probability = 0.0; return ret; } +int game_fetch_preset(int i, char **name, game_params **params) +{ + game_params *ret; + char str[80]; + static const struct { int x, y, wrap; } values[] = { + {5, 5, FALSE}, + {7, 7, FALSE}, + {9, 9, FALSE}, + {11, 11, FALSE}, + {13, 11, FALSE}, + {5, 5, TRUE}, + {7, 7, TRUE}, + {9, 9, TRUE}, + {11, 11, TRUE}, + {13, 11, TRUE}, + }; + + if (i < 0 || i >= lenof(values)) + return FALSE; + + ret = snew(game_params); + ret->width = values[i].x; + ret->height = values[i].y; + ret->wrapping = values[i].wrap; + ret->barrier_probability = 0.0; + + sprintf(str, "%dx%d%s", ret->width, ret->height, + ret->wrapping ? " wrapping" : ""); + + *name = dupstr(str); + *params = ret; + return TRUE; +} + void free_params(game_params *params) { sfree(params); } +game_params *dup_params(game_params *params) +{ + game_params *ret = snew(game_params); + *ret = *params; /* structure copy */ + return ret; +} + /* ---------------------------------------------------------------------- * Randomly select a new game seed. */ @@ -480,27 +521,27 @@ game_state *new_game(game_params *params, char *seed) x1 = x + X(dir), y1 = y + Y(dir); if (x1 >= 0 && x1 < state->width && - y1 >= 0 && y1 < state->width && + y1 >= 0 && y1 < state->height && (barrier(state, x1, y1) & dir2)) corner = TRUE; x2 = x + X(dir2), y2 = y + Y(dir2); if (x2 >= 0 && x2 < state->width && - y2 >= 0 && y2 < state->width && + y2 >= 0 && y2 < state->height && (barrier(state, x2, y2) & dir)) corner = TRUE; if (corner) { barrier(state, x, y) |= (dir << 4); if (x1 >= 0 && x1 < state->width && - y1 >= 0 && y1 < state->width) + y1 >= 0 && y1 < state->height) barrier(state, x1, y1) |= (A(dir) << 4); if (x2 >= 0 && x2 < state->width && - y2 >= 0 && y2 < state->width) + y2 >= 0 && y2 < state->height) barrier(state, x2, y2) |= (C(dir) << 4); x3 = x + X(dir) + X(dir2), y3 = y + Y(dir) + Y(dir2); if (x3 >= 0 && x3 < state->width && - y3 >= 0 && y3 < state->width) + y3 >= 0 && y3 < state->height) barrier(state, x3, y3) |= (F(dir) << 4); } } diff --git a/puzzles.h b/puzzles.h index 6300e74..86c2cc1 100644 --- a/puzzles.h +++ b/puzzles.h @@ -61,6 +61,9 @@ int midend_process_key(midend_data *me, int x, int y, int button); void midend_redraw(midend_data *me); float *midend_colours(midend_data *me, int *ncolours); void midend_timer(midend_data *me, float tplus); +int midend_num_presets(midend_data *me); +void midend_fetch_preset(midend_data *me, int n, + char **name, game_params **params); /* * malloc.c @@ -87,7 +90,9 @@ void random_free(random_state *state); * Game-specific routines */ game_params *default_params(void); +int game_fetch_preset(int i, char **name, game_params **params); void free_params(game_params *params); +game_params *dup_params(game_params *params); char *new_game_seed(game_params *params); game_state *new_game(game_params *params, char *seed); game_state *dup_game(game_state *state); diff --git a/windows.c b/windows.c index 1fda71b..618d440 100644 --- a/windows.c +++ b/windows.c @@ -11,6 +11,13 @@ #include "puzzles.h" +#define IDM_NEW 0x0010 +#define IDM_RESTART 0x0020 +#define IDM_UNDO 0x0030 +#define IDM_REDO 0x0040 +#define IDM_QUIT 0x0050 +#define IDM_PRESETS 0x0100 + struct frontend { midend_data *me; HWND hwnd; @@ -20,6 +27,8 @@ struct frontend { HBRUSH *brushes; HPEN *pens; UINT timer; + int npresets; + game_params **presets; }; void fatal(char *fmt, ...) @@ -178,7 +187,7 @@ static frontend *new_window(HINSTANCE inst) r.bottom = y; AdjustWindowRectEx(&r, WS_OVERLAPPEDWINDOW &~ (WS_THICKFRAME | WS_MAXIMIZEBOX | WS_OVERLAPPED), - FALSE, 0); + TRUE, 0); fe->hwnd = CreateWindowEx(0, "puzzle", "puzzle", WS_OVERLAPPEDWINDOW &~ @@ -187,6 +196,44 @@ static frontend *new_window(HINSTANCE inst) r.right - r.left, r.bottom - r.top, NULL, NULL, inst, NULL); + { + HMENU bar = CreateMenu(); + HMENU menu = CreateMenu(); + + AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, "Game"); + AppendMenu(menu, MF_ENABLED, IDM_NEW, "New"); + AppendMenu(menu, MF_ENABLED, IDM_RESTART, "Restart"); + + if ((fe->npresets = midend_num_presets(fe->me)) > 0) { + HMENU sub = CreateMenu(); + int i; + + AppendMenu(menu, MF_ENABLED|MF_POPUP, (UINT)sub, "Type"); + + fe->presets = snewn(fe->npresets, game_params *); + + for (i = 0; i < fe->npresets; i++) { + char *name; + + midend_fetch_preset(fe->me, i, &name, &fe->presets[i]); + + /* + * FIXME: we ought to go through and do something + * with ampersands here. + */ + + AppendMenu(sub, MF_ENABLED, IDM_PRESETS + 0x10 * i, name); + } + } + + AppendMenu(menu, MF_SEPARATOR, 0, 0); + AppendMenu(menu, MF_ENABLED, IDM_UNDO, "Undo"); + AppendMenu(menu, MF_ENABLED, IDM_REDO, "Redo"); + AppendMenu(menu, MF_SEPARATOR, 0, 0); + AppendMenu(menu, MF_ENABLED, IDM_QUIT, "Exit"); + SetMenu(fe->hwnd, bar); + } + hdc = GetDC(fe->hwnd); fe->bitmap = CreateCompatibleBitmap(hdc, x, y); ReleaseDC(fe->hwnd, hdc); @@ -210,6 +257,65 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case WM_CLOSE: DestroyWindow(hwnd); return 0; + case WM_COMMAND: + switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */ + case IDM_NEW: + if (!midend_process_key(fe->me, 0, 0, 'n')) + PostQuitMessage(0); + break; + case IDM_RESTART: + if (!midend_process_key(fe->me, 0, 0, 'r')) + PostQuitMessage(0); + break; + case IDM_UNDO: + if (!midend_process_key(fe->me, 0, 0, 'u')) + PostQuitMessage(0); + break; + case IDM_REDO: + if (!midend_process_key(fe->me, 0, 0, '\x12')) + PostQuitMessage(0); + break; + case IDM_QUIT: + if (!midend_process_key(fe->me, 0, 0, 'q')) + PostQuitMessage(0); + break; + default: + { + int p = ((wParam &~ 0xF) - IDM_PRESETS) / 0x10; + + if (p >= 0 && p < fe->npresets) { + RECT r; + HDC hdc; + int x, y; + + midend_set_params(fe->me, fe->presets[p]); + midend_new_game(fe->me, NULL); + midend_size(fe->me, &x, &y); + + r.left = r.top = 0; + r.right = x; + r.bottom = y; + AdjustWindowRectEx(&r, WS_OVERLAPPEDWINDOW &~ + (WS_THICKFRAME | WS_MAXIMIZEBOX | + WS_OVERLAPPED), + TRUE, 0); + + SetWindowPos(fe->hwnd, NULL, 0, 0, + r.right - r.left, r.bottom - r.top, + SWP_NOMOVE | SWP_NOZORDER); + + DeleteObject(fe->bitmap); + + hdc = GetDC(fe->hwnd); + fe->bitmap = CreateCompatibleBitmap(hdc, x, y); + ReleaseDC(fe->hwnd, hdc); + + midend_redraw(fe->me); + } + } + break; + } + break; case WM_DESTROY: PostQuitMessage(0); return 0; @@ -246,7 +352,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } if (key != -1) { - if (!midend_process_key(fe->me, -1, -1, key)) + if (!midend_process_key(fe->me, 0, 0, key)) PostQuitMessage(0); } } @@ -262,7 +368,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, break; case WM_CHAR: - if (!midend_process_key(fe->me, -1, -1, (unsigned char)wParam)) + if (!midend_process_key(fe->me, 0, 0, (unsigned char)wParam)) PostQuitMessage(0); return 0; case WM_TIMER: -- 2.11.0