diff --git a/new/ui_windows.h b/new/ui_windows.h new file mode 100644 index 00000000..8bd8c8c4 --- /dev/null +++ b/new/ui_windows.h @@ -0,0 +1,58 @@ +// 7 april 2015 + +/* +This file assumes that you have included and "ui.h" beforehand. It provides API-specific functions for interfacing with foreign controls in Windows. +*/ + +#ifndef __UI_UI_WINDOWS_H__ +#define __UI_UI_WINDOWS_H__ + +// uiWindowsNewControl() initializes the given uiControl with the given Windows API control inside. +// You will need to provide the preferredSize() method yourself. +typedef struct uiWindowsNewControlParams uiWindowsNewControlParams; +struct uiWindowsNewControlParams { + // These match the CreateWindowExW() function. + DWORD dwExStyle; + LPCWSTR lpClassName; + LPCWSTR lpWindowName; + DWORD dwStyle; // WS_CHILD and WS_VISIBLE are automatically applied. + HINSTANCE hInstance; + + // Set this to non-FALSE to use the standard control font used by other ui controls. + BOOL useStandardControlFont; + + // These are called when the control sends a WM_COMMAND or WM_NOTIFY (respectively) to its parent. + // ui redirects the message back and calls these functions. + // Store the result in *lResult and return any non-FALSE value (such as TRUE) to return the given result; return FALSE to pass the notification up to your window procedure. + // Note that these are only issued if they come from the uiControl itself; notifications from children of the uiControl (such as a header control) will be received normally. + BOOL (*onWM_COMMAND)(uiControl *c, WORD code, LRESULT *lResult); + // TODO set idFrom to 0? + BOOL (*onWM_NOTIFY)(uiControl *c, NMHDR *nm, LRESULT *lResult); + + // This is called when the widget is ready to be destroyed. + void (*onDestroy)(void *data); + void *onDestroyData; +}; +void uiWindowsNewControl(uiControl *c, uiWindowsNewControlParams *p); + +// This contains the Windows-specific parts of the uiSizing structure. +// baseX and baseY are the dialog base units. +// internalLeading is the standard control font's internal leading; labels in uiForms use this for correct Y positioning. +struct uiSizingSys { + int baseX; + int baseY; + LONG internalLeading; +}; +// Use these in your preferredSize() implementation with baseX and baseY. +#define uiDlgUnitsToX(dlg, baseX) MulDiv((dlg), baseX, 4) +#define uiDlgUnitsToY(dlg, baseY) MulDiv((dlg), baseY, 8) + +// and use this if you need the text of the window width +extern intmax_t uiWindowsWindowTextWidth(HWND hwnd); + +// these functions get and set the window text for such a uiControl +// the value returned should be freed with uiFreeText() +extern char *uiWindowsControlText(uiControl *); +extern void uiWindowsControlSetText(uiControl *, const char *); + +#endif diff --git a/new/windows/GNUmakeinc.mk b/new/windows/GNUmakeinc.mk index 44893a53..0b42fe0c 100644 --- a/new/windows/GNUmakeinc.mk +++ b/new/windows/GNUmakeinc.mk @@ -2,6 +2,8 @@ osCFILES = \ windows/alloc.c \ + windows/comctl32.c \ + windows/debug.c \ windows/init.c \ windows/main.c \ windows/oscontainer.c \ diff --git a/new/windows/comctl32.c b/new/windows/comctl32.c new file mode 100644 index 00000000..93b3a271 --- /dev/null +++ b/new/windows/comctl32.c @@ -0,0 +1,105 @@ +// 17 july 2014 +#include "uipriv_windows.h" + +static ULONG_PTR comctlManifestCookie; +static HMODULE comctl32; + +// these are listed as WINAPI in both Microsoft's and MinGW's headers, but not on MSDN for some reason +BOOL (*WINAPI fv_SetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR); +BOOL (*WINAPI fv_RemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR); +LRESULT (*WINAPI fv_DefSubclassProc)(HWND, UINT, WPARAM, LPARAM); + +#define wantedICCClasses ( \ + ICC_STANDARD_CLASSES | /* user32.dll controls */ \ + ICC_PROGRESS_CLASS | /* progress bars */ \ + ICC_TAB_CLASSES | /* tabs */ \ + ICC_LISTVIEW_CLASSES | /* table headers */ \ + ICC_UPDOWN_CLASS | /* spinboxes */ \ + 0) + +// note that this is an 8-bit character string we're writing; see the encoding clause +static const char manifest[] = "\n\n\nYour application description here.\n\n \n \n \n\n\n"; + +/* +Windows requires a manifest file to enable Common Controls version 6. +The only way to not require an external manifest is to synthesize the manifest ourselves. +We can use the activation context API to load it at runtime. +References: +- http://stackoverflow.com/questions/4308503/how-to-enable-visual-styles-without-a-manifest +- http://support.microsoft.com/kb/830033 +Because neither Go nor MinGW have ways to compile in resources like this (as far as I know), we have to do the work ourselves. +*/ +const char *initCommonControls(void) +{ + WCHAR temppath[MAX_PATH + 1]; + WCHAR filename[MAX_PATH + 1]; + HANDLE file; + DWORD nExpected, nGot; + ACTCTX actctx; + HANDLE ac; + INITCOMMONCONTROLSEX icc; + FARPROC f; + // this is listed as WINAPI in both Microsoft's and MinGW's headers, but not on MSDN for some reason + BOOL (*WINAPI ficc)(const LPINITCOMMONCONTROLSEX); + + if (GetTempPathW(MAX_PATH + 1, temppath) == 0) + return "getting temporary path for writing manifest file in initCommonControls()"; + if (GetTempFileNameW(temppath, L"manifest", 0, filename) == 0) + return "getting temporary filename for writing manifest file in initCommonControls()"; + file = CreateFileW(filename, GENERIC_WRITE, + 0, // don't share while writing + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (file == NULL) + return "creating manifest file in initCommonControls()"; + nExpected = (sizeof manifest / sizeof manifest[0]) - 1; // - 1 to omit the terminating null character) + SetLastError(0); // catch errorless short writes + if (WriteFile(file, manifest, nExpected, &nGot, NULL) == 0) + return "writing manifest file in initCommonControls()"; + if (nGot != nExpected) { + DWORD lasterr; + + lasterr = GetLastError(); + if (lasterr == 0) + return "writing entire manifest file (short write) without error code in initCommonControls()"; + return "writing entire manifest file (short write) in initCommonControls()"; + } + if (CloseHandle(file) == 0) + return "closing manifest file (this IS an error here because not doing so will prevent Windows from being able to use the manifest file in an activation context) in initCommonControls()"; + + ZeroMemory(&actctx, sizeof (ACTCTX)); + actctx.cbSize = sizeof (ACTCTX); + actctx.dwFlags = ACTCTX_FLAG_SET_PROCESS_DEFAULT; + actctx.lpSource = filename; + ac = CreateActCtx(&actctx); + if (ac == INVALID_HANDLE_VALUE) + return "creating activation context for synthesized manifest file in initCommonControls()"; + if (ActivateActCtx(ac, &comctlManifestCookie) == FALSE) + return "activating activation context for synthesized manifest file in initCommonControls()"; + + ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX)); + icc.dwSize = sizeof (INITCOMMONCONTROLSEX); + icc.dwICC = wantedICCClasses; + + comctl32 = LoadLibraryW(L"comctl32.dll"); + if (comctl32 == NULL) + return "loading comctl32.dll in initCommonControls()"; + + // GetProcAddress() only takes a multibyte string +#define LOAD(fn) f = GetProcAddress(comctl32, fn); \ + if (f == NULL) \ + return "loading " fn "() in initCommonControls()"; + + LOAD("InitCommonControlsEx"); + ficc = (BOOL (*WINAPI)(const LPINITCOMMONCONTROLSEX)) f; + LOAD("SetWindowSubclass"); + fv_SetWindowSubclass = (BOOL (*WINAPI)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR)) f; + LOAD("RemoveWindowSubclass"); + fv_RemoveWindowSubclass = (BOOL (*WINAPI)(HWND, SUBCLASSPROC, UINT_PTR)) f; + LOAD("DefSubclassProc"); + fv_DefSubclassProc = (LRESULT (*WINAPI)(HWND, UINT, WPARAM, LPARAM)) f; + + if ((*ficc)(&icc) == FALSE) + return "initializing Common Controls (comctl32.dll) in initCommonControls()"; + + return NULL; +} diff --git a/new/windows/debug.c b/new/windows/debug.c new file mode 100644 index 00000000..bc84b639 --- /dev/null +++ b/new/windows/debug.c @@ -0,0 +1,111 @@ +// 25 february 2015 +#include "uipriv_windows.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/new/windows/oscontainer.c b/new/windows/oscontainer.c index 8bbdd150..a7001764 100644 --- a/new/windows/oscontainer.c +++ b/new/windows/oscontainer.c @@ -25,7 +25,7 @@ static void paintControlBackground(HWND hwnd, HDC dc) parent = hwnd; for (;;) { - parent = GetOSContainer(parent); + parent = GetParent(parent); if (parent == NULL) logLastError("error getting parent control of control in paintControlBackground()"); // wine sends these messages early, yay... diff --git a/new/windows/uipriv_windows.h b/new/windows/uipriv_windows.h index 3c913c89..a2b01a6d 100644 --- a/new/windows/uipriv_windows.h +++ b/new/windows/uipriv_windows.h @@ -1,4 +1,4 @@ -// 24 april 2015 +// 6 january 2015 #define UNICODE #define _UNICODE #define STRICT @@ -37,3 +37,42 @@ enum { msgUpdateChild, // fake because Windows seems to SWP_NOSIZE MoveWindow()s and SetWindowPos()s that don't change the window size (even if SWP_NOSIZE isn't specified) msgCanDestroyNow, }; + +#define HWND(c) ((HWND) uiControlHandle(uiControl(c))) +#define uiOSContainerHWND(p) ((HWND) uiOSContainerHandle(p)) + +// debug.c +extern HRESULT logLastError(const char *); +extern HRESULT logHRESULT(const char *, HRESULT); +extern HRESULT logMemoryExhausted(const char *); + +// init.c +extern HINSTANCE hInstance; +extern int nCmdShow; +extern HFONT hMessageFont; +extern HBRUSH hollowBrush; + +// util.c +extern int windowClassOf(HWND, ...); + +// text.c +extern WCHAR *toUTF16(const char *); +extern char *toUTF8(const WCHAR *); +extern WCHAR *windowText(HWND); + +// comctl32.c +extern BOOL (*WINAPI fv_SetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR); +extern BOOL (*WINAPI fv_RemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR); +extern LRESULT (*WINAPI fv_DefSubclassProc)(HWND, UINT, WPARAM, LPARAM); +extern const char *initCommonControls(void); + +// window.c +extern ATOM registerWindowClass(HICON, HCURSOR); + +// parent.c +extern HWND initialOSContainer; +extern const char *initOSContainer(HICON, HCURSOR); + +// menu.c +extern HMENU makeMenubar(void); +extern const uiMenuItem *menuIDToItem(UINT_PTR); diff --git a/new/windows/util.c b/new/windows/util.c new file mode 100644 index 00000000..63529e59 --- /dev/null +++ b/new/windows/util.c @@ -0,0 +1,83 @@ +// 6 april 2015 +#include "uipriv_windows.h" + +intmax_t uiWindowsWindowTextWidth(HWND hwnd) +{ + LRESULT len; + WCHAR *text; + HDC dc; + HFONT prevfont; + SIZE size; + + size.cx = 0; + size.cy = 0; + + // first we need the window text + len = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0); + if (len == 0) // no text; nothing to do + return 0; + text = (WCHAR *) uiAlloc((len + 1) * sizeof (WCHAR), "WCHAR[]"); + // note the comparison: the size includes the null terminator, but the return does not + if (GetWindowText(hwnd, text, len + 1) != len) + logLastError("error getting window text in uiWindowsWindowTextWidth()"); + + // now we can do the calculations + dc = GetDC(hwnd); + if (dc == NULL) + logLastError("error getting DC in uiWindowsWindowTextWidth()"); + prevfont = (HFONT) SelectObject(dc, hMessageFont); + if (prevfont == NULL) + logLastError("error loading control font into device context in uiWindowsWindowTextWidth()"); + if (GetTextExtentPoint32W(dc, text, len, &size) == 0) + logLastError("error getting text extent point in uiWindowsWindowTextWidth()"); + if (SelectObject(dc, prevfont) != hMessageFont) + logLastError("error restoring previous font into device context in uiWindowsWindowTextWidth()"); + if (ReleaseDC(hwnd, dc) == 0) + logLastError("error releasing DC in uiWindowsWindowTextWidth()"); + uiFree(text); + + return size.cx; +} + +// this is a helper function that takes the logic of determining window classes and puts it all in one place +// there are a number of places where we need to know what window class an arbitrary handle has +// theoretically we could use the class atom to avoid a _wcsicmp() +// however, raymond chen advises against this - http://blogs.msdn.com/b/oldnewthing/archive/2004/10/11/240744.aspx (and we're not in control of the Tab class, before you say anything) +// usage: windowClassOf(hwnd, L"class 1", L"class 2", ..., NULL) +int windowClassOf(HWND hwnd, ...) +{ +// MSDN says 256 is the maximum length of a class name; add a few characters just to be safe (because it doesn't say whether this includes the terminating null character) +#define maxClassName 260 + WCHAR classname[maxClassName + 1]; + va_list ap; + WCHAR *curname; + int i; + + if (GetClassNameW(hwnd, classname, maxClassName) == 0) + logLastError("error getting name of window class in windowClassOf()"); + va_start(ap, hwnd); + i = 0; + for (;;) { + curname = va_arg(ap, WCHAR *); + if (curname == NULL) + break; + if (_wcsicmp(classname, curname) == 0) { + va_end(ap); + return i; + } + i++; + } + // no match + va_end(ap); + return -1; +} + +void complain(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + abort(); +} diff --git a/new/windows/window.c b/new/windows/window.c new file mode 100644 index 00000000..a12f2b5e --- /dev/null +++ b/new/windows/window.c @@ -0,0 +1,251 @@ +// 6 april 2015 +#include "uipriv_windows.h" + +struct window { + uiWindow w; + HWND hwnd; + uiOSContainer *content; + BOOL shownOnce; + int (*onClosing)(uiWindow *, void *); + void *onClosingData; + int margined; + BOOL canDestroy; +}; + +#define uiWindowClass L"uiWindowClass" + +static LRESULT CALLBACK uiWindowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + struct window *w; + CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam; + WINDOWPOS *wp = (WINDOWPOS *) lParam; + RECT r; + HWND contenthwnd; + const uiMenuItem *item; + + w = (struct window *) 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_COMMAND: + // not a menu + if (lParam != 0) + break; + if (HIWORD(wParam) != 0) + break; +/*TODO item = menuIDToItem(LOWORD(wParam)); + printf("%d", item->Type); + if (item->Type == uiMenuItemTypeCommand) + printf(" %s", item->Name); + printf("\n"); +*/ return 0; + case WM_WINDOWPOSCHANGED: + if ((wp->flags & SWP_NOSIZE) != 0) + break; + // fall through + case msgUpdateChild: + if (GetClientRect(w->hwnd, &r) == 0) + logLastError("error getting window client rect for resize in uiWindowWndProc()"); + contenthwnd = uiOSContainerHWND(w->content); + if (MoveWindow(contenthwnd, r.left, r.top, r.right - r.left, r.bottom - r.top, TRUE) == 0) + logLastError("error resizing window content parent in uiWindowWndProc()"); + return 0; + case WM_CLOSE: + if (!(*(w->onClosing))(uiWindow(w), w->onClosingData)) + uiWindowDestroy(uiWindow(w)); + return 0; // we destroyed it already + case WM_DESTROY: + if (!w->canDestroy) + complain("attempt to destroy uiWindow at %p before uiWindowDestroy()", w); + uiFree(w); + 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; +} + +static void windowDestroy(uiWindow *ww) +{ + struct window *w = (struct window *) ww; + + // first destroy the content + uiOSContainerDestroy(w->content); + // then mark that we're ready to destroy + w->canDestroy = TRUE; + // and finally destroy + // TODO check for errors + DestroyWindow(w->hwnd); + // no need to explicitly destroy the menubar, if any; that's done automatically during window destruction +} + +static uintptr_t windowHandle(uiWindow *ww) +{ + struct window *w = (struct window *) ww; + + return (uintptr_t) (w->hwnd); +} + +static char *windowTitle(uiWindow *ww) +{ + struct window *w = (struct window *) ww; + WCHAR *wtext; + char *text; + + wtext = windowText(w->hwnd); + text = toUTF8(wtext); + uiFree(wtext); + return text; +} + +static void windowSetTitle(uiWindow *ww, const char *text) +{ + struct window *w = (struct window *) ww; + WCHAR *wtext; + + wtext = toUTF16(text); + if (SetWindowTextW(w->hwnd, wtext) == 0) + logLastError("error setting window title in uiWindowSetTitle()"); + uiFree(wtext); +} + +static void windowShow(uiWindow *ww) +{ + struct window *w = (struct window *) ww; + + 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"); +} + +static void windowHide(uiWindow *ww) +{ + struct window *w = (struct window *) ww; + + ShowWindow(w->hwnd, SW_HIDE); +} + +static void windowOnClosing(uiWindow *ww, int (*f)(uiWindow *, void *), void *data) +{ + struct window *w = (struct window *) ww; + + w->onClosing = f; + w->onClosingData = data; +} + +static void windowSetChild(uiWindow *ww, uiControl *c) +{ + struct window *w = (struct window *) ww; + + uiOSContainerSetMainControl(w->content, c); + // don't call uiOSContainerUpdate(); instead, synthesize a resize + // otherwise, we'll have a 0x0 content area at first + SendMessageW(w->hwnd, msgUpdateChild, 0, 0); +} + +static int windowMargined(uiWindow *ww) +{ + struct window *w = (struct window *) ww; + + return w->margined; +} + +// from https://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing +#define windowMargin 7 + +static void windowSetMargined(uiWindow *ww, int margined) +{ + struct window *w = (struct window *) ww; + + w->margined = margined; + if (w->margined) + uiOSContainerSetMargins(w->content, windowMargin, windowMargin, windowMargin, windowMargin); + else + uiOSContainerSetMargins(w->content, 0, 0, 0, 0); + uiOSContainerUpdate(w->content); +} + +uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) +{ + struct window *w; + RECT adjust; + WCHAR *wtitle; + BOOL hasMenubarBOOL; + HMENU hmenu; + + w = uiNew(struct window); + w->onClosing = defaultOnClosing; + + hasMenubarBOOL = FALSE; + if (hasMenubar) + hasMenubarBOOL = TRUE; + + adjust.left = 0; + adjust.top = 0; + adjust.right = width; + adjust.bottom = height; + // TODO does not handle menu wrapping; see http://blogs.msdn.com/b/oldnewthing/archive/2003/09/11/54885.aspx + if (AdjustWindowRectEx(&adjust, style, hasMenubarBOOL, 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); + + w->content = uiNewOSContainer((uintptr_t) (w->hwnd)); + +/*TODO if (hasMenubar) { + hmenu = makeMenubar(); + if (SetMenu(w->hwnd, hmenu) == 0) + logLastError("error giving menu to window in uiNewWindow()"); + }*/ + + uiWindow(w)->Destroy = windowDestroy; + uiWindow(w)->Handle = windowHandle; + uiWindow(w)->Title = windowTitle; + uiWindow(w)->SetTitle = windowSetTitle; + uiWindow(w)->Show = windowShow; + uiWindow(w)->Hide = windowHide; + uiWindow(w)->OnClosing = windowOnClosing; + uiWindow(w)->SetChild = windowSetChild; + uiWindow(w)->Margined = windowMargined; + uiWindow(w)->SetMargined = windowSetMargined; + + return uiWindow(w); +}