From 4eed0d77aed5ee4208dab7cca3a7b4d37a70e152 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 8 Sep 2015 11:51:12 -0400 Subject: [PATCH] More uiArea work, this time on Windows. --- gtkarea/draw.c | 1 + winarea/area.c | 114 ++++++++++++++++++++++++++++++++++ winarea/area.h | 16 +++++ winarea/debug.c | 111 +++++++++++++++++++++++++++++++++ winarea/draw.c | 12 ++-- winarea/main.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 411 insertions(+), 5 deletions(-) create mode 100644 winarea/area.c create mode 100644 winarea/area.h create mode 100644 winarea/debug.c create mode 100644 winarea/main.c diff --git a/gtkarea/draw.c b/gtkarea/draw.c index 36538d89..ba7977c7 100644 --- a/gtkarea/draw.c +++ b/gtkarea/draw.c @@ -9,6 +9,7 @@ uiDrawContext *newContext(cairo_t *cr) { uiDrawContext *c; + // TODO use uiNew c = (uiDrawContext *) g_malloc0(sizeof (uiDrawContext)); c->cr = cr; return c; diff --git a/winarea/area.c b/winarea/area.c new file mode 100644 index 00000000..d326bd32 --- /dev/null +++ b/winarea/area.c @@ -0,0 +1,114 @@ +// 8 september 2015 +#include "area.h" + +#define areaClass L"libui_uiAreaClass" + +static void doPaint(uiArea *a, uiAreaHandler *ah, HDC dc, RECT *client, RECT *clip) +{ + uiAreaDrawParams dp; + + dp.Context = newContext(dc); + + dp.ClientWidth = client->right - client->left; + dp.ClientHeight = client->bottom - client->top; + + dp.ClipX = clip->left; + dp.ClipY = clip->top; + dp.ClipWidth = clip->right - clip->left; + dp.ClipHeight = clip->bottom - clip->top; + + // TODO is this really the best for multimonitor setups? + dp.DPIX = GetDeviceCaps(dc, LOGPIXELSX); + dp.DPIY = GetDeviceCaps(dc, LOGPIXELSY); + +/* TODO + dp.HScrollPos = gtk_adjustment_get_value(ap->ha); + dp.VScrollPos = gtk_adjustment_get_value(ap->va); +*/ + + (*(ah->Draw))(ah, a, &dp); +} + +struct areainit { + uiArea *a; + uiAreaHandler *ah; +}; + +#define gwlpArea 0 +#define gwlpAreaHandler (sizeof (uiArea *)) + +static LRESULT CALLBACK areaWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + uiArea *a; + uiAreaHandler *ah; + struct areainit *ai; + CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam; + HDC dc; + PAINTSTRUCT ps; + RECT client; + + a = (uiArea *) GetWindowLongPtrW(hwnd, gwlpArea); + ah = (uiAreaHandler *) GetWindowLongPtrW(hwnd, gwlpAreaHandler); + if (a == NULL) { + if (uMsg == WM_NCCREATE) { + ai = (struct areainit *) (cs->lpCreateParams); + SetWindowLongPtrW(hwnd, gwlpArea, (LONG_PTR) (ai->a)); + SetWindowLongPtrW(hwnd, gwlpAreaHandler, (LONG_PTR) (ai->ah)); + } + // fall through to DefWindowProcW() anyway + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } + + switch (uMsg) { + case WM_PAINT: + dc = BeginPaint(hwnd, &ps); + if (dc == NULL) + logLastError("error beginning paint in areaWndProc"); + if (GetClientRect(hwnd, &client) == 0) + logLastError("error getting client rect in WM_PAINT in areaWndProc()"); + doPaint(a, ah, dc, &client, &(ps.rcPaint)); + EndPaint(hwnd, &ps); + return 0; + case WM_PRINTCLIENT: + if (GetClientRect(hwnd, &client) == 0) + logLastError("error getting client rect in WM_PRINTCLIENT in areaWndProc()"); + doPaint(a, ah, (HDC) wParam, &client, &client); + return 0; + } + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +ATOM registerAreaClass(void) +{ + WNDCLASSW wc; + + ZeroMemory(&wc, sizeof (WNDCLASSW)); + wc.lpszClassName = areaClass; + wc.lpfnWndProc = areaWndProc; + wc.hInstance = hInstance; +//TODO wc.hIcon = hDefaultIcon; +//TODO wc.hCursor = hDefaultCursor; + wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.cbWndExtra = sizeof (uiArea *) + sizeof (uiAreaHandler *); + return RegisterClassW(&wc); +} + +void unregisterAreaClass(void) +{ + if (UnregisterClassW(areaClass, hInstance) == 0) + logLastError("error unregistering uiArea window class in unregisterAreaClass()"); +} + +HWND makeArea(DWORD exstyle, DWORD style, int x, int y, int cx, int cy, HWND parent, uiAreaHandler *ah) +{ + struct areainit ai; + + ai.a = NULL; + ai.ah = ah; + return CreateWindowExW(exstyle, + areaClass, L"", + style | WS_HSCROLL | WS_VSCROLL, + x, y, cx, cy, + parent, NULL, hInstance, &ai); +} diff --git a/winarea/area.h b/winarea/area.h new file mode 100644 index 00000000..3f47a932 --- /dev/null +++ b/winarea/area.h @@ -0,0 +1,16 @@ +// 8 september 2015 +#include "../windows/winapi.h" +#include +#include "ui.h" + +extern HINSTANCE hInstance; + +extern ATOM registerAreaClass(void); +extern void unregisterAreaClass(void); +extern HWND makeArea(DWORD exstyle, DWORD style, int x, int y, int cx, int cy, HWND parent, uiAreaHandler *ah); + +extern HRESULT logLastError(const char *); +extern HRESULT logHRESULT(const char *, HRESULT); +extern HRESULT logMemoryExhausted(const char *); + +extern uiDrawContext *newContext(HDC); diff --git a/winarea/debug.c b/winarea/debug.c new file mode 100644 index 00000000..ca8e20be --- /dev/null +++ b/winarea/debug.c @@ -0,0 +1,111 @@ +// 25 february 2015 +#include "area.h" + +// uncomment the following line to enable debug messages +#define tableDebug +// uncomment the following line to halt on a debug message +#define tableDebugStop + +#ifdef tableDebug + +#include + +HRESULT logLastError(const char *context) +{ + DWORD le; + WCHAR *msg; + BOOL parenthesize = FALSE; + BOOL localFreeFailed = FALSE; + DWORD localFreeLastError; + + le = GetLastError(); + fprintf(stderr, "%s: ", context); + if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, le, 0, (LPWSTR) (&msg), 0, NULL) != 0) { + fprintf(stderr, "%S (", msg); + if (LocalFree(msg) != NULL) { + localFreeFailed = TRUE; + localFreeLastError = GetLastError(); + } + parenthesize = TRUE; + } + fprintf(stderr, "GetLastError() == %I32u", le); + if (parenthesize) + fprintf(stderr, ")"); + if (localFreeFailed) + fprintf(stderr, "; local free of system message failed with last error %I32u", localFreeLastError); + fprintf(stderr, "\n"); +#ifdef tableDebugStop + DebugBreak(); +#endif + SetLastError(le); + // a function does not have to set a last error + // if the last error we get is actually 0, then HRESULT_FROM_WIN32(0) will return S_OK (0 cast to an HRESULT, since 0 <= 0), which we don't want + // prevent this by returning E_FAIL, so the rest of the Table code doesn't barge onward + if (le == 0) + return E_FAIL; + return HRESULT_FROM_WIN32(le); +} + +HRESULT logHRESULT(const char *context, HRESULT hr) +{ + WCHAR *msg; + BOOL parenthesize = FALSE; + BOOL localFreeFailed = FALSE; + DWORD localFreeLastError; + + fprintf(stderr, "%s: ", context); + // this isn't technically documented, but everyone does it, including Microsoft (see the implementation of _com_error::ErrorMessage() in a copy of comdef.h that comes with the Windows DDK) + if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD) hr, 0, (LPWSTR) (&msg), 0, NULL) != 0) { + fprintf(stderr, "%S (", msg); + if (LocalFree(msg) != NULL) { + localFreeFailed = TRUE; + localFreeLastError = GetLastError(); + } + parenthesize = TRUE; + } + fprintf(stderr, "HRESULT == 0x%I32X", hr); + if (parenthesize) + fprintf(stderr, ")"); + if (localFreeFailed) + fprintf(stderr, "; local free of system message failed with last error %I32u", localFreeLastError); + fprintf(stderr, "\n"); +#ifdef tableDebugStop + DebugBreak(); +#endif + return hr; +} + +HRESULT logMemoryExhausted(const char *reason) +{ + fprintf(stderr, "memory exhausted %s\n", reason); +#ifdef tableDebugStop + DebugBreak(); +#endif + return E_OUTOFMEMORY; +} + +#else + +HRESULT logLastError(const char *reason) +{ + DWORD le; + + le = GetLastError(); + // we shouldn't need to do this, but let's do this anyway just to be safe + SetLastError(le); + if (le == 0) + return E_FAIL; + return HRESULT_FROM_WIN32(le); +} + +HRESULT logHRESULT(const char *reason, HRESULT hr) +{ + return hr; +} + +HRESULT logMemoryExhausted(const char *reason) +{ + return E_OUTOFMEMORY; +} + +#endif diff --git a/winarea/draw.c b/winarea/draw.c index a09b51ff..b70c8a93 100644 --- a/winarea/draw.c +++ b/winarea/draw.c @@ -17,7 +17,8 @@ uiDrawContext *newContext(HDC dc) { uiDrawContext *c; - c = (uiDrawContext *) g_malloc0(sizeof (uiDrawContext)); + // TODO use uiNew + c = (uiDrawContext *) malloc(sizeof (uiDrawContext)); c->dc = dc; return c; } @@ -70,7 +71,7 @@ void uiDrawArc(uiDrawContext *c, intmax_t xCenter, intmax_t yCenter, intmax_t ra } // TODO convert radians to degrees if (AngleArc(c->dc, - xCenter, yCenter + xCenter, yCenter, radius, startAngle, // the "sweep angle" is relative to the start angle, not to 0 @@ -94,7 +95,7 @@ void uiDrawBezierTo(uiDrawContext *c, intmax_t c1x, intmax_t c1y, intmax_t c2x, void uiDrawCloseFigure(uiDrawContext *c) { - if (CloseFigure(c->currentDC) == 0) + if (CloseFigure(c->dc) == 0) logLastError("error closing figure in uiDrawCloseFigure()"); } @@ -199,7 +200,7 @@ static void startAlpha(uiDrawContext *c, struct alpha *a) logLastError("error creating compatible DC in startAlpha()"); // now create and select in a *device-independent* bitmap that will hold the data that we're going to alpha blend - ZeroMemory(&bmi, sizeof (BITMAPINFO)); + ZeroMemory(&bi, sizeof (BITMAPINFO)); bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); bi.bmiHeader.biWidth = a->r.right - a->r.left; bi.bmiHeader.biHeight = -(a->r.bottom - a->r.top); // negative to draw top-down @@ -299,6 +300,7 @@ static HBRUSH toBrush(uiDrawContext *c) return brush; } // TODO + return NULL; } void uiDrawFill(uiDrawContext *c, uiDrawFillMode mode) @@ -333,7 +335,7 @@ void uiDrawFill(uiDrawContext *c, uiDrawFillMode mode) logLastError("error selecting brush into DC in uiDrawFill()"); if (FillPath(fillDC) == 0) logLastError("error filling path in uiDrawFill()"); - if (SelectObject(strokeDC, prevbrush) != brush) + if (SelectObject(fillDC, prevbrush) != brush) logLastError("error deselecting brush from DC in uiDrawFill()"); if (DeleteObject(brush) == 0) logLastError("error deleting pen in uiDrawStroke()"); diff --git a/winarea/main.c b/winarea/main.c new file mode 100644 index 00000000..4db60ddb --- /dev/null +++ b/winarea/main.c @@ -0,0 +1,162 @@ +// 4 september 2015 +#define _GNU_SOURCE +#include "area.h" +#include + +// #qo LIBS: user32 kernel32 gdi32 msimg32 + +struct handler { + uiAreaHandler ah; +}; + +static HWND area; +static struct handler h; + +static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) +{ + uiDrawStrokeParams sp; + + uiDrawBeginPathRGB(p->Context, 0xFF, 0x00, 0x00); + uiDrawMoveTo(p->Context, p->ClipX + 5, p->ClipY + 5); + uiDrawLineTo(p->Context, (p->ClipX + p->ClipWidth) - 5, (p->ClipY + p->ClipHeight) - 5); + sp.Cap = uiDrawLineCapFlat; + sp.Join = uiDrawLineJoinMiter; + sp.Thickness = 1; + sp.MiterLimit = uiDrawDefaultMiterLimit; + uiDrawStroke(p->Context, &sp); + + uiDrawBeginPathRGB(p->Context, 0x00, 0x00, 0xC0); + uiDrawMoveTo(p->Context, p->ClipX, p->ClipY); + uiDrawLineTo(p->Context, p->ClipX + p->ClipWidth, p->ClipY); + uiDrawLineTo(p->Context, 50, 150); + uiDrawLineTo(p->Context, 50, 50); + uiDrawCloseFigure(p->Context); + sp.Cap = uiDrawLineCapFlat; + sp.Join = uiDrawLineJoinRound; + sp.Thickness = 5; + uiDrawStroke(p->Context, &sp); + + uiDrawBeginPathRGBA(p->Context, 0x00, 0xC0, 0x00, 0x80); + uiDrawRectangle(p->Context, 120, 80, 50, 50); + uiDrawFill(p->Context, uiDrawFillModeWinding); + + uiDrawBeginPathRGB(p->Context, 0x00, 0x80, 0x00); + uiDrawMoveTo(p->Context, 5, 10); + uiDrawLineTo(p->Context, 5, 50); + sp.Cap = uiDrawLineCapFlat; + sp.Join = uiDrawLineJoinMiter; + sp.Thickness = 1; + sp.MiterLimit = uiDrawDefaultMiterLimit; + uiDrawStroke(p->Context, &sp); + + uiDrawBeginPathRGB(p->Context, 0x80, 0xC0, 0x00); + uiDrawMoveTo(p->Context, 400, 100); + uiDrawArc(p->Context, + 400, 100, + 50, + 30. * (M_PI / 180.), + // note the end angle here + // in GDI, the second angle to AngleArc() is relative to the start, not to 0 + 330. * (M_PI / 180.), + 1); + // TODO add a checkbox for this + uiDrawLineTo(p->Context, 400, 100); + uiDrawArc(p->Context, + 510, 100, + 50, + 30. * (M_PI / 180.), + 330. * (M_PI / 180.), + 0); + uiDrawCloseFigure(p->Context); + sp.Cap = uiDrawLineCapFlat; + sp.Join = uiDrawLineJoinMiter; + sp.Thickness = 1; + sp.MiterLimit = uiDrawDefaultMiterLimit; + uiDrawStroke(p->Context, &sp); + + uiDrawBeginPathRGB(p->Context, 0x00, 0x80, 0xC0); + uiDrawMoveTo(p->Context, 300, 300); + uiDrawBezierTo(p->Context, + 350, 320, + 310, 390, + 435, 372); + sp.Cap = uiDrawLineCapFlat; + sp.Join = uiDrawLineJoinMiter; + sp.Thickness = 1; + sp.MiterLimit = uiDrawDefaultMiterLimit; + uiDrawStroke(p->Context, &sp); +} + +static uintmax_t handlerHScrollMax(uiAreaHandler *a, uiArea *area) +{ +return 0;//TODO return gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(nhspinb)); +} + +static uintmax_t handlerVScrollMax(uiAreaHandler *a, uiArea *area) +{ +return 0;//TODO return gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(nvspinb)); +} + +/* TODO +static void recalcScroll(GtkSpinButton *sb, gpointer data) +{ + areaUpdateScroll(area); +} + +static GtkWidget *makeSpinbox(int min) +{ + GtkWidget *sb; + + sb = gtk_spin_button_new_with_range(min, 100000, 1); + gtk_spin_button_set_digits(GTK_SPIN_BUTTON(sb), 0); + g_signal_connect(sb, "value-changed", G_CALLBACK(recalcScroll), NULL); + return sb; +} +*/ + +HINSTANCE hInstance; + +int main(void) +{ + MSG msg; + + hInstance = GetModuleHandle(NULL); + + h.ah.Draw = handlerDraw; + h.ah.HScrollMax = handlerHScrollMax; + h.ah.VScrollMax = handlerVScrollMax; + + registerAreaClass(); + + area = makeArea(0, + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + NULL, + (uiAreaHandler *) (&h)); + +/* TODO + gtk_grid_attach(GTK_GRID(grid), + gtk_label_new("H Count"), + 0, 0, 1, 1); + nhspinb = makeSpinbox(0); + gtk_grid_attach(GTK_GRID(grid), nhspinb, + 1, 0, 1, 1); + + gtk_grid_attach(GTK_GRID(grid), + gtk_label_new("V Count"), + 0, 1, 1, 1); + nvspinb = makeSpinbox(0); + gtk_grid_attach(GTK_GRID(grid), nvspinb, + 1, 1, 1, 1); +*/ + + ShowWindow(area, SW_SHOWDEFAULT); + UpdateWindow(area); + + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return 0; +}