diff --git a/LICENCE b/LICENCE index 8cfd89d..a3937f4 100644 --- a/LICENCE +++ b/LICENCE @@ -1,7 +1,7 @@ This software is copyright (c) 2004-2007 Simon Tatham. -Portions copyright Richard Boulton, James Harvey, Mike Pinna and -Jonas Kölker. +Portions copyright Richard Boulton, James Harvey, Mike Pinna, Jonas +Kölker and Dariusz Olszewski. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files diff --git a/Recipe b/Recipe index b2c6502..885b0d7 100644 --- a/Recipe +++ b/Recipe @@ -10,6 +10,7 @@ !makefile gtk Makefile !makefile vc Makefile.vc +!makefile wce Makefile.wce !makefile cygwin Makefile.cyg !makefile osx Makefile.osx @@ -89,6 +90,11 @@ version.obj: *.c *.h cl $(VER) $(CFLAGS) /c version.c !end !specialobj vc version +!begin wce +version.obj: *.c *.h + $(CC) $(VER) $(CFLAGS) /c version.c +!end +!specialobj wce version !begin cygwin version.o: FORCE; FORCE: diff --git a/blackbox.c b/blackbox.c index dbb4999..458c0b8 100644 --- a/blackbox.c +++ b/blackbox.c @@ -1431,7 +1431,7 @@ const struct game thegame = { FALSE, FALSE, game_print_size, game_print, TRUE, /* wants_statusbar */ FALSE, game_timing_state, - 0, /* flags */ + REQUIRE_RBUTTON, /* flags */ }; /* vim: set shiftwidth=4 tabstop=8: */ diff --git a/bridges.c b/bridges.c index a8fd8b3..d56f287 100644 --- a/bridges.c +++ b/bridges.c @@ -2662,7 +2662,7 @@ const struct game thegame = { TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, - 0, /* flags */ + REQUIRE_RBUTTON, /* flags */ }; /* vim: set shiftwidth=4 tabstop=8: */ diff --git a/filling.c b/filling.c index fba86e5..6f6c6c3 100644 --- a/filling.c +++ b/filling.c @@ -47,7 +47,6 @@ #include #include -#include #include #include #include @@ -984,9 +983,8 @@ static game_state *execute_move(game_state *state, char *move) new_state->cheated = TRUE; } else { char *endptr; - const int i = strtol(move, &endptr, errno = 0); + const int i = strtol(move, &endptr, 0); int value; - if (errno == ERANGE) return NULL; if (endptr == move) return NULL; if (*endptr != '_') return NULL; move = endptr + 1; diff --git a/galaxies.c b/galaxies.c index 9487a54..d7a90a4 100644 --- a/galaxies.c +++ b/galaxies.c @@ -3234,7 +3234,7 @@ const struct game thegame = { FALSE, /* wants_statusbar */ #endif FALSE, game_timing_state, - 0, /* flags */ + REQUIRE_RBUTTON, /* flags */ }; #ifdef STANDALONE_SOLVER diff --git a/icons/Makefile b/icons/Makefile index 78abb35..3ab0e99 100644 --- a/icons/Makefile +++ b/icons/Makefile @@ -126,7 +126,8 @@ $(ICONS): %.ico: %-48d24.png %-48d8.png %-48d4.png \ # Build the .RC files which bind the icons into the applications. $(RC): %.rc: - echo '200 ICON "$*.ico"' > $@ + echo '#include "puzzles.rc2"' > $@ + echo '200 ICON "$*.ico"' >> $@ # Build the GTK icon source files. $(CICONS): %-icon.c: %-16d24.png %-32d24.png %-48d24.png diff --git a/inertia.c b/inertia.c index 10ea538..4e84f56 100644 --- a/inertia.c +++ b/inertia.c @@ -88,8 +88,11 @@ static game_params *default_params(void) game_params *ret = snew(game_params); ret->w = 10; +#ifdef PORTRAIT_SCREEN + ret->h = 10; +#else ret->h = 8; - +#endif return ret; } @@ -106,9 +109,15 @@ static game_params *dup_params(game_params *params) } static const struct game_params inertia_presets[] = { +#ifdef PORTRAIT_SCREEN + { 10, 10 }, + { 12, 12 }, + { 16, 16 }, +#else { 10, 8 }, { 15, 12 }, { 20, 16 }, +#endif }; static int game_fetch_preset(int i, char **name, game_params **params) @@ -1511,7 +1520,11 @@ struct game_drawstate { #define PREFERRED_TILESIZE 32 #define TILESIZE (ds->tilesize) +#ifdef SMALL_SCREEN +#define BORDER (TILESIZE / 4) +#else #define BORDER (TILESIZE) +#endif #define HIGHLIGHT_WIDTH (TILESIZE / 10) #define COORD(x) ( (x) * TILESIZE + BORDER ) #define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 ) diff --git a/latin.c b/latin.c index 2d6ae74..dc2af8f 100644 --- a/latin.c +++ b/latin.c @@ -12,6 +12,11 @@ #include "latin.h" +static void assert_f(p) +{ + assert(p); +} + /* -------------------------------------------------------- * Solver. */ @@ -26,7 +31,7 @@ void latin_solver_place(struct latin_solver *solver, int x, int y, int n) int i, o = solver->o; assert(n <= o); - assert(cube(x,y,n)); + assert_f(cube(x,y,n)); /* * Rule out all other numbers in this square. @@ -1188,7 +1193,7 @@ int latin_check(digit *sq, int order) lcp = snew(lcparams); lcp->elt = ELT(sq, c, r); lcp->count = 1; - assert(add234(dict, lcp) == lcp); + assert_f(add234(dict, lcp) == lcp); } else { lcp->count++; } diff --git a/loopy.c b/loopy.c index 8f1e345..f0344fe 100644 --- a/loopy.c +++ b/loopy.c @@ -512,10 +512,12 @@ static const game_params presets[] = { { 15, 15, DIFF_EASY, 0 }, { 15, 15, DIFF_NORMAL, 0 }, { 15, 15, DIFF_HARD, 0 }, +#ifndef SMALL_SCREEN { 30, 20, DIFF_EASY, 0 }, { 30, 20, DIFF_NORMAL, 0 }, { 30, 20, DIFF_HARD, 0 } #endif +#endif }; static int game_fetch_preset(int i, char **name, game_params **params) diff --git a/map.c b/map.c index 098fc47..b6b6194 100644 --- a/map.c +++ b/map.c @@ -100,8 +100,13 @@ static game_params *default_params(void) { game_params *ret = snew(game_params); +#ifdef PORTRAIT_SCREEN + ret->w = 16; + ret->h = 18; +#else ret->w = 20; ret->h = 15; +#endif ret->n = 30; ret->diff = DIFF_NORMAL; @@ -109,12 +114,21 @@ static game_params *default_params(void) } static const struct game_params map_presets[] = { +#ifdef PORTRAIT_SCREEN + {16, 18, 30, DIFF_EASY}, + {16, 18, 30, DIFF_NORMAL}, + {16, 18, 30, DIFF_HARD}, + {16, 18, 30, DIFF_RECURSE}, + {25, 30, 75, DIFF_NORMAL}, + {25, 30, 75, DIFF_HARD}, +#else {20, 15, 30, DIFF_EASY}, {20, 15, 30, DIFF_NORMAL}, {20, 15, 30, DIFF_HARD}, {20, 15, 30, DIFF_RECURSE}, {30, 25, 75, DIFF_NORMAL}, {30, 25, 75, DIFF_HARD}, +#endif }; static int game_fetch_preset(int i, char **name, game_params **params) @@ -2517,10 +2531,18 @@ static void game_set_size(drawing *dr, game_drawstate *ds, } const float map_colours[FOUR][3] = { +#ifdef VIVID_COLOURS + // Use more vivid colours (e.g. on the Pocket PC) + {0.75F, 0.25F, 0.25F}, + {0.3F, 0.7F, 0.3F}, + {0.3F, 0.3F, 0.7F}, + {0.85F, 0.85F, 0.1F}, +#else {0.7F, 0.5F, 0.4F}, {0.8F, 0.7F, 0.4F}, {0.5F, 0.6F, 0.4F}, {0.55F, 0.45F, 0.35F}, +#endif }; const int map_hatching[FOUR] = { HATCH_VERT, HATCH_SLASH, HATCH_HORIZ, HATCH_BACKSLASH diff --git a/mines.c b/mines.c index 73aea0b..3c4dc13 100644 --- a/mines.c +++ b/mines.c @@ -28,7 +28,11 @@ enum { #define PREFERRED_TILE_SIZE 20 #define TILE_SIZE (ds->tilesize) +#ifdef SMALL_SCREEN +#define BORDER 8 +#else #define BORDER (TILE_SIZE * 3 / 2) +#endif #define HIGHLIGHT_WIDTH (TILE_SIZE / 10) #define OUTER_HIGHLIGHT_WIDTH (BORDER / 10) #define COORD(x) ( (x) * TILE_SIZE + BORDER ) @@ -102,8 +106,10 @@ static const struct game_params mines_presets[] = { {9, 9, 35, TRUE}, {16, 16, 40, TRUE}, {16, 16, 99, TRUE}, +#ifndef SMALL_SCREEN {30, 16, 99, TRUE}, {30, 16, 170, TRUE}, +#endif }; static int game_fetch_preset(int i, char **name, game_params **params) @@ -3103,7 +3109,7 @@ const struct game thegame = { FALSE, FALSE, game_print_size, game_print, TRUE, /* wants_statusbar */ TRUE, game_timing_state, - BUTTON_BEATS(LEFT_BUTTON, RIGHT_BUTTON), + BUTTON_BEATS(LEFT_BUTTON, RIGHT_BUTTON) | REQUIRE_RBUTTON, }; #ifdef STANDALONE_OBFUSCATOR diff --git a/mkfiles.pl b/mkfiles.pl index 52c9a74..b25d86c 100755 --- a/mkfiles.pl +++ b/mkfiles.pl @@ -284,7 +284,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","mpw","osx")) { + ("vc","vcproj","cygwin","borland","lcc","gtk","mpw","osx","wce")) { return 1; } warn "$.:unknown makefile type '$type'\n"; @@ -719,6 +719,114 @@ if (defined $makefiles{'vc'}) { select STDOUT; close OUT; } +if (defined $makefiles{'wce'}) { + $mftyp = 'wce'; + $dirpfx = &dirpfx($makefiles{'wce'}, "\\"); + + ##-- eMbedded Visual C PocketPC makefile + open OUT, ">$makefiles{'wce'}"; select OUT; + print + "# Makefile for $project_name on PocketPC using eMbedded Visual C.\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". + "# If you rename this file to `Makefile', you should change this line,\n". + "# so that the .rsp files still depend on the correct makefile.\n". + "MAKEFILE = Makefile.wce\n". + "\n". + "# This makefile expects the environment to have been set up by one\n". + "# of the PocketPC batch files wcearmv4.bat and wceemulator.bat. No\n". + "# other build targets are currently supported, because they would\n". + "# need a section in this if statement.\n". + "!if \"\$(TARGETCPU)\" == \"emulator\"\n". + "PLATFORM_DEFS=/D \"_i386_\" /D \"i_386_\" /D \"_X86_\" /D \"x86\"\n". + "CC=cl\n". + "BASELIBS=commctrl.lib coredll.lib corelibc.lib aygshell.lib\n". + "MACHINE=IX86\n". + "!else\n". + "PLATFORM_DEFS=/D \"ARM\" /D \"_ARM_\" /D \"ARMV4\"\n". + "CC=clarm\n". + "BASELIBS=commctrl.lib coredll.lib aygshell.lib\n". + "MACHINE=ARM\n". + "!endif\n". + "\n". + "# C compilation flags\n". + "CFLAGS = /nologo /W3 /O1 /MC /D _WIN32_WCE=420 /D \"WIN32_PLATFORM_PSPC=400\" /D UNDER_CE=420 \\\n". + " \$(PLATFORM_DEFS) \\\n". + " /D \"UNICODE\" /D \"_UNICODE\" /D \"NDEBUG\" /D \"NO_HTMLHELP\"\n". + "\n". + "LFLAGS = /nologo /incremental:no \\\n". + " /base:0x00010000 /stack:0x10000,0x1000 /entry:WinMainCRTStartup \\\n". + " /nodefaultlib:libc.lib /nodefaultlib:libcmt.lib /nodefaultlib:msvcrt.lib /nodefaultlib:OLDNAMES.lib \\\n". + " /subsystem:windowsce,4.20 /align:4096 /MACHINE:\$(MACHINE)\n". + "\n". + "RCFL = /d UNDER_CE=420 /d _WIN32_WCE=420 /d \"WIN32_PLATFORM_PSPC=400\" \\\n". + " \$(PLATFORM_DEFS) \\\n". + " /d \"NDEBUG\" /d \"UNICODE\" /d \"_UNICODE\"\n". + "\n"; + print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G")); + print "\n\n"; + foreach $p (&prognames("G")) { + ($prog, $type) = split ",", $p; + $objstr = &objects($p, "X.obj", "X.res", undef); + print &splitline("$prog.exe: " . $objstr . " $prog.rsp"), "\n"; + print "\tlink \$(LFLAGS) -out:$prog.exe -map:$prog.map \@$prog.rsp\n\n"; + } + foreach $p (&prognames("G")) { + ($prog, $type) = split ",", $p; + print $prog, ".rsp: \$(MAKEFILE)\n"; + $objstr = &objects($p, "X.obj", "X.res", undef); + @objlist = split " ", $objstr; + @objlines = (""); + foreach $i (@objlist) { + if (length($objlines[$#objlines] . " $i") > 50) { + push @objlines, ""; + } + $objlines[$#objlines] .= " $i"; + } + print "\techo \$(BASELIBS) > $prog.rsp\n"; + for ($i=0; $i<=$#objlines; $i++) { + print "\techo$objlines[$i] >> $prog.rsp\n"; + } + print "\n"; + } + foreach $d (&deps("X.obj", "X.res", $dirpfx, "\\")) { + print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})), + "\n"; + if ($d->{obj} =~ /\.res$/) { + print "\trc \$(FWHACK) \$(RCFL) -r -fo". + $d->{obj}." ".$d->{deps}->[0]."\n"; + } else { + $deflist = join "", map { " /D$_" } @{$d->{defs}}; + print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS)$deflist". + " /c ".$d->{deps}->[0]." /Fo$d->{obj}\n"; + } + } + print "\n"; + print $makefile_extra{'wce'}; + print "\nclean: tidy\n". + "\t-del *.exe\n\n". + "tidy:\n". + "\t-del *.obj\n". + "\t-del *.res\n". + "\t-del *.pch\n". + "\t-del *.aps\n". + "\t-del *.ilk\n". + "\t-del *.pdb\n". + "\t-del *.rsp\n". + "\t-del *.dsp\n". + "\t-del *.dsw\n". + "\t-del *.ncb\n". + "\t-del *.opt\n". + "\t-del *.plg\n". + "\t-del *.map\n". + "\t-del *.idb\n". + "\t-del debug.log\n"; + select STDOUT; close OUT; +} + if (defined $makefiles{'vcproj'}) { $mftyp = 'vcproj'; diff --git a/net.c b/net.c index eb83a16..bf0f7e7 100644 --- a/net.c +++ b/net.c @@ -46,7 +46,11 @@ #define PREFERRED_TILE_SIZE 32 #define TILE_SIZE (ds->tilesize) #define TILE_BORDER 1 +#ifdef SMALL_SCREEN +#define WINDOW_OFFSET 4 +#else #define WINDOW_OFFSET 16 +#endif #define ROTATE_TIME 0.13F #define FLASH_FRAME 0.07F @@ -150,12 +154,16 @@ static const struct game_params net_presets[] = { {7, 7, FALSE, TRUE, 0.0}, {9, 9, FALSE, TRUE, 0.0}, {11, 11, FALSE, TRUE, 0.0}, +#ifndef SMALL_SCREEN {13, 11, FALSE, TRUE, 0.0}, +#endif {5, 5, TRUE, TRUE, 0.0}, {7, 7, TRUE, TRUE, 0.0}, {9, 9, TRUE, TRUE, 0.0}, {11, 11, TRUE, TRUE, 0.0}, +#ifndef SMALL_SCREEN {13, 11, TRUE, TRUE, 0.0}, +#endif }; static int game_fetch_preset(int i, char **name, game_params **params) diff --git a/padtoolbar.bmp b/padtoolbar.bmp new file mode 100644 index 0000000..46c3e0d Binary files /dev/null and b/padtoolbar.bmp differ diff --git a/pattern.c b/pattern.c index a85877f..2c24ad2 100644 --- a/pattern.c +++ b/pattern.c @@ -1276,7 +1276,7 @@ const struct game thegame = { TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, - 0, /* flags */ + REQUIRE_RBUTTON, /* flags */ }; #ifdef STANDALONE_SOLVER diff --git a/puzzles.h b/puzzles.h index 30ffcb0..ff991ad 100644 --- a/puzzles.h +++ b/puzzles.h @@ -67,8 +67,19 @@ enum { #define BUTTON_BEATS(x,y) ( 1 << (((x)-LEFT_BUTTON)*3+(y)-LEFT_BUTTON) ) /* Flag indicating that Solve operations should be animated */ #define SOLVE_ANIMATES ( 1 << 9 ) +/* Pocket PC: Game requires right mouse button emulation */ +#define REQUIRE_RBUTTON ( 1 << 10 ) +/* Pocket PC: Game requires numeric input */ +#define REQUIRE_NUMPAD ( 1 << 11 ) /* end of `flags' word definitions */ +#ifdef _WIN32_WCE + /* Pocket PC devices have small, portrait screen that requires more vivid colours */ + #define SMALL_SCREEN + #define PORTRAIT_SCREEN + #define VIVID_COLOURS +#endif + #define IGNOREARG(x) ( (x) = (x) ) typedef struct frontend frontend; diff --git a/puzzles.rc2 b/puzzles.rc2 new file mode 100644 index 0000000..4442a9b --- /dev/null +++ b/puzzles.rc2 @@ -0,0 +1,65 @@ +/* Standard stuff that goes into the Windows resources for all puzzles. */ + +#ifdef _WIN32_WCE + +#include +#include + +#define SHMENUBAR RCDATA +#define I_IMAGENONE (-2) +#define IDC_STATIC (-1) + +#include "resource.h" + +IDR_MENUBAR1 MENU DISCARDABLE +BEGIN + POPUP "Game" + BEGIN + MENUITEM "Dummy", 0 + END + POPUP "Type" + BEGIN + MENUITEM "Dummy", 0 + END +END + +IDR_MENUBAR1 SHMENUBAR DISCARDABLE +BEGIN + IDR_MENUBAR1, 2, + I_IMAGENONE, ID_GAME, TBSTATE_ENABLED, + TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_GAME, 0, 0, + I_IMAGENONE, ID_TYPE, TBSTATE_ENABLED, + TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_TYPE, 0, 1, +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_CAP_GAME "Game" + IDS_CAP_TYPE "Type" +END + +IDD_ABOUT DIALOG DISCARDABLE 0, 0, 0, 0 +STYLE WS_POPUP +FONT 8, "Tahoma" +BEGIN + LTEXT "", IDC_ABOUT_CAPTION, 4, 4, 150, 8 + LTEXT "", IDC_ABOUT_LINE, 4, 16, 150, 1, WS_BORDER + LTEXT "", IDC_ABOUT_GAME, 4, 22, 150, 8 + LTEXT "from Simon Tatham's Portable Puzzle Collection", + IDC_STATIC, 4, 36, 150, 8, SS_LEFTNOWORDWRAP + LTEXT "Pocket PC port by Darek Olszewski", + IDC_STATIC, 4, 46, 150, 8 + LTEXT "", IDC_ABOUT_VERSION, 4, 60, 150, 8 +END + +IDD_CONFIG DIALOG DISCARDABLE 0, 0, 0, 0 +STYLE WS_POPUP +FONT 8, "Tahoma" +BEGIN + LTEXT "", IDC_CONFIG_CAPTION, 4, 4, 150, 8 + LTEXT "", IDC_CONFIG_LINE, 4, 16, 150, 1, WS_BORDER +END + +IDR_PADTOOLBAR BITMAP DISCARDABLE "padtoolbar.bmp" + +#endif /* _WIN32_WCE */ diff --git a/rect.c b/rect.c index e73db7d..281b4f5 100644 --- a/rect.c +++ b/rect.c @@ -60,7 +60,11 @@ struct game_params { #define PREFERRED_TILE_SIZE 24 #define TILE_SIZE (ds->tilesize) +#ifdef SMALL_SCREEN +#define BORDER (2) +#else #define BORDER (TILE_SIZE * 3 / 4) +#endif #define CORNER_TOLERANCE 0.15F #define CENTRE_TOLERANCE 0.15F @@ -102,8 +106,10 @@ static int game_fetch_preset(int i, char **name, game_params **params) case 2: w = 11, h = 11; break; case 3: w = 13, h = 13; break; case 4: w = 15, h = 15; break; +#ifndef SMALL_SCREEN case 5: w = 17, h = 17; break; case 6: w = 19, h = 19; break; +#endif default: return FALSE; } diff --git a/resource.h b/resource.h new file mode 100644 index 0000000..f0bfa16 --- /dev/null +++ b/resource.h @@ -0,0 +1,20 @@ + +#define IDR_MENUBAR1 101 + +#define ID_GAME 40005 +#define ID_TYPE 40006 + +#define IDS_CAP_GAME 40105 +#define IDS_CAP_TYPE 40106 + +#define IDD_ABOUT 2000 +#define IDC_ABOUT_CAPTION 2001 +#define IDC_ABOUT_LINE 2002 +#define IDC_ABOUT_GAME 2003 +#define IDC_ABOUT_VERSION 2004 + +#define IDD_CONFIG 2100 +#define IDC_CONFIG_CAPTION 2101 +#define IDC_CONFIG_LINE 2102 + +#define IDR_PADTOOLBAR 4000 diff --git a/solo.c b/solo.c index 0a4e852..19d91b4 100644 --- a/solo.c +++ b/solo.c @@ -3085,7 +3085,7 @@ const struct game thegame = { TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, - 0, /* flags */ + REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */ }; #ifdef STANDALONE_SOLVER diff --git a/tents.c b/tents.c index e5e8c6f..de20300 100644 --- a/tents.c +++ b/tents.c @@ -2086,7 +2086,7 @@ const struct game thegame = { TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, - 0, /* flags */ + REQUIRE_RBUTTON, /* flags */ }; #ifdef STANDALONE_SOLVER diff --git a/unequal.c b/unequal.c index 81720e4..8006ea1 100644 --- a/unequal.c +++ b/unequal.c @@ -26,6 +26,11 @@ #include "puzzles.h" #include "latin.h" /* contains typedef for digit */ +static void assert_f(p) +{ + assert(p); +} + /* ---------------------------------------------------------- * Constant and structure definitions */ @@ -971,7 +976,7 @@ static void game_strip(game_state *new, int *scratch, digit *latin, gg_solved++; if (solver_state(copy, difficulty) != 1) { /* put clue back, we can't solve without it. */ - assert(gg_place_clue(new, scratch[i], latin, 0) == 1); + assert_f(gg_place_clue(new, scratch[i], latin, 0) == 1); } else { #ifdef STANDALONE_SOLVER if (solver_show_working) @@ -1355,7 +1360,7 @@ static game_state *execute_move(game_state *state, char *move) p++; } if (*p) goto badmove; - assert(check_complete(ret->nums, ret, 1) > 0); + assert_f(check_complete(ret->nums, ret, 1) > 0); return ret; } else if (move[0] == 'H') { return solver_hint(state, NULL, DIFF_EASY, DIFF_EASY); @@ -1748,7 +1753,7 @@ const struct game thegame = { TRUE, FALSE, game_print_size, game_print, FALSE, /* wants_statusbar */ FALSE, game_timing_state, - 0, /* flags */ + REQUIRE_RBUTTON | REQUIRE_NUMPAD, /* flags */ }; /* ---------------------------------------------------------------------- diff --git a/windows.c b/windows.c index 82767e4..ba0ed52 100644 --- a/windows.c +++ b/windows.c @@ -8,6 +8,11 @@ #include #endif /* NO_HTMLHELP */ +#ifdef _WIN32_WCE +#include +#include +#endif + #include #include #include @@ -18,6 +23,10 @@ #include "puzzles.h" +#ifdef _WIN32_WCE +#include "resource.h" +#endif + #define IDM_NEW 0x0010 #define IDM_RESTART 0x0020 #define IDM_UNDO 0x0030 @@ -36,6 +45,8 @@ #define IDM_PRINT 0x0100 #define IDM_PRESETS 0x0110 +#define IDM_KEYEMUL 0x0400 + #define HELP_FILE_NAME "puzzles.hlp" #define HELP_CNT_NAME "puzzles.cnt" #ifndef NO_HTMLHELP @@ -52,6 +63,58 @@ char *help_path; const char *help_topic; int help_has_contents; +#ifndef FILENAME_MAX +#define FILENAME_MAX (260) +#endif + +#ifndef HGDI_ERROR +#define HGDI_ERROR ((HANDLE)GDI_ERROR) +#endif + +#ifdef _WIN32_WCE + +/* + * Wrapper implementations of functions not supplied by the + * PocketPC API. + */ + +#define SHGetSubMenu(hWndMB,ID_MENU) (HMENU)SendMessage((hWndMB), SHCMBM_GETSUBMENU, (WPARAM)0, (LPARAM)ID_MENU) + +#undef MessageBox + +int MessageBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) +{ + TCHAR wText[2048]; + TCHAR wCaption[2048]; + + MultiByteToWideChar (CP_ACP, 0, lpText, -1, wText, 2048); + MultiByteToWideChar (CP_ACP, 0, lpCaption, -1, wCaption, 2048); + + return MessageBoxW (hWnd, wText, wCaption, uType); +} + +BOOL SetDlgItemTextA(HWND hDlg, int nIDDlgItem, LPCSTR lpString) +{ + TCHAR wText[256]; + + MultiByteToWideChar (CP_ACP, 0, lpString, -1, wText, 256); + return SetDlgItemTextW(hDlg, nIDDlgItem, wText); +} + +LPCSTR getenv(LPCSTR buf) +{ + return NULL; +} + +BOOL GetKeyboardState(PBYTE pb) +{ + return FALSE; +} + +static TCHAR wGameName[256]; + +#endif + #ifdef DEBUGGING static FILE *debug_fp = NULL; static HANDLE debug_hdl = INVALID_HANDLE_VALUE; @@ -90,8 +153,12 @@ void debug_printf(char *fmt, ...) } #endif +#ifndef _WIN32_WCE #define WINFLAGS (WS_OVERLAPPEDWINDOW &~ \ (WS_THICKFRAME | WS_MAXIMIZEBOX | WS_OVERLAPPED)) +#else +#define WINFLAGS (WS_CAPTION | WS_SYSMENU) +#endif static void new_game_size(frontend *fe); @@ -116,8 +183,12 @@ enum { CFG_PRINT = CFG_FRONTEND_SPECIFIC }; struct frontend { midend *me; HWND hwnd, statusbar, cfgbox; +#ifdef _WIN32_WCE + HWND numpad; /* window handle for the numeric pad */ +#endif HINSTANCE inst; HBITMAP bitmap, prevbm; + RECT bitmapPosition; /* game bitmap position within game window */ HDC hdc; COLORREF *colours; HBRUSH *brushes; @@ -185,17 +256,29 @@ char *geterrstr(void) void get_random_seed(void **randseed, int *randseedsize) { - time_t *tp = snew(time_t); - time(tp); - *randseed = (void *)tp; - *randseedsize = sizeof(time_t); + SYSTEMTIME *st = snew(SYSTEMTIME); + + GetLocalTime(st); + + *randseed = (void *)st; + *randseedsize = sizeof(SYSTEMTIME); } static void win_status_bar(void *handle, char *text) { +#ifdef _WIN32_WCE + TCHAR wText[255]; +#endif frontend *fe = (frontend *)handle; +#ifdef _WIN32_WCE + MultiByteToWideChar (CP_ACP, 0, text, -1, wText, 255); + SendMessage(fe->statusbar, SB_SETTEXT, + (WPARAM) 255 | SBT_NOBORDERS, + (LPARAM) wText); +#else SetWindowText(fe->statusbar, text); +#endif } static blitter *win_blitter_new(void *handle, int w, int h) @@ -334,13 +417,20 @@ static void win_set_brush(frontend *fe, int colour) float r, g, b; print_get_colour(fe->dr, colour, &hatch, &r, &g, &b); - if (fe->printcolour) + if (fe->printcolour) { br = CreateSolidBrush(RGB(r * 255, g * 255, b * 255)); - else if (hatch == HATCH_SOLID) + } else if (hatch == HATCH_SOLID) { br = CreateSolidBrush(RGB(0,0,0)); - else if (hatch == HATCH_CLEAR) + } else if (hatch == HATCH_CLEAR) { br = CreateSolidBrush(RGB(255,255,255)); - else + } else { +#ifdef _WIN32_WCE + /* + * This is only ever required during printing, and the + * PocketPC port doesn't support printing. + */ + fatal("CreateHatchBrush not supported"); +#else br = CreateHatchBrush(hatch == HATCH_BACKSLASH ? HS_FDIAGONAL : hatch == HATCH_SLASH ? HS_BDIAGONAL : hatch == HATCH_HORIZ ? HS_HORIZONTAL : @@ -348,6 +438,8 @@ static void win_set_brush(frontend *fe, int colour) hatch == HATCH_PLUS ? HS_CROSS : /* hatch == HATCH_X ? */ HS_DIAGCROSS, RGB(0,0,0)); +#endif + } } else { br = fe->brushes[colour]; } @@ -433,6 +525,7 @@ static void win_draw_text(void *handle, int x, int y, int fonttype, frontend *fe = (frontend *)handle; POINT xy; int i; + LOGFONT lf; if (fe->drawstatus == NOTHING) return; @@ -460,15 +553,21 @@ static void win_draw_text(void *handle, int x, int y, int fonttype, fe->fonts[i].type = fonttype; fe->fonts[i].size = fontsize; - fe->fonts[i].font = CreateFont(-fontsize, 0, 0, 0, - fe->drawstatus == PRINTING ? 0 : FW_BOLD, - FALSE, FALSE, FALSE, DEFAULT_CHARSET, - OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, - DEFAULT_QUALITY, - (fonttype == FONT_FIXED ? - FIXED_PITCH | FF_DONTCARE : - VARIABLE_PITCH | FF_SWISS), - NULL); + memset (&lf, 0, sizeof(LOGFONT)); + lf.lfHeight = -fontsize; + lf.lfWeight = (fe->drawstatus == PRINTING ? 0 : FW_BOLD); + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfOutPrecision = OUT_DEFAULT_PRECIS; + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf.lfQuality = DEFAULT_QUALITY; + lf.lfPitchAndFamily = (fonttype == FONT_FIXED ? + FIXED_PITCH | FF_DONTCARE : + VARIABLE_PITCH | FF_SWISS); +#ifdef _WIN32_WCE + wcscpy(lf.lfFaceName, TEXT("Tahoma")); +#endif + + fe->fonts[i].font = CreateFontIndirect(&lf); } /* @@ -478,6 +577,10 @@ static void win_draw_text(void *handle, int x, int y, int fonttype, HFONT oldfont; TEXTMETRIC tm; SIZE size; +#ifdef _WIN32_WCE + TCHAR wText[256]; + MultiByteToWideChar (CP_ACP, 0, text, -1, wText, 256); +#endif oldfont = SelectObject(fe->hdc, fe->fonts[i].font); if (GetTextMetrics(fe->hdc, &tm)) { @@ -486,7 +589,11 @@ static void win_draw_text(void *handle, int x, int y, int fonttype, else xy.y -= tm.tmAscent; } +#ifndef _WIN32_WCE if (GetTextExtentPoint32(fe->hdc, text, strlen(text), &size)) { +#else + if (GetTextExtentPoint32(fe->hdc, wText, wcslen(wText), &size)) { +#endif if (align & ALIGN_HCENTRE) xy.x -= size.cx / 2; else if (align & ALIGN_HRIGHT) @@ -494,7 +601,11 @@ static void win_draw_text(void *handle, int x, int y, int fonttype, } SetBkMode(fe->hdc, TRANSPARENT); win_text_colour(fe, colour); +#ifndef _WIN32_WCE TextOut(fe->hdc, xy.x, xy.y, text, strlen(text)); +#else + ExtTextOut(fe->hdc, xy.x, xy.y, 0, NULL, wText, wcslen(wText), NULL); +#endif SelectObject(fe->hdc, oldfont); } } @@ -529,18 +640,17 @@ static void win_draw_rect(void *handle, int x, int y, int w, int h, int colour) static void win_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour) { frontend *fe = (frontend *)handle; - POINT p, q; + POINT pp[2]; if (fe->drawstatus == NOTHING) return; win_set_pen(fe, colour, FALSE); - p = win_transform_point(fe, x1, y1); - q = win_transform_point(fe, x2, y2); - MoveToEx(fe->hdc, p.x, p.y, NULL); - LineTo(fe->hdc, q.x, q.y); + pp[0] = win_transform_point(fe, x1, y1); + pp[1] = win_transform_point(fe, x2, y2); + Polyline(fe->hdc, pp, 2); if (fe->drawstatus == DRAWING) - SetPixel(fe->hdc, q.x, q.y, fe->colours[colour]); + SetPixel(fe->hdc, pp[1].x, pp[1].y, fe->colours[colour]); win_reset_pen(fe); } @@ -548,29 +658,24 @@ static void win_draw_circle(void *handle, int cx, int cy, int radius, int fillcolour, int outlinecolour) { frontend *fe = (frontend *)handle; - POINT p, q, r; + POINT p, q; assert(outlinecolour >= 0); if (fe->drawstatus == NOTHING) return; - if (fillcolour >= 0) { + if (fillcolour >= 0) win_set_brush(fe, fillcolour); - win_set_pen(fe, outlinecolour, FALSE); - p = win_transform_point(fe, cx - radius, cy - radius); - q = win_transform_point(fe, cx + radius, cy + radius); - Ellipse(fe->hdc, p.x, p.y, q.x+1, q.y+1); - win_reset_brush(fe); - win_reset_pen(fe); - } else { - win_set_pen(fe, outlinecolour, FALSE); - p = win_transform_point(fe, cx - radius, cy - radius); - q = win_transform_point(fe, cx + radius, cy + radius); - r = win_transform_point(fe, cx - radius, cy); - Arc(fe->hdc, p.x, p.y, q.x+1, q.y+1, r.x, r.y, r.x, r.y); - win_reset_pen(fe); - } + else + fe->oldbr = SelectObject(fe->hdc, GetStockObject(NULL_BRUSH)); + + win_set_pen(fe, outlinecolour, FALSE); + p = win_transform_point(fe, cx - radius, cy - radius); + q = win_transform_point(fe, cx + radius, cy + radius); + Ellipse(fe->hdc, p.x, p.y, q.x+1, q.y+1); + win_reset_brush(fe); + win_reset_pen(fe); } static void win_draw_polygon(void *handle, int *coords, int npoints, @@ -619,7 +724,9 @@ static void win_start_draw(void *handle) fe->prevbm = SelectObject(fe->hdc, fe->bitmap); ReleaseDC(fe->hwnd, hdc_win); fe->clip = NULL; +#ifndef _WIN32_WCE SetMapMode(fe->hdc, MM_TEXT); +#endif fe->drawstatus = DRAWING; } @@ -636,6 +743,7 @@ static void win_draw_update(void *handle, int x, int y, int w, int h) r.right = x + w; r.bottom = y + h; + OffsetRect(&r, fe->bitmapPosition.left, fe->bitmapPosition.top); InvalidateRect(fe->hwnd, &r, FALSE); } @@ -838,6 +946,7 @@ const struct drawing_api win_drawing = { void print(frontend *fe) { +#ifndef _WIN32_WCE PRINTDLG pd; char doctitle[256]; document *doc; @@ -928,6 +1037,7 @@ void print(frontend *fe) DeleteDC(pd.hDC); document_free(doc); +#endif } void deactivate_timer(frontend *fe) @@ -943,7 +1053,7 @@ void activate_timer(frontend *fe) if (!fe) return; /* for non-interactive midend */ if (!fe->timer) { - fe->timer = SetTimer(fe->hwnd, fe->timer, 20, NULL); + fe->timer = SetTimer(fe->hwnd, 1, 20, NULL); fe->timer_last_tickcount = GetTickCount(); } } @@ -1000,6 +1110,7 @@ void write_clip(HWND hwnd, char *data) */ static void init_help(void) { +#ifndef _WIN32_WCE char b[2048], *p, *q, *r; FILE *fp; @@ -1066,8 +1177,11 @@ static void init_help(void) } help_type = NONE; /* didn't find any */ +#endif } +#ifndef _WIN32_WCE + /* * Start Help. */ @@ -1137,6 +1251,8 @@ static void stop_help(frontend *fe) } } +#endif + /* * Terminate Help on process exit. */ @@ -1177,14 +1293,18 @@ static void check_window_size(frontend *fe, int *px, int *py) r.right = x; r.bottom = y + sy; AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0); +#ifndef _WIN32_WCE SetWindowPos(fe->hwnd, NULL, 0, 0, r.right - r.left, r.bottom - r.top, SWP_NOMOVE | SWP_NOZORDER); +#endif } if (fe->statusbar) { GetClientRect(fe->hwnd, &r); +#ifndef _WIN32_WCE SetWindowPos(fe->statusbar, NULL, 0, r.bottom-r.top-sy, r.right-r.left, sy, SWP_NOZORDER); +#endif } *px = x; @@ -1215,6 +1335,23 @@ static void get_max_puzzle_size(frontend *fe, int *x, int *y) } } +#ifdef _WIN32_WCE +/* Toolbar buttons on the numeric pad */ +static TBBUTTON tbNumpadButtons[] = +{ + {0, IDM_KEYEMUL + '1', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}, + {1, IDM_KEYEMUL + '2', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}, + {2, IDM_KEYEMUL + '3', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}, + {3, IDM_KEYEMUL + '4', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}, + {4, IDM_KEYEMUL + '5', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}, + {5, IDM_KEYEMUL + '6', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}, + {6, IDM_KEYEMUL + '7', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}, + {7, IDM_KEYEMUL + '8', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}, + {8, IDM_KEYEMUL + '9', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}, + {9, IDM_KEYEMUL + ' ', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1} +}; +#endif + static frontend *new_window(HINSTANCE inst, char *game_id, char **error) { frontend *fe; @@ -1271,7 +1408,7 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error) } if (midend_wants_statusbar(fe->me)) { - fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME, "ooh", + fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME, TEXT("ooh"), WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, /* status bar does these */ NULL, NULL, inst, NULL); @@ -1286,53 +1423,123 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error) r.bottom = y; AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0); +#ifdef _WIN32_WCE + fe->hwnd = CreateWindowEx(0, wGameName, wGameName, + WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, inst, NULL); + + { + SHMENUBARINFO mbi; + RECT rc, rcBar, rcTB, rcClient; + + memset (&mbi, 0, sizeof(SHMENUBARINFO)); + mbi.cbSize = sizeof(SHMENUBARINFO); + mbi.hwndParent = fe->hwnd; + mbi.nToolBarId = IDR_MENUBAR1; + mbi.hInstRes = inst; + + SHCreateMenuBar(&mbi); + + GetWindowRect(fe->hwnd, &rc); + GetWindowRect(mbi.hwndMB, &rcBar); + rc.bottom -= rcBar.bottom - rcBar.top; + MoveWindow(fe->hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, FALSE); + + if (thegame.flags & REQUIRE_NUMPAD) + { + fe->numpad = CreateToolbarEx (fe->hwnd, + WS_VISIBLE | WS_CHILD | CCS_NOPARENTALIGN | TBSTYLE_FLAT, + 0, 10, inst, IDR_PADTOOLBAR, + tbNumpadButtons, sizeof (tbNumpadButtons) / sizeof (TBBUTTON), + 0, 0, 14, 15, sizeof (TBBUTTON)); + GetWindowRect(fe->numpad, &rcTB); + GetClientRect(fe->hwnd, &rcClient); + MoveWindow(fe->numpad, + 0, + rcClient.bottom - (rcTB.bottom - rcTB.top) - 1, + rcClient.right, + rcTB.bottom - rcTB.top, + FALSE); + SendMessage(fe->numpad, TB_SETINDENT, (rcClient.right - (10 * 21)) / 2, 0); + } + else + fe->numpad = NULL; + } +#else fe->hwnd = CreateWindowEx(0, thegame.name, thegame.name, WS_OVERLAPPEDWINDOW &~ (WS_THICKFRAME | WS_MAXIMIZEBOX), CW_USEDEFAULT, CW_USEDEFAULT, r.right - r.left, r.bottom - r.top, NULL, NULL, inst, NULL); +#endif if (midend_wants_statusbar(fe->me)) { RECT sr; DestroyWindow(fe->statusbar); - fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME, "ooh", + fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME, TEXT("ooh"), WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, /* status bar does these */ fe->hwnd, NULL, inst, NULL); +#ifdef _WIN32_WCE + /* Flat status bar looks better on the Pocket PC */ + SendMessage(fe->statusbar, SB_SIMPLE, (WPARAM) TRUE, 0); + SendMessage(fe->statusbar, SB_SETTEXT, + (WPARAM) 255 | SBT_NOBORDERS, + (LPARAM) L""); +#endif + /* * Now resize the window to take account of the status bar. */ GetWindowRect(fe->statusbar, &sr); GetWindowRect(fe->hwnd, &r); +#ifndef _WIN32_WCE SetWindowPos(fe->hwnd, NULL, 0, 0, r.right - r.left, r.bottom - r.top + sr.bottom - sr.top, SWP_NOMOVE | SWP_NOZORDER); +#endif } else { fe->statusbar = NULL; } { +#ifndef _WIN32_WCE 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"); - AppendMenu(menu, MF_ENABLED, IDM_DESC, "Specific..."); - AppendMenu(menu, MF_ENABLED, IDM_SEED, "Random Seed..."); +#else + HMENU menu = SHGetSubMenu(SHFindMenuBar(fe->hwnd), ID_GAME); + DeleteMenu(menu, 0, MF_BYPOSITION); +#endif + AppendMenu(menu, MF_ENABLED, IDM_NEW, TEXT("New")); + AppendMenu(menu, MF_ENABLED, IDM_RESTART, TEXT("Restart")); +#ifndef _WIN32_WCE + AppendMenu(menu, MF_ENABLED, IDM_DESC, TEXT("Specific...")); + AppendMenu(menu, MF_ENABLED, IDM_SEED, TEXT("Random Seed...")); +#endif if ((fe->npresets = midend_num_presets(fe->me)) > 0 || thegame.can_configure) { - HMENU sub = CreateMenu(); int i; +#ifndef _WIN32_WCE + HMENU sub = CreateMenu(); AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)sub, "Type"); - +#else + HMENU sub = SHGetSubMenu(SHFindMenuBar(fe->hwnd), ID_TYPE); + DeleteMenu(sub, 0, MF_BYPOSITION); +#endif fe->presets = snewn(fe->npresets, game_params *); for (i = 0; i < fe->npresets; i++) { char *name; +#ifdef _WIN32_WCE + TCHAR wName[255]; +#endif midend_fetch_preset(fe->me, i, &name, &fe->presets[i]); @@ -1341,40 +1548,51 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error) * with ampersands here. */ +#ifndef _WIN32_WCE AppendMenu(sub, MF_ENABLED, IDM_PRESETS + 0x10 * i, name); +#else + MultiByteToWideChar (CP_ACP, 0, name, -1, wName, 255); + AppendMenu(sub, MF_ENABLED, IDM_PRESETS + 0x10 * i, wName); +#endif } - if (thegame.can_configure) { - AppendMenu(sub, MF_ENABLED, IDM_CONFIG, "Custom..."); + AppendMenu(sub, MF_ENABLED, IDM_CONFIG, TEXT("Custom...")); } } AppendMenu(menu, MF_SEPARATOR, 0, 0); - AppendMenu(menu, MF_ENABLED, IDM_LOAD, "Load..."); - AppendMenu(menu, MF_ENABLED, IDM_SAVE, "Save..."); +#ifndef _WIN32_WCE + AppendMenu(menu, MF_ENABLED, IDM_LOAD, TEXT("Load...")); + AppendMenu(menu, MF_ENABLED, IDM_SAVE, TEXT("Save...")); AppendMenu(menu, MF_SEPARATOR, 0, 0); if (thegame.can_print) { - AppendMenu(menu, MF_ENABLED, IDM_PRINT, "Print..."); + AppendMenu(menu, MF_ENABLED, IDM_PRINT, TEXT("Print...")); AppendMenu(menu, MF_SEPARATOR, 0, 0); } - AppendMenu(menu, MF_ENABLED, IDM_UNDO, "Undo"); - AppendMenu(menu, MF_ENABLED, IDM_REDO, "Redo"); +#endif + AppendMenu(menu, MF_ENABLED, IDM_UNDO, TEXT("Undo")); + AppendMenu(menu, MF_ENABLED, IDM_REDO, TEXT("Redo")); +#ifndef _WIN32_WCE if (thegame.can_format_as_text) { AppendMenu(menu, MF_SEPARATOR, 0, 0); - AppendMenu(menu, MF_ENABLED, IDM_COPY, "Copy"); + AppendMenu(menu, MF_ENABLED, IDM_COPY, TEXT("Copy")); } +#endif if (thegame.can_solve) { AppendMenu(menu, MF_SEPARATOR, 0, 0); - AppendMenu(menu, MF_ENABLED, IDM_SOLVE, "Solve"); + AppendMenu(menu, MF_ENABLED, IDM_SOLVE, TEXT("Solve")); } AppendMenu(menu, MF_SEPARATOR, 0, 0); - AppendMenu(menu, MF_ENABLED, IDM_QUIT, "Exit"); +#ifndef _WIN32_WCE + AppendMenu(menu, MF_ENABLED, IDM_QUIT, TEXT("Exit")); menu = CreateMenu(); - AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, "Help"); - AppendMenu(menu, MF_ENABLED, IDM_ABOUT, "About"); + AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, TEXT("Help")); +#endif + AppendMenu(menu, MF_ENABLED, IDM_ABOUT, TEXT("About")); +#ifndef _WIN32_WCE if (help_type != NONE) { AppendMenu(menu, MF_SEPARATOR, 0, 0); - AppendMenu(menu, MF_ENABLED, IDM_HELPC, "Contents"); + AppendMenu(menu, MF_ENABLED, IDM_HELPC, TEXT("Contents")); if (help_topic) { char *item; assert(thegame.name); @@ -1385,6 +1603,7 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error) } } SetMenu(fe->hwnd, bar); +#endif } fe->bitmap = NULL; @@ -1393,7 +1612,7 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error) SetWindowLong(fe->hwnd, GWL_USERDATA, (LONG)fe); - ShowWindow(fe->hwnd, SW_NORMAL); + ShowWindow(fe->hwnd, SW_SHOWNORMAL); SetForegroundWindow(fe->hwnd); midend_redraw(fe->me); @@ -1401,6 +1620,36 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error) return fe; } +#ifdef _WIN32_WCE +static HFONT dialog_title_font() +{ + static HFONT hf = NULL; + LOGFONT lf; + + if (hf) + return hf; + + memset (&lf, 0, sizeof(LOGFONT)); + lf.lfHeight = -11; /* - ((8 * GetDeviceCaps(hdc, LOGPIXELSY)) / 72) */ + lf.lfWeight = FW_BOLD; + wcscpy(lf.lfFaceName, TEXT("Tahoma")); + + return hf = CreateFontIndirect(&lf); +} + +static void make_dialog_full_screen(HWND hwnd) +{ + SHINITDLGINFO shidi; + + /* Make dialog full screen */ + shidi.dwMask = SHIDIM_FLAGS; + shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIZEDLGFULLSCREEN | + SHIDIF_EMPTYMENU; + shidi.hDlg = hwnd; + SHInitDialog(&shidi); +} +#endif + static int CALLBACK AboutDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { @@ -1408,17 +1657,39 @@ static int CALLBACK AboutDlgProc(HWND hwnd, UINT msg, switch (msg) { case WM_INITDIALOG: - return 0; +#ifdef _WIN32_WCE + { + char title[256]; + + make_dialog_full_screen(hwnd); + + sprintf(title, "About %.250s", thegame.name); + SetDlgItemTextA(hwnd, IDC_ABOUT_CAPTION, title); + + SendDlgItemMessage(hwnd, IDC_ABOUT_CAPTION, WM_SETFONT, + (WPARAM) dialog_title_font(), 0); + + SetDlgItemTextA(hwnd, IDC_ABOUT_GAME, thegame.name); + SetDlgItemTextA(hwnd, IDC_ABOUT_VERSION, ver); + } +#endif + return TRUE; case WM_COMMAND: - if ((HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) && - LOWORD(wParam) == IDOK) + if (LOWORD(wParam) == IDOK) +#ifdef _WIN32_WCE + EndDialog(hwnd, 1); +#else fe->dlg_done = 1; +#endif return 0; case WM_CLOSE: +#ifdef _WIN32_WCE + EndDialog(hwnd, 1); +#else fe->dlg_done = 1; +#endif return 0; } @@ -1525,6 +1796,117 @@ static char *frontend_set_config(frontend *fe, int which, config_item *cfg) } } +#ifdef _WIN32_WCE +/* Separate version of mkctrl function for the Pocket PC. */ +/* Control coordinates should be specified in dialog units. */ +HWND mkctrl(frontend *fe, int x1, int x2, int y1, int y2, + LPCTSTR wclass, int wstyle, + int exstyle, const char *wtext, int wid) +{ + RECT rc; + TCHAR wwtext[256]; + + /* Convert dialog units into pixels */ + rc.left = x1; rc.right = x2; + rc.top = y1; rc.bottom = y2; + MapDialogRect(fe->cfgbox, &rc); + + MultiByteToWideChar (CP_ACP, 0, wtext, -1, wwtext, 256); + + return CreateWindowEx(exstyle, wclass, wwtext, + wstyle | WS_CHILD | WS_VISIBLE, + rc.left, rc.top, + rc.right - rc.left, rc.bottom - rc.top, + fe->cfgbox, (HMENU) wid, fe->inst, NULL); +} + +static void create_config_controls(frontend * fe) +{ + int id, nctrls; + int col1l, col1r, col2l, col2r, y; + config_item *i; + struct cfg_aux *j; + HWND ctl; + + /* Control placement done in dialog units */ + col1l = 4; col1r = 96; /* Label column */ + col2l = 100; col2r = 154; /* Input column (edit boxes and combo boxes) */ + + /* + * Count the controls so we can allocate cfgaux. + */ + for (nctrls = 0, i = fe->cfg; i->type != C_END; i++) + nctrls++; + fe->cfgaux = snewn(nctrls, struct cfg_aux); + + id = 1000; + y = 22; /* Leave some room for the dialog title */ + for (i = fe->cfg, j = fe->cfgaux; i->type != C_END; i++, j++) { + switch (i->type) { + case C_STRING: + /* + * Edit box with a label beside it. + */ + mkctrl(fe, col1l, col1r, y + 1, y + 11, + TEXT("Static"), SS_LEFTNOWORDWRAP, 0, i->name, id++); + mkctrl(fe, col2l, col2r, y, y + 12, + TEXT("EDIT"), WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL, + 0, "", (j->ctlid = id++)); + SetDlgItemTextA(fe->cfgbox, j->ctlid, i->sval); + break; + + case C_BOOLEAN: + /* + * Simple checkbox. + */ + mkctrl(fe, col1l, col2r, y + 1, y + 11, TEXT("BUTTON"), + BS_NOTIFY | BS_AUTOCHECKBOX | WS_TABSTOP, + 0, i->name, (j->ctlid = id++)); + CheckDlgButton(fe->cfgbox, j->ctlid, (i->ival != 0)); + break; + + case C_CHOICES: + /* + * Drop-down list with a label beside it. + */ + mkctrl(fe, col1l, col1r, y + 1, y + 11, + TEXT("STATIC"), SS_LEFTNOWORDWRAP, 0, i->name, id++); + ctl = mkctrl(fe, col2l, col2r, y, y + 48, + TEXT("COMBOBOX"), WS_BORDER | WS_TABSTOP | + CBS_DROPDOWNLIST | CBS_HASSTRINGS, + 0, "", (j->ctlid = id++)); + { + char c, *p, *q, *str; + + p = i->sval; + c = *p++; + while (*p) { + q = p; + while (*q && *q != c) q++; + str = snewn(q-p+1, char); + strncpy(str, p, q-p); + str[q-p] = '\0'; + { + TCHAR ws[50]; + MultiByteToWideChar (CP_ACP, 0, str, -1, ws, 50); + SendMessage(ctl, CB_ADDSTRING, 0, (LPARAM)ws); + } + + sfree(str); + if (*q) q++; + p = q; + } + } + SendMessage(ctl, CB_SETCURSEL, i->ival, 0); + break; + } + + y += 15; + } + +} +#endif + static int CALLBACK ConfigDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { @@ -1534,15 +1916,32 @@ static int CALLBACK ConfigDlgProc(HWND hwnd, UINT msg, switch (msg) { case WM_INITDIALOG: - return 0; +#ifdef _WIN32_WCE + { + char *title; + + fe = (frontend *) lParam; + SetWindowLong(hwnd, GWL_USERDATA, lParam); + fe->cfgbox = hwnd; + + fe->cfg = frontend_get_config(fe, fe->cfg_which, &title); + + make_dialog_full_screen(hwnd); + + SetDlgItemTextA(hwnd, IDC_CONFIG_CAPTION, title); + SendDlgItemMessage(hwnd, IDC_CONFIG_CAPTION, WM_SETFONT, + (WPARAM) dialog_title_font(), 0); + + create_config_controls(fe); + } +#endif + return TRUE; case WM_COMMAND: /* * OK and Cancel are special cases. */ - if ((HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED) && - (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) { + if ((LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) { if (LOWORD(wParam) == IDOK) { char *err = frontend_set_config(fe, fe->cfg_which, fe->cfg); @@ -1550,10 +1949,18 @@ static int CALLBACK ConfigDlgProc(HWND hwnd, UINT msg, MessageBox(hwnd, err, "Validation error", MB_ICONERROR | MB_OK); } else { +#ifdef _WIN32_WCE + EndDialog(hwnd, 2); +#else fe->dlg_done = 2; +#endif } } else { +#ifdef _WIN32_WCE + EndDialog(hwnd, 1); +#else fe->dlg_done = 1; +#endif } return 0; } @@ -1570,13 +1977,19 @@ static int CALLBACK ConfigDlgProc(HWND hwnd, UINT msg, if (i->type == C_STRING && HIWORD(wParam) == EN_CHANGE) { char buffer[4096]; +#ifdef _WIN32_WCE + TCHAR wBuffer[4096]; + GetDlgItemText(fe->cfgbox, j->ctlid, wBuffer, 4096); + WideCharToMultiByte(CP_ACP, 0, wBuffer, -1, buffer, 4096, NULL, NULL); +#else GetDlgItemText(fe->cfgbox, j->ctlid, buffer, lenof(buffer)); +#endif buffer[lenof(buffer)-1] = '\0'; sfree(i->sval); i->sval = dupstr(buffer); } else if (i->type == C_BOOLEAN && (HIWORD(wParam) == BN_CLICKED || - HIWORD(wParam) == BN_DOUBLECLICKED)) { + HIWORD(wParam) == BN_DBLCLK)) { i->ival = IsDlgButtonChecked(fe->cfgbox, j->ctlid); } else if (i->type == C_CHOICES && HIWORD(wParam) == CBN_SELCHANGE) { @@ -1594,6 +2007,7 @@ static int CALLBACK ConfigDlgProc(HWND hwnd, UINT msg, return 0; } +#ifndef _WIN32_WCE HWND mkctrl(frontend *fe, int x1, int x2, int y1, int y2, char *wclass, int wstyle, int exstyle, const char *wtext, int wid) @@ -1605,9 +2019,13 @@ HWND mkctrl(frontend *fe, int x1, int x2, int y1, int y2, SendMessage(ret, WM_SETFONT, (WPARAM)fe->cfgfont, MAKELPARAM(TRUE, 0)); return ret; } +#endif static void about(frontend *fe) { +#ifdef _WIN32_WCE + DialogBox(fe->inst, MAKEINTRESOURCE(IDD_ABOUT), fe->hwnd, AboutDlgProc); +#else int i; WNDCLASS wc; MSG msg; @@ -1629,7 +2047,7 @@ static void about(frontend *fe) strings[nstrings++] = "from Simon Tatham's Portable Puzzle Collection"; strings[nstrings++] = ver; - wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW; + wc.style = CS_DBLCLKS | CS_SAVEBITS; wc.lpfnWndProc = DefDlgProc; wc.cbClsExtra = 0; wc.cbWndExtra = DLGWINDOWEXTRA + 8; @@ -1745,7 +2163,7 @@ static void about(frontend *fe) SendMessage(fe->cfgbox, WM_INITDIALOG, 0, 0); EnableWindow(fe->hwnd, FALSE); - ShowWindow(fe->cfgbox, SW_NORMAL); + ShowWindow(fe->cfgbox, SW_SHOWNORMAL); while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) { if (!IsDialogMessage(fe->cfgbox, &msg)) DispatchMessage(&msg); @@ -1756,10 +2174,19 @@ static void about(frontend *fe) SetForegroundWindow(fe->hwnd); DestroyWindow(fe->cfgbox); DeleteObject(fe->cfgfont); +#endif } static int get_config(frontend *fe, int which) { +#ifdef _WIN32_WCE + fe->cfg_which = which; + + return DialogBoxParam(fe->inst, + MAKEINTRESOURCE(IDD_CONFIG), + fe->hwnd, ConfigDlgProc, + (LPARAM) fe) == 2; +#else config_item *i; struct cfg_aux *j; char *title; @@ -1774,7 +2201,7 @@ static int get_config(frontend *fe, int which) int winwidth, winheight, col1l, col1r, col2l, col2r, y; int height, width, maxlabel, maxcheckbox; - wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW; + wc.style = CS_DBLCLKS | CS_SAVEBITS; wc.lpfnWndProc = DefDlgProc; wc.cbClsExtra = 0; wc.cbWndExtra = DLGWINDOWEXTRA + 8; @@ -1981,7 +2408,7 @@ static int get_config(frontend *fe, int which) SendMessage(fe->cfgbox, WM_INITDIALOG, 0, 0); EnableWindow(fe->hwnd, FALSE); - ShowWindow(fe->cfgbox, SW_NORMAL); + ShowWindow(fe->cfgbox, SW_SHOWNORMAL); while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) { if (!IsDialogMessage(fe->cfgbox, &msg)) DispatchMessage(&msg); @@ -1997,8 +2424,48 @@ static int get_config(frontend *fe, int which) sfree(fe->cfgaux); return (fe->dlg_done == 2); +#endif } +#ifdef _WIN32_WCE +static void calculate_bitmap_position(frontend *fe, int x, int y) +{ + /* Pocket PC - center the game in the full screen window */ + int yMargin; + RECT rcClient; + + GetClientRect(fe->hwnd, &rcClient); + fe->bitmapPosition.left = (rcClient.right - x) / 2; + yMargin = rcClient.bottom - y; + + if (fe->numpad != NULL) { + RECT rcPad; + GetWindowRect(fe->numpad, &rcPad); + yMargin -= rcPad.bottom - rcPad.top; + } + + if (fe->statusbar != NULL) { + RECT rcStatus; + GetWindowRect(fe->statusbar, &rcStatus); + yMargin -= rcStatus.bottom - rcStatus.top; + } + + fe->bitmapPosition.top = yMargin / 2; + + fe->bitmapPosition.right = fe->bitmapPosition.left + x; + fe->bitmapPosition.bottom = fe->bitmapPosition.top + y; +} +#else +static void calculate_bitmap_position(frontend *fe, int x, int y) +{ + /* Plain Windows - position the game in the upper-left corner */ + fe->bitmapPosition.left = 0; + fe->bitmapPosition.top = 0; + fe->bitmapPosition.right = fe->bitmapPosition.left + x; + fe->bitmapPosition.bottom = fe->bitmapPosition.top + y; +} +#endif + static void new_game_size(frontend *fe) { RECT r, sr; @@ -2018,23 +2485,31 @@ static void new_game_size(frontend *fe) } else { sr.left = sr.right = sr.top = sr.bottom = 0; } +#ifndef _WIN32_WCE SetWindowPos(fe->hwnd, NULL, 0, 0, r.right - r.left, r.bottom - r.top + sr.bottom - sr.top, SWP_NOMOVE | SWP_NOZORDER); +#endif check_window_size(fe, &x, &y); +#ifndef _WIN32_WCE if (fe->statusbar != NULL) SetWindowPos(fe->statusbar, NULL, 0, y, x, sr.bottom - sr.top, SWP_NOZORDER); +#endif if (fe->bitmap) DeleteObject(fe->bitmap); hdc = GetDC(fe->hwnd); fe->bitmap = CreateCompatibleBitmap(hdc, x, y); + calculate_bitmap_position(fe, x, y); ReleaseDC(fe->hwnd, hdc); +#ifdef _WIN32_WCE + InvalidateRect(fe->hwnd, NULL, TRUE); +#endif midend_redraw(fe->me); } @@ -2083,6 +2558,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, DestroyWindow(hwnd); return 0; case WM_COMMAND: +#ifdef _WIN32_WCE + /* Numeric pad sends WM_COMMAND messages */ + if ((wParam >= IDM_KEYEMUL) && (wParam < IDM_KEYEMUL + 256)) + { + midend_process_key(fe->me, 0, 0, wParam - IDM_KEYEMUL); + } +#endif cmd = wParam & ~0xF; /* low 4 bits reserved to Windows */ switch (cmd) { case IDM_NEW: @@ -2227,12 +2709,14 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } break; +#ifndef _WIN32_WCE case IDM_HELPC: start_help(fe, NULL); break; case IDM_GAMEHELP: start_help(fe, help_topic); break; +#endif default: { int p = ((wParam &~ 0xF) - IDM_PRESETS) / 0x10; @@ -2246,7 +2730,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } break; case WM_DESTROY: +#ifndef _WIN32_WCE stop_help(fe); +#endif PostQuitMessage(0); return 0; case WM_PAINT: @@ -2254,16 +2740,22 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, PAINTSTRUCT p; HDC hdc, hdc2; HBITMAP prevbm; + RECT rcDest; hdc = BeginPaint(hwnd, &p); hdc2 = CreateCompatibleDC(hdc); prevbm = SelectObject(hdc2, fe->bitmap); +#ifdef _WIN32_WCE + FillRect(hdc, &(p.rcPaint), (HBRUSH) GetStockObject(WHITE_BRUSH)); +#endif + IntersectRect(&rcDest, &(fe->bitmapPosition), &(p.rcPaint)); BitBlt(hdc, - p.rcPaint.left, p.rcPaint.top, - p.rcPaint.right - p.rcPaint.left, - p.rcPaint.bottom - p.rcPaint.top, + rcDest.left, rcDest.top, + rcDest.right - rcDest.left, + rcDest.bottom - rcDest.top, hdc2, - p.rcPaint.left, p.rcPaint.top, + rcDest.left - fe->bitmapPosition.left, + rcDest.top - fe->bitmapPosition.top, SRCCOPY); SelectObject(hdc2, prevbm); DeleteDC(hdc2); @@ -2368,10 +2860,32 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, else if (message == WM_RBUTTONDOWN || is_alt_pressed()) button = RIGHT_BUTTON; else +#ifndef _WIN32_WCE button = LEFT_BUTTON; +#else + if ((thegame.flags & REQUIRE_RBUTTON) == 0) + button = LEFT_BUTTON; + else + { + SHRGINFO shrgi; - if (!midend_process_key(fe->me, (signed short)LOWORD(lParam), - (signed short)HIWORD(lParam), button)) + shrgi.cbSize = sizeof(SHRGINFO); + shrgi.hwndClient = hwnd; + shrgi.ptDown.x = (signed short)LOWORD(lParam); + shrgi.ptDown.y = (signed short)HIWORD(lParam); + shrgi.dwFlags = SHRG_RETURNCMD; + + if (GN_CONTEXTMENU == SHRecognizeGesture(&shrgi)) + button = RIGHT_BUTTON; + else + button = LEFT_BUTTON; + } +#endif + + if (!midend_process_key(fe->me, + (signed short)LOWORD(lParam) - fe->bitmapPosition.left, + (signed short)HIWORD(lParam) - fe->bitmapPosition.top, + button)) PostQuitMessage(0); SetCapture(hwnd); @@ -2395,8 +2909,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, else button = LEFT_RELEASE; - if (!midend_process_key(fe->me, (signed short)LOWORD(lParam), - (signed short)HIWORD(lParam), button)) + if (!midend_process_key(fe->me, + (signed short)LOWORD(lParam) - fe->bitmapPosition.left, + (signed short)HIWORD(lParam) - fe->bitmapPosition.top, + button)) PostQuitMessage(0); ReleaseCapture(); @@ -2413,8 +2929,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, else button = LEFT_DRAG; - if (!midend_process_key(fe->me, (signed short)LOWORD(lParam), - (signed short)HIWORD(lParam), button)) + if (!midend_process_key(fe->me, + (signed short)LOWORD(lParam) - fe->bitmapPosition.left, + (signed short)HIWORD(lParam) - fe->bitmapPosition.top, + button)) PostQuitMessage(0); } break; @@ -2435,11 +2953,34 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, return DefWindowProc(hwnd, message, wParam, lParam); } +#ifdef _WIN32_WCE +static int FindPreviousInstance() +{ + /* Check if application is running. If it's running then focus on the window */ + HWND hOtherWnd = NULL; + + hOtherWnd = FindWindow (wGameName, wGameName); + if (hOtherWnd) + { + SetForegroundWindow (hOtherWnd); + return TRUE; + } + + return FALSE; +} +#endif + int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { MSG msg; char *error; +#ifdef _WIN32_WCE + MultiByteToWideChar (CP_ACP, 0, thegame.name, -1, wGameName, 256); + if (FindPreviousInstance ()) + return 0; +#endif + InitCommonControls(); if (!prev) { @@ -2451,12 +2992,18 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) wndclass.cbWndExtra = 0; wndclass.hInstance = inst; wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(200)); +#ifndef _WIN32_WCE if (!wndclass.hIcon) /* in case resource file is absent */ wndclass.hIcon = LoadIcon(inst, IDI_APPLICATION); +#endif wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = NULL; wndclass.lpszMenuName = NULL; +#ifdef _WIN32_WCE + wndclass.lpszClassName = wGameName; +#else wndclass.lpszClassName = thegame.name; +#endif RegisterClass(&wndclass); }