Implemented uiMain() and friends on Windows. As a result, started the new HRESULT wrapper stuff.

This commit is contained in:
Pietro Gagliardi 2019-04-28 16:23:25 -04:00
parent 49bde22f81
commit 812c559b11
9 changed files with 226 additions and 98 deletions

View File

@ -4,6 +4,32 @@
HINSTANCE uipriv_hInstance = NULL;
int uipriv_nCmdShow;
#ifndef uiStatic
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, LPVOID lpvReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH)
uipriv_hInstance = hInstance;
return TRUE;
}
static inline void setHInstance(void)
{
// do nothing; set by DllMain() above
}
#else
// see https://devblogs.microsoft.com/oldnewthing/20041025-00/?p=37483
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
static inline void setHInstance(void)
{
uipriv_hInstance = (HINSTANCE) (&__ImageBase);
}
#endif
//HFONT hMessageFont;
#define wantedICCClasses ( \
@ -16,15 +42,18 @@ int uipriv_nCmdShow;
ICC_DATE_CLASSES | /* date/time picker */ \
0)
// see https://devblogs.microsoft.com/oldnewthing/20041025-00/?p=37483
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define errGetDefaultIconFailed "failed to load default icon"
#define errGetDefaultCursorFailed "failed to load default cursor"
#define errInitUtilWindowFailed "failed to initialize the utility window"
#define errICCFailed "InitCommonControlsEx() failed"
#define errICCFailedNoLastError "InitCommonControlsEx() failed, but didn't specify why. This usually means you forgot the Common Controls v6 manifest; refer to the libui documentation for instructions."
#define errCoInitializeFailed "CoInitialize() failed"
#define errHRESULTInitErrorsSuffix ": 0x00000000"
static const char *initErrors[] = {
errGetDefaultIconFailed errHRESULTInitErrorsSuffix
errGetDefaultCursorFailed errHRESULTInitErrorsSuffix
errInitUtilWindowFailed errHRESULTInitErrorsSuffix
errICCFailed errHRESULTInitErrorsSuffix,
errICCFailedNoLastError,
errCoInitializeFailed errHRESULTInitErrorsSuffix,
@ -35,19 +64,31 @@ static const char *initErrors[] = {
int uiInit(void *options, uiInitError *err)
{
STARTUPINFOW si;
HICON hDefaultIcon;
HCURSOR hDefaultCursor;
INITCOMMONCONTROLSEX icc;
HRESULT hr;
if (!uiprivInitCheckParams(options, err, initErrors))
return 0;
if (uipriv_hInstance == NULL)
uipriv_hInstance = (HINSTANCE) (&__ImageBase);
setHInstance();
uipriv_nCmdShow = SW_SHOWDEFAULT;
GetStartupInfoW(&si);
if ((si.dwFlags & STARTF_USESHOWWINDOW) != 0)
uipriv_nCmdShow = si.wShowWindow;
hr = uiprivHrLoadIconW(NULL, IDI_APPLICATION, &hDefaultIcon);
if (hr != S_OK)
return uiprivInitReturnHRESULT(err, errLoadDefaultIconFailed, hr);
hr = uiprivHrLoadCursorW(NULL, IDC_ARROW, &hDefaultCursor);
if (hr != S_OK)
return uiprivInitReturnHRESULT(err, errLoadDefaultCursorFailed, hr);
hr = uiprivInitUtilWindow(hDefaultIcon, hDefaultCursor);
if (hr != S_OK)
return uiprivInitReturnHRESULT(err, errInitUtilWindowFailed, hr);
ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX));
icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
icc.dwICC = wantedICCClasses;
@ -57,7 +98,8 @@ int uiInit(void *options, uiInitError *err)
lasterr = GetLastError();
if (lasterr == 0)
return uiprivInitReturnError(err, errICCFailedNoLastError);
return uiprivInitReturnHRESULT(err, errICCFailed, HRESULT_FROM_WIN32(lasterr));
hr = HRESULT_FROM_WIN32(lasterr);
return uiprivInitReturnHRESULT(err, errICCFailed, hr);
}
/* hr = CoInitialize(NULL);
@ -70,13 +112,37 @@ int uiInit(void *options, uiInitError *err)
return 1;
}
#ifndef uiStatic
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, LPVOID lpvReserved)
void uiMain(void)
{
if (fdwReason == DLL_PROCESS_ATTACH)
uipriv_hInstance = hInstance;
return TRUE;
MSG msg;
BOOL ret;
HRESULT hr;
for (;;) {
hr = uiprivHrGetMessageW(&msg, NULL, 0, 0, &ret);
if (hr != S_OK) {
// TODO log error
return;
}
if (ret == 0) // WM_QUIT
return;
// TODO IsDialogMessage()
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
#endif
void uiQuit(void)
{
PostQuitMessage(0);
}
void uiQueueMain(void (*f)(void *data), void *data)
{
HRESULT hr;
hr = uiprivHrPostMessageW(uiprivUtilWindow, uiprivUtilWindowMsgQueueMain, (WPARAM) f, (LPARAM) data);
if (hr != S_OK) {
// TODO handle error
}
}

View File

@ -3,7 +3,9 @@
windows = import('windows')
libui_sources += [
'windows/init.cpp',
'windows/main.cpp',
'windows/utilwin.cpp',
'windows/winhresult.cpp',
]
# resources.rc only contains the libui manifest.

View File

@ -1,11 +1,19 @@
// 21 april 2016
#include "winapi.hpp"
#include "winhresult.hpp"
#include "../ui.h"
#include "../ui_windows.h"
#include "../common/uipriv.h"
//TODO#include "compilerver.hpp"
// init.cpp
// main.cpp
extern HINSTANCE uipriv_hInstance;
extern int uipriv_nCmdShow;
//TODOextern HFONT hMessageFont;
// utilwin.cpp
enum {
uiprivUtilWindowMsgQueueMain = WM_USER + 40,
};
extern HWND uiprivUtilWindow;
extern HRESULT uiprivInitUtilWindow(HICON hDefaultIcon, HCURSOR hDefaultCursor);

45
windows/utilwin.cpp Normal file
View File

@ -0,0 +1,45 @@
// 14 may 2015
#include "uipriv_windows.hpp"
// The utility window is a special window that performs certain tasks internal to libui.
// TODO should it be message-only?
#define utilWindowClass L"libui_utilWindowClass"
HWND uiprivUtilWindow = NULL;
static LRESULT CALLBACK utilWindowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
void (*qf)(void *);
switch (uMsg) {
case uiprivUtilWindowMsgQueueMain:
qf = (void (*)(void *)) wParam;
(*qf)((void *) lParam);
return 0;
}
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
HRESULT uiprivInitUtilWindow(HICON hDefaultIcon, HCURSOR hDefaultCursor)
{
WNDCLASSW wc;
HRESULT hr;
ZeroMemory(&wc, sizeof (WNDCLASSW));
wc.lpszClassName = utilWindowClass;
wc.lpfnWndProc = utilWindowWndProc;
wc.hInstance = hInstance;
wc.hIcon = hDefaultIcon;
wc.hCursor = hDefaultCursor;
wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
hr = uiprivHrRegisterClassW(&wc);
if (hr != S_OK)
return hr;
return uiprivHrCreateWindowExW(0,
utilWindowClass, L"libui utility window",
WS_OVERLAPPEDWINDOW,
0, 0, 100, 100,
NULL, NULL, hInstance, NULL,
&uiprivUtilWindow);
}

8
windows/winhresult.cpp Normal file
View File

@ -0,0 +1,8 @@
// 28 april 2019
extern HRESULT WINAPI uiprivHrRegisterClassW(const WNDCLASSW *wc);
extern HRESULT WINAPI uiprivHrCreateWindowExW(DWORD exStyle, LPCWSTR className, LPCWSTR windowName, DWORD style, int x, int y, int width, int height, HWND parent, HMENU menu, HINSTANCE hInstance, LPVOID lpParam, HWND *hwnd);
extern HRESULT WINAPI uiprivHrGetMessageW(LPMSG msg, HWND hwnd, UINT filterMin, UINT filterMax, BOOL *ret);
extern HRESULT WINAPI uiprivHrPostMessageW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
extern HRESULT WINAPI uiprivHrLoadIconW(HINSTANCE hInstance, LPCWSTR name, HICON *hIcon);
extern HRESULT WINAPI uiprivHrLoadCursorW(HINSTANCE hInstance, LPCWSTR name, HCURSOR *hCursor);

81
windows/winhresult.hpp Normal file
View File

@ -0,0 +1,81 @@
// 28 april 2019
#include "winapi.h"
#include "winhresult.h"
// This file wraps standard Windows API functions that don't use HRESULTs to return HRESULTs.
// It also calls SetLastError(0) before each such call.
static inline HRESULT lastErrorCodeToHRESULT(DWORD lastError)
{
if (lastError == 0)
return E_FAIL;
return HRESULT_FROM_WIN32(lastError);
}
static inline HRESULT lastErrorToHRESULT(void)
{
return lastErrorCodeToHRESULT(GetLastError());
}
HRESULT WINAPI uiprivHrRegisterClassW(const WNDCLASSW *wc)
{
ATOM a;
SetLastError(0);
a = RegisterClassW(wc);
if (a == 0)
return lastErrorToHRESULT();
return S_OK;
}
HRESULT WINAPI uiprivHrCreateWindowExW(DWORD exStyle, LPCWSTR className, LPCWSTR windowName, DWORD style, int x, int y, int width, int height, HWND parent, HMENU menu, HINSTANCE hInstance, LPVOID lpParam, HWND *hwnd)
{
SetLastError(0);
*hwnd = CreateWindowExW(exStyle,
className, windowName,
style,
x, y, width, height,
parent, menu, hInstance, lpParam);
if (*hwnd == NULL)
return lastErrorToHRESULT();
return S_OK;
}
// TODO turn ret into S_OK/S_FALSE?
HRESULT WINAPI uiprivHrGetMessageW(LPMSG msg, HWND hwnd, UINT filterMin, UINT filterMax, BOOL *ret)
{
SetLastError(0);
*ret = GetMessageW(msg, hwnd, filterMin, filterMax);
if (*ret < 0)
return lastErrorToHRESULT();
return S_OK;
}
HRESULT WINAPI uiprivHrPostMessageW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
BOOL ret;
SetLastError(0);
ret = PostMessageW(hwnd, uMsg, wParam, lParam);
if (ret == 0)
return lastErrorToHRESULT();
return S_OK;
}
HRESULT WINAPI uiprivHrLoadIconW(HINSTANCE hInstance, LPCWSTR name, HICON *hIcon)
{
SetLastError(0);
*hIcon = LoadIconW(hInstance, name);
if (*hIcon == NULL)
return lastErrorToHRESULT();
return S_OK;
}
HRESULT WINAPI uiprivHrLoadCursorW(HINSTANCE hInstance, LPCWSTR name, HCURSOR *hCursor)
{
SetLastError(0);
*hCursor = LoadCursorW(hInstance, name);
if (*hCursor == NULL)
return lastErrorToHRESULT();
return S_OK;
}

View File

@ -73,17 +73,6 @@ const char *uiInit(uiInitOptions *o)
// LONGTERM set DPI awareness
hDefaultIcon = LoadIconW(NULL, IDI_APPLICATION);
if (hDefaultIcon == NULL)
return ieLastErr("loading default icon for window classes");
hDefaultCursor = LoadCursorW(NULL, IDC_ARROW);
if (hDefaultCursor == NULL)
return ieLastErr("loading default cursor for window classes");
ce = initUtilWindow(hDefaultIcon, hDefaultCursor);
if (ce != NULL)
return initerr(ce, L"GetLastError() ==", GetLastError());
if (registerWindowClass(hDefaultIcon, hDefaultCursor) == 0)
return ieLastErr("registering uiWindow window class");

View File

@ -59,24 +59,6 @@ static void processMessage(MSG *msg)
DispatchMessageW(msg);
}
static int waitMessage(MSG *msg)
{
int res;
res = GetMessageW(msg, NULL, 0, 0);
if (res < 0) {
logLastError(L"error calling GetMessage()");
return 0; // bail out on error
}
return res != 0; // returns false on WM_QUIT
}
void uiMain(void)
{
while (uiMainStep(1))
;
}
void uiMainSteps(void)
{
// don't need to do anything here
@ -98,12 +80,7 @@ int uiMainStep(int wait)
{
MSG msg;
if (wait) {
if (!waitMessage(&msg))
return 0;
processMessage(&msg);
return 1;
}
if (wait) { /* deleted */ }
// don't wait for a message
switch (peekMessage(&msg)) {
@ -117,18 +94,6 @@ int uiMainStep(int wait)
return 1; // no message
}
void uiQuit(void)
{
PostQuitMessage(0);
}
void uiQueueMain(void (*f)(void *data), void *data)
{
if (PostMessageW(utilWindow, msgQueued, (WPARAM) f, (LPARAM) data) == 0)
// LONGTERM this is likely not safe to call across threads (allocates memory)
logLastError(L"error queueing function to run on main thread");
}
static std::map<uiprivTimer *, bool> timers;
void uiTimer(int milliseconds, int (*f)(void *data), void *data)

