More Windows stuff. Ugh this is what I was trying to AVOID but oh well
This commit is contained in:
parent
3ea84f9bf5
commit
c42b9399cd
|
@ -0,0 +1,58 @@
|
||||||
|
// 7 april 2015
|
||||||
|
|
||||||
|
/*
|
||||||
|
This file assumes that you have included <windows.h> 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
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
osCFILES = \
|
osCFILES = \
|
||||||
windows/alloc.c \
|
windows/alloc.c \
|
||||||
|
windows/comctl32.c \
|
||||||
|
windows/debug.c \
|
||||||
windows/init.c \
|
windows/init.c \
|
||||||
windows/main.c \
|
windows/main.c \
|
||||||
windows/oscontainer.c \
|
windows/oscontainer.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[] = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n<assemblyIdentity\n version=\"1.0.0.0\"\n processorArchitecture=\"*\"\n name=\"CompanyName.ProductName.YourApplication\"\n type=\"win32\"\n/>\n<description>Your application description here.</description>\n<dependency>\n <dependentAssembly>\n <assemblyIdentity\n type=\"win32\"\n name=\"Microsoft.Windows.Common-Controls\"\n version=\"6.0.0.0\"\n processorArchitecture=\"*\"\n publicKeyToken=\"6595b64144ccf1df\"\n language=\"*\"\n />\n </dependentAssembly>\n</dependency>\n</assembly>\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;
|
||||||
|
}
|
|
@ -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 <stdio.h>
|
||||||
|
|
||||||
|
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
|
|
@ -25,7 +25,7 @@ static void paintControlBackground(HWND hwnd, HDC dc)
|
||||||
|
|
||||||
parent = hwnd;
|
parent = hwnd;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
parent = GetOSContainer(parent);
|
parent = GetParent(parent);
|
||||||
if (parent == NULL)
|
if (parent == NULL)
|
||||||
logLastError("error getting parent control of control in paintControlBackground()");
|
logLastError("error getting parent control of control in paintControlBackground()");
|
||||||
// wine sends these messages early, yay...
|
// wine sends these messages early, yay...
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// 24 april 2015
|
// 6 january 2015
|
||||||
#define UNICODE
|
#define UNICODE
|
||||||
#define _UNICODE
|
#define _UNICODE
|
||||||
#define STRICT
|
#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)
|
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,
|
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);
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
Loading…
Reference in New Issue