#include "puzzles.h"
+#define IDM_NEW 0x0010
+#define IDM_RESTART 0x0020
+#define IDM_UNDO 0x0030
+#define IDM_REDO 0x0040
+#define IDM_QUIT 0x0050
+#define IDM_PRESETS 0x0100
+
+#ifdef DEBUG
+static FILE *debug_fp = NULL;
+static HANDLE debug_hdl = INVALID_HANDLE_VALUE;
+static int debug_got_console = 0;
+
+void dputs(char *buf)
+{
+ DWORD dw;
+
+ if (!debug_got_console) {
+ if (AllocConsole()) {
+ debug_got_console = 1;
+ debug_hdl = GetStdHandle(STD_OUTPUT_HANDLE);
+ }
+ }
+ if (!debug_fp) {
+ debug_fp = fopen("debug.log", "w");
+ }
+
+ if (debug_hdl != INVALID_HANDLE_VALUE) {
+ WriteFile(debug_hdl, buf, strlen(buf), &dw, NULL);
+ }
+ fputs(buf, debug_fp);
+ fflush(debug_fp);
+}
+
+void debug_printf(char *fmt, ...)
+{
+ char buf[4096];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ dputs(buf);
+ va_end(ap);
+}
+
+#define debug(x) (debug_printf x)
+
+#else
+
+#define debug(x)
+
+#endif
+
struct frontend {
midend_data *me;
HWND hwnd;
HBRUSH *brushes;
HPEN *pens;
UINT timer;
+ int npresets;
+ game_params **presets;
};
void fatal(char *fmt, ...)
void draw_rect(frontend *fe, int x, int y, int w, int h, int colour)
{
- HBRUSH oldbrush = SelectObject(fe->hdc_bm, fe->brushes[colour]);
- HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]);
- Rectangle(fe->hdc_bm, x, y, x+w, y+h);
- SelectObject(fe->hdc_bm, oldbrush);
- SelectObject(fe->hdc_bm, oldpen);
+ if (w == 1 && h == 1) {
+ /*
+ * Rectangle() appears to get uppity if asked to draw a 1x1
+ * rectangle, presumably on the grounds that that's beneath
+ * its dignity and you ought to be using SetPixel instead.
+ * So I will.
+ */
+ SetPixel(fe->hdc_bm, x, y, fe->colours[colour]);
+ } else {
+ HBRUSH oldbrush = SelectObject(fe->hdc_bm, fe->brushes[colour]);
+ HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]);
+ Rectangle(fe->hdc_bm, x, y, x+w, y+h);
+ SelectObject(fe->hdc_bm, oldbrush);
+ SelectObject(fe->hdc_bm, oldpen);
+ }
}
void draw_line(frontend *fe, int x1, int y1, int x2, int y2, int colour)
r.bottom = y;
AdjustWindowRectEx(&r, WS_OVERLAPPEDWINDOW &~
(WS_THICKFRAME | WS_MAXIMIZEBOX | WS_OVERLAPPED),
- FALSE, 0);
+ TRUE, 0);
fe->hwnd = CreateWindowEx(0, "puzzle", "puzzle",
WS_OVERLAPPEDWINDOW &~
r.right - r.left, r.bottom - r.top,
NULL, NULL, inst, NULL);
+ {
+ HMENU bar = CreateMenu();
+ HMENU menu = CreateMenu();
+
+ AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, "Game");
+ AppendMenu(menu, MF_ENABLED, IDM_NEW, "New");
+ AppendMenu(menu, MF_ENABLED, IDM_RESTART, "Restart");
+
+ if ((fe->npresets = midend_num_presets(fe->me)) > 0) {
+ HMENU sub = CreateMenu();
+ int i;
+
+ AppendMenu(menu, MF_ENABLED|MF_POPUP, (UINT)sub, "Type");
+
+ fe->presets = snewn(fe->npresets, game_params *);
+
+ for (i = 0; i < fe->npresets; i++) {
+ char *name;
+
+ midend_fetch_preset(fe->me, i, &name, &fe->presets[i]);
+
+ /*
+ * FIXME: we ought to go through and do something
+ * with ampersands here.
+ */
+
+ AppendMenu(sub, MF_ENABLED, IDM_PRESETS + 0x10 * i, name);
+ }
+ }
+
+ AppendMenu(menu, MF_SEPARATOR, 0, 0);
+ AppendMenu(menu, MF_ENABLED, IDM_UNDO, "Undo");
+ AppendMenu(menu, MF_ENABLED, IDM_REDO, "Redo");
+ AppendMenu(menu, MF_SEPARATOR, 0, 0);
+ AppendMenu(menu, MF_ENABLED, IDM_QUIT, "Exit");
+ SetMenu(fe->hwnd, bar);
+ }
+
hdc = GetDC(fe->hwnd);
fe->bitmap = CreateCompatibleBitmap(hdc, x, y);
ReleaseDC(fe->hwnd, hdc);
case WM_CLOSE:
DestroyWindow(hwnd);
return 0;
+ case WM_COMMAND:
+ switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
+ case IDM_NEW:
+ if (!midend_process_key(fe->me, 0, 0, 'n'))
+ PostQuitMessage(0);
+ break;
+ case IDM_RESTART:
+ if (!midend_process_key(fe->me, 0, 0, 'r'))
+ PostQuitMessage(0);
+ break;
+ case IDM_UNDO:
+ if (!midend_process_key(fe->me, 0, 0, 'u'))
+ PostQuitMessage(0);
+ break;
+ case IDM_REDO:
+ if (!midend_process_key(fe->me, 0, 0, '\x12'))
+ PostQuitMessage(0);
+ break;
+ case IDM_QUIT:
+ if (!midend_process_key(fe->me, 0, 0, 'q'))
+ PostQuitMessage(0);
+ break;
+ default:
+ {
+ int p = ((wParam &~ 0xF) - IDM_PRESETS) / 0x10;
+
+ if (p >= 0 && p < fe->npresets) {
+ RECT r;
+ HDC hdc;
+ int x, y;
+
+ midend_set_params(fe->me, fe->presets[p]);
+ midend_new_game(fe->me, NULL);
+ midend_size(fe->me, &x, &y);
+
+ r.left = r.top = 0;
+ r.right = x;
+ r.bottom = y;
+ AdjustWindowRectEx(&r, WS_OVERLAPPEDWINDOW &~
+ (WS_THICKFRAME | WS_MAXIMIZEBOX |
+ WS_OVERLAPPED),
+ TRUE, 0);
+
+ SetWindowPos(fe->hwnd, NULL, 0, 0,
+ r.right - r.left, r.bottom - r.top,
+ SWP_NOMOVE | SWP_NOZORDER);
+
+ DeleteObject(fe->bitmap);
+
+ hdc = GetDC(fe->hwnd);
+ fe->bitmap = CreateCompatibleBitmap(hdc, x, y);
+ ReleaseDC(fe->hwnd, hdc);
+
+ midend_redraw(fe->me);
+ }
+ }
+ break;
+ }
+ break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case VK_RIGHT: key = CURSOR_RIGHT; break;
case VK_UP: key = CURSOR_UP; break;
case VK_DOWN: key = CURSOR_DOWN; break;
+ /*
+ * Diagonal keys on the numeric keypad.
+ */
+ case VK_PRIOR:
+ if (!(lParam & 0x01000000)) key = CURSOR_UP_RIGHT;
+ break;
+ case VK_NEXT:
+ if (!(lParam & 0x01000000)) key = CURSOR_DOWN_RIGHT;
+ break;
+ case VK_HOME:
+ if (!(lParam & 0x01000000)) key = CURSOR_UP_LEFT;
+ break;
+ case VK_END:
+ if (!(lParam & 0x01000000)) key = CURSOR_DOWN_LEFT;
+ break;
+ /*
+ * Numeric keypad keys with Num Lock on.
+ */
+ case VK_NUMPAD4: key = CURSOR_LEFT; break;
+ case VK_NUMPAD6: key = CURSOR_RIGHT; break;
+ case VK_NUMPAD8: key = CURSOR_UP; break;
+ case VK_NUMPAD2: key = CURSOR_DOWN; break;
+ case VK_NUMPAD9: key = CURSOR_UP_RIGHT; break;
+ case VK_NUMPAD3: key = CURSOR_DOWN_RIGHT; break;
+ case VK_NUMPAD7: key = CURSOR_UP_LEFT; break;
+ case VK_NUMPAD1: key = CURSOR_DOWN_LEFT; break;
}
if (key != -1) {
- if (!midend_process_key(fe->me, -1, -1, key))
+ if (!midend_process_key(fe->me, 0, 0, key))
PostQuitMessage(0);
+ } else {
+ MSG m;
+ m.hwnd = hwnd;
+ m.message = WM_KEYDOWN;
+ m.wParam = wParam;
+ m.lParam = lParam & 0xdfff;
+ TranslateMessage(&m);
}
}
break;
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
- if (!midend_process_key(fe->me, LOWORD(lParam), HIWORD(lParam),
- (message == WM_LBUTTONDOWN ? LEFT_BUTTON :
- message == WM_RBUTTONDOWN ? RIGHT_BUTTON :
- MIDDLE_BUTTON)))
- PostQuitMessage(0);
-
+ {
+ int button;
+
+ /*
+ * Shift-clicks count as middle-clicks, since otherwise
+ * two-button Windows users won't have any kind of
+ * middle click to use.
+ */
+ if (message == WM_MBUTTONDOWN || (wParam & MK_SHIFT))
+ button = MIDDLE_BUTTON;
+ else if (message == WM_LBUTTONDOWN)
+ button = LEFT_BUTTON;
+ else
+ button = RIGHT_BUTTON;
+
+ if (!midend_process_key(fe->me, LOWORD(lParam),
+ HIWORD(lParam), button))
+ PostQuitMessage(0);
+ }
break;
case WM_CHAR:
- if (!midend_process_key(fe->me, -1, -1, (unsigned char)wParam))
+ if (!midend_process_key(fe->me, 0, 0, (unsigned char)wParam))
PostQuitMessage(0);
return 0;
case WM_TIMER:
new_window(inst);
while (GetMessage(&msg, NULL, 0, 0)) {
- TranslateMessage(&msg);
DispatchMessage(&msg);
}