puzzles: resync with upstream

This brings puzzles to upstream commit 84d3fd2.

Change-Id: I808a197f868032d771fc101a15666c5ec4b9f94b
This commit is contained in:
Franklin Wei 2017-09-30 17:47:13 -04:00
parent ea679de837
commit b9386109e8
26 changed files with 1037 additions and 583 deletions

View file

@ -54,24 +54,30 @@ in puzzles do make -f Makefile.doc clean
in puzzles do make -f Makefile.doc # build help files for installer
in puzzles do mason.pl --args '{"version":"$(Version)","descfile":"gamedesc.txt"}' winwix.mc > puzzles.wxs
in puzzles do perl winiss.pl $(Version) gamedesc.txt > puzzles.iss
delegate windows
# FIXME: Cygwin alternative?
in puzzles with visualstudio do/win nmake -f Makefile.vc clean
in puzzles with visualstudio do/win nmake -f Makefile.vc VER=-DVER=$(Version)
ifneq "$(VISUAL_STUDIO)" "yes" then
in puzzles with clangcl64 do Platform=x64 make -f Makefile.clangcl clean
in puzzles with clangcl64 do Platform=x64 make -f Makefile.clangcl VER=-DVER=$(Version)
# Code-sign the binaries, if the local bob config provides a script
# to do so. We assume here that the script accepts an -i option to
# provide a 'more info' URL, and an optional -n option to provide a
# program name, and that it can take multiple .exe filename
# arguments and sign them all in place.
ifneq "$(winsigncode)" "" in puzzles do $(winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/puzzles/ *.exe
ifneq "$(cross_winsigncode)" "" in puzzles do $(cross_winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/puzzles/ *.exe
# Build installers.
in puzzles with wix do/win candle puzzles.wxs && light -ext WixUIExtension -sval puzzles.wixobj
in puzzles with innosetup do/win iscc puzzles.iss
ifneq "$(winsigncode)" "" in puzzles do $(winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/puzzles/ -n "Simon Tatham's Portable Puzzle Collection Installer" puzzles.msi Output/installer.exe
return puzzles/*.exe
return puzzles/Output/installer.exe
return puzzles/puzzles.msi
enddelegate
in puzzles with wixonlinux do candle -arch x64 puzzles.wxs && light -ext WixUIExtension -sval puzzles.wixobj
ifneq "$(cross_winsigncode)" "" in puzzles do $(cross_winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/puzzles/ -n "Simon Tatham's Portable Puzzle Collection Installer" puzzles.msi
else
delegate windows
in puzzles with visualstudio do/win nmake -f Makefile.vc clean
in puzzles with visualstudio do/win nmake -f Makefile.vc VER=-DVER=$(Version)
ifneq "$(winsigncode)" "" in puzzles do $(winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/puzzles/ *.exe
# Build installers.
in puzzles with wix do/win candle puzzles.wxs && light -ext WixUIExtension -sval puzzles.wixobj
in puzzles with innosetup do/win iscc puzzles.iss
return puzzles/*.exe
return puzzles/puzzles.msi
enddelegate
endif
in puzzles do chmod +x *.exe
# Build the Pocket PC binaries and CAB.
@ -152,13 +158,22 @@ delegate emscripten
return puzzles/js/*.js
enddelegate
# Build a set of wrapping HTML pages for easy testing of the
# Javascript puzzles. These aren't quite the same as the versions that
# will go on my live website, because those ones will substitute in a
# different footer, and not have to link to the .js files with the
# ../js/ prefix. But these ones should be good enough to just open
# using a file:// URL in a browser after running a build, and make
# sure the main functionality works.
in puzzles do mkdir jstest
in puzzles/jstest do ../html/jspage.pl --jspath=../js/ /dev/null ../html/*.html
# Set up .htaccess containing a redirect for the archive filename.
in puzzles do echo "AddType application/octet-stream .chm" > .htaccess
in puzzles do echo "AddType application/octet-stream .hlp" >> .htaccess
in puzzles do echo "AddType application/octet-stream .cnt" >> .htaccess
in . do set -- puzzles*.tar.gz; echo RedirectMatch temp '(.*/)'puzzles.tar.gz '$$1'"$$1" >> puzzles/.htaccess
in puzzles do echo RedirectMatch temp '(.*/)'puzzles-installer.msi '$$1'puzzles-$(Version)-installer.msi >> .htaccess
in puzzles do echo RedirectMatch temp '(.*/)'puzzles-installer.exe '$$1'puzzles-$(Version)-installer.exe >> .htaccess
# Phew, we're done. Deliver everything!
deliver puzzles/icons/*-web.png $@
@ -172,9 +187,9 @@ deliver puzzles/puzzles.hlp $@
deliver puzzles/puzzles.cnt $@
deliver puzzles/puzzles.zip $@
deliver puzzles/puzzles.msi puzzles-$(Version)-installer.msi
deliver puzzles/Output/installer.exe puzzles-$(Version)-installer.exe
deliver puzzles/*.jar java/$@
deliver puzzles/js/*.js js/$@
deliver puzzles/jstest/*.html jstest/$@
deliver puzzles/html/*.html html/$@
deliver puzzles/html/*.pl html/$@
deliver puzzles/wwwspans.html $@

View file

@ -61,19 +61,19 @@ public class PuzzleApplet extends JApplet implements Runtime.CallJavaCB {
JMenuBar menubar = new JMenuBar();
JMenu jm;
menubar.add(jm = new JMenu("Game"));
addMenuItemWithKey(jm, "New", 'n');
addMenuItemCallback(jm, "New", "jcallback_newgame_event");
addMenuItemCallback(jm, "Restart", "jcallback_restart_event");
addMenuItemCallback(jm, "Specific...", "jcallback_config_event", CFG_DESC);
addMenuItemCallback(jm, "Random Seed...", "jcallback_config_event", CFG_SEED);
jm.addSeparator();
addMenuItemWithKey(jm, "Undo", 'u');
addMenuItemWithKey(jm, "Redo", 'r');
addMenuItemCallback(jm, "Undo", "jcallback_undo_event");
addMenuItemCallback(jm, "Redo", "jcallback_redo_event");
jm.addSeparator();
solveCommand = addMenuItemCallback(jm, "Solve", "jcallback_solve_event");
solveCommand.setEnabled(false);
if (mainWindow != null) {
jm.addSeparator();
addMenuItemWithKey(jm, "Exit", 'q');
addMenuItemCallback(jm, "Exit", "jcallback_quit_event");
}
menubar.add(typeMenu = new JMenu("Type"));
typeMenu.setVisible(false);
@ -126,7 +126,12 @@ public class PuzzleApplet extends JApplet implements Runtime.CallJavaCB {
}
}
public void keyTyped(KeyEvent e) {
runtimeCall("jcallback_key_event", new int[] {0, 0, e.getKeyChar()});
int key = e.getKeyChar();
if (key == 26 && e.isShiftDown() && e.isControlDown()) {
runtimeCall("jcallback_redo_event", new int[0]);
return;
}
runtimeCall("jcallback_key_event", new int[] {0, 0, key});
}
});
pp.addMouseListener(new MouseAdapter() {
@ -217,10 +222,6 @@ public class PuzzleApplet extends JApplet implements Runtime.CallJavaCB {
runtimeCall("jcallback_resize", new int[] {pp.getWidth(), pp.getHeight()});
}
private void addMenuItemWithKey(JMenu jm, String name, int key) {
addMenuItemCallback(jm, name, "jcallback_menu_key_event", key);
}
private JMenuItem addMenuItemCallback(JMenu jm, String name, final String callback, final int arg) {
return addMenuItemCallback(jm, name, callback, new int[] {arg}, false);
}

View file

@ -17,6 +17,7 @@
!makefile gnustep Makefile.gnustep
!makefile nestedvm Makefile.nestedvm
!makefile emcc Makefile.emcc
!makefile clangcl Makefile.clangcl
!srcdir icons/

View file

@ -1,21 +0,0 @@
\# File containing the magic HTML configuration directives to create
\# an MS HTML Help project. We put this on the end of the Puzzles
\# docs build command line to build the HHP and friends.
\cfg{html-leaf-level}{infinite}
\cfg{html-leaf-contains-contents}{false}
\cfg{html-suppress-navlinks}{true}
\cfg{html-suppress-address}{true}
\cfg{html-contents-filename}{index.html}
\cfg{html-template-filename}{%k.html}
\cfg{html-template-fragment}{%k}
\cfg{html-mshtmlhelp-chm}{puzzles.chm}
\cfg{html-mshtmlhelp-project}{puzzles.hhp}
\cfg{html-mshtmlhelp-contents}{puzzles.hhc}
\cfg{html-mshtmlhelp-index}{puzzles.hhk}
\cfg{html-body-end}{}
\cfg{html-head-end}{<link rel="stylesheet" type="text/css" href="chm.css">}

View file

@ -1928,6 +1928,9 @@ Indeed, even horizontal or vertical lines may be anti-aliased.
This function may be used for both drawing and printing.
If the specified thickness is less than 1.0, 1.0 is used.
This ensures that thin lines are visible even at small scales.
\S{drawing-draw-text} \cw{draw_text()}
\c void draw_text(drawing *dr, int x, int y, int fonttype,

View file

@ -90,6 +90,8 @@ void draw_line(drawing *dr, int x1, int y1, int x2, int y2, int colour)
void draw_thick_line(drawing *dr, float thickness,
float x1, float y1, float x2, float y2, int colour)
{
if (thickness < 1.0)
thickness = 1.0;
if (dr->api->draw_thick_line) {
dr->api->draw_thick_line(dr->handle, thickness,
x1, y1, x2, y2, colour);

View file

@ -310,6 +310,8 @@ void key(int keycode, int charcode, const char *key, const char *chr,
keyevent = MOD_NUM_KEYPAD | '7';
} else if (!strnullcmp(key, "PageUp") || keycode==33) {
keyevent = MOD_NUM_KEYPAD | '9';
} else if (shift && ctrl && (keycode & 0x1F) == 26) {
keyevent = UI_REDO;
} else if (chr && chr[0] && !chr[1]) {
keyevent = chr[0] & 0xFF;
} else if (keycode >= 96 && keycode < 106) {
@ -323,10 +325,10 @@ void key(int keycode, int charcode, const char *key, const char *chr,
}
if (keyevent >= 0) {
if (shift && keyevent >= 0x100)
if (shift && (keyevent >= 0x100 && !IS_UI_FAKE_KEY(keyevent)))
keyevent |= MOD_SHFT;
if (ctrl) {
if (ctrl && !IS_UI_FAKE_KEY(keyevent)) {
if (keyevent >= 0x100)
keyevent |= MOD_CTRL;
else
@ -725,7 +727,7 @@ void command(int n)
update_undo_redo();
break;
case 5: /* New Game */
midend_process_key(me, 0, 0, 'n');
midend_process_key(me, 0, 0, UI_NEWGAME);
update_undo_redo();
js_focus_canvas();
break;
@ -735,12 +737,12 @@ void command(int n)
js_focus_canvas();
break;
case 7: /* Undo */
midend_process_key(me, 0, 0, 'u');
midend_process_key(me, 0, 0, UI_UNDO);
update_undo_redo();
js_focus_canvas();
break;
case 8: /* Redo */
midend_process_key(me, 0, 0, 'r');
midend_process_key(me, 0, 0, UI_REDO);
update_undo_redo();
js_focus_canvas();
break;
@ -756,6 +758,83 @@ void command(int n)
}
}
/* ----------------------------------------------------------------------
* Called from JS to prepare a save-game file, and free one after it's
* been used.
*/
struct savefile_write_ctx {
char *buffer;
size_t pos;
};
static void savefile_write(void *vctx, void *buf, int len)
{
struct savefile_write_ctx *ctx = (struct savefile_write_ctx *)vctx;
if (ctx->buffer)
memcpy(ctx->buffer + ctx->pos, buf, len);
ctx->pos += len;
}
char *get_save_file(void)
{
struct savefile_write_ctx ctx;
size_t size;
/* First pass, to count up the size */
ctx.buffer = NULL;
ctx.pos = 0;
midend_serialise(me, savefile_write, &ctx);
size = ctx.pos;
/* Second pass, to actually write out the data */
ctx.buffer = snewn(size, char);
ctx.pos = 0;
midend_serialise(me, savefile_write, &ctx);
assert(ctx.pos == size);
return ctx.buffer;
}
void free_save_file(char *buffer)
{
sfree(buffer);
}
struct savefile_read_ctx {
const char *buffer;
int len_remaining;
};
static int savefile_read(void *vctx, void *buf, int len)
{
struct savefile_read_ctx *ctx = (struct savefile_read_ctx *)vctx;
if (ctx->len_remaining < len)
return FALSE;
memcpy(buf, ctx->buffer, len);
ctx->len_remaining -= len;
ctx->buffer += len;
return TRUE;
}
void load_game(const char *buffer, int len)
{
struct savefile_read_ctx ctx;
const char *err;
ctx.buffer = buffer;
ctx.len_remaining = len;
err = midend_deserialise(me, savefile_read, &ctx);
if (err) {
js_error_box(err);
} else {
select_appropriate_preset();
resize();
midend_redraw(me);
}
}
/* ----------------------------------------------------------------------
* Setup function called at page load time. It's called main() because
* that's the most convenient thing in Emscripten, but it's not main()

View file

@ -108,7 +108,6 @@ mergeInto(LibraryManager.library, {
item.appendChild(tick);
item.appendChild(document.createTextNode(name));
var submenu = document.createElement("ul");
submenu.className = "left";
item.appendChild(submenu);
gametypesubmenus[menuid].appendChild(item);
var toret = gametypesubmenus.length;
@ -575,38 +574,7 @@ mergeInto(LibraryManager.library, {
* overlay on top of the rest of the puzzle web page.
*/
js_dialog_init: function(titletext) {
// Create an overlay on the page which darkens everything
// beneath it.
dlg_dimmer = document.createElement("div");
dlg_dimmer.style.width = "100%";
dlg_dimmer.style.height = "100%";
dlg_dimmer.style.background = '#000000';
dlg_dimmer.style.position = 'fixed';
dlg_dimmer.style.opacity = 0.3;
dlg_dimmer.style.top = dlg_dimmer.style.left = 0;
dlg_dimmer.style["z-index"] = 99;
// Now create a form which sits on top of that in turn.
dlg_form = document.createElement("form");
dlg_form.style.width = (window.innerWidth * 2 / 3) + "px";
dlg_form.style.opacity = 1;
dlg_form.style.background = '#ffffff';
dlg_form.style.color = '#000000';
dlg_form.style.position = 'absolute';
dlg_form.style.border = "2px solid black";
dlg_form.style.padding = "20px";
dlg_form.style.top = (window.innerHeight / 10) + "px";
dlg_form.style.left = (window.innerWidth / 6) + "px";
dlg_form.style["z-index"] = 100;
var title = document.createElement("p");
title.style.marginTop = "0px";
title.appendChild(document.createTextNode
(Pointer_stringify(titletext)));
dlg_form.appendChild(title);
dlg_return_funcs = [];
dlg_next_id = 0;
dialog_init(Pointer_stringify(titletext));
},
/*
@ -701,29 +669,13 @@ mergeInto(LibraryManager.library, {
* everything else on the page.
*/
js_dialog_launch: function() {
// Put in the OK and Cancel buttons at the bottom.
var button;
button = document.createElement("input");
button.type = "button";
button.value = "OK";
button.onclick = function(event) {
dialog_launch(function(event) {
for (var i in dlg_return_funcs)
dlg_return_funcs[i]();
command(3);
}
dlg_form.appendChild(button);
button = document.createElement("input");
button.type = "button";
button.value = "Cancel";
button.onclick = function(event) {
command(4);
}
dlg_form.appendChild(button);
document.body.appendChild(dlg_dimmer);
document.body.appendChild(dlg_form);
command(3); // OK
}, function(event) {
command(4); // Cancel
});
},
/*
@ -733,10 +685,7 @@ mergeInto(LibraryManager.library, {
* associated with it.
*/
js_dialog_cleanup: function() {
document.body.removeChild(dlg_dimmer);
document.body.removeChild(dlg_form);
dlg_dimmer = dlg_form = null;
onscreen_canvas.focus();
dialog_cleanup();
},
/*

View file

@ -129,6 +129,72 @@ function disable_menu_item(item, disabledFlag) {
item.className = "";
}
// Dialog-box functions called from both C and JS.
function dialog_init(titletext) {
// Create an overlay on the page which darkens everything
// beneath it.
dlg_dimmer = document.createElement("div");
dlg_dimmer.style.width = "100%";
dlg_dimmer.style.height = "100%";
dlg_dimmer.style.background = '#000000';
dlg_dimmer.style.position = 'fixed';
dlg_dimmer.style.opacity = 0.3;
dlg_dimmer.style.top = dlg_dimmer.style.left = 0;
dlg_dimmer.style["z-index"] = 99;
// Now create a form which sits on top of that in turn.
dlg_form = document.createElement("form");
dlg_form.style.width = (window.innerWidth * 2 / 3) + "px";
dlg_form.style.opacity = 1;
dlg_form.style.background = '#ffffff';
dlg_form.style.color = '#000000';
dlg_form.style.position = 'absolute';
dlg_form.style.border = "2px solid black";
dlg_form.style.padding = "20px";
dlg_form.style.top = (window.innerHeight / 10) + "px";
dlg_form.style.left = (window.innerWidth / 6) + "px";
dlg_form.style["z-index"] = 100;
var title = document.createElement("p");
title.style.marginTop = "0px";
title.appendChild(document.createTextNode(titletext));
dlg_form.appendChild(title);
dlg_return_funcs = [];
dlg_next_id = 0;
}
function dialog_launch(ok_function, cancel_function) {
// Put in the OK and Cancel buttons at the bottom.
var button;
if (ok_function) {
button = document.createElement("input");
button.type = "button";
button.value = "OK";
button.onclick = ok_function;
dlg_form.appendChild(button);
}
if (cancel_function) {
button = document.createElement("input");
button.type = "button";
button.value = "Cancel";
button.onclick = cancel_function;
dlg_form.appendChild(button);
}
document.body.appendChild(dlg_dimmer);
document.body.appendChild(dlg_form);
}
function dialog_cleanup() {
document.body.removeChild(dlg_dimmer);
document.body.removeChild(dlg_form);
dlg_dimmer = dlg_form = null;
onscreen_canvas.focus();
}
// Init function called from body.onload.
function initPuzzle() {
// Construct the off-screen canvas used for double buffering.
@ -230,6 +296,58 @@ function initPuzzle() {
command(9);
};
// 'number' is used for C pointers
get_save_file = Module.cwrap('get_save_file', 'number', []);
free_save_file = Module.cwrap('free_save_file', 'void', ['number']);
load_game = Module.cwrap('load_game', 'void', ['string', 'number']);
document.getElementById("save").onclick = function(event) {
if (dlg_dimmer === null) {
var savefile_ptr = get_save_file();
var savefile_text = Pointer_stringify(savefile_ptr);
free_save_file(savefile_ptr);
dialog_init("Download saved-game file");
dlg_form.appendChild(document.createTextNode(
"Click to download the "));
var a = document.createElement("a");
a.download = "puzzle.sav";
a.href = "data:application/octet-stream," +
encodeURIComponent(savefile_text);
a.appendChild(document.createTextNode("saved-game file"));
dlg_form.appendChild(a);
dlg_form.appendChild(document.createTextNode("."));
dlg_form.appendChild(document.createElement("br"));
dialog_launch(function(event) {
dialog_cleanup();
});
}
};
document.getElementById("load").onclick = function(event) {
if (dlg_dimmer === null) {
dialog_init("Upload saved-game file");
var input = document.createElement("input");
input.type = "file";
input.multiple = false;
dlg_form.appendChild(input);
dlg_form.appendChild(document.createElement("br"));
dialog_launch(function(event) {
if (input.files.length == 1) {
var file = input.files.item(0);
var reader = new FileReader();
reader.addEventListener("loadend", function() {
var string = reader.result;
load_game(string, string.length);
});
reader.readAsBinaryString(file);
}
dialog_cleanup();
}, function(event) {
dialog_cleanup();
});
}
};
gametypelist = document.getElementById("gametype");
gametypesubmenus.push(gametypelist);

View file

@ -18,6 +18,10 @@
'_timer_callback',
// Callback from button presses in the UI outside the canvas
'_command',
// Game-saving and game-loading functions
'_get_save_file',
'_free_save_file',
'_load_game',
// Callbacks to return values from dialog boxes
'_dlg_return_sval',
'_dlg_return_ival',

View file

@ -140,7 +140,7 @@ struct font {
*/
struct frontend {
GtkWidget *window;
GtkAccelGroup *accelgroup;
GtkAccelGroup *dummy_accelgroup;
GtkWidget *area;
GtkWidget *statusbar;
GtkWidget *menubar;
@ -1160,16 +1160,6 @@ static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
if (!backing_store_ok(fe))
return TRUE;
#if !GTK_CHECK_VERSION(2,0,0)
/* Gtk 1.2 passes a key event to this function even if it's also
* defined as an accelerator.
* Gtk 2 doesn't do this, and this function appears not to exist there. */
if (fe->accelgroup &&
gtk_accel_group_get_entry(fe->accelgroup,
event->keyval, event->state))
return TRUE;
#endif
/* Handle mnemonics. */
if (gtk_window_activate_key(GTK_WINDOW(fe->window), event))
return TRUE;
@ -1216,6 +1206,8 @@ static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
event->keyval == GDK_KEY_Delete ||
event->keyval == GDK_KEY_KP_Delete)
keyval = '\177';
else if ((event->keyval == 'z' || event->keyval == 'Z') && shift && ctrl)
keyval = UI_REDO;
else if (event->string[0] && !event->string[1])
keyval = (unsigned char)event->string[0];
else
@ -2348,32 +2340,34 @@ static void menu_about_event(GtkMenuItem *menuitem, gpointer data)
#endif
}
static GtkWidget *add_menu_item_with_key(frontend *fe, GtkContainer *cont,
char *text, int key)
static GtkWidget *add_menu_ui_item(
frontend *fe, GtkContainer *cont, char *text, int action,
int accel_key, int accel_keyqual)
{
GtkWidget *menuitem = gtk_menu_item_new_with_label(text);
int keyqual;
gtk_container_add(cont, menuitem);
g_object_set_data(G_OBJECT(menuitem), "user-data", GINT_TO_POINTER(key));
g_object_set_data(G_OBJECT(menuitem), "user-data",
GINT_TO_POINTER(action));
g_signal_connect(G_OBJECT(menuitem), "activate",
G_CALLBACK(menu_key_event), fe);
switch (key & ~0x1F) {
case 0x00:
key += 0x60;
keyqual = GDK_CONTROL_MASK;
break;
case 0x40:
key += 0x20;
keyqual = GDK_SHIFT_MASK;
break;
default:
keyqual = 0;
break;
if (accel_key) {
/*
* Display a keyboard accelerator alongside this menu item.
* Actually this won't be processed via the usual GTK
* accelerator system, because we add it to a dummy
* accelerator group which is never actually activated on the
* main window; this permits back ends to override special
* keys like 'n' and 'r' and 'u' in some UI states. So
* whatever keystroke we display here will still go to
* key_event and be handled in the normal way.
*/
gtk_widget_add_accelerator(menuitem,
"activate", fe->dummy_accelgroup,
accel_key, accel_keyqual,
GTK_ACCEL_VISIBLE | GTK_ACCEL_LOCKED);
}
gtk_widget_add_accelerator(menuitem,
"activate", fe->accelgroup,
key, keyqual,
GTK_ACCEL_VISIBLE);
gtk_widget_show(menuitem);
return menuitem;
}
@ -2535,8 +2529,11 @@ static frontend *new_window(char *arg, int argtype, char **error)
gtk_container_add(GTK_CONTAINER(fe->window), GTK_WIDGET(vbox));
gtk_widget_show(GTK_WIDGET(vbox));
fe->accelgroup = gtk_accel_group_new();
gtk_window_add_accel_group(GTK_WINDOW(fe->window), fe->accelgroup);
fe->dummy_accelgroup = gtk_accel_group_new();
/*
* Intentionally _not_ added to the window via
* gtk_window_add_accel_group; see menu_key_event
*/
hbox = GTK_BOX(gtk_hbox_new(FALSE, 0));
gtk_box_pack_start(vbox, GTK_WIDGET(hbox), FALSE, FALSE, 0);
@ -2553,7 +2550,7 @@ static frontend *new_window(char *arg, int argtype, char **error)
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_ui_item(fe, GTK_CONTAINER(menu), "New", UI_NEWGAME, 'n', 0);
menuitem = gtk_menu_item_new_with_label("Restart");
gtk_container_add(GTK_CONTAINER(menu), menuitem);
@ -2623,8 +2620,8 @@ static frontend *new_window(char *arg, int argtype, char **error)
gtk_widget_show(menuitem);
#ifndef STYLUS_BASED
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", 'r');
add_menu_ui_item(fe, GTK_CONTAINER(menu), "Undo", UI_UNDO, 'u', 0);
add_menu_ui_item(fe, GTK_CONTAINER(menu), "Redo", UI_REDO, 'r', 0);
#endif
if (thegame.can_format_as_text_ever) {
add_menu_separator(GTK_CONTAINER(menu));
@ -2646,7 +2643,7 @@ static frontend *new_window(char *arg, int argtype, char **error)
gtk_widget_show(menuitem);
}
add_menu_separator(GTK_CONTAINER(menu));
add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Exit", 'q');
add_menu_ui_item(fe, GTK_CONTAINER(menu), "Exit", UI_QUIT, 'q', 0);
menuitem = gtk_menu_item_new_with_mnemonic("_Help");
gtk_container_add(GTK_CONTAINER(fe->menubar), menuitem);
@ -2664,7 +2661,7 @@ static frontend *new_window(char *arg, int argtype, char **error)
#ifdef STYLUS_BASED
menuitem=gtk_button_new_with_mnemonic("_Redo");
g_object_set_data(G_OBJECT(menuitem), "user-data",
GINT_TO_POINTER((int)('r')));
GINT_TO_POINTER(UI_REDO));
g_signal_connect(G_OBJECT(menuitem), "clicked",
G_CALLBACK(menu_key_event), fe);
gtk_box_pack_end(hbox, menuitem, FALSE, FALSE, 0);
@ -2672,7 +2669,7 @@ static frontend *new_window(char *arg, int argtype, char **error)
menuitem=gtk_button_new_with_mnemonic("_Undo");
g_object_set_data(G_OBJECT(menuitem), "user-data",
GINT_TO_POINTER((int)('u')));
GINT_TO_POINTER(UI_UNDO));
g_signal_connect(G_OBJECT(menuitem), "clicked",
G_CALLBACK(menu_key_event), fe);
gtk_box_pack_end(hbox, menuitem, FALSE, FALSE, 0);

