Files
waveform-draw/test.c
Your Name e744172521 gpt code
2025-12-07 14:56:12 +00:00

293 lines
10 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* -------------------------------------------------------------
* framebuffer_demo_tcp.c (modified for hard B/W output)
*
* Same SDL2 window and singleclient TCP server as before.
* After a complete frame is received, the data is turned into
* a black/white image by applying a threshold of 127 to the
* pixels luminance.
*
* Build (Linux/macOS):
* gcc -Wall -Wextra -pedantic -std=c11 framebuffer_demo_tcp.c \
* -lSDL2 -lpthread -o framebuffer_demo_tcp
*
* ------------------------------------------------------------- */
#include <SDL2/SDL.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h> // memcpy, memset
#include <unistd.h> // close, read, write
#include <fcntl.h> // fcntl
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h> // select()
#include <errno.h>
/* -------------------------------------------------------------
* Configuration
* ------------------------------------------------------------- */
#define WIDTH 600
#define HEIGHT 400
#define LISTEN_PORT 12345 // change if you like
#define BACKLOG 1 // only one pending connection
/* -------------------------------------------------------------
* Simple pixel format helpers
* ------------------------------------------------------------- */
typedef Uint32 pixel_t; // 0xAARRGGBB in memory (littleendian)
static inline pixel_t make_pixel(Uint8 r, Uint8 g, Uint8 b, Uint8 a)
{
return (pixel_t)((a << 24) | (r << 16) | (g << 8) | b);
}
#define MAKE_OPAQUE_PIXEL(r,g,b) make_pixel((r),(g),(b),0xFF)
/* -------------------------------------------------------------
* Global framebuffer (contiguous block)
* ------------------------------------------------------------- */
static pixel_t framebuffer[HEIGHT][WIDTH]; // 600*400*4 = 960000 bytes
/* -------------------------------------------------------------
* Fill with a demo pattern (visible before any network data)
* ------------------------------------------------------------- */
static void init_demo_pattern(void)
{
for (int y = 0; y < HEIGHT; ++y) {
for (int x = 0; x < WIDTH; ++x) {
Uint8 r = (Uint8)((x * 255) / (WIDTH - 1));
Uint8 g = (Uint8)((y * 255) / (HEIGHT - 1));
Uint8 b = 0x80;
framebuffer[y][x] = MAKE_OPAQUE_PIXEL(r, g, b);
}
}
}
/* -------------------------------------------------------------
* --- B/W conversion -------------------------------------------------
* -------------------------------------------------------------
* Turn the whole framebuffer into a hard black/white image.
* Luminance is computed with the simple ITUBT.601 formula:
* Y = 0.299*R + 0.587*G + 0.114*B
* If Y >= 127 → white (0xFFFFFFFF), else black (0xFF000000).
*
* The function is called **once per complete frame** after the
* network read finishes.
* ------------------------------------------------------------- */
static void apply_bw_threshold(void)
{
const Uint8 THRESHOLD = 200;
const pixel_t WHITE = 0xFFFFFFFFU; /* ARGB = (255,255,255,255) */
const pixel_t BLACK = 0xFF000000U; /* ARGB = (255,0,0,0) */
for (int y = 0; y < HEIGHT; ++y) {
for (int x = 0; x < WIDTH; ++x) {
pixel_t p = framebuffer[y][x];
Uint8 r = (Uint8)((p >> 16) & 0xFF);
Uint8 g = (Uint8)((p >> 8) & 0xFF);
Uint8 b = (Uint8)((p ) & 0xFF);
/* Compute luminance (rounded to nearest integer). */
Uint16 yval = (Uint16)( (299 * r + 587 * g + 114 * b) / 1000 );
framebuffer[y][x] = (yval >= THRESHOLD) ? ((255)<<24 | 255-(y*255/HEIGHT)): BLACK;
}
}
}
/* -------------------------------------------------------------
* ---------- TCP SERVER ------------------------------------
* ------------------------------------------------------------- */
/* Returns a nonblocking listening socket, or -1 on error. */
static int create_listen_socket(void)
{
int lst = socket(AF_INET, SOCK_STREAM, 0);
if (lst < 0) {
perror("socket");
return -1;
}
int opt = 1;
setsockopt(lst, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(LISTEN_PORT),
.sin_addr = { .s_addr = INADDR_ANY }
};
if (bind(lst, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("bind");
close(lst);
return -1;
}
if (listen(lst, BACKLOG) < 0) {
perror("listen");
close(lst);
return -1;
}
int flags = fcntl(lst, F_GETFL, 0);
fcntl(lst, F_SETFL, flags | O_NONBLOCK);
printf("[net] Listening on 0.0.0.0:%d (single client allowed)\n", LISTEN_PORT);
return lst;
}
/* Helper: set a socket to nonblocking mode */
static void set_nonblocking(int fd)
{
int flags = fcntl(fd, F_GETFL, 0);
if (flags != -1) fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
/* -------------------------------------------------------------
* Main entry point
* ------------------------------------------------------------- */
int main(void)
{
/* ---- SDL initialisation ----------------------------------- */
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
fprintf(stderr, "SDL_Init error: %s\n", SDL_GetError());
return EXIT_FAILURE;
}
SDL_Window *window = SDL_CreateWindow("Framebuffer Demo TCP Input (B/W)",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
WIDTH, HEIGHT,
SDL_WINDOW_SHOWN);
if (!window) {
fprintf(stderr, "SDL_CreateWindow error: %s\n", SDL_GetError());
SDL_Quit();
return EXIT_FAILURE;
}
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1,
SDL_RENDERER_ACCELERATED |
SDL_RENDERER_PRESENTVSYNC);
if (!renderer) {
fprintf(stderr, "SDL_CreateRenderer error: %s\n", SDL_GetError());
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_FAILURE;
}
SDL_Texture *texture = SDL_CreateTexture(renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
WIDTH, HEIGHT);
if (!texture) {
fprintf(stderr, "SDL_CreateTexture error: %s\n", SDL_GetError());
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_FAILURE;
}
/* ---- Demo pattern (will be shown until first network frame arrives) */
init_demo_pattern();
/* ---- Networking ------------------------------------------ */
int listen_sock = create_listen_socket(); /* may be -1 on failure */
int client_sock = -1; /* -1 → no client yet */
size_t bytes_needed = WIDTH * HEIGHT * sizeof(pixel_t);
size_t recv_offset = 0; /* bytes already received for the current frame */
bool quit = false;
SDL_Event ev;
/* ---- Main loop ------------------------------------------- */
while (!quit) {
/* 1) SDL events */
while (SDL_PollEvent(&ev)) {
if (ev.type == SDL_QUIT) quit = true;
else if (ev.type == SDL_KEYDOWN && ev.key.keysym.sym == SDLK_ESCAPE)
quit = true;
}
/* 2) Accept a new client if none is present */
if (listen_sock >= 0 && client_sock < 0) {
struct sockaddr_in remote;
socklen_t remote_len = sizeof(remote);
int tmp = accept(listen_sock,
(struct sockaddr *)&remote,
&remote_len);
if (tmp >= 0) {
client_sock = tmp;
set_nonblocking(client_sock);
recv_offset = 0;
char ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &remote.sin_addr, ip, sizeof(ip));
printf("[net] Client connected from %s:%d\n", ip, ntohs(remote.sin_port));
} else if (errno != EAGAIN && errno != EWOULDBLOCK) {
perror("[net] accept");
}
}
void *tex_pixels;
int tex_pitch;
/* 3) Read raw pixel data */
if (client_sock >= 0) {
size_t still_needed = bytes_needed - recv_offset;
ssize_t n = recv(client_sock,
((Uint8 *)framebuffer) + recv_offset,
still_needed,
0);
if (n > 0) {
recv_offset += (size_t)n;
if (recv_offset == bytes_needed) {
/* -------------------------------------------------
* Full frame received turn it into hard B/W now.
* ------------------------------------------------- */
apply_bw_threshold(); /* < the new call */
recv_offset = 0; /* prepare for next frame */
for (int y = 0; y < HEIGHT; ++y) {
memcpy((Uint8 *)tex_pixels + y * tex_pitch,
framebuffer[y],
WIDTH * sizeof(pixel_t));
}
}
} else if (n == 0) {
printf("[net] Client disconnected.\n");
close(client_sock);
client_sock = -1;
} else if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
perror("[net] recv");
close(client_sock);
client_sock = -1;
}
}
/* 4) Copy framebuffer into the SDL texture */
if (SDL_LockTexture(texture, NULL, &tex_pixels, &tex_pitch) != 0) {
fprintf(stderr, "SDL_LockTexture error: %s\n", SDL_GetError());
break;
}
SDL_UnlockTexture(texture);
/* 5) Render */
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
/* VSYNC caps the loop to the monitor refresh rate. */
}
/* ---- Cleanup --------------------------------------------------- */
if (client_sock >= 0) close(client_sock);
if (listen_sock >= 0) close(listen_sock);
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
}