From 1189877ca555d61bdafc02cfe9fe74c5727903ab Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 8 Jun 2019 13:00:30 -0400 Subject: [PATCH] Started writing the uiControl implementation. --- common/controls.c | 162 ++++++++++++++++++++++++++++++++++++++ common/programmererrors.h | 25 +++++- doc/controls.md | 10 ++- ui.h | 2 +- 4 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 common/controls.c diff --git a/common/controls.c b/common/controls.c new file mode 100644 index 00000000..be354bdc --- /dev/null +++ b/common/controls.c @@ -0,0 +1,162 @@ +// 8 june 2019 +#include "uipriv.h" + +struct controlType { + uint32_t id; + char *name; + uiControlVtable vtable; + uiControlOSVtable *osVtable; + size_t implDataSize; +}; + +static int controlTypeCmp(const void *a, const void *b) +{ + const struct controlType *ca = (const struct controlType *) a; + const struct controlType *cb = (const struct controlType *) b; + + if (ca->id < cb->id) + return -1; + if (ca->id > cb->id) + return 1; + return 0; +} + +struct uiControl { + uint32_t controlID; + uint32_t typeID; + struct controlType *type; + void *implData; +}; + +static uiprivArray controlTypes = uiprivArrayStaticInit(struct controlType, 32, "uiControl type information"); + +#define controlTypeID UINT32_C(0x1F2E3C4D) + +uint32_t uiControlType(void) +{ + if (!uiprivCheckInitializedAndThread()) + return 0; + return controlTypeID; +} + +static uint32_t nextControlID = UINT32_C(0x80000000); + +uint32_t uiRegisterControlType(const char *name, uiControlVtable *vtable, uiControlOSVtable *osVtable, size_t implDataSize) +{ + struct controlType *ct; + + if (!uiprivCheckInitializedAndThread()) + return 0; + if (vtable == NULL) { + uiprivProgrammerErrorNullPointer("uiControlVtable", uiprivFunc); + return 0; + } + if (vtable->Size != sizeof (uiControlVtable)) { + uiprivProgrammerErrorWrongStructSize(vtable->Size, "uiControlVtable"); + return 0; + } +#define checkMethod(method) \ + if (vtable->method == NULL) { \ + uiprivProgrammerErrorRequiredMethodMissing(name, "uiControlVtable", #method, uiprivFunc); \ + return 0; \ + } + checkMethod(Init) + checkMethod(Free) +#undef checkMethod + + if (osVtable == NULL) { + uiprivProgrammerErrorNullPointer("uiControlOSVtable", uiprivFunc); + return 0; + } + if (!uiprivOSVtableValid(osVtable, uiprivFunc)) + return 0; + + ct = (struct controlType *) uiprivArr +ayAppend(&controlTypes, 1); + ct->id = nextControlID; + nextControlID++; + ct->name = uiprivStrdup(name); + ct->vtable = *vtable; + ct->osVtable = uiprivCloneOSVtable(osVtable); + ct->implDataSize = implDataSize; + return ct; +} + +void *uiCheckControlType(void *c, uint32_t type) +{ + struct controlType *got, *want; + struct controlType key; + + if (!uiprivCheckInitializedAndThread()) + return NULL; + if (c == NULL) { + uiprivProgrammerErrorNullPointer("uiControl", uiprivFunc); + return NULL; + } + if (c->controlID != controlTypeID) { + uiprivProgrammerErrorNotAControl(uiprivFunc); + return NULL; + } + + // now grab the type information for c itself + // do this even if we were asked if this is a uiControl; we want to make absolutely sure this is a *real* uiControl + memset(&key, 0, sizeof (struct controlType)); + key.id = c->typeID; + got = (struct controlType *) uiprivArrayBsearch(&controlTypes &key, controlTypeCmp); + if (got == NULL) { + uiprivProgrammerErrorUnknownTypeUsed(c->typeID, uiprivFunc); + return NULL; + } + + if (type == controlTypeID) + // this is a real uiControl; no need to check further + return c; + + // type isn't uiControlType(); make sure it is valid too + memset(&key, 0, sizeof (struct controlType)); + key.id = type; + want = (struct controlType *) uiprivArrayBsearch(&controlTypes &key, controlTypeCmp); + if (want == NULL) { + uiprivProgrammerErrorUnknownTypeRequested(type, uiprivFunc); + return NULL; + } + + if (c->typeID != type) { + uiprivProgrammerErrorWrongType(got->name, want->name, uiprivFunc); + return NULL; + } + return c; +} + +uiControl *uiNewControl(uint32_t type, void *initData) +{ + uiControl *c; + struct controlType *ct; + struct controlType key; + + if (!uiprivCheckInitializedAndThread()) + return NULL; + if (type == controlTypeID) { + uiprivProgrammerErrorCannotCreateBaseControl(); + return NULL; + } + memset(&key, 0, sizeof (struct controlType)); + key.id = type; + ct = (struct controlType *) uiprivArrayBsearch(&controlTypes, &key, controlTypeCmp); + if (ct == NULL) { + uiprivProgrammerErrorUnknownTypeRequested(type, uiprivFunc); + return NULL; + } +} + +void uiControlFree(uiControl *c) +{ + if (!uiprivCheckInitializedAndThread()) + return; +} + +void *uiControlImplData(uiControl *c) +{ + if (!uiprivCheckInitializedAndThread()) + return NULL; +} diff --git a/common/programmererrors.h b/common/programmererrors.h index 1210c0e8..f58f2d3e 100644 --- a/common/programmererrors.h +++ b/common/programmererrors.h @@ -24,7 +24,25 @@ uiprivProgrammerError("%s identifier %d not found in %s()", \ idDesc, badID, func) -// TODO type mismatch +#define uiprivProgrammerErrorRequiredMethodMissing(typeName, tableType, methodName, func) \ + uiprivProgrammerError("%s: required %s method %s() missing in %s()", \ + typeName, tableType, methodName, func) + +#define uiprivProgrammerErrorNotAControl(func) \ + uiprivProgrammerError("object passed in to %s() not a uiControl", \ + func) + +#define uiprivProgrammerErrorUnknownTypeUsed(type, func) \ + uiprivProgrammerError("unknown type %" PRIu32 " found in uiControl passed to %s(); this is likely not a real uiControl or some data is corrupt", \ + type, func) + +#define uiprivProgrammerErrorUnknownTypeRequested(type, func) \ + uiprivProgrammerError("unknown type %" PRIu32 " passed to %s()", \ + type, func) + +#define uiprivProgrammerErrorWrongType(got, want, func) \ + uiprivProgrammerError("wrong type passed to %s(): got %s, want %s", + func, got, want) #define uiprivProgrammerErrorBadSenderForEvent(senderDesc, eventDesc, func) \ uiprivProgrammerError("attempt to use a %s sender with a %s event in %s()", \ @@ -45,3 +63,8 @@ #define uiprivProgrammerErrorInvalidatingGlobalEvent() \ uiprivProgrammerError("attempt to call uiEventInvalidateSender() on a global uiEvent") + +// TODO move the type stuff here + +#define uiprivProgrammerErrorCannotCreateBaseControl() \ + uiprivProgrammerError("cannot create a uiControl of type uiControl; you must use a specific control type") diff --git a/doc/controls.md b/doc/controls.md index 6435aea0..6ca5f655 100644 --- a/doc/controls.md +++ b/doc/controls.md @@ -42,14 +42,18 @@ Each method is named for the `uiControl` function that it implements. As such, d ### `uiRegisterControlType()` ```c -uint32_t uiRegisterControlType(uiControlVtable *vtable, uiControlOSVtable *osVtable, size_t implDataSize); +uint32_t uiRegisterControlType(const char *name, uiControlVtable *vtable, uiControlOSVtable *osVtable, size_t implDataSize); ``` `uiRegisterControlType()` registers a new `uiControl` type with the given vtables and returns its ID as passed to `uiNewControl()`. `implDataSize` is the size of the implementation data struct that is created by `uiNewControl()`. -`uiControlVtable` describes the functions of a `uiControl` common between platforms, and is discussed on this page. `uiControlOSVtable` describes functionst hat vary from OS to OS, and are described in the respective OS-specific uiControl implementation pages. +Each type has a name, passed in the `name` parameter. The type name is only used for debugging and error reporting purposes. The type name is copied into libui-internal memory; the `name` pointer passed to `uiRegisterControlType()` is not used after it returns. -It is a programmer error to specify `NULL` for either vtable. An `implDataSize` of 0 is legal; the implementation data pointer will be `NULL`. This is not particularly useful, however. It is also a programmer error to specify `NULL` for any of the methods in either vtable — that is, all methods are required. It is also a programmer error to pass the wrong value to the `Size` field of either vtable. +`uiControlVtable` describes the functions of a `uiControl` common between platforms, and is discussed on this page. `uiControlOSVtable` describes functionst hat vary from OS to OS, and are described in the respective OS-specific uiControl implementation pages. The two vtables are copied into libui-internal memory; the vtable pointers passed to `uiRegisterControlType()` are not used after it returns. + +It is a programmer error to specify `NULL` for either vtable. It is also a programmer error to specify `NULL` for any of the methods in either vtable — that is, all methods are required. It is also a programmer error to pass the wrong value to the `Size` field of either vtable. + +An `implDataSize` of 0 is legal; the implementation data pointer will be `NULL`. This is not particularly useful, however. ### `uiCheckControlType()` diff --git a/ui.h b/ui.h index bbef596e..7ba942ba 100644 --- a/ui.h +++ b/ui.h @@ -75,7 +75,7 @@ struct uiControlVtable { void (*Free)(uiControl *c, void *implData); }; -uiprivExtern uint32_t uiRegisterControlType(uiControlVtable *vtable, uiControlOSVtable *osVtable, size_t implDataSize); +uiprivExtern uint32_t uiRegisterControlType(const char *nane, uiControlVtable *vtable, uiControlOSVtable *osVtable, size_t implDataSize); uiprivExtern void *uiCheckControlType(void *c, uint32_t type); uiprivExtern uiControl *uiNewControl(uint32_t type, void *initData);