aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/coopgammad.c1
-rw-r--r--src/filter.c26
-rw-r--r--src/filter.h6
-rw-r--r--src/message.c15
-rw-r--r--src/message.h15
-rw-r--r--src/output.c98
-rw-r--r--src/output.h32
-rw-r--r--src/server.c354
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;
}