View File

@ -10,10 +10,6 @@
// - It handles executing functions queued to run by uiQueueMain().
// TODO explain why it isn't message-only
#define utilWindowClass L"libui_utilWindowClass"
HWND utilWindow;
static LRESULT CALLBACK utilWindowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
void (*qf)(void *);
@ -33,10 +29,6 @@ static LRESULT CALLBACK utilWindowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, L
case WM_WININICHANGE:
issueWM_WININICHANGE(wParam, lParam);
return 0;
case msgQueued:
qf = (void (*)(void *)) wParam;
(*qf)((void *) lParam);
return 0;
case WM_TIMER:
timer = (uiprivTimer *) wParam;
if (!(*(timer->f))(timer->data)) {
@ -49,34 +41,6 @@ static LRESULT CALLBACK utilWindowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, L
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
const char *initUtilWindow(HICON hDefaultIcon, HCURSOR hDefaultCursor)
{
WNDCLASSW wc;
ZeroMemory(&wc, sizeof (WNDCLASSW));
wc.lpszClassName = utilWindowClass;
wc.lpfnWndProc = utilWindowWndProc;
wc.hInstance = hInstance;
wc.hIcon = hDefaultIcon;
wc.hCursor = hDefaultCursor;
wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
if (RegisterClass(&wc) == 0)
// see init.cpp for an explanation of the =s
return "=registering utility window class";
utilWindow = CreateWindowExW(0,
utilWindowClass, L"libui utility window",
WS_OVERLAPPEDWINDOW,
0, 0, 100, 100,
NULL, NULL, hInstance, NULL);
if (utilWindow == NULL)
return "=creating utility window";
// and just to be safe
EnableWindow(utilWindow, FALSE);
return NULL;
}
void uninitUtilWindow(void)
{
if (DestroyWindow(utilWindow) == 0)