X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/3fee7a57faa025c2785c79625075761e91e98dba..aafaa7fbd122f8109c1a7d99faf74d4892bf22de:/windows.c diff --git a/windows.c b/windows.c index b93847d..08513d8 100644 --- a/windows.c +++ b/windows.c @@ -1,14 +1,5 @@ /* * windows.c: Windows front end for my puzzle collection. - * - * TODO: - * - * - Figure out what to do if a puzzle requests a size bigger than - * the screen will take. In principle we could put scrollbars in - * the window, although that would be pretty horrid. Another - * option is to detect in advance that this will be a problem - - * we can probably tell this using midend_size() before actually - * generating the puzzle - and simply refuse to do it. */ #include @@ -37,12 +28,14 @@ #define IDM_HELPC 0x00B0 #define IDM_GAMEHELP 0x00C0 #define IDM_ABOUT 0x00D0 +#define IDM_SAVE 0x00E0 +#define IDM_LOAD 0x00F0 #define IDM_PRESETS 0x0100 #define HELP_FILE_NAME "puzzles.hlp" #define HELP_CNT_NAME "puzzles.cnt" -#ifdef DEBUG +#ifdef DEBUGGING static FILE *debug_fp = NULL; static HANDLE debug_hdl = INVALID_HANDLE_VALUE; static int debug_got_console = 0; @@ -90,6 +83,12 @@ struct cfg_aux { int ctlid; }; +struct blitter { + HBITMAP bitmap; + frontend *fe; + int x, y, w, h; +}; + struct frontend { midend_data *me; HWND hwnd, statusbar, cfgbox; @@ -149,6 +148,82 @@ void status_bar(frontend *fe, char *text) } } +blitter *blitter_new(int w, int h) +{ + blitter *bl = snew(blitter); + + memset(bl, 0, sizeof(blitter)); + bl->w = w; + bl->h = h; + bl->bitmap = 0; + + return bl; +} + +void blitter_free(blitter *bl) +{ + if (bl->bitmap) DeleteObject(bl->bitmap); + sfree(bl); +} + +static void blitter_mkbitmap(frontend *fe, blitter *bl) +{ + HDC hdc = GetDC(fe->hwnd); + bl->bitmap = CreateCompatibleBitmap(hdc, bl->w, bl->h); + ReleaseDC(fe->hwnd, hdc); +} + +/* BitBlt(dstDC, dstX, dstY, dstW, dstH, srcDC, srcX, srcY, dType) */ + +void blitter_save(frontend *fe, blitter *bl, int x, int y) +{ + HDC hdc_win, hdc_blit; + HBITMAP prev_blit; + + if (!bl->bitmap) blitter_mkbitmap(fe, bl); + + bl->x = x; bl->y = y; + + hdc_win = GetDC(fe->hwnd); + hdc_blit = CreateCompatibleDC(hdc_win); + if (!hdc_blit) fatal("hdc_blit failed: 0x%x", GetLastError()); + + prev_blit = SelectObject(hdc_blit, bl->bitmap); + if (prev_blit == NULL || prev_blit == HGDI_ERROR) + fatal("SelectObject for hdc_main failed: 0x%x", GetLastError()); + + if (!BitBlt(hdc_blit, 0, 0, bl->w, bl->h, + fe->hdc_bm, x, y, SRCCOPY)) + fatal("BitBlt failed: 0x%x", GetLastError()); + + SelectObject(hdc_blit, prev_blit); + DeleteDC(hdc_blit); + ReleaseDC(fe->hwnd, hdc_win); +} + +void blitter_load(frontend *fe, blitter *bl, int x, int y) +{ + HDC hdc_win, hdc_blit; + HBITMAP prev_blit; + + assert(bl->bitmap); /* we should always have saved before loading */ + + if (x == BLITTER_FROMSAVED) x = bl->x; + if (y == BLITTER_FROMSAVED) y = bl->y; + + hdc_win = GetDC(fe->hwnd); + hdc_blit = CreateCompatibleDC(hdc_win); + + prev_blit = SelectObject(hdc_blit, bl->bitmap); + + BitBlt(fe->hdc_bm, x, y, bl->w, bl->h, + hdc_blit, 0, 0, SRCCOPY); + + SelectObject(hdc_blit, prev_blit); + DeleteDC(hdc_blit); + ReleaseDC(fe->hwnd, hdc_win); +} + void frontend_default_colour(frontend *fe, float *output) { DWORD c = GetSysColor(COLOR_MENU); /* ick */ @@ -257,8 +332,29 @@ void draw_line(frontend *fe, int x1, int y1, int x2, int y2, int colour) SelectObject(fe->hdc_bm, oldpen); } +void draw_circle(frontend *fe, int cx, int cy, int radius, + int fillcolour, int outlinecolour) +{ + assert(outlinecolour >= 0); + + if (fillcolour >= 0) { + HBRUSH oldbrush = SelectObject(fe->hdc_bm, fe->brushes[fillcolour]); + HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[outlinecolour]); + Ellipse(fe->hdc_bm, cx - radius, cy - radius, + cx + radius + 1, cy + radius + 1); + SelectObject(fe->hdc_bm, oldbrush); + SelectObject(fe->hdc_bm, oldpen); + } else { + HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[outlinecolour]); + Arc(fe->hdc_bm, cx - radius, cy - radius, + cx + radius + 1, cy + radius + 1, + cx - radius, cy, cx - radius, cy); + SelectObject(fe->hdc_bm, oldpen); + } +} + void draw_polygon(frontend *fe, int *coords, int npoints, - int fill, int colour) + int fillcolour, int outlinecolour) { POINT *pts = snewn(npoints+1, POINT); int i; @@ -269,14 +365,16 @@ void draw_polygon(frontend *fe, int *coords, int npoints, pts[i].y = coords[j*2+1]; } - if (fill) { - HBRUSH oldbrush = SelectObject(fe->hdc_bm, fe->brushes[colour]); - HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]); + assert(outlinecolour >= 0); + + if (fillcolour >= 0) { + HBRUSH oldbrush = SelectObject(fe->hdc_bm, fe->brushes[fillcolour]); + HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[outlinecolour]); Polygon(fe->hdc_bm, pts, npoints); SelectObject(fe->hdc_bm, oldbrush); SelectObject(fe->hdc_bm, oldpen); } else { - HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]); + HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[outlinecolour]); Polyline(fe->hdc_bm, pts, npoints+1); SelectObject(fe->hdc_bm, oldpen); } @@ -578,6 +676,9 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error) } AppendMenu(menu, MF_SEPARATOR, 0, 0); + AppendMenu(menu, MF_ENABLED, IDM_LOAD, "Load"); + AppendMenu(menu, MF_ENABLED, IDM_SAVE, "Save"); + AppendMenu(menu, MF_SEPARATOR, 0, 0); AppendMenu(menu, MF_ENABLED, IDM_UNDO, "Undo"); AppendMenu(menu, MF_ENABLED, IDM_REDO, "Redo"); if (thegame.can_format_as_text) { @@ -1122,13 +1223,12 @@ static int get_config(frontend *fe, int which) return (fe->dlg_done == 2); } -static void new_game_type(frontend *fe) +static void new_game_size(frontend *fe) { RECT r, sr; HDC hdc; int x, y; - midend_new_game(fe->me); x = y = INT_MAX; midend_size(fe->me, &x, &y, FALSE); @@ -1165,6 +1265,12 @@ static void new_game_type(frontend *fe) midend_redraw(fe->me); } +static void new_game_type(frontend *fe) +{ + midend_new_game(fe->me); + new_game_size(fe); +} + static int is_alt_pressed(void) { BYTE keystate[256]; @@ -1178,17 +1284,34 @@ static int is_alt_pressed(void) return FALSE; } +static void savefile_write(void *wctx, void *buf, int len) +{ + FILE *fp = (FILE *)wctx; + fwrite(buf, 1, len, fp); +} + +static int savefile_read(void *wctx, void *buf, int len) +{ + FILE *fp = (FILE *)wctx; + int ret; + + ret = fread(buf, 1, len, fp); + return (ret == len); +} + static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { frontend *fe = (frontend *)GetWindowLong(hwnd, GWL_USERDATA); + int cmd; switch (message) { case WM_CLOSE: DestroyWindow(hwnd); return 0; case WM_COMMAND: - switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */ + cmd = wParam & ~0xF; /* low 4 bits reserved to Windows */ + switch (cmd) { case IDM_NEW: if (!midend_process_key(fe->me, 0, 0, 'n')) PostQuitMessage(0); @@ -1241,6 +1364,92 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case IDM_ABOUT: about(fe); break; + case IDM_LOAD: + case IDM_SAVE: + { + OPENFILENAME of; + char filename[FILENAME_MAX]; + int ret; + + memset(&of, 0, sizeof(of)); + of.hwndOwner = hwnd; + of.lpstrFilter = "All Files (*.*)\0*\0\0\0"; + of.lpstrCustomFilter = NULL; + of.nFilterIndex = 1; + of.lpstrFile = filename; + filename[0] = '\0'; + of.nMaxFile = lenof(filename); + of.lpstrFileTitle = NULL; + of.lpstrTitle = (cmd == IDM_SAVE ? + "Enter name of game file to save" : + "Enter name of saved game file to load"); + of.Flags = 0; +#ifdef OPENFILENAME_SIZE_VERSION_400 + of.lStructSize = OPENFILENAME_SIZE_VERSION_400; +#else + of.lStructSize = sizeof(of); +#endif + of.lpstrInitialDir = NULL; + + if (cmd == IDM_SAVE) + ret = GetSaveFileName(&of); + else + ret = GetOpenFileName(&of); + + if (ret) { + if (cmd == IDM_SAVE) { + FILE *fp; + + if ((fp = fopen(filename, "r")) != NULL) { + char buf[256 + FILENAME_MAX]; + fclose(fp); + /* file exists */ + + sprintf(buf, "Are you sure you want to overwrite" + " the file \"%.*s\"?", + FILENAME_MAX, filename); + if (MessageBox(hwnd, buf, "Question", + MB_YESNO | MB_ICONQUESTION) + != IDYES) + break; + } + + fp = fopen(filename, "w"); + + if (!fp) { + MessageBox(hwnd, "Unable to open save file", + "Error", MB_ICONERROR | MB_OK); + break; + } + + midend_serialise(fe->me, savefile_write, fp); + + fclose(fp); + } else { + FILE *fp = fopen(filename, "r"); + char *err; + + if (!fp) { + MessageBox(hwnd, "Unable to open saved game file", + "Error", MB_ICONERROR | MB_OK); + break; + } + + err = midend_deserialise(fe->me, savefile_read, fp); + + fclose(fp); + + if (err) { + MessageBox(hwnd, err, "Error", MB_ICONERROR|MB_OK); + break; + } + + new_game_size(fe); + } + } + } + + break; case IDM_HELPC: assert(fe->help_path); WinHelp(hwnd, fe->help_path, @@ -1481,7 +1690,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) RegisterClass(&wndclass); } - while (*cmdline && isspace(*cmdline)) + while (*cmdline && isspace((unsigned char)*cmdline)) cmdline++; if (!new_window(inst, *cmdline ? cmdline : NULL, &error)) {