chiark / gitweb /
Implemented text and clipping primitives in the frontend, and added
[sgt-puzzles.git] / windows.c
index 618d440fe5bfc90205ddfd873bb721344c05093f..ea5d7a27393c1c4cc1bd5378d89a152d52bf3976 100644 (file)
--- a/windows.c
+++ b/windows.c
@@ -5,6 +5,7 @@
 #include <windows.h>
 
 #include <stdio.h>
+#include <assert.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <time.h>
 #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 font {
+    HFONT font;
+    int type;
+    int size;
+};
+
 struct frontend {
     midend_data *me;
     HWND hwnd;
@@ -26,9 +78,12 @@ struct frontend {
     COLORREF *colours;
     HBRUSH *brushes;
     HPEN *pens;
+    HRGN clip;
     UINT timer;
     int npresets;
     game_params **presets;
+    struct font *fonts;
+    int nfonts, fontsize;
 };
 
 void fatal(char *fmt, ...)
@@ -54,6 +109,86 @@ void frontend_default_colour(frontend *fe, float *output)
     output[2] = (float)(GetBValue(c) / 255.0);
 }
 
+void clip(frontend *fe, int x, int y, int w, int h)
+{
+    if (!fe->clip) {
+       fe->clip = CreateRectRgn(0, 0, 1, 1);
+       GetClipRgn(fe->hdc_bm, fe->clip);
+    }
+
+    IntersectClipRect(fe->hdc_bm, x, y, x+w, y+h);
+}
+
+void unclip(frontend *fe)
+{
+    assert(fe->clip);
+    SelectClipRgn(fe->hdc_bm, fe->clip);
+}
+
+void draw_text(frontend *fe, int x, int y, int fonttype, int fontsize,
+               int align, int colour, char *text)
+{
+    int i;
+
+    /*
+     * Find or create the font.
+     */
+    for (i = 0; i < fe->nfonts; i++)
+        if (fe->fonts[i].type == fonttype && fe->fonts[i].size == fontsize)
+            break;
+
+    if (i == fe->nfonts) {
+        if (fe->fontsize <= fe->nfonts) {
+            fe->fontsize = fe->nfonts + 10;
+            fe->fonts = sresize(fe->fonts, fe->fontsize, struct font);
+        }
+
+        fe->nfonts++;
+
+        fe->fonts[i].type = fonttype;
+        fe->fonts[i].size = fontsize;
+
+        /*
+         * FIXME: Really I should make at least _some_ effort to
+         * pick the correct font.
+         */
+        fe->fonts[i].font = CreateFont(-fontsize, 0, 0, 0, 0,
+                                      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);
+    }
+
+    /*
+     * Position and draw the text.
+     */
+    {
+       HFONT oldfont;
+       TEXTMETRIC tm;
+       SIZE size;
+
+       oldfont = SelectObject(fe->hdc_bm, fe->fonts[i].font);
+       if (GetTextMetrics(fe->hdc_bm, &tm)) {
+           if (align & ALIGN_VCENTRE)
+               y -= (tm.tmAscent+tm.tmDescent)/2;
+           else
+               y -= tm.tmAscent;
+       }
+       if (GetTextExtentPoint32(fe->hdc_bm, text, strlen(text), &size)) {
+           if (align & ALIGN_HCENTRE)
+               x -= size.cx / 2;
+           else if (align & ALIGN_HRIGHT)
+               x -= size.cx;
+       }
+       SetBkMode(fe->hdc_bm, TRANSPARENT);
+       TextOut(fe->hdc_bm, x, y, text, strlen(text));
+       SelectObject(fe->hdc_bm, oldfont);
+    }
+}
+
 void draw_rect(frontend *fe, int x, int y, int w, int h, int colour)
 {
     if (w == 1 && h == 1) {
@@ -116,6 +251,7 @@ void start_draw(frontend *fe)
     fe->hdc_bm = CreateCompatibleDC(hdc_win);
     fe->prevbm = SelectObject(fe->hdc_bm, fe->bitmap);
     ReleaseDC(fe->hwnd, hdc_win);
+    fe->clip = NULL;
 }
 
 void draw_update(frontend *fe, int x, int y, int w, int h)
@@ -134,6 +270,10 @@ void end_draw(frontend *fe)
 {
     SelectObject(fe->hdc_bm, fe->prevbm);
     DeleteDC(fe->hdc_bm);
+    if (fe->clip) {
+       DeleteObject(fe->clip);
+       fe->clip = NULL;
+    }
 }
 
 void deactivate_timer(frontend *fe)
@@ -189,7 +329,7 @@ static frontend *new_window(HINSTANCE inst)
                       (WS_THICKFRAME | WS_MAXIMIZEBOX | WS_OVERLAPPED),
                       TRUE, 0);
 
-    fe->hwnd = CreateWindowEx(0, "puzzle", "puzzle",
+    fe->hwnd = CreateWindowEx(0, game_name, game_name,
                              WS_OVERLAPPEDWINDOW &~
                              (WS_THICKFRAME | WS_MAXIMIZEBOX),
                              CW_USEDEFAULT, CW_USEDEFAULT,
@@ -349,23 +489,69 @@ 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, 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, 0, 0, (unsigned char)wParam))
@@ -398,7 +584,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
        wndclass.hbrBackground = NULL;
        wndclass.lpszMenuName = NULL;
-       wndclass.lpszClassName = "puzzle";
+       wndclass.lpszClassName = game_name;
 
        RegisterClass(&wndclass);
     }
@@ -406,7 +592,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);
     }