add struct ct_surface as a struct _win_st (ncurses) replacement

This commit is contained in:
Emile Clark-Boman 2025-09-24 15:47:56 +10:00
parent bcfcbaf529
commit 08e4376a35
3 changed files with 186 additions and 0 deletions

7
src/_ct_shared.h Normal file
View file

@ -0,0 +1,7 @@
#ifndef _CURSETREE__SHARED_H
#define _CURSETREE__SHARED_H
#define OK (0)
#define ERR (1)
#endif /* _CURSETREE__SHARED_H */

163
src/surface.c Normal file
View file

@ -0,0 +1,163 @@
#include <errno.h>
#include <stdlib.h>
#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);
}

16
src/surface.h Normal file
View file

@ -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 */