mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 08:01:30 -07:00
Introduced a new function in every game which formats a game_state
as text. This is used by front ends to implement copy-to-clipboard. Currently the function does nothing (and is disabled) in every game except Solo, but it's a start. [originally from svn r5724]
This commit is contained in:
6
cube.c
6
cube.c
@ -979,6 +979,11 @@ static void free_game(game_state *state)
|
|||||||
sfree(state);
|
sfree(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *game_text_format(game_state *state)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static game_ui *new_ui(game_state *state)
|
static game_ui *new_ui(game_state *state)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1545,6 +1550,7 @@ const struct game thegame = {
|
|||||||
new_game,
|
new_game,
|
||||||
dup_game,
|
dup_game,
|
||||||
free_game,
|
free_game,
|
||||||
|
NULL, game_text_format,
|
||||||
new_ui,
|
new_ui,
|
||||||
free_ui,
|
free_ui,
|
||||||
make_move,
|
make_move,
|
||||||
|
@ -370,6 +370,11 @@ static void free_game(game_state *state)
|
|||||||
sfree(state);
|
sfree(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *game_text_format(game_state *state)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static game_ui *new_ui(game_state *state)
|
static game_ui *new_ui(game_state *state)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -738,6 +743,7 @@ const struct game thegame = {
|
|||||||
new_game,
|
new_game,
|
||||||
dup_game,
|
dup_game,
|
||||||
free_game,
|
free_game,
|
||||||
|
FALSE, game_text_format,
|
||||||
new_ui,
|
new_ui,
|
||||||
free_ui,
|
free_ui,
|
||||||
make_move,
|
make_move,
|
||||||
|
128
gtk.c
128
gtk.c
@ -14,10 +14,10 @@
|
|||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
#include <gdk/gdkkeysyms.h>
|
#include <gdk/gdkkeysyms.h>
|
||||||
|
|
||||||
#if GTK_CHECK_VERSION(2,0,0) && !defined HAVE_SENSIBLE_ABSOLUTE_SIZE_FUNCTION
|
|
||||||
#include <gdk/gdkx.h>
|
#include <gdk/gdkx.h>
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#endif
|
#include <X11/Xutil.h>
|
||||||
|
#include <X11/Xatom.h>
|
||||||
|
|
||||||
#include "puzzles.h"
|
#include "puzzles.h"
|
||||||
|
|
||||||
@ -76,6 +76,8 @@ struct frontend {
|
|||||||
config_item *cfg;
|
config_item *cfg;
|
||||||
int cfg_which, cfgret;
|
int cfg_which, cfgret;
|
||||||
GtkWidget *cfgbox;
|
GtkWidget *cfgbox;
|
||||||
|
char *paste_data;
|
||||||
|
int paste_data_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
void get_random_seed(void **randseed, int *randseedsize)
|
void get_random_seed(void **randseed, int *randseedsize)
|
||||||
@ -804,6 +806,113 @@ static void menu_preset_event(GtkMenuItem *menuitem, gpointer data)
|
|||||||
fe->h = y;
|
fe->h = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GdkAtom compound_text_atom, utf8_string_atom;
|
||||||
|
int paste_initialised = FALSE;
|
||||||
|
|
||||||
|
void init_paste()
|
||||||
|
{
|
||||||
|
if (paste_initialised)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!compound_text_atom)
|
||||||
|
compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE);
|
||||||
|
if (!utf8_string_atom)
|
||||||
|
utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure that all the cut buffers exist - according to the
|
||||||
|
* ICCCM, we must do this before we start using cut buffers.
|
||||||
|
*/
|
||||||
|
XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
|
||||||
|
XA_CUT_BUFFER0, XA_STRING, 8, PropModeAppend, "", 0);
|
||||||
|
XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
|
||||||
|
XA_CUT_BUFFER1, XA_STRING, 8, PropModeAppend, "", 0);
|
||||||
|
XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
|
||||||
|
XA_CUT_BUFFER2, XA_STRING, 8, PropModeAppend, "", 0);
|
||||||
|
XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
|
||||||
|
XA_CUT_BUFFER3, XA_STRING, 8, PropModeAppend, "", 0);
|
||||||
|
XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
|
||||||
|
XA_CUT_BUFFER4, XA_STRING, 8, PropModeAppend, "", 0);
|
||||||
|
XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
|
||||||
|
XA_CUT_BUFFER5, XA_STRING, 8, PropModeAppend, "", 0);
|
||||||
|
XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
|
||||||
|
XA_CUT_BUFFER6, XA_STRING, 8, PropModeAppend, "", 0);
|
||||||
|
XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
|
||||||
|
XA_CUT_BUFFER7, XA_STRING, 8, PropModeAppend, "", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store data in a cut-buffer. */
|
||||||
|
void store_cutbuffer(char *ptr, int len)
|
||||||
|
{
|
||||||
|
/* ICCCM says we must rotate the buffers before storing to buffer 0. */
|
||||||
|
XRotateBuffers(GDK_DISPLAY(), 1);
|
||||||
|
XStoreBytes(GDK_DISPLAY(), ptr, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_clip(frontend *fe, char *data)
|
||||||
|
{
|
||||||
|
init_paste();
|
||||||
|
|
||||||
|
if (fe->paste_data)
|
||||||
|
sfree(fe->paste_data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For this simple application we can safely assume that the
|
||||||
|
* data passed to this function is pure ASCII, which means we
|
||||||
|
* can return precisely the same stuff for types STRING,
|
||||||
|
* COMPOUND_TEXT or UTF8_STRING.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fe->paste_data = data;
|
||||||
|
fe->paste_data_len = strlen(data);
|
||||||
|
|
||||||
|
store_cutbuffer(fe->paste_data, fe->paste_data_len);
|
||||||
|
|
||||||
|
if (gtk_selection_owner_set(fe->area, GDK_SELECTION_PRIMARY,
|
||||||
|
CurrentTime)) {
|
||||||
|
gtk_selection_add_target(fe->area, GDK_SELECTION_PRIMARY,
|
||||||
|
GDK_SELECTION_TYPE_STRING, 1);
|
||||||
|
gtk_selection_add_target(fe->area, GDK_SELECTION_PRIMARY,
|
||||||
|
compound_text_atom, 1);
|
||||||
|
gtk_selection_add_target(fe->area, GDK_SELECTION_PRIMARY,
|
||||||
|
utf8_string_atom, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void selection_get(GtkWidget *widget, GtkSelectionData *seldata,
|
||||||
|
guint info, guint time_stamp, gpointer data)
|
||||||
|
{
|
||||||
|
frontend *fe = (frontend *)data;
|
||||||
|
gtk_selection_data_set(seldata, seldata->target, 8,
|
||||||
|
fe->paste_data, fe->paste_data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
frontend *fe = (frontend *)data;
|
||||||
|
|
||||||
|
if (fe->paste_data)
|
||||||
|
sfree(fe->paste_data);
|
||||||
|
fe->paste_data = NULL;
|
||||||
|
fe->paste_data_len = 0;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menu_copy_event(GtkMenuItem *menuitem, gpointer data)
|
||||||
|
{
|
||||||
|
frontend *fe = (frontend *)data;
|
||||||
|
char *text;
|
||||||
|
|
||||||
|
text = midend_text_format(fe->me);
|
||||||
|
|
||||||
|
if (text) {
|
||||||
|
write_clip(fe, text);
|
||||||
|
} else {
|
||||||
|
gdk_beep();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void menu_config_event(GtkMenuItem *menuitem, gpointer data)
|
static void menu_config_event(GtkMenuItem *menuitem, gpointer data)
|
||||||
{
|
{
|
||||||
frontend *fe = (frontend *)data;
|
frontend *fe = (frontend *)data;
|
||||||
@ -933,6 +1042,14 @@ static frontend *new_window(char *game_id, char **error)
|
|||||||
add_menu_separator(GTK_CONTAINER(menu));
|
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), "Undo", 'u');
|
||||||
add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Redo", '\x12');
|
add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Redo", '\x12');
|
||||||
|
if (thegame.can_format_as_text) {
|
||||||
|
add_menu_separator(GTK_CONTAINER(menu));
|
||||||
|
menuitem = gtk_menu_item_new_with_label("Copy");
|
||||||
|
gtk_container_add(GTK_CONTAINER(menu), menuitem);
|
||||||
|
gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
|
||||||
|
GTK_SIGNAL_FUNC(menu_copy_event), fe);
|
||||||
|
gtk_widget_show(menuitem);
|
||||||
|
}
|
||||||
add_menu_separator(GTK_CONTAINER(menu));
|
add_menu_separator(GTK_CONTAINER(menu));
|
||||||
add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Exit", 'q');
|
add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Exit", 'q');
|
||||||
|
|
||||||
@ -999,6 +1116,9 @@ static frontend *new_window(char *game_id, char **error)
|
|||||||
|
|
||||||
fe->timer_active = FALSE;
|
fe->timer_active = FALSE;
|
||||||
|
|
||||||
|
fe->paste_data = NULL;
|
||||||
|
fe->paste_data_len = 0;
|
||||||
|
|
||||||
gtk_signal_connect(GTK_OBJECT(fe->window), "destroy",
|
gtk_signal_connect(GTK_OBJECT(fe->window), "destroy",
|
||||||
GTK_SIGNAL_FUNC(destroy), fe);
|
GTK_SIGNAL_FUNC(destroy), fe);
|
||||||
gtk_signal_connect(GTK_OBJECT(fe->window), "key_press_event",
|
gtk_signal_connect(GTK_OBJECT(fe->window), "key_press_event",
|
||||||
@ -1009,6 +1129,10 @@ static frontend *new_window(char *game_id, char **error)
|
|||||||
GTK_SIGNAL_FUNC(button_event), fe);
|
GTK_SIGNAL_FUNC(button_event), fe);
|
||||||
gtk_signal_connect(GTK_OBJECT(fe->area), "motion_notify_event",
|
gtk_signal_connect(GTK_OBJECT(fe->area), "motion_notify_event",
|
||||||
GTK_SIGNAL_FUNC(motion_event), fe);
|
GTK_SIGNAL_FUNC(motion_event), fe);
|
||||||
|
gtk_signal_connect(GTK_OBJECT(fe->area), "selection_get",
|
||||||
|
GTK_SIGNAL_FUNC(selection_get), fe);
|
||||||
|
gtk_signal_connect(GTK_OBJECT(fe->area), "selection_clear_event",
|
||||||
|
GTK_SIGNAL_FUNC(selection_clear), fe);
|
||||||
gtk_signal_connect(GTK_OBJECT(fe->area), "expose_event",
|
gtk_signal_connect(GTK_OBJECT(fe->area), "expose_event",
|
||||||
GTK_SIGNAL_FUNC(expose_area), fe);
|
GTK_SIGNAL_FUNC(expose_area), fe);
|
||||||
gtk_signal_connect(GTK_OBJECT(fe->window), "map_event",
|
gtk_signal_connect(GTK_OBJECT(fe->window), "map_event",
|
||||||
|
8
midend.c
8
midend.c
@ -573,3 +573,11 @@ char *midend_set_config(midend_data *me, int which, config_item *cfg)
|
|||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *midend_text_format(midend_data *me)
|
||||||
|
{
|
||||||
|
if (me->ourgame->can_format_as_text && me->statepos > 0)
|
||||||
|
return me->ourgame->text_format(me->states[me->statepos-1]);
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
6
net.c
6
net.c
@ -699,6 +699,11 @@ static void free_game(game_state *state)
|
|||||||
sfree(state);
|
sfree(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *game_text_format(game_state *state)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------
|
/* ----------------------------------------------------------------------
|
||||||
* Utility routine.
|
* Utility routine.
|
||||||
*/
|
*/
|
||||||
@ -1508,6 +1513,7 @@ const struct game thegame = {
|
|||||||
new_game,
|
new_game,
|
||||||
dup_game,
|
dup_game,
|
||||||
free_game,
|
free_game,
|
||||||
|
FALSE, game_text_format,
|
||||||
new_ui,
|
new_ui,
|
||||||
free_ui,
|
free_ui,
|
||||||
make_move,
|
make_move,
|
||||||
|
@ -740,6 +740,11 @@ static void free_game(game_state *state)
|
|||||||
sfree(state);
|
sfree(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *game_text_format(game_state *state)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------
|
/* ----------------------------------------------------------------------
|
||||||
* Utility routine.
|
* Utility routine.
|
||||||
*/
|
*/
|
||||||
@ -1532,6 +1537,7 @@ const struct game thegame = {
|
|||||||
new_game,
|
new_game,
|
||||||
dup_game,
|
dup_game,
|
||||||
free_game,
|
free_game,
|
||||||
|
FALSE, game_text_format,
|
||||||
new_ui,
|
new_ui,
|
||||||
free_ui,
|
free_ui,
|
||||||
make_move,
|
make_move,
|
||||||
|
@ -121,6 +121,11 @@ static void free_game(game_state *state)
|
|||||||
sfree(state);
|
sfree(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *game_text_format(game_state *state)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static game_ui *new_ui(game_state *state)
|
static game_ui *new_ui(game_state *state)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -222,6 +227,7 @@ const struct game thegame = {
|
|||||||
new_game,
|
new_game,
|
||||||
dup_game,
|
dup_game,
|
||||||
free_game,
|
free_game,
|
||||||
|
FALSE, game_text_format,
|
||||||
new_ui,
|
new_ui,
|
||||||
free_ui,
|
free_ui,
|
||||||
make_move,
|
make_move,
|
||||||
|
24
osx.m
24
osx.m
@ -571,6 +571,28 @@ struct frontend {
|
|||||||
[self processButton:'r'&0x1F x:-1 y:-1];
|
[self processButton:'r'&0x1F x:-1 y:-1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)copy:(id)sender
|
||||||
|
{
|
||||||
|
char *text;
|
||||||
|
|
||||||
|
if ((text = midend_text_format(me)) != NULL) {
|
||||||
|
NSPasteboard *pb = [NSPasteboard generalPasteboard];
|
||||||
|
NSArray *a = [NSArray arrayWithObject:NSStringPboardType];
|
||||||
|
[pb declareTypes:a owner:nil];
|
||||||
|
[pb setString:[NSString stringWithCString:text]
|
||||||
|
forType:NSStringPboardType];
|
||||||
|
} else
|
||||||
|
NSBeep();
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)validateMenuItem:(NSMenuItem *)item
|
||||||
|
{
|
||||||
|
if ([item action] == @selector(copy:))
|
||||||
|
return (ourgame->can_format_as_text ? YES : NO);
|
||||||
|
else
|
||||||
|
return [super validateMenuItem:item];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)clearTypeMenu
|
- (void)clearTypeMenu
|
||||||
{
|
{
|
||||||
while ([typemenu numberOfItems] > 1)
|
while ([typemenu numberOfItems] > 1)
|
||||||
@ -1215,6 +1237,8 @@ int main(int argc, char **argv)
|
|||||||
item = newitem(menu, "Undo", "z", NULL, @selector(undoMove:));
|
item = newitem(menu, "Undo", "z", NULL, @selector(undoMove:));
|
||||||
item = newitem(menu, "Redo", "S-z", NULL, @selector(redoMove:));
|
item = newitem(menu, "Redo", "S-z", NULL, @selector(redoMove:));
|
||||||
[menu addItem:[NSMenuItem separatorItem]];
|
[menu addItem:[NSMenuItem separatorItem]];
|
||||||
|
item = newitem(menu, "Copy", "c", NULL, @selector(copy:));
|
||||||
|
[menu addItem:[NSMenuItem separatorItem]];
|
||||||
item = newitem(menu, "Close", "w", NULL, @selector(performClose:));
|
item = newitem(menu, "Close", "w", NULL, @selector(performClose:));
|
||||||
|
|
||||||
menu = newsubmenu([NSApp mainMenu], "Type");
|
menu = newsubmenu([NSApp mainMenu], "Type");
|
||||||
|
@ -648,6 +648,11 @@ static void free_game(game_state *state)
|
|||||||
sfree(state);
|
sfree(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *game_text_format(game_state *state)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
struct game_ui {
|
struct game_ui {
|
||||||
int dragging;
|
int dragging;
|
||||||
int drag_start_x;
|
int drag_start_x;
|
||||||
@ -1029,6 +1034,7 @@ const struct game thegame = {
|
|||||||
new_game,
|
new_game,
|
||||||
dup_game,
|
dup_game,
|
||||||
free_game,
|
free_game,
|
||||||
|
FALSE, game_text_format,
|
||||||
new_ui,
|
new_ui,
|
||||||
free_ui,
|
free_ui,
|
||||||
make_move,
|
make_move,
|
||||||
|
@ -140,6 +140,7 @@ enum { CFG_SETTINGS, CFG_SEED };
|
|||||||
config_item *midend_get_config(midend_data *me, int which, char **wintitle);
|
config_item *midend_get_config(midend_data *me, int which, char **wintitle);
|
||||||
char *midend_set_config(midend_data *me, int which, config_item *cfg);
|
char *midend_set_config(midend_data *me, int which, config_item *cfg);
|
||||||
char *midend_game_id(midend_data *me, char *id, int def_seed);
|
char *midend_game_id(midend_data *me, char *id, int def_seed);
|
||||||
|
char *midend_text_format(midend_data *me);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* malloc.c
|
* malloc.c
|
||||||
@ -193,6 +194,8 @@ struct game {
|
|||||||
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);
|
||||||
void (*free_game)(game_state *state);
|
void (*free_game)(game_state *state);
|
||||||
|
int can_format_as_text;
|
||||||
|
char *(*text_format)(game_state *state);
|
||||||
game_ui *(*new_ui)(game_state *state);
|
game_ui *(*new_ui)(game_state *state);
|
||||||
void (*free_ui)(game_ui *ui);
|
void (*free_ui)(game_ui *ui);
|
||||||
game_state *(*make_move)(game_state *from, game_ui *ui, int x, int y,
|
game_state *(*make_move)(game_state *from, game_ui *ui, int x, int y,
|
||||||
|
6
rect.c
6
rect.c
@ -997,6 +997,11 @@ static void free_game(game_state *state)
|
|||||||
sfree(state);
|
sfree(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *game_text_format(game_state *state)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static unsigned char *get_correct(game_state *state)
|
static unsigned char *get_correct(game_state *state)
|
||||||
{
|
{
|
||||||
unsigned char *ret;
|
unsigned char *ret;
|
||||||
@ -1614,6 +1619,7 @@ const struct game thegame = {
|
|||||||
new_game,
|
new_game,
|
||||||
dup_game,
|
dup_game,
|
||||||
free_game,
|
free_game,
|
||||||
|
FALSE, game_text_format,
|
||||||
new_ui,
|
new_ui,
|
||||||
free_ui,
|
free_ui,
|
||||||
make_move,
|
make_move,
|
||||||
|
@ -379,6 +379,11 @@ static void free_game(game_state *state)
|
|||||||
sfree(state);
|
sfree(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *game_text_format(game_state *state)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static game_ui *new_ui(game_state *state)
|
static game_ui *new_ui(game_state *state)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -788,6 +793,7 @@ const struct game thegame = {
|
|||||||
new_game,
|
new_game,
|
||||||
dup_game,
|
dup_game,
|
||||||
free_game,
|
free_game,
|
||||||
|
FALSE, game_text_format,
|
||||||
new_ui,
|
new_ui,
|
||||||
free_ui,
|
free_ui,
|
||||||
make_move,
|
make_move,
|
||||||
|
96
solo.c
96
solo.c
@ -1607,6 +1607,68 @@ static void free_game(game_state *state)
|
|||||||
sfree(state);
|
sfree(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *grid_text_format(int c, int r, digit *grid)
|
||||||
|
{
|
||||||
|
int cr = c*r;
|
||||||
|
int x, y;
|
||||||
|
int maxlen;
|
||||||
|
char *ret, *p;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There are cr lines of digits, plus r-1 lines of block
|
||||||
|
* separators. Each line contains cr digits, cr-1 separating
|
||||||
|
* spaces, and c-1 two-character block separators. Thus, the
|
||||||
|
* total length of a line is 2*cr+2*c-3 (not counting the
|
||||||
|
* newline), and there are cr+r-1 of them.
|
||||||
|
*/
|
||||||
|
maxlen = (cr+r-1) * (2*cr+2*c-2);
|
||||||
|
ret = snewn(maxlen+1, char);
|
||||||
|
p = ret;
|
||||||
|
|
||||||
|
for (y = 0; y < cr; y++) {
|
||||||
|
for (x = 0; x < cr; x++) {
|
||||||
|
int ch = grid[y * cr + x];
|
||||||
|
if (ch == 0)
|
||||||
|
ch = ' ';
|
||||||
|
else if (ch <= 9)
|
||||||
|
ch = '0' + ch;
|
||||||
|
else
|
||||||
|
ch = 'a' + ch-10;
|
||||||
|
*p++ = ch;
|
||||||
|
if (x+1 < cr) {
|
||||||
|
*p++ = ' ';
|
||||||
|
if ((x+1) % r == 0) {
|
||||||
|
*p++ = '|';
|
||||||
|
*p++ = ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*p++ = '\n';
|
||||||
|
if (y+1 < cr && (y+1) % c == 0) {
|
||||||
|
for (x = 0; x < cr; x++) {
|
||||||
|
*p++ = '-';
|
||||||
|
if (x+1 < cr) {
|
||||||
|
*p++ = '-';
|
||||||
|
if ((x+1) % r == 0) {
|
||||||
|
*p++ = '+';
|
||||||
|
*p++ = '-';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*p++ = '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(p - ret == maxlen);
|
||||||
|
*p = '\0';
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *game_text_format(game_state *state)
|
||||||
|
{
|
||||||
|
return grid_text_format(state->c, state->r, state->grid);
|
||||||
|
}
|
||||||
|
|
||||||
struct game_ui {
|
struct game_ui {
|
||||||
/*
|
/*
|
||||||
* These are the coordinates of the currently highlighted
|
* These are the coordinates of the currently highlighted
|
||||||
@ -1901,6 +1963,7 @@ const struct game thegame = {
|
|||||||
new_game,
|
new_game,
|
||||||
dup_game,
|
dup_game,
|
||||||
free_game,
|
free_game,
|
||||||
|
TRUE, game_text_format,
|
||||||
new_ui,
|
new_ui,
|
||||||
free_ui,
|
free_ui,
|
||||||
make_move,
|
make_move,
|
||||||
@ -2034,38 +2097,7 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (y = 0; y < p->c * p->r; y++) {
|
printf("%s\n", grid_text_format(p->c, p->r, s->grid));
|
||||||
for (x = 0; x < p->c * p->r; x++) {
|
|
||||||
int c = s->grid[y * p->c * p->r + x];
|
|
||||||
if (c == 0)
|
|
||||||
c = ' ';
|
|
||||||
else if (c <= 9)
|
|
||||||
c = '0' + c;
|
|
||||||
else
|
|
||||||
c = 'a' + c-10;
|
|
||||||
printf("%c", c);
|
|
||||||
if (x+1 < p->c * p->r) {
|
|
||||||
if ((x+1) % p->r)
|
|
||||||
printf(" ");
|
|
||||||
else
|
|
||||||
printf(" | ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
if (y+1 < p->c * p->r && (y+1) % p->c == 0) {
|
|
||||||
for (x = 0; x < p->c * p->r; x++) {
|
|
||||||
printf("-");
|
|
||||||
if (x+1 < p->c * p->r) {
|
|
||||||
if ((x+1) % p->r)
|
|
||||||
printf("-");
|
|
||||||
else
|
|
||||||
printf("-+-");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -452,6 +452,11 @@ static void free_game(game_state *state)
|
|||||||
sfree(state);
|
sfree(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *game_text_format(game_state *state)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static game_ui *new_ui(game_state *state)
|
static game_ui *new_ui(game_state *state)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -942,6 +947,7 @@ const struct game thegame = {
|
|||||||
new_game,
|
new_game,
|
||||||
dup_game,
|
dup_game,
|
||||||
free_game,
|
free_game,
|
||||||
|
FALSE, game_text_format,
|
||||||
new_ui,
|
new_ui,
|
||||||
free_ui,
|
free_ui,
|
||||||
make_move,
|
make_move,
|
||||||
|
Reference in New Issue
Block a user