diff --git a/new/alloc_windows.c b/new/alloc_windows.c new file mode 100644 index 0000000..049e909 --- /dev/null +++ b/new/alloc_windows.c @@ -0,0 +1,39 @@ +// 4 december 2014 +#include "ui_windows.h" + +// wrappers for allocator of choice +// panics on memory exhausted, undefined on heap corruption or other unreliably-detected malady (see http://stackoverflow.com/questions/28761680/is-there-a-windows-api-memory-allocator-deallocator-i-can-use-that-will-just-giv) +// new memory is set to zero +// passing NULL to tableRealloc() acts like tableAlloc() +// passing NULL to tableFree() is a no-op + +void *uiAlloc(size_t size) +{ + void *out; + + out = malloc(size); + if (out == NULL) + abort(); // TODO figure this part out + ZeroMemory(out, size); + return out; +} + +void *uiRealloc(void *p, size_t size) +{ + void *out; + + if (p == NULL) + return uiAlloc(size); + out = realloc(p, size); + if (out == NULL) + abort(); + // TODO zero the extra memory + return out; +} + +void uiFree(void *p) +{ + if (p == NULL) + return; + free(p); +} diff --git a/new/init_unix.c b/new/init_unix.c index 4d951af..bda6481 100644 --- a/new/init_unix.c +++ b/new/init_unix.c @@ -7,22 +7,22 @@ struct uiInitError { uiInitError *uiInit(uiInitOptions *o) { - uiInitError *e; + uiInitError *err; - e = g_new0(uiInitError, 1); - if (gtk_init_with_args(NULL, NULL, NULL, NULL, NULL, &(e->err)) == FALSE) - return e; - g_free(e); + err = g_new0(uiInitError, 1); + if (gtk_init_with_args(NULL, NULL, NULL, NULL, NULL, &(err->err)) == FALSE) + return err; + g_free(err); return NULL; } -const char *uiInitErrorMessage(uiInitError *e) +const char *uiInitErrorMessage(uiInitError *err) { - return e->err->message; + return err->err->message; } -void uiInitErrorFree(uiInitError *e) +void uiInitErrorFree(uiInitError *err) { - g_error_free(e->err); - g_free(e); + g_error_free(err->err); + g_free(err); } diff --git a/new/init_windows.c b/new/init_windows.c new file mode 100644 index 0000000..07031aa --- /dev/null +++ b/new/init_windows.c @@ -0,0 +1,87 @@ +// 6 april 2015 +#include "ui_windows.h" + +HINSTANCE hInstance; +int nCmdShow; + +HFONT hMessageFont; + +struct uiInitError { + char *msg; + char failbuf[256]; +}; + +static void loadLastError(uiInitError *err, char *message) +{ + DWORD le; + + le = GetLastError(); + // TODO FormatMessageW() it + _snprintf_s(err->failbuf, 256, _TRUNCATE, "error %s (last error %I32u)", message, le); + err->msg = err->failbuf; +} + +uiInitError *uiInit(uiInitOptions *o) +{ + uiInitError *err; + STARTUPINFOW si; + HICON hDefaultIcon; + HCURSOR hDefaultCursor; + NONCLIENTMETRICSW ncm; + + err = (uiInitError *) uiAlloc(sizeof (uiInitError)); + + hInstance = GetModuleHandle(NULL); + if (hInstance == NULL) { + loadLastError(err, "getting program HINSTANCE"); + return err; + } + + nCmdShow = SW_SHOWDEFAULT; + GetStartupInfoW(&si); + if ((si.dwFlags & STARTF_USESHOWWINDOW) != 0) + nCmdShow = si.wShowWindow; + + hDefaultIcon = LoadIconW(NULL, IDI_APPLICATION); + if (hDefaultIcon == NULL) { + loadLastError(err, "loading default icon for window classes"); + return err; + } + hDefaultCursor = LoadCursorW(NULL, IDC_ARROW); + if (hDefaultCursor == NULL) { + loadLastError(err, "loading default cursor for window classes"); + return err; + } + + if (registerWindowClass(hDefaultIcon, hDefaultCursor) == 0) { + loadLastError(err, "registering uiWindow window class"); + return err; + } + + ZeroMemory(&ncm, sizeof (NONCLIENTMETRICSW)); + ncm.cbSize = sizeof (NONCLIENTMETRICSW); + if (SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof (NONCLIENTMETRICSW), &ncm, sizeof (NONCLIENTMETRICSW)) == 0) { + loadLastError(err, "getting default fonts"); + return err; + } + hMessageFont = CreateFontIndirectW(&(ncm.lfMessageFont)); + if (hMessageFont == NULL) { + loadLastError(err, "loading default messagebox font; this is the default UI font"); + return err; + } + + uiFree(err); + return NULL; +} + +const char *uiInitErrorMessage(uiInitError *err) +{ + return err->msg; +} + +void uiInitErrorFree(uiInitError *err) +{ + if (err->msg != err->failbuf) + uiFree(err->msg); + uiFree(err); +} diff --git a/new/main_unix.c b/new/main_unix.c index 988519a..b95e99e 100644 --- a/new/main_unix.c +++ b/new/main_unix.c @@ -1,6 +1,8 @@ // 6 april 2015 #include "ui_unix.h" +// #qo pkg-config: gtk+-3.0 + void uiMain(void) { gtk_main(); diff --git a/new/main_windows.c b/new/main_windows.c new file mode 100644 index 0000000..43e6b5c --- /dev/null +++ b/new/main_windows.c @@ -0,0 +1,56 @@ +// 6 april 2015 +#include "ui_windows.h" + +// #qo LDFLAGS: -luser32 -lkernel32 -lgdi32 -luxtheme -lmsimg32 -lcomdlg32 -lole32 -loleaut32 -loleacc -luuid + +static void uimsgloop_else(MSG *msg) +{ + TranslateMessage(msg); + DispatchMessage(msg); +} + +void uiMain(void) +{ + MSG msg; + int res; + HWND active, focus; + + for (;;) { + SetLastError(0); + res = GetMessageW(&msg, NULL, 0, 0); + if (res < 0) + logLastError("error calling GetMessage() in uiMain()"); + if (res == 0) // WM_QUIT + break; + active = GetActiveWindow(); + if (active == NULL) { + uimsgloop_else(&msg); + continue; + } + + // bit of logic involved here: + // we don't want dialog messages passed into Areas, so we don't call IsDialogMessageW() there + // as for Tabs, we can't have both WS_TABSTOP and WS_EX_CONTROLPARENT set at the same time, so we hotswap the two styles to get the behavior we want + focus = GetFocus(); + if (focus != NULL) { +/*TODO switch (windowClassOf(focus, areaWindowClass, WC_TABCONTROLW, NULL)) { + case 0: // areaWindowClass + uimsgloop_area(active, focus, &msg); + continue; + case 1: // WC_TABCONTROLW + uimsgloop_tab(active, focus, &msg); + continue; + } + // else fall through +*/ } + + if (IsDialogMessage(active, &msg) != 0) + continue; + uimsgloop_else(&msg); + } +} + +void uiQuit(void) +{ + PostQuitMessage(0); +} diff --git a/new/test.c b/new/test.c index cd35a5b..6bd5049 100644 --- a/new/test.c +++ b/new/test.c @@ -2,8 +2,6 @@ #include "ui.h" #include -// #qo pkg-config: gtk+-3.0 - int onClosing(uiWindow *w, void *data) { printf("in closing!\n"); diff --git a/new/ui_windows.h b/new/ui_windows.h new file mode 100644 index 0000000..3545244 --- /dev/null +++ b/new/ui_windows.h @@ -0,0 +1,55 @@ +// 6 january 2015 + +#ifndef __UI_UI_WINDOWS_H__ +#define __UI_UI_WINDOWS_H__ + +#define UNICODE +#define _UNICODE +#define STRICT +#define STRICT_TYPED_ITEMIDS +#define CINTERFACE +#define COBJMACROS +// see https://github.com/golang/go/issues/9916#issuecomment-74812211 +#define INITGUID +// get Windows version right; right now Windows XP +#define WINVER 0x0501 +#define _WIN32_WINNT 0x0501 +#define _WIN32_WINDOWS 0x0501 /* according to Microsoft's winperf.h */ +#define _WIN32_IE 0x0600 /* according to Microsoft's sdkddkver.h */ +#define NTDDI_VERSION 0x05010000 /* according to Microsoft's sdkddkver.h */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ui.h" + +// alloc_windows.c +extern void *uiAlloc(size_t); +extern void *uiRealloc(void *, size_t); +extern void uiFree(void *); + +// debug_windows.c +extern HRESULT logLastError(const char *); +extern HRESULT logHRESULT(const char *, HRESULT); +extern HRESULT logMemoryExhausted(const char *); + +// init_windows.c +extern HINSTANCE hInstance; +extern int nCmdShow; +extern HFONT hMessageFont; + +// util_windows.c +extern WCHAR *toUTF16(const char *); + +// window_windows.c +extern ATOM registerWindowClass(HICON, HCURSOR); + +#endif diff --git a/new/util_windows.c b/new/util_windows.c new file mode 100644 index 0000000..c3f8971 --- /dev/null +++ b/new/util_windows.c @@ -0,0 +1,18 @@ +// 6 april 2015 +#include "ui_windows.h" + +#define MBTWC(str, wstr, bufsiz) MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, bufsiz) + +WCHAR *toUTF16(const char *str) +{ + WCHAR *wstr; + int n; + + n = MBTWC(str, NULL, 0); + if (n == 0) + logLastError("error figuring out number of characters to convert to in toUTF16()"); + wstr = (WCHAR *) uiAlloc(n * sizeof (WCHAR)); + if (MBTWC(str, wstr, n) != n) + logLastError("error converting from UTF-8 to UTF-16 in toUTF16()"); + return wstr; +} diff --git a/new/window_windows.c b/new/window_windows.c new file mode 100644 index 0000000..0452161 --- /dev/null +++ b/new/window_windows.c @@ -0,0 +1,120 @@ +// 6 april 2015 +#include "ui_windows.h" + +struct uiWindow { + HWND hwnd; + BOOL shownOnce; + int (*onClosing)(uiWindow *, void *); + void *onClosingData; +}; + +#define uiWindowClass L"uiWindowClass" + +static LRESULT CALLBACK uiWindowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + uiWindow *w; + CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam; + + w = (uiWindow *) GetWindowLongPtrW(hwnd, GWLP_USERDATA); + if (w == NULL) { + if (uMsg == WM_CREATE) + SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR) (cs->lpCreateParams)); + // fall through to DefWindowProc() anyway + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } + switch (uMsg) { + case WM_CLOSE: + if (!(*(w->onClosing))(w, w->onClosingData)) + return 0; + break; // fall through to DefWindowProcW() + } + return DefWindowProcW(hwnd, uMsg, wParam, lParam); +} + +ATOM registerWindowClass(HICON hDefaultIcon, HCURSOR hDefaultCursor) +{ + WNDCLASSW wc; + + ZeroMemory(&wc, sizeof (WNDCLASSW)); + wc.lpszClassName = uiWindowClass; + wc.lpfnWndProc = uiWindowWndProc; + wc.hInstance = hInstance; + wc.hIcon = hDefaultIcon; + wc.hCursor = hDefaultCursor; + wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); + return RegisterClassW(&wc); +} + +#define exstyle 0 +#define style WS_OVERLAPPEDWINDOW + +static int defaultOnClosing(uiWindow *w, void *data) +{ + return 1; +} + +uiWindow *uiNewWindow(char *title, int width, int height) +{ + uiWindow *w; + RECT adjust; + WCHAR *wtitle; + + w = (uiWindow *) uiAlloc(sizeof (uiWindow)); + w->onClosing = defaultOnClosing; + + adjust.left = 0; + adjust.top = 0; + adjust.right = width; + adjust.bottom = height; + if (AdjustWindowRectEx(&adjust, style, FALSE, exstyle) == 0) + logLastError("error getting real window coordinates in uiWindow()"); + + wtitle = toUTF16(title); + w->hwnd = CreateWindowExW(exstyle, + uiWindowClass, wtitle, + style, + CW_USEDEFAULT, CW_USEDEFAULT, + adjust.right - adjust.left, adjust.bottom - adjust.top, + NULL, NULL, hInstance, w); + if (w->hwnd == NULL) + logLastError("error creating window in uiWindow()"); + + uiFree(wtitle); + return w; +} + +void uiWindowDestroy(uiWindow *w) +{ + DestroyWindow(w->hwnd); + uiFree(w); +} + +uintptr_t uiWindowHandle(uiWindow *w) +{ + return (uintptr_t) (w->hwnd); +} + +// TODO titles + +void uiWindowShow(uiWindow *w) +{ + if (w->shownOnce) { + ShowWindow(w->hwnd, SW_SHOW); + return; + } + w->shownOnce = TRUE; + ShowWindow(w->hwnd, nCmdShow); + if (UpdateWindow(w->hwnd) == 0) + logLastError("error calling UpdateWindow() after showing uiWindow for the first time"); +} + +void uiWindowHide(uiWindow *w) +{ + ShowWindow(w->hwnd, SW_HIDE); +} + +void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data) +{ + w->onClosing = f; + w->onClosingData = data; +}