diff options
author | Mattias Andrée <m@maandree.se> | 2025-02-10 17:50:58 +0100 |
---|---|---|
committer | Mattias Andrée <m@maandree.se> | 2025-02-10 17:52:46 +0100 |
commit | ec1bcdcd0dd6e196303e8d9a30b3b2740e32c502 (patch) | |
tree | dcc759aaf897c915827659e00644f12503cf1268 | |
parent | Improve makefile (diff) | |
download | libcoopgamma-ec1bcdcd0dd6e196303e8d9a30b3b2740e32c502.tar.gz libcoopgamma-ec1bcdcd0dd6e196303e8d9a30b3b2740e32c502.tar.bz2 libcoopgamma-ec1bcdcd0dd6e196303e8d9a30b3b2740e32c502.tar.xz |
Minor code improvements and split into multiple c files
Signed-off-by: Mattias Andrée <m@maandree.se>
66 files changed, 2784 insertions, 2596 deletions
@@ -15,13 +15,89 @@ LIB_MINOR = 2 LIB_VERSION = $(LIB_MAJOR).$(LIB_MINOR) -OBJ = libcoopgamma.o +OBJ_MAN3 =\ + libcoopgamma_async_context_destroy.o\ + libcoopgamma_async_context_initialise.o\ + libcoopgamma_async_context_marshal.o\ + libcoopgamma_async_context_unmarshal.o\ + libcoopgamma_connect.o\ + libcoopgamma_context_destroy.o\ + libcoopgamma_context_initialise.o\ + libcoopgamma_context_marshal.o\ + libcoopgamma_context_unmarshal.o\ + libcoopgamma_crtc_info_destroy.o\ + libcoopgamma_crtc_info_initialise.o\ + libcoopgamma_crtc_info_marshal.o\ + libcoopgamma_crtc_info_unmarshal.o\ + libcoopgamma_error_destroy.o\ + libcoopgamma_error_initialise.o\ + libcoopgamma_error_marshal.o\ + libcoopgamma_error_unmarshal.o\ + libcoopgamma_filter_destroy.o\ + libcoopgamma_filter_initialise.o\ + libcoopgamma_filter_marshal.o\ + libcoopgamma_filter_query_destroy.o\ + libcoopgamma_filter_query_initialise.o\ + libcoopgamma_filter_query_marshal.o\ + libcoopgamma_filter_query_unmarshal.o\ + libcoopgamma_filter_table_destroy.o\ + libcoopgamma_filter_table_initialise.o\ + libcoopgamma_filter_table_marshal.o\ + libcoopgamma_filter_table_unmarshal.o\ + libcoopgamma_filter_unmarshal.o\ + libcoopgamma_flush.o\ + libcoopgamma_get_crtcs_recv.o\ + libcoopgamma_get_crtcs_send.o\ + libcoopgamma_get_crtcs_sync.o\ + libcoopgamma_get_gamma_info_recv.o\ + libcoopgamma_get_gamma_info_send.o\ + libcoopgamma_get_gamma_info_sync.o\ + libcoopgamma_get_gamma_recv.o\ + libcoopgamma_get_gamma_send.o\ + libcoopgamma_get_gamma_sync.o\ + libcoopgamma_get_method_and_site.o\ + libcoopgamma_get_methods.o\ + libcoopgamma_get_pid_file.o\ + libcoopgamma_get_socket_file.o\ + libcoopgamma_queried_filter_destroy.o\ + libcoopgamma_queried_filter_initialise.o\ + libcoopgamma_queried_filter_marshal.o\ + libcoopgamma_queried_filter_unmarshal.o\ + libcoopgamma_ramps_destroy.o\ + libcoopgamma_set_gamma_recv.o\ + libcoopgamma_set_gamma_send.o\ + libcoopgamma_set_gamma_sync.o\ + libcoopgamma_set_nonblocking.o\ + libcoopgamma_skip_message.o\ + libcoopgamma_synchronise.o + + +OBJ =\ + $(OBJ_MAN3)\ + libcoopgamma.o\ + libcoopgamma_check_error__.o\ + libcoopgamma_query__.o\ + libcoopgamma_ramps_initialise_.o\ + libcoopgamma_ramps_marshal_.o\ + libcoopgamma_ramps_unmarshal_.o\ + libcoopgamma_send_message__.o + HDR = libcoopgamma.h LOBJ = $(OBJ:.o=.lo) -include man.mk +MAN0 =\ + libcoopgamma.h.0 + +MAN3 =\ + $(OBJ_MAN3:.o=.3)\ + libcoopgamma_ramps_initialise.3\ + libcoopgamma_ramps_marshal.3\ + libcoopgamma_ramps_unmarshal.3 + +MAN7 =\ + libcoopgamma.7 all: libcoopgamma.a libcoopgamma.$(LIBEXT) test diff --git a/common.h b/common.h new file mode 100644 index 0000000..2d87b85 --- /dev/null +++ b/common.h @@ -0,0 +1,259 @@ +/* See LICENSE file for copyright and license details. */ +#include "libcoopgamma.h" + +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/wait.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <poll.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + + +#if !defined(COOPGAMMAD) +# define COOPGAMMAD "coopgammad" +#endif + + +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wdocumentation" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# pragma clang diagnostic ignored "-Wcast-align" +#endif + + +#if defined(__GNUC__) +# define NAME_OF_THE_PROCESS (argv0) +extern const char *argv0; +#else +# define NAME_OF_THE_PROCESS ("libcoopgamma") +#endif + + +#if defined(__GNUC__) +# define HIDDEN __attribute__((__visibility__("hidden"))) +#else +# define HIDDEN +#endif + + + +#define SUBBUF\ + (buf ? &buf[off] : NULL) + +#define NNSUBBUF\ + (&buf[off]) + +#define MARSHAL_PROLOGUE\ + char *restrict buf = vbuf;\ + size_t off = 0; + +#define UNMARSHAL_PROLOGUE\ + const char *restrict buf = vbuf;\ + size_t off = 0; + +#define MARSHAL_EPILOGUE\ + return off + +#define UNMARSHAL_EPILOGUE\ + return *np = off, LIBCOOPGAMMA_SUCCESS + +#define marshal_prim(datum, type)\ + ((buf != NULL ? *(type *)&buf[off] = (datum) : 0), off += sizeof(type)) + +#define unmarshal_prim(datum, type)\ + ((datum) = *(const type *)&buf[off], off += sizeof(type)) + +#define marshal_version(version)\ + marshal_prim(version, int) + +#define unmarshal_version(version)\ + do {\ + int version__;\ + unmarshal_prim(version__, int);\ + if (version__ < (version))\ + return LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE;\ + if (version__ > (version))\ + return LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE;\ + } while (0) + +#define marshal_buffer(data, n)\ + ((buf ? (memcpy(&buf[off], (data), (n)), 0) : 0), off += (n)) + +#define unmarshal_buffer(data, n)\ + do {\ + (data) = malloc((n));\ + if (!(data))\ + return LIBCOOPGAMMA_ERRNO_SET;\ + memcpy((data), &buf[off], (n));\ + off += (n);\ + } while (0) + +#define marshal_string(datum)\ + (!(datum) ? marshal_prim(0, char) :\ + (marshal_prim(1, char), marshal_buffer((datum), strlen(datum) + 1U))) + +#define unmarshal_string(datum)\ + do {\ + char nonnull__;\ + unmarshal_prim(nonnull__, char);\ + if (nonnull__)\ + unmarshal_buffer((datum), strlen(&buf[off]) + 1U);\ + else\ + (datum) = NULL;\ + } while (0) + + + +#define copy_errno(ctx)\ + (!errno ? NULL :\ + ((ctx)->error.number = (uint64_t)errno,\ + (ctx)->error.custom = 0,\ + (ctx)->error.server_side = 0,\ + free((ctx)->error.description),\ + (ctx)->error.description = NULL)) + + +#define SYNC_CALL(send_call, recv_call, fail_return)\ + libcoopgamma_async_context_t async;\ + if (send_call < 0) {\ + reflush:\ + if (errno != EINTR)\ + return fail_return;\ + if (libcoopgamma_flush(ctx) < 0)\ + goto reflush;\ + }\ + resync:\ + if (libcoopgamma_synchronise(ctx, &async, (size_t)1, &(size_t){0}) < 0) {\ + if (errno != EINTR && errno)\ + return fail_return;\ + goto resync;\ + }\ + return recv_call + + +#define INTEGRAL_DEPTHS\ + case LIBCOOPGAMMA_UINT8:\ + case LIBCOOPGAMMA_UINT16:\ + case LIBCOOPGAMMA_UINT32:\ + case LIBCOOPGAMMA_UINT64: + + +/** + * Send a message to the server and wait for response + * + * @param resp:char** Output parameter for the response, + * will be NUL-terminated + * @param ctx:libcoopgamma_context_t* The state of the library + * @param payload:void* Data to append to the end of the message + * @param payload_size:size_t Byte-size of `payload` + * @param format:string-literal Message formatting string + * @param ... Message formatting arguments + * + * On error, the macro goes to `fail`. + */ +#define SEND_MESSAGE(ctx, payload, payload_size, format, ...)\ + do {\ + ssize_t n__;\ + char *msg__;\ + snprintf(NULL, (size_t)0, format "%zn", __VA_ARGS__, &n__);\ + msg__ = malloc((size_t)n__ + (payload_size) + (size_t)1);\ + if (!msg__)\ + goto fail;\ + sprintf(msg__, format, __VA_ARGS__);\ + if (payload)\ + memcpy(&msg__[n__], (payload), (payload_size));\ + if (libcoopgamma_send_message__((ctx), msg__, (size_t)n__ + (payload_size)) < 0)\ + goto fail;\ + } while (0) + + +/** + * Run coopgammad with -q or -qq and return the response + * + * SIGCHLD must not be ignored or blocked + * + * @param method The adjustment method, `NULL` for automatic + * @param site The site, `NULL` for automatic + * @param arg "-q" or "-qq", which shall be passed to coopgammad? + * @return The output of coopgammad, `NULL` on error. This + * will be NUL-terminated and will not contain any + * other `NUL` bytes. + */ +HIDDEN char *libcoopgamma_query__(const char *restrict method, const char *restrict site, const char *restrict arg); + +/** + * Send a message to the server and wait for response + * + * @param ctx The state of the library + * @param msg The message to send + * @param n The length of `msg` + * @return Zero on success, -1 on error + */ +HIDDEN int libcoopgamma_send_message__(libcoopgamma_context_t *restrict ctx, char *msg, size_t n); + +/** + * Check whether the server sent an error, if so copy it to `ctx` + * + * This function will also reports EBADMSG if the message ID + * that the message is a response to does not match the request + * information, or if it is missing + * + * @param ctx The state of the library, must be connected + * @param async Information about the request + * @return 1 if the server sent an error (even indicating success), + * 0 on success, -1 on failure. Information about failure + * is copied `ctx`. + */ +HIDDEN int libcoopgamma_check_error__(libcoopgamma_context_t *restrict ctx, libcoopgamma_async_context_t *restrict async); + + +/** + * Get the next header of the inbound message + * + * All headers must be read before the payload is read + * + * @param ctx The state of the library, must be connected + * @return The next header line, can never be `NULL`, + * the empty string marks the end of the headers. + * This memory segment must not be freed. + */ +HIDDEN inline char * +libcoopgamma_next_header__(libcoopgamma_context_t *restrict ctx) +{ + char *rc = ctx->inbound + ctx->inbound_tail; + ctx->inbound_tail += strlen(rc) + 1U; + return rc; +} + + +/** + * Get the payload of the inbound message + * + * Calling this function marks that the inbound message + * has been fully ready. You must call this function + * even if you do not expect a payload + * + * @param ctx The state of the library, must be connected + * @param n Output parameter for the size of the payload + * @return The payload (not NUL-terminated), `NULL` if + * there is no payload. Failure is impossible. + * This memory segment must not be freed. + */ +HIDDEN inline char * +libcoopgamma_next_payload__(libcoopgamma_context_t *restrict ctx, size_t *n) +{ + char *rc; + ctx->have_all_headers = 0; + if ((*n = ctx->length)) { + rc = ctx->inbound + ctx->inbound_tail; + ctx->inbound_tail += *n; + ctx->length = 0; + return rc; + } + return NULL; +} diff --git a/libcoopgamma.c b/libcoopgamma.c index b4a411a..79e90f9 100644 --- a/libcoopgamma.c +++ b/libcoopgamma.c @@ -1,2538 +1,11 @@ /* See LICENSE file for copyright and license details. */ -#include "libcoopgamma.h" - -#include <sys/socket.h> -#include <sys/un.h> -#include <sys/wait.h> -#include <errno.h> -#include <fcntl.h> -#include <inttypes.h> -#include <poll.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - - -#if !defined(COOPGAMMAD) -# define COOPGAMMAD "coopgammad" -#endif - - -#if defined(__clang__) -# pragma GCC diagnostic ignored "-Wdocumentation" -# pragma GCC diagnostic ignored "-Wcovered-switch-default" -# pragma GCC diagnostic ignored "-Wcast-align" -#endif +#include "common.h" #if defined(__GNUC__) -# define NAME_OF_THE_PROCESS (argv0) -extern const char *argv0; const char *argv0 __attribute__((weak)) = "libcoopgamma"; -#else -# define NAME_OF_THE_PROCESS ("libcoopgamma") -#endif - - - -#define SUBBUF\ - (buf ? &buf[off] : NULL) - -#define NNSUBBUF\ - (buf + off) - -#define MARSHAL_PROLOGUE\ - char *restrict buf = vbuf;\ - size_t off = 0; - -#define UNMARSHAL_PROLOGUE\ - const char *restrict buf = vbuf;\ - size_t off = 0; - -#define MARSHAL_EPILOGUE\ - return off - -#define UNMARSHAL_EPILOGUE\ - return *np = off, LIBCOOPGAMMA_SUCCESS - -#define marshal_prim(datum, type)\ - ((buf != NULL ? *(type *)&buf[off] = (datum) : 0), off += sizeof(type)) - -#define unmarshal_prim(datum, type)\ - ((datum) = *(const type *)&buf[off], off += sizeof(type)) - -#define marshal_version(version)\ - marshal_prim(version, int) - -#define unmarshal_version(version)\ - do {\ - int version__;\ - unmarshal_prim(version__, int);\ - if (version__ < (version))\ - return LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE;\ - if (version__ > (version))\ - return LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE;\ - } while (0) - -#define marshal_buffer(data, n)\ - ((buf ? (memcpy(buf + off, (data), (n)), 0) : 0), off += (n)) - -#define unmarshal_buffer(data, n)\ - do {\ - (data) = malloc(n);\ - if (!(data))\ - return LIBCOOPGAMMA_ERRNO_SET;\ - memcpy((data), &buf[off], (n));\ - off += (n);\ - } while (0) - -#define marshal_string(datum)\ - (!(datum) ? marshal_prim(0, char) :\ - (marshal_prim(1, char), marshal_buffer((datum), strlen(datum) + 1))) - -#define unmarshal_string(datum)\ - do {\ - char nonnull__;\ - unmarshal_prim(nonnull__, char);\ - if (nonnull__)\ - unmarshal_buffer((datum), strlen(buf + off) + 1);\ - else\ - (datum) = NULL;\ - } while (0) - - - -#define copy_errno(ctx)\ - (!errno ? NULL :\ - ((ctx)->error.number = (uint64_t)errno,\ - (ctx)->error.custom = 0,\ - (ctx)->error.server_side = 0,\ - free((ctx)->error.description),\ - (ctx)->error.description = NULL)) - - -#define SYNC_CALL(send_call, recv_call, fail_return)\ - libcoopgamma_async_context_t async;\ - if (send_call < 0) {\ - reflush:\ - if (errno != EINTR)\ - return fail_return;\ - if (libcoopgamma_flush(ctx) < 0)\ - goto reflush;\ - }\ - resync:\ - if (libcoopgamma_synchronise(ctx, &async, (size_t)1, &(size_t){0}) < 0) {\ - if (errno != EINTR && errno)\ - return fail_return;\ - goto resync;\ - }\ - return recv_call - - -#define INTEGRAL_DEPTHS\ - case LIBCOOPGAMMA_UINT8:\ - case LIBCOOPGAMMA_UINT16:\ - case LIBCOOPGAMMA_UINT32:\ - case LIBCOOPGAMMA_UINT64: - - - -/** - * Initialise a `libcoopgamma_ramps8_t`, `libcoopgamma_ramps16_t`, `libcoopgamma_ramps32_t`, - * `libcoopgamma_ramps64_t`, `libcoopgamma_rampsf_t`, or `libcoopgamma_rampsd_t` - * - * `this->red_size`, `this->green_size`, and `this->blue_size` must already be set - * - * @param this The record to initialise - * @param width The `sizeof(*(this->red))` - * @return Zero on success, -1 on error - */ -int -libcoopgamma_ramps_initialise_(void *restrict this, size_t width) -{ - libcoopgamma_ramps8_t *restrict this8 = (libcoopgamma_ramps8_t *restrict)this; - this8->red = this8->green = this8->blue = NULL; - this8->red = malloc((this8->red_size + this8->green_size + this8->blue_size) * width); - if (!this8->red) - return -1; - this8->green = this8->red + this8->red_size * width; - this8->blue = this8->green + this8->green_size * width; - return 0; -} - - -/** - * Release all resources allocated to a `libcoopgamma_ramps8_t`, `libcoopgamma_ramps16_t`, - * `libcoopgamma_ramps32_t`, `libcoopgamma_ramps64_t`, `libcoopgamma_rampsf_t`, - * `libcoopgamma_rampsd_t`, or `libcoopgamma_ramps_t`, the allocation of the record - * itself is not freed - * - * Always call this function after failed call to `libcoopgamma_ramps_initialise` - * or failed call to `libcoopgamma_ramps_unmarshal` - * - * @param this The record to destroy - */ -void -libcoopgamma_ramps_destroy(void *restrict this) -{ - libcoopgamma_ramps8_t *restrict this8 = (libcoopgamma_ramps8_t *restrict)this; - free(this8->red); - this8->red = this8->green = this8->blue = NULL; -} - - -/** - * Marshal a `libcoopgamma_ramps8_t`, `libcoopgamma_ramps16_t`, `libcoopgamma_ramps32_t`, - * `libcoopgamma_ramps64_t`, `libcoopgamma_rampsf_t`, or `libcoopgamma_rampsd_t` into a buffer - * - * @param this The record to marshal - * @param vbuf The output buffer, `NULL` to only measure - * how large this buffer has to be - * @param width The `sizeof(*(this->red))` - * @return The number of marshalled bytes, or if `buf == NULL`, - * how many bytes would be marshalled if `buf != NULL` - */ -size_t -libcoopgamma_ramps_marshal_(const void *restrict this, void *restrict vbuf, size_t width) -{ - const libcoopgamma_ramps8_t *restrict this8 = (const libcoopgamma_ramps8_t *restrict)this; - MARSHAL_PROLOGUE; - marshal_version(LIBCOOPGAMMA_RAMPS_VERSION); - marshal_prim(this8->red_size, size_t); - marshal_prim(this8->green_size, size_t); - marshal_prim(this8->blue_size, size_t); - marshal_buffer(this8->red, (this8->red_size + this8->green_size + this8->blue_size) * width); - MARSHAL_EPILOGUE; -} - - -/** - * Unmarshal a `libcoopgamma_ramps8_t`, `libcoopgamma_ramps16_t`, `libcoopgamma_ramps32_t`, - * `libcoopgamma_ramps64_t`, `libcoopgamma_rampsf_t`, or `libcoopgamma_rampsd_t` from a buffer - * - * @param this The output parameter for unmarshalled record - * @param vbuf The buffer with the marshalled record - * @param np Output parameter for the number of unmarshalled bytes, undefined on failure - * @param width The `sizeof(*(this->red))` - * @return `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`, - * `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET` - */ -int -libcoopgamma_ramps_unmarshal_(void *restrict this, const void *restrict vbuf, - size_t *restrict np, size_t width) -{ - libcoopgamma_ramps8_t *restrict this8 = (libcoopgamma_ramps8_t *restrict)this; - UNMARSHAL_PROLOGUE; - unmarshal_version(LIBCOOPGAMMA_RAMPS_VERSION); - unmarshal_prim(this8->red_size, size_t); - unmarshal_prim(this8->green_size, size_t); - unmarshal_prim(this8->blue_size, size_t); - unmarshal_buffer(this8->red, (this8->red_size + this8->green_size + this8->blue_size) * width); - this8->green = this8->red + this8->red_size * width; - this8->blue = this8->green + this8->green_size * width; - UNMARSHAL_EPILOGUE; -} - - - -/** - * Initialise a `libcoopgamma_filter_t` - * - * @param this The record to initialise - * @return Zero on success, -1 on error - */ -int -libcoopgamma_filter_initialise(libcoopgamma_filter_t *restrict this) -{ - memset(this, 0, sizeof(*this)); - return 0; -} - - -/** - * Release all resources allocated to a `libcoopgamma_filter_t`, - * the allocation of the record itself is not freed - * - * Always call this function after failed call to `libcoopgamma_filter_initialise` - * or failed call to `libcoopgamma_filter_unmarshal` - * - * @param this The record to destroy - */ -void -libcoopgamma_filter_destroy(libcoopgamma_filter_t *restrict this) -{ - free(this->crtc); - free(this->class); - free(this->ramps.u8.red); - memset(this, 0, sizeof(*this)); -} - - -/** - * Marshal a `libcoopgamma_filter_t` into a buffer - * - * @param this The record to marshal - * @param vbuf The output buffer, `NULL` to only measure - * how large this buffer has to be - * @return The number of marshalled bytes, or if `buf == NULL`, - * how many bytes would be marshalled if `buf != NULL` - */ -size_t -libcoopgamma_filter_marshal(const libcoopgamma_filter_t *restrict this, void *restrict vbuf) -{ - MARSHAL_PROLOGUE; - marshal_version(LIBCOOPGAMMA_FILTER_VERSION); - marshal_version(LIBCOOPGAMMA_DEPTH_VERSION); - marshal_version(LIBCOOPGAMMA_LIFESPAN_VERSION); - marshal_prim(this->depth, libcoopgamma_depth_t); - marshal_prim(this->priority, int64_t); - marshal_string(this->crtc); - marshal_string(this->class); - marshal_prim(this->lifespan, libcoopgamma_lifespan_t); - switch (this->depth) { - case LIBCOOPGAMMA_UINT8: off += libcoopgamma_ramps_marshal(&this->ramps.u8, SUBBUF); break; - case LIBCOOPGAMMA_UINT16: off += libcoopgamma_ramps_marshal(&this->ramps.u16, SUBBUF); break; - case LIBCOOPGAMMA_UINT32: off += libcoopgamma_ramps_marshal(&this->ramps.u32, SUBBUF); break; - case LIBCOOPGAMMA_UINT64: off += libcoopgamma_ramps_marshal(&this->ramps.u64, SUBBUF); break; - case LIBCOOPGAMMA_FLOAT: off += libcoopgamma_ramps_marshal(&this->ramps.f, SUBBUF); break; - case LIBCOOPGAMMA_DOUBLE: off += libcoopgamma_ramps_marshal(&this->ramps.d, SUBBUF); break; - default: - break; - } - MARSHAL_EPILOGUE; -} - - -/** - * Unmarshal a `libcoopgamma_filter_t` from a buffer - * - * @param this The output parameter for unmarshalled record - * @param vbuf The buffer with the marshalled record - * @param np Output parameter for the number of unmarshalled bytes, undefined on failure - * @return `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`, - * `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET` - */ -int -libcoopgamma_filter_unmarshal(libcoopgamma_filter_t *restrict this, const void *restrict vbuf, size_t *restrict np) -{ - int r = LIBCOOPGAMMA_SUCCESS; - size_t n = 0; - UNMARSHAL_PROLOGUE; - memset(this, 0, sizeof(*this)); - unmarshal_version(LIBCOOPGAMMA_FILTER_VERSION); - unmarshal_version(LIBCOOPGAMMA_DEPTH_VERSION); - unmarshal_version(LIBCOOPGAMMA_LIFESPAN_VERSION); - unmarshal_prim(this->depth, libcoopgamma_depth_t); - unmarshal_prim(this->priority, int64_t); - unmarshal_string(this->crtc); - unmarshal_string(this->class); - unmarshal_prim(this->lifespan, libcoopgamma_lifespan_t); - switch (this->depth) { - case LIBCOOPGAMMA_UINT8: r = libcoopgamma_ramps_unmarshal(&(this->ramps.u8), NNSUBBUF, &n); break; - case LIBCOOPGAMMA_UINT16: r = libcoopgamma_ramps_unmarshal(&(this->ramps.u16), NNSUBBUF, &n); break; - case LIBCOOPGAMMA_UINT32: r = libcoopgamma_ramps_unmarshal(&(this->ramps.u32), NNSUBBUF, &n); break; - case LIBCOOPGAMMA_UINT64: r = libcoopgamma_ramps_unmarshal(&(this->ramps.u64), NNSUBBUF, &n); break; - case LIBCOOPGAMMA_FLOAT: r = libcoopgamma_ramps_unmarshal(&(this->ramps.f), NNSUBBUF, &n); break; - case LIBCOOPGAMMA_DOUBLE: r = libcoopgamma_ramps_unmarshal(&(this->ramps.d), NNSUBBUF, &n); break; - default: - break; - } - if (r != LIBCOOPGAMMA_SUCCESS) - return r; - off += n; - UNMARSHAL_EPILOGUE; -} - - - -/** - * Initialise a `libcoopgamma_crtc_info_t` - * - * @param this The record to initialise - * @return Zero on success, -1 on error - */ -int -libcoopgamma_crtc_info_initialise(libcoopgamma_crtc_info_t *restrict this) -{ - memset(this, 0, sizeof(*this)); - return 0; -} - - -/** - * Release all resources allocated to a `libcoopgamma_crtc_info_t`, - * the allocation of the record itself is not freed - * - * Always call this function after failed call to `libcoopgamma_crtc_info_initialise` - * or failed call to `libcoopgamma_crtc_info_unmarshal` - * - * @param this The record to destroy - */ -void -libcoopgamma_crtc_info_destroy(libcoopgamma_crtc_info_t *restrict this) -{ - (void) this; -} - - -/** - * Marshal a `libcoopgamma_crtc_info_t` into a buffer - * - * @param this The record to marshal - * @param vbuf The output buffer, `NULL` to only measure - * how large this buffer has to be - * @return The number of marshalled bytes, or if `buf == NULL`, - * how many bytes would be marshalled if `buf != NULL` - */ -size_t -libcoopgamma_crtc_info_marshal(const libcoopgamma_crtc_info_t *restrict this, void *restrict vbuf) -{ - MARSHAL_PROLOGUE; - marshal_version(LIBCOOPGAMMA_CRTC_INFO_VERSION); - marshal_version(LIBCOOPGAMMA_DEPTH_VERSION); - marshal_version(LIBCOOPGAMMA_SUPPORT_VERSION); - marshal_version(LIBCOOPGAMMA_COLOURSPACE_VERSION); - marshal_prim(this->cooperative, int); - marshal_prim(this->depth, libcoopgamma_depth_t); - marshal_prim(this->red_size, size_t); - marshal_prim(this->green_size, size_t); - marshal_prim(this->blue_size, size_t); - marshal_prim(this->supported, libcoopgamma_support_t); - marshal_prim(this->colourspace, libcoopgamma_colourspace_t); - marshal_prim(this->have_gamut, int); - marshal_prim(this->red_x, unsigned); - marshal_prim(this->red_y, unsigned); - marshal_prim(this->green_x, unsigned); - marshal_prim(this->green_y, unsigned); - marshal_prim(this->blue_x, unsigned); - marshal_prim(this->blue_y, unsigned); - marshal_prim(this->white_x, unsigned); - marshal_prim(this->white_y, unsigned); - MARSHAL_EPILOGUE; -} - - -/** - * Unmarshal a `libcoopgamma_crtc_info_t` from a buffer - * - * @param this The output parameter for unmarshalled record - * @param vbuf The buffer with the marshalled record - * @param np Output parameter for the number of unmarshalled bytes, undefined on failure - * @return `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`, - * `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET` - */ -int -libcoopgamma_crtc_info_unmarshal(libcoopgamma_crtc_info_t *restrict this, const void *restrict vbuf, size_t *restrict np) -{ - UNMARSHAL_PROLOGUE; - unmarshal_version(LIBCOOPGAMMA_CRTC_INFO_VERSION); - unmarshal_version(LIBCOOPGAMMA_DEPTH_VERSION); - unmarshal_version(LIBCOOPGAMMA_SUPPORT_VERSION); - unmarshal_version(LIBCOOPGAMMA_COLOURSPACE_VERSION); - unmarshal_prim(this->cooperative, int); - unmarshal_prim(this->depth, libcoopgamma_depth_t); - unmarshal_prim(this->red_size, size_t); - unmarshal_prim(this->green_size, size_t); - unmarshal_prim(this->blue_size, size_t); - unmarshal_prim(this->supported, libcoopgamma_support_t); - unmarshal_prim(this->colourspace, libcoopgamma_colourspace_t); - unmarshal_prim(this->have_gamut, int); - unmarshal_prim(this->red_x, unsigned); - unmarshal_prim(this->red_y, unsigned); - unmarshal_prim(this->green_x, unsigned); - unmarshal_prim(this->green_y, unsigned); - unmarshal_prim(this->blue_x, unsigned); - unmarshal_prim(this->blue_y, unsigned); - unmarshal_prim(this->white_x, unsigned); - unmarshal_prim(this->white_y, unsigned); - UNMARSHAL_EPILOGUE; -} - - - -/** - * Initialise a `libcoopgamma_filter_query_t` - * - * @param this The record to initialise - * @return Zero on success, -1 on error - */ -int -libcoopgamma_filter_query_initialise(libcoopgamma_filter_query_t *restrict this) -{ - this->crtc = NULL; - this->coalesce = 0; - this->high_priority = INT64_MAX; - this->low_priority = INT64_MIN; - return 0; -} - - -/** - * Release all resources allocated to a `libcoopgamma_filter_query_t`, - * the allocation of the record itself is not freed - * - * Always call this function after failed call to `libcoopgamma_filter_query_initialise` - * or failed call to `libcoopgamma_filter_query_unmarshal` - * - * @param this The record to destroy - */ -void -libcoopgamma_filter_query_destroy(libcoopgamma_filter_query_t *restrict this) -{ - free(this->crtc); - this->crtc = NULL; -} - - -/** - * Marshal a `libcoopgamma_filter_query_t` into a buffer - * - * @param this The record to marshal - * @param vbuf The output buffer, `NULL` to only measure - * how large this buffer has to be - * @return The number of marshalled bytes, or if `buf == NULL`, - * how many bytes would be marshalled if `buf != NULL` - */ -size_t -libcoopgamma_filter_query_marshal(const libcoopgamma_filter_query_t *restrict this, void* restrict vbuf) -{ - MARSHAL_PROLOGUE; - marshal_version(LIBCOOPGAMMA_FILTER_QUERY_VERSION); - marshal_string(this->crtc); - marshal_prim(this->coalesce, int); - marshal_prim(this->high_priority, int64_t); - marshal_prim(this->low_priority, int64_t); - MARSHAL_EPILOGUE; -} - - -/** - * Unmarshal a `libcoopgamma_filter_query_t` from a buffer - * - * @param this The output parameter for unmarshalled record - * @param vbuf The buffer with the marshalled record - * @param np Output parameter for the number of unmarshalled bytes, undefined on failure - * @return `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`, - * `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET` - */ -int -libcoopgamma_filter_query_unmarshal(libcoopgamma_filter_query_t *restrict this, const void *restrict vbuf, size_t *restrict np) -{ - UNMARSHAL_PROLOGUE; - this->crtc = NULL; - unmarshal_version(LIBCOOPGAMMA_FILTER_QUERY_VERSION); - unmarshal_string(this->crtc); - unmarshal_prim(this->coalesce, int); - unmarshal_prim(this->high_priority, int64_t); - unmarshal_prim(this->low_priority, int64_t); - UNMARSHAL_EPILOGUE; -} - - - -/** - * Initialise a `libcoopgamma_queried_filter_t` - * - * @param this The record to initialise - * @return Zero on success, -1 on error - */ -int -libcoopgamma_queried_filter_initialise(libcoopgamma_queried_filter_t *restrict this) -{ - memset(this, 0, sizeof(*this)); - return 0; -} - - -/** - * Release all resources allocated to a `libcoopgamma_queried_filter_t`, - * the allocation of the record itself is not freed - * - * Always call this function after failed call to `libcoopgamma_queried_filter_initialise` - * or failed call to `libcoopgamma_queried_filter_unmarshal` - * - * @param this The record to destroy - */ -void -libcoopgamma_queried_filter_destroy(libcoopgamma_queried_filter_t *restrict this) -{ - free(this->class); - this->class = NULL; - libcoopgamma_ramps_destroy(&this->ramps.u8); -} - - -/** - * Marshal a `libcoopgamma_queried_filter_t` into a buffer - * - * @param this The record to marshal - * @param vbuf The output buffer, `NULL` to only measure - * how large this buffer has to be - * @param depth The type used of ramp stops - * @return The number of marshalled bytes, or if `buf == NULL`, - * how many bytes would be marshalled if `buf != NULL` - */ -size_t -libcoopgamma_queried_filter_marshal(const libcoopgamma_queried_filter_t *restrict this, - void *restrict vbuf, libcoopgamma_depth_t depth) -{ - MARSHAL_PROLOGUE; - marshal_version(LIBCOOPGAMMA_QUERIED_FILTER_VERSION); - marshal_prim(this->priority, int64_t); - marshal_string(this->class); - switch (depth) { - case LIBCOOPGAMMA_UINT8: off += libcoopgamma_ramps_marshal(&this->ramps.u8, SUBBUF); break; - case LIBCOOPGAMMA_UINT16: off += libcoopgamma_ramps_marshal(&this->ramps.u16, SUBBUF); break; - case LIBCOOPGAMMA_UINT32: off += libcoopgamma_ramps_marshal(&this->ramps.u32, SUBBUF); break; - case LIBCOOPGAMMA_UINT64: off += libcoopgamma_ramps_marshal(&this->ramps.u64, SUBBUF); break; - case LIBCOOPGAMMA_FLOAT: off += libcoopgamma_ramps_marshal(&this->ramps.f, SUBBUF); break; - case LIBCOOPGAMMA_DOUBLE: off += libcoopgamma_ramps_marshal(&this->ramps.d, SUBBUF); break; - default: - break; - } - MARSHAL_EPILOGUE; -} - - -/** - * Unmarshal a `libcoopgamma_queried_filter_t` from a buffer - * - * @param this The output parameter for unmarshalled record - * @param vbuf The buffer with the marshalled record - * @param np Output parameter for the number of unmarshalled bytes, undefined on failure - * @param depth The type used of ramp stops - * @return `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`, - * `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET` - */ -int libcoopgamma_queried_filter_unmarshal(libcoopgamma_queried_filter_t *restrict this, const void *restrict vbuf, - size_t *restrict np, libcoopgamma_depth_t depth) -{ - int r = LIBCOOPGAMMA_SUCCESS; - size_t n = 0; - UNMARSHAL_PROLOGUE; - memset(this, 0, sizeof(*this)); - unmarshal_version(LIBCOOPGAMMA_QUERIED_FILTER_VERSION); - unmarshal_prim(this->priority, int64_t); - unmarshal_string(this->class); - switch (depth) { - case LIBCOOPGAMMA_UINT8: r = libcoopgamma_ramps_unmarshal(&this->ramps.u8, NNSUBBUF, &n); break; - case LIBCOOPGAMMA_UINT16: r = libcoopgamma_ramps_unmarshal(&this->ramps.u16, NNSUBBUF, &n); break; - case LIBCOOPGAMMA_UINT32: r = libcoopgamma_ramps_unmarshal(&this->ramps.u32, NNSUBBUF, &n); break; - case LIBCOOPGAMMA_UINT64: r = libcoopgamma_ramps_unmarshal(&this->ramps.u64, NNSUBBUF, &n); break; - case LIBCOOPGAMMA_FLOAT: r = libcoopgamma_ramps_unmarshal(&this->ramps.f, NNSUBBUF, &n); break; - case LIBCOOPGAMMA_DOUBLE: r = libcoopgamma_ramps_unmarshal(&this->ramps.d, NNSUBBUF, &n); break; - default: - break; - } - if (r != LIBCOOPGAMMA_SUCCESS) - return r; - off += n; - UNMARSHAL_EPILOGUE; -} - - - -/** - * Initialise a `libcoopgamma_filter_table_t` - * - * @param this The record to initialise - * @return Zero on success, -1 on error - */ -int -libcoopgamma_filter_table_initialise(libcoopgamma_filter_table_t *restrict this) -{ - memset(this, 0, sizeof(*this)); - return 0; -} - - -/** - * Release all resources allocated to a `libcoopgamma_filter_table_t`, - * the allocation of the record itself is not freed - * - * Always call this function after failed call to `libcoopgamma_filter_table_initialise` - * or failed call to `libcoopgamma_filter_table_unmarshal` - * - * @param this The record to destroy - */ -void -libcoopgamma_filter_table_destroy(libcoopgamma_filter_table_t *restrict this) -{ - while (this->filter_count) - libcoopgamma_queried_filter_destroy(this->filters + --this->filter_count); - free(this->filters); - this->filters = NULL; -} - - -/** - * Marshal a `libcoopgamma_filter_table_t` into a buffer - * - * @param this The record to marshal - * @param vbuf The output buffer, `NULL` to only measure - * how large this buffer has to be - * @return The number of marshalled bytes, or if `buf == NULL`, - * how many bytes would be marshalled if `buf != NULL` - */ -size_t -libcoopgamma_filter_table_marshal(const libcoopgamma_filter_table_t *restrict this, void *restrict vbuf) -{ - size_t i; - MARSHAL_PROLOGUE; - marshal_version(LIBCOOPGAMMA_FILTER_TABLE_VERSION); - marshal_version(LIBCOOPGAMMA_DEPTH_VERSION); - marshal_prim(this->depth, libcoopgamma_depth_t); - marshal_prim(this->red_size, size_t); - marshal_prim(this->green_size, size_t); - marshal_prim(this->blue_size, size_t); - marshal_prim(this->filter_count, size_t); - for (i = 0; i < this->filter_count; i++) - off += libcoopgamma_queried_filter_marshal(&this->filters[i], SUBBUF, this->depth); - MARSHAL_EPILOGUE; -} - - -/** - * Unmarshal a `libcoopgamma_filter_table_t` from a buffer - * - * @param this The output parameter for unmarshalled record - * @param vbuf The buffer with the marshalled record - * @param np Output parameter for the number of unmarshalled bytes, undefined on failure - * @return `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`, - * `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET` - */ -int -libcoopgamma_filter_table_unmarshal(libcoopgamma_filter_table_t *restrict this, const void *restrict vbuf, size_t *restrict np) -{ - size_t i, n, fn; - int r; - UNMARSHAL_PROLOGUE; - this->filter_count = 0; - this->filters = NULL; - unmarshal_version(LIBCOOPGAMMA_FILTER_TABLE_VERSION); - unmarshal_version(LIBCOOPGAMMA_DEPTH_VERSION); - unmarshal_prim(this->depth, libcoopgamma_depth_t); - unmarshal_prim(this->red_size, size_t); - unmarshal_prim(this->green_size, size_t); - unmarshal_prim(this->blue_size, size_t); - unmarshal_prim(fn, size_t); - this->filters = malloc(fn * sizeof(*this->filters)); - if (!this->filters) - return LIBCOOPGAMMA_ERRNO_SET; - for (i = 0; i < fn; i++) { - r = libcoopgamma_queried_filter_unmarshal(&this->filters[i], NNSUBBUF, &n, this->depth); - if (r != LIBCOOPGAMMA_SUCCESS) - return r; - off += n; - this->filter_count += 1; - } - UNMARSHAL_EPILOGUE; -} - - - -/** - * Initialise a `libcoopgamma_error_t` - * - * @param this The record to initialise - * @return Zero on success, -1 on error - */ -int -libcoopgamma_error_initialise(libcoopgamma_error_t *restrict this) -{ - this->number = 0; - this->custom = 0; - this->description = NULL; - return 0; -} - - -/** - * Release all resources allocated to a `libcoopgamma_error_t`, - * the allocation of the record itself is not freed - * - * Always call this function after failed call to `libcoopgamma_error_initialise` - * or failed call to `libcoopgamma_error_unmarshal` - * - * @param this The record to destroy - */ -void -libcoopgamma_error_destroy(libcoopgamma_error_t *restrict this) -{ - free(this->description); - this->description = NULL; -} - - -/** - * Marshal a `libcoopgamma_error_t` into a buffer - * - * @param this The record to marshal - * @param vbuf The output buffer, `NULL` to only measure - * how large this buffer has to be - * @return The number of marshalled bytes, or if `buf == NULL`, - * how many bytes would be marshalled if `buf != NULL` - */ -size_t -libcoopgamma_error_marshal(const libcoopgamma_error_t *restrict this, void *restrict vbuf) -{ - MARSHAL_PROLOGUE; - marshal_version(LIBCOOPGAMMA_ERROR_VERSION); - marshal_prim(this->number, uint64_t); - marshal_prim(this->custom, int); - marshal_prim(this->server_side, int); - marshal_string(this->description); - MARSHAL_EPILOGUE; -} - - -/** - * Unmarshal a `libcoopgamma_error_t` from a buffer - * - * @param this The output parameter for unmarshalled record - * @param vbuf The buffer with the marshalled record - * @param np Output parameter for the number of unmarshalled bytes, undefined on failure - * @return `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`, - * `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET` - */ -int -libcoopgamma_error_unmarshal(libcoopgamma_error_t *restrict this, const void *restrict vbuf, size_t *restrict np) -{ - UNMARSHAL_PROLOGUE; - this->description = NULL; - unmarshal_version(LIBCOOPGAMMA_ERROR_VERSION); - unmarshal_prim(this->number, uint64_t); - unmarshal_prim(this->custom, int); - unmarshal_prim(this->server_side, int); - unmarshal_string(this->description); - UNMARSHAL_EPILOGUE; -} - - - -/** - * Initialise a `libcoopgamma_context_t` - * - * @param this The record to initialise - * @return Zero on success, -1 on error - */ -int -libcoopgamma_context_initialise(libcoopgamma_context_t *restrict this) -{ - memset(this, 0, sizeof(*this)); - this->fd = -1; - this->blocking = 1; - return 0; -} - - -/** - * Release all resources allocated to a `libcoopgamma_context_t`, - * the allocation of the record itself is not freed - * - * Always call this function after failed call to `libcoopgamma_context_initialise` - * or failed call to `libcoopgamma_context_unmarshal` - * - * @param this The record to destroy - * @param disconnect Disconnect from the server? - */ -void -libcoopgamma_context_destroy(libcoopgamma_context_t *restrict this, int disconnect) -{ - if (disconnect && this->fd >= 0) { - shutdown(this->fd, SHUT_RDWR); - close(this->fd); - } - this->fd = -1; - libcoopgamma_error_destroy(&this->error); - free(this->outbound); - free(this->inbound); - this->outbound = NULL; - this->inbound = NULL; -} - - -/** - * Marshal a `libcoopgamma_context_t` into a buffer - * - * @param this The record to marshal - * @param vbuf The output buffer, `NULL` to only measure - * how large this buffer has to be - * @return The number of marshalled bytes, or if `buf == NULL`, - * how many bytes would be marshalled if `buf != NULL` - */ -size_t -libcoopgamma_context_marshal(const libcoopgamma_context_t *restrict this, void *restrict vbuf) -{ - MARSHAL_PROLOGUE; - marshal_version(LIBCOOPGAMMA_CONTEXT_VERSION); - marshal_prim(this->fd, int); - off += libcoopgamma_error_marshal(&this->error, SUBBUF); - marshal_prim(this->message_id, uint32_t); - marshal_prim(this->outbound_head - this->outbound_tail, size_t); - marshal_buffer(this->outbound + this->outbound_tail, this->outbound_head - this->outbound_tail); - marshal_prim(this->inbound_head - this->inbound_tail, size_t); - marshal_buffer(this->inbound + this->inbound_tail, this->inbound_head - this->inbound_tail); - marshal_prim(this->length, size_t); - marshal_prim(this->curline, size_t); - marshal_prim(this->in_response_to, uint32_t); - marshal_prim(this->have_all_headers, int); - marshal_prim(this->bad_message, int); - marshal_prim(this->blocking, int); - MARSHAL_EPILOGUE; -} - - -/** - * Unmarshal a `libcoopgamma_context_t` from a buffer - * - * @param this The output parameter for unmarshalled record - * @param vbuf The buffer with the marshalled record - * @param np Output parameter for the number of unmarshalled bytes, undefined on failure - * @return `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`, - * `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET` - */ -int -libcoopgamma_context_unmarshal(libcoopgamma_context_t *restrict this, const void *restrict vbuf, size_t *restrict np) -{ - size_t n; - int r; - UNMARSHAL_PROLOGUE; - memset(this, 0, sizeof(*this)); - unmarshal_version(LIBCOOPGAMMA_CONTEXT_VERSION); - unmarshal_prim(this->fd, int); - r = libcoopgamma_error_unmarshal(&this->error, NNSUBBUF, &n); - if (r != LIBCOOPGAMMA_SUCCESS) - return r; - off += n; - unmarshal_prim(this->message_id, uint32_t); - unmarshal_prim(this->outbound_head, size_t); - this->outbound_size = this->outbound_head; - unmarshal_buffer(this->outbound, this->outbound_head); - unmarshal_prim(this->inbound_head, size_t); - this->inbound_size = this->inbound_head; - unmarshal_buffer(this->inbound, this->inbound_head); - unmarshal_prim(this->length, size_t); - unmarshal_prim(this->curline, size_t); - unmarshal_prim(this->in_response_to, uint32_t); - unmarshal_prim(this->have_all_headers, int); - unmarshal_prim(this->bad_message, int); - unmarshal_prim(this->blocking, int); - UNMARSHAL_EPILOGUE; -} - - - -/** - * Initialise a `libcoopgamma_async_context_t` - * - * @param this The record to initialise - * @return Zero on success, -1 on error - */ -int -libcoopgamma_async_context_initialise(libcoopgamma_async_context_t *restrict this) -{ - this->message_id = 0; - this->coalesce = 0; - return 0; -} - - -/** - * Release all resources allocated to a `libcoopgamma_async_context_t`, - * the allocation of the record itself is not freed - * - * Always call this function after failed call to `libcoopgamma_async_context_initialise` - * or failed call to `libcoopgamma_async_context_unmarshal` - * - * @param this The record to destroy - */ -void -libcoopgamma_async_context_destroy(libcoopgamma_async_context_t *restrict this) -{ - (void) this; -} - - -/** - * Marshal a `libcoopgamma_async_context_t` into a buffer - * - * @param this The record to marshal - * @param vbuf The output buffer, `NULL` to only measure - * how large this buffer has to be - * @return The number of marshalled bytes, or if `buf == NULL`, - * how many bytes would be marshalled if `buf != NULL` - */ -size_t -libcoopgamma_async_context_marshal(const libcoopgamma_async_context_t *restrict this, void *restrict vbuf) -{ - MARSHAL_PROLOGUE; - marshal_version(LIBCOOPGAMMA_ASYNC_CONTEXT_VERSION); - marshal_prim(this->message_id, uint32_t); - marshal_prim(this->coalesce, int); - MARSHAL_EPILOGUE; -} - - -/** - * Unmarshal a `libcoopgamma_async_context_t` from a buffer - * - * @param this The output parameter for unmarshalled record - * @param vbuf The buffer with the marshalled record - * @param np Output parameter for the number of unmarshalled bytes, undefined on failure - * @return `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`, - * `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET` - */ -int -libcoopgamma_async_context_unmarshal(libcoopgamma_async_context_t *restrict this, const void *restrict vbuf, size_t *restrict np) -{ - UNMARSHAL_PROLOGUE; - unmarshal_version(LIBCOOPGAMMA_ASYNC_CONTEXT_VERSION); - unmarshal_prim(this->message_id, uint32_t); - unmarshal_prim(this->coalesce, int); - UNMARSHAL_EPILOGUE; -} - - - -/** - * List all recognised adjustment method - * - * SIGCHLD must not be ignored or blocked - * - * @return A `NULL`-terminated list of names. You should only free - * the outer pointer, inner pointers are subpointers of the - * outer pointer and cannot be freed. `NULL` on error. - */ -char ** -libcoopgamma_get_methods(void) -{ - char num[5]; /* The size is base on the fact that we have limited `n` in the loop below */ - char **methods = NULL; - char *method; - char **rc; - char *buffer; - int n = 0; - size_t size = 0; - void *new; - - methods = malloc(4 * sizeof(*methods)); - if (!methods) - goto fail; - - for (n = 0; n < 10000 /* just to be safe */; n++) { - if (n >= 4 && (n & -n) == n) { - new = realloc(methods, (size_t)(n << 1) * sizeof(*methods)); - if (!new) - goto fail; - methods = new; - } - sprintf(num, "%i", n); - if (libcoopgamma_get_method_and_site(num, NULL, &method, NULL)) - goto fail; - if (!strcmp(method, num)) { - free(method); - break; - } - methods[n] = method; - size += strlen(method) + 1; - } - - rc = malloc((size_t)(n + 1) * sizeof(char *) + size); - if (!rc) - goto fail; - buffer = ((char *)rc) + (size_t)(n + 1) * sizeof(char *); - rc[n] = NULL; - while (n--) { - rc[n] = buffer; - buffer = stpcpy(buffer, methods[n]) + 1; - free(methods[n]); - } - free(methods); - - return rc; - -fail: - while (n--) - free(methods[n]); - free(methods); - return NULL; -} - - -/** - * Run coopgammad with -q or -qq and return the response - * - * SIGCHLD must not be ignored or blocked - * - * @param method The adjustment method, `NULL` for automatic - * @param site The site, `NULL` for automatic - * @param arg "-q" or "-qq", which shall be passed to coopgammad? - * @return The output of coopgammad, `NULL` on error. This - * will be NUL-terminated and will not contain any - * other `NUL` bytes. - */ -static char * -libcoopgamma_query(const char *restrict method, const char *restrict site, const char *restrict arg) -{ - const char *(args[7]) = {COOPGAMMAD, arg}; - size_t i = 2, n = 0, size = 0; - int pipe_rw[2] = { -1, -1 }; - pid_t pid; - int saved_errno, status; - char *msg = NULL; - ssize_t got; - void *new; - - if (method) args[i++] = "-m", args[i++] = method; - if (site) args[i++] = "-s", args[i++] = site; - args[i] = NULL; - - if (pipe(pipe_rw) < 0) - goto fail; - - switch ((pid = fork())) { - case -1: - goto fail; - - case 0: - /* Child */ - close(pipe_rw[0]); - if (pipe_rw[1] != STDOUT_FILENO) { - close(STDOUT_FILENO); - if (dup2(pipe_rw[1], STDOUT_FILENO) < 0) - goto fail_child; - close(pipe_rw[1]); - } -#if defined(__GNUC__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcast-qual" -#endif - execvp(COOPGAMMAD, (char* const*)(args)); -#if defined(__GNUC__) -# pragma GCC diagnostic pop -#endif - fail_child: - saved_errno = errno; - perror(NAME_OF_THE_PROCESS); - if (write(STDOUT_FILENO, &saved_errno, sizeof(int)) != sizeof(int)) - perror(NAME_OF_THE_PROCESS); - exit(1); - - default: - /* Parent */ - close(pipe_rw[1]), pipe_rw[1] = -1; - for (;;) { - if (n == size) { - new = realloc(msg, size = (n ? (n << 1) : 256)); - if (!new) - goto fail; - msg = new; - } - got = read(pipe_rw[0], msg + n, size - n); - if (got < 0) { - if (errno == EINTR) - continue; - goto fail; - } else if (got == 0) { - break; - } - n += (size_t)got; - } - close(pipe_rw[0]), pipe_rw[0] = -1; - if (waitpid(pid, &status, 0) < 0) - goto fail; - if (status) { - errno = EINVAL; - if (n == sizeof(int) && *(int *)msg) - errno = *(int*)msg; - } - break; - } - - if (n == size) { - new = realloc(msg, n + 1); - if (!new) - goto fail; - msg = new; - } - msg[n] = '\0'; - - if (strchr(msg, '\0') != msg + n) { - errno = EBADMSG; - goto fail; - } - - return msg; - -fail: - saved_errno = errno; - if (pipe_rw[0] >= 0) - close(pipe_rw[0]); - if (pipe_rw[1] >= 0) - close(pipe_rw[1]); - free(msg); - errno = saved_errno; - return NULL; -} - - -/** - * Get the adjustment method and site - * - * SIGCHLD must not be ignored or blocked - * - * @param method The adjustment method, `NULL` for automatic - * @param site The site, `NULL` for automatic - * @param methodp Output pointer for the selected adjustment method, - * which cannot be `NULL`. It is safe to call - * this function with this parameter set to `NULL`. - * @param sitep Output pointer for the selected site, which will - * be `NULL` the method only supports one site or if - * `site == NULL` and no site can be selected - * automatically. It is safe to call this function - * with this parameter set to `NULL`. - * @return Zero on success, -1 on error - */ -int -libcoopgamma_get_method_and_site(const char *restrict method, const char *restrict site, - char **restrict methodp, char **restrict sitep) -{ - int saved_errno; - char *raw; - char *p; - char *q; - - raw = libcoopgamma_query(method, site, "-q"); - if (!raw) - return -1; - - if (methodp) *methodp = NULL; - if (sitep) *sitep = NULL; - - p = strchr(raw, '\n'); - if (!p) { - errno = EBADMSG; - goto fail; - } - *p++ = '\0'; - - if (methodp) { - *methodp = malloc(strlen(raw) + 1); - if (!*methodp) - goto fail; - strcpy(*methodp, raw); - } - - if (site && *(q = strchr(p, '\0') - 1)) { - if (*q != '\n') { - errno = EBADMSG; - goto fail; - } - *q = '\0'; - *sitep = malloc(strlen(p) + 1); - if (!*sitep) - goto fail; - strcpy(*sitep, p); - } - - free(raw); - return 0; - -fail: - saved_errno = errno; - if (methodp) { - free(*methodp); - *methodp = NULL; - } - free(raw); - errno = saved_errno; - return -1; -} - - -/** - * Get the PID file of the coopgamma server - * - * SIGCHLD must not be ignored or blocked - * - * @param method The adjustment method, `NULL` for automatic - * @param site The site, `NULL` for automatic - * @return The pathname of the server's PID file, `NULL` on error - * or if there server does not use PID files. The later - * case is detected by checking that `errno` is set to 0. - */ -char * -libcoopgamma_get_pid_file(const char *restrict method, const char *restrict site) -{ - char *path; - size_t n; - - path = libcoopgamma_get_socket_file(method, site); - if (!path) - return NULL; - - n = strlen(path); - if (n < 7 || strcmp(path + n - 7, ".socket")) { - free(path); - errno = EBADMSG; - return NULL; - } - - strcpy(path + n - 7, ".pid"); - return path; -} - - -/** - * Get the socket file of the coopgamma server - * - * SIGCHLD must not be ignored or blocked - * - * @param method The adjustment method, `NULL` for automatic - * @param site The site, `NULL` for automatic - * @return The pathname of the server's socket, `NULL` on error - * or if there server does have its own socket. The later - * case is detected by checking that `errno` is set to 0, - * and is the case when communicating with a server in a - * multi-server display server like mds. - */ -char * -libcoopgamma_get_socket_file(const char *restrict method, const char *restrict site) -{ - char *raw; - char *p; - - raw = libcoopgamma_query(method, site, "-qq"); - if (!raw) - return NULL; - - p = strchr(raw, '\0') - 1; - if (p < raw || *p != '\n') { - errno = EBADMSG; - goto fail; - } - *p = '\0'; - if (!*raw) { - errno = EBADMSG; - goto fail; - } - - return raw; -fail: - free(raw); - return NULL; -} - - - -/** - * Connect to a coopgamma server, and start it if necessary - * - * Use `libcoopgamma_context_destroy` to disconnect - * - * SIGCHLD must not be ignored or blocked - * - * @param method The adjustment method, `NULL` for automatic - * @param site The site, `NULL` for automatic - * @param ctx The state of the library, must be initialised - * @return Zero on success, -1 on error. On error, `errno` is set - * to 0 if the server could not be initialised. - */ -int -libcoopgamma_connect(const char *restrict method, const char *restrict site, libcoopgamma_context_t *restrict ctx) -{ - const char *(args[6]) = {COOPGAMMAD}; - struct sockaddr_un address; - char* path; - int saved_errno; - int tries = 0, status; - pid_t pid; - size_t i = 1; - - ctx->blocking = 1; - - if (method) args[i++] = "-m", args[i++] = method; - if (site) args[i++] = "-s", args[i++] = site; - args[i] = NULL; - - path = libcoopgamma_get_socket_file(method, site); - if (!path) - return -1; - if (strlen(path) >= sizeof(address.sun_path)) { - free(path); - errno = ENAMETOOLONG; - return -1; - } - - address.sun_family = AF_UNIX; - strcpy(address.sun_path, path); - free(path); - if ((ctx->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) - return -1; - -retry: - if (connect(ctx->fd, (struct sockaddr *)&address, (socklen_t)sizeof(address)) < 0) { - if ((errno == ECONNREFUSED || errno == ENOENT || errno == ENOTDIR) && !tries++) { - switch ((pid = fork())) { - case -1: - goto fail; - - case 0: - /* Child */ - close(ctx->fd); -#if defined(__GNUC__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcast-qual" -#endif - execvp(COOPGAMMAD, (char *const *)(args)); -#if defined(__GNUC__) -# pragma GCC diagnostic pop -#endif - perror(NAME_OF_THE_PROCESS); - exit(1); - - default: - /* Parent */ - if (waitpid(pid, &status, 0) < 0) - goto fail; - if (status) { - errno = 0; - goto fail; - } - break; - } - goto retry; - } - goto fail; - } - - return 0; - -fail: - saved_errno = errno; - close(ctx->fd); - ctx->fd = -1; - errno = saved_errno; - return -1; -} - - -/** - * By default communication is blocking, this function - * can be used to switch between blocking and nonblocking - * - * After setting the communication to nonblocking, - * `libcoopgamma_flush`, `libcoopgamma_synchronise` and - * and request-sending functions can fail with EAGAIN and - * EWOULDBLOCK. It is safe to continue with `libcoopgamma_flush` - * (for `libcoopgamma_flush` it selfand equest-sending functions) - * or `libcoopgamma_synchronise` just like EINTR failure. - * - * @param ctx The state of the library, must be connected - * @param nonblocking Nonblocking mode? - * @return Zero on success, -1 on error - */ -int -libcoopgamma_set_nonblocking(libcoopgamma_context_t *restrict ctx, int nonblocking) -{ - int flags = fcntl(ctx->fd, F_GETFL); - if (flags == -1) - return -1; - if (nonblocking) - flags |= O_NONBLOCK; - else - flags &= ~O_NONBLOCK; - if (fcntl(ctx->fd, F_SETFL, flags) == -1) - return -1; - ctx->blocking = !nonblocking; - return 0; -} - - -/** - * Send all pending outbound data - * - * If this function or another function that sends a request - * to the server fails with EINTR, call this function to - * complete the transfer. The `async` parameter will always - * be in a properly configured state if a function fails - * with EINTR. - * - * @param ctx The state of the library, must be connected - * @return Zero on success, -1 on error - */ -int -libcoopgamma_flush(libcoopgamma_context_t *restrict ctx) -{ - ssize_t sent; - size_t chunksize = ctx->outbound_head - ctx->outbound_tail; - size_t sendsize; - - while (ctx->outbound_tail < ctx->outbound_head) { - sendsize = ctx->outbound_head - ctx->outbound_tail; - sendsize = sendsize < chunksize ? sendsize : chunksize; - sent = send(ctx->fd, ctx->outbound + ctx->outbound_tail, sendsize, MSG_NOSIGNAL); - if (sent < 0) { - if (errno == EPIPE) - errno = ECONNRESET; - if (errno != EMSGSIZE) - return -1; - if (!(chunksize >>= 1)) - return -1; - continue; - } - -#ifdef DEBUG_MODE - fprintf(stderr, "\033[31m"); - fwrite(ctx->outbound + ctx->outbound_tail, (size_t)sent, 1, stderr); - fprintf(stderr, "\033[m"); - fflush(stderr); -#endif - - ctx->outbound_tail += (size_t)sent; - } - - return 0; -} - - -/** - * Wait for the next message to be received - * - * @param ctx The state of the library, must be connected - * @param pending Information for each pending request - * @param n The number of elements in `pending` - * @param selected The index of the element in `pending` which corresponds - * to the first inbound message, note that this only means - * that the message is not for any of the other request, - * if the message is corrupt any of the listed requests can - * be selected even if it is not for any of the requests. - * Functions that parse the message will detect such corruption. - * @return Zero on success, -1 on error. If the the message is ignored, - * which happens if corresponding `libcoopgamma_async_context_t` - * is not listed, -1 is returned and `errno` is set to 0. If -1 - * is returned, `errno` is set to `ENOTRECOVERABLE` you have - * received a corrupt message and the context has been tainted - * beyond recover. - */ -int -libcoopgamma_synchronise(libcoopgamma_context_t *restrict ctx, libcoopgamma_async_context_t *restrict pending, - size_t n, size_t *restrict selected) -{ - char temp[3 * sizeof(size_t) + 1]; - ssize_t got; - size_t i; - char *p; - char *line; - char *value; - struct pollfd pollfd; - size_t new_size; - void *new; - - if (ctx->inbound_head == ctx->inbound_tail) { - ctx->inbound_head = ctx->inbound_tail = ctx->curline = 0; - } else if (ctx->inbound_tail > 0) { - memmove(ctx->inbound, ctx->inbound + ctx->inbound_tail, ctx->inbound_head -= ctx->inbound_tail); - ctx->curline -= ctx->inbound_tail; - ctx->inbound_tail = 0; - } - - pollfd.fd = ctx->fd; - pollfd.events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; - - if (ctx->inbound_head) - goto skip_recv; - for (;;) { - if (ctx->inbound_head == ctx->inbound_size) { - new_size = ctx->inbound_size ? (ctx->inbound_size << 1) : 1024; - new = realloc(ctx->inbound, new_size); - if (!new) - return -1; - ctx->inbound = new; - ctx->inbound_size = new_size; - } - - if (ctx->blocking) { - pollfd.revents = 0; - if (poll(&pollfd, (nfds_t)1, -1) < 0) - return -1; - } - got = recv(ctx->fd, ctx->inbound + ctx->inbound_head, ctx->inbound_size - ctx->inbound_head, 0); - if (got <= 0) { - if (got == 0) - errno = ECONNRESET; - return -1; - } - -#ifdef DEBUG_MODE - fprintf(stderr, "\033[32m"); - fwrite(ctx->inbound + ctx->inbound_head, (size_t)got, 1, stderr); - fprintf(stderr, "\033[m"); - fflush(stderr); -#endif - - ctx->inbound_head += (size_t)got; - - skip_recv: - while (!ctx->have_all_headers) { - line = ctx->inbound + ctx->curline; - p = memchr(line, '\n', ctx->inbound_head - ctx->curline); - if (!p) - break; - if (memchr(line, '\0', (size_t)(p - line)) != NULL) - ctx->bad_message = 1; - *p++ = '\0'; - ctx->curline = (size_t)(p - ctx->inbound); - if (!*line) { - ctx->have_all_headers = 1; - } else if (strstr(line, "In response to: ") == line) { - value = line + (sizeof("In response to: ") - 1); - ctx->in_response_to = (uint32_t)atol(value); - } else if (strstr(line, "Length: ") == line) { - value = line + (sizeof("Length: ") - 1); - ctx->length = (size_t)atol(value); - sprintf(temp, "%zu", ctx->length); - if (strcmp(value, temp)) - goto fatal; - } - } - - if (ctx->have_all_headers && ctx->inbound_head >= ctx->curline + ctx->length) { - ctx->curline += ctx->length; - if (ctx->bad_message) { - ctx->bad_message = 0; - ctx->have_all_headers = 0; - ctx->length = 0; - ctx->inbound_tail = ctx->curline; - errno = EBADMSG; - return -1; - } - for (i = 0; i < n; i++) { - if (pending[i].message_id == ctx->in_response_to) { - *selected = i; - return 0; - } - } - *selected = 0; - ctx->bad_message = 0; - ctx->have_all_headers = 0; - ctx->length = 0; - ctx->inbound_tail = ctx->curline; - errno = 0; - return -1; - } - } - -fatal: - errno = ENOTRECOVERABLE; - return -1; -} - - -/** - * Send a message to the server and wait for response - * - * @param resp:char** Output parameter for the response, - * will be NUL-terminated - * @param ctx:libcoopgamma_context_t* The state of the library - * @param payload:void* Data to append to the end of the message - * @param payload_size:size_t Byte-size of `payload` - * @param format:string-literal Message formatting string - * @param ... Message formatting arguments - * - * On error, the macro goes to `fail`. - */ -#define SEND_MESSAGE(ctx, payload, payload_size, format, ...)\ - do {\ - ssize_t n__;\ - char *msg__;\ - snprintf(NULL, (size_t)0, format "%zn", __VA_ARGS__, &n__);\ - msg__ = malloc((size_t)n__ + (payload_size) + (size_t)1);\ - if (!msg__)\ - goto fail;\ - sprintf(msg__, format, __VA_ARGS__);\ - if (payload)\ - memcpy(msg__ + n__, (payload), (payload_size));\ - if (send_message((ctx), msg__, (size_t)n__ + (payload_size)) < 0)\ - goto fail;\ - } while (0) - - -/** - * Send a message to the server and wait for response - * - * @param ctx The state of the library - * @param msg The message to send - * @param n The length of `msg` - * @return Zero on success, -1 on error - */ -static int -send_message(libcoopgamma_context_t *restrict ctx, char *msg, size_t n) -{ - void *new; - if (ctx->outbound_head == ctx->outbound_tail) { - free(ctx->outbound); - ctx->outbound = msg; - ctx->outbound_tail = 0; - ctx->outbound_head = n; - ctx->outbound_size = n; - } else { - if (ctx->outbound_head + n > ctx->outbound_size) { - memmove(ctx->outbound, ctx->outbound + ctx->outbound_tail, ctx->outbound_head -= ctx->outbound_tail); - ctx->outbound_tail = 0; - } - if (ctx->outbound_head + n > ctx->outbound_size) { - new = realloc(ctx->outbound, ctx->outbound_head + n); - if (!new) { - free(msg); - return -1; - } - ctx->outbound = new; - ctx->outbound_size = ctx->outbound_head + n; - } - memcpy(ctx->outbound + ctx->outbound_head, msg, n); - ctx->outbound_head += n; - free(msg); - } - ctx->message_id += 1; - return libcoopgamma_flush(ctx); -} - - -/** - * Get the next header of the inbound message - * - * All headers must be read before the payload is read - * - * @param ctx The state of the library, must be connected - * @return The next header line, can never be `NULL`, - * the empty string marks the end of the headers. - * This memory segment must not be freed. - */ -static char * -next_header(libcoopgamma_context_t *restrict ctx) -{ - char *rc = ctx->inbound + ctx->inbound_tail; - ctx->inbound_tail += strlen(rc) + 1; - return rc; -} - - -/** - * Get the payload of the inbound message - * - * Calling this function marks that the inbound message - * has been fully ready. You must call this function - * even if you do not expect a payload - * - * @param ctx The state of the library, must be connected - * @param n Output parameter for the size of the payload - * @return The payload (not NUL-terminated), `NULL` if - * there is no payload. Failure is impossible. - * This memory segment must not be freed. - */ -static char * -next_payload(libcoopgamma_context_t *restrict ctx, size_t *n) -{ - char *rc; - ctx->have_all_headers = 0; - if ((*n = ctx->length)) { - rc = ctx->inbound + ctx->inbound_tail; - ctx->inbound_tail += *n; - ctx->length = 0; - return rc; - } - return NULL; -} - - -/** - * Tell the library that you will not be parsing a receive message - * - * @param ctx The state of the library, must be connected - */ -void -libcoopgamma_skip_message(libcoopgamma_context_t *restrict ctx) -{ - size_t _n; - while (*next_header(ctx)); - (void) next_payload(ctx, &_n); -} - - -/** - * Check whether the server sent an error, if so copy it to `ctx` - * - * This function will also reports EBADMSG if the message ID - * that the message is a response to does not match the request - * information, or if it is missing - * - * @param ctx The state of the library, must be connected - * @param async Information about the request - * @return 1 if the server sent an error (even indicating success), - * 0 on success, -1 on failure. Information about failure - * is copied `ctx`. - */ -static int -check_error(libcoopgamma_context_t *restrict ctx, libcoopgamma_async_context_t *restrict async) -{ - char temp[3 * sizeof(uint64_t) + 1]; - size_t old_tail = ctx->inbound_tail; - char *line; - char *value; - int command_ok = 0; - int have_in_response_to = 0; - int have_error = 0; - int bad = 0; - char *payload; - size_t n; - - for (;;) { - line = next_header(ctx); - value = strchr(line, ':') + 2; - if (!*line) { - break; - } else if (!strcmp(line, "Command: error")) { - command_ok = 1; - } else if (strstr(line, "In response to: ") == line) { - uint32_t id = (uint32_t)atol(value); - have_in_response_to = 1 + !!have_in_response_to; - if (id != async->message_id) { - bad = 1; - } else { - sprintf(temp, "%" PRIu32, id); - if (strcmp(value, temp)) - bad = 1; - } - } else if (strstr(line, "Error: ") == line) { - have_error = 1 + !!have_error; - ctx->error.server_side = 1; - ctx->error.custom = (strstr(value, "custom") == value); - if (ctx->error.custom) { - if (value[6] == '\0') { - ctx->error.number = 0; - continue; - } else if (value[6] != ' ') { - bad = 1; - continue; - } - value += 7; - } - ctx->error.number = (uint64_t)atoll(value); - sprintf(temp, "%" PRIu64, ctx->error.number); - if (strcmp(value, temp)) - bad = 1; - } - } - - if (!command_ok) { - ctx->inbound_tail = old_tail; - return 0; - } - - payload = next_payload(ctx, &n); - if (payload) { - if (memchr(payload, '\0', n) || payload[n - 1] != '\n') - goto badmsg; - ctx->error.description = malloc(n); - if (ctx->error.description == NULL) - goto fail; - memcpy(ctx->error.description, payload, n - 1); - ctx->error.description[n - 1] = '\0'; - } - - if (bad || have_in_response_to != 1 || have_error != 1) - goto badmsg; - - return 1; - -badmsg: - errno = EBADMSG; -fail: - copy_errno(ctx); - return -1; -} - - - -#if defined(__GNUC__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wnonnull" -#endif - - - -/** - * List all available CRTC:s, send request part - * - * Cannot be used before connecting to the server - * - * @param ctx The state of the library, must be connected - * @param async Information about the request, that is needed to - * identify and parse the response, is stored here - * @return Zero on success, -1 on error - */ -int -libcoopgamma_get_crtcs_send(libcoopgamma_context_t *restrict ctx, libcoopgamma_async_context_t *restrict async) -{ - async->message_id = ctx->message_id; - SEND_MESSAGE(ctx, NULL, (size_t)0, - "Command: enumerate-crtcs\n" - "Message ID: %" PRIu32 "\n" - "\n", - ctx->message_id); - - return 0; -fail: - copy_errno(ctx); - return -1; -} - - -/** - * List all available CRTC:s, receive response part - * - * @param ctx The state of the library, must be connected - * @param async Information about the request - * @return A `NULL`-terminated list of names. You should only free - * the outer pointer, inner pointers are subpointers of the - * outer pointer and cannot be freed. `NULL` on error, in - * which case `ctx->error` (rather than `errno`) is read - * for information about the error. - */ -char ** -libcoopgamma_get_crtcs_recv(libcoopgamma_context_t *restrict ctx, libcoopgamma_async_context_t *restrict async) -{ - char *line; - char *payload; - char *end; - int command_ok = 0; - size_t i, n, lines, len, length; - char **rc; - - if (check_error(ctx, async)) - return NULL; - - for (;;) { - line = next_header(ctx); - if (!*line) - break; - else if (!strcmp(line, "Command: crtc-enumeration")) - command_ok = 1; - } - - payload = next_payload(ctx, &n); - - if (!command_ok || (n > 0 && payload[n - 1] != '\n')) { - errno = EBADMSG; - copy_errno(ctx); - return NULL; - } - - line = payload; - end = payload + n; - lines = length = 0; - while (line != end) { - lines += 1; - length += len = (size_t)(strchr(line, '\n') + 1 - line); - line += len; - line[-1] = '\0'; - } - - rc = malloc((lines + 1) * sizeof(char *) + length); - if (!rc) { - copy_errno(ctx); - return NULL; - } - - line = ((char *)rc) + (lines + 1) * sizeof(char *); - memcpy(line, payload, length); - rc[lines] = NULL; - for (i = 0; i < lines; i++) { - rc[i] = line; - line = strchr(line, '\0') + 1; - } - - return rc; -} - - -/** - * List all available CRTC:s, synchronous version - * - * This is a synchronous request function, as such, - * you have to ensure that communication is blocking - * (default), and that there are not asynchronous - * requests waiting, it also means that EINTR:s are - * silently ignored and there no wait to cancel the - * operation without disconnection from the server - * - * @param ctx The state of the library, must be connected - * @return A `NULL`-terminated list of names. You should only free - * the outer pointer, inner pointers are subpointers of the - * outer pointer and cannot be freed. `NULL` on error, in - * which case `ctx->error` (rather than `errno`) is read - * for information about the error. - */ -char ** -libcoopgamma_get_crtcs_sync(libcoopgamma_context_t *restrict ctx) -{ - SYNC_CALL(libcoopgamma_get_crtcs_send(ctx, &async), - libcoopgamma_get_crtcs_recv(ctx, &async), (copy_errno(ctx), NULL)); -} - - - -/** - * Retrieve information about a CRTC:s gamma ramps, send request part - * - * Cannot be used before connecting to the server - * - * @param crtc The name of the CRTC - * @param ctx The state of the library, must be connected - * @param async Information about the request, that is needed to - * identify and parse the response, is stored here - * @return Zero on success, -1 on error - */ -int -libcoopgamma_get_gamma_info_send(const char *restrict crtc, libcoopgamma_context_t *restrict ctx, - libcoopgamma_async_context_t *restrict async) -{ -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wnonnull-compare" -#endif - if (crtc == NULL || strchr(crtc, '\n')) { - errno = EINVAL; - goto fail; - } -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic pop -#endif - - async->message_id = ctx->message_id; - SEND_MESSAGE(ctx, NULL, (size_t)0, - "Command: get-gamma-info\n" - "Message ID: %" PRIu32 "\n" - "CRTC: %s\n" - "\n", - ctx->message_id, crtc); - - return 0; -fail: - copy_errno(ctx); - return 0; -} - - -/** - * Retrieve information about a CRTC:s gamma ramps, receive response part - * - * @param info Output parameter for the information, must be initialised - * @param ctx The state of the library, must be connected - * @param async Information about the request - * @return Zero on success, -1 on error, in which case `ctx->error` - * (rather than `errno`) is read for information about the error - */ -int -libcoopgamma_get_gamma_info_recv(libcoopgamma_crtc_info_t *restrict info, libcoopgamma_context_t *restrict ctx, - libcoopgamma_async_context_t *restrict async) -{ - char temp[3 * sizeof(size_t) + 1]; - char *line; - char *value; - size_t _n; - int have_cooperative = 0, have_gamma_support = 0, have_colourspace = 0; - int have_depth = 0, have_white_x = 0, have_white_y = 0; - int have_red_size = 0, have_red_x = 0, have_red_y = 0; - int have_green_size = 0, have_green_x = 0, have_green_y = 0; - int have_blue_size = 0, have_blue_x = 0, have_blue_y = 0; - int bad = 0, r = 0, g = 0, b = 0, x = 0; - size_t *outz; - unsigned *outu; - int *have; - - if (check_error(ctx, async)) - return -1; - - info->cooperative = 0; /* Should be in the response, but ... */ - info->colourspace = LIBCOOPGAMMA_UNKNOWN; - - for (;;) { - line = next_header(ctx); - value = strchr(line, ':') + 2; - if (!*line) { - break; - } else if (strstr(line, "Cooperative: ") == line) { - have_cooperative = 1 + !!have_cooperative; - if (!strcmp(value, "yes")) info->cooperative = 1; - else if (!strcmp(value, "no")) info->cooperative = 0; - else - bad = 1; - } else if (strstr(line, "Depth: ") == line) { - have_depth = 1 + !!have_depth; - if (!strcmp(value, "8")) info->depth = LIBCOOPGAMMA_UINT8; - else if (!strcmp(value, "16")) info->depth = LIBCOOPGAMMA_UINT16; - else if (!strcmp(value, "32")) info->depth = LIBCOOPGAMMA_UINT32; - else if (!strcmp(value, "64")) info->depth = LIBCOOPGAMMA_UINT64; - else if (!strcmp(value, "f")) info->depth = LIBCOOPGAMMA_FLOAT; - else if (!strcmp(value, "d")) info->depth = LIBCOOPGAMMA_DOUBLE; - else - bad = 1; - } else if (strstr(line, "Gamma support: ") == line) { - have_gamma_support = 1 + !!have_gamma_support; - if (!strcmp(value, "yes")) info->supported = LIBCOOPGAMMA_YES; - else if (!strcmp(value, "no")) info->supported = LIBCOOPGAMMA_NO; - else if (!strcmp(value, "maybe")) info->supported = LIBCOOPGAMMA_MAYBE; - else - bad = 1; - } else if ((r = (strstr(line, "Red size: ") == line)) || - (g = (strstr(line, "Green size: ") == line)) || - strstr(line, "Blue size: ") == line) { - if (r) have_red_size = 1 + !!have_red_size, outz = &info->red_size; - else if (g) have_green_size = 1 + !!have_green_size, outz = &info->green_size; - else have_blue_size = 1 + !!have_blue_size, outz = &info->blue_size; - *outz = (size_t)atol(value); - sprintf(temp, "%zu", *outz); - if (strcmp(value, temp)) - bad = 1; - } else if ((x = r = (strstr(line, "Red x: ") == line)) || - (r = (strstr(line, "Red y: ") == line)) || - (x = g = (strstr(line, "Green x: ") == line)) || - (g = (strstr(line, "Green y: ") == line)) || - (x = b = (strstr(line, "Blue x: ") == line)) || - (b = (strstr(line, "Blue y: ") == line)) || - (x = (strstr(line, "White x: ") == line)) || - strstr(line, "White y: ") == line) { - if (r && x) have = &have_red_x, outu = &info->red_x; - else if (r) have = &have_red_y, outu = &info->red_y; - else if (g && x) have = &have_green_x, outu = &info->green_x; - else if (g) have = &have_green_y, outu = &info->green_y; - else if (b && x) have = &have_blue_x, outu = &info->blue_x; - else if (b) have = &have_blue_y, outu = &info->blue_y; - else if (x) have = &have_white_x, outu = &info->white_x; - else have = &have_white_y, outu = &info->white_y; - *have = 1 + !!*have; - *outu = (unsigned)atoi(value); - sprintf(temp, "%u", *outu); - if (strcmp(value, temp)) - bad = 1; - } else if (strstr(line, "Colour space: ") == line) { - have_colourspace = 1 + !!have_colourspace; - if (!strcmp(value, "sRGB")) info->colourspace = LIBCOOPGAMMA_SRGB; - else if (!strcmp(value, "RGB")) info->colourspace = LIBCOOPGAMMA_RGB; - else if (!strcmp(value, "non-RGB")) info->colourspace = LIBCOOPGAMMA_NON_RGB; - else if (!strcmp(value, "grey")) info->colourspace = LIBCOOPGAMMA_GREY; - else - info->colourspace = LIBCOOPGAMMA_UNKNOWN; - } - } - - (void) next_payload(ctx, &_n); - - info->have_gamut = (have_red_x && have_green_x && have_blue_x && have_white_x && - have_red_y && have_green_y && have_blue_y && have_white_y); - - if (bad || have_gamma_support != 1 || have_red_x > 1 || have_red_y > 1 || - have_green_x > 1 || have_green_y > 1 || have_blue_x > 1 || have_blue_y > 1 || - have_white_x > 1 || have_white_y > 1 || have_colourspace > 1) { - errno = EBADMSG; - copy_errno(ctx); - return -1; - } - if (info->supported != LIBCOOPGAMMA_NO) { - if (have_cooperative > 1 || have_depth != 1 || have_gamma_support != 1 || - have_red_size != 1 || have_green_size != 1 || have_blue_size != 1) { - errno = EBADMSG; - copy_errno(ctx); - return -1; - } - } - - return 0; -} - - -/** - * Retrieve information about a CRTC:s gamma ramps, synchronous version - * - * This is a synchronous request function, as such, - * you have to ensure that communication is blocking - * (default), and that there are not asynchronous - * requests waiting, it also means that EINTR:s are - * silently ignored and there no wait to cancel the - * operation without disconnection from the server - * - * @param crtc The name of the CRTC - * @param info Output parameter for the information, must be initialised - * @param ctx The state of the library, must be connected - * @return Zero on success, -1 on error, in which case `ctx->error` - * (rather than `errno`) is read for information about the error - */ -int -libcoopgamma_get_gamma_info_sync(const char *restrict ctrc, libcoopgamma_crtc_info_t *restrict info, - libcoopgamma_context_t *restrict ctx) -{ - SYNC_CALL(libcoopgamma_get_gamma_info_send(ctrc, ctx, &async), - libcoopgamma_get_gamma_info_recv(info, ctx, &async), (copy_errno(ctx), -1)); -} - - - -/** - * Retrieve the current gamma ramp adjustments, send request part - * - * Cannot be used before connecting to the server - * - * @param query The query to send - * @param ctx The state of the library, must be connected - * @param async Information about the request, that is needed to - * identify and parse the response, is stored here - * @return Zero on success, -1 on error - */ -int -libcoopgamma_get_gamma_send(const libcoopgamma_filter_query_t *restrict query, libcoopgamma_context_t *restrict ctx, - libcoopgamma_async_context_t *restrict async) -{ -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wnonnull-compare" -#endif - if (!query || !query->crtc || strchr(query->crtc, '\n')) { - errno = EINVAL; - goto fail; - } -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic pop #endif - async->message_id = ctx->message_id; - async->coalesce = query->coalesce; - SEND_MESSAGE(ctx, NULL, (size_t)0, - "Command: get-gamma\n" - "Message ID: %" PRIu32 "\n" - "CRTC: %s\n" - "Coalesce: %s\n" - "High priority: %" PRIi64 "\n" - "Low priority: %" PRIi64 "\n" - "\n", - ctx->message_id, query->crtc, query->coalesce ? "yes" : "no", - query->high_priority, query->low_priority); - return 0; -fail: - copy_errno(ctx); - return -1; -} - - -/** - * Retrieve the current gamma ramp adjustments, receive response part - * - * @param table Output for the response, must be initialised - * @param ctx The state of the library, must be connected - * @param async Information about the request - * @return Zero on success, -1 on error, in which case `ctx->error` - * (rather than `errno`) is read for information about the error - */ -int -libcoopgamma_get_gamma_recv(libcoopgamma_filter_table_t *restrict table, libcoopgamma_context_t *restrict ctx, - libcoopgamma_async_context_t *restrict async) -{ - char temp[3 * sizeof(size_t) + 1]; - char *line; - char *value; - char *payload; - size_t i, n, width, clutsize; - int have_depth = 0; - int have_red_size = 0; - int have_green_size = 0; - int have_blue_size = 0; - int have_tables = 0; - int bad = 0, r = 0, g = 0; - size_t *out; - size_t off, len; - - if (check_error(ctx, async)) - return -1; - - libcoopgamma_filter_table_destroy(table); - - for (;;) { - line = next_header(ctx); - value = strchr(line, ':') + 2; - if (!*line) { - break; - } else if (strstr(line, "Depth: ") == line) { - have_depth = 1 + !!have_depth; - if (!strcmp(value, "8")) table->depth = LIBCOOPGAMMA_UINT8; - else if (!strcmp(value, "16")) table->depth = LIBCOOPGAMMA_UINT16; - else if (!strcmp(value, "32")) table->depth = LIBCOOPGAMMA_UINT32; - else if (!strcmp(value, "64")) table->depth = LIBCOOPGAMMA_UINT64; - else if (!strcmp(value, "f")) table->depth = LIBCOOPGAMMA_FLOAT; - else if (!strcmp(value, "d")) table->depth = LIBCOOPGAMMA_DOUBLE; - else - bad = 1; - } else if ((r = (strstr(line, "Red size: ") == line)) || - (g = (strstr(line, "Green size: ") == line)) || - strstr(line, "Blue size: ") == line) { - if (r) have_red_size = 1 + !!have_red_size, out = &table->red_size; - else if (g) have_green_size = 1 + !!have_green_size, out = &table->green_size; - else have_blue_size = 1 + !!have_blue_size, out = &table->blue_size; - *out = (size_t)atol(value); - sprintf(temp, "%zu", *out); - if (strcmp(value, temp)) - bad = 1; - } else if (strstr(line, "Tables: ") == line) { - have_tables = 1 + have_tables; - table->filter_count = (size_t)atol(value); - sprintf(temp, "%zu", table->filter_count); - if (strcmp(value, temp)) - bad = 1; - } - } - - payload = next_payload(ctx, &n); - - if (bad || have_depth != 1 || have_red_size != 1 || have_green_size != 1 || - have_blue_size != 1 || (async->coalesce ? have_tables > 1 : !have_tables) || - ((!payload || !n) && (async->coalesce || table->filter_count > 0)) || - (n > 0 && have_tables && !table->filter_count) || - (async->coalesce && have_tables && table->filter_count != 1)) - goto bad; - - switch (table->depth) { - case LIBCOOPGAMMA_FLOAT: width = sizeof(float); break; - case LIBCOOPGAMMA_DOUBLE: width = sizeof(double); break; - default: INTEGRAL_DEPTHS - if (table->depth <= 0 || (table->depth & 7)) - goto bad; - width = (size_t)(table->depth / 8); - break; - } - - clutsize = table->red_size + table->green_size + table->blue_size; - clutsize *= width; - - if (async->coalesce) { - if (n != clutsize) - goto bad; - table->filters = malloc(sizeof(*(table->filters))); - if (!table->filters) - goto fail; - table->filters->priority = 0; - table->filters->class = NULL; - table->filters->ramps.u8.red_size = table->red_size; - table->filters->ramps.u8.green_size = table->green_size; - table->filters->ramps.u8.blue_size = table->blue_size; - if (libcoopgamma_ramps_initialise_(&table->filters->ramps, width) < 0) - goto fail; - memcpy(table->filters->ramps.u8.red, payload, clutsize); - table->filter_count = 1; - } else if (!table->filter_count) { - table->filters = NULL; - } else { - off = 0; - table->filters = calloc(table->filter_count, sizeof(*table->filters)); - if (!table->filters) - goto fail; - for (i = 0; i < table->filter_count; i++) { - if (off + sizeof(int64_t) > n) - goto bad; - table->filters[i].priority = *(int64_t *)(payload + off); - off += sizeof(int64_t); - if (!memchr(payload + off, '\0', n - off)) - goto bad; - len = strlen(payload + off) + 1; - table->filters[i].class = malloc(len); - if (!table->filters[i].class) - goto fail; - memcpy(table->filters[i].class, payload + off, len); - off += len; - if (off + clutsize > n) - goto bad; - table->filters[i].ramps.u8.red_size = table->red_size; - table->filters[i].ramps.u8.green_size = table->green_size; - table->filters[i].ramps.u8.blue_size = table->blue_size; - if (libcoopgamma_ramps_initialise_(&(table->filters[i].ramps), width) < 0) - goto fail; - memcpy(table->filters[i].ramps.u8.red, payload + off, clutsize); - off += clutsize; - } - if (off != n) - goto bad; - } - - return 0; -bad: - errno = EBADMSG; -fail: - copy_errno(ctx); - return -1; -} - - -/** - * Retrieve the current gamma ramp adjustments, synchronous version - * - * This is a synchronous request function, as such, - * you have to ensure that communication is blocking - * (default), and that there are not asynchronous - * requests waiting, it also means that EINTR:s are - * silently ignored and there no wait to cancel the - * operation without disconnection from the server - * - * @param query The query to send - * @param table Output for the response, must be initialised - * @param ctx The state of the library, must be connected - * @return Zero on success, -1 on error, in which case `ctx->error` - * (rather than `errno`) is read for information about the error - */ -int -libcoopgamma_get_gamma_sync(const libcoopgamma_filter_query_t *restrict query, libcoopgamma_filter_table_t *restrict table, - libcoopgamma_context_t *restrict ctx) -{ - SYNC_CALL(libcoopgamma_get_gamma_send(query, ctx, &async), - libcoopgamma_get_gamma_recv(table, ctx, &async), (copy_errno(ctx), -1)); -} - - - -/** - * Apply, update, or remove a gamma ramp adjustment, send request part - * - * Cannot be used before connecting to the server - * - * @param filter The filter to apply, update, or remove, gamma ramp meta-data must match the CRTC's - * @param ctx The state of the library, must be connected - * @param async Information about the request, that is needed to - * identify and parse the response, is stored here - * @return Zero on success, -1 on error - */ -int -libcoopgamma_set_gamma_send(const libcoopgamma_filter_t *restrict filter, libcoopgamma_context_t *restrict ctx, - libcoopgamma_async_context_t *restrict async) -{ - const void *payload = NULL; - const char *lifespan; - char priority[sizeof("Priority: \n") + 3 * sizeof(int64_t)] = {'\0'}; - char length [sizeof("Length: \n") + 3 * sizeof(size_t) ] = {'\0'}; - size_t payload_size = 0, stopwidth = 0; - -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wnonnull-compare" -#endif - if (!filter || !filter->crtc || strchr(filter->crtc, '\n') || !filter->class || strchr(filter->class, '\n')) { - errno = EINVAL; - goto fail; - } -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic pop -#endif - - switch (filter->lifespan) { - case LIBCOOPGAMMA_REMOVE: lifespan = "remove"; break; - case LIBCOOPGAMMA_UNTIL_DEATH: lifespan = "until-death"; break; - case LIBCOOPGAMMA_UNTIL_REMOVAL: lifespan = "until-removal"; break; - default: - errno = EINVAL; - goto fail; - } - - if (filter->lifespan != LIBCOOPGAMMA_REMOVE) { - switch (filter->depth) { - case LIBCOOPGAMMA_FLOAT: stopwidth = sizeof(float); break; - case LIBCOOPGAMMA_DOUBLE: stopwidth = sizeof(double); break; - default: INTEGRAL_DEPTHS - if (filter->depth <= 0 || (filter->depth & 7)) { - errno = EINVAL; - goto fail; - } - stopwidth = (size_t)(filter->depth / 8); - break; - } - - payload_size = filter->ramps.u8.red_size; - payload_size += filter->ramps.u8.green_size; - payload_size += filter->ramps.u8.blue_size; - payload_size *= stopwidth; - payload = filter->ramps.u8.red; - sprintf(priority, "Priority: %" PRIi64 "\n", filter->priority); - sprintf(length, "Length: %zu\n", payload_size); - } - - async->message_id = ctx->message_id; - SEND_MESSAGE(ctx, payload, payload_size, - "Command: set-gamma\n" - "Message ID: %" PRIu32 "\n" - "CRTC: %s\n" - "Class: %s\n" - "Lifespan: %s\n" - "%s" - "%s" - "\n", - ctx->message_id, filter->crtc, filter->class, lifespan, priority, length); - - return 0; -fail: - copy_errno(ctx); - return -1; -} - - -/** - * Apply, update, or remove a gamma ramp adjustment, receive response part - * - * @param ctx The state of the library, must be connected - * @param async Information about the request - * @return Zero on success, -1 on error, in which case `ctx->error` - * (rather than `errno`) is read for information about the error - */ -int -libcoopgamma_set_gamma_recv(libcoopgamma_context_t *restrict ctx, libcoopgamma_async_context_t *restrict async) -{ - size_t _n = 0; - - if (check_error(ctx, async)) - return -(ctx->error.custom || ctx->error.number); - - while (*next_header(ctx)); - (void) next_payload(ctx, &_n); - - errno = EBADMSG; - copy_errno(ctx); - return -1; -} - - -/** - * Apply, update, or remove a gamma ramp adjustment, synchronous version - * - * This is a synchronous request function, as such, - * you have to ensure that communication is blocking - * (default), and that there are not asynchronous - * requests waiting, it also means that EINTR:s are - * silently ignored and there no wait to cancel the - * operation without disconnection from the server - * - * @param filter The filter to apply, update, or remove, gamma ramp meta-data must match the CRTC's - * @param depth The datatype for the stops in the gamma ramps, must match the CRTC's - * @param ctx The state of the library, must be connected - * @return Zero on success, -1 on error, in which case `ctx->error` - * (rather than `errno`) is read for information about the error - */ -int -libcoopgamma_set_gamma_sync(const libcoopgamma_filter_t *restrict filter, libcoopgamma_context_t *restrict ctx) -{ - SYNC_CALL(libcoopgamma_set_gamma_send(filter, ctx, &async), - libcoopgamma_set_gamma_recv(ctx, &async), (copy_errno(ctx), -1)); -} - - - -#if defined(__GNUC__) -# pragma GCC diagnostic pop -#endif +extern inline char *libcoopgamma_next_header__(libcoopgamma_context_t *restrict ctx); +extern inline char *libcoopgamma_next_payload__(libcoopgamma_context_t *restrict ctx, size_t *n); diff --git a/libcoopgamma.h b/libcoopgamma.h index 5f5e800..f6a7554 100644 --- a/libcoopgamma.h +++ b/libcoopgamma.h @@ -714,6 +714,8 @@ typedef struct libcoopgamma_context { /** * File descriptor for the socket + * + * `-1` if no file descriptor is used */ int fd; diff --git a/libcoopgamma.h.0 b/libcoopgamma.h.0 index 73a0b8a..0f6da8d 100644 --- a/libcoopgamma.h.0 +++ b/libcoopgamma.h.0 @@ -453,6 +453,8 @@ function call. .B "int fd" File descriptor for the socket that connects the client to the server. +.B -1 +if no file descriptor is used. .P The .B <libcoopgamma.h> diff --git a/libcoopgamma_async_context_destroy.c b/libcoopgamma_async_context_destroy.c new file mode 100644 index 0000000..6f04018 --- /dev/null +++ b/libcoopgamma_async_context_destroy.c @@ -0,0 +1,18 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Release all resources allocated to a `libcoopgamma_async_context_t`, + * the allocation of the record itself is not freed + * + * Always call this function after failed call to `libcoopgamma_async_context_initialise` + * or failed call to `libcoopgamma_async_context_unmarshal` + * + * @param this The record to destroy + */ +void +libcoopgamma_async_context_destroy(libcoopgamma_async_context_t *restrict this) +{ + (void) this; +} diff --git a/libcoopgamma_async_context_initialise.c b/libcoopgamma_async_context_initialise.c new file mode 100644 index 0000000..ec683dc --- /dev/null +++ b/libcoopgamma_async_context_initialise.c @@ -0,0 +1,17 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Initialise a `libcoopgamma_async_context_t` + * + * @param this The record to initialise + * @return Zero on success, -1 on error + */ +int +libcoopgamma_async_context_initialise(libcoopgamma_async_context_t *restrict this) +{ + this->message_id = 0; + this->coalesce = 0; + return 0; +} diff --git a/libcoopgamma_async_context_marshal.c b/libcoopgamma_async_context_marshal.c new file mode 100644 index 0000000..6f1b96d --- /dev/null +++ b/libcoopgamma_async_context_marshal.c @@ -0,0 +1,22 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Marshal a `libcoopgamma_async_context_t` into a buffer + * + * @param this The record to marshal + * @param vbuf The output buffer, `NULL` to only measure + * how large this buffer has to be + * @return The number of marshalled bytes, or if `buf == NULL`, + * how many bytes would be marshalled if `buf != NULL` + */ +size_t +libcoopgamma_async_context_marshal(const libcoopgamma_async_context_t *restrict this, void *restrict vbuf) +{ + MARSHAL_PROLOGUE; + marshal_version(LIBCOOPGAMMA_ASYNC_CONTEXT_VERSION); + marshal_prim(this->message_id, uint32_t); + marshal_prim(this->coalesce, int); + MARSHAL_EPILOGUE; +} diff --git a/libcoopgamma_async_context_unmarshal.c b/libcoopgamma_async_context_unmarshal.c new file mode 100644 index 0000000..9530779 --- /dev/null +++ b/libcoopgamma_async_context_unmarshal.c @@ -0,0 +1,22 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Unmarshal a `libcoopgamma_async_context_t` from a buffer + * + * @param this The output parameter for unmarshalled record + * @param vbuf The buffer with the marshalled record + * @param np Output parameter for the number of unmarshalled bytes, undefined on failure + * @return `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`, + * `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET` + */ +int +libcoopgamma_async_context_unmarshal(libcoopgamma_async_context_t *restrict this, const void *restrict vbuf, size_t *restrict np) +{ + UNMARSHAL_PROLOGUE; + unmarshal_version(LIBCOOPGAMMA_ASYNC_CONTEXT_VERSION); + unmarshal_prim(this->message_id, uint32_t); + unmarshal_prim(this->coalesce, int); + UNMARSHAL_EPILOGUE; +} diff --git a/libcoopgamma_check_error__.c b/libcoopgamma_check_error__.c new file mode 100644 index 0000000..44fb8b7 --- /dev/null +++ b/libcoopgamma_check_error__.c @@ -0,0 +1,96 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Check whether the server sent an error, if so copy it to `ctx` + * + * This function will also reports EBADMSG if the message ID + * that the message is a response to does not match the request + * information, or if it is missing + * + * @param ctx The state of the library, must be connected + * @param async Information about the request + * @return 1 if the server sent an error (even indicating success), + * 0 on success, -1 on failure. Information about failure + * is copied `ctx`. + */ +int +libcoopgamma_check_error__(libcoopgamma_context_t *restrict ctx, libcoopgamma_async_context_t *restrict async) +{ + char temp[3 * sizeof(uint64_t) + 1]; + size_t old_tail = ctx->inbound_tail; + char *line; + char *value; + int command_ok = 0; + int have_in_response_to = 0; + int have_error = 0; + int bad = 0; + char *payload; + size_t n; + + for (;;) { + line = libcoopgamma_next_header__(ctx); + value = &strchr(line, ':')[2U]; + if (!*line) { + break; + } else if (!strcmp(line, "Command: error")) { + command_ok = 1; + } else if (strstr(line, "In response to: ") == line) { + uint32_t id = (uint32_t)atol(value); + have_in_response_to = 1 + !!have_in_response_to; + if (id != async->message_id) { + bad = 1; + } else { + sprintf(temp, "%" PRIu32, id); + if (strcmp(value, temp)) + bad = 1; + } + } else if (strstr(line, "Error: ") == line) { + have_error = 1 + !!have_error; + ctx->error.server_side = 1; + ctx->error.custom = (strstr(value, "custom") == value); + if (ctx->error.custom) { + if (value[6] == '\0') { + ctx->error.number = 0; + continue; + } else if (value[6] != ' ') { + bad = 1; + continue; + } + value += 7; + } + ctx->error.number = (uint64_t)atoll(value); + sprintf(temp, "%" PRIu64, ctx->error.number); + if (strcmp(value, temp)) + bad = 1; + } + } + + if (!command_ok) { + ctx->inbound_tail = old_tail; + return 0; + } + + payload = libcoopgamma_next_payload__(ctx, &n); + if (payload) { + if (memchr(payload, '\0', n) || payload[n - 1U] != '\n') + goto badmsg; + ctx->error.description = malloc(n); + if (ctx->error.description == NULL) + goto fail; + memcpy(ctx->error.description, payload, n - 1U); + ctx->error.description[n - 1U] = '\0'; + } + + if (bad || have_in_response_to != 1 || have_error != 1) + goto badmsg; + + return 1; + +badmsg: + errno = EBADMSG; +fail: + copy_errno(ctx); + return -1; +} diff --git a/libcoopgamma_connect.c b/libcoopgamma_connect.c new file mode 100644 index 0000000..d6c931c --- /dev/null +++ b/libcoopgamma_connect.c @@ -0,0 +1,94 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Connect to a coopgamma server, and start it if necessary + * + * Use `libcoopgamma_context_destroy` to disconnect + * + * SIGCHLD must not be ignored or blocked + * + * @param method The adjustment method, `NULL` for automatic + * @param site The site, `NULL` for automatic + * @param ctx The state of the library, must be initialised + * @return Zero on success, -1 on error. On error, `errno` is set + * to 0 if the server could not be initialised. + */ +int +libcoopgamma_connect(const char *restrict method, const char *restrict site, libcoopgamma_context_t *restrict ctx) +{ + const char *(args[6]) = {COOPGAMMAD}; + struct sockaddr_un address; + char *path; + int saved_errno; + int tries = 0, status; + pid_t pid; + size_t i = 1; + + ctx->blocking = 1; + + if (method) args[i++] = "-m", args[i++] = method; + if (site) args[i++] = "-s", args[i++] = site; + args[i] = NULL; + + path = libcoopgamma_get_socket_file(method, site); + if (!path) + return -1; + if (strlen(path) >= sizeof(address.sun_path)) { + free(path); + errno = ENAMETOOLONG; + return -1; + } + + address.sun_family = AF_UNIX; + stpcpy(address.sun_path, path); + free(path); + if ((ctx->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) + return -1; + +retry: + if (connect(ctx->fd, (const struct sockaddr *)&address, (socklen_t)sizeof(address)) < 0) { + if ((errno == ECONNREFUSED || errno == ENOENT || errno == ENOTDIR) && !tries++) { + switch ((pid = fork())) { + case -1: + goto fail; + + case 0: + /* Child */ + close(ctx->fd); +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-qual" +#endif + execvp(COOPGAMMAD, (char *const *)(args)); +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + perror(NAME_OF_THE_PROCESS); + exit(1); + + default: + /* Parent */ + if (waitpid(pid, &status, 0) < 0) + goto fail; + if (status) { + errno = 0; + goto fail; + } + break; + } + goto retry; + } + goto fail; + } + + return 0; + +fail: + saved_errno = errno; + close(ctx->fd); + ctx->fd = -1; + errno = saved_errno; + return -1; +} diff --git a/libcoopgamma_context_destroy.c b/libcoopgamma_context_destroy.c new file mode 100644 index 0000000..bd8668f --- /dev/null +++ b/libcoopgamma_context_destroy.c @@ -0,0 +1,28 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Release all resources allocated to a `libcoopgamma_context_t`, + * the allocation of the record itself is not freed + * + * Always call this function after failed call to `libcoopgamma_context_initialise` + * or failed call to `libcoopgamma_context_unmarshal` + * + * @param this The record to destroy + * @param disconnect Disconnect from the server? + */ +void +libcoopgamma_context_destroy(libcoopgamma_context_t *restrict this, int disconnect) +{ + if (disconnect && this->fd >= 0) { + shutdown(this->fd, SHUT_RDWR); + close(this->fd); + } + this->fd = -1; + libcoopgamma_error_destroy(&this->error); + free(this->outbound); + free(this->inbound); + this->outbound = NULL; + this->inbound = NULL; +} diff --git a/libcoopgamma_context_initialise.c b/libcoopgamma_context_initialise.c new file mode 100644 index 0000000..5ece7d8 --- /dev/null +++ b/libcoopgamma_context_initialise.c @@ -0,0 +1,18 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Initialise a `libcoopgamma_context_t` + * + * @param this The record to initialise + * @return Zero on success, -1 on error + */ +int +libcoopgamma_context_initialise(libcoopgamma_context_t *restrict this) +{ + memset(this, 0, sizeof(*this)); + this->fd = -1; + this->blocking = 1; + return 0; +} diff --git a/libcoopgamma_context_marshal.c b/libcoopgamma_context_marshal.c new file mode 100644 index 0000000..1a7ad88 --- /dev/null +++ b/libcoopgamma_context_marshal.c @@ -0,0 +1,33 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Marshal a `libcoopgamma_context_t` into a buffer + * + * @param this The record to marshal + * @param vbuf The output buffer, `NULL` to only measure + * how large this buffer has to be + * @return The number of marshalled bytes, or if `buf == NULL`, + * how many bytes would be marshalled if `buf != NULL` + */ +size_t +libcoopgamma_context_marshal(const libcoopgamma_context_t *restrict this, void *restrict vbuf) +{ + MARSHAL_PROLOGUE; + marshal_version(LIBCOOPGAMMA_CONTEXT_VERSION); + marshal_prim(this->fd, int); + off += libcoopgamma_error_marshal(&this->error, SUBBUF); + marshal_prim(this->message_id, uint32_t); + marshal_prim(this->outbound_head - this->outbound_tail, size_t); + marshal_buffer(this->outbound + this->outbound_tail, this->outbound_head - this->outbound_tail); + marshal_prim(this->inbound_head - this->inbound_tail, size_t); + marshal_buffer(this->inbound + this->inbound_tail, this->inbound_head - this->inbound_tail); + marshal_prim(this->length, size_t); + marshal_prim(this->curline, size_t); + marshal_prim(this->in_response_to, uint32_t); + marshal_prim(this->have_all_headers, int); + marshal_prim(this->bad_message, int); + marshal_prim(this->blocking, int); + MARSHAL_EPILOGUE; +} diff --git a/libcoopgamma_context_unmarshal.c b/libcoopgamma_context_unmarshal.c new file mode 100644 index 0000000..a96d739 --- /dev/null +++ b/libcoopgamma_context_unmarshal.c @@ -0,0 +1,41 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Unmarshal a `libcoopgamma_context_t` from a buffer + * + * @param this The output parameter for unmarshalled record + * @param vbuf The buffer with the marshalled record + * @param np Output parameter for the number of unmarshalled bytes, undefined on failure + * @return `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`, + * `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET` + */ +int +libcoopgamma_context_unmarshal(libcoopgamma_context_t *restrict this, const void *restrict vbuf, size_t *restrict np) +{ + size_t n; + int r; + UNMARSHAL_PROLOGUE; + memset(this, 0, sizeof(*this)); + unmarshal_version(LIBCOOPGAMMA_CONTEXT_VERSION); + unmarshal_prim(this->fd, int); + r = libcoopgamma_error_unmarshal(&this->error, NNSUBBUF, &n); + if (r != LIBCOOPGAMMA_SUCCESS) + return r; + off += n; + unmarshal_prim(this->message_id, uint32_t); + unmarshal_prim(this->outbound_head, size_t); + this->outbound_size = this->outbound_head; + unmarshal_buffer(this->outbound, this->outbound_head); + unmarshal_prim(this->inbound_head, size_t); + this->inbound_size = this->inbound_head; + unmarshal_buffer(this->inbound, this->inbound_head); + unmarshal_prim(this->length, size_t); + unmarshal_prim(this->curline, size_t); + unmarshal_prim(this->in_response_to, uint32_t); + unmarshal_prim(this->have_all_headers, int); + unmarshal_prim(this->bad_message, int); + unmarshal_prim(this->blocking, int); + UNMARSHAL_EPILOGUE; +} diff --git a/libcoopgamma_crtc_info_destroy.c b/libcoopgamma_crtc_info_destroy.c new file mode 100644 index 0000000..0e768b7 --- /dev/null +++ b/libcoopgamma_crtc_info_destroy.c @@ -0,0 +1,18 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Release all resources allocated to a `libcoopgamma_crtc_info_t`, + * the allocation of the record itself is not freed + * + * Always call this function after failed call to `libcoopgamma_crtc_info_initialise` + * or failed call to `libcoopgamma_crtc_info_unmarshal` + * + * @param this The record to destroy + */ +void +libcoopgamma_crtc_info_destroy(libcoopgamma_crtc_info_t *restrict this) +{ + (void) this; +} diff --git a/libcoopgamma_crtc_info_initialise.c b/libcoopgamma_crtc_info_initialise.c new file mode 100644 index 0000000..80ce72f --- /dev/null +++ b/libcoopgamma_crtc_info_initialise.c @@ -0,0 +1,16 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Initialise a `libcoopgamma_crtc_info_t` + * + * @param this The record to initialise + * @return Zero on success, -1 on error + */ +int +libcoopgamma_crtc_info_initialise(libcoopgamma_crtc_info_t *restrict this) +{ + memset(this, 0, sizeof(*this)); + return 0; +} diff --git a/libcoopgamma_crtc_info_marshal.c b/libcoopgamma_crtc_info_marshal.c new file mode 100644 index 0000000..06106ab --- /dev/null +++ b/libcoopgamma_crtc_info_marshal.c @@ -0,0 +1,39 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Marshal a `libcoopgamma_crtc_info_t` into a buffer + * + * @param this The record to marshal + * @param vbuf The output buffer, `NULL` to only measure + * how large this buffer has to be + * @return The number of marshalled bytes, or if `buf == NULL`, + * how many bytes would be marshalled if `buf != NULL` + */ +size_t +libcoopgamma_crtc_info_marshal(const libcoopgamma_crtc_info_t *restrict this, void *restrict vbuf) +{ + MARSHAL_PROLOGUE; + marshal_version(LIBCOOPGAMMA_CRTC_INFO_VERSION); + marshal_version(LIBCOOPGAMMA_DEPTH_VERSION); + marshal_version(LIBCOOPGAMMA_SUPPORT_VERSION); + marshal_version(LIBCOOPGAMMA_COLOURSPACE_VERSION); + marshal_prim(this->cooperative, int); + marshal_prim(this->depth, libcoopgamma_depth_t); + marshal_prim(this->red_size, size_t); + marshal_prim(this->green_size, size_t); + marshal_prim(this->blue_size, size_t); + marshal_prim(this->supported, libcoopgamma_support_t); + marshal_prim(this->colourspace, libcoopgamma_colourspace_t); + marshal_prim(this->have_gamut, int); + marshal_prim(this->red_x, unsigned); + marshal_prim(this->red_y, unsigned); + marshal_prim(this->green_x, unsigned); + marshal_prim(this->green_y, unsigned); + marshal_prim(this->blue_x, unsigned); + marshal_prim(this->blue_y, unsigned); + marshal_prim(this->white_x, unsigned); + marshal_prim(this->white_y, unsigned); + MARSHAL_EPILOGUE; +} diff --git a/libcoopgamma_crtc_info_unmarshal.c b/libcoopgamma_crtc_info_unmarshal.c new file mode 100644 index 0000000..0afd77b --- /dev/null +++ b/libcoopgamma_crtc_info_unmarshal.c @@ -0,0 +1,39 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Unmarshal a `libcoopgamma_crtc_info_t` from a buffer + * + * @param this The output parameter for unmarshalled record + * @param vbuf The buffer with the marshalled record + * @param np Output parameter for the number of unmarshalled bytes, undefined on failure + * @return `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`, + * `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET` + */ +int +libcoopgamma_crtc_info_unmarshal(libcoopgamma_crtc_info_t *restrict this, const void *restrict vbuf, size_t *restrict np) +{ + UNMARSHAL_PROLOGUE; + unmarshal_version(LIBCOOPGAMMA_CRTC_INFO_VERSION); + unmarshal_version(LIBCOOPGAMMA_DEPTH_VERSION); + unmarshal_version(LIBCOOPGAMMA_SUPPORT_VERSION); + unmarshal_version(LIBCOOPGAMMA_COLOURSPACE_VERSION); + unmarshal_prim(this->cooperative, int); + unmarshal_prim(this->depth, libcoopgamma_depth_t); + unmarshal_prim(this->red_size, size_t); + unmarshal_prim(this->green_size, size_t); + unmarshal_prim(this->blue_size, size_t); + unmarshal_prim(this->supported, libcoopgamma_support_t); + unmarshal_prim(this->colourspace, libcoopgamma_colourspace_t); + unmarshal_prim(this->have_gamut, int); + unmarshal_prim(this->red_x, unsigned); + unmarshal_prim(this->red_y, unsigned); + unmarshal_prim(this->green_x, unsigned); + unmarshal_prim(this->green_y, unsigned); + unmarshal_prim(this->blue_x, unsigned); + unmarshal_prim(this->blue_y, unsigned); + unmarshal_prim(this->white_x, unsigned); + unmarshal_prim(this->white_y, unsigned); + UNMARSHAL_EPILOGUE; +} diff --git a/libcoopgamma_error_destroy.c b/libcoopgamma_error_destroy.c new file mode 100644 index 0000000..0e7fa54 --- /dev/null +++ b/libcoopgamma_error_destroy.c @@ -0,0 +1,19 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Release all resources allocated to a `libcoopgamma_error_t`, + * the allocation of the record itself is not freed + * + * Always call this function after failed call to `libcoopgamma_error_initialise` + * or failed call to `libcoopgamma_error_unmarshal` + * + * @param this The record to destroy + */ +void +libcoopgamma_error_destroy(libcoopgamma_error_t *restrict this) +{ + free(this->description); + this->description = NULL; +} diff --git a/libcoopgamma_error_initialise.c b/libcoopgamma_error_initialise.c new file mode 100644 index 0000000..5aa4085 --- /dev/null +++ b/libcoopgamma_error_initialise.c @@ -0,0 +1,18 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Initialise a `libcoopgamma_error_t` + * + * @param this The record to initialise + * @return Zero on success, -1 on error + */ +int +libcoopgamma_error_initialise(libcoopgamma_error_t *restrict this) +{ + this->number = 0; + this->custom = 0; + this->description = NULL; + return 0; +} diff --git a/libcoopgamma_error_marshal.c b/libcoopgamma_error_marshal.c new file mode 100644 index 0000000..fcd9e38 --- /dev/null +++ b/libcoopgamma_error_marshal.c @@ -0,0 +1,24 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Marshal a `libcoopgamma_error_t` into a buffer + * + * @param this The record to marshal + * @param vbuf The output buffer, `NULL` to only measure + * how large this buffer has to be + * @return The number of marshalled bytes, or if `buf == NULL`, + * how many bytes would be marshalled if `buf != NULL` + */ +size_t +libcoopgamma_error_marshal(const libcoopgamma_error_t *restrict this, void *restrict vbuf) +{ + MARSHAL_PROLOGUE; + marshal_version(LIBCOOPGAMMA_ERROR_VERSION); + marshal_prim(this->number, uint64_t); + marshal_prim(this->custom, int); + marshal_prim(this->server_side, int); + marshal_string(this->description); + MARSHAL_EPILOGUE; +} diff --git a/libcoopgamma_error_unmarshal.c b/libcoopgamma_error_unmarshal.c new file mode 100644 index 0000000..489f47d --- /dev/null +++ b/libcoopgamma_error_unmarshal.c @@ -0,0 +1,25 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Unmarshal a `libcoopgamma_error_t` from a buffer + * + * @param this The output parameter for unmarshalled record + * @param vbuf The buffer with the marshalled record + * @param np Output parameter for the number of unmarshalled bytes, undefined on failure + * @return `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`, + * `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET` + */ +int +libcoopgamma_error_unmarshal(libcoopgamma_error_t *restrict this, const void *restrict vbuf, size_t *restrict np) +{ + UNMARSHAL_PROLOGUE; + this->description = NULL; + unmarshal_version(LIBCOOPGAMMA_ERROR_VERSION); + unmarshal_prim(this->number, uint64_t); + unmarshal_prim(this->custom, int); + unmarshal_prim(this->server_side, int); + unmarshal_string(this->description); + UNMARSHAL_EPILOGUE; +} diff --git a/libcoopgamma_filter_destroy.c b/libcoopgamma_filter_destroy.c new file mode 100644 index 0000000..89765ad --- /dev/null +++ b/libcoopgamma_filter_destroy.c @@ -0,0 +1,21 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Release all resources allocated to a `libcoopgamma_filter_t`, + * the allocation of the record itself is not freed + * + * Always call this function after failed call to `libcoopgamma_filter_initialise` + * or failed call to `libcoopgamma_filter_unmarshal` + * + * @param this The record to destroy + */ +void +libcoopgamma_filter_destroy(libcoopgamma_filter_t *restrict this) +{ + free(this->crtc); + free(this->class); + free(this->ramps.u8.red); + memset(this, 0, sizeof(*this)); +} diff --git a/libcoopgamma_filter_initialise.c b/libcoopgamma_filter_initialise.c new file mode 100644 index 0000000..fcf71ce --- /dev/null +++ b/libcoopgamma_filter_initialise.c @@ -0,0 +1,16 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Initialise a `libcoopgamma_filter_t` + * + * @param this The record to initialise + * @return Zero on success, -1 on error + */ +int +libcoopgamma_filter_initialise(libcoopgamma_filter_t *restrict this) +{ + memset(this, 0, sizeof(*this)); + return 0; +} diff --git a/libcoopgamma_filter_marshal.c b/libcoopgamma_filter_marshal.c new file mode 100644 index 0000000..8c2723e --- /dev/null +++ b/libcoopgamma_filter_marshal.c @@ -0,0 +1,37 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Marshal a `libcoopgamma_filter_t` into a buffer + * + * @param this The record to marshal + * @param vbuf The output buffer, `NULL` to only measure + * how large this buffer has to be + * @return The number of marshalled bytes, or if `buf == NULL`, + * how many bytes would be marshalled if `buf != NULL` + */ +size_t +libcoopgamma_filter_marshal(const libcoopgamma_filter_t *restrict this, void *restrict vbuf) +{ + MARSHAL_PROLOGUE; + marshal_version(LIBCOOPGAMMA_FILTER_VERSION); + marshal_version(LIBCOOPGAMMA_DEPTH_VERSION); + marshal_version(LIBCOOPGAMMA_LIFESPAN_VERSION); + marshal_prim(this->depth, libcoopgamma_depth_t); + marshal_prim(this->priority, int64_t); + marshal_string(this->crtc); + marshal_string(this->class); + marshal_prim(this->lifespan, libcoopgamma_lifespan_t); + switch (this->depth) { + case LIBCOOPGAMMA_UINT8: off += libcoopgamma_ramps_marshal(&this->ramps.u8, SUBBUF); break; + case LIBCOOPGAMMA_UINT16: off += libcoopgamma_ramps_marshal(&this->ramps.u16, SUBBUF); break; + case LIBCOOPGAMMA_UINT32: off += libcoopgamma_ramps_marshal(&this->ramps.u32, SUBBUF); break; + case LIBCOOPGAMMA_UINT64: off += libcoopgamma_ramps_marshal(&this->ramps.u64, SUBBUF); break; + case LIBCOOPGAMMA_FLOAT: off += libcoopgamma_ramps_marshal(&this->ramps.f, SUBBUF); break; + case LIBCOOPGAMMA_DOUBLE: off += libcoopgamma_ramps_marshal(&this->ramps.d, SUBBUF); break; + default: + break; + } + MARSHAL_EPILOGUE; +} diff --git a/libcoopgamma_filter_query_destroy.c b/libcoopgamma_filter_query_destroy.c new file mode 100644 index 0000000..071a313 --- /dev/null +++ b/libcoopgamma_filter_query_destroy.c @@ -0,0 +1,19 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Release all resources allocated to a `libcoopgamma_filter_query_t`, + * the allocation of the record itself is not freed + * + * Always call this function after failed call to `libcoopgamma_filter_query_initialise` + * or failed call to `libcoopgamma_filter_query_unmarshal` + * + * @param this The record to destroy + */ +void +libcoopgamma_filter_query_destroy(libcoopgamma_filter_query_t *restrict this) +{ + free(this->crtc); + this->crtc = NULL; +} diff --git a/libcoopgamma_filter_query_initialise.c b/libcoopgamma_filter_query_initialise.c new file mode 100644 index 0000000..7a31780 --- /dev/null +++ b/libcoopgamma_filter_query_initialise.c @@ -0,0 +1,19 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Initialise a `libcoopgamma_filter_query_t` + * + * @param this The record to initialise + * @return Zero on success, -1 on error + */ +int +libcoopgamma_filter_query_initialise(libcoopgamma_filter_query_t *restrict this) +{ + this->crtc = NULL; + this->coalesce = 0; + this->high_priority = INT64_MAX; + this->low_priority = INT64_MIN; + return 0; +} diff --git a/libcoopgamma_filter_query_marshal.c b/libcoopgamma_filter_query_marshal.c new file mode 100644 index 0000000..1049623 --- /dev/null +++ b/libcoopgamma_filter_query_marshal.c @@ -0,0 +1,24 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Marshal a `libcoopgamma_filter_query_t` into a buffer + * + * @param this The record to marshal + * @param vbuf The output buffer, `NULL` to only measure + * how large this buffer has to be + * @return The number of marshalled bytes, or if `buf == NULL`, + * how many bytes would be marshalled if `buf != NULL` + */ +size_t +libcoopgamma_filter_query_marshal(const libcoopgamma_filter_query_t *restrict this, void* restrict vbuf) +{ + MARSHAL_PROLOGUE; + marshal_version(LIBCOOPGAMMA_FILTER_QUERY_VERSION); + marshal_string(this->crtc); + marshal_prim(this->coalesce, int); + marshal_prim(this->high_priority, int64_t); + marshal_prim(this->low_priority, int64_t); + MARSHAL_EPILOGUE; +} diff --git a/libcoopgamma_filter_query_unmarshal.c b/libcoopgamma_filter_query_unmarshal.c new file mode 100644 index 0000000..ac2aa45 --- /dev/null +++ b/libcoopgamma_filter_query_unmarshal.c @@ -0,0 +1,25 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Unmarshal a `libcoopgamma_filter_query_t` from a buffer + * + * @param this The output parameter for unmarshalled record + * @param vbuf The buffer with the marshalled record + * @param np Output parameter for the number of unmarshalled bytes, undefined on failure + * @return `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`, + * `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET` + */ +int +libcoopgamma_filter_query_unmarshal(libcoopgamma_filter_query_t *restrict this, const void *restrict vbuf, size_t *restrict np) +{ + UNMARSHAL_PROLOGUE; + this->crtc = NULL; + unmarshal_version(LIBCOOPGAMMA_FILTER_QUERY_VERSION); + unmarshal_string(this->crtc); + unmarshal_prim(this->coalesce, int); + unmarshal_prim(this->high_priority, int64_t); + unmarshal_prim(this->low_priority, int64_t); + UNMARSHAL_EPILOGUE; +} diff --git a/libcoopgamma_filter_table_destroy.c b/libcoopgamma_filter_table_destroy.c new file mode 100644 index 0000000..e6c3f9c --- /dev/null +++ b/libcoopgamma_filter_table_destroy.c @@ -0,0 +1,21 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Release all resources allocated to a `libcoopgamma_filter_table_t`, + * the allocation of the record itself is not freed + * + * Always call this function after failed call to `libcoopgamma_filter_table_initialise` + * or failed call to `libcoopgamma_filter_table_unmarshal` + * + * @param this The record to destroy + */ +void +libcoopgamma_filter_table_destroy(libcoopgamma_filter_table_t *restrict this) +{ + while (this->filter_count) + libcoopgamma_queried_filter_destroy(this->filters + --this->filter_count); + free(this->filters); + this->filters = NULL; +} diff --git a/libcoopgamma_filter_table_initialise.c b/libcoopgamma_filter_table_initialise.c new file mode 100644 index 0000000..c6969b6 --- /dev/null +++ b/libcoopgamma_filter_table_initialise.c @@ -0,0 +1,16 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Initialise a `libcoopgamma_filter_table_t` + * + * @param this The record to initialise + * @return Zero on success, -1 on error + */ +int +libcoopgamma_filter_table_initialise(libcoopgamma_filter_table_t *restrict this) +{ + memset(this, 0, sizeof(*this)); + return 0; +} diff --git a/libcoopgamma_filter_table_marshal.c b/libcoopgamma_filter_table_marshal.c new file mode 100644 index 0000000..5a7e6c3 --- /dev/null +++ b/libcoopgamma_filter_table_marshal.c @@ -0,0 +1,29 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Marshal a `libcoopgamma_filter_table_t` into a buffer + * + * @param this The record to marshal + * @param vbuf The output buffer, `NULL` to only measure + * how large this buffer has to be + * @return The number of marshalled bytes, or if `buf == NULL`, + * how many bytes would be marshalled if `buf != NULL` + */ +size_t +libcoopgamma_filter_table_marshal(const libcoopgamma_filter_table_t *restrict this, void *restrict vbuf) +{ + size_t i; + MARSHAL_PROLOGUE; + marshal_version(LIBCOOPGAMMA_FILTER_TABLE_VERSION); + marshal_version(LIBCOOPGAMMA_DEPTH_VERSION); + marshal_prim(this->depth, libcoopgamma_depth_t); + marshal_prim(this->red_size, size_t); + marshal_prim(this->green_size, size_t); + marshal_prim(this->blue_size, size_t); + marshal_prim(this->filter_count, size_t); + for (i = 0; i < this->filter_count; i++) + off += libcoopgamma_queried_filter_marshal(&this->filters[i], SUBBUF, this->depth); + MARSHAL_EPILOGUE; +} diff --git a/libcoopgamma_filter_table_unmarshal.c b/libcoopgamma_filter_table_unmarshal.c new file mode 100644 index 0000000..1059d85 --- /dev/null +++ b/libcoopgamma_filter_table_unmarshal.c @@ -0,0 +1,40 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Unmarshal a `libcoopgamma_filter_table_t` from a buffer + * + * @param this The output parameter for unmarshalled record + * @param vbuf The buffer with the marshalled record + * @param np Output parameter for the number of unmarshalled bytes, undefined on failure + * @return `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`, + * `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET` + */ +int +libcoopgamma_filter_table_unmarshal(libcoopgamma_filter_table_t *restrict this, const void *restrict vbuf, size_t *restrict np) +{ + size_t i, n, fn; + int r; + UNMARSHAL_PROLOGUE; + this->filter_count = 0; + this->filters = NULL; + unmarshal_version(LIBCOOPGAMMA_FILTER_TABLE_VERSION); + unmarshal_version(LIBCOOPGAMMA_DEPTH_VERSION); + unmarshal_prim(this->depth, libcoopgamma_depth_t); + unmarshal_prim(this->red_size, size_t); + unmarshal_prim(this->green_size, size_t); + unmarshal_prim(this->blue_size, size_t); + unmarshal_prim(fn, size_t); + this->filters = malloc(fn * sizeof(*this->filters)); + if (!this->filters) + return LIBCOOPGAMMA_ERRNO_SET; + for (i = 0; i < fn; i++) { + r = libcoopgamma_queried_filter_unmarshal(&this->filters[i], NNSUBBUF, &n, this->depth); + if (r != LIBCOOPGAMMA_SUCCESS) + return r; + off += n; + this->filter_count += 1; + } + UNMARSHAL_EPILOGUE; +} diff --git a/libcoopgamma_filter_unmarshal.c b/libcoopgamma_filter_unmarshal.c new file mode 100644 index 0000000..9351215 --- /dev/null +++ b/libcoopgamma_filter_unmarshal.c @@ -0,0 +1,43 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Unmarshal a `libcoopgamma_filter_t` from a buffer + * + * @param this The output parameter for unmarshalled record + * @param vbuf The buffer with the marshalled record + * @param np Output parameter for the number of unmarshalled bytes, undefined on failure + * @return `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`, + * `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET` + */ +int +libcoopgamma_filter_unmarshal(libcoopgamma_filter_t *restrict this, const void *restrict vbuf, size_t *restrict np) +{ + int r = LIBCOOPGAMMA_SUCCESS; + size_t n = 0; + UNMARSHAL_PROLOGUE; + memset(this, 0, sizeof(*this)); + unmarshal_version(LIBCOOPGAMMA_FILTER_VERSION); + unmarshal_version(LIBCOOPGAMMA_DEPTH_VERSION); + unmarshal_version(LIBCOOPGAMMA_LIFESPAN_VERSION); + unmarshal_prim(this->depth, libcoopgamma_depth_t); + unmarshal_prim(this->priority, int64_t); + unmarshal_string(this->crtc); + unmarshal_string(this->class); + unmarshal_prim(this->lifespan, libcoopgamma_lifespan_t); + switch (this->depth) { + case LIBCOOPGAMMA_UINT8: r = libcoopgamma_ramps_unmarshal(&(this->ramps.u8), NNSUBBUF, &n); break; + case LIBCOOPGAMMA_UINT16: r = libcoopgamma_ramps_unmarshal(&(this->ramps.u16), NNSUBBUF, &n); break; + case LIBCOOPGAMMA_UINT32: r = libcoopgamma_ramps_unmarshal(&(this->ramps.u32), NNSUBBUF, &n); break; + case LIBCOOPGAMMA_UINT64: r = libcoopgamma_ramps_unmarshal(&(this->ramps.u64), NNSUBBUF, &n); break; + case LIBCOOPGAMMA_FLOAT: r = libcoopgamma_ramps_unmarshal(&(this->ramps.f), NNSUBBUF, &n); break; + case LIBCOOPGAMMA_DOUBLE: r = libcoopgamma_ramps_unmarshal(&(this->ramps.d), NNSUBBUF, &n); break; + default: + break; + } + if (r != LIBCOOPGAMMA_SUCCESS) + return r; + off += n; + UNMARSHAL_EPILOGUE; +} diff --git a/libcoopgamma_flush.c b/libcoopgamma_flush.c new file mode 100644 index 0000000..a1f23d1 --- /dev/null +++ b/libcoopgamma_flush.c @@ -0,0 +1,49 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Send all pending outbound data + * + * If this function or another function that sends a request + * to the server fails with EINTR, call this function to + * complete the transfer. The `async` parameter will always + * be in a properly configured state if a function fails + * with EINTR. + * + * @param ctx The state of the library, must be connected + * @return Zero on success, -1 on error + */ +int +libcoopgamma_flush(libcoopgamma_context_t *restrict ctx) +{ + ssize_t sent; + size_t chunksize = ctx->outbound_head - ctx->outbound_tail; + size_t sendsize; + + while (ctx->outbound_tail < ctx->outbound_head) { + sendsize = ctx->outbound_head - ctx->outbound_tail; + sendsize = sendsize < chunksize ? sendsize : chunksize; + sent = send(ctx->fd, &ctx->outbound[ctx->outbound_tail], sendsize, MSG_NOSIGNAL); + if (sent < 0) { + if (errno == EPIPE) + errno = ECONNRESET; + if (errno != EMSGSIZE) + return -1; + if (!(chunksize >>= 1)) + return -1; + continue; + } + +#ifdef DEBUG_MODE + fprintf(stderr, "\033[31m"); + fwrite(&ctx->outbound[ctx->outbound_tail], (size_t)sent, 1U, stderr); + fprintf(stderr, "\033[m"); + fflush(stderr); +#endif + + ctx->outbound_tail += (size_t)sent; + } + + return 0; +} diff --git a/libcoopgamma_get_crtcs_recv.c b/libcoopgamma_get_crtcs_recv.c new file mode 100644 index 0000000..16e9ba5 --- /dev/null +++ b/libcoopgamma_get_crtcs_recv.c @@ -0,0 +1,70 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * List all available CRTC:s, receive response part + * + * @param ctx The state of the library, must be connected + * @param async Information about the request + * @return A `NULL`-terminated list of names. You should only free + * the outer pointer, inner pointers are subpointers of the + * outer pointer and cannot be freed. `NULL` on error, in + * which case `ctx->error` (rather than `errno`) is read + * for information about the error. + */ +char ** +libcoopgamma_get_crtcs_recv(libcoopgamma_context_t *restrict ctx, libcoopgamma_async_context_t *restrict async) +{ + char *line; + char *payload; + char *end; + int command_ok = 0; + size_t i, n, lines, len, length; + char **rc; + + if (libcoopgamma_check_error__(ctx, async)) + return NULL; + + for (;;) { + line = libcoopgamma_next_header__(ctx); + if (!*line) + break; + else if (!strcmp(line, "Command: crtc-enumeration")) + command_ok = 1; + } + + payload = libcoopgamma_next_payload__(ctx, &n); + + if (!command_ok || (n > 0 && payload[n - 1U] != '\n')) { + errno = EBADMSG; + copy_errno(ctx); + return NULL; + } + + line = payload; + end = &payload[n]; + lines = length = 0; + while (line != end) { + lines += 1U; + length += len = (size_t)(&strchr(line, '\n')[1U] - line); + line += len; + line[-1] = '\0'; + } + + rc = malloc((lines + 1U) * sizeof(char *) + length); + if (!rc) { + copy_errno(ctx); + return NULL; + } + + line = ((char *)rc) + (lines + 1U) * sizeof(char *); + memcpy(line, payload, length); + rc[lines] = NULL; + for (i = 0; i < lines; i++) { + rc[i] = line; + line = &strchr(line, '\0')[1U]; + } + + return rc; +} diff --git a/libcoopgamma_get_crtcs_send.c b/libcoopgamma_get_crtcs_send.c new file mode 100644 index 0000000..6c740b7 --- /dev/null +++ b/libcoopgamma_get_crtcs_send.c @@ -0,0 +1,37 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/* Silence falls warning for `memcpy(&msg__[n__], (payload), (payload_size))` + * in `SEND_MESSAGE` where `payload` is assumed to be `NULL` despite being + * inside `if (payload)` */ +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wnonnull" +#endif + + +/** + * List all available CRTC:s, send request part + * + * Cannot be used before connecting to the server + * + * @param ctx The state of the library, must be connected + * @param async Information about the request, that is needed to + * identify and parse the response, is stored here + * @return Zero on success, -1 on error + */ +int +libcoopgamma_get_crtcs_send(libcoopgamma_context_t *restrict ctx, libcoopgamma_async_context_t *restrict async) +{ + async->message_id = ctx->message_id; + SEND_MESSAGE(ctx, NULL, (size_t)0, + "Command: enumerate-crtcs\n" + "Message ID: %" PRIu32 "\n" + "\n", + ctx->message_id); + + return 0; +fail: + copy_errno(ctx); + return -1; +} diff --git a/libcoopgamma_get_crtcs_sync.c b/libcoopgamma_get_crtcs_sync.c new file mode 100644 index 0000000..91f31b8 --- /dev/null +++ b/libcoopgamma_get_crtcs_sync.c @@ -0,0 +1,27 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * List all available CRTC:s, synchronous version + * + * This is a synchronous request function, as such, + * you have to ensure that communication is blocking + * (default), and that there are not asynchronous + * requests waiting, it also means that EINTR:s are + * silently ignored and there no wait to cancel the + * operation without disconnection from the server + * + * @param ctx The state of the library, must be connected + * @return A `NULL`-terminated list of names. You should only free + * the outer pointer, inner pointers are subpointers of the + * outer pointer and cannot be freed. `NULL` on error, in + * which case `ctx->error` (rather than `errno`) is read + * for information about the error. + */ +char ** +libcoopgamma_get_crtcs_sync(libcoopgamma_context_t *restrict ctx) +{ + SYNC_CALL(libcoopgamma_get_crtcs_send(ctx, &async), + libcoopgamma_get_crtcs_recv(ctx, &async), (copy_errno(ctx), NULL)); +} diff --git a/libcoopgamma_get_gamma_info_recv.c b/libcoopgamma_get_gamma_info_recv.c new file mode 100644 index 0000000..763abf2 --- /dev/null +++ b/libcoopgamma_get_gamma_info_recv.c @@ -0,0 +1,130 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Retrieve information about a CRTC:s gamma ramps, receive response part + * + * @param info Output parameter for the information, must be initialised + * @param ctx The state of the library, must be connected + * @param async Information about the request + * @return Zero on success, -1 on error, in which case `ctx->error` + * (rather than `errno`) is read for information about the error + */ +int +libcoopgamma_get_gamma_info_recv(libcoopgamma_crtc_info_t *restrict info, libcoopgamma_context_t *restrict ctx, + libcoopgamma_async_context_t *restrict async) +{ + char temp[3 * sizeof(size_t) + 1]; + char *line; + char *value; + size_t _n; + int have_cooperative = 0, have_gamma_support = 0, have_colourspace = 0; + int have_depth = 0, have_white_x = 0, have_white_y = 0; + int have_red_size = 0, have_red_x = 0, have_red_y = 0; + int have_green_size = 0, have_green_x = 0, have_green_y = 0; + int have_blue_size = 0, have_blue_x = 0, have_blue_y = 0; + int bad = 0, r = 0, g = 0, b = 0, x = 0; + size_t *outz; + unsigned *outu; + int *have; + + if (libcoopgamma_check_error__(ctx, async)) + return -1; + + info->cooperative = 0; /* Should be in the response, but ... */ + info->colourspace = LIBCOOPGAMMA_UNKNOWN; + + for (;;) { + line = libcoopgamma_next_header__(ctx); + value = &strchr(line, ':')[2U]; + if (!*line) { + break; + } else if (strstr(line, "Cooperative: ") == line) { + have_cooperative = 1 + !!have_cooperative; + if (!strcmp(value, "yes")) info->cooperative = 1; + else if (!strcmp(value, "no")) info->cooperative = 0; + else + bad = 1; + } else if (strstr(line, "Depth: ") == line) { + have_depth = 1 + !!have_depth; + if (!strcmp(value, "8")) info->depth = LIBCOOPGAMMA_UINT8; + else if (!strcmp(value, "16")) info->depth = LIBCOOPGAMMA_UINT16; + else if (!strcmp(value, "32")) info->depth = LIBCOOPGAMMA_UINT32; + else if (!strcmp(value, "64")) info->depth = LIBCOOPGAMMA_UINT64; + else if (!strcmp(value, "f")) info->depth = LIBCOOPGAMMA_FLOAT; + else if (!strcmp(value, "d")) info->depth = LIBCOOPGAMMA_DOUBLE; + else + bad = 1; + } else if (strstr(line, "Gamma support: ") == line) { + have_gamma_support = 1 + !!have_gamma_support; + if (!strcmp(value, "yes")) info->supported = LIBCOOPGAMMA_YES; + else if (!strcmp(value, "no")) info->supported = LIBCOOPGAMMA_NO; + else if (!strcmp(value, "maybe")) info->supported = LIBCOOPGAMMA_MAYBE; + else + bad = 1; + } else if ((r = (strstr(line, "Red size: ") == line)) || + (g = (strstr(line, "Green size: ") == line)) || + strstr(line, "Blue size: ") == line) { + if (r) have_red_size = 1 + !!have_red_size, outz = &info->red_size; + else if (g) have_green_size = 1 + !!have_green_size, outz = &info->green_size; + else have_blue_size = 1 + !!have_blue_size, outz = &info->blue_size; + *outz = (size_t)atol(value); + sprintf(temp, "%zu", *outz); + if (strcmp(value, temp)) + bad = 1; + } else if ((x = r = (strstr(line, "Red x: ") == line)) || + (r = (strstr(line, "Red y: ") == line)) || + (x = g = (strstr(line, "Green x: ") == line)) || + (g = (strstr(line, "Green y: ") == line)) || + (x = b = (strstr(line, "Blue x: ") == line)) || + (b = (strstr(line, "Blue y: ") == line)) || + (x = (strstr(line, "White x: ") == line)) || + strstr(line, "White y: ") == line) { + if (r && x) have = &have_red_x, outu = &info->red_x; + else if (r) have = &have_red_y, outu = &info->red_y; + else if (g && x) have = &have_green_x, outu = &info->green_x; + else if (g) have = &have_green_y, outu = &info->green_y; + else if (b && x) have = &have_blue_x, outu = &info->blue_x; + else if (b) have = &have_blue_y, outu = &info->blue_y; + else if (x) have = &have_white_x, outu = &info->white_x; + else have = &have_white_y, outu = &info->white_y; + *have = 1 + !!*have; + *outu = (unsigned)atoi(value); + sprintf(temp, "%u", *outu); + if (strcmp(value, temp)) + bad = 1; + } else if (strstr(line, "Colour space: ") == line) { + have_colourspace = 1 + !!have_colourspace; + if (!strcmp(value, "sRGB")) info->colourspace = LIBCOOPGAMMA_SRGB; + else if (!strcmp(value, "RGB")) info->colourspace = LIBCOOPGAMMA_RGB; + else if (!strcmp(value, "non-RGB")) info->colourspace = LIBCOOPGAMMA_NON_RGB; + else if (!strcmp(value, "grey")) info->colourspace = LIBCOOPGAMMA_GREY; + else + info->colourspace = LIBCOOPGAMMA_UNKNOWN; + } + } + + (void) libcoopgamma_next_payload__(ctx, &_n); + + info->have_gamut = (have_red_x && have_green_x && have_blue_x && have_white_x && + have_red_y && have_green_y && have_blue_y && have_white_y); + + if (bad || have_gamma_support != 1 || have_red_x > 1 || have_red_y > 1 || + have_green_x > 1 || have_green_y > 1 || have_blue_x > 1 || have_blue_y > 1 || + have_white_x > 1 || have_white_y > 1 || have_colourspace > 1) { + errno = EBADMSG; + copy_errno(ctx); + return -1; + } + if (info->supported != LIBCOOPGAMMA_NO) { + if (have_cooperative > 1 || have_depth != 1 || have_gamma_support != 1 || + have_red_size != 1 || have_green_size != 1 || have_blue_size != 1) { + errno = EBADMSG; + copy_errno(ctx); + return -1; + } + } + + return 0; +} diff --git a/libcoopgamma_get_gamma_info_send.c b/libcoopgamma_get_gamma_info_send.c new file mode 100644 index 0000000..a00a7e7 --- /dev/null +++ b/libcoopgamma_get_gamma_info_send.c @@ -0,0 +1,52 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/* Silence falls warning for `memcpy(&msg__[n__], (payload), (payload_size))` + * in `SEND_MESSAGE` where `payload` is assumed to be `NULL` despite being + * inside `if (payload)` */ +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wnonnull" +#endif + + +/** + * Retrieve information about a CRTC:s gamma ramps, send request part + * + * Cannot be used before connecting to the server + * + * @param crtc The name of the CRTC + * @param ctx The state of the library, must be connected + * @param async Information about the request, that is needed to + * identify and parse the response, is stored here + * @return Zero on success, -1 on error + */ +int +libcoopgamma_get_gamma_info_send(const char *restrict crtc, libcoopgamma_context_t *restrict ctx, + libcoopgamma_async_context_t *restrict async) +{ +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (crtc == NULL || strchr(crtc, '\n')) { + errno = EINVAL; + goto fail; + } +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic pop +#endif + + async->message_id = ctx->message_id; + SEND_MESSAGE(ctx, NULL, (size_t)0, + "Command: get-gamma-info\n" + "Message ID: %" PRIu32 "\n" + "CRTC: %s\n" + "\n", + ctx->message_id, crtc); + + return 0; +fail: + copy_errno(ctx); + return 0; +} diff --git a/libcoopgamma_get_gamma_info_sync.c b/libcoopgamma_get_gamma_info_sync.c new file mode 100644 index 0000000..2c83dda --- /dev/null +++ b/libcoopgamma_get_gamma_info_sync.c @@ -0,0 +1,27 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Retrieve information about a CRTC:s gamma ramps, synchronous version + * + * This is a synchronous request function, as such, + * you have to ensure that communication is blocking + * (default), and that there are not asynchronous + * requests waiting, it also means that EINTR:s are + * silently ignored and there no wait to cancel the + * operation without disconnection from the server + * + * @param crtc The name of the CRTC + * @param info Output parameter for the information, must be initialised + * @param ctx The state of the library, must be connected + * @return Zero on success, -1 on error, in which case `ctx->error` + * (rather than `errno`) is read for information about the error + */ +int +libcoopgamma_get_gamma_info_sync(const char *restrict ctrc, libcoopgamma_crtc_info_t *restrict info, + libcoopgamma_context_t *restrict ctx) +{ + SYNC_CALL(libcoopgamma_get_gamma_info_send(ctrc, ctx, &async), + libcoopgamma_get_gamma_info_recv(info, ctx, &async), (copy_errno(ctx), -1)); +} diff --git a/libcoopgamma_get_gamma_recv.c b/libcoopgamma_get_gamma_recv.c new file mode 100644 index 0000000..4207184 --- /dev/null +++ b/libcoopgamma_get_gamma_recv.c @@ -0,0 +1,148 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Retrieve the current gamma ramp adjustments, receive response part + * + * @param table Output for the response, must be initialised + * @param ctx The state of the library, must be connected + * @param async Information about the request + * @return Zero on success, -1 on error, in which case `ctx->error` + * (rather than `errno`) is read for information about the error + */ +int +libcoopgamma_get_gamma_recv(libcoopgamma_filter_table_t *restrict table, libcoopgamma_context_t *restrict ctx, + libcoopgamma_async_context_t *restrict async) +{ + char temp[3 * sizeof(size_t) + 1]; + char *line; + char *value; + char *payload; + size_t i, n, width, clutsize; + int have_depth = 0; + int have_red_size = 0; + int have_green_size = 0; + int have_blue_size = 0; + int have_tables = 0; + int bad = 0, r = 0, g = 0; + size_t *out; + size_t off, len; + + if (libcoopgamma_check_error__(ctx, async)) + return -1; + + libcoopgamma_filter_table_destroy(table); + + for (;;) { + line = libcoopgamma_next_header__(ctx); + value = &strchr(line, ':')[2U]; + if (!*line) { + break; + } else if (strstr(line, "Depth: ") == line) { + have_depth = 1 + !!have_depth; + if (!strcmp(value, "8")) table->depth = LIBCOOPGAMMA_UINT8; + else if (!strcmp(value, "16")) table->depth = LIBCOOPGAMMA_UINT16; + else if (!strcmp(value, "32")) table->depth = LIBCOOPGAMMA_UINT32; + else if (!strcmp(value, "64")) table->depth = LIBCOOPGAMMA_UINT64; + else if (!strcmp(value, "f")) table->depth = LIBCOOPGAMMA_FLOAT; + else if (!strcmp(value, "d")) table->depth = LIBCOOPGAMMA_DOUBLE; + else + bad = 1; + } else if ((r = (strstr(line, "Red size: ") == line)) || + (g = (strstr(line, "Green size: ") == line)) || + strstr(line, "Blue size: ") == line) { + if (r) have_red_size = 1 + !!have_red_size, out = &table->red_size; + else if (g) have_green_size = 1 + !!have_green_size, out = &table->green_size; + else have_blue_size = 1 + !!have_blue_size, out = &table->blue_size; + *out = (size_t)atol(value); + sprintf(temp, "%zu", *out); + if (strcmp(value, temp)) + bad = 1; + } else if (strstr(line, "Tables: ") == line) { + have_tables = 1 + have_tables; + table->filter_count = (size_t)atol(value); + sprintf(temp, "%zu", table->filter_count); + if (strcmp(value, temp)) + bad = 1; + } + } + + payload = libcoopgamma_next_payload__(ctx, &n); + + if (bad || have_depth != 1 || have_red_size != 1 || have_green_size != 1 || + have_blue_size != 1 || (async->coalesce ? have_tables > 1 : !have_tables) || + ((!payload || !n) && (async->coalesce || table->filter_count > 0)) || + (n > 0 && have_tables && !table->filter_count) || + (async->coalesce && have_tables && table->filter_count != 1)) + goto bad; + + switch (table->depth) { + case LIBCOOPGAMMA_FLOAT: width = sizeof(float); break; + case LIBCOOPGAMMA_DOUBLE: width = sizeof(double); break; + default: INTEGRAL_DEPTHS + if (table->depth <= 0 || (table->depth & 7)) + goto bad; + width = (size_t)(table->depth / 8); + break; + } + + clutsize = table->red_size + table->green_size + table->blue_size; + clutsize *= width; + + if (async->coalesce) { + if (n != clutsize) + goto bad; + table->filters = malloc(sizeof(*(table->filters))); + if (!table->filters) + goto fail; + table->filters->priority = 0; + table->filters->class = NULL; + table->filters->ramps.u8.red_size = table->red_size; + table->filters->ramps.u8.green_size = table->green_size; + table->filters->ramps.u8.blue_size = table->blue_size; + if (libcoopgamma_ramps_initialise_(&table->filters->ramps, width) < 0) + goto fail; + memcpy(table->filters->ramps.u8.red, payload, clutsize); + table->filter_count = 1; + } else if (!table->filter_count) { + table->filters = NULL; + } else { + off = 0; + table->filters = calloc(table->filter_count, sizeof(*table->filters)); + if (!table->filters) + goto fail; + for (i = 0; i < table->filter_count; i++) { + if (off + sizeof(int64_t) > n) + goto bad; + table->filters[i].priority = *(int64_t *)&payload[off]; + off += sizeof(int64_t); + if (!memchr(&payload[off], '\0', n - off)) + goto bad; + len = strlen(&payload[off]) + 1U; + table->filters[i].class = malloc(len); + if (!table->filters[i].class) + goto fail; + memcpy(table->filters[i].class, &payload[off], len); + off += len; + if (off + clutsize > n) + goto bad; + table->filters[i].ramps.u8.red_size = table->red_size; + table->filters[i].ramps.u8.green_size = table->green_size; + table->filters[i].ramps.u8.blue_size = table->blue_size; + if (libcoopgamma_ramps_initialise_(&table->filters[i].ramps, width) < 0) + goto fail; + memcpy(table->filters[i].ramps.u8.red, &payload[off], clutsize); + off += clutsize; + } + if (off != n) + goto bad; + } + + return 0; +bad: + errno = EBADMSG; +fail: + copy_errno(ctx); + return -1; +} diff --git a/libcoopgamma_get_gamma_send.c b/libcoopgamma_get_gamma_send.c new file mode 100644 index 0000000..99b6da4 --- /dev/null +++ b/libcoopgamma_get_gamma_send.c @@ -0,0 +1,57 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/* Silence falls warning for `memcpy(&msg__[n__], (payload), (payload_size))` + * in `SEND_MESSAGE` where `payload` is assumed to be `NULL` despite being + * inside `if (payload)` */ +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wnonnull" +#endif + + +/** + * Retrieve the current gamma ramp adjustments, send request part + * + * Cannot be used before connecting to the server + * + * @param query The query to send + * @param ctx The state of the library, must be connected + * @param async Information about the request, that is needed to + * identify and parse the response, is stored here + * @return Zero on success, -1 on error + */ +int +libcoopgamma_get_gamma_send(const libcoopgamma_filter_query_t *restrict query, libcoopgamma_context_t *restrict ctx, + libcoopgamma_async_context_t *restrict async) +{ +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (!query || !query->crtc || strchr(query->crtc, '\n')) { + errno = EINVAL; + goto fail; + } +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic pop +#endif + + async->message_id = ctx->message_id; + async->coalesce = query->coalesce; + SEND_MESSAGE(ctx, NULL, (size_t)0, + "Command: get-gamma\n" + "Message ID: %" PRIu32 "\n" + "CRTC: %s\n" + "Coalesce: %s\n" + "High priority: %" PRIi64 "\n" + "Low priority: %" PRIi64 "\n" + "\n", + ctx->message_id, query->crtc, query->coalesce ? "yes" : "no", + query->high_priority, query->low_priority); + + return 0; +fail: + copy_errno(ctx); + return -1; +} diff --git a/libcoopgamma_get_gamma_sync.c b/libcoopgamma_get_gamma_sync.c new file mode 100644 index 0000000..ad93b7c --- /dev/null +++ b/libcoopgamma_get_gamma_sync.c @@ -0,0 +1,27 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Retrieve the current gamma ramp adjustments, synchronous version + * + * This is a synchronous request function, as such, + * you have to ensure that communication is blocking + * (default), and that there are not asynchronous + * requests waiting, it also means that EINTR:s are + * silently ignored and there no wait to cancel the + * operation without disconnection from the server + * + * @param query The query to send + * @param table Output for the response, must be initialised + * @param ctx The state of the library, must be connected + * @return Zero on success, -1 on error, in which case `ctx->error` + * (rather than `errno`) is read for information about the error + */ +int +libcoopgamma_get_gamma_sync(const libcoopgamma_filter_query_t *restrict query, libcoopgamma_filter_table_t *restrict table, + libcoopgamma_context_t *restrict ctx) +{ + SYNC_CALL(libcoopgamma_get_gamma_send(query, ctx, &async), + libcoopgamma_get_gamma_recv(table, ctx, &async), (copy_errno(ctx), -1)); +} diff --git a/libcoopgamma_get_method_and_site.c b/libcoopgamma_get_method_and_site.c new file mode 100644 index 0000000..73de048 --- /dev/null +++ b/libcoopgamma_get_method_and_site.c @@ -0,0 +1,76 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Get the adjustment method and site + * + * SIGCHLD must not be ignored or blocked + * + * @param method The adjustment method, `NULL` for automatic + * @param site The site, `NULL` for automatic + * @param methodp Output pointer for the selected adjustment method, + * which cannot be `NULL`. It is safe to call + * this function with this parameter set to `NULL`. + * @param sitep Output pointer for the selected site, which will + * be `NULL` the method only supports one site or if + * `site == NULL` and no site can be selected + * automatically. It is safe to call this function + * with this parameter set to `NULL`. + * @return Zero on success, -1 on error + */ +int +libcoopgamma_get_method_and_site(const char *restrict method, const char *restrict site, + char **restrict methodp, char **restrict sitep) +{ + int saved_errno; + char *raw; + char *p; + char *q; + + raw = libcoopgamma_query__(method, site, "-q"); + if (!raw) + return -1; + + if (methodp) *methodp = NULL; + if (sitep) *sitep = NULL; + + p = strchr(raw, '\n'); + if (!p) { + errno = EBADMSG; + goto fail; + } + *p++ = '\0'; + + if (methodp) { + *methodp = malloc(strlen(raw) + 1U); + if (!*methodp) + goto fail; + strcpy(*methodp, raw); + } + + if (site && *(q = &strchr(p, '\0')[-1])) { + if (*q != '\n') { + errno = EBADMSG; + goto fail; + } + *q = '\0'; + *sitep = malloc(strlen(p) + 1U); + if (!*sitep) + goto fail; + strcpy(*sitep, p); + } + + free(raw); + return 0; + +fail: + saved_errno = errno; + if (methodp) { + free(*methodp); + *methodp = NULL; + } + free(raw); + errno = saved_errno; + return -1; +} diff --git a/libcoopgamma_get_methods.c b/libcoopgamma_get_methods.c new file mode 100644 index 0000000..2761bd1 --- /dev/null +++ b/libcoopgamma_get_methods.c @@ -0,0 +1,67 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * List all recognised adjustment method + * + * SIGCHLD must not be ignored or blocked + * + * @return A `NULL`-terminated list of names. You should only free + * the outer pointer, inner pointers are subpointers of the + * outer pointer and cannot be freed. `NULL` on error. + */ +char ** +libcoopgamma_get_methods(void) +{ + char num[5]; /* The size is base on the fact that we have limited `n` in the loop below */ + char **methods = NULL; + char *method; + char **rc; + char *buffer; + size_t n = 0; + size_t size = 0; + void *new; + + methods = malloc(4U * sizeof(*methods)); + if (!methods) + goto fail; + + for (n = 0; n < 10000 /* just to be safe */; n++) { + if (n >= 4 && (n & (~n + 1U)) == n) { + new = realloc(methods, (n << 1) * sizeof(*methods)); + if (!new) + goto fail; + methods = new; + } + sprintf(num, "%zu", n); + if (libcoopgamma_get_method_and_site(num, NULL, &method, NULL)) + goto fail; + if (!strcmp(method, num)) { + free(method); + break; + } + methods[n] = method; + size += strlen(method) + 1U; + } + + rc = malloc((n + 1U) * sizeof(char *) + size); + if (!rc) + goto fail; + buffer = (char *)&rc[n + 1U]; + rc[n] = NULL; + while (n--) { + rc[n] = buffer; + buffer = &stpcpy(buffer, methods[n])[1U]; + free(methods[n]); + } + free(methods); + + return rc; + +fail: + while (n--) + free(methods[n]); + free(methods); + return NULL; +} diff --git a/libcoopgamma_get_pid_file.c b/libcoopgamma_get_pid_file.c new file mode 100644 index 0000000..45afd1b --- /dev/null +++ b/libcoopgamma_get_pid_file.c @@ -0,0 +1,35 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Get the PID file of the coopgamma server + * + * SIGCHLD must not be ignored or blocked + * + * @param method The adjustment method, `NULL` for automatic + * @param site The site, `NULL` for automatic + * @return The pathname of the server's PID file, `NULL` on error + * or if there server does not use PID files. The later + * case is detected by checking that `errno` is set to 0. + */ +char * +libcoopgamma_get_pid_file(const char *restrict method, const char *restrict site) +{ + char *path; + size_t n; + + path = libcoopgamma_get_socket_file(method, site); + if (!path) + return NULL; + + n = strlen(path); + if (n < 7U || strcmp(&path[n - 7U], ".socket")) { + free(path); + errno = EBADMSG; + return NULL; + } + + stpcpy(&path[n - 7U], ".pid"); + return path; +} diff --git a/libcoopgamma_get_socket_file.c b/libcoopgamma_get_socket_file.c new file mode 100644 index 0000000..3ac9a2b --- /dev/null +++ b/libcoopgamma_get_socket_file.c @@ -0,0 +1,43 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Get the socket file of the coopgamma server + * + * SIGCHLD must not be ignored or blocked + * + * @param method The adjustment method, `NULL` for automatic + * @param site The site, `NULL` for automatic + * @return The pathname of the server's socket, `NULL` on error + * or if there server does have its own socket. The later + * case is detected by checking that `errno` is set to 0, + * and is the case when communicating with a server in a + * multi-server display server like mds. + */ +char * +libcoopgamma_get_socket_file(const char *restrict method, const char *restrict site) +{ + char *raw; + char *p; + + raw = libcoopgamma_query__(method, site, "-qq"); + if (!raw) + return NULL; + + p = &strchr(raw, '\0')[-1]; + if (p < raw || *p != '\n') { + errno = EBADMSG; + goto fail; + } + *p = '\0'; + if (!*raw) { + errno = EBADMSG; + goto fail; + } + + return raw; +fail: + free(raw); + return NULL; +} diff --git a/libcoopgamma_queried_filter_destroy.c b/libcoopgamma_queried_filter_destroy.c new file mode 100644 index 0000000..fffa11f --- /dev/null +++ b/libcoopgamma_queried_filter_destroy.c @@ -0,0 +1,20 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Release all resources allocated to a `libcoopgamma_queried_filter_t`, + * the allocation of the record itself is not freed + * + * Always call this function after failed call to `libcoopgamma_queried_filter_initialise` + * or failed call to `libcoopgamma_queried_filter_unmarshal` + * + * @param this The record to destroy + */ +void +libcoopgamma_queried_filter_destroy(libcoopgamma_queried_filter_t *restrict this) +{ + free(this->class); + this->class = NULL; + libcoopgamma_ramps_destroy(&this->ramps.u8); +} diff --git a/libcoopgamma_queried_filter_initialise.c b/libcoopgamma_queried_filter_initialise.c new file mode 100644 index 0000000..b289949 --- /dev/null +++ b/libcoopgamma_queried_filter_initialise.c @@ -0,0 +1,16 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Initialise a `libcoopgamma_queried_filter_t` + * + * @param this The record to initialise + * @return Zero on success, -1 on error + */ +int +libcoopgamma_queried_filter_initialise(libcoopgamma_queried_filter_t *restrict this) +{ + memset(this, 0, sizeof(*this)); + return 0; +} diff --git a/libcoopgamma_queried_filter_marshal.c b/libcoopgamma_queried_filter_marshal.c new file mode 100644 index 0000000..054ebfa --- /dev/null +++ b/libcoopgamma_queried_filter_marshal.c @@ -0,0 +1,34 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Marshal a `libcoopgamma_queried_filter_t` into a buffer + * + * @param this The record to marshal + * @param vbuf The output buffer, `NULL` to only measure + * how large this buffer has to be + * @param depth The type used of ramp stops + * @return The number of marshalled bytes, or if `buf == NULL`, + * how many bytes would be marshalled if `buf != NULL` + */ +size_t +libcoopgamma_queried_filter_marshal(const libcoopgamma_queried_filter_t *restrict this, + void *restrict vbuf, libcoopgamma_depth_t depth) +{ + MARSHAL_PROLOGUE; + marshal_version(LIBCOOPGAMMA_QUERIED_FILTER_VERSION); + marshal_prim(this->priority, int64_t); + marshal_string(this->class); + switch (depth) { + case LIBCOOPGAMMA_UINT8: off += libcoopgamma_ramps_marshal(&this->ramps.u8, SUBBUF); break; + case LIBCOOPGAMMA_UINT16: off += libcoopgamma_ramps_marshal(&this->ramps.u16, SUBBUF); break; + case LIBCOOPGAMMA_UINT32: off += libcoopgamma_ramps_marshal(&this->ramps.u32, SUBBUF); break; + case LIBCOOPGAMMA_UINT64: off += libcoopgamma_ramps_marshal(&this->ramps.u64, SUBBUF); break; + case LIBCOOPGAMMA_FLOAT: off += libcoopgamma_ramps_marshal(&this->ramps.f, SUBBUF); break; + case LIBCOOPGAMMA_DOUBLE: off += libcoopgamma_ramps_marshal(&this->ramps.d, SUBBUF); break; + default: + break; + } + MARSHAL_EPILOGUE; +} diff --git a/libcoopgamma_queried_filter_unmarshal.c b/libcoopgamma_queried_filter_unmarshal.c new file mode 100644 index 0000000..99d6bc3 --- /dev/null +++ b/libcoopgamma_queried_filter_unmarshal.c @@ -0,0 +1,40 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Unmarshal a `libcoopgamma_queried_filter_t` from a buffer + * + * @param this The output parameter for unmarshalled record + * @param vbuf The buffer with the marshalled record + * @param np Output parameter for the number of unmarshalled bytes, undefined on failure + * @param depth The type used of ramp stops + * @return `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`, + * `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET` + */ +int +libcoopgamma_queried_filter_unmarshal(libcoopgamma_queried_filter_t *restrict this, const void *restrict vbuf, + size_t *restrict np, libcoopgamma_depth_t depth) +{ + int r = LIBCOOPGAMMA_SUCCESS; + size_t n = 0; + UNMARSHAL_PROLOGUE; + memset(this, 0, sizeof(*this)); + unmarshal_version(LIBCOOPGAMMA_QUERIED_FILTER_VERSION); + unmarshal_prim(this->priority, int64_t); + unmarshal_string(this->class); + switch (depth) { + case LIBCOOPGAMMA_UINT8: r = libcoopgamma_ramps_unmarshal(&this->ramps.u8, NNSUBBUF, &n); break; + case LIBCOOPGAMMA_UINT16: r = libcoopgamma_ramps_unmarshal(&this->ramps.u16, NNSUBBUF, &n); break; + case LIBCOOPGAMMA_UINT32: r = libcoopgamma_ramps_unmarshal(&this->ramps.u32, NNSUBBUF, &n); break; + case LIBCOOPGAMMA_UINT64: r = libcoopgamma_ramps_unmarshal(&this->ramps.u64, NNSUBBUF, &n); break; + case LIBCOOPGAMMA_FLOAT: r = libcoopgamma_ramps_unmarshal(&this->ramps.f, NNSUBBUF, &n); break; + case LIBCOOPGAMMA_DOUBLE: r = libcoopgamma_ramps_unmarshal(&this->ramps.d, NNSUBBUF, &n); break; + default: + break; + } + if (r != LIBCOOPGAMMA_SUCCESS) + return r; + off += n; + UNMARSHAL_EPILOGUE; +} diff --git a/libcoopgamma_query__.c b/libcoopgamma_query__.c new file mode 100644 index 0000000..706a3cf --- /dev/null +++ b/libcoopgamma_query__.c @@ -0,0 +1,119 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Run coopgammad with -q or -qq and return the response + * + * SIGCHLD must not be ignored or blocked + * + * @param method The adjustment method, `NULL` for automatic + * @param site The site, `NULL` for automatic + * @param arg "-q" or "-qq", which shall be passed to coopgammad? + * @return The output of coopgammad, `NULL` on error. This + * will be NUL-terminated and will not contain any + * other `NUL` bytes. + */ +char * +libcoopgamma_query__(const char *restrict method, const char *restrict site, const char *restrict arg) +{ + const char *(args[7]) = {COOPGAMMAD, arg}; + size_t i = 2, n = 0, size = 0; + int pipe_rw[2] = { -1, -1 }; + pid_t pid; + int saved_errno, status; + char *msg = NULL; + ssize_t got; + void *new; + + if (method) args[i++] = "-m", args[i++] = method; + if (site) args[i++] = "-s", args[i++] = site; + args[i] = NULL; + + if (pipe(pipe_rw) < 0) + goto fail; + + switch ((pid = fork())) { + case -1: + goto fail; + + case 0: + /* Child */ + close(pipe_rw[0]); + if (pipe_rw[1] != STDOUT_FILENO) { + close(STDOUT_FILENO); + if (dup2(pipe_rw[1], STDOUT_FILENO) < 0) + goto fail_child; + close(pipe_rw[1]); + } +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-qual" +#endif + execvp(COOPGAMMAD, (char* const*)(args)); +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + fail_child: + saved_errno = errno; + perror(NAME_OF_THE_PROCESS); + if (write(STDOUT_FILENO, &saved_errno, sizeof(int)) != sizeof(int)) + perror(NAME_OF_THE_PROCESS); + exit(1); + + default: + /* Parent */ + close(pipe_rw[1]), pipe_rw[1] = -1; + for (;;) { + if (n == size) { + new = realloc(msg, size = (n ? (n << 1) : 256U)); + if (!new) + goto fail; + msg = new; + } + got = read(pipe_rw[0], &msg[n], size - n); + if (got < 0) { + if (errno == EINTR) + continue; + goto fail; + } else if (got == 0) { + break; + } + n += (size_t)got; + } + close(pipe_rw[0]), pipe_rw[0] = -1; + if (waitpid(pid, &status, 0) < 0) + goto fail; + if (status) { + errno = EINVAL; + if (n == sizeof(int) && *(int *)msg) + errno = *(int*)msg; + } + break; + } + + if (n == size) { + new = realloc(msg, n + 1U); + if (!new) + goto fail; + msg = new; + } + msg[n] = '\0'; + + if (strchr(msg, '\0') != msg + n) { + errno = EBADMSG; + goto fail; + } + + return msg; + +fail: + saved_errno = errno; + if (pipe_rw[0] >= 0) + close(pipe_rw[0]); + if (pipe_rw[1] >= 0) + close(pipe_rw[1]); + free(msg); + errno = saved_errno; + return NULL; +} diff --git a/libcoopgamma_ramps_destroy.c b/libcoopgamma_ramps_destroy.c new file mode 100644 index 0000000..f69d4b6 --- /dev/null +++ b/libcoopgamma_ramps_destroy.c @@ -0,0 +1,22 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Release all resources allocated to a `libcoopgamma_ramps8_t`, `libcoopgamma_ramps16_t`, + * `libcoopgamma_ramps32_t`, `libcoopgamma_ramps64_t`, `libcoopgamma_rampsf_t`, + * `libcoopgamma_rampsd_t`, or `libcoopgamma_ramps_t`, the allocation of the record + * itself is not freed + * + * Always call this function after failed call to `libcoopgamma_ramps_initialise` + * or failed call to `libcoopgamma_ramps_unmarshal` + * + * @param this The record to destroy + */ +void +libcoopgamma_ramps_destroy(void *restrict this) +{ + libcoopgamma_ramps8_t *restrict this8 = (libcoopgamma_ramps8_t *restrict)this; + free(this8->red); + this8->red = this8->green = this8->blue = NULL; +} diff --git a/libcoopgamma_ramps_initialise_.c b/libcoopgamma_ramps_initialise_.c new file mode 100644 index 0000000..0d6f070 --- /dev/null +++ b/libcoopgamma_ramps_initialise_.c @@ -0,0 +1,26 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Initialise a `libcoopgamma_ramps8_t`, `libcoopgamma_ramps16_t`, `libcoopgamma_ramps32_t`, + * `libcoopgamma_ramps64_t`, `libcoopgamma_rampsf_t`, or `libcoopgamma_rampsd_t` + * + * `this->red_size`, `this->green_size`, and `this->blue_size` must already be set + * + * @param this The record to initialise + * @param width The `sizeof(*(this->red))` + * @return Zero on success, -1 on error + */ +int +libcoopgamma_ramps_initialise_(void *restrict this, size_t width) +{ + libcoopgamma_ramps8_t *restrict this8 = (libcoopgamma_ramps8_t *restrict)this; + this8->red = this8->green = this8->blue = NULL; + this8->red = malloc((this8->red_size + this8->green_size + this8->blue_size) * width); + if (!this8->red) + return -1; + this8->green = this8->red + this8->red_size * width; + this8->blue = this8->green + this8->green_size * width; + return 0; +} diff --git a/libcoopgamma_ramps_marshal_.c b/libcoopgamma_ramps_marshal_.c new file mode 100644 index 0000000..65b5571 --- /dev/null +++ b/libcoopgamma_ramps_marshal_.c @@ -0,0 +1,27 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Marshal a `libcoopgamma_ramps8_t`, `libcoopgamma_ramps16_t`, `libcoopgamma_ramps32_t`, + * `libcoopgamma_ramps64_t`, `libcoopgamma_rampsf_t`, or `libcoopgamma_rampsd_t` into a buffer + * + * @param this The record to marshal + * @param vbuf The output buffer, `NULL` to only measure + * how large this buffer has to be + * @param width The `sizeof(*(this->red))` + * @return The number of marshalled bytes, or if `buf == NULL`, + * how many bytes would be marshalled if `buf != NULL` + */ +size_t +libcoopgamma_ramps_marshal_(const void *restrict this, void *restrict vbuf, size_t width) +{ + const libcoopgamma_ramps8_t *restrict this8 = (const libcoopgamma_ramps8_t *restrict)this; + MARSHAL_PROLOGUE; + marshal_version(LIBCOOPGAMMA_RAMPS_VERSION); + marshal_prim(this8->red_size, size_t); + marshal_prim(this8->green_size, size_t); + marshal_prim(this8->blue_size, size_t); + marshal_buffer(this8->red, (this8->red_size + this8->green_size + this8->blue_size) * width); + MARSHAL_EPILOGUE; +} diff --git a/libcoopgamma_ramps_unmarshal_.c b/libcoopgamma_ramps_unmarshal_.c new file mode 100644 index 0000000..fe07c22 --- /dev/null +++ b/libcoopgamma_ramps_unmarshal_.c @@ -0,0 +1,30 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Unmarshal a `libcoopgamma_ramps8_t`, `libcoopgamma_ramps16_t`, `libcoopgamma_ramps32_t`, + * `libcoopgamma_ramps64_t`, `libcoopgamma_rampsf_t`, or `libcoopgamma_rampsd_t` from a buffer + * + * @param this The output parameter for unmarshalled record + * @param vbuf The buffer with the marshalled record + * @param np Output parameter for the number of unmarshalled bytes, undefined on failure + * @param width The `sizeof(*(this->red))` + * @return `LIBCOOPGAMMA_SUCCESS` (0), `LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE`, + * `LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE`, or `LIBCOOPGAMMA_ERRNO_SET` + */ +int +libcoopgamma_ramps_unmarshal_(void *restrict this, const void *restrict vbuf, + size_t *restrict np, size_t width) +{ + libcoopgamma_ramps8_t *restrict this8 = (libcoopgamma_ramps8_t *restrict)this; + UNMARSHAL_PROLOGUE; + unmarshal_version(LIBCOOPGAMMA_RAMPS_VERSION); + unmarshal_prim(this8->red_size, size_t); + unmarshal_prim(this8->green_size, size_t); + unmarshal_prim(this8->blue_size, size_t); + unmarshal_buffer(this8->red, (this8->red_size + this8->green_size + this8->blue_size) * width); + this8->green = this8->red + this8->red_size * width; + this8->blue = this8->green + this8->green_size * width; + UNMARSHAL_EPILOGUE; +} diff --git a/libcoopgamma_send_message__.c b/libcoopgamma_send_message__.c new file mode 100644 index 0000000..63ff2b5 --- /dev/null +++ b/libcoopgamma_send_message__.c @@ -0,0 +1,43 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Send a message to the server and wait for response + * + * @param ctx The state of the library + * @param msg The message to send + * @param n The length of `msg` + * @return Zero on success, -1 on error + */ +int +libcoopgamma_send_message__(libcoopgamma_context_t *restrict ctx, char *msg, size_t n) +{ + void *new; + if (ctx->outbound_head == ctx->outbound_tail) { + free(ctx->outbound); + ctx->outbound = msg; + ctx->outbound_tail = 0; + ctx->outbound_head = n; + ctx->outbound_size = n; + } else { + if (ctx->outbound_head + n > ctx->outbound_size) { + memmove(ctx->outbound, ctx->outbound + ctx->outbound_tail, ctx->outbound_head -= ctx->outbound_tail); + ctx->outbound_tail = 0; + } + if (ctx->outbound_head + n > ctx->outbound_size) { + new = realloc(ctx->outbound, ctx->outbound_head + n); + if (!new) { + free(msg); + return -1; + } + ctx->outbound = new; + ctx->outbound_size = ctx->outbound_head + n; + } + memcpy(ctx->outbound + ctx->outbound_head, msg, n); + ctx->outbound_head += n; + free(msg); + } + ctx->message_id += 1; + return libcoopgamma_flush(ctx); +} diff --git a/libcoopgamma_set_gamma_recv.c b/libcoopgamma_set_gamma_recv.c new file mode 100644 index 0000000..71b45a7 --- /dev/null +++ b/libcoopgamma_set_gamma_recv.c @@ -0,0 +1,25 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Apply, update, or remove a gamma ramp adjustment, receive response part + * + * @param ctx The state of the library, must be connected + * @param async Information about the request + * @return Zero on success, -1 on error, in which case `ctx->error` + * (rather than `errno`) is read for information about the error + */ +int +libcoopgamma_set_gamma_recv(libcoopgamma_context_t *restrict ctx, libcoopgamma_async_context_t *restrict async) +{ + if (libcoopgamma_check_error__(ctx, async)) + return -(ctx->error.custom || ctx->error.number); + + while (*libcoopgamma_next_header__(ctx)); + (void) libcoopgamma_next_payload__(ctx, &(size_t){0}); + + errno = EBADMSG; + copy_errno(ctx); + return -1; +} diff --git a/libcoopgamma_set_gamma_send.c b/libcoopgamma_set_gamma_send.c new file mode 100644 index 0000000..d6daf91 --- /dev/null +++ b/libcoopgamma_set_gamma_send.c @@ -0,0 +1,85 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Apply, update, or remove a gamma ramp adjustment, send request part + * + * Cannot be used before connecting to the server + * + * @param filter The filter to apply, update, or remove, gamma ramp meta-data must match the CRTC's + * @param ctx The state of the library, must be connected + * @param async Information about the request, that is needed to + * identify and parse the response, is stored here + * @return Zero on success, -1 on error + */ +int +libcoopgamma_set_gamma_send(const libcoopgamma_filter_t *restrict filter, libcoopgamma_context_t *restrict ctx, + libcoopgamma_async_context_t *restrict async) +{ + const void *payload = NULL; + const char *lifespan; + char priority[sizeof("Priority: \n") + 3 * sizeof(int64_t)] = {'\0'}; + char length [sizeof("Length: \n") + 3 * sizeof(size_t) ] = {'\0'}; + size_t payload_size = 0, stopwidth = 0; + +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wnonnull-compare" +#endif + if (!filter || !filter->crtc || strchr(filter->crtc, '\n') || !filter->class || strchr(filter->class, '\n')) { + errno = EINVAL; + goto fail; + } +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic pop +#endif + + switch (filter->lifespan) { + case LIBCOOPGAMMA_REMOVE: lifespan = "remove"; break; + case LIBCOOPGAMMA_UNTIL_DEATH: lifespan = "until-death"; break; + case LIBCOOPGAMMA_UNTIL_REMOVAL: lifespan = "until-removal"; break; + default: + errno = EINVAL; + goto fail; + } + + if (filter->lifespan != LIBCOOPGAMMA_REMOVE) { + switch (filter->depth) { + case LIBCOOPGAMMA_FLOAT: stopwidth = sizeof(float); break; + case LIBCOOPGAMMA_DOUBLE: stopwidth = sizeof(double); break; + default: INTEGRAL_DEPTHS + if (filter->depth <= 0 || (filter->depth & 7)) { + errno = EINVAL; + goto fail; + } + stopwidth = (size_t)(filter->depth / 8); + break; + } + + payload_size = filter->ramps.u8.red_size; + payload_size += filter->ramps.u8.green_size; + payload_size += filter->ramps.u8.blue_size; + payload_size *= stopwidth; + payload = filter->ramps.u8.red; + sprintf(priority, "Priority: %" PRIi64 "\n", filter->priority); + sprintf(length, "Length: %zu\n", payload_size); + } + + async->message_id = ctx->message_id; + SEND_MESSAGE(ctx, payload, payload_size, + "Command: set-gamma\n" + "Message ID: %" PRIu32 "\n" + "CRTC: %s\n" + "Class: %s\n" + "Lifespan: %s\n" + "%s" + "%s" + "\n", + ctx->message_id, filter->crtc, filter->class, lifespan, priority, length); + + return 0; +fail: + copy_errno(ctx); + return -1; +} diff --git a/libcoopgamma_set_gamma_sync.c b/libcoopgamma_set_gamma_sync.c new file mode 100644 index 0000000..b682378 --- /dev/null +++ b/libcoopgamma_set_gamma_sync.c @@ -0,0 +1,26 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Apply, update, or remove a gamma ramp adjustment, synchronous version + * + * This is a synchronous request function, as such, + * you have to ensure that communication is blocking + * (default), and that there are not asynchronous + * requests waiting, it also means that EINTR:s are + * silently ignored and there no wait to cancel the + * operation without disconnection from the server + * + * @param filter The filter to apply, update, or remove, gamma ramp meta-data must match the CRTC's + * @param depth The datatype for the stops in the gamma ramps, must match the CRTC's + * @param ctx The state of the library, must be connected + * @return Zero on success, -1 on error, in which case `ctx->error` + * (rather than `errno`) is read for information about the error + */ +int +libcoopgamma_set_gamma_sync(const libcoopgamma_filter_t *restrict filter, libcoopgamma_context_t *restrict ctx) +{ + SYNC_CALL(libcoopgamma_set_gamma_send(filter, ctx, &async), + libcoopgamma_set_gamma_recv(ctx, &async), (copy_errno(ctx), -1)); +} diff --git a/libcoopgamma_set_nonblocking.c b/libcoopgamma_set_nonblocking.c new file mode 100644 index 0000000..91bae5a --- /dev/null +++ b/libcoopgamma_set_nonblocking.c @@ -0,0 +1,34 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * By default communication is blocking, this function + * can be used to switch between blocking and nonblocking + * + * After setting the communication to nonblocking, + * `libcoopgamma_flush`, `libcoopgamma_synchronise` and + * and request-sending functions can fail with EAGAIN and + * EWOULDBLOCK. It is safe to continue with `libcoopgamma_flush` + * (for `libcoopgamma_flush` it selfand equest-sending functions) + * or `libcoopgamma_synchronise` just like EINTR failure. + * + * @param ctx The state of the library, must be connected + * @param nonblocking Nonblocking mode? + * @return Zero on success, -1 on error + */ +int +libcoopgamma_set_nonblocking(libcoopgamma_context_t *restrict ctx, int nonblocking) +{ + int flags = fcntl(ctx->fd, F_GETFL); + if (flags == -1) + return -1; + if (nonblocking) + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + if (fcntl(ctx->fd, F_SETFL, flags) == -1) + return -1; + ctx->blocking = !nonblocking; + return 0; +} diff --git a/libcoopgamma_skip_message.c b/libcoopgamma_skip_message.c new file mode 100644 index 0000000..f9e8d1f --- /dev/null +++ b/libcoopgamma_skip_message.c @@ -0,0 +1,16 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Tell the library that you will not be parsing a receive message + * + * @param ctx The state of the library, must be connected + */ +void +libcoopgamma_skip_message(libcoopgamma_context_t *restrict ctx) +{ + size_t _n; + while (*libcoopgamma_next_header__(ctx)); + (void) libcoopgamma_next_payload__(ctx, &_n); +} diff --git a/libcoopgamma_synchronise.c b/libcoopgamma_synchronise.c new file mode 100644 index 0000000..2b85116 --- /dev/null +++ b/libcoopgamma_synchronise.c @@ -0,0 +1,135 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Wait for the next message to be received + * + * @param ctx The state of the library, must be connected + * @param pending Information for each pending request + * @param n The number of elements in `pending` + * @param selected The index of the element in `pending` which corresponds + * to the first inbound message, note that this only means + * that the message is not for any of the other request, + * if the message is corrupt any of the listed requests can + * be selected even if it is not for any of the requests. + * Functions that parse the message will detect such corruption. + * @return Zero on success, -1 on error. If the the message is ignored, + * which happens if corresponding `libcoopgamma_async_context_t` + * is not listed, -1 is returned and `errno` is set to 0. If -1 + * is returned, `errno` is set to `ENOTRECOVERABLE` you have + * received a corrupt message and the context has been tainted + * beyond recover. + */ +int +libcoopgamma_synchronise(libcoopgamma_context_t *restrict ctx, libcoopgamma_async_context_t *restrict pending, + size_t n, size_t *restrict selected) +{ + char temp[3 * sizeof(size_t) + 1]; + ssize_t got; + size_t i; + char *p; + char *line; + char *value; + struct pollfd pollfd; + size_t new_size; + void *new; + + if (ctx->inbound_head == ctx->inbound_tail) { + ctx->inbound_head = ctx->inbound_tail = ctx->curline = 0; + } else if (ctx->inbound_tail > 0) { + memmove(ctx->inbound, ctx->inbound + ctx->inbound_tail, ctx->inbound_head -= ctx->inbound_tail); + ctx->curline -= ctx->inbound_tail; + ctx->inbound_tail = 0; + } + + pollfd.fd = ctx->fd; + pollfd.events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; + + if (ctx->inbound_head) + goto skip_recv; + for (;;) { + if (ctx->inbound_head == ctx->inbound_size) { + new_size = ctx->inbound_size ? (ctx->inbound_size << 1) : 1024U; + new = realloc(ctx->inbound, new_size); + if (!new) + return -1; + ctx->inbound = new; + ctx->inbound_size = new_size; + } + + if (ctx->blocking) { + pollfd.revents = 0; + if (poll(&pollfd, (nfds_t)1, -1) < 0) + return -1; + } + got = recv(ctx->fd, &ctx->inbound[ctx->inbound_head], ctx->inbound_size - ctx->inbound_head, 0); + if (got <= 0) { + if (got == 0) + errno = ECONNRESET; + return -1; + } + +#ifdef DEBUG_MODE + fprintf(stderr, "\033[32m"); + fwrite(&ctx->inbound[ctx->inbound_head], (size_t)got, 1U, stderr); + fprintf(stderr, "\033[m"); + fflush(stderr); +#endif + + ctx->inbound_head += (size_t)got; + + skip_recv: + while (!ctx->have_all_headers) { + line = &ctx->inbound[ctx->curline]; + p = memchr(line, '\n', ctx->inbound_head - ctx->curline); + if (!p) + break; + if (memchr(line, '\0', (size_t)(p - line)) != NULL) + ctx->bad_message = 1; + *p++ = '\0'; + ctx->curline = (size_t)(p - ctx->inbound); + if (!*line) { + ctx->have_all_headers = 1; + } else if (strstr(line, "In response to: ") == line) { + value = &line[sizeof("In response to: ") - 1U]; + ctx->in_response_to = (uint32_t)atol(value); + } else if (strstr(line, "Length: ") == line) { + value = &line[sizeof("Length: ") - 1U]; + ctx->length = (size_t)atol(value); + sprintf(temp, "%zu", ctx->length); + if (strcmp(value, temp)) + goto fatal; + } + } + + if (ctx->have_all_headers && ctx->inbound_head >= ctx->curline + ctx->length) { + ctx->curline += ctx->length; + if (ctx->bad_message) { + ctx->bad_message = 0; + ctx->have_all_headers = 0; + ctx->length = 0; + ctx->inbound_tail = ctx->curline; + errno = EBADMSG; + return -1; + } + for (i = 0; i < n; i++) { + if (pending[i].message_id == ctx->in_response_to) { + *selected = i; + return 0; + } + } + *selected = 0; + ctx->bad_message = 0; + ctx->have_all_headers = 0; + ctx->length = 0; + ctx->inbound_tail = ctx->curline; + errno = 0; + return -1; + } + } + +fatal: + errno = ENOTRECOVERABLE; + return -1; +} @@ -1,64 +0,0 @@ -MAN0 =\ - libcoopgamma.h.0 - -MAN3=\ - libcoopgamma_async_context_destroy.3\ - libcoopgamma_async_context_initialise.3\ - libcoopgamma_async_context_marshal.3\ - libcoopgamma_async_context_unmarshal.3\ - libcoopgamma_connect.3\ - libcoopgamma_context_destroy.3\ - libcoopgamma_context_initialise.3\ - libcoopgamma_context_marshal.3\ - libcoopgamma_context_unmarshal.3\ - libcoopgamma_crtc_info_destroy.3\ - libcoopgamma_crtc_info_initialise.3\ - libcoopgamma_crtc_info_marshal.3\ - libcoopgamma_crtc_info_unmarshal.3\ - libcoopgamma_error_destroy.3\ - libcoopgamma_error_initialise.3\ - libcoopgamma_error_marshal.3\ - libcoopgamma_error_unmarshal.3\ - libcoopgamma_filter_destroy.3\ - libcoopgamma_filter_initialise.3\ - libcoopgamma_filter_marshal.3\ - libcoopgamma_filter_query_destroy.3\ - libcoopgamma_filter_query_initialise.3\ - libcoopgamma_filter_query_marshal.3\ - libcoopgamma_filter_query_unmarshal.3\ - libcoopgamma_filter_table_destroy.3\ - libcoopgamma_filter_table_initialise.3\ - libcoopgamma_filter_table_marshal.3\ - libcoopgamma_filter_table_unmarshal.3\ - libcoopgamma_filter_unmarshal.3\ - libcoopgamma_flush.3\ - libcoopgamma_get_crtcs_recv.3\ - libcoopgamma_get_crtcs_send.3\ - libcoopgamma_get_crtcs_sync.3\ - libcoopgamma_get_gamma_info_recv.3\ - libcoopgamma_get_gamma_info_send.3\ - libcoopgamma_get_gamma_info_sync.3\ - libcoopgamma_get_gamma_recv.3\ - libcoopgamma_get_gamma_send.3\ - libcoopgamma_get_gamma_sync.3\ - libcoopgamma_get_method_and_site.3\ - libcoopgamma_get_methods.3\ - libcoopgamma_get_pid_file.3\ - libcoopgamma_get_socket_file.3\ - libcoopgamma_queried_filter_destroy.3\ - libcoopgamma_queried_filter_initialise.3\ - libcoopgamma_queried_filter_marshal.3\ - libcoopgamma_queried_filter_unmarshal.3\ - libcoopgamma_ramps_destroy.3\ - libcoopgamma_ramps_initialise.3\ - libcoopgamma_ramps_marshal.3\ - libcoopgamma_ramps_unmarshal.3\ - libcoopgamma_set_gamma_recv.3\ - libcoopgamma_set_gamma_send.3\ - libcoopgamma_set_gamma_sync.3\ - libcoopgamma_set_nonblocking.3\ - libcoopgamma_skip_message.3\ - libcoopgamma_synchronise.3 - -MAN7 =\ - libcoopgamma.7 |