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.

[originally from svn r4158]
This commit is contained in:
Simon Tatham
2004-04-28 12:07:15 +00:00
parent 03e4862683
commit 3d8e7585b7
7 changed files with 362 additions and 23 deletions

1
Recipe
View File

@ -10,6 +10,7 @@
!makefile gtk Makefile !makefile gtk Makefile
!makefile vc Makefile.vc !makefile vc Makefile.vc
!makefile cygwin Makefile.cyg
WINDOWS = windows user32.lib gdi32.lib WINDOWS = windows user32.lib gdi32.lib
COMMON = midend malloc COMMON = midend malloc

64
cube.c
View File

@ -21,6 +21,7 @@ struct solid {
int faces[MAXFACES * MAXORDER]; /* order*nfaces point indices */ int faces[MAXFACES * MAXORDER]; /* order*nfaces point indices */
float normals[MAXFACES * 3]; /* 3*npoints vector components */ float normals[MAXFACES * 3]; /* 3*npoints vector components */
float shear; /* isometric shear for nice drawing */ float shear; /* isometric shear for nice drawing */
float border; /* border required around arena */
}; };
static const struct solid tetrahedron = { static const struct solid tetrahedron = {
@ -41,7 +42,7 @@ static const struct solid tetrahedron = {
0.816496580928, -0.471404520791, 0.333333333334, 0.816496580928, -0.471404520791, 0.333333333334,
0.0, 0.0, -1.0, 0.0, 0.0, -1.0,
}, },
0.0 0.0, 0.3
}; };
static const struct solid cube = { 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 -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 = { 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.816496580928, 0.471404520791, 0.333333333334, 0.816496580928, 0.471404520791, 0.333333333334,
}, },
0.0 0.0, 0.5
}; };
static const struct solid icosahedron = { 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.57735026919, -0.333333333334, -0.745355992501, 0.57735026919, -0.333333333334, -0.745355992501,
}, },
0.0 0.0, 0.8
}; };
enum { enum {
@ -216,11 +217,58 @@ game_params *default_params(void)
return ret; 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) void free_params(game_params *params)
{ {
sfree(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, static void enum_grid_squares(game_params *params,
void (*callback)(void *, struct grid_square *), void (*callback)(void *, struct grid_square *),
void *ctx) void *ctx)
@ -1083,8 +1131,8 @@ static struct bbox find_bbox(game_params *params)
void game_size(game_params *params, int *x, int *y) void game_size(game_params *params, int *x, int *y)
{ {
struct bbox bb = find_bbox(params); struct bbox bb = find_bbox(params);
*x = (bb.r - bb.l + 2) * GRID_SCALE; *x = (bb.r - bb.l + 2*solids[params->solid]->border) * GRID_SCALE;
*y = (bb.d - bb.u + 2) * GRID_SCALE; *y = (bb.d - bb.u + 2*solids[params->solid]->border) * GRID_SCALE;
} }
float *game_colours(frontend *fe, game_state *state, int *ncolours) 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 game_drawstate *ds = snew(struct game_drawstate);
struct bbox bb = find_bbox(&state->params); struct bbox bb = find_bbox(&state->params);
ds->ox = -(bb.l - 1) * GRID_SCALE; ds->ox = -(bb.l - state->solid->border) * GRID_SCALE;
ds->oy = -(bb.u - 1) * GRID_SCALE; ds->oy = -(bb.u - state->solid->border) * GRID_SCALE;
return ds; return ds;
} }

102
gtk.c
View File

@ -206,6 +206,9 @@ static gint configure_area(GtkWidget *widget,
frontend *fe = (frontend *)data; frontend *fe = (frontend *)data;
GdkGC *gc; GdkGC *gc;
if (fe->pixmap)
gdk_pixmap_unref(fe->pixmap);
fe->pixmap = gdk_pixmap_new(widget->window, fe->w, fe->h, -1); fe->pixmap = gdk_pixmap_new(widget->window, fe->w, fe->h, -1);
gc = gdk_gc_new(fe->area->window); gc = gdk_gc_new(fe->area->window);
@ -239,10 +242,56 @@ void activate_timer(frontend *fe)
fe->timer_active = TRUE; 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) static frontend *new_window(void)
{ {
frontend *fe; frontend *fe;
int x, y; GtkBox *vbox;
GtkWidget *menubar, *menu, *menuitem;
int x, y, n;
fe = snew(frontend); fe = snew(frontend);
@ -255,6 +304,55 @@ static frontend *new_window(void)
#else #else
gtk_window_set_policy(GTK_WINDOW(fe->window), FALSE, FALSE, TRUE); gtk_window_set_policy(GTK_WINDOW(fe->window), FALSE, FALSE, TRUE);
#endif #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, &params);
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; int i, ncolours;
@ -288,7 +386,7 @@ static frontend *new_window(void)
fe->w = x; fe->w = x;
fe->h = y; 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; fe->pixmap = NULL;

View File

@ -14,6 +14,11 @@ struct midend_data {
frontend *frontend; frontend *frontend;
char *seed; char *seed;
int nstates, statesize, statepos; int nstates, statesize, statepos;
game_params **presets;
char **preset_names;
int npresets, presetsize;
game_params *params; game_params *params;
game_state **states; game_state **states;
game_drawstate *drawstate; game_drawstate *drawstate;
@ -39,6 +44,9 @@ midend_data *midend_new(frontend *frontend)
me->seed = NULL; me->seed = NULL;
me->drawstate = NULL; me->drawstate = NULL;
me->oldstate = NULL; me->oldstate = NULL;
me->presets = NULL;
me->preset_names = NULL;
me->npresets = me->presetsize = 0;
return me; 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) void midend_set_params(midend_data *me, game_params *params)
{ {
free_params(me->params); free_params(me->params);
me->params = params; me->params = dup_params(params);
} }
void midend_new_game(midend_data *me, char *seed) void midend_new_game(midend_data *me, char *seed)
@ -227,3 +235,35 @@ float *midend_colours(midend_data *me, int *ncolours)
return ret; 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];
}

59
net.c
View File

@ -126,19 +126,60 @@ game_params *default_params(void)
{ {
game_params *ret = snew(game_params); game_params *ret = snew(game_params);
ret->width = 11; ret->width = 5;
ret->height = 11; ret->height = 5;
ret->wrapping = TRUE; ret->wrapping = FALSE;
ret->barrier_probability = 0.1; ret->barrier_probability = 0.0;
return ret; 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) void free_params(game_params *params)
{ {
sfree(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. * 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); x1 = x + X(dir), y1 = y + Y(dir);
if (x1 >= 0 && x1 < state->width && if (x1 >= 0 && x1 < state->width &&
y1 >= 0 && y1 < state->width && y1 >= 0 && y1 < state->height &&
(barrier(state, x1, y1) & dir2)) (barrier(state, x1, y1) & dir2))
corner = TRUE; corner = TRUE;
x2 = x + X(dir2), y2 = y + Y(dir2); x2 = x + X(dir2), y2 = y + Y(dir2);
if (x2 >= 0 && x2 < state->width && if (x2 >= 0 && x2 < state->width &&
y2 >= 0 && y2 < state->width && y2 >= 0 && y2 < state->height &&
(barrier(state, x2, y2) & dir)) (barrier(state, x2, y2) & dir))
corner = TRUE; corner = TRUE;
if (corner) { if (corner) {
barrier(state, x, y) |= (dir << 4); barrier(state, x, y) |= (dir << 4);
if (x1 >= 0 && x1 < state->width && if (x1 >= 0 && x1 < state->width &&
y1 >= 0 && y1 < state->width) y1 >= 0 && y1 < state->height)
barrier(state, x1, y1) |= (A(dir) << 4); barrier(state, x1, y1) |= (A(dir) << 4);
if (x2 >= 0 && x2 < state->width && if (x2 >= 0 && x2 < state->width &&
y2 >= 0 && y2 < state->width) y2 >= 0 && y2 < state->height)
barrier(state, x2, y2) |= (C(dir) << 4); barrier(state, x2, y2) |= (C(dir) << 4);
x3 = x + X(dir) + X(dir2), y3 = y + Y(dir) + Y(dir2); x3 = x + X(dir) + X(dir2), y3 = y + Y(dir) + Y(dir2);
if (x3 >= 0 && x3 < state->width && if (x3 >= 0 && x3 < state->width &&
y3 >= 0 && y3 < state->width) y3 >= 0 && y3 < state->height)
barrier(state, x3, y3) |= (F(dir) << 4); barrier(state, x3, y3) |= (F(dir) << 4);
} }
} }

View File

@ -61,6 +61,9 @@ int midend_process_key(midend_data *me, int x, int y, int button);
void midend_redraw(midend_data *me); void midend_redraw(midend_data *me);
float *midend_colours(midend_data *me, int *ncolours); float *midend_colours(midend_data *me, int *ncolours);
void midend_timer(midend_data *me, float tplus); 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 * malloc.c
@ -87,7 +90,9 @@ void random_free(random_state *state);
* Game-specific routines * Game-specific routines
*/ */
game_params *default_params(void); game_params *default_params(void);
int game_fetch_preset(int i, char **name, game_params **params);
void free_params(game_params *params); void free_params(game_params *params);
game_params *dup_params(game_params *params);
char *new_game_seed(game_params *params); char *new_game_seed(game_params *params);
game_state *new_game(game_params *params, char *seed); game_state *new_game(game_params *params, char *seed);
game_state *dup_game(game_state *state); game_state *dup_game(game_state *state);

112
windows.c
View File

@ -11,6 +11,13 @@
#include "puzzles.h" #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 { struct frontend {
midend_data *me; midend_data *me;
HWND hwnd; HWND hwnd;
@ -20,6 +27,8 @@ struct frontend {
HBRUSH *brushes; HBRUSH *brushes;
HPEN *pens; HPEN *pens;
UINT timer; UINT timer;
int npresets;
game_params **presets;
}; };
void fatal(char *fmt, ...) void fatal(char *fmt, ...)
@ -178,7 +187,7 @@ static frontend *new_window(HINSTANCE inst)
r.bottom = y; r.bottom = y;
AdjustWindowRectEx(&r, WS_OVERLAPPEDWINDOW &~ AdjustWindowRectEx(&r, WS_OVERLAPPEDWINDOW &~
(WS_THICKFRAME | WS_MAXIMIZEBOX | WS_OVERLAPPED), (WS_THICKFRAME | WS_MAXIMIZEBOX | WS_OVERLAPPED),
FALSE, 0); TRUE, 0);
fe->hwnd = CreateWindowEx(0, "puzzle", "puzzle", fe->hwnd = CreateWindowEx(0, "puzzle", "puzzle",
WS_OVERLAPPEDWINDOW &~ WS_OVERLAPPEDWINDOW &~
@ -187,6 +196,44 @@ static frontend *new_window(HINSTANCE inst)
r.right - r.left, r.bottom - r.top, r.right - r.left, r.bottom - r.top,
NULL, NULL, inst, NULL); 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); hdc = GetDC(fe->hwnd);
fe->bitmap = CreateCompatibleBitmap(hdc, x, y); fe->bitmap = CreateCompatibleBitmap(hdc, x, y);
ReleaseDC(fe->hwnd, hdc); ReleaseDC(fe->hwnd, hdc);
@ -210,6 +257,65 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
case WM_CLOSE: case WM_CLOSE:
DestroyWindow(hwnd); DestroyWindow(hwnd);
return 0; 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: case WM_DESTROY:
PostQuitMessage(0); PostQuitMessage(0);
return 0; return 0;
@ -246,7 +352,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
} }
if (key != -1) { if (key != -1) {
if (!midend_process_key(fe->me, -1, -1, key)) if (!midend_process_key(fe->me, 0, 0, key))
PostQuitMessage(0); PostQuitMessage(0);
} }
} }
@ -262,7 +368,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
break; break;
case WM_CHAR: 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); PostQuitMessage(0);
return 0; return 0;
case WM_TIMER: case WM_TIMER: