diff --git a/common/main.c b/common/main.c index ac3a56f2..fb0bfc78 100644 --- a/common/main.c +++ b/common/main.c @@ -1,24 +1,47 @@ // 19 april 2019 #include "uipriv.h" -static bool initialized = false; +enum { + stateUninitialized, + stateBeforeMain, + stateInMain, + stateQuitting, + stateAfterMain, + stateError, +}; + +static int state = stateUninitialized; + +#define initialized() (state != stateUninitialized && state != stateError) bool uiInit(void *options, uiInitError *err) { - if (err == NULL) + if (state != stateUninitialized) { + uiprivProgrammerErrorMultipleCalls(uiprivFunc); + state = stateError; return false; - if (err->Size != sizeof (uiInitError)) + } + if (options != NULL) { + uiprivProgrammerErrorBadInitOptions(uiprivFunc); + state = stateError; return false; - - if (initialized) - return uiprivInitReturnErrorf(err, "libui already initialized"); - - if (options != NULL) - return uiprivInitReturnErrorf(err, "options parameter to uiInit() must be NULL"); - - if (!uiprivSysInit(options, err)) + } + if (err == NULL) { + uiprivProgrammerErrorNullPointer("uiInitError", uiprivFunc); + state = stateError; return false; - initialized = true; + } + if (err->Size != sizeof (uiInitError)) { + uiprivProgrammerErrorWrongStructSize(err->Size, "uiInitError", uiprivFunc); + state = stateError; + return false; + } + + if (!uiprivSysInit(options, err)) { + state = stateError; + return false; + } + state = stateBeforeMain; return true; } @@ -44,9 +67,39 @@ bool uiprivInitReturnErrorf(uiInitError *err, const char *fmt, ...) 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) { + if (!initialized()) { uiprivProgrammerErrorNotInitialized(uiprivFunc); return; } @@ -56,7 +109,7 @@ void uiQueueMain(void (*f)(void *data), void *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) { + if (!initialized()) { uiprivProgrammerErrorNotInitialized(func); return false; } diff --git a/common/programmererrors.h b/common/programmererrors.h index a529878a..038422be 100644 --- a/common/programmererrors.h +++ b/common/programmererrors.h @@ -24,6 +24,19 @@ // } +// main { + +#define uiprivProgrammerErrorMultipleCalls(func) \ + uiprivProgrammerError("%s(): attempt to call more than once", func) + +#define uiprivProgrammerErrorBadInitOptions(func) \ + uiprivProgrammerError("%s(): invalid uiInitOptions passed", func) + +#define uiprivProgrammerErrorQuitBeforeMain(func) \ + uiprivProgrammerError("%s(): attempt to call before uiMain()", func) + +// } + // controls { #define uiprivProgrammerErrorRequiredControlMethodMissing(typeName, tableType, methodName, func) \ diff --git a/common/uipriv.h b/common/uipriv.h index bd84dd22..cb661dc5 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -40,6 +40,8 @@ extern bool uiprivSysInit(void *options, uiInitError *err); uiprivPrintfFunc( extern bool uiprivInitReturnErrorf(uiInitError *err, const char *fmt, ...), 2, 3); +extern void uiprivSysMain(void); +extern void uiprivSysQuit(void); extern void uiprivSysQueueMain(void (*f)(void *data), void *data); extern bool uiprivCheckInitializedAndThreadImpl(const char *func); #define uiprivCheckInitializedAndThread() uiprivCheckInitializedAndThreadImpl(uiprivFunc) diff --git a/darwin/main.m b/darwin/main.m index e26b1566..2f9074c2 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -56,17 +56,13 @@ bool uiprivSysInit(void *options, uiInitError *err) return true; } -void uiMain(void) +void uiprivSysMain(void) { - if (!uiprivCheckInitializedAndThread()) - return; [uiprivApp run]; } -void uiQuit(void) +void uiprivSysQuit(void) { - if (!uiprivCheckInitializedAndThread()) - return; @autoreleasepool { NSEvent *e;