initial commit
authorJohn Tsiombikas <nuclear@member.fsf.org>
Thu, 29 Dec 2022 04:48:34 +0000 (06:48 +0200)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Thu, 29 Dec 2022 04:48:34 +0000 (06:48 +0200)
12 files changed:
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
src/darray.c [new file with mode: 0644]
src/darray.h [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/tgfx.c [new file with mode: 0644]
src/tgfx.h [new file with mode: 0644]
src/tui.c [new file with mode: 0644]
src/tui.h [new file with mode: 0644]
src/tuipriv.h [new file with mode: 0644]
src/util.c [new file with mode: 0644]
src/util.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..9a0d302
--- /dev/null
@@ -0,0 +1,5 @@
+*.o
+*.swp
+*.d
+oftp
+*.exe
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
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 (file)
index 0000000..5abbf66
--- /dev/null
@@ -0,0 +1,121 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#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 (file)
index 0000000..b9a7051
--- /dev/null
@@ -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 (file)
index 0000000..5fb66a6
--- /dev/null
@@ -0,0 +1,24 @@
+#include <conio.h>
+#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 (file)
index 0000000..9818286
--- /dev/null
@@ -0,0 +1,104 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <dos.h>
+#include <conio.h>
+#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<ysz; i++) {
+               for(j=0; j<xsz; j++) {
+                       fbptr[j] = attr | bgchar;
+               }
+               fbptr += 80;
+       }
+
+       if(flags & TGFX_FRAME) {
+               fbptr = framebuf + y * 80 + x;
+               for(i=0; i<xsz-2; i++) {
+                       fbptr[i + 1] = attr | 0xcd;
+                       fbptr[(ysz-1) * 80 + i + 1] = attr | 0xcd;
+               }
+               for(i=0; i<ysz-2; i++) {
+                       fbptr[(i + 1) * 80] = attr | 0xba;
+                       fbptr[(xsz-1) + (i + 1) * 80] = attr | 0xba;
+               }
+               fbptr[0] = attr | 0xc9;
+               fbptr[xsz-1] = attr | 0xbb;
+               fbptr += (ysz - 1) * 80;
+               fbptr[0] = attr | 0xc8;
+               fbptr[xsz-1] = attr | 0xbc;
+       }
+
+       if(label) {
+               tg_text(x + 2, y, "%s", label);
+       }
+}
diff --git a/src/tgfx.h b/src/tgfx.h
new file mode 100644 (file)
index 0000000..a9af11d
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef TGFX_H_
+#define TGFX_H_
+
+enum {
+       TGFX_FRAME              = 1,
+       TGFX_SHADOW             = 2
+};
+
+void tg_clear(void);
+
+void tg_fgcolor(int col);
+void tg_bgcolor(int col);
+void tg_color(int col);
+void tg_bgchar(int c);
+
+void tg_setcursor(int x, int y);
+
+void tg_printf(int x, int y, const char *fmt, ...);
+
+void tg_rect(const char *label, int x, int y, int xsz, int ysz, unsigned int flags);
+
+#endif /* TGFX_H_ */
diff --git a/src/tui.c b/src/tui.c
new file mode 100644 (file)
index 0000000..5eb7a29
--- /dev/null
+++ b/src/tui.c
@@ -0,0 +1,113 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#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 (file)
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 (file)
index 0000000..144e500
--- /dev/null
@@ -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 (file)
index 0000000..7be1859
--- /dev/null
@@ -0,0 +1,54 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#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 (file)
index 0000000..8b27bac
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef UTIL_H_
+#define UTIL_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(__WATCOMC__) || defined(WIN32)
+#include <malloc.h>
+#else
+#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
+#include <alloca.h>
+#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_ */