diff --git a/src/_ct_shared.h b/src/_ct_shared.h new file mode 100644 index 0000000..6e9cac6 --- /dev/null +++ b/src/_ct_shared.h @@ -0,0 +1,7 @@ +#ifndef _CURSETREE__SHARED_H +#define _CURSETREE__SHARED_H + +#define OK (0) +#define ERR (1) + +#endif /* _CURSETREE__SHARED_H */ diff --git a/src/surface.c b/src/surface.c new file mode 100644 index 0000000..6c1fdbc --- /dev/null +++ b/src/surface.c @@ -0,0 +1,163 @@ +#include +#include + +#include "_ct_shared.h" +#include "surface.h" + +#define ALLOC_ROW(w) ((char *)calloc(w, sizeof(char))) + +/* Allocates a new 2D surface buffer with ROWS=h, COLS=w. + * Returns NULL on failure and sets errno, otherwise + * a pointer to the new buffer is returned. + */ +static inline __attribute__((malloc, warn_unused_result)) char ** +sfbuf(const pos w, const pos h) { + char **buf; + pos i; + + buf = (char **)malloc(sizeof(char *) * h); + if (buf == NULL) + return buf; + + for (i = 0; i < w; i++) { + buf[i] = ALLOC_ROW(w); + if (buf[i] == NULL) + goto fail; + } + return buf; + +fail: + // Immediately decrement since we know the + // current index is where ALLOW_ROW() failed. + while (--i >= 0) + free(buf[i]); + free(buf); + return NULL; +} + +/* Resize the width (columns) of a surface. + * Returns 0 on success, or a non-zero errno on failure. + * WARNING: Don't use sfseth directly, see sfresize() + * WARNING: and how it optimises based on how height changes. + */ +static inline int sfsetw(const pos w1, surface *const s) { + void *ptr; + pos i, w0 = s->w; + char **row; + + errno = 0; // sanity check + + if (w1 == w0) + return OK; + else if (w1 == 0) { + return EINVAL; + } + + for (i = 0, row = s->buf; i < s->h; i++, row++) { + ptr = (void *)reallocarray(*row, w1, sizeof(char)); + if (ptr == NULL) { + return errno; + } + *row = (char *)ptr; + } + + s->w = w1; + return OK; +} + +/* Resize the height (rows) of a surface. + * Returns 0 on success, or a non-zero errno on failure. + * WARNING: Don't use sfseth directly, see sfresize() + * WARNING: and how it optimises based on how height changes. + */ +static inline int sfseth(const pos h1, surface *const s) { + void *ptr; + pos i, h0 = s->h; + char **row; + + errno = 0; // sanity check + + if (h1 == h0) + return OK; + else if (h1 == 0) { + return EINVAL; + } + + // height decreased (free bottom rows) + if (h1 < h0) { + for (i = h1, row = s->buf; i < h0; i++, row++) { + free(*row); + } + } + + // use ptr to avoid losing unfreed s->buf + ptr = (void *)reallocarray(s->buf, h1, sizeof(char)); + if (ptr == NULL) + return errno; + // ENOMEM X_X + + s->buf = (char **)ptr; + s->h = h1; + + // height increased (alloc bottom rows) + if (h1 > h0) { + for (i = h0, row = s->buf; i < h1; i++, row++) { + *row = ALLOC_ROW(s->w); + if (*row == NULL) + return errno; + } + } + + s->h = h1; + return OK; +} + +/* + */ +int sfresize(const pos w1, const pos h1, surface *const s) { + int retv; + + /* Height is the 2D array's primary index dimension, + * whether it grows/shrinks determines whether it is + * optimal to resize width then height, or height then width. + * ie Height increasing but doing sfseth() before sfsetw() + * would result in new rows being allocated with the old width, + * then resized again to the new width (not optimal). + */ + if (h1 > s->h) { + if ((retv = sfsetw(w1, s)) || (retv = sfseth(h1, s))) + return retv; + } else if ((retv = sfseth(h1, s)) || (retv = sfsetw(w1, s))) + return retv; + return OK; +} + +__attribute__((malloc, warn_unused_result)) surface *sfalloc(const pos x, const pos y, const pos w, const pos h) { + surface *s; + + char **buf = sfbuf(w, h); + if (buf == NULL) + return NULL; + + s = (surface *)malloc(sizeof(struct ct_surface)); + *s = (surface){ + .x = x, + .y = y, + .w = w, + .h = y, + .vcx = 1, + .vcy = 1, + .buf = buf, + }; + + return s; +} + +void sffree(surface *const s) { + pos i; + char **row; + for (i=0, row=s->buf; i < s->h; i++, row++) { + free(*row); + } + free(s->buf); +} diff --git a/src/surface.h b/src/surface.h new file mode 100644 index 0000000..4744f30 --- /dev/null +++ b/src/surface.h @@ -0,0 +1,16 @@ +#ifndef _CURSETREE_SURFACE_H +#define _CURSETREE_SURFACE_H + +/* Initial row and column coordinates */ +#define ROW0 1 +#define COL0 1 + +typedef unsigned short pos; + +typedef struct ct_surface { + pos x, y, w, h; + char **buf; + pos vcx, vcy; /* virtual cursor position */ +} surface; + +#endif /* _CURSETREE_SURFACE_H */