/* Yet Another PixelFlut Server * $Id: yapfs.c,v 1.3 2025/03/03 20:25:58 oj14ozun Exp $ * https://wwwcip.cs.fau.de/~oj14ozun/src+etc/yapf.c */ #define _POSIX_C_SOURCE 200809L #define SDL_MAIN_USE_CALLBACKS #include #include #include #include #include #include #include #include #include #include #include #include #define HELP "Figure it out yourself.\n" static SDL_Window *window; static SDL_Renderer *renderer; static SDL_Surface *surface; static volatile bool dirty = false; static bool get_rgb(unsigned x, unsigned y, uint8_t *r, uint8_t *g, uint8_t *b) { if (x >= (unsigned)surface->w || y >= (unsigned)surface->h) { return false; } const SDL_PixelFormatDetails *pfd = SDL_GetPixelFormatDetails(surface->format); uint8_t bbp = pfd->bytes_per_pixel; SDL_GetRGB( (uint32_t)*((uint8_t *)surface->pixels + y * surface->pitch + x * bbp), pfd, NULL, r, g, b); return true; } static bool set_rgba(unsigned x, unsigned y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { if (x >= (unsigned)surface->w || y >= (unsigned)surface->h) { return false; } const SDL_PixelFormatDetails *pfd = SDL_GetPixelFormatDetails(surface->format); uint8_t bbp = pfd->bytes_per_pixel; uint8_t *pix = (uint8_t *)surface->pixels + y * surface->pitch + x * bbp; uint32_t val = SDL_MapRGBA(pfd, NULL, r, g, b, a); if (pix != NULL && bbp == 4) { pix[0] = 0xFF & val; pix[1] = 0xFF & (val >> 8); pix[2] = 0xFF & (val >> 16); pix[3] = 0xFF & (val >> 24); dirty = true; } return true; } static bool get_size(unsigned *x, unsigned *y) { *x = surface->w; *y = surface->h; return true; } static void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { (void)handle; buf->base = realloc(buf->base, buf->len = suggested_size); } static void on_close(uv_handle_t *handle) { free(handle); } struct req { uv_write_t write; uv_buf_t buf; }; static void on_write(uv_write_t *req, int status) { (void)status; free(req->bufs); free(req); } static void handle_conn(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { if (nread > 0) { uv_buf_t *write = NULL; unsigned nwrite = 0; char *in = buf->base, *p = in; new_cmd: while ((p - in) < nread && *p) { switch (*p++) { case 'P': { enum { INIT, AFTER_CMD, INVALID, X_COORD, AFTER_X_COORD, Y_COORD, AFTER_Y_COORD, R_COMP1, R_COMP2, G_COMP1, G_COMP2, B_COMP1, B_COMP2, A_COMP1, A_COMP2, EOL, } state = INIT; unsigned x = 0, y = 0; uint8_t r = 0, g = 0, b = 0, a = 0; while ((p - in) < nread && *p) { switch (state) { case INIT: if (*p == 'X') { state = AFTER_CMD; p++; } else { state = INVALID; } break; case AFTER_CMD: if (isspace(*p)) { p++; } else if (isdigit(*p)) { state = X_COORD; } else { state = INVALID; } break; case X_COORD: if (isspace(*p)) { state = AFTER_X_COORD; } else if (isdigit(*p)) { x = x * 10 + (*p - '0'); p++; } else { state = INVALID; } break; case AFTER_X_COORD: if (isspace(*p)) { p++; } else if (isdigit(*p)) { state = Y_COORD; } else { state = INVALID; } break; case Y_COORD: if (isspace(*p)) { state = AFTER_Y_COORD; } else if (isdigit(*p)) { y = y * 10 + (*p - '0'); p++; } else { state = INVALID; } break; case AFTER_Y_COORD: if (*p == '\n') { char resp[100]; get_rgb(x, y, &r, &g, &b); sprintf(resp, "PX %d %d %02x%02x%02x\n", x, y, r, g, b); write = realloc(write, (sizeof *write) * (nwrite + 1)); if (write == NULL) { return; } write[nwrite++] = uv_buf_init(strdup(resp), strlen(resp)); p++; goto new_cmd; } else if (isspace(*p)) { p++; } else if (isxdigit(*p)) { state = R_COMP1; } else { state = INVALID; } break; case R_COMP1: state = R_COMP2; if (isdigit(*p)) { r |= (*p++ - '0') << 4; } else if (isupper(*p)) { r |= (10 + *p++ - 'A') << 4; } else if (islower(*p)) { r |= (10 + *p++ - 'a') << 4; } else { state = INVALID; } break; case R_COMP2: state = G_COMP1; if (isdigit(*p)) { r |= (*p++ - '0'); } else if (isupper(*p)) { r |= (10 + *p++ - 'A'); } else if (islower(*p)) { r |= (10 + *p++ - 'a'); } else { state = INVALID; } break; case G_COMP1: state = G_COMP2; if (*p == '\0') { /* if the input ends right here, then we interpret it as * a grayscale value. */ g = b = r; state = EOL; } else if (isdigit(*p)) { g |= (*p++ - '0') << 4; } else if (isupper(*p)) { g |= (10 + *p++ - 'A') << 4; } else if (islower(*p)) { g |= (10 + *p++ - 'a') << 4; } else { state = INVALID; } break; case G_COMP2: state = B_COMP1; if (isdigit(*p)) { g |= (*p++ - '0'); } else if (isupper(*p)) { g |= (10 + *p++ - 'A'); } else if (islower(*p)) { g |= (10 + *p++ - 'a'); } else { state = INVALID; } break; case B_COMP1: state = B_COMP2; if (isdigit(*p)) { b |= (*p++ - '0') << 4; } else if (isupper(*p)) { b |= (10 + *p++ - 'A') << 4; } else if (islower(*p)) { b |= (10 + *p++ - 'a') << 4; } else { state = INVALID; } break; case B_COMP2: state = A_COMP1; if (isspace(*p)) { state = EOL; } else if (isdigit(*p)) { b |= (*p++ - '0'); } else if (isupper(*p)) { b |= (10 + *p++ - 'A'); } else if (islower(*p)) { b |= (10 + *p++ - 'a'); } else { state = INVALID; } break; case A_COMP1: state = B_COMP2; if (isspace(*p)) { state = EOL; } else if (isdigit(*p)) { a |= (*p++ - '0') << 4; } else if (isupper(*p)) { a |= (10 + *p++ - 'A') << 4; } else if (islower(*p)) { a |= (10 + *p++ - 'a') << 4; } else { state = INVALID; } break; case A_COMP2: state = EOL; if (isspace(*p)) { /* noop */ } else if (isdigit(*p)) { a |= (*p++ - '0'); } else if (isupper(*p)) { a |= (10 + *p++ - 'A'); } else if (islower(*p)) { a |= (10 + *p++ - 'a'); } else { state = INVALID; } break; case EOL: if (*p == '\n') { set_rgba(x, y, r, g, b, a); p++; goto new_cmd; } else if (isspace(*p)) { p++; } else { state = INVALID; } break; case INVALID: while (!(*p == '\n' || *p == '\0')) { p++; } goto new_cmd; } } } break; case 'S': { char resp[100]; unsigned x, y; get_size(&x, &y); sprintf(resp, "SIZE %u %u\n", x, y); write = realloc(write, (sizeof *write) * (nwrite + 1)); if (write != NULL) { write[nwrite++] = uv_buf_init(strdup(resp), strlen(resp)); } break; } case 'H': write = realloc(write, (sizeof *write) * (nwrite + 1)); if (write != NULL) { write[nwrite++] = uv_buf_init(strdup(HELP), strlen(HELP)); } break; } } if (nwrite > 0) { uv_write_t *req = (uv_write_t *)malloc(sizeof(uv_write_t)); if (req != NULL) { uv_write(req, client, write, nwrite, on_write); } } } else if (nread < 0) { if (nread != UV_EOF) { fprintf(stderr, "Read error %s\n", uv_err_name(nread)); } uv_close((uv_handle_t *)client, on_close); } free(buf->base); } static void on_new_conn(uv_stream_t *server, int status) { if (status < 0) { fprintf(stderr, "New connection error %s\n", uv_strerror(status)); return; } uv_tcp_t *client = (uv_tcp_t *)malloc(sizeof(uv_tcp_t)); uv_tcp_init(server->loop, client); if (uv_accept(server, (uv_stream_t *)client) == 0) { uv_read_start((uv_stream_t *)client, alloc_buffer, handle_conn); } else { uv_close((uv_handle_t *)client, on_close); } } static int network(void *arg) { uv_loop_t loop = {0}; (void)arg; uv_loop_init(&loop); uv_tcp_t server; uv_tcp_init(&loop, &server); struct sockaddr_in6 addr; uv_ip6_addr("0.0.0.0", 1234, &addr); uv_tcp_bind(&server, (const struct sockaddr *)&addr, 0); int r = uv_listen((uv_stream_t *)&server, 128, on_new_conn); if (r) { fprintf(stderr, "Failure to listen: %s\n", uv_strerror(r)); return 0; } uv_run(&loop, UV_RUN_DEFAULT); return 0; } SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) { (void)appstate; (void)argc; (void)argv; SDL_SetAppMetadata("Example Renderer Clear", "1.0", "com.example.renderer-clear"); if (!SDL_Init(SDL_INIT_VIDEO)) { SDL_Log("Couldn't initialize SDL: %s", SDL_GetError()); return SDL_APP_FAILURE; } if (!SDL_CreateWindowAndRenderer("YAPFS", 640, 480, 0, &window, &renderer)) { SDL_Log("Couldn't create window/renderer: %s", SDL_GetError()); return SDL_APP_FAILURE; } surface = SDL_GetWindowSurface(window); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); SDL_RenderPresent(renderer); SDL_CreateThread(network, "io", NULL); return SDL_APP_CONTINUE; } SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { (void)appstate; if (event->type == SDL_EVENT_QUIT) { return SDL_APP_SUCCESS; } return SDL_APP_CONTINUE; } SDL_AppResult SDL_AppIterate(void *appstate) { (void)appstate; if (SDL_GetTicks() % 100 == 0 && dirty) { SDL_UpdateWindowSurface(window); dirty = false; } return SDL_APP_CONTINUE; } void SDL_AppQuit(void *appstate, SDL_AppResult result) { (void)appstate; (void)result; SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); } /* Local Variables: */ /* compile-command: "cc -o yapfs -O3 -W `pkg-config --cflags --libs libuv sdl3` yapfs.c" */ /* End: */