From 433de277b359edb9e43120ab1532601233c530a1 Mon Sep 17 00:00:00 2001 From: cancel Date: Sun, 25 Nov 2018 03:20:44 +0900 Subject: [PATCH] Split stuff out from main.c into separate files --- Makefile | 2 +- base.h | 6 ++ field.c | 131 ++++++++++++++++++++++++++++++++ field.h | 18 +++++ main.c | 228 +------------------------------------------------------ 5 files changed, 158 insertions(+), 227 deletions(-) create mode 100644 base.h create mode 100644 field.c create mode 100644 field.h diff --git a/Makefile b/Makefile index 4a2f59c..c67c04e 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ debug_flags := -DDEBUG -O0 -ggdb -feliminate-unused-debug-symbols sanitize_flags := -fsanitize=address -fsanitize=leak -fsanitize=undefined release_flags := -DNDEBUG -O2 -s -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fpie -Wl,-pie library_flags := -lncurses -source_files := main.c +source_files := field.c main.c all: debug diff --git a/base.h b/base.h new file mode 100644 index 0000000..b13104d --- /dev/null +++ b/base.h @@ -0,0 +1,6 @@ +#pragma once +#include + +typedef char Term; +typedef uint32_t U32; +typedef int32_t I32; diff --git a/field.c b/field.c new file mode 100644 index 0000000..5257b82 --- /dev/null +++ b/field.c @@ -0,0 +1,131 @@ +#include "field.h" +#include +#include +#include +#include + +void field_init_zeros(Field* f, U32 height, U32 width) { + size_t num_cells = height * width; + f->buffer = calloc(num_cells, sizeof(Term)); + f->height = height; + f->width = width; +} + +void field_init_fill(Field* f, U32 height, U32 width, Term fill_char) { + size_t num_cells = height * width; + f->buffer = malloc(num_cells * sizeof(Term)); + memset(f->buffer, fill_char, num_cells); + f->height = height; + f->width = width; +} + +void field_resize_raw(Field* f, U32 height, U32 width) { + size_t cells = height * width; + f->buffer = realloc(f->buffer, cells * sizeof(Term)); + f->height = height; + f->width = width; +} + +void field_deinit(Field* f) { + assert(f->buffer != NULL); + free(f->buffer); +#ifndef NDEBUG + f->buffer = NULL; +#endif +} + +void field_copy_subrect(Field* src, Field* dest, U32 src_y, U32 src_x, + U32 dest_y, U32 dest_x, U32 height, U32 width) { + size_t src_height = src->height; + size_t src_width = src->width; + size_t dest_height = dest->height; + size_t dest_width = dest->width; + if (src_height <= src_y || src_width <= src_x || dest_height <= dest_y || + dest_width <= dest_x) + return; + size_t ny_0 = src_height - src_y; + size_t ny_1 = dest_height - dest_y; + size_t ny = height; + if (ny_0 < ny) + ny = ny_0; + if (ny_1 < ny) + ny = ny_1; + if (ny == 0) + return; + size_t row_copy_0 = src_width - src_x; + size_t row_copy_1 = dest_width - dest_x; + size_t row_copy = width; + if (row_copy_0 < row_copy) + row_copy = row_copy_0; + if (row_copy_1 < row_copy) + row_copy = row_copy_1; + size_t copy_bytes = row_copy * sizeof(Term); + Term* src_p = src->buffer + src_y * src_width + src_x; + Term* dest_p = dest->buffer + dest_y * dest_width + dest_x; + size_t src_stride; + size_t dest_stride; + if (src_y >= dest_y) { + src_stride = src_width; + dest_stride = dest_width; + } else { + src_p += (ny - 1) * src_width; + dest_p += (ny - 1) * dest_width; + src_stride = -src_width; + dest_stride = -dest_width; + } + size_t iy = 0; + for (;;) { + memmove(dest_p, src_p, copy_bytes); + ++iy; + if (iy == ny) + break; + src_p += src_stride; + dest_p += dest_stride; + } +} + +void field_fill_subrect(Field* f, U32 y, U32 x, U32 height, U32 width, + Term fill_char) { + size_t f_height = f->height; + size_t f_width = f->width; + if (y >= f_height || x >= f_width) + return; + size_t rows_0 = f_height - y; + size_t rows = height; + if (rows_0 < rows) + rows = rows_0; + if (rows == 0) + return; + size_t columns_0 = f_width - x; + size_t columns = width; + if (columns_0 < columns) + columns = columns_0; + size_t fill_bytes = columns * sizeof(Term); + Term* p = f->buffer + y * f_width + x; + size_t iy = 0; + for (;;) { + memset(p, fill_char, fill_bytes); + ++iy; + if (iy == rows) + break; + p += f_width; + } +} + +void field_debug_draw(Field* f, int offset_y, int offset_x) { + enum { Line_buffer_count = 4096 }; + chtype line_buffer[Line_buffer_count]; + size_t f_height = f->height; + size_t f_width = f->width; + Term* f_buffer = f->buffer; + if (f_width > Line_buffer_count) + return; + for (size_t iy = 0; iy < f_height; ++iy) { + Term* row_p = f_buffer + f_width * iy; + for (size_t ix = 0; ix < f_width; ++ix) { + line_buffer[ix] = (chtype)row_p[ix]; + } + move(iy + offset_y, offset_x); + addchnstr(line_buffer, (int)f_width); + } +} diff --git a/field.h b/field.h new file mode 100644 index 0000000..07b8c9a --- /dev/null +++ b/field.h @@ -0,0 +1,18 @@ +#pragma once +#include "base.h" + +typedef struct { + Term* buffer; + U32 height; + U32 width; +} Field; + +void field_init_zeros(Field* f, U32 height, U32 width); +void field_init_fill(Field* f, U32 height, U32 width, Term fill_char); +void field_resize_raw(Field* f, U32 height, U32 width); +void field_deinit(Field* f); +void field_copy_subrect(Field* src, Field* dest, U32 src_y, U32 src_x, + U32 dest_y, U32 dest_x, U32 height, U32 width); +void field_fill_subrect(Field* f, U32 y, U32 x, U32 height, U32 width, + Term fill_char); +void field_debug_draw(Field* f, int offset_y, int offset_x); diff --git a/main.c b/main.c index 8bf2dc8..d761c35 100644 --- a/main.c +++ b/main.c @@ -1,238 +1,14 @@ +#include "base.h" +#include "field.h" #include #include #include #include #include -#include #include #include #include -typedef char Term; -typedef uint32_t U32; -typedef int32_t I32; - -typedef struct { - Term* buffer; - U32 height; - U32 width; -} Field; - -void field_init_zeros(Field* f, U32 height, U32 width) { - size_t num_cells = height * width; - f->buffer = calloc(num_cells, sizeof(Term)); - f->height = height; - f->width = width; -} - -void field_init_fill(Field* f, U32 height, U32 width, Term fill_char) { - size_t num_cells = height * width; - f->buffer = malloc(num_cells * sizeof(Term)); - memset(f->buffer, fill_char, num_cells); - f->height = height; - f->width = width; -} - -void field_realloc(Field* f, U32 height, U32 width) { - size_t cells = height * width; - f->buffer = realloc(f->buffer, cells * sizeof(Term)); - f->height = height; - f->width = width; -} - -void field_deinit(Field* f) { - assert(f->buffer != NULL); - free(f->buffer); -#ifndef NDEBUG - f->buffer = NULL; -#endif -} - -void field_copy_subrect(Field* src, Field* dest, U32 src_y, U32 src_x, - U32 dest_y, U32 dest_x, U32 height, U32 width) { - size_t src_height = src->height; - size_t src_width = src->width; - size_t dest_height = dest->height; - size_t dest_width = dest->width; - if (src_height <= src_y || src_width <= src_x || dest_height <= dest_y || - dest_width <= dest_x) - return; - size_t ny_0 = src_height - src_y; - size_t ny_1 = dest_height - dest_y; - size_t ny = height; - if (ny_0 < ny) - ny = ny_0; - if (ny_1 < ny) - ny = ny_1; - if (ny == 0) - return; - size_t row_copy_0 = src_width - src_x; - size_t row_copy_1 = dest_width - dest_x; - size_t row_copy = width; - if (row_copy_0 < row_copy) - row_copy = row_copy_0; - if (row_copy_1 < row_copy) - row_copy = row_copy_1; - size_t copy_bytes = row_copy * sizeof(Term); - Term* src_p = src->buffer + src_y * src_width + src_x; - Term* dest_p = dest->buffer + dest_y * dest_width + dest_x; - size_t src_stride; - size_t dest_stride; - if (src_y >= dest_y) { - src_stride = src_width; - dest_stride = dest_width; - } else { - src_p += (ny - 1) * src_width; - dest_p += (ny - 1) * dest_width; - src_stride = -src_width; - dest_stride = -dest_width; - } - size_t iy = 0; - for (;;) { - memmove(dest_p, src_p, copy_bytes); - ++iy; - if (iy == ny) - break; - src_p += src_stride; - dest_p += dest_stride; - } -} - -void field_fill_subrect(Field* f, U32 y, U32 x, U32 height, U32 width, - Term fill_char) { - size_t f_height = f->height; - size_t f_width = f->width; - if (y >= f_height || x >= f_width) - return; - size_t rows_0 = f_height - y; - size_t rows = height; - if (rows_0 < rows) - rows = rows_0; - if (rows == 0) - return; - size_t columns_0 = f_width - x; - size_t columns = width; - if (columns_0 < columns) - columns = columns_0; - size_t fill_bytes = columns * sizeof(Term); - Term* p = f->buffer + y * f_width + x; - size_t iy = 0; - for (;;) { - memset(p, fill_char, fill_bytes); - ++iy; - if (iy == rows) - break; - p += f_width; - } -} - -void field_debug_draw(Field* f, int term_y, int term_x) { - enum { Line_buffer_count = 4096 }; - chtype line_buffer[Line_buffer_count]; - size_t f_height = f->height; - size_t f_width = f->width; - Term* f_buffer = f->buffer; - if (f_width > Line_buffer_count) - return; - for (size_t iy = 0; iy < f_height; ++iy) { - Term* row_p = f_buffer + f_width * iy; - for (size_t ix = 0; ix < f_width; ++ix) { - line_buffer[ix] = (chtype)row_p[ix]; - } - move(iy + term_y, term_x); - addchnstr(line_buffer, (int)f_width); - } -} - -typedef struct { - chtype* buffer; - int size_y; - int size_x; - chtype fill_char; -} view_state; - -void init_view_state(view_state* vs) { - vs->buffer = NULL; - vs->size_y = 0; - vs->size_x = 0; - vs->fill_char = '?'; -} - -void deinit_view_state(view_state* vs) { - // Note that we don't have to check if the buffer was ever actually set to a - // non-null pointer: `free` does this for us. - free(vs->buffer); -} - -void update_view_state(view_state* vs, int term_height, int term_width, - chtype fill_char) { - bool same_dimensions = vs->size_y == term_height && vs->size_x == term_width; - bool same_fill_char = vs->fill_char == fill_char; - // If nothing has changed, we don't have any work to do. - if (same_dimensions && same_fill_char) - return; - if (!same_dimensions) { - // Note that this doesn't check for overflow. In theory that's unsafe, but - // really unlikely to happen here. - size_t term_cells = term_height * term_width; - size_t new_mem_size = term_cells * sizeof(chtype); - - // 'realloc' is like malloc, but it lets you re-use a buffer instead of - // having to throw away an old one and create a new one. Oftentimes, the - // cost of 'realloc' is cheaper than 'malloc' for the C runtime, and it - // reduces memory fragmentation. - // - // It's called 'realloc', but you can also use it even you're starting out - // with a NULL pointer for your buffer. - vs->buffer = realloc(vs->buffer, new_mem_size); - vs->size_y = term_height; - vs->size_x = term_width; - } - if (!same_fill_char) { - vs->fill_char = fill_char; - } - - // (Re-)fill the buffer with the new data. - chtype* buff = vs->buffer; - // For each row, in the buffer, fill it with an alternating pattern of spaces - // and the character the user typed. - for (int iy = 0; iy < term_height; ++iy) { - // Make a pointer to the start of this line in the buffer. We don't - // actually have to do this -- we could replace line[ix] with (buff + iy * - // term_width + ix), but this makes it easier to see what's going on. - chtype* line = buff + iy * term_width; - for (int ix = 0; ix < term_width; ++ix) { - // Note that 'if' here is being used with a numerical value instead a - // boolean. C doesn't actually have real booleans: a 0 value (whatever - // the number type happens to be, int, char, etc.) is considered 'false', - // and anything else is 'true'. - if ((iy + ix) % 2) { - line[ix] = ' '; - } else { - line[ix] = fill_char; - } - } - } -} - -void draw_view_state(view_state* vs) { - // Loop over each row in the buffer, and send the entire row to ncurses all - // at once. This is the fastest way to draw to the terminal with ncurses. - for (int i = 0; i < vs->size_y; ++i) { - // Move the cursor directly to the start of the row. - move(i, 0); - // Send the entire line at once. If it's too long, it will be truncated - // instead of wrapping. - // - // We use addchnstr instead of addchstr (notice the 'n') because we know - // exactly how long the line is, and we don't have a null terminator in our - // string. If we tried to use addchstr, it would keep trying to read until - // it got to the end of our buffer, and then past the end of our buffer - // into unknown memory, because we don't have a null terminator in it. - addchnstr(vs->buffer + i * vs->size_x, vs->size_x); - } -} - int main() { // Enable UTF-8 by explicitly initializing our locale before initializing // ncurses.