feat(sterni/blipqn): interact with a flipdot display from BQN

The idea of this “library” is to do the least. The most natural way to
represent the image to render on a flipdot image is a two dimensional
array of booleans. This something BQN is very well equipped for, i.e. it
has primitives that are designed to deal with this type of data
structure. The only thing we have to do is to take care of sending such
arrays to the flipdot display via the μCCC's un(der)documented UDP
protocol.

Compact implements the conversion from a boolean array to a bitmap that
only uses 1 bit per pixel. All socket code is written in C and invoked
via •FFI. Currently, every time a bitmap is sent to a display, the
target host has to be resolved again. This should be fixed in the
future.

Change-Id: Idea7c81baac919da93c88a69f98cbbbd026fa328
Reviewed-on: https://cl.tvl.fyi/c/depot/+/13010
Tested-by: BuildkiteCI
Reviewed-by: sterni <sternenseemann@systemli.org>
This commit is contained in:
sterni 2025-01-12 22:18:14 +01:00
parent b51720f844
commit 1027e21eee
6 changed files with 148 additions and 0 deletions

View file

@ -0,0 +1,47 @@
#define _DEFAULT_SOURCE // see getnameinfo(3), for NI_MAX*
#define _POSIX_C_SOURCE 200112L // see getaddrinfo(3)
#include <assert.h>
#include <limits.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
int resolve_addr(char *host, char *port, struct addrinfo **addrs) {
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_DGRAM;
hints.ai_family = AF_UNSPEC;
hints.ai_flags |= AI_NUMERICSERV;
return getaddrinfo(host, port, &hints, addrs);
}
// Send given bytes[len] to the host:port via UDP, returns 1 if all bytes
// where sent and no locally detectable errors occurred.
int8_t send_to_flipdot(char *host, in_port_t port_number, uint8_t *bitmap,
size_t bitmap_len) {
char port[NI_MAXSERV];
int sockfd = -1;
ssize_t sent = 0;
struct addrinfo *addrs = NULL;
if (snprintf(port, sizeof port, "%d", port_number) < 0) goto error;
if (resolve_addr(host, port, &addrs) != 0) goto error;
sockfd = socket(addrs->ai_family, SOCK_DGRAM, IPPROTO_UDP);
if (sockfd < 0) goto error;
sent =
sendto(sockfd, bitmap, bitmap_len, 0, addrs->ai_addr, addrs->ai_addrlen);
if (sent != (ssize_t)bitmap_len) goto error;
error:
if (addrs != NULL) freeaddrinfo(addrs);
if (sockfd >= 0) close(sockfd);
return (sent == (ssize_t)bitmap_len);
}