Add grotty casts to prevent negative -> large positive conversion of cursor
[sgt/puzzles] / windows.c
index 7288719..494caae 100644 (file)
--- a/windows.c
+++ b/windows.c
@@ -7,6 +7,7 @@
 
 #include <stdio.h>
 #include <assert.h>
+#include <ctype.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <time.h>
 #define IDM_REDO      0x0040
 #define IDM_QUIT      0x0050
 #define IDM_CONFIG    0x0060
+#define IDM_SEED      0x0070
+#define IDM_HELPC     0x0080
+#define IDM_GAMEHELP  0x0090
 #define IDM_PRESETS   0x0100
 
+#define HELP_FILE_NAME  "puzzles.hlp"
+#define HELP_CNT_NAME   "puzzles.cnt"
+
 #ifdef DEBUG
 static FILE *debug_fp = NULL;
 static HANDLE debug_hdl = INVALID_HANDLE_VALUE;
@@ -87,14 +94,17 @@ struct frontend {
     HPEN *pens;
     HRGN clip;
     UINT timer;
+    DWORD timer_last_tickcount;
     int npresets;
     game_params **presets;
     struct font *fonts;
     int nfonts, fontsize;
     config_item *cfg;
     struct cfg_aux *cfgaux;
-    int cfg_done;
+    int cfg_which, cfg_done;
     HFONT cfgfont;
+    char *help_path;
+    int help_has_contents;
 };
 
 void fatal(char *fmt, ...)
@@ -301,24 +311,75 @@ void deactivate_timer(frontend *fe)
 
 void activate_timer(frontend *fe)
 {
-    fe->timer = SetTimer(fe->hwnd, fe->timer, 20, NULL);
+    if (!fe->timer) {
+       fe->timer = SetTimer(fe->hwnd, fe->timer, 20, NULL);
+       fe->timer_last_tickcount = GetTickCount();
+    }
+}
+
+/*
+ * See if we can find a help file.
+ */
+static void find_help_file(frontend *fe)
+{
+    char b[2048], *p, *q, *r;
+    FILE *fp;
+    if (!fe->help_path) {
+        GetModuleFileName(NULL, b, sizeof(b) - 1);
+        r = b;
+        p = strrchr(b, '\\');
+        if (p && p >= r) r = p+1;
+        q = strrchr(b, ':');
+        if (q && q >= r) r = q+1;
+        strcpy(r, HELP_FILE_NAME);
+        if ( (fp = fopen(b, "r")) != NULL) {
+            fe->help_path = dupstr(b);
+            fclose(fp);
+        } else
+            fe->help_path = NULL;
+        strcpy(r, HELP_CNT_NAME);
+        if ( (fp = fopen(b, "r")) != NULL) {
+            fe->help_has_contents = TRUE;
+            fclose(fp);
+        } else
+            fe->help_has_contents = FALSE;
+    }
 }
 