View file

@ -3,6 +3,17 @@
use strict;
use warnings;
my $jspath = "";
while ($ARGV[0] =~ /^-/) {
my $opt = shift @ARGV;
last if $opt eq "--";
if ($opt =~ /^--jspath=(.+)$/) {
$jspath = $1;
} else {
die "jspage.pl: unrecognised option '$opt'\n";
}
}
open my $footerfile, "<", shift @ARGV or die "footer: open: $!\n";
my $footer = "";
$footer .= $_ while <$footerfile>;
@ -62,7 +73,7 @@ EOF
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ASCII" />
<title>${puzzlename}, ${unfinishedtitlefragment}from Simon Tatham's Portable Puzzle Collection</title>
<script type="text/javascript" src="${filename}.js"></script>
<script type="text/javascript" src="${jspath}${filename}.js"></script>
<style class="text/css">
/* Margins and centring on the top-level div for the game menu */
#gamemenu { margin-top: 0; margin-bottom: 0.5em; text-align: center }
@ -103,6 +114,15 @@ EOF
color: rgba(0,0,0,0.5);
}
#gamemenu ul li.separator {
color: transparent;
border: 0;
}
#gamemenu ul li.afterseparator {
border-left: 1px solid rgba(0,0,0,0.3);
}
#gamemenu ul li:first-of-type {
/* Reinstate the left border for the leftmost top-level menu item */
border-left: 1px solid rgba(0,0,0,0.3);
@ -196,14 +216,19 @@ ${unfinishedpara}
<hr>
<div id="puzzle" style="display: none">
<div id="gamemenu"><ul><li id="new">New game</li
<div id="gamemenu"><ul><li>Game...<ul
><li id="specific">Enter game ID</li
><li id="random">Enter random seed</li
><li id="save">Download save file</li
><li id="load">Upload save file</li
></ul></li
><li>Type...<ul id="gametype"></ul></li
><li class="separator"></li
><li id="new" class="afterseparator">New game</li
><li id="restart">Restart game</li
><li id="undo">Undo move</li
><li id="redo">Redo move</li
><li id="solve">Solve game</li
><li id="specific">Enter game ID</li
><li id="random">Enter random seed</li
><li>Select game type<ul id="gametype" class="left"></ul></li
></ul></div>
<div align=center>
<div id="resizable" style="position:relative; left:0; top:0">

