diff options
-rw-r--r-- | src/coopgammad.c | 1 | ||||
-rw-r--r-- | src/filter.c | 26 | ||||
-rw-r--r-- | src/filter.h | 6 | ||||
-rw-r--r-- | src/message.c | 15 | ||||
-rw-r--r-- | src/message.h | 15 | ||||
-rw-r--r-- | src/output.c | 98 | ||||
-rw-r--r-- | src/output.h | 32 | ||||
-rw-r--r-- | src/server.c | 354 |
8 files changed, 492 insertions, 55 deletions
diff --git a/src/coopgammad.c b/src/coopgammad.c index 7398d51..ea67328 100644 --- a/src/coopgammad.c +++ b/src/coopgammad.c @@ -673,7 +673,6 @@ static int initialise(int full, int preserve, int foreground, int keep_stderr, i { struct filter filter = { .client = -1, - .crtc = NULL, .priority = 0, .class = NULL, .lifespan = LIFESPAN_UNTIL_REMOVAL, diff --git a/src/filter.c b/src/filter.c index 54bb9ad..4287c2e 100644 --- a/src/filter.c +++ b/src/filter.c @@ -31,7 +31,6 @@ */ void filter_destroy(struct filter* this) { - free(this->crtc); free(this->class); free(this->ramps); } @@ -55,9 +54,8 @@ size_t filter_marshal(const struct filter* this, void* buf, size_t ramps_size) if (bs != NULL) { - if (this->crtc != NULL) nonnulls |= 1; - if (this->class != NULL) nonnulls |= 2; - if (this->ramps != NULL) nonnulls |= 4; + if (this->class != NULL) nonnulls |= 1; + if (this->ramps != NULL) nonnulls |= 2; *(bs + off) = nonnulls; } off += 1; @@ -70,14 +68,6 @@ size_t filter_marshal(const struct filter* this, void* buf, size_t ramps_size) *(enum lifespan*)(bs + off) = this->lifespan; off += sizeof(enum lifespan); - if (this->crtc != NULL) - { - n = strlen(this->crtc) + 1; - if (bs != NULL) - memcpy(bs + off, this->crtc, n); - off += n; - } - if (this->class != NULL) { n = strlen(this->class) + 1; @@ -114,7 +104,6 @@ size_t filter_unmarshal(struct filter* this, const void* buf, size_t ramps_size) nonnulls = *(bs + off); off += 1; - this->crtc = NULL; this->class = NULL; this->ramps = NULL; @@ -127,20 +116,12 @@ size_t filter_unmarshal(struct filter* this, const void* buf, size_t ramps_size) if (nonnulls & 1) { n = strlen(bs + off) + 1; - if (!(this->crtc = memdup(bs + off, n))) - goto fail; - off += n; - } - - if (nonnulls & 2) - { - n = strlen(bs + off) + 1; if (!(this->class = memdup(bs + off, n))) goto fail; off += n; } - if (nonnulls & 4) + if (nonnulls & 2) { if (!(this->ramps = memdup(bs + off, ramps_size))) goto fail; @@ -150,7 +131,6 @@ size_t filter_unmarshal(struct filter* this, const void* buf, size_t ramps_size) return off; fail: - free(this->crtc); free(this->class); free(this->ramps); return 0; diff --git a/src/filter.h b/src/filter.h index 8ffcc26..d8e4c4f 100644 --- a/src/filter.h +++ b/src/filter.h @@ -60,12 +60,6 @@ struct filter int client; /** - * Identifier for the CRTC to which this filter - * should be applied. May be removed once applied. - */ - char* crtc; - - /** * The priority of the filter */ int64_t priority; diff --git a/src/message.c b/src/message.c index 343a26b..fd7d030 100644 --- a/src/message.c +++ b/src/message.c @@ -458,13 +458,14 @@ static int continue_read(struct message* this, int fd) * * @param this Memory slot in which to store the new message * @param fd The file descriptor - * @return Non-zero on error or interruption, `errno` will be - * set accordingly. Destroy the message on error, - * be aware that the reading could have been - * interrupted by a signal rather than canonical error. - * If -2 is returned `errno` will not have been set, - * -2 indicates that the message is malformated, - * which is a state that cannot be recovered from. + * @return 0: At least one message is available + * -1: Exceptional connection: + * EINTR: System call interrupted + * EAGAIN: No message is available + * EWOULDBLOCK: No message is available + * ECONNRESET: Connection closed + * Other: Failure + * -2: Corrupt message (unrecoverable) */ int message_read(struct message* this, int fd) { diff --git a/src/message.h b/src/message.h index 15ceaf7..d1d6840 100644 --- a/src/message.h +++ b/src/message.h @@ -120,13 +120,14 @@ size_t message_unmarshal(struct message* this, const void* buf); * * @param this Memory slot in which to store the new message * @param fd The file descriptor - * @return Non-zero on error or interruption, `errno` will be - * set accordingly. Destroy the message on error, - * be aware that the reading could have been - * interrupted by a signal rather than canonical error. - * If -2 is returned `errno` will not have been set, - * -2 indicates that the message is malformated, - * which is a state that cannot be recovered from. + * @return 0: At least one message is available + * -1: Exceptional connection: + * EINTR: System call interrupted + * EAGAIN: No message is available + * EWOULDBLOCK: No message is available + * ECONNRESET: Connection closed + * Other: Failure + * -2: Corrupt message (unrecoverable) */ int message_read(struct message* this, int fd); diff --git a/src/output.c b/src/output.c index 5e8e8b0..b2085db 100644 --- a/src/output.c +++ b/src/output.c @@ -224,3 +224,101 @@ int output_cmp_by_name(const void* a, const void* b) return strcmp(an, bn); } + +/** + * Find an output by its name + * + * @param key The name of the output + * @param base The array of outputs + * @param n The number of elements in `base` + * @return Output find in `base`, `NULL` if not found + */ +struct output* output_find_by_name(const char* key, struct output* base, size_t n) +{ + struct output k; + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-qual" +#endif + k.name = (char*)key; +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + return bsearch(&k, base, n, sizeof(*base), output_cmp_by_name); +} + + +/** + * Add a filter to an output + * + * @param out The output + * @param filter The filter + * @return The index given to the filter, -1 on error + */ +ssize_t add_filter(struct output* out, struct filter* filter) +{ + size_t i, n = out->table_size; + int r = -1; + + for (i = 0; i < n; i++) + if (filter->priority > out->table_filters[i].priority) + break; + + if (n == out->table_alloc) + { + void* new; + + new = realloc(out->table_filters, (n + 10) * sizeof(*(out->table_filters))); + if (new == NULL) + return -1; + out->table_filters = new; + + new = realloc(out->table_sums, (n + 10) * sizeof(*(out->table_sums))); + if (new == NULL) + return -1; + out->table_sums = new; + + out->table_alloc += 10; + } + + memmove(out->table_filters + i + 1, out->table_filters + i, (n - i) * sizeof(*(out->table_filters))); + memmove(out->table_sums + i + 1, out->table_sums + i, (n - i) * sizeof(*(out->table_sums))); + out->table_size++; + + COPY_RAMP_SIZES(&(out->table_sums[i].u8), out); + switch (out->depth) + { + case 8: r = libgamma_gamma_ramps8_initialise(&(out->table_sums[i].u8)); break; + case 16: r = libgamma_gamma_ramps16_initialise(&(out->table_sums[i].u16)); break; + case 32: r = libgamma_gamma_ramps32_initialise(&(out->table_sums[i].u32)); break; + case 64: r = libgamma_gamma_ramps64_initialise(&(out->table_sums[i].u64)); break; + case -1: r = libgamma_gamma_rampsf_initialise(&(out->table_sums[i].f)); break; + case -2: r = libgamma_gamma_rampsd_initialise(&(out->table_sums[i].d)); break; + default: + abort(); + } + if (r < 0) + return -1; + + out->table_filters[i] = *filter; + + return (ssize_t)i; +} + + +/** + * Recalculate the resulting gamma and + * update push the new gamam ramps to the CRTC + * + * @param output The output + * @param first_updated The index of the first added or removed filter + * @return Zero on success, -1 on error + */ +int flush_filters(struct output* output, size_t first_updated) +{ + /* TODO */ (void) output, (void) first_updated; + return 0; +} + diff --git a/src/output.h b/src/output.h index 3c56de5..8d59eec 100644 --- a/src/output.h +++ b/src/output.h @@ -175,3 +175,35 @@ __attribute__((pure)) #endif int output_cmp_by_name(const void* a, const void* b); +/** + * Find an output by its name + * + * @param key The name of the output + * @param base The array of outputs + * @param n The number of elements in `base` + * @return Output find in `base`, `NULL` if not found + */ +#if defined(__GNUC__) +__attribute__((pure)) +#endif +struct output* output_find_by_name(const char* key, struct output* base, size_t n); + +/** + * Add a filter to an output + * + * @param output The output + * @param filter The filter + * @return The index given to the filter, -1 on error + */ +ssize_t add_filter(struct output* output, struct filter* filter); + +/** + * Recalculate the resulting gamma and + * update push the new gamam ramps to the CRTC + * + * @param output The output + * @param first_updated The index of the first added or removed filter + * @return Zero on success, -1 on error + */ +int flush_filters(struct output* output, size_t first_updated); + diff --git a/src/server.c b/src/server.c index 96300e6..d1bdc30 100644 --- a/src/server.c +++ b/src/server.c @@ -16,14 +16,18 @@ * along with this library. If not, see <http://www.gnu.org/licenses/>. */ #include "server.h" +#include "output.h" #include "util.h" #include <sys/select.h> #include <sys/socket.h> #include <errno.h> +#include <fcntl.h> #include <signal.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> @@ -57,6 +61,11 @@ struct message* client_messages = NULL; /** + * The name of the process + */ +extern char* argv0; + +/** * The server socket's file descriptor */ extern int socketfd; @@ -73,6 +82,16 @@ extern volatile sig_atomic_t reexec; */ extern volatile sig_atomic_t terminate; +/** + * Array of all outputs + */ +extern struct output* outputs; + +/** + * The nubmer of elements in `outputs` + */ +extern size_t outputs_n; + /** @@ -83,12 +102,16 @@ extern volatile sig_atomic_t terminate; void server_destroy(int disconnect) { size_t i; - - /* TODO */ (void) disconnect; - for (i = 0; i < connections_used; i++) if (connections[i] >= 0) - message_destroy(client_messages + i); + { + if (disconnect) + { + shutdown(connections[i], SHUT_RDWR); + close(connections[i]); + } + message_destroy(client_messages + i); + } free(client_messages); free(connections); } @@ -204,7 +227,7 @@ static int update_fdset(fd_set* fds) */ static int handle_server(void) { - int fd; + int fd, flags, saved_errno; fd = accept(socketfd, NULL, NULL); if (fd < 0) @@ -220,17 +243,30 @@ static int handle_server(void) return -1; } + flags = fcntl(fd, F_GETFL); + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) + goto fail; + if (connections_ptr == connections_alloc) { - int* new; + void* new; + new = realloc(connections, (connections_alloc + 10) * sizeof(*connections)); if (new == NULL) - return -1; + goto fail; connections = new; + connections[connections_ptr] = fd; + + new = realloc(client_messages, (connections_alloc + 10) * sizeof(*client_messages)); + if (new == NULL) + goto fail; + client_messages = new; connections_alloc += 10; + if (message_initialise(client_messages + connections_ptr)) + goto fail; } - connections[connections_ptr++] = fd; + connections_ptr++; while (connections_ptr < connections_used) if (connections[connections_ptr] >= 0) connections_ptr++; @@ -238,6 +274,212 @@ static int handle_server(void) connections_used = connections_ptr; return 1; + fail: + saved_errno = errno; + shutdown(fd, SHUT_RDWR); + close(fd); + errno = saved_errno; + return -1; +} + + +/** + * Handle a closed connection + * + * @param client The file descriptor for the client + * @retunr Zero on success, -1 on error + */ +static int connection_closed(int client) +{ + size_t i, j, k; + int remove; + + for (i = 0; i < outputs_n; i++) + { + struct output* output = outputs + i; + ssize_t updated = -1; + for (j = k = 0; j < output->table_size; j += !remove, k++) + { + remove = output->table_filters[j].client == client; + if (remove) + { + filter_destroy(output->table_filters + j); + libgamma_gamma_ramps8_destroy(&(output->table_sums[j].u8)); + output->table_size -= 1; + if (updated == -1) + updated = (ssize_t)j; + } + output->table_filters[j] = output->table_filters[k]; + output->table_sums[j] = output->table_sums[k]; + } + if (updated >= 0) + flush_filters(output, (size_t)updated); + } + + return 0; +} + + +/** + * Handle a ‘Command: enumerate-crtcs’ message + * + * @param conn The index of the connection + * @return Zero on success (even if ignored), -1 on error + */ +static int enumerate_crtcs(size_t conn) +{ + /* TODO */ (void) conn; + + return 0; +} + + +/** + * Handle a ‘Command: set-gamma’ message + * + * @param conn The index of the connection + * @param crtc The value of the ‘CRTC’ header + * @return Zero on success (even if ignored), -1 on error + */ +static int get_gamma_info(size_t conn, char* crtc) +{ + if (crtc == NULL) + return fprintf(stderr, "%s: ignoring incomplete Command: get-gamma-info message\n", argv0), 0; + + /* TODO */ (void) conn; + + return 0; +} + + +/** + * Handle a ‘Command: get-gamma’ message + * + * @param conn The index of the connection + * @param crtc The value of the ‘CRTC’ header + * @param coalesce The value of the ‘Coalesce’ header + * @param high_priority The value of the ‘High priority’ header + * @param low_priority The value of the ‘Low priority’ header + * @return Zero on success (even if ignored), -1 on error + */ +static int get_gamma(size_t conn, char* crtc, char* coalesce, char* high_priority, char* low_priority) +{ + int64_t high, low; + int coal; + + if ((crtc == NULL) || + (coalesce == NULL) || + (high_priority == NULL) || + (low_priority == NULL)) + return fprintf(stderr, "%s: ignoring incomplete Command: get-gamma message\n", argv0), 0; + + high = (int64_t)atoll(high_priority); + low = (int64_t)atoll(low_priority); + + if (!strcmp(coalesce, "yes")) + coal = 1; + else if (!strcmp(coalesce, "no")) + coal = 0; + else + return fprintf(stderr, "%s: ignoring Command: get-gamma message with bad Coalesce value: %s\n", + argv0, coalesce), 0; + + /* TODO */ (void) coal, (void) high, (void) low, (void) conn; + + return 0; +} + + +/** + * Handle a ‘Command: set-gamma’ message + * + * @param conn The index of the connection + * @param crtc The value of the ‘CRTC’ header + * @param priority The value of the ‘Priority’ header + * @param class The value of the ‘Class’ header + * @param lifespan The value of the ‘Lifespan’ header + * @return Zero on success (even if ignored), -1 on error + */ +static int set_gamma(size_t conn, char* crtc, char* priority, char* class, char* lifespan) +{ + struct message* msg = client_messages + conn; + struct output* output = NULL; + struct filter filter; + char* p; + char* q; + int saved_errno; + ssize_t r; + + if ((crtc == NULL) || + (priority == NULL) || + (class == NULL) || + (lifespan == NULL)) + return fprintf(stderr, "%s: ignoring incomplete Command: set-gamma message\n", argv0), 0; + + filter.client = connections[conn]; + filter.priority = (int64_t)atoll(priority); + filter.ramps = NULL; + + output = output_find_by_name(crtc, outputs, outputs_n); + if (output == NULL) + return fprintf(stderr, "%s: ignoring Command: set-gamma message with non-existing CRTC: %s\n", + argv0, crtc), 0; + + p = strstr(class, "::"); + if ((p == NULL) || (p == class)) + return fprintf(stderr, "%s: ignoring Command: set-gamma message with malformatted class: %s\n", + argv0, class), 0; + q = strstr(p + 2, "::"); + if ((q == NULL) || (q == p)) + return fprintf(stderr, "%s: ignoring Command: set-gamma message with malformatted class: %s\n", + argv0, class), 0; + + if (!strcmp(lifespan, "until-removal")) + filter.lifespan = LIFESPAN_UNTIL_REMOVAL; + else if (!strcmp(lifespan, "until-death")) + filter.lifespan = LIFESPAN_UNTIL_DEATH; + else if (!strcmp(lifespan, "remove")) + filter.lifespan = LIFESPAN_REMOVE; + else + return fprintf(stderr, "%s: ignoring Command: set-gamma message with bad lifetime: %s\n", + argv0, lifespan), 0; + + if (filter.lifespan == LIFESPAN_REMOVE) + { + if (msg->payload_size) + fprintf(stderr, "%s: ignoring superfluous playload on Command: set-gamma message with " + "Lifespan: remove\n", argv0); + } + else if (msg->payload_size != output->ramps_size) + return fprintf(stderr, "%s: ignoring Command: set-gamma message bad payload: size: %zu instead of %zu\n", + argv0, msg->payload_size, output->ramps_size), 0; + + filter.class = memdup(class, strlen(class) + 1); + if (filter.class == NULL) + goto fail; + + if (filter.lifespan != LIFESPAN_REMOVE) + { + filter.ramps = memdup(msg->payload, msg->payload_size); + if (filter.ramps == NULL) + goto fail; + } + + if ((r = add_filter(output, &filter)) < 0) + goto fail; + filter.class = NULL; + filter.ramps = NULL; + if (flush_filters(output, (size_t)r)) + goto fail; + + return 0; + + fail: + saved_errno = errno; + free(filter.class); + free(filter.ramps); + errno = saved_errno; + return -1; } @@ -245,14 +487,104 @@ static int handle_server(void) * Handle event on a connection to a client * * @param conn The index of the connection - * @return 1: New connection accepted + * @return 1: The connection as closed * 0: Successful * -1: Failure */ static int handle_connection(size_t conn) { - /* TODO */ - return 0; + struct message* msg = client_messages + conn; + int r, fd = connections[conn]; + char* command = NULL; + char* crtc = NULL; + char* coalesce = NULL; + char* high_priority = NULL; + char* low_priority = NULL; + char* priority = NULL; + char* class = NULL; + char* lifespan = NULL; + size_t i; + + again: + switch (message_read(msg, fd)) + { + default: + break; + case -1: + switch (errno) + { + case EINTR: + case EAGAIN: +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + return 0; + default: + return -1; + case ECONNRESET:; + /* Fall throught to `case -2` in outer switch. */ + } + case -2: + shutdown(fd, SHUT_RDWR); + close(fd); + connections[conn] = -1; + if (conn < connections_ptr) + connections_ptr = conn; + if (conn == connections_used) + connections_used -= 1; + message_destroy(msg); + connection_closed(fd); + return 1; + } + + for (i = 0; i < msg->header_count; i++) + { + char* header = msg->headers[i]; + if (strstr(header, "Command: ") == header) command = strstr(header, ": ") + 2; + else if (strstr(header, "CRTC: ") == header) crtc = strstr(header, ": ") + 2; + else if (strstr(header, "Coalesce: ") == header) coalesce = strstr(header, ": ") + 2; + else if (strstr(header, "High priority: ") == header) high_priority = strstr(header, ": ") + 2; + else if (strstr(header, "Low priority: ") == header) low_priority = strstr(header, ": ") + 2; + else if (strstr(header, "Priority: ") == header) priority = strstr(header, ": ") + 2; + else if (strstr(header, "Class: ") == header) class = strstr(header, ": ") + 2; + else if (strstr(header, "Lifespan: ") == header) lifespan = strstr(header, ": ") + 2; + else + fprintf(stderr, "%s: ignoring unrecognised header: %s\n", argv0, header); + } + + r = 0; + if (command == NULL) + fprintf(stderr, "%s: ignoring message without command header\n", argv0); + else if (!strcmp(command, "enumerate-crtcs")) + { + if (crtc || coalesce || high_priority || low_priority || priority || class || lifespan) + fprintf(stderr, "%s: ignoring superfluous headers in Command: enumerate-crtcs message\n", argv0); + r = enumerate_crtcs(conn); + } + else if (!strcmp(command, "get-gamma-info")) + { + if (coalesce || high_priority || low_priority || priority || class || lifespan) + fprintf(stderr, "%s: ignoring superfluous headers in Command: get-gamma-info message\n", argv0); + r = get_gamma_info(conn, crtc); + } + else if (!strcmp(command, "get-gamma")) + { + if (priority || class || lifespan) + fprintf(stderr, "%s: ignoring superfluous headers in Command: get-gamma message\n", argv0); + r = get_gamma(conn, crtc, coalesce, high_priority, low_priority); + } + else if (!strcmp(command, "set-gamma")) + { + if (coalesce || high_priority || low_priority) + fprintf(stderr, "%s: ignoring superfluous headers in Command: set-gamma message\n", argv0); + r = set_gamma(conn, crtc, priority, class, lifespan); + } + else + fprintf(stderr, "%s: ignoring unrecognised command: Command: %s\n", argv0, command); + if (r < 0) + return -1; + + goto again; } |