From 6478a82a947e3662c31b95682661f2de9952944d Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Thu, 29 Dec 2022 06:48:34 +0200 Subject: [PATCH 1/1] initial commit --- .gitignore | 5 +++ Makefile | 28 +++++++++++++ src/darray.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/darray.h | 48 +++++++++++++++++++++++ src/main.c | 24 ++++++++++++ src/tgfx.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++ src/tgfx.h | 22 +++++++++++ src/tui.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tui.h | 32 +++++++++++++++ src/tuipriv.h | 29 ++++++++++++++ src/util.c | 54 +++++++++++++++++++++++++ src/util.h | 33 ++++++++++++++++ 12 files changed, 613 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 src/darray.c create mode 100644 src/darray.h create mode 100644 src/main.c create mode 100644 src/tgfx.c create mode 100644 src/tgfx.h create mode 100644 src/tui.c create mode 100644 src/tui.h create mode 100644 src/tuipriv.h create mode 100644 src/util.c create mode 100644 src/util.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9a0d302 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.o +*.swp +*.d +oftp +*.exe diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..26c7bac --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +obj = main.obj tgfx.obj tui.obj darray.obj util.obj +bin = oftp.exe + +#opt = -otexan +warn = -w=3 +dbg = -d3 + +CC = wcc386 +LD = wlink +CFLAGS = $(warn) $(dbg) $(opt) $(def) -zq -bt=dos + +$(bin): $(obj) + %write objects.lnk $(obj) + $(LD) debug all option map name $@ system dos4g file { @objects } $(LDFLAGS) + +.c: src +.asm: src + +.c.obj: .autodepend + $(CC) -fo=$@ $(CFLAGS) $< + +.asm.obj: + nasm -f obj -o $@ $[*.asm + +clean: .symbolic + del *.obj + del objects.lnk + del $(bin) diff --git a/src/darray.c b/src/darray.c new file mode 100644 index 0000000..5abbf66 --- /dev/null +++ b/src/darray.c @@ -0,0 +1,121 @@ +#include +#include +#include +#include "darray.h" +#include "util.h" + +/* The array descriptor keeps auxilliary information needed to manipulate + * the dynamic array. It's allocated adjacent to the array buffer. + */ +struct arrdesc { + int nelem, szelem; + int max_elem; + int bufsz; /* not including the descriptor */ +}; + +#define DESC(x) ((struct arrdesc*)((char*)(x) - sizeof(struct arrdesc))) + +void *darr_alloc(int elem, int szelem) +{ + struct arrdesc *desc; + + desc = malloc_nf(elem * szelem + sizeof *desc); + desc->nelem = desc->max_elem = elem; + desc->szelem = szelem; + desc->bufsz = elem * szelem; + return (char*)desc + sizeof *desc; +} + +void darr_free(void *da) +{ + if(da) { + free(DESC(da)); + } +} + +void *darr_resize_impl(void *da, int elem) +{ + int newsz; + struct arrdesc *desc; + + if(!da) return 0; + desc = DESC(da); + + newsz = desc->szelem * elem; + desc = realloc_nf(desc, newsz + sizeof *desc); + + desc->nelem = desc->max_elem = elem; + desc->bufsz = newsz; + return (char*)desc + sizeof *desc; +} + +int darr_empty(void *da) +{ + return DESC(da)->nelem ? 0 : 1; +} + +int darr_size(void *da) +{ + return DESC(da)->nelem; +} + + +void *darr_clear_impl(void *da) +{ + return darr_resize_impl(da, 0); +} + +/* stack semantics */ +void *darr_push_impl(void *da, void *item) +{ + struct arrdesc *desc; + int nelem; + + desc = DESC(da); + nelem = desc->nelem; + + if(nelem >= desc->max_elem) { + /* need to resize */ + int newsz = desc->max_elem ? desc->max_elem * 2 : 1; + + da = darr_resize_impl(da, newsz); + desc = DESC(da); + desc->nelem = nelem; + } + + if(item) { + memcpy((char*)da + desc->nelem * desc->szelem, item, desc->szelem); + } + desc->nelem++; + return da; +} + +void *darr_pop_impl(void *da) +{ + struct arrdesc *desc; + int nelem; + + desc = DESC(da); + nelem = desc->nelem; + + if(!nelem) return da; + + if(nelem <= desc->max_elem / 3) { + /* reclaim space */ + int newsz = desc->max_elem / 2; + + da = darr_resize_impl(da, newsz); + desc = DESC(da); + desc->nelem = nelem; + } + desc->nelem--; + + return da; +} + +void *darr_finalize(void *da) +{ + struct arrdesc *desc = DESC(da); + memmove(desc, da, desc->bufsz); + return desc; +} diff --git a/src/darray.h b/src/darray.h new file mode 100644 index 0000000..b9a7051 --- /dev/null +++ b/src/darray.h @@ -0,0 +1,48 @@ +#ifndef DYNAMIC_ARRAY_H_ +#define DYNAMIC_ARRAY_H_ + +void *darr_alloc(int elem, int szelem); +void darr_free(void *da); +void *darr_resize_impl(void *da, int elem); +#define darr_resize(da, elem) do { (da) = darr_resize_impl(da, elem); } while(0) + +int darr_empty(void *da); +int darr_size(void *da); + +void *darr_clear_impl(void *da); +#define darr_clear(da) do { (da) = darr_clear_impl(da); } while(0) + +/* stack semantics */ +void *darr_push_impl(void *da, void *item); +#define darr_push(da, item) do { (da) = darr_push_impl(da, item); } while(0) +void *darr_pop_impl(void *da); +#define darr_pop(da) do { (da) = darr_pop_impl(da); } while(0) + +/* Finalize the array. No more resizing is possible after this call. + * Use free() instead of dynarr_free() to deallocate a finalized array. + * Returns pointer to the finalized array. + * Complexity: O(n) + */ +void *darr_finalize(void *da); + +/* utility macros to push characters to a string. assumes and maintains + * the invariant that the last element is always a zero + */ +#define darr_strpush(da, c) \ + do { \ + char cnull = 0, ch = (char)(c); \ + (da) = dynarr_pop_impl(da); \ + (da) = dynarr_push_impl((da), &ch); \ + (da) = dynarr_push_impl((da), &cnull); \ + } while(0) + +#define darr_strpop(da) \ + do { \ + char cnull = 0; \ + (da) = dynarr_pop_impl(da); \ + (da) = dynarr_pop_impl(da); \ + (da) = dynarr_push_impl((da), &cnull); \ + } while(0) + + +#endif /* DYNAMIC_ARRAY_H_ */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..5fb66a6 --- /dev/null +++ b/src/main.c @@ -0,0 +1,24 @@ +#include +#include "tgfx.h" + +int main(void) +{ + tg_bgchar(' '); + tg_clear(); + + tg_bgcolor(1); + tg_rect("Remote", 0, 0, 40, 23, TGFX_FRAME); + tg_rect("Local", 40, 0, 40, 23, TGFX_FRAME); + + tg_fgcolor(7); + tg_text(0, 24, "fooolalala bar"); +/* tg_setcursor(2, 24);*/ + + while(getch() != 27); + + tg_bgchar(' '); + tg_bgcolor(0); + tg_fgcolor(7); + tg_clear(); + return 0; +} diff --git a/src/tgfx.c b/src/tgfx.c new file mode 100644 index 0000000..9818286 --- /dev/null +++ b/src/tgfx.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include "tgfx.h" + +static unsigned short attr = 0x0700; +static int bgcol = 0, fgcol = 7; +static int bgchar = ' '; +static unsigned short *framebuf = (unsigned short*)0xb8000; + +#define UPD_ATTR attr = (fgcol << 8) | (bgcol << 12) + +void tg_clear(void) +{ + tg_rect(0, 0, 0, 80, 25, 0); +} + +void tg_fgcolor(int col) +{ + fgcol = col & 0xf; + UPD_ATTR; +} + +void tg_bgcolor(int col) +{ + bgcol = col & 0xf; + UPD_ATTR; +} + +void tg_color(int col) +{ + fgcol = col & 0xf; + bgcol = (col >> 4) & 0xf; + attr = col << 8; +} + +void tg_bgchar(int c) +{ + bgchar = c; +} + +#define CRTC_ADDR_PORT 0x3d4 +#define CRTC_DATA_PORT 0x3d5 +#define REG_CRTC_CURH 0xe +#define REG_CRTC_CURL 0xf + +void tg_setcursor(int x, int y) +{ + unsigned int addr = y * 80 + x; + + outpw(CRTC_ADDR_PORT, (addr & 0xff00) | REG_CRTC_CURH); + outpw(CRTC_ADDR_PORT, (addr << 8) | REG_CRTC_CURL); +} + +void tg_text(int x, int y, const char *fmt, ...) +{ + va_list ap; + char buf[128], *ptr; + unsigned short *fbptr = framebuf + y * 80 + x; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + + ptr = buf; + while(*ptr) { + *fbptr++ = *ptr++ | attr; + } +} + +void tg_rect(const char *label, int x, int y, int xsz, int ysz, unsigned int flags) +{ + int i, j; + unsigned short *fbptr = framebuf + y * 80 + x; + + for(i=0; i +#include +#include +#include +#include "tui.h" +#include "tuipriv.h" +#include "tgfx.h" +#include "darray.h" + + +int tui_init(void) +{ + return 0; +} + +void tui_shutdown(void) +{ +} + + +struct tui_widget *tui_widget(int type) +{ + struct tui_widget *w; + + if(!(w = calloc(1, sizeof *w))) { + return 0; + } + w->type = type; + return w; +} + +void tui_free(struct tui_widget *w) +{ + if(w) { + free(w->title); + free(w); + } +} + +void tui_set_callback(struct tui_widget *w, int type, tui_callback func, void *cls) +{ + w->cbfunc[type] = func; + w->cbcls[type] = cls; +} + +struct tui_widget *tui_window(const char *title, int x, int y, int width, int height) +{ + struct tui_widget *w; + + if(!(w = tui_widget(TUI_WINDOW))) { + return 0; + } + w->title = strdup(title); + w->x = x; + w->y = y; + w->width = width; + w->height = height; + return w; +} + +struct tui_widget *tui_button(const char *title, int x, int y, tui_callback cbfunc, void *cbdata) +{ + struct tui_widget *w; + + if(!(w = tui_widget(TUI_BUTTON))) { + return 0; + } + w->title = strdup(title); + w->x = x; + w->y = y; + w->width = strlen(title) + 2; + w->height = 1; + + if(cbfunc) { + tui_set_callback(w, TUI_ONCLICK, cbfunc, cbdata); + } + return w; +} + +struct tui_widget *tui_list(const char *title, int x, int y, int width, int height, tui_callback cbfunc, void *cbdata) +{ + struct tui_list *w; + + if(!(w = calloc(1, sizeof *w))) { + return 0; + } + w->type = TUI_LIST; + w->title = strdup(title); + w->x = x; + w->y = y; + w->width = width; + w->height = height; + + if(cbfunc) { + tui_set_callback((struct tui_widget*)w, TUI_ONMODIFY, cbfunc, cbdata); + } + return (struct tui_widget*)w; +} + + +void tui_clear_list(struct tui_widget *w) +{ + struct tui_list *wl = (struct tui_list*)w; + assert(wl->type == TUI_LIST); + darr_clear(wl->entries); +} + +void tui_add_list_item(struct tui_widget *w, const char *text) +{ + struct tui_list *wl = (struct tui_list*)w; + assert(wl->type == TUI_LIST); + darr_push(wl->entries, &text); +} diff --git a/src/tui.h b/src/tui.h new file mode 100644 index 0000000..b5f7faf --- /dev/null +++ b/src/tui.h @@ -0,0 +1,32 @@ +#ifndef TUI_H_ +#define TUI_H_ + +struct tui_widget; +typedef void (*tui_callback)(struct tui_widget*); + +/* widget types */ +enum { TUI_UNKNOWN, TUI_WINDOW, TUI_BUTTON, TUI_LIST }; +/* callback types */ +enum { + TUI_ONCLICK, + TUI_ONMODIFY, + TUI_NUM_CALLBACKS +}; + +int tui_init(void); +void tui_shutdown(void); + +struct tui_widget *tui_widget(int type); +void tui_free(struct tui_widget *w); + +void tui_set_callback(struct tui_widget *w, int type, tui_callback func, void *cls); + +struct tui_widget *tui_window(const char *title, int x, int y, int w, int h); +struct tui_widget *tui_button(const char *title, int x, int y, tui_callback cbfunc, void *cbdata); +struct tui_widget *tui_list(const char *title, int x, int y, int w, int h, tui_callback cbfunc, void *cbdata); + +void tui_clear_list(struct tui_widget *w); +void tui_add_list_item(struct tui_widget *w, const char *text); + + +#endif /* TUI_H_ */ diff --git a/src/tuipriv.h b/src/tuipriv.h new file mode 100644 index 0000000..144e500 --- /dev/null +++ b/src/tuipriv.h @@ -0,0 +1,29 @@ +#ifndef TUIPRIV_H_ +#define TUIPRIV_H_ + +#include "tui.h" + +#define WCOMMON \ + int type; \ + char *title; \ + int x, y, width, height; \ + tui_callback cbfunc[TUI_NUM_CALLBACKS]; \ + void *cbcls[TUI_NUM_CALLBACKS]; \ + struct widget *next + +struct tui_widget { + WCOMMON; +}; + +struct tui_list_entry { + char *text; + void *data; +}; + +struct tui_list { + WCOMMON; + char **entries; /* darr */ +}; + + +#endif /* TUIPRIV_H_ */ diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..7be1859 --- /dev/null +++ b/src/util.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include "util.h" + +void *malloc_nf_impl(size_t sz, const char *file, int line) +{ + void *p; + if(!(p = malloc(sz))) { + fprintf(stderr, "%s:%d failed to allocate %lu bytes\n", file, line, (unsigned long)sz); + abort(); + } + return p; +} + +void *calloc_nf_impl(size_t num, size_t sz, const char *file, int line) +{ + void *p; + if(!(p = calloc(num, sz))) { + fprintf(stderr, "%s:%d failed to allocate %lu bytes\n", file, line, (unsigned long)(sz * num)); + abort(); + } + return p; +} + +void *realloc_nf_impl(void *p, size_t sz, const char *file, int line) +{ + if(!(p = realloc(p, sz))) { + fprintf(stderr, "%s:%d failed to realloc %lu bytes\n", file, line, (unsigned long)sz); + abort(); + } + return p; +} + +char *strdup_nf_impl(const char *s, const char *file, int line) +{ + char *res; + if(!(res = strdup(s))) { + fprintf(stderr, "%s:%d failed to duplicate string\n", file, line); + abort(); + } + return res; +} + + +int match_prefix(const char *str, const char *prefix) +{ + while(*str && *prefix) { + if(*str++ != *prefix++) { + return 0; + } + } + return *prefix ? 0 : 1; +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..8b27bac --- /dev/null +++ b/src/util.h @@ -0,0 +1,33 @@ +#ifndef UTIL_H_ +#define UTIL_H_ + +#include +#include + +#if defined(__WATCOMC__) || defined(WIN32) +#include +#else +#if !defined(__FreeBSD__) && !defined(__OpenBSD__) +#include +#endif +#endif + +#ifdef _MSC_VER +#define strcasecmp(s, k) stricmp(s, k) +#endif + +#define malloc_nf(sz) malloc_nf_impl(sz, __FILE__, __LINE__) +void *malloc_nf_impl(size_t sz, const char *file, int line); + +#define calloc_nf(num, sz) calloc_nf_impl(num, sz, __FILE__, __LINE__) +void *calloc_nf_impl(size_t num, size_t sz, const char *file, int line); + +#define realloc_nf(p, sz) realloc_nf_impl(p, sz, __FILE__, __LINE__) +void *realloc_nf_impl(void *p, size_t sz, const char *file, int line); + +#define strdup_nf(s) strdup_nf_impl(s, __FILE__, __LINE__) +char *strdup_nf_impl(const char *s, const char *file, int line); + +int match_prefix(const char *str, const char *prefix); + +#endif /* UTIL_H_ */ -- 1.7.10.4