libui/common/main.c

133 lines
3.0 KiB
C

// 19 april 2019
#include "uipriv.h"
enum {
stateUninitialized,
stateBeforeMain,
stateInMain,
stateQuitting,
stateAfterMain,
stateError,
};
static int state = stateUninitialized;
#define initialized() (state != stateUninitialized && state != stateError)
bool testHookInitShouldFail = false;
void uiprivTestHookSetInitShouldFailArtificially(bool shouldFail)
{
testHookInitShouldFail = shouldFail;
}
bool uiInit(void *options, uiInitError *err)
{
if (state != stateUninitialized) {
uiprivProgrammerErrorMultipleCalls(uiprivFunc);
state = stateError;
return false;
}
if (options != NULL) {
uiprivProgrammerErrorBadInitOptions(uiprivFunc);
state = stateError;
return false;
}
if (err == NULL) {
uiprivProgrammerErrorNullPointer("uiInitError", uiprivFunc);
state = stateError;
return false;
}
if (err->Size != sizeof (uiInitError)) {
uiprivProgrammerErrorWrongStructSize(err->Size, "uiInitError", uiprivFunc);
state = stateError;
return false;
}
if (testHookInitShouldFail) {
state = stateError;
return uiprivInitReturnErrorf(err, "general failure");
}
if (!uiprivSysInit(options, err)) {
state = stateError;
return false;
}
state = stateBeforeMain;
return true;
}
bool uiprivInitReturnErrorf(uiInitError *err, const char *fmt, ...)
{
int n;
va_list ap;
va_start(ap, fmt);
n = uiprivVsnprintf(err->Message, 256, fmt, ap);
va_end(ap);
if (n < 0) {
uiprivInternalError("encoding error returning initialization error; this means something is very very wrong with libui itself");
abort(); // TODO handle this scenario more gracefully
}
if (n >= 256) {
// the formatted message is too long; truncate it
err->Message[252] = '.';
err->Message[253] = '.';
err->Message[254] = '.';
err->Message[255] = '\0';
}
return false;
}
void uiMain(void)
{
if (!uiprivCheckInitializedAndThread())
return;
if (state != stateBeforeMain) {
uiprivProgrammerErrorMultipleCalls(uiprivFunc);
return;
}
state = stateInMain;
uiprivSysMain();
state = stateAfterMain;
}
void uiQuit(void)
{
if (!uiprivCheckInitializedAndThread())
return;
if (state == stateQuitting || state == stateAfterMain) {
uiprivProgrammerErrorMultipleCalls(uiprivFunc);
return;
}
if (state != stateInMain) {
// the above handle the other states, so stateBeforeMain is what's left
uiprivProgrammerErrorQuitBeforeMain(uiprivFunc);
return;
}
state = stateQuitting;
uiprivSysQuit();
}
void uiQueueMain(void (*f)(void *data), void *data)
{
if (!initialized()) {
uiprivProgrammerErrorNotInitialized(uiprivFunc);
return;
}
uiprivSysQueueMain(f, data);
}
bool uiprivCheckInitializedAndThreadImpl(const char *func)
{
// While it does seem risky to not lock this, if this changes during the execution of this function it means only that it was changed from a different thread, and since it can only change from false to true, an error will be reported anyway.
if (!initialized()) {
uiprivProgrammerErrorNotInitialized(func);
return false;
}
if (!uiprivSysCheckThread()) {
uiprivProgrammerErrorWrongThread(func);
return false;
}
return true;
}