/* ------------------------------------------------------------- * framebuffer_demo_tcp.c (modified for hard B/W output) * * – Same SDL‑2 window and single‑client 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 * pixel’s luminance. * * Build (Linux/macOS): * gcc -Wall -Wextra -pedantic -std=c11 framebuffer_demo_tcp.c \ * -lSDL2 -lpthread -o framebuffer_demo_tcp * * ------------------------------------------------------------- */ #include #include #include #include #include // memcpy, memset #include // close, read, write #include // fcntl #include #include #include #include #include // select() #include /* ------------------------------------------------------------- * 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 (little‑endian) 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 = 960 000 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 ITU‑BT.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 non‑blocking 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 non‑blocking 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; }