summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPietro Gagliardi <[email protected]>2015-04-06 17:41:33 -0400
committerPietro Gagliardi <[email protected]>2015-04-06 17:41:33 -0400
commit9001ca34f73ad861c049f367e2ef8477d19e7432 (patch)
tree464ee1262b8a8f7d11fcd6c35ae7c9d1226fae17
parentd1be6e3ce158384b62ffab3bb439683a2018099d (diff)
Implemented what we have so far, but on Windows.
-rw-r--r--new/alloc_windows.c39
-rw-r--r--new/init_unix.c20
-rw-r--r--new/init_windows.c87
-rw-r--r--new/main_unix.c2
-rw-r--r--new/main_windows.c56
-rw-r--r--new/test.c2
-rw-r--r--new/ui_windows.h55
-rw-r--r--new/util_windows.c18
-rw-r--r--new/window_windows.c120
9 files changed, 387 insertions, 12 deletions
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 <stdio.h>
-// #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 <windows.h>
+#include <commctrl.h>
+#include <stdint.h>
+#include <uxtheme.h>
+#include <string.h>
+#include <wchar.h>
+#include <windowsx.h>
+#include <vsstyle.h>
+#include <vssym32.h>
+#include <stdarg.h>
+#include <oleacc.h>
+#include <stdio.h>
+#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;
+}