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 vc Makefile.vc
!makefile cygwin Makefile.cyg
WINDOWS = windows user32.lib gdi32.lib
COMMON = midend malloc

64
cube.c
View File

@ -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;
}

102
gtk.c
View File

@ -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, &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;
@ -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;

View File

@ -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];
}

59
net.c
View File

@ -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);
}
}

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);
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);

112
windows.c
View File

@ -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: