|
| 1 | +#ifndef NOTIFY_CAPI_H |
| 2 | +#define NOTIFY_CAPI_H |
| 3 | + |
| 4 | +#define NOTIFY_CAPI_ID_STRING "_capi_notify" |
| 5 | +#define NOTIFY_CAPI_VERSION_MAJOR -1 |
| 6 | +#define NOTIFY_CAPI_VERSION_MINOR 1 |
| 7 | +#define NOTIFY_CAPI_VERSION_PATCH 0 |
| 8 | + |
| 9 | +typedef struct notify_notifier notify_notifier; |
| 10 | +typedef struct notify_capi notify_capi; |
| 11 | + |
| 12 | +#ifndef NOTIFY_CAPI_IMPLEMENT_SET_CAPI |
| 13 | +# define NOTIFY_CAPI_IMPLEMENT_SET_CAPI 0 |
| 14 | +#endif |
| 15 | + |
| 16 | +#ifndef NOTIFY_CAPI_IMPLEMENT_GET_CAPI |
| 17 | +# define NOTIFY_CAPI_IMPLEMENT_GET_CAPI 0 |
| 18 | +#endif |
| 19 | + |
| 20 | +/** |
| 21 | + * Type for pointer to function that may be called if an error occurs. |
| 22 | + * ehdata: void pointer that is given in notify method (see below) |
| 23 | + * msg: detailed error message |
| 24 | + * msglen: length of error message |
| 25 | + */ |
| 26 | +typedef void (*notifier_error_handler)(void* ehdata, const char* msg, size_t msglen); |
| 27 | + |
| 28 | +/** |
| 29 | + * Notify C API. |
| 30 | + */ |
| 31 | +struct notify_capi |
| 32 | +{ |
| 33 | + int version_major; |
| 34 | + int version_minor; |
| 35 | + int version_patch; |
| 36 | + |
| 37 | + /** |
| 38 | + * May point to another (incompatible) version of this API implementation. |
| 39 | + * NULL if no such implementation exists. |
| 40 | + * |
| 41 | + * The usage of next_capi makes it possible to implement two or more |
| 42 | + * incompatible versions of the C API. |
| 43 | + * |
| 44 | + * An API is compatible to another API if both have the same major |
| 45 | + * version number and if the minor version number of the first API is |
| 46 | + * greater or equal than the second one's. |
| 47 | + */ |
| 48 | + void* next_capi; |
| 49 | + |
| 50 | + /** |
| 51 | + * Must return a valid pointer if the Lua object at the given stack |
| 52 | + * index is a valid notifier, otherwise must return NULL. |
| 53 | + * |
| 54 | + * The returned notifier object must be valid as long as the Lua |
| 55 | + * object at the given stack index remains valid. |
| 56 | + * To keep the notifier object beyond this call, the function |
| 57 | + * retainNotifier() should be called (see below). |
| 58 | + */ |
| 59 | + notify_notifier* (*toNotifier)(lua_State* L, int index); |
| 60 | + |
| 61 | + /** |
| 62 | + * Increase the reference counter of the notifier object. |
| 63 | + * Must be thread safe. |
| 64 | + * |
| 65 | + * This function must be called after the function toNotifier() |
| 66 | + * as long as the Lua object on the given stack index is |
| 67 | + * valid (see above). |
| 68 | + */ |
| 69 | + void (*retainNotifier)(notify_notifier* n); |
| 70 | + |
| 71 | + /** |
| 72 | + * Decrease the reference counter of the notifier object and |
| 73 | + * destructs the notifier object if no reference is left. |
| 74 | + * Must be thread safe. |
| 75 | + */ |
| 76 | + void (*releaseNotifier)(notify_notifier* n); |
| 77 | + |
| 78 | + /** |
| 79 | + * Calls the notify method of the given notifier object. |
| 80 | + * Must be thread safe. |
| 81 | + * |
| 82 | + * eh: error handling function, may be NULL |
| 83 | + * ehdata: additional data that is given to error handling function. |
| 84 | + |
| 85 | + * Returns 0 - on success otherwise an error code != 0. |
| 86 | + * 1 - if notifier is closed. The caller is expected to release the notifier. |
| 87 | + * Subsequent calls will always result this return code again. |
| 88 | + * All other error codes are implementation specific. |
| 89 | + */ |
| 90 | + int (*notify)(notify_notifier* n, notifier_error_handler eh, void* ehdata); |
| 91 | +}; |
| 92 | + |
| 93 | +#if NOTIFY_CAPI_IMPLEMENT_SET_CAPI |
| 94 | +/** |
| 95 | + * Sets the Notify C API into the metatable at the given index. |
| 96 | + * |
| 97 | + * index: index of the table that is be used as metatable for objects |
| 98 | + * that are associated to the given capi. |
| 99 | + */ |
| 100 | +static int notify_set_capi(lua_State* L, int index, const notify_capi* capi) |
| 101 | +{ |
| 102 | + lua_pushlstring(L, NOTIFY_CAPI_ID_STRING, strlen(NOTIFY_CAPI_ID_STRING)); /* -> key */ |
| 103 | + void** udata = lua_newuserdata(L, sizeof(void*) + strlen(NOTIFY_CAPI_ID_STRING) + 1); /* -> key, value */ |
| 104 | + *udata = (void*)capi; |
| 105 | + strcpy((char*)(udata + 1), NOTIFY_CAPI_ID_STRING); /* -> key, value */ |
| 106 | + lua_rawset(L, (index < 0) ? (index - 2) : index); /* -> */ |
| 107 | + return 0; |
| 108 | +} |
| 109 | +#endif /* NOTIFY_CAPI_IMPLEMENT_SET_CAPI */ |
| 110 | + |
| 111 | +#if NOTIFY_CAPI_IMPLEMENT_GET_CAPI |
| 112 | +/** |
| 113 | + * Gives the associated Notify C API for the object at the given stack index. |
| 114 | + * Returns NULL, if the object at the given stack index does not have an |
| 115 | + * associated Notify C API or only has a Notify C API with incompatible version |
| 116 | + * number. If errorReason is not NULL it receives the error reason in this case: |
| 117 | + * 1 for incompatible version nummber and 2 for no associated C API at all. |
| 118 | + */ |
| 119 | +static const notify_capi* notify_get_capi(lua_State* L, int index, int* errorReason) |
| 120 | +{ |
| 121 | + if (luaL_getmetafield(L, index, NOTIFY_CAPI_ID_STRING) != LUA_TNIL) /* -> _capi */ |
| 122 | + { |
| 123 | + void** udata = lua_touserdata(L, -1); /* -> _capi */ |
| 124 | + |
| 125 | + if ( udata |
| 126 | + && (lua_rawlen(L, -1) >= sizeof(void*) + strlen(NOTIFY_CAPI_ID_STRING) + 1) |
| 127 | + && (memcmp((char*)(udata + 1), NOTIFY_CAPI_ID_STRING, |
| 128 | + strlen(NOTIFY_CAPI_ID_STRING) + 1) == 0)) |
| 129 | + { |
| 130 | + const notify_capi* capi = *udata; /* -> _capi */ |
| 131 | + while (capi) { |
| 132 | + if ( capi->version_major == NOTIFY_CAPI_VERSION_MAJOR |
| 133 | + && capi->version_minor >= NOTIFY_CAPI_VERSION_MINOR) |
| 134 | + { /* -> _capi */ |
| 135 | + lua_pop(L, 1); /* -> */ |
| 136 | + return capi; |
| 137 | + } |
| 138 | + capi = capi->next_capi; |
| 139 | + } |
| 140 | + if (errorReason) { |
| 141 | + *errorReason = 1; |
| 142 | + } |
| 143 | + } else { /* -> _capi */ |
| 144 | + if (errorReason) { |
| 145 | + *errorReason = 2; |
| 146 | + } |
| 147 | + } |
| 148 | + lua_pop(L, 1); /* -> */ |
| 149 | + } else { /* -> */ |
| 150 | + if (errorReason) { |
| 151 | + *errorReason = 2; |
| 152 | + } |
| 153 | + } |
| 154 | + return NULL; |
| 155 | +} |
| 156 | +#endif /* NOTIFY_CAPI_IMPLEMENT_GET_CAPI */ |
| 157 | + |
| 158 | +#endif /* NOTIFY_CAPI_H */ |
0 commit comments