-static frontend *new_window(HINSTANCE inst)
+static frontend *new_window(HINSTANCE inst, char *game_id, char **error)
 {
     frontend *fe;
     int x, y;
     RECT r, sr;
     HDC hdc;
+    time_t t;
 
     fe = snew(frontend);
-    fe->me = midend_new(fe);
+
+    time(&t);
+    fe->me = midend_new(fe, &t, sizeof(t));
+
+    if (game_id) {
+        *error = midend_game_id(fe->me, game_id, FALSE);
+        if (*error) {
+            midend_free(fe->me);
+            sfree(fe);
+            return NULL;
+        }
+    }
+
+    fe->help_path = NULL;
+    find_help_file(fe);
+
     fe->inst = inst;
-    midend_new_game(fe->me, NULL);
+    midend_new_game(fe->me);
     midend_size(fe->me, &x, &y);
 
     fe->timer = 0;
 
+    fe->fonts = NULL;
+    fe->nfonts = fe->fontsize = 0;
+
     {
        int i, ncolours;
         float *colours;
@@ -359,6 +420,7 @@ static frontend *new_window(HINSTANCE inst)
        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_SEED, "Specific...");
 
        if ((fe->npresets = midend_num_presets(fe->me)) > 0 ||
            game_can_configure) {
@@ -392,6 +454,19 @@ static frontend *new_window(HINSTANCE inst)
        AppendMenu(menu, MF_ENABLED, IDM_REDO, "Redo");
        AppendMenu(menu, MF_SEPARATOR, 0, 0);
        AppendMenu(menu, MF_ENABLED, IDM_QUIT, "Exit");
+        if (fe->help_path) {
+            HMENU hmenu = CreateMenu();
+            AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)hmenu, "Help");
+            AppendMenu(hmenu, MF_ENABLED, IDM_HELPC, "Contents");
+            if (game_winhelp_topic) {
+                char *item;
+                assert(game_name);
+                item = snewn(9+strlen(game_name), char); /*ick*/
+                sprintf(item, "Help on %s", game_name);
+                AppendMenu(hmenu, MF_ENABLED, IDM_GAMEHELP, item);
+                sfree(item);
+            }
+        }
        SetMenu(fe->hwnd, bar);
     }
 