View file

@ -288,7 +288,7 @@ static void check_caches(const solver_state* sstate);
{amin, omin, \
"Width and height for this grid type must both be at least " #amin, \
"At least one of width and height for this grid type must be at least " #omin,},
enum { GRIDLIST(GRID_LOOPYTYPE) };
enum { GRIDLIST(GRID_LOOPYTYPE) LOOPY_GRID_DUMMY_TERMINATOR };
static char const *const gridnames[] = { GRIDLIST(GRID_NAME) };
#define GRID_CONFIGS GRIDLIST(GRID_CONFIG)
static grid_type grid_types[] = { GRIDLIST(GRID_GRIDTYPE) };

View file

@ -590,33 +590,40 @@ static int midend_really_process_key(midend *me, int x, int y, int button)
int type = MOVE, gottype = FALSE, ret = 1;
float anim_time;
game_state *s;
char *movestr;
movestr =
me->ourgame->interpret_move(me->states[me->statepos-1].state,
me->ui, me->drawstate, x, y, button);
char *movestr = NULL;
if (!IS_UI_FAKE_KEY(button)) {
movestr = me->ourgame->interpret_move(
me->states[me->statepos-1].state,
me->ui, me->drawstate, x, y, button);
}
if (!movestr) {
if (button == 'n' || button == 'N' || button == '\x0E') {
if (button == 'n' || button == 'N' || button == '\x0E' ||
button == UI_NEWGAME) {
midend_new_game(me);
midend_redraw(me);
goto done; /* never animate */
} else if (button == 'u' || button == 'U' ||
button == '\x1A' || button == '\x1F') {
button == '\x1A' || button == '\x1F' ||
button == UI_UNDO) {
midend_stop_anim(me);
type = me->states[me->statepos-1].movetype;
gottype = TRUE;
if (!midend_undo(me))
goto done;
} else if (button == 'r' || button == 'R' ||
button == '\x12' || button == '\x19') {
button == '\x12' || button == '\x19' ||
button == UI_REDO) {
midend_stop_anim(me);
if (!midend_redo(me))
goto done;
} else if (button == '\x13' && me->ourgame->can_solve) {
} else if ((button == '\x13' || button == UI_SOLVE) &&
me->ourgame->can_solve) {
if (midend_solve(me))
goto done;
} else if (button == 'q' || button == 'Q' || button == '\x11') {
} else if (button == 'q' || button == 'Q' || button == '\x11' ||
button == UI_QUIT) {
ret = 0;
goto done;
} else
@ -2059,6 +2066,8 @@ char *midend_deserialise(midend *me,
me->ourgame->new_drawstate(me->drawing,
me->states[me->statepos-1].state);
midend_size_new_drawstate(me);
if (me->game_id_change_notify_function)
me->game_id_change_notify_function(me->game_id_change_notify_ctx);
ret = NULL; /* success! */

View file

@ -2963,7 +2963,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
float animtime, float flashtime)
{
int x, y;
int mines, markers, bg;
int mines, markers, closed, bg;
int cx = -1, cy = -1, cmoved;
if (flashtime) {
@ -3013,13 +3013,15 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
/*
* Now draw the tiles. Also in this loop, count up the number
* of mines and mine markers.
* of mines, mine markers, and closed squares.
*/
mines = markers = 0;
mines = markers = closed = 0;
for (y = 0; y < ds->h; y++)
for (x = 0; x < ds->w; x++) {
int v = state->grid[y*ds->w+x], cc = 0;
if (v < 0)
closed++;
if (v == -1)
markers++;
if (state->layout->mines && state->layout->mines[y*ds->w+x])
@ -3078,7 +3080,42 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
else
sprintf(statusbar, "COMPLETED!");
} else {
int safe_closed = closed - mines;
sprintf(statusbar, "Marked: %d / %d", markers, mines);
if (safe_closed > 0 && safe_closed <= 9) {
/*
* In the situation where there's a very small number
* of _non_-mine squares left unopened, it's helpful
* to mention that number in the status line, to save
* the player from having to count it up
* painstakingly. This is particularly important if
* the player has turned up the mine density to the
* point where game generation resorts to its weird
* pathological fallback of a very dense mine area
* with a clearing in the middle, because that often
* leads to a deduction you can only make by knowing
* that there is (say) exactly one non-mine square to
* find, and it's a real pain to have to count up two
* large numbers of squares and subtract them to get
* that value of 1.
*
* The threshold value of 8 for displaying this
* information is because that's the largest number of
* non-mine squares that might conceivably fit around
* a single central square, and the most likely way to
* _use_ this information is to observe that if all
* the remaining safe squares are adjacent to _this_
* square then everything else can be immediately
* flagged as a mine.
*/
if (safe_closed == 1) {
sprintf(statusbar + strlen(statusbar),
" (1 safe square remains)");
} else {
sprintf(statusbar + strlen(statusbar),
" (%d safe squares remain)", safe_closed);
}
}
}
if (ui->deaths)
sprintf(statusbar + strlen(statusbar),

View file

@ -375,7 +375,7 @@ void copy_left_justified(char *buf, size_t sz, const char *str)
/* another kludge for platforms without %g support in *printf() */
int ftoa(char *buf, float f)
{
return sprintf(buf, "%d.%06d", (int)f, (int)((f - (int)f)*1e6));
return sprintf(buf, "%d.%06d", (int)f, abs((int)((f - (int)f)*1e6)));
}
/* vim: set shiftwidth=4 tabstop=8: */

View file

@ -319,7 +319,7 @@ sub mfval($) {
# Returns true if the argument is a known makefile type. Otherwise,
# prints a warning and returns false;
if (grep { $type eq $_ }
("vc","vcproj","cygwin","borland","lcc","gtk","am","mpw","nestedvm","osx","wce","gnustep","emcc")) {
("vc","vcproj","cygwin","borland","lcc","gtk","am","mpw","nestedvm","osx","wce","gnustep","emcc","clangcl")) {
return 1;
}
warn "$.:unknown makefile type '$type'\n";
@ -503,6 +503,151 @@ $orig_dir = cwd;
# Now we're ready to output the actual Makefiles.
if (defined $makefiles{'clangcl'}) {
$mftyp = 'clangcl';
$dirpfx = &dirpfx($makefiles{'clangcl'}, "/");
##-- Makefile for cross-compiling using clang-cl, lld-link, and
## MinGW's windres for resource compilation.
#
# This makefile allows a complete Linux-based cross-compile, but
# using the real Visual Studio header files and libraries. In
# order to run it, you will need:
#
# - MinGW windres on your PATH.
# * On Ubuntu as of 16.04, you can apt-get install
# binutils-mingw-w64-x86-64 and binutils-mingw-w64-i686
# which will provide (respectively) 64- and 32-bit versions,
# under the names to which RCCMD is defined below.
# - clang-cl and lld-link on your PATH.
# * I built these from the up-to-date LLVM project trunk git
# repositories, as of 2017-02-05.
# - case-mashed copies of the Visual Studio include directories.
# * On a real VS installation, run vcvars32.bat and look at
# the resulting value of %INCLUDE%. Take a full copy of each
# of those directories, and inside the copy, for each
# include file that has an uppercase letter in its name,
# make a lowercased symlink to it. Additionally, one of the
# directories will contain files called driverspecs.h and
# specstrings.h, and those will need symlinks called
# DriverSpecs.h and SpecStrings.h.
# * Now, on Linux, define the environment variable INCLUDE to
# be a list, separated by *semicolons* (in the Windows
# style), of those directories, but before all of them you
# must also include lib/clang/5.0.0/include from the clang
# installation area (which contains in particular a
# clang-compatible stdarg.h overriding the Visual Studio
# one).
# - similarly case-mashed copies of the library directories.
# * Again, on a real VS installation, run vcvars32 or
# vcvarsx86_amd64 (as appropriate), look at %LIB%, make a
# copy of each directory, and provide symlinks within that
# directory so that all the files can be opened as
# lowercase.
# * Then set LIB to be a semicolon-separated list of those
# directories (but you'll need to change which set of
# directories depending on whether you want to do a 32-bit
# or 64-bit build).
# - for a 64-bit build, set 'Platform=x64' in the environment as
# well, or else on the make command line.
# * This is a variable understood only by this makefile - none
# of the tools we invoke will know it - but it's consistent
# with the way the VS scripts like vcvarsx86_amd64.bat set
# things up, and since the environment has to change
# _anyway_ between 32- and 64-bit builds (different set of
# paths in $LIB) it's reasonable to have the choice of
# compilation target driven by another environment variable
# set in parallel with that one.
# - for older versions of the VS libraries you may also have to
# set EXTRA_console and/or EXTRA_windows to the name of an
# object file manually extracted from one of those libraries.
# * This is because old VS seems to manage its startup code by
# having libcmt.lib contain lots of *crt0.obj objects, one
# for each possible user entry point (main, WinMain and the
# wide-char versions of both), of which the linker arranges
# to include the right one by special-case code. But lld
# only seems to mimic half of that code - it does include
# the right crt0 object, but it doesn't also deliberately
# _avoid_ including the _wrong_ ones, and since all those
# objects define a common set of global symbols for other
# parts of the library to use, lld may well select an
# arbitrary one of them the first time it sees a reference
# to one of those global symbols, and then later also select
# the _right_ one for the application's entry point, causing
# a multiple-definitions crash.
# * So the workaround is to explicitly include the right
# *crt0.obj file on the linker command line before lld even
# begins searching libraries. Hence, for a console
# application, you might extract crt0.obj from the library
# in question and set EXTRA_console=crt0.obj, and for a GUI
# application, do the same with wincrt0.obj. Then this
# makefile will include the right one of those objects
# alongside the matching /subsystem linker option.
open OUT, ">$makefiles{'clangcl'}"; select OUT;
print
"# Makefile for cross-compiling $project_name using clang-cl, lld-link,\n".
"# and MinGW's windres, using GNU make on Linux.\n".
"#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n".
"# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n";
print $help;
print
"\n".
"CCCMD = clang-cl\n".
"ifeq (\$(Platform),x64)\n".
"CCTARGET = x86_64-pc-windows-msvc18.0.0\n".
"RCCMD = x86_64-w64-mingw32-windres\n".
"else\n".
"CCTARGET = i386-pc-windows-msvc18.0.0\n".
"RCCMD = i686-w64-mingw32-windres\n".
"endif\n".
"CC = \$(CCCMD) --target=\$(CCTARGET)\n".
&splitline("RC = \$(RCCMD) --preprocessor=\$(CCCMD) ".
"--preprocessor-arg=/TC --preprocessor-arg=/E")."\n".
"LD = lld-link\n".
"\n".
"# C compilation flags\n".
&splitline("CFLAGS = /nologo /W3 /O1 " .
(join " ", map {"-I$dirpfx$_"} @srcdirs) .
" /D_WINDOWS /D_WIN32_WINDOWS=0x401 /DWINVER=0x401 ".
"/D_CRT_SECURE_NO_WARNINGS")."\n".
"LFLAGS = /incremental:no /dynamicbase /nxcompat\n".
&splitline("RCFLAGS = ".(join " ", map {"-I$dirpfx$_"} @srcdirs).
" -DWIN32 -D_WIN32 -DWINVER=0x0400 --define MINGW32_FIX=1")."\n".
"\n".
"\n";
print &splitline("all:" . join "", map { " \$(BUILDDIR)$_.exe" } &progrealnames("G:C"));
print "\n\n";
foreach $p (&prognames("G:C")) {
($prog, $type) = split ",", $p;
$objstr = &objects($p, "\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", undef);
print &splitline("\$(BUILDDIR)$prog.exe: " . $objstr), "\n";
$objstr = &objects($p, "\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", "X.lib");
$subsys = ($type eq "G") ? "windows" : "console";
print &splitline("\t\$(LD) \$(LFLAGS) \$(XLFLAGS) ".
"/out:\$(BUILDDIR)$prog.exe ".
"/lldmap:\$(BUILDDIR)$prog.map ".
"/subsystem:$subsys\$(SUBSYSVER) ".
"\$(EXTRA_$subsys) $objstr")."\n\n";
}
foreach $d (&deps("\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", $dirpfx, "/", "vc")) {
print &splitline(sprintf("%s: %s", $d->{obj},
join " ", @{$d->{deps}})), "\n";
if ($d->{obj} =~ /\.res$/) {
print "\t\$(RC) \$(RCFLAGS) ".$d->{deps}->[0]." -o ".$d->{obj}."\n\n";
} else {
$deflist = join "", map { " /D$_" } @{$d->{defs}};
print "\t\$(CC) /Fo\$(BUILDDIR)".$d->{obj}." \$(COMPAT) \$(CFLAGS) \$(XFLAGS)$deflist /c \$<\n\n";
}
}
print "\nclean:\n".
&splitline("\trm -f \$(BUILDDIR)*.obj \$(BUILDDIR)*.exe ".
"\$(BUILDDIR)*.res \$(BUILDDIR)*.map ".
"\$(BUILDDIR)*.exe.manifest")."\n";
select STDOUT; close OUT;
}
if (defined $makefiles{'cygwin'}) {
$mftyp = 'cygwin';
$dirpfx = &dirpfx($makefiles{'cygwin'}, "/");

View file

@ -305,10 +305,34 @@ static int get_config(frontend *fe, int which)
return fe->cfgret;
}
int jcallback_menu_key_event(int key)
int jcallback_newgame_event(void)
{
frontend *fe = (frontend *)_fe;
if (!midend_process_key(fe->me, 0, 0, key))
if (!midend_process_key(fe->me, 0, 0, UI_NEWGAME))
return 42;
return 0;
}
int jcallback_undo_event(void)
{
frontend *fe = (frontend *)_fe;
if (!midend_process_key(fe->me, 0, 0, UI_UNDO))
return 42;
return 0;
}
int jcallback_redo_event(void)
{
frontend *fe = (frontend *)_fe;
if (!midend_process_key(fe->me, 0, 0, UI_REDO))
return 42;
return 0;
}
int jcallback_quit_event(void)
{
frontend *fe = (frontend *)_fe;
if (!midend_process_key(fe->me, 0, 0, UI_QUIT))
return 42;
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -687,6 +687,10 @@ struct frontend {
if (c >= '0' && c <= '9' && ([ev modifierFlags] & NSNumericPadKeyMask))
c |= MOD_NUM_KEYPAD;
if (c == 26 &&
!((NSShiftKeyMask | NSControlKeyMask) & ~[ev modifierFlags]))
c = UI_REDO;
[self processKey:c];
}
}
@ -735,7 +739,7 @@ struct frontend {
- (void)newGame:(id)sender
{
[self processKey:'n'];
[self processKey:UI_NEWGAME];
}
- (void)restartGame:(id)sender
{
@ -809,11 +813,11 @@ struct frontend {
}
- (void)undoMove:(id)sender
{
[self processKey:'u'];
[self processKey:UI_UNDO];
}
- (void)redoMove:(id)sender
{
[self processKey:'r'&0x1F];
[self processKey:UI_REDO];
}
- (void)copy:(id)sender

View file

@ -310,7 +310,18 @@ static void generate(random_state *rs, int w, int h, unsigned char *retgrid)
fgrid2 = snewn(w*h, float);
memcpy(fgrid2, fgrid, w*h*sizeof(float));
qsort(fgrid2, w*h, sizeof(float), float_compare);
threshold = fgrid2[w*h/2];
/* Choose a threshold that makes half the pixels black. In case of
* an odd number of pixels, select randomly between just under and
* just over half. */
{
int index = w * h / 2;
if (w & h & 1)
index += random_upto(rs, 2);
if (index < w*h)
threshold = fgrid2[index];
else
threshold = fgrid2[w*h-1] + 1;
}
sfree(fgrid2);
for (i = 0; i < h; i++) {
@ -448,6 +459,8 @@ static int do_row(unsigned char *known, unsigned char *deduced,
if (rowlen == 0) {
memset(deduced, DOT, len);
} else if (rowlen == 1 && data[0] == len) {
memset(deduced, BLOCK, len);
} else {
do_recurse(known, deduced, row, minpos_done, maxpos_done, minpos_ok,
maxpos_ok, data, len, freespace, 0, 0);

View file

@ -47,6 +47,15 @@ enum {
CURSOR_RIGHT,
CURSOR_SELECT,
CURSOR_SELECT2,
/* UI_* are special keystrokes generated by front ends in response
* to menu actions, never passed to back ends */
UI_LOWER_BOUND,
UI_QUIT,
UI_NEWGAME,
UI_SOLVE,
UI_UNDO,
UI_REDO,
UI_UPPER_BOUND,
/* made smaller because of 'limited range of datatype' errors. */
MOD_CTRL = 0x1000,
@ -64,6 +73,7 @@ enum {
#define IS_CURSOR_MOVE(m) ( (m) == CURSOR_UP || (m) == CURSOR_DOWN || \
(m) == CURSOR_RIGHT || (m) == CURSOR_LEFT )
#define IS_CURSOR_SELECT(m) ( (m) == CURSOR_SELECT || (m) == CURSOR_SELECT2)
#define IS_UI_FAKE_KEY(m) ( (m) > UI_LOWER_BOUND && (m) < UI_UPPER_BOUND )
/*
* Flags in the back end's `flags' word.

View file

@ -1718,7 +1718,10 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
#define TILE_SIZE (ds->sz6*6)
#define BORDER (TILE_SIZE/8)
#define BORDER_WIDTH (max(TILE_SIZE / 32, 1))
#define LINE_THICK (TILE_SIZE/16)
#define GRID_LINE_TL (ds->grid_line_tl)
#define GRID_LINE_BR (ds->grid_line_br)
#define GRID_LINE_ALL (ds->grid_line_all)
#define COORD(x) ( (x+1) * TILE_SIZE + BORDER )
#define CENTERED_COORD(x) ( COORD(x) + TILE_SIZE/2 )
@ -1738,7 +1741,7 @@ static void game_changed_state(game_ui *ui, const game_state *oldstate,
#define DS_CSHIFT 20 /* R/U/L/D shift, for cursor-on-edge */
struct game_drawstate {
int sz6;
int sz6, grid_line_all, grid_line_tl, grid_line_br;
int started;
int w, h, sz;
@ -2118,7 +2121,6 @@ static void game_compute_size(const game_params *params, int tilesize,
int sz6;
} ads, *ds = &ads;
ads.sz6 = tilesize/6;
*x = (params->w+2) * TILE_SIZE + 2 * BORDER;
*y = (params->h+2) * TILE_SIZE + 2 * BORDER;
}
@ -2127,6 +2129,9 @@ static void game_set_size(drawing *dr, game_drawstate *ds,
const game_params *params, int tilesize)
{
ds->sz6 = tilesize/6;
ds->grid_line_all = max(LINE_THICK, 1);
ds->grid_line_br = ds->grid_line_all / 2;
ds->grid_line_tl = ds->grid_line_all - ds->grid_line_br;
}
enum {
@ -2346,14 +2351,13 @@ static void draw_square(drawing *dr, game_drawstate *ds,
/* Clip to the grid square. */
clip(dr, ox, oy, TILE_SIZE, TILE_SIZE);
/* Clear the square. */
/* Clear the square so that it's got an appropriately-sized border
* in COL_GRID and a central area in the right background colour. */
best_bits((flags & DS_TRACK) == DS_TRACK,
(flags_drag & DS_TRACK) == DS_TRACK, &bg);
draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, bg);
/* Draw outline of grid square */
draw_line(dr, ox, oy, COORD(x+1), oy, COL_GRID);
draw_line(dr, ox, oy, ox, COORD(y+1), COL_GRID);
draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, COL_GRID);
draw_rect(dr, ox + GRID_LINE_TL, oy + GRID_LINE_TL,
TILE_SIZE - GRID_LINE_ALL, TILE_SIZE - GRID_LINE_ALL, bg);
/* More outlines for clue squares. */
if (flags & DS_CURSOR) {
@ -2389,8 +2393,8 @@ static void draw_square(drawing *dr, game_drawstate *ds,
(flags_drag & DS_NOTRACK) == DS_NOTRACK, &c);
if (flags_best) {
off = HALFSZ/2;
draw_line(dr, cx - off, cy - off, cx + off, cy + off, c);
draw_line(dr, cx - off, cy + off, cx + off, cy - off, c);
draw_thick_line(dr, LINE_THICK, cx - off, cy - off, cx + off, cy + off, c);
draw_thick_line(dr, LINE_THICK, cx - off, cy + off, cx + off, cy - off, c);
}
c = COL_TRACK;
@ -2404,8 +2408,8 @@ static void draw_square(drawing *dr, game_drawstate *ds,
cx += (d == R) ? t2 : (d == L) ? -t2 : 0;
cy += (d == D) ? t2 : (d == U) ? -t2 : 0;
draw_line(dr, cx - off, cy - off, cx + off, cy + off, c);
draw_line(dr, cx - off, cy + off, cx + off, cy - off, c);
draw_thick_line(dr, LINE_THICK, cx - off, cy - off, cx + off, cy + off, c);
draw_thick_line(dr, LINE_THICK, cx - off, cy + off, cx + off, cy - off, c);
}
}
@ -2426,12 +2430,14 @@ static void draw_clue(drawing *dr, game_drawstate *ds, int w, int clue, int i, i
cy = CENTERED_COORD(i-w);
}
draw_rect(dr, cx - tsz + BORDER, cy - tsz + BORDER,
TILE_SIZE - BORDER, TILE_SIZE - BORDER, COL_BACKGROUND);
draw_rect(dr, cx - tsz + GRID_LINE_TL, cy - tsz + GRID_LINE_TL,
TILE_SIZE - GRID_LINE_ALL, TILE_SIZE - GRID_LINE_ALL,
COL_BACKGROUND);
sprintf(buf, "%d", clue);
draw_text(dr, cx, cy, FONT_VARIABLE, tsz, ALIGN_VCENTRE|ALIGN_HCENTRE,
col, buf);
draw_update(dr, cx - tsz, cy - tsz, TILE_SIZE, TILE_SIZE);
draw_update(dr, cx - tsz + GRID_LINE_TL, cy - tsz + GRID_LINE_TL,
TILE_SIZE - GRID_LINE_ALL, TILE_SIZE - GRID_LINE_ALL);
}
static void draw_loop_ends(drawing *dr, game_drawstate *ds,
@ -2498,8 +2504,9 @@ static void game_redraw(drawing *dr, game_drawstate *ds, const game_state *oldst
draw_loop_ends(dr, ds, state, COL_CLUE);
draw_line(dr, COORD(ds->w), COORD(0), COORD(ds->w), COORD(ds->h), COL_GRID);
draw_line(dr, COORD(0), COORD(ds->h), COORD(ds->w), COORD(ds->h), COL_GRID);
draw_rect(dr, COORD(0) - GRID_LINE_BR, COORD(0) - GRID_LINE_BR,
ds->w * TILE_SIZE + GRID_LINE_ALL,
ds->h * TILE_SIZE + GRID_LINE_ALL, COL_GRID);
draw_update(dr, 0, 0, (w+2)*TILE_SIZE + 2*BORDER, (h+2)*TILE_SIZE + 2*BORDER);

View file

@ -27,7 +27,7 @@ while (<$desc>) {
'<span class="puzzle"><table>'.
'<tr><th align="center">%s</th></tr>'.
'<tr><td align="center">'.
'<img style="margin: 0.5em" alt="" title="%s" width=150 height=150 border=0 src="%s-web.png" />'.
'<a href="js/%s.html"><img style="margin: 0.5em" alt="" title="%s" width=150 height=150 border=0 src="%s-web.png" /></a>'.
'</td></tr>'.
'<tr><td align="center" style="font-size: 70%%"><code>[</code>'.
' <a href="java/%s.html">java</a> '.
@ -41,6 +41,7 @@ while (<$desc>) {
'<tr><td align="center">%s</td></tr></table></span>'.
"\n",
encode_entities($displayname),
encode_entities($id),
encode_entities($description),
encode_entities($id),
encode_entities($id),

View file

@ -1545,7 +1545,7 @@ static frontend *frontend_new(HINSTANCE inst)
fe->statusbar = NULL;
fe->bitmap = NULL;
SetWindowLong(fe->hwnd, GWL_USERDATA, (LONG)fe);
SetWindowLongPtr(fe->hwnd, GWLP_USERDATA, (LONG_PTR)fe);
return fe;
}
@ -1992,7 +1992,7 @@ static void make_dialog_full_screen(HWND hwnd)
static int CALLBACK AboutDlgProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
frontend *fe = (frontend *)GetWindowLong(hwnd, GWL_USERDATA);
frontend *fe = (frontend *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
switch (msg) {
case WM_INITDIALOG:
@ -2249,7 +2249,7 @@ static void create_config_controls(frontend * fe)
static int CALLBACK ConfigDlgProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
frontend *fe = (frontend *)GetWindowLong(hwnd, GWL_USERDATA);
frontend *fe = (frontend *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
config_item *i;
struct cfg_aux *j;
@ -2260,7 +2260,7 @@ static int CALLBACK ConfigDlgProc(HWND hwnd, UINT msg,
char *title;
fe = (frontend *) lParam;
SetWindowLong(hwnd, GWL_USERDATA, lParam);
SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
fe->cfgbox = hwnd;
fe->cfg = frontend_get_config(fe, fe->cfg_which, &title);
@ -2479,8 +2479,8 @@ static void about(frontend *fe)
SendMessage(fe->cfgbox, WM_SETFONT, (WPARAM)fe->cfgfont, FALSE);
SetWindowLong(fe->cfgbox, GWL_USERDATA, (LONG)fe);
SetWindowLong(fe->cfgbox, DWL_DLGPROC, (LONG)AboutDlgProc);
SetWindowLongPtr(fe->cfgbox, GWLP_USERDATA, (LONG_PTR)fe);
SetWindowLongPtr(fe->cfgbox, DWLP_DLGPROC, (LONG_PTR)AboutDlgProc);
id = 1000;
y = height/2;
@ -2660,8 +2660,8 @@ static int get_config(frontend *fe, int which)
SendMessage(fe->cfgbox, WM_SETFONT, (WPARAM)fe->cfgfont, FALSE);
SetWindowLong(fe->cfgbox, GWL_USERDATA, (LONG)fe);
SetWindowLong(fe->cfgbox, DWL_DLGPROC, (LONG)ConfigDlgProc);
SetWindowLongPtr(fe->cfgbox, GWLP_USERDATA, (LONG_PTR)fe);
SetWindowLongPtr(fe->cfgbox, DWLP_DLGPROC, (LONG_PTR)ConfigDlgProc);
/*
* Count the controls so we can allocate cfgaux.
@ -2975,7 +2975,7 @@ static int is_alt_pressed(void)
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
frontend *fe = (frontend *)GetWindowLong(hwnd, GWL_USERDATA);
frontend *fe = (frontend *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
int cmd;
switch (message) {
@ -2993,18 +2993,18 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
cmd = wParam & ~0xF; /* low 4 bits reserved to Windows */
switch (cmd) {
case IDM_NEW:
if (!midend_process_key(fe->me, 0, 0, 'n'))
if (!midend_process_key(fe->me, 0, 0, UI_NEWGAME))
PostQuitMessage(0);
break;
case IDM_RESTART:
midend_restart_game(fe->me);
break;
case IDM_UNDO:
if (!midend_process_key(fe->me, 0, 0, 'u'))
if (!midend_process_key(fe->me, 0, 0, UI_UNDO))
PostQuitMessage(0);
break;
case IDM_REDO:
if (!midend_process_key(fe->me, 0, 0, '\x12'))
if (!midend_process_key(fe->me, 0, 0, UI_REDO))
PostQuitMessage(0);
break;
case IDM_COPY:
@ -3026,7 +3026,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
}
break;
case IDM_QUIT:
if (!midend_process_key(fe->me, 0, 0, 'q'))
if (!midend_process_key(fe->me, 0, 0, UI_QUIT))
PostQuitMessage(0);
break;
case IDM_CONFIG:
@ -3405,8 +3405,18 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
}
break;
case WM_CHAR:
if (!midend_process_key(fe->me, 0, 0, (unsigned char)wParam))
PostQuitMessage(0);
{
int key = (unsigned char)wParam;
if (key == '\x1A') {
BYTE keystate[256];
if (GetKeyboardState(keystate) &&
(keystate[VK_SHIFT] & 0x80) &&
(keystate[VK_CONTROL] & 0x80))
key = UI_REDO;
}
if (!midend_process_key(fe->me, 0, 0, key))
PostQuitMessage(0);
}
return 0;
case WM_TIMER:
if (fe->timer) {

View file

@ -61,7 +61,7 @@ has 'descfile' => (required => 1);
% # (individual files or shortcuts or additions to PATH) that are
% # installed.
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder" Name="PFiles">
<Directory Id="ProgramFiles64Folder" Name="PFiles">
<Directory Id="INSTALLDIR" Name="Simon Tatham's Portable Puzzle Collection">
% # The following components all install things in the main