diff --git a/Makefile b/Makefile index ba381f1..3a1971e 100644 --- a/Makefile +++ b/Makefile @@ -3,13 +3,12 @@ CC=gcc .PHONY: all all: server - server: buffer.o connection.o net.o canvas.o main.o - $(CC) -o $@ $^ `sdl2-config --libs` -lpthread + $(CC) -o $@ $^ `sdl2-config --libs` -lpthread -lSDL2_ttf %.o: %.c common.h buffer.h connection.h canvas.h net.h $(CC) -Wall -Wextra -O -c -g `sdl2-config --cflags` -o $@ $< .PHONY: clean clean: - rm -f buffer.o connection.o main.o canvas.o net.o server + rm -f buffer.o connection.o main.o canvas.o text.o net.o server diff --git a/canvas.c b/canvas.c index ca02d18..a3a5577 100644 --- a/canvas.c +++ b/canvas.c @@ -3,15 +3,19 @@ #include #include #include +#include #include "SDL.h" +#include "SDL2/SDL_ttf.h" #include "common.h" #include "canvas.h" +#include "connection.h" #define TEX_SIZE_X 512 #define TEX_SIZE_Y 512 #define SCREEN_SIZE_X 1024 #define SCREEN_SIZE_Y 1024 +#define NET_MASK 0xff SDL_Window *window; SDL_Renderer *renderer; @@ -27,7 +31,101 @@ pthread_t canvas_thread; } \ } while (0) +#define WHITE (SDL_Color){0xff, 0xff, 0xff, 0xff} + +#define FONT_TEXTURE_SIZE 512 +#define GLYPH_SIZE 256 + +struct FontTexture { + SDL_Rect glyphs[GLYPH_SIZE]; + SDL_Texture * texture; +} fontTexture; + +enum FontStatus { + Success, + InvalidArg, + InitFailed, + FailedOpen, + FontTooBig, +}; + +enum FontStatus init_font(SDL_Renderer * renderer) { + SDL_Surface * text = NULL; + SDL_Surface * surface = NULL; + + TTF_Font * font = NULL; + enum FontStatus status = Success; + + if(renderer == NULL) { + status = InvalidArg; + goto exit; + } + + if(TTF_Init()) { + status = InitFailed; + goto exit; + } + + font = TTF_OpenFont("./fonts/SourceCodePro-Regular.ttf", 10); + + if(font == NULL) { + status = FailedOpen; + goto exit; + } + + surface = SDL_CreateRGBSurface(0, FONT_TEXTURE_SIZE, FONT_TEXTURE_SIZE, 32, 0, 0, 0, 0xff); + + /* load all ascii printable characters into the glyph atlas */ + size_t x = 0; + size_t y = 0; + for(char c = ' '; c <= '~'; c++) { + int w, h; + + const char ctext[] = {c, 0}; + text = TTF_RenderUTF8_Blended(font, ctext, WHITE); + assert(TTF_SizeText(font, ctext, &w, &h) == 0); + + if (x + w >= FONT_TEXTURE_SIZE) { + x = 0; + y += h+1; + } + + if (y + h >= FONT_TEXTURE_SIZE) { + status = FontTooBig; + goto exit; + } + + { + SDL_Rect * rect; + + rect = fontTexture.glyphs + (size_t)c; + rect->x = x; + rect->y = y; + rect->w = w; + rect->h = h; + + SDL_BlitSurface(text, NULL, surface, rect); + } + + x += w; + + SDL_FreeSurface(text); + } + + /* obtain a texture containing all the glyphs we are interested in */ + fontTexture.texture = SDL_CreateTextureFromSurface(renderer, surface); + + exit: + SDL_FreeSurface(surface); + TTF_CloseFont(font); + return status; +} + void canvas_stop(void) { + if (fontTexture.texture) { + SDL_DestroyTexture(fontTexture.texture); + fontTexture.texture = NULL; + } if (screen_texture) { SDL_DestroyTexture(screen_texture); screen_texture = NULL; @@ -43,10 +141,53 @@ void canvas_stop(void) { SDL_Quit(); } + +void canvas_connection_draw() { + size_t i = 0; + + /* start drawing at the upper right corner */ + int x = 0; + int y = 0; + + /* compute the pixels set for an ip */ + unsigned int ip_buckets[NET_MASK + 1]; + memset(ip_buckets, 0, (NET_MASK + 1) * sizeof(*ip_buckets)); + for(i = 0; i < num_conns; i++){ + size_t lsb = (conns + i)->addr.sin_addr.s_addr & NET_MASK; + ip_buckets[lsb] += (conns + i)->tracker.pixels_set; + } + + for(i = 0; i < NET_MASK + 1; i++){ + if (ip_buckets[i] == 0) { + continue; + } + char text_buffer[0x100]; + memset(text_buffer, 0, sizeof(text_buffer)); + + snprintf(text_buffer, sizeof(text_buffer), "ip: 192.168.2.%03ld %d", + i, ip_buckets[i]); + + size_t j = 0; + int max_height = 0; + for(j = 0; j < sizeof(text_buffer) && text_buffer[j]; j++) { + SDL_Rect src = fontTexture.glyphs[(size_t)text_buffer[j]]; + SDL_Rect dst = {.x=x, .y=y, .w=src.w, .h=src.h}; + SDL_RenderCopy(renderer, fontTexture.texture, &src, &dst); + x += dst.w; + if (dst.h > max_height) { + max_height = dst.h; + } + } + x = 0; + y += max_height + 1; + } +} + void canvas_draw(void) { // ? SDL_RenderClear(renderer); SDL_UpdateTexture(screen_texture, NULL, pixels, TEX_SIZE_X*4); SDL_RenderCopy(renderer, screen_texture, NULL, NULL); + canvas_connection_draw(); SDL_RenderPresent(renderer); } @@ -66,6 +207,8 @@ void canvas_start(void) { SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, TEX_SIZE_X, TEX_SIZE_Y); CLEANUP_AND_EXIT_IF(screen_texture == NULL, "SDL_CreateTexture"); + + CLEANUP_AND_EXIT_IF(init_font(renderer), "init_font"); } int canvas_set_px(const struct pixel *px) { diff --git a/connection.c b/connection.c index 7120e2c..2c5eadf 100644 --- a/connection.c +++ b/connection.c @@ -33,10 +33,10 @@ void connection_tracker_init(struct connection_tracker *t, in_addr_t addr, unsig void connection_tracker_print(const struct connection_tracker *t) { printf("Tracker {\n"); printf(" ip: %d.%d.%d.%d,\n", t->addr & 0xff, (t->addr >> 8) & 0xff, (t->addr >> 16) & 0xff, (t->addr >> 24) & 0xff); + printf(" pixels_set: %d,\n", t->pixels_set); printf(" start_time: %lld,\n", t->start_time); printf(" end_time: %lld,\n", t->end_time); printf("}\n"); - } void rect_iter_init(struct rect_iter *r) { diff --git a/connection.h b/connection.h index 17aba99..93f863c 100644 --- a/connection.h +++ b/connection.h @@ -4,10 +4,14 @@ #include #include "buffer.h" +#define MAX_CONNS 1024 +#include + void set_nonblocking(int fd); struct connection_tracker { in_addr_t addr; + unsigned int pixels_set; unsigned long long start_time; unsigned long long end_time; }; @@ -38,6 +42,9 @@ struct connection { struct buffer sendbuf; }; +extern struct connection conns[MAX_CONNS]; +extern size_t num_conns; + void connection_print(const struct connection *c); void connection_init(struct connection *c, int connfd, struct sockaddr_in connaddr); void connection_close(struct connection *c); diff --git a/fonts/SourceCodePro-Regular.ttf b/fonts/SourceCodePro-Regular.ttf new file mode 100644 index 0000000..b1fa336 Binary files /dev/null and b/fonts/SourceCodePro-Regular.ttf differ diff --git a/net.c b/net.c index 05c5353..9a09d70 100644 --- a/net.c +++ b/net.c @@ -20,7 +20,6 @@ // if one connection in the middle is removed, conns[num_conns - 1] is moved in its spot. // this is like rust's Vec::swap_remove. // when iterating over the connections, we need to make sure that the one that got swapped is not skipped. -#define MAX_CONNS 1024 struct connection conns[MAX_CONNS]; size_t num_conns = 0; pthread_t net_thread; @@ -95,6 +94,7 @@ static void *net_thread_main(void *arg) { int status = connection_recv(c, &px); if (status == COMMAND_MULTIRECV_NEXT || status == COMMAND_PRINT) { + c->tracker.pixels_set++; canvas_set_px(&px); } else if (status == COMMAND_GET) { int inside_canvas = canvas_get_px(&px);