More WPF foundations/boilerplate work.
This commit is contained in:
parent
896beb036d
commit
f93d9a4c91
|
@ -8,10 +8,7 @@ HFONT hMessageFont;
|
|||
|
||||
HBRUSH hollowBrush;
|
||||
|
||||
struct uiInitError {
|
||||
char *msg;
|
||||
char failbuf[256];
|
||||
};
|
||||
// TODO this won't work if initAlloc() failed
|
||||
|
||||
#define initErrorFormat L"error %s: %s%s%s %I32u (0x%I32X)%s"
|
||||
#define initErrorArgs wmessage, sysmsg, beforele, label, value, value, afterle
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
// 4 december 2014
|
||||
#ifdef __cplusplus
|
||||
#error msbuild is being dumb and making this a C++ file
|
||||
#endif
|
||||
#include "unmanaged.h"
|
||||
#include <stdio.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
|
||||
|
||||
static HANDLE heap;
|
||||
|
||||
int initAlloc(void)
|
||||
{
|
||||
heap = HeapCreate(0, 0, 0);
|
||||
return heap != NULL;
|
||||
}
|
||||
|
||||
#define UINT8(p) ((uint8_t *) (p))
|
||||
#define PVOID(p) ((void *) (p))
|
||||
#define EXTRA (sizeof (const char **))
|
||||
#define DATA(p) PVOID(UINT8(p) + EXTRA)
|
||||
#define BASE(p) PVOID(UINT8(p) - EXTRA)
|
||||
#define CCHAR(p) ((const char **) (p))
|
||||
#define TYPE(p) CCHAR(UINT8(p))
|
||||
|
||||
void uninitAlloc(void)
|
||||
{
|
||||
BOOL hasEntry;
|
||||
PROCESS_HEAP_ENTRY phe;
|
||||
DWORD le;
|
||||
|
||||
hasEntry = FALSE;
|
||||
ZeroMemory(&phe, sizeof (PROCESS_HEAP_ENTRY));
|
||||
while (HeapWalk(heap, &phe) != 0) {
|
||||
// skip non-allocations
|
||||
if ((phe.wFlags & PROCESS_HEAP_ENTRY_BUSY) == 0)
|
||||
continue;
|
||||
if (!hasEntry) {
|
||||
fprintf(stderr, "[libui] leaked allocations:\n");
|
||||
hasEntry = TRUE;
|
||||
}
|
||||
fprintf(stderr, "[libui] %p %s\n", phe.lpData, *TYPE(phe.lpData));
|
||||
}
|
||||
le = GetLastError();
|
||||
SetLastError(le); // just in case
|
||||
if (le != ERROR_NO_MORE_ITEMS)
|
||||
logLastError("error walking heap in uninitAlloc()");
|
||||
if (hasEntry)
|
||||
complain("either you left something around or there's a bug in libui");
|
||||
if (HeapDestroy(heap) == 0)
|
||||
logLastError("error destroying heap in uninitAlloc()");
|
||||
}
|
||||
|
||||
void *uiAlloc(size_t size, const char *type)
|
||||
{
|
||||
void *out;
|
||||
|
||||
out = HeapAlloc(heap, HEAP_ZERO_MEMORY, EXTRA + size);
|
||||
if (out == NULL) {
|
||||
fprintf(stderr, "memory exhausted in uiAlloc()\n");
|
||||
abort();
|
||||
}
|
||||
*TYPE(out) = type;
|
||||
return DATA(out);
|
||||
}
|
||||
|
||||
void *uiRealloc(void *p, size_t size, const char *type)
|
||||
{
|
||||
void *out;
|
||||
|
||||
if (p == NULL)
|
||||
return uiAlloc(size, type);
|
||||
p = BASE(p);
|
||||
out = HeapReAlloc(heap, HEAP_ZERO_MEMORY, p, EXTRA + size);
|
||||
if (out == NULL) {
|
||||
fprintf(stderr, "memory exhausted in uiRealloc()\n");
|
||||
abort();
|
||||
}
|
||||
return DATA(out);
|
||||
}
|
||||
|
||||
void uiFree(void *p)
|
||||
{
|
||||
if (p == NULL)
|
||||
complain("attempt to uiFree(NULL); there's a bug somewhere");
|
||||
p = BASE(p);
|
||||
if (HeapFree(heap, 0, p) == 0)
|
||||
logLastError("error freeing memory in uiFree()");
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
// 25 february 2015
|
||||
#include "unmanaged.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
|
77
wpf/init.c
77
wpf/init.c
|
@ -2,11 +2,58 @@
|
|||
#ifdef __cplusplus
|
||||
#error msbuild is being dumb and making this a C++ file
|
||||
#endif
|
||||
#include "winapi.h"
|
||||
#include "../ui.h"
|
||||
// TODO to make sure wpfInit() is exported properly
|
||||
#include "wpf.h"
|
||||
#include "unmanaged.h"
|
||||
|
||||
// TODO this won't work if initAlloc() failed
|
||||
|
||||
#define initErrorFormat L"error %s: %s%s%s %I32u (0x%I32X)%s"
|
||||
#define initErrorArgs wmessage, sysmsg, beforele, label, value, value, afterle
|
||||
|
||||
static const char *initerr(const char *message, const WCHAR *label, DWORD value)
|
||||
{
|
||||
WCHAR *sysmsg;
|
||||
BOOL hassysmsg;
|
||||
WCHAR *beforele;
|
||||
WCHAR *afterle;
|
||||
int n;
|
||||
WCHAR *wmessage;
|
||||
WCHAR *wstr;
|
||||
const char *str;
|
||||
|
||||
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, value, 0, (LPWSTR) (&sysmsg), 0, NULL) != 0) {
|
||||
hassysmsg = TRUE;
|
||||
beforele = L" (";
|
||||
afterle = L")";
|
||||
} else {
|
||||
hassysmsg = FALSE;
|
||||
sysmsg = L"";
|
||||
beforele = L"";
|
||||
afterle = L"";
|
||||
}
|
||||
wmessage = toUTF16(message);
|
||||
n = _scwprintf(initErrorFormat, initErrorArgs);
|
||||
wstr = (WCHAR *) uiAlloc((n + 1) * sizeof (WCHAR), "WCHAR[]");
|
||||
snwprintf(wstr, n + 1, initErrorFormat, initErrorArgs);
|
||||
str = toUTF8(wstr);
|
||||
uiFree(wstr);
|
||||
if (hassysmsg)
|
||||
if (LocalFree(sysmsg) != NULL)
|
||||
logLastError("error freeing system message in loadLastError()");
|
||||
uiFree(wmessage);
|
||||
return str;
|
||||
}
|
||||
|
||||
static const char *loadLastError(const char *message)
|
||||
{
|
||||
return initerr(message, L"GetLastError() ==", GetLastError());
|
||||
}
|
||||
|
||||
static const char *loadHRESULT(const char *message, HRESULT hr)
|
||||
{
|
||||
return initerr(message, L"HRESULT", (DWORD) hr);
|
||||
}
|
||||
|
||||
// On the subject of CoInitialize(), or why this isn't in main.cpp:
|
||||
// If we don't set up the current thread otherwise, the first time .net tries to call out to unmanaged code, it will automatically set up a MTA for COM.
|
||||
// This is not what we want; we need a STA instead.
|
||||
// Since we're not in control of main(), we can't stick a [STAThread] on it, so we have to do it ourselves.
|
||||
|
@ -15,26 +62,36 @@
|
|||
// 2) To avoid mixing Windows API headers with .net
|
||||
// See also http://stackoverflow.com/questions/24348205/how-do-i-solve-this-com-issue-in-c
|
||||
|
||||
extern void initWPF(void);
|
||||
//extern void uninitWPF(void);
|
||||
uiInitOptions options;
|
||||
|
||||
void wpfInit(void)
|
||||
const char *uiInit(uiInitOptions *o)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
options = *o;
|
||||
|
||||
if (initAlloc() == 0)
|
||||
return loadLastError("error initializing memory allocations");
|
||||
|
||||
// TODO https://msdn.microsoft.com/en-us/library/5s8ee185%28v=vs.71%29.aspx use CoInitializeEx()?
|
||||
hr = CoInitialize(NULL);
|
||||
if (hr != S_OK && hr != S_FALSE)
|
||||
DebugBreak();
|
||||
return loadHRESULT("initializing COM", hr);
|
||||
|
||||
// now do the rest of initialization on the managed side
|
||||
initWPF();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*TODO
|
||||
void uiUninit(void)
|
||||
{
|
||||
uninitWPF();
|
||||
CoUninitialize();
|
||||
uninitAlloc();
|
||||
}
|
||||
|
||||
void uiFreeInitError(const char *err)
|
||||
{
|
||||
uiFree((void *) err);
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -70,13 +70,19 @@
|
|||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
|
||||
<!-- TODO readd headers? -->
|
||||
|
||||
<ClCompile Include="alloc.c">
|
||||
<CompileAsManaged>false</CompileAsManaged>
|
||||
</ClCompile>
|
||||
<ClCompile Include="debug.c">
|
||||
<CompileAsManaged>false</CompileAsManaged>
|
||||
</ClCompile>
|
||||
<ClCompile Include="init.c">
|
||||
<CompileAsManaged>false</CompileAsManaged>
|
||||
</ClCompile>
|
||||
|
||||
<ClCompile Include="winapi.h" />
|
||||
|
||||
<ClCompile Include="..\ui.h" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="text.cpp" />
|
||||
|
||||
<ClCompile Include="..\common\areaevents.c">
|
||||
<CompileAsManaged>false</CompileAsManaged>
|
||||
|
@ -99,8 +105,6 @@
|
|||
<ClCompile Include="..\common\types.c">
|
||||
<CompileAsManaged>false</CompileAsManaged>
|
||||
</ClCompile>
|
||||
|
||||
<ClCompile Include="..\common\uipriv.h" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
// 25 november 2015
|
||||
#include "uipriv_wpf.hpp"
|
||||
|
||||
static gcroot<Application ^> *app;
|
||||
|
||||
void initWPF(void)
|
||||
{
|
||||
app = new gcroot<Application ^>();
|
||||
*app = gcnew Application();
|
||||
}
|
||||
|
||||
void uninitWPF(void)
|
||||
{
|
||||
delete app;
|
||||
}
|
||||
|
||||
void uiMain(void)
|
||||
{
|
||||
(*app)->Run();
|
||||
}
|
||||
|
||||
void uiQuit(void)
|
||||
{
|
||||
(*app)->Shutdown();
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// 25 november 2015
|
||||
#include "uipriv_wpf.hpp"
|
||||
#include <string.h>
|
||||
|
||||
using namespace System::Text;
|
||||
using namespace System::Runtime::InteropServices;
|
||||
|
||||
// TODO export?
|
||||
String ^fromUTF8(const char *str)
|
||||
{
|
||||
array<Byte> ^bytes;
|
||||
|
||||
// TODO avoid the cast
|
||||
Marshal::Copy(IntPtr((char *) str), bytes, 0, strlen(str));
|
||||
return Encoding::UTF8->GetString(bytes);
|
||||
}
|
||||
|
||||
// see http://stackoverflow.com/questions/27526093/convert-systemstring-to-stdstring-in-utf8-which-later-converted-to-char-as
|
||||
char *uiWindowsCLRStringToText(gcroot<System::String ^> str)
|
||||
{
|
||||
array<Byte> ^bytes;
|
||||
char *cstr;
|
||||
|
||||
bytes = Encoding::UTF8->GetBytes(str);
|
||||
cstr = (char *) uiAlloc((bytes->Length + 1) * sizeof (char), "char[]");
|
||||
Marshal::Copy(bytes, 0, IntPtr(cstr), bytes->Length);
|
||||
cstr[bytes->Length] = '\0';
|
||||
return cstr;
|
||||
}
|
||||
|
||||
void uiFreeText(char *c)
|
||||
{
|
||||
uiFree(c);
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
// 7 april 2015
|
||||
|
||||
/*
|
||||
This file assumes that you have included <vccrt.h> and "ui.h" beforehand, as well as #using <System.dll> (but not necessarily beforehand). It provides API-specific functions for interfacing with foreign controls in Windows.
|
||||
*/
|
||||
|
||||
#ifndef __LIBUI_UI_WINDOWS_H__
|
||||
#define __LIBUI_UI_WINDOWS_H__
|
||||
|
||||
#ifndef __cplusplus_cli
|
||||
#error Sorry; ui_wpf.hpp can currently only be used from C++/CLI code.
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
|
||||
typedef struct uiWindowsSizing uiWindowsSizing;
|
||||
|
||||
typedef struct uiWindowsControl uiWindowsControl;
|
||||
struct uiWindowsControl {
|
||||
uiControl c;
|
||||
};
|
||||
_UI_EXTERN uintmax_t uiWindowsControlType(void);
|
||||
#define uiWindowsControl(this) ((uiWindowsControl *) uiIsA((this), uiWindowsControlType(), 1))
|
||||
|
||||
// TODO document
|
||||
#define uiWindowsDefineControlWithOnDestroy(type, typefn, handle, onDestroy) \
|
||||
static uintmax_t _ ## type ## Type = 0; \
|
||||
uintmax_t typefn(void) \
|
||||
{ \
|
||||
if (_ ## type ## Type == 0) \
|
||||
_ ## type ## Type = uiRegisterType(#type, uiWindowsControlType(), sizeof (type)); \
|
||||
return _ ## type ## Type; \
|
||||
} \
|
||||
static void _ ## type ## CommitDestroy(uiControl *c) \
|
||||
{ \
|
||||
type *hthis = type(c); \
|
||||
onDestroy; \
|
||||
delete hthis->handle; \
|
||||
} \
|
||||
static uintptr_t _ ## type ## Handle(uiControl *c) \
|
||||
{ \
|
||||
return (uintptr_t) (type(c)->handle); \
|
||||
} \
|
||||
static void _ ## type ## ContainerUpdateState(uiControl *c) \
|
||||
{ \
|
||||
/* do nothing */ \
|
||||
}
|
||||
|
||||
#define uiWindowsDefineControl(type, typefn, handle) \
|
||||
uiWindowsDefineControlWithOnDestroy(type, typefn, handle, (void) hthis;)
|
||||
|
||||
#define uiWindowsFinishNewControl(variable, type) \
|
||||
uiControl(variable)->CommitDestroy = _ ## type ## CommitDestroy; \
|
||||
uiControl(variable)->Handle = _ ## type ## Handle; \
|
||||
uiControl(variable)->ContainerUpdateState = _ ## type ## ContainerUpdateState; \
|
||||
uiWindowsFinishControl(uiControl(variable));
|
||||
|
||||
// This is a function used to set up a control.
|
||||
// Don't call it directly; use uiWindowsFinishNewControl() instead.
|
||||
_UI_EXTERN void uiWindowsFinishControl(uiControl *c);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN char *uiWindowsCLRStringToText(gcroot<System::String ^> str);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,17 @@
|
|||
// 25 november 2015
|
||||
#include <vcclr.h>
|
||||
#include "../ui.h"
|
||||
#include "ui_wpf.hpp"
|
||||
#include "../common/uipriv.h"
|
||||
#include "unmanaged.h"
|
||||
|
||||
#using <System.dll>
|
||||
#using <WindowsBase.dll>
|
||||
#using <PresentationCore.dll>
|
||||
#using <PresentationFramework.dll>
|
||||
using namespace System;
|
||||
using namespace System::ComponentModel;
|
||||
using namespace System::Windows;
|
||||
|
||||
// text.cpp
|
||||
String ^fromUTF8(const char *);
|
|
@ -0,0 +1,23 @@
|
|||
// 25 november 2015
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#else
|
||||
#include "winapi.h"
|
||||
#include "../ui.h"
|
||||
#include "../common/uipriv.h"
|
||||
#endif
|
||||
|
||||
// main.cpp
|
||||
extern void initWPF(void);
|
||||
extern void uninitWPF(void);
|
||||
|
||||
// alloc.c
|
||||
extern int initAlloc(void);
|
||||
extern void uninitAlloc(void);
|
||||
|
||||
// init.c
|
||||
extern uiInitOptions options;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
18
wpf/wpf.cpp
18
wpf/wpf.cpp
|
@ -55,21 +55,3 @@ void wpfWindowOnClosing(wpfWindow *w, void (*f)(wpfWindow *w, void *data), void
|
|||
w->onClosing = f;
|
||||
w->onClosingData = data;
|
||||
}
|
||||
|
||||
static gcroot<Application ^> app;
|
||||
|
||||
// wpfInit() is in sta.c; see that or details.
|
||||
extern "C" void initWPF(void)
|
||||
{
|
||||
app = gcnew Application();
|
||||
}
|
||||
|
||||
void wpfRun(void)
|
||||
{
|
||||
app->Run();
|
||||
}
|
||||
|
||||
void wpfQuit(void)
|
||||
{
|
||||
app->Shutdown();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue