X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/4e7ef6e6e2e7a7bbc29bdaa2db69ae3956634a68..b0f067190f385284449015d12d592b3f385b6a11:/windows.c diff --git a/windows.c b/windows.c index 63ee3dd..29b50bc 100644 --- a/windows.c +++ b/windows.c @@ -11,6 +11,58 @@ #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; @@ -20,6 +72,8 @@ struct frontend { HBRUSH *brushes; HPEN *pens; UINT timer; + int npresets; + game_params **presets; }; void fatal(char *fmt, ...) @@ -47,11 +101,21 @@ void frontend_default_colour(frontend *fe, float *output) 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) @@ -168,7 +232,7 @@ static frontend *new_window(HINSTANCE inst) r.bottom = y; AdjustWindowRectEx(&r, WS_OVERLAPPEDWINDOW &~ (WS_THICKFRAME | WS_MAXIMIZEBOX | WS_OVERLAPPED), - FALSE, 0); + TRUE, 0); fe->hwnd = CreateWindowEx(0, "puzzle", "puzzle", WS_OVERLAPPEDWINDOW &~ @@ -177,6 +241,44 @@ static frontend *new_window(HINSTANCE inst) r.right - r.left, r.bottom - r.top, NULL, NULL, inst, NULL); + { + HMENU bar = CreateMenu(); + HMENU menu = CreateMenu(); + + AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, "Game"); + AppendMenu(menu, MF_ENABLED, IDM_NEW, "New"); + AppendMenu(menu, MF_ENABLED, IDM_RESTART, "Restart"); + + if ((fe->npresets = midend_num_presets(fe->me)) > 0) { + HMENU sub = CreateMenu(); + int i; + + AppendMenu(menu, MF_ENABLED|MF_POPUP, (UINT)sub, "Type"); + + fe->presets = snewn(fe->npresets, game_params *); + + for (i = 0; i < fe->npresets; i++) { + char *name; + + midend_fetch_preset(fe->me, i, &name, &fe->presets[i]); + + /* + * FIXME: we ought to go through and do something + * with ampersands here. + */ + + AppendMenu(sub, MF_ENABLED, IDM_PRESETS + 0x10 * i, name); + } + } + + AppendMenu(menu, MF_SEPARATOR, 0, 0); + AppendMenu(menu, MF_ENABLED, IDM_UNDO, "Undo"); + AppendMenu(menu, MF_ENABLED, IDM_REDO, "Redo"); + AppendMenu(menu, MF_SEPARATOR, 0, 0); + AppendMenu(menu, MF_ENABLED, IDM_QUIT, "Exit"); + SetMenu(fe->hwnd, bar); + } + hdc = GetDC(fe->hwnd); fe->bitmap = CreateCompatibleBitmap(hdc, x, y); ReleaseDC(fe->hwnd, hdc); @@ -200,6 +302,65 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case WM_CLOSE: DestroyWindow(hwnd); return 0; + case WM_COMMAND: + switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */ + case IDM_NEW: + if (!midend_process_key(fe->me, 0, 0, 'n')) + PostQuitMessage(0); + break; + case IDM_RESTART: + if (!midend_process_key(fe->me, 0, 0, 'r')) + PostQuitMessage(0); + break; + case IDM_UNDO: + if (!midend_process_key(fe->me, 0, 0, 'u')) + PostQuitMessage(0); + break; + case IDM_REDO: + if (!midend_process_key(fe->me, 0, 0, '\x12')) + PostQuitMessage(0); + break; + case IDM_QUIT: + if (!midend_process_key(fe->me, 0, 0, 'q')) + PostQuitMessage(0); + break; + default: + { + int p = ((wParam &~ 0xF) - IDM_PRESETS) / 0x10; + + if (p >= 0 && p < fe->npresets) { + RECT r; + HDC hdc; + int x, y; + + midend_set_params(fe->me, fe->presets[p]); + midend_new_game(fe->me, NULL); + midend_size(fe->me, &x, &y); + + r.left = r.top = 0; + r.right = x; + r.bottom = y; + AdjustWindowRectEx(&r, WS_OVERLAPPEDWINDOW &~ + (WS_THICKFRAME | WS_MAXIMIZEBOX | + WS_OVERLAPPED), + TRUE, 0); + + SetWindowPos(fe->hwnd, NULL, 0, 0, + r.right - r.left, r.bottom - r.top, + SWP_NOMOVE | SWP_NOZORDER); + + DeleteObject(fe->bitmap); + + hdc = GetDC(fe->hwnd); + fe->bitmap = CreateCompatibleBitmap(hdc, x, y); + ReleaseDC(fe->hwnd, hdc); + + midend_redraw(fe->me); + } + } + break; + } + break; case WM_DESTROY: PostQuitMessage(0); return 0; @@ -233,26 +394,72 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, 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: @@ -290,7 +497,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) new_window(inst); while (GetMessage(&msg, NULL, 0, 0)) { - TranslateMessage(&msg); DispatchMessage(&msg); }