@@ -443,7 +518,7 @@ static int CALLBACK ConfigDlgProc(HWND hwnd, UINT msg,
             HIWORD(wParam) == BN_DOUBLECLICKED) &&
            (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) {
            if (LOWORD(wParam) == IDOK) {
-               char *err = midend_set_config(fe->me, fe->cfg);
+               char *err = midend_set_config(fe->me, fe->cfg_which, fe->cfg);
 
                if (err) {
                    MessageBox(hwnd, err, "Validation error",
@@ -505,10 +580,11 @@ HWND mkctrl(frontend *fe, int x1, int x2, int y1, int y2,
     return ret;
 }
 
-static int get_config(frontend *fe)
+static int get_config(frontend *fe, int which)
 {
     config_item *i;
     struct cfg_aux *j;
+    char *title;
     WNDCLASS wc;
     MSG msg;
     TEXTMETRIC tm;
@@ -553,7 +629,8 @@ static int get_config(frontend *fe)
        height = width = 30;
     }
 
-    fe->cfg = midend_get_config(fe->me);
+    fe->cfg = midend_get_config(fe->me, which, &title);
+    fe->cfg_which = which;
 
     /*
      * Figure out the layout of the config box by measuring the
@@ -627,12 +704,13 @@ static int get_config(frontend *fe)
        r.right += r.left;
        r.bottom += r.top;
 
-       fe->cfgbox = CreateWindowEx(0, wc.lpszClassName, "Configuration",
+       fe->cfgbox = CreateWindowEx(0, wc.lpszClassName, title,
                                    DS_MODALFRAME | WS_POPUP | WS_VISIBLE |
                                    WS_CAPTION | WS_SYSMENU,
                                    r.left, r.top,
                                    r.right-r.left, r.bottom-r.top,
                                    fe->hwnd, NULL, fe->inst, NULL);
+       sfree(title);
     }
 
     SendMessage(fe->cfgbox, WM_SETFONT, (WPARAM)fe->cfgfont, FALSE);
@@ -671,6 +749,7 @@ static int get_config(frontend *fe)
            mkctrl(fe, col1l, col2r, y, y+height, "BUTTON",
                   BS_NOTIFY | BS_AUTOCHECKBOX | WS_TABSTOP,
                   0, i->name, (j->ctlid = id++));
+           CheckDlgButton(fe->cfgbox, j->ctlid, (i->ival != 0));
            y += height;
            break;
 
@@ -747,7 +826,7 @@ static void new_game_type(frontend *fe)
     HDC hdc;
     int x, y;
 
-    midend_new_game(fe->me, NULL);
+    midend_new_game(fe->me);
     midend_size(fe->me, &x, &y);
 
     r.left = r.top = 0;
@@ -812,9 +891,28 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                PostQuitMessage(0);
            break;
          case IDM_CONFIG:
-           if (get_config(fe))
+           if (get_config(fe, CFG_SETTINGS))
+               new_game_type(fe);
+           break;
+         case IDM_SEED:
+           if (get_config(fe, CFG_SEED))
                new_game_type(fe);
            break;
+          case IDM_HELPC:
+            assert(fe->help_path);
+            WinHelp(hwnd, fe->help_path,
+                    fe->help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0);
+            break;
+          case IDM_GAMEHELP:
+            assert(fe->help_path);
+            assert(game_winhelp_topic);
+            {
+                char *cmd = snewn(10+strlen(game_winhelp_topic), char); /*ick*/
+                sprintf(cmd, "JI(`',`%s')", game_winhelp_topic);
+                WinHelp(hwnd, fe->help_path, HELP_COMMAND, (DWORD)cmd);
+                sfree(cmd);
+            }
+            break;
          default:
            {
                int p = ((wParam &~ 0xF) - IDM_PRESETS) / 0x10;
@@ -918,9 +1016,52 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                button = LEFT_BUTTON;
            else
                button = RIGHT_BUTTON;
-               
-           if (!midend_process_key(fe->me, LOWORD(lParam),
-                                   HIWORD(lParam), button))
+
+           if (!midend_process_key(fe->me, (signed short)LOWORD(lParam),
+                                   (signed short)HIWORD(lParam), button))
+               PostQuitMessage(0);
+
+           SetCapture(hwnd);
+       }
+       break;
+      case WM_LBUTTONUP:
+      case WM_RBUTTONUP:
+      case WM_MBUTTONUP:
+       {
+           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_MBUTTONUP || (wParam & MK_SHIFT))
+               button = MIDDLE_RELEASE;
+           else if (message == WM_LBUTTONUP)
+               button = LEFT_RELEASE;
+           else
+               button = RIGHT_RELEASE;
+
+           if (!midend_process_key(fe->me, (signed short)LOWORD(lParam),
+                                   (signed short)HIWORD(lParam), button))
+               PostQuitMessage(0);
+
+           ReleaseCapture();
+       }
+       break;
+      case WM_MOUSEMOVE:
+       {
+           int button;
+
+           if (wParam & (MK_MBUTTON | MK_SHIFT))
+               button = MIDDLE_DRAG;
+           else if (wParam & MK_LBUTTON)
+               button = LEFT_DRAG;
+           else
+               button = RIGHT_DRAG;
+           
+           if (!midend_process_key(fe->me, (signed short)LOWORD(lParam),
+                                   (signed short)HIWORD(lParam), button))
                PostQuitMessage(0);
        }
        break;
@@ -929,8 +1070,12 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            PostQuitMessage(0);
        return 0;
       case WM_TIMER:
-       if (fe->timer)
-           midend_timer(fe->me, (float)0.02);
+       if (fe->timer) {
+           DWORD now = GetTickCount();
+           float elapsed = (float) (now - fe->timer_last_tickcount) * 0.001F;
+           midend_timer(fe->me, elapsed);
+           fe->timer_last_tickcount = now;
+       }
        return 0;
     }
 
@@ -940,8 +1085,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 {
     MSG msg;
-
-    srand(time(NULL));
+    char *error;
 
     InitCommonControls();
 
@@ -962,7 +1106,15 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        RegisterClass(&wndclass);
     }
 
-    new_window(inst);
+    while (*cmdline && isspace(*cmdline))
+       cmdline++;
+
+    if (!new_window(inst, *cmdline ? cmdline : NULL, &error)) {
+       char buf[128];
+       sprintf(buf, "%.100s Error", game_name);
+       MessageBox(NULL, error, buf, MB_OK|MB_ICONERROR);
+       return 1;
+    }
 
     while (GetMessage(&msg, NULL, 0, 0)) {
        DispatchMessage(&msg);