Start of major refactor for dynamic library and widget loading.

This commit is contained in:
Scott Duensing 2026-03-22 20:50:25 -05:00
parent 7ae7ea1a97
commit 163959a192
95 changed files with 3885 additions and 3292 deletions

View file

@ -1,28 +1,37 @@
# DVX GUI -- Top-level Makefile
#
# Builds the full DVX stack: library, task switcher, shell, and apps.
# Builds the full DVX stack: core library, task switcher,
# bootstrap loader, widgets, shell, and apps.
.PHONY: all clean dvx tasks dvxshell apps
.PHONY: all clean core tasks loader widgets shell apps
all: dvx tasks dvxshell apps
all: core tasks loader widgets shell apps
dvx:
$(MAKE) -C dvx
core:
$(MAKE) -C core
tasks:
$(MAKE) -C tasks
dvxshell: dvx tasks
$(MAKE) -C dvxshell
loader: core tasks
$(MAKE) -C loader
apps: dvx tasks dvxshell
widgets: core tasks
$(MAKE) -C widgets
shell: core tasks
$(MAKE) -C shell
apps: core tasks shell
$(MAKE) -C apps
clean:
$(MAKE) -C dvx clean
$(MAKE) -C core clean
$(MAKE) -C tasks clean
$(MAKE) -C dvxshell clean
$(MAKE) -C loader clean
$(MAKE) -C widgets clean
$(MAKE) -C shell clean
$(MAKE) -C apps clean
-rmdir obj/dvx/widgets obj/dvx/platform obj/dvx/thirdparty obj/dvx obj/tasks obj/dvxshell obj/apps obj 2>/dev/null
-rm -rf bin/config
-rmdir bin/apps/cpanel bin/apps/imgview bin/apps/progman bin/apps/notepad bin/apps/clock bin/apps/dvxdemo bin/apps bin lib 2>/dev/null
-rmdir obj 2>/dev/null
-rm -rf bin/config bin/widgets bin/libs
-rmdir bin/apps/cpanel bin/apps/imgview bin/apps/progman bin/apps/notepad bin/apps/clock bin/apps/dvxdemo bin/apps bin 2>/dev/null

View file

@ -4,7 +4,7 @@ DJGPP_PREFIX = $(HOME)/djgpp/djgpp
DJGPP_LIBPATH = $(HOME)/claude/windriver/tools/lib
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
DXE3GEN = PATH=$(DJGPP_PREFIX)/bin:$(PATH) DJDIR=$(DJGPP_PREFIX)/i586-pc-msdosdjgpp $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/dxe3gen
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../dvx -I../tasks -I../dvxshell
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../core -I../core/platform -I../core/thirdparty -I../tasks -I../tasks/thirdparty -I../shell
OBJDIR = ../obj/apps
BINDIR = ../bin/apps
@ -84,12 +84,12 @@ $(BINDIR)/dvxdemo:
mkdir -p $(BINDIR)/dvxdemo
# Dependencies
$(OBJDIR)/imgview.o: imgview/imgview.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvx/dvxVideo.h ../dvxshell/shellApp.h
$(OBJDIR)/cpanel.o: cpanel/cpanel.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxPrefs.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvx/platform/dvxPlatform.h ../dvxshell/shellApp.h
$(OBJDIR)/progman.o: progman/progman.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvxshell/shellApp.h ../dvxshell/shellInfo.h
$(OBJDIR)/notepad.o: notepad/notepad.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvxshell/shellApp.h
$(OBJDIR)/clock.o: clock/clock.c ../dvx/dvxApp.h ../dvx/dvxWidget.h ../dvx/dvxDraw.h ../dvx/dvxVideo.h ../dvxshell/shellApp.h ../tasks/taskswitch.h
$(OBJDIR)/dvxdemo.o: dvxdemo/dvxdemo.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvx/dvxVideo.h ../dvxshell/shellApp.h
$(OBJDIR)/imgview.o: imgview/imgview.c ../core/dvxApp.h ../core/dvxDialog.h ../core/dvxWidget.h ../core/dvxWm.h ../core/dvxVideo.h ../shell/shellApp.h
$(OBJDIR)/cpanel.o: cpanel/cpanel.c ../core/dvxApp.h ../core/dvxDialog.h ../core/dvxPrefs.h ../core/dvxWidget.h ../core/dvxWm.h ../core/platform/dvxPlatform.h ../shell/shellApp.h
$(OBJDIR)/progman.o: progman/progman.c ../core/dvxApp.h ../core/dvxDialog.h ../core/dvxWidget.h ../core/dvxWm.h ../shell/shellApp.h ../shell/shellInfo.h
$(OBJDIR)/notepad.o: notepad/notepad.c ../core/dvxApp.h ../core/dvxDialog.h ../core/dvxWidget.h ../core/dvxWm.h ../shell/shellApp.h
$(OBJDIR)/clock.o: clock/clock.c ../core/dvxApp.h ../core/dvxWidget.h ../core/dvxDraw.h ../core/dvxVideo.h ../shell/shellApp.h ../tasks/taskswitch.h
$(OBJDIR)/dvxdemo.o: dvxdemo/dvxdemo.c ../core/dvxApp.h ../core/dvxDialog.h ../core/dvxWidget.h ../core/dvxWm.h ../core/dvxVideo.h ../shell/shellApp.h
clean:
rm -f $(OBJDIR)/cpanel.o $(OBJDIR)/imgview.o $(OBJDIR)/progman.o $(OBJDIR)/notepad.o $(OBJDIR)/clock.o $(OBJDIR)/dvxdemo.o

View file

@ -14,9 +14,9 @@
#include "dvxPrefs.h"
#include "dvxWidget.h"
#include "dvxWm.h"
#include "platform/dvxPlatform.h"
#include "dvxPlatform.h"
#include "shellApp.h"
#include "thirdparty/stb_ds.h"
#include "stb_ds.h"
#include <dirent.h>
#include <stdio.h>

View file

@ -9,7 +9,7 @@
#include "dvxWidget.h"
#include "dvxWm.h"
#include "shellApp.h"
#include "thirdparty/stb_image.h"
#include "stb_image.h"
#include <stdio.h>
#include <stdlib.h>

View file

@ -14,7 +14,7 @@
#include "dvxDialog.h"
#include "dvxWidget.h"
#include "dvxWm.h"
#include "platform/dvxPlatform.h"
#include "dvxPlatform.h"
#include "shellApp.h"
#include <stdint.h>

View file

@ -23,7 +23,7 @@
#include "dvxPrefs.h"
#include "dvxWidget.h"
#include "dvxWm.h"
#include "platform/dvxPlatform.h"
#include "dvxPlatform.h"
#include "shellApp.h"
#include "shellTaskMgr.h"
#include "shellInfo.h"

14
config/dvxshell.dep Normal file
View file

@ -0,0 +1,14 @@
libtasks
libdvx
box
button
checkbox
dropdown
label
listbox
listview
radio
separator
spacer
statbar
textinpt

1
config/libdvx.dep Normal file
View file

@ -0,0 +1 @@
libtasks

74
core/Makefile Normal file
View file

@ -0,0 +1,74 @@
# DVX Core Library Makefile for DJGPP cross-compilation
#
# Builds libdvx.lib -- core GUI infrastructure (draw, compositor,
# window manager, event dispatch, layout engine, widget infrastructure).
# Zero widget implementations -- those are in ../widgets/ as .wgt modules.
DJGPP_PREFIX = $(HOME)/djgpp/djgpp
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
DXE3GEN = PATH=$(DJGPP_PREFIX)/bin:$(PATH) DJDIR=$(DJGPP_PREFIX)/i586-pc-msdosdjgpp $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/dxe3gen
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I. -Iplatform -I../tasks
OBJDIR = ../obj/core
LIBSDIR = ../bin/libs
# Core sources
SRCS = dvxVideo.c dvxDraw.c dvxComp.c dvxWm.c dvxIcon.c dvxImageWrite.c \
dvxApp.c dvxDialog.c dvxPrefs.c \
widgetClass.c widgetCore.c widgetScrollbar.c \
widgetLayout.c widgetEvent.c widgetOps.c
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
TARGET = $(LIBSDIR)/libdvx.lib
# libdvx.lib export prefixes
DVX_EXPORTS = -E _dvx -E _wgt -E _wm -E _prefs -E _rect -E _draw -E _pack -E _text \
-E _setClip -E _resetClip -E _stbi_ -E _stbi_write -E _dirtyList \
-E _widget \
-E _sCursor -E _sDbl -E _sDebug -E _sClosed -E _sFocused -E _sKey \
-E _sOpen -E _sPressed -E _sDrag -E _sDrawing -E _sResize \
-E _sListView -E _sSplitter -E _sTreeView \
-E _accelParse \
-E _clearOther -E _clipboard -E _isWord -E _multiClick -E _wordStart -E _wordEnd
.PHONY: all clean
all: $(TARGET) $(LIBSDIR)/libdvx.dep
$(LIBSDIR)/libdvx.dep: ../config/libdvx.dep | $(LIBSDIR)
sed 's/$$/\r/' $< > $@
$(TARGET): $(OBJS) | $(LIBSDIR)
$(DXE3GEN) -o $@ $(DVX_EXPORTS) -U $(OBJS)
$(OBJDIR)/%.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJDIR):
mkdir -p $(OBJDIR)
$(LIBSDIR):
mkdir -p $(LIBSDIR)
# Dependencies
CORE_HDRS = dvxTypes.h dvxApp.h dvxDraw.h dvxWm.h dvxVideo.h dvxWidget.h platform/dvxPlatform.h
$(OBJDIR)/dvxVideo.o: dvxVideo.c dvxVideo.h platform/dvxPlatform.h dvxTypes.h dvxPalette.h
$(OBJDIR)/dvxDraw.o: dvxDraw.c dvxDraw.h platform/dvxPlatform.h dvxTypes.h
$(OBJDIR)/dvxComp.o: dvxComp.c dvxComp.h platform/dvxPlatform.h dvxTypes.h
$(OBJDIR)/dvxWm.o: dvxWm.c dvxWm.h dvxTypes.h dvxDraw.h dvxComp.h dvxVideo.h dvxWidget.h thirdparty/stb_image.h
$(OBJDIR)/dvxIcon.o: dvxIcon.c thirdparty/stb_image.h
$(OBJDIR)/dvxImageWrite.o: dvxImageWrite.c thirdparty/stb_image_write.h
$(OBJDIR)/dvxApp.o: dvxApp.c dvxApp.h platform/dvxPlatform.h dvxTypes.h dvxVideo.h dvxDraw.h dvxComp.h dvxWm.h dvxFont.h dvxCursor.h
$(OBJDIR)/dvxDialog.o: dvxDialog.c dvxDialog.h platform/dvxPlatform.h dvxApp.h dvxWidget.h widgetInternal.h dvxTypes.h dvxDraw.h
$(OBJDIR)/dvxPrefs.o: dvxPrefs.c dvxPrefs.h
WIDGET_DEPS = widgetInternal.h dvxWidget.h dvxTypes.h dvxApp.h dvxDraw.h dvxWm.h dvxVideo.h
$(OBJDIR)/widgetClass.o: widgetClass.c $(WIDGET_DEPS)
$(OBJDIR)/widgetCore.o: widgetCore.c $(WIDGET_DEPS)
$(OBJDIR)/widgetScrollbar.o: widgetScrollbar.c $(WIDGET_DEPS)
$(OBJDIR)/widgetLayout.o: widgetLayout.c $(WIDGET_DEPS)
$(OBJDIR)/widgetEvent.o: widgetEvent.c $(WIDGET_DEPS)
$(OBJDIR)/widgetOps.o: widgetOps.c $(WIDGET_DEPS)
clean:
rm -f $(OBJS) $(TARGET) $(LIBSDIR)/libdvx.dep

View file

@ -31,11 +31,11 @@
#include "dvxApp.h"
#include "dvxDialog.h"
#include "dvxWidget.h"
#include "widgets/widgetInternal.h"
#include "widgetInternal.h"
#include "dvxFont.h"
#include "dvxCursor.h"
#include "platform/dvxPlatform.h"
#include "dvxPlatform.h"
#include "thirdparty/stb_ds.h"
#include <string.h>
@ -726,14 +726,18 @@ static bool dispatchAccelKey(AppContextT *ctx, char key) {
case WidgetCheckboxE:
if (sFocusedWidget) { sFocusedWidget->focused = false; }
widgetCheckboxOnMouse(target, win->widgetRoot, 0, 0);
if (target->wclass && target->wclass->onMouse) {
target->wclass->onMouse(target, win->widgetRoot, 0, 0);
}
sFocusedWidget = target;
wgtInvalidate(target);
return true;
case WidgetRadioE:
if (sFocusedWidget) { sFocusedWidget->focused = false; }
widgetRadioOnMouse(target, win->widgetRoot, 0, 0);
if (target->wclass && target->wclass->onMouse) {
target->wclass->onMouse(target, win->widgetRoot, 0, 0);
}
sFocusedWidget = target;
wgtInvalidate(target);
return true;
@ -4628,7 +4632,7 @@ static void updateCursorShape(AppContextT *ctx) {
WidgetT *hit = widgetHitTest(win->widgetRoot, vx, vy);
if (hit && hit->type == WidgetListViewE && widgetListViewColBorderHit(hit, vx, vy)) {
if (hit && hit->type == WidgetListViewE && sListViewColBorderHitFn && sListViewColBorderHitFn(hit, vx, vy)) {
newCursor = CURSOR_RESIZE_H;
}

View file

@ -18,7 +18,7 @@
// exactly once per pixel per frame.
#include "dvxComp.h"
#include "platform/dvxPlatform.h"
#include "dvxPlatform.h"
#include <string.h>

View file

@ -21,9 +21,9 @@
// context pointers through every widget callback.
#include "dvxDialog.h"
#include "platform/dvxPlatform.h"
#include "dvxPlatform.h"
#include "dvxWidget.h"
#include "widgets/widgetInternal.h"
#include "widgetInternal.h"
#include <ctype.h>
#include <dirent.h>

View file

@ -50,7 +50,7 @@
// unlikely, helping the branch predictor on Pentium and later.
#include "dvxDraw.h"
#include "platform/dvxPlatform.h"
#include "dvxPlatform.h"
#include <string.h>

View file

@ -42,7 +42,7 @@
// shuffling.
#include "dvxVideo.h"
#include "platform/dvxPlatform.h"
#include "dvxPlatform.h"
#include "dvxPalette.h"
#include <string.h>

View file

@ -67,10 +67,10 @@ struct WidgetClassT;
// Widget type enum
// ============================================================
//
// Used as the index into widgetClassTable[] (in widgetInternal.h) to
// look up the vtable for each widget type. Adding a new widget type
// requires adding an enum value here, a corresponding union member in
// WidgetT, and a WidgetClassT entry in widgetClassTable[].
// Used as the index into widgetClassTable[] to look up the vtable for
// each widget type. Built-in types occupy slots 0..WGT_TYPE_BUILTIN_COUNT-1.
// Dynamic types registered via wgtRegisterClass() start at
// WGT_TYPE_DYNAMIC_BASE.
typedef enum {
WidgetVBoxE,
@ -104,9 +104,15 @@ typedef enum {
WidgetSpinnerE,
WidgetScrollPaneE,
WidgetSplitterE,
WidgetTimerE
WidgetTimerE,
WGT_TYPE_BUILTIN_COUNT // must be last built-in entry
} WidgetTypeE;
// Dynamic widget type IDs start here. External DXEs register new
// widget types via wgtRegisterClass() and receive IDs >= this value.
#define WGT_TYPE_DYNAMIC_BASE 32
#define WGT_MAX_TYPES 64
// ============================================================
// ListView types
// ============================================================
@ -272,6 +278,41 @@ typedef struct {
void (*onHeaderClick)(struct WidgetT *w, int32_t col, ListViewSortE dir);
} ListViewDataT;
// ============================================================
// Widget class vtable
// ============================================================
//
// Each widget type has a WidgetClassT that defines its behavior.
// Built-in classes are static const; dynamically registered classes
// (from DXE plugins) are stored in the mutable region of
// widgetClassTable[].
//
// Flags encode static properties checked by the framework:
// FOCUSABLE -- can receive keyboard focus (Tab navigation)
// BOX_CONTAINER -- uses the generic VBox/HBox layout algorithm
// HORIZ_CONTAINER -- lays out children horizontally (vs. vertical)
// PAINTS_CHILDREN -- widget handles child rendering itself
// NO_HIT_RECURSE -- hit testing stops here, no child recursion
#define WCLASS_FOCUSABLE 0x0001
#define WCLASS_BOX_CONTAINER 0x0002
#define WCLASS_HORIZ_CONTAINER 0x0004
#define WCLASS_PAINTS_CHILDREN 0x0008
#define WCLASS_NO_HIT_RECURSE 0x0010
typedef struct WidgetClassT {
uint32_t flags;
void (*paint)(struct WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors);
void (*paintOverlay)(struct WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors);
void (*calcMinSize)(struct WidgetT *w, const BitmapFontT *font);
void (*layout)(struct WidgetT *w, const BitmapFontT *font);
void (*onMouse)(struct WidgetT *w, struct WidgetT *root, int32_t vx, int32_t vy);
void (*onKey)(struct WidgetT *w, int32_t key, int32_t mod);
void (*destroy)(struct WidgetT *w);
const char *(*getText)(const struct WidgetT *w);
void (*setText)(struct WidgetT *w, const char *text);
} WidgetClassT;
// ============================================================
// Widget structure
// ============================================================
@ -348,6 +389,7 @@ typedef struct WidgetT {
// Type-specific handlers (e.g. button press animation, listbox
// selection) run first, then these universal callbacks fire.
void *userData;
void *customData; // DXE widget plugin private data (NULL for built-in types)
const char *tooltip; // tooltip text (NULL = none, caller owns string)
MenuT *contextMenu; // right-click context menu (NULL = none, caller owns)
void (*onClick)(struct WidgetT *w);
@ -987,6 +1029,16 @@ void wgtSetTooltip(WidgetT *w, const char *text);
// Draw borders around layout containers in ugly colors
void wgtSetDebugLayout(struct AppContextT *ctx, bool enabled);
// ============================================================
// Dynamic widget registration
// ============================================================
// Register a new widget class at runtime. Returns the assigned type ID
// (>= WGT_TYPE_DYNAMIC_BASE) on success, or -1 if the table is full.
// The WidgetClassT must remain valid for the lifetime of the process
// (typically a static const in the registering DXE).
int32_t wgtRegisterClass(const WidgetClassT *wclass);
// ============================================================
// Layout (called internally; available for manual trigger)
// ============================================================

View file

@ -22,7 +22,7 @@
#ifndef DVX_PLATFORM_H
#define DVX_PLATFORM_H
#include "../dvxTypes.h"
#include "dvxTypes.h"
// ============================================================
// Keyboard event
@ -231,4 +231,14 @@ const char *platformLineEnding(void);
// On DOS this removes '\r' from CR+LF pairs. Returns the new length.
int32_t platformStripLineEndings(char *buf, int32_t len);
// ============================================================
// DXE module support
// ============================================================
// Register platform and C runtime symbols with the dynamic module
// loader so that DXE modules can resolve them at load time. On DOS
// this calls dlregsym() with the full DJGPP libc/libm/libgcc/platform
// export table. On platforms without DXE (Linux/SDL), this is a no-op.
void platformRegisterDxeExports(void);
#endif // DVX_PLATFORM_H

View file

@ -27,17 +27,24 @@
// of installing a real-mode callback for mouse events.
#include "dvxPlatform.h"
#include "../dvxPalette.h"
#include "dvxPalette.h"
#include <ctype.h>
#include <dir.h>
#include <dirent.h>
#include <dlfcn.h>
#include <errno.h>
#include <math.h>
#include <setjmp.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/dxe.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
// DJGPP-specific headers -- this is the ONLY file that includes these
@ -1917,3 +1924,276 @@ static int32_t setVesaMode(uint16_t mode) {
return 0;
}
// ============================================================
// DXE3 export table
// ============================================================
//
// DJGPP's DXE3 modules have no implicit access to the host's symbol
// table. Every function or variable a DXE needs must be explicitly
// listed here and registered via dlregsym() before any dlopen().
//
// Three categories:
// 1. Platform functions (all platform*() from this file)
// 2. C runtime (libc, libm, libgcc, DJGPP internals)
// 3. DXE3 runtime (dlopen/dlsym/dlclose/dlerror/dlregsym)
// DJGPP stdio internals
extern FILE __dj_stdin;
extern FILE __dj_stdout;
extern FILE __dj_stderr;
// libgcc 64-bit integer math helpers
extern long long __divdi3(long long, long long);
extern long long __moddi3(long long, long long);
extern long long __muldi3(long long, long long);
extern unsigned long long __udivdi3(unsigned long long, unsigned long long);
extern unsigned long long __udivmoddi4(unsigned long long, unsigned long long, unsigned long long *);
extern unsigned long long __umoddi3(unsigned long long, unsigned long long);
// DJGPP runtime internals
extern void *__djgpp_exception_state_ptr;
extern void __dj_assert(const char *, const char *, int);
extern unsigned short __dj_ctype_flags[];
extern unsigned char __dj_ctype_tolower[];
// GCC emulated thread-local storage
extern void *__emutls_get_address(void *);
DXE_EXPORT_TABLE(sDxeExportTable)
// --- platform ---
DXE_EXPORT(platformAltScanToChar)
DXE_EXPORT(platformChdir)
DXE_EXPORT(platformFlushRect)
DXE_EXPORT(platformGetMemoryInfo)
DXE_EXPORT(platformGetSystemInfo)
DXE_EXPORT(platformInit)
DXE_EXPORT(platformKeyboardGetModifiers)
DXE_EXPORT(platformKeyboardRead)
DXE_EXPORT(platformLineEnding)
DXE_EXPORT(platformMkdirRecursive)
DXE_EXPORT(platformMouseInit)
DXE_EXPORT(platformMousePoll)
DXE_EXPORT(platformMouseSetAccel)
DXE_EXPORT(platformMouseWarp)
DXE_EXPORT(platformMouseWheelInit)
DXE_EXPORT(platformMouseWheelPoll)
DXE_EXPORT(platformPathDirEnd)
DXE_EXPORT(platformRegisterDxeExports)
DXE_EXPORT(platformSpanCopy8)
DXE_EXPORT(platformSpanCopy16)
DXE_EXPORT(platformSpanCopy32)
DXE_EXPORT(platformSpanFill8)
DXE_EXPORT(platformSpanFill16)
DXE_EXPORT(platformSpanFill32)
DXE_EXPORT(platformStripLineEndings)
DXE_EXPORT(platformValidateFilename)
DXE_EXPORT(platformVideoEnumModes)
DXE_EXPORT(platformVideoFreeBuffers)
DXE_EXPORT(platformVideoInit)
DXE_EXPORT(platformVideoSetPalette)
DXE_EXPORT(platformVideoShutdown)
DXE_EXPORT(platformYield)
// --- memory ---
DXE_EXPORT(calloc)
DXE_EXPORT(free)
DXE_EXPORT(malloc)
DXE_EXPORT(realloc)
// --- string / memory ops ---
DXE_EXPORT(memchr)
DXE_EXPORT(memcmp)
DXE_EXPORT(memcpy)
DXE_EXPORT(memmove)
DXE_EXPORT(memset)
DXE_EXPORT(strcasecmp)
DXE_EXPORT(strcat)
DXE_EXPORT(strchr)
DXE_EXPORT(strcmp)
DXE_EXPORT(strcpy)
DXE_EXPORT(strcspn)
DXE_EXPORT(strdup)
DXE_EXPORT(strerror)
DXE_EXPORT(stricmp)
DXE_EXPORT(strlen)
DXE_EXPORT(strncasecmp)
DXE_EXPORT(strncat)
DXE_EXPORT(strncmp)
DXE_EXPORT(strncpy)
DXE_EXPORT(strpbrk)
DXE_EXPORT(strrchr)
DXE_EXPORT(strspn)
DXE_EXPORT(strstr)
DXE_EXPORT(strtok)
// --- ctype ---
DXE_EXPORT(isalnum)
DXE_EXPORT(isalpha)
DXE_EXPORT(isdigit)
DXE_EXPORT(islower)
DXE_EXPORT(isprint)
DXE_EXPORT(ispunct)
DXE_EXPORT(isspace)
DXE_EXPORT(isupper)
DXE_EXPORT(isxdigit)
DXE_EXPORT(tolower)
DXE_EXPORT(toupper)
// --- conversion ---
DXE_EXPORT(abs)
DXE_EXPORT(atof)
DXE_EXPORT(atoi)
DXE_EXPORT(atol)
DXE_EXPORT(labs)
DXE_EXPORT(strtod)
DXE_EXPORT(strtol)
DXE_EXPORT(strtoul)
// --- formatted I/O ---
DXE_EXPORT(fprintf)
DXE_EXPORT(fputs)
DXE_EXPORT(fscanf)
DXE_EXPORT(printf)
DXE_EXPORT(puts)
DXE_EXPORT(snprintf)
DXE_EXPORT(sprintf)
DXE_EXPORT(sscanf)
DXE_EXPORT(vfprintf)
DXE_EXPORT(vprintf)
DXE_EXPORT(vsnprintf)
DXE_EXPORT(vsprintf)
// --- character I/O ---
DXE_EXPORT(fgetc)
DXE_EXPORT(fgets)
DXE_EXPORT(fputc)
DXE_EXPORT(getc)
DXE_EXPORT(putc)
DXE_EXPORT(putchar)
DXE_EXPORT(ungetc)
// --- file I/O ---
DXE_EXPORT(fclose)
DXE_EXPORT(feof)
DXE_EXPORT(ferror)
DXE_EXPORT(fflush)
DXE_EXPORT(fopen)
DXE_EXPORT(fread)
DXE_EXPORT(freopen)
DXE_EXPORT(fseek)
DXE_EXPORT(ftell)
DXE_EXPORT(fwrite)
DXE_EXPORT(remove)
DXE_EXPORT(rename)
DXE_EXPORT(rewind)
DXE_EXPORT(tmpfile)
DXE_EXPORT(tmpnam)
// --- directory ---
DXE_EXPORT(closedir)
DXE_EXPORT(mkdir)
DXE_EXPORT(opendir)
DXE_EXPORT(readdir)
DXE_EXPORT(rmdir)
// --- filesystem ---
DXE_EXPORT(access)
DXE_EXPORT(chdir)
DXE_EXPORT(getcwd)
DXE_EXPORT(realpath)
DXE_EXPORT(stat)
DXE_EXPORT(unlink)
// --- time ---
DXE_EXPORT(clock)
DXE_EXPORT(difftime)
DXE_EXPORT(gmtime)
DXE_EXPORT(localtime)
DXE_EXPORT(mktime)
DXE_EXPORT(strftime)
DXE_EXPORT(time)
// --- process / environment ---
DXE_EXPORT(abort)
DXE_EXPORT(atexit)
DXE_EXPORT(exit)
DXE_EXPORT(getenv)
DXE_EXPORT(system)
// --- sorting / searching ---
DXE_EXPORT(bsearch)
DXE_EXPORT(qsort)
// --- random ---
DXE_EXPORT(rand)
DXE_EXPORT(srand)
// --- setjmp / signal ---
DXE_EXPORT(longjmp)
DXE_EXPORT(setjmp)
DXE_EXPORT(signal)
// --- libm ---
DXE_EXPORT(acos)
DXE_EXPORT(asin)
DXE_EXPORT(atan)
DXE_EXPORT(atan2)
DXE_EXPORT(ceil)
DXE_EXPORT(cos)
DXE_EXPORT(exp)
DXE_EXPORT(fabs)
DXE_EXPORT(floor)
DXE_EXPORT(fmod)
DXE_EXPORT(frexp)
DXE_EXPORT(ldexp)
DXE_EXPORT(log)
DXE_EXPORT(log10)
DXE_EXPORT(modf)
DXE_EXPORT(pow)
DXE_EXPORT(sin)
DXE_EXPORT(sqrt)
DXE_EXPORT(tan)
// --- errno ---
DXE_EXPORT(errno)
// --- libgcc 64-bit integer math ---
DXE_EXPORT(__divdi3)
DXE_EXPORT(__moddi3)
DXE_EXPORT(__muldi3)
DXE_EXPORT(__udivdi3)
DXE_EXPORT(__udivmoddi4)
DXE_EXPORT(__umoddi3)
// --- DJGPP internals ---
DXE_EXPORT(__dj_stdin)
DXE_EXPORT(__dj_stdout)
DXE_EXPORT(__dj_stderr)
DXE_EXPORT(__dj_assert)
DXE_EXPORT(__dj_ctype_flags)
DXE_EXPORT(__dj_ctype_tolower)
DXE_EXPORT(__djgpp_exception_state_ptr)
// --- GCC internals ---
DXE_EXPORT(__emutls_get_address)
// --- DXE3 runtime ---
DXE_EXPORT(dlopen)
DXE_EXPORT(dlsym)
DXE_EXPORT(dlclose)
DXE_EXPORT(dlerror)
DXE_EXPORT(dlregsym)
DXE_EXPORT_END
// ============================================================
// platformRegisterDxeExports
// ============================================================
void platformRegisterDxeExports(void) {
dlregsym(sDxeExportTable);
}

49
core/widgetClass.c Normal file
View file

@ -0,0 +1,49 @@
// widgetClass.c -- Widget class table and dynamic registration
//
// The widgetClassTable[] is the central dispatch table for the widget
// system. All entries start NULL and are filled at runtime by widget
// DXE registration functions (wgtBoxRegister, wgtButtonRegister, etc.).
// The loader loads each widget DXE and calls its register function
// before starting the shell.
//
// This file contains only the table and the dynamic registration API.
// All class definitions live in their respective widget .c files.
#include "widgetInternal.h"
// ============================================================
// Class table -- indexed by WidgetTypeE
// ============================================================
//
// Every entry is NULL until the corresponding widget DXE calls its
// registration function (e.g. wgtBoxRegister sets VBox/HBox/Frame).
// Sized WGT_MAX_TYPES to accommodate both built-in widget types
// (0..WGT_TYPE_BUILTIN_COUNT-1) and dynamically registered types
// (WGT_TYPE_DYNAMIC_BASE..WGT_MAX_TYPES-1).
const WidgetClassT *widgetClassTable[WGT_MAX_TYPES];
// ============================================================
// wgtRegisterClass -- register a dynamic widget type at runtime
// ============================================================
//
// External DXE plugins call this to add new widget types without
// modifying the core library. Returns the assigned type ID
// (>= WGT_TYPE_DYNAMIC_BASE) so the caller can use it with
// widgetAlloc(). Returns -1 if the table is full.
int32_t wgtRegisterClass(const WidgetClassT *wclass) {
if (!wclass) {
return -1;
}
for (int32_t i = WGT_TYPE_DYNAMIC_BASE; i < WGT_MAX_TYPES; i++) {
if (!widgetClassTable[i]) {
widgetClassTable[i] = wclass;
return i;
}
}
return -1;
}

1805
core/widgetCore.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -258,7 +258,9 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
// Handle canvas drawing (mouse move while pressed)
if (sDrawingCanvas && (buttons & MOUSE_LEFT)) {
widgetCanvasOnMouse(sDrawingCanvas, root, x, y);
if (sDrawingCanvas->wclass && sDrawingCanvas->wclass->onMouse) {
sDrawingCanvas->wclass->onMouse(sDrawingCanvas, root, x, y);
}
wgtInvalidatePaint(root);
return;
}
@ -387,7 +389,9 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
pos = y - sDragSplitter->y - sDragSplitStart;
}
widgetSplitterClampPos(sDragSplitter, &pos);
if (sSplitterClampPosFn) {
sSplitterClampPosFn(sDragSplitter, &pos);
}
if (pos != sDragSplitter->as.splitter.dividerPos) {
sDragSplitter->as.splitter.dividerPos = pos;
@ -1088,7 +1092,7 @@ void widgetReorderUpdate(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
WidgetT *target = NULL;
bool after = false;
for (WidgetT *v = first; v; v = widgetTreeViewNextVisible(v, w)) {
for (WidgetT *v = first; v; v = sTreeViewNextVisibleFn ? sTreeViewNextVisibleFn(v, w) : NULL) {
int32_t itemBot = curY + font->charHeight;
int32_t mid = curY + font->charHeight / 2;
@ -1101,7 +1105,7 @@ void widgetReorderUpdate(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
curY = itemBot;
// Check if mouse is between this item and next
WidgetT *next = widgetTreeViewNextVisible(v, w);
WidgetT *next = sTreeViewNextVisibleFn ? sTreeViewNextVisibleFn(v, w) : NULL;
if (!next || y < itemBot) {
target = v;

View file

@ -14,61 +14,28 @@
#ifndef WIDGET_INTERNAL_H
#define WIDGET_INTERNAL_H
#include "../dvxWidget.h"
#include "../dvxApp.h"
#include "../dvxDraw.h"
#include "../dvxWm.h"
#include "../dvxVideo.h"
#include "dvxWidget.h"
#include "dvxApp.h"
#include "dvxDraw.h"
#include "dvxWm.h"
#include "dvxVideo.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
// ============================================================
// Widget class vtable
// Widget class table
// ============================================================
//
// Each widget type has a WidgetClassT entry in widgetClassTable[], indexed
// by WidgetTypeE. This is a C implementation of virtual dispatch: instead
// of a switch(type) in every operation, the code calls w->wclass->paint()
// etc. The vtable pointer is set once at widget creation and never changes.
// WidgetClassT and WCLASS_* flags are now in dvxWidget.h (public API)
// so that DXE plugins can register new widget types.
//
// Flags encode static properties that the framework needs without calling
// into the vtable:
// FOCUSABLE -- can receive keyboard focus (Tab navigation)
// BOX_CONTAINER -- uses the generic VBox/HBox layout algorithm
// HORIZ_CONTAINER -- lays out children horizontally (vs. default vertical)
// PAINTS_CHILDREN -- the widget's paint function handles child rendering
// (e.g. TabControl only paints the active tab page)
// NO_HIT_RECURSE -- hit testing stops at this widget and doesn't recurse
// into children (e.g. ListBox handles its own items)
//
// NULL function pointers are valid and mean "no-op" for that operation.
// paintOverlay is used by widgets that need to draw outside their bounds
// (dropdown popup lists), rendered in a separate pass after all widgets.
#define WCLASS_FOCUSABLE 0x0001
#define WCLASS_BOX_CONTAINER 0x0002
#define WCLASS_HORIZ_CONTAINER 0x0004
#define WCLASS_PAINTS_CHILDREN 0x0008
#define WCLASS_NO_HIT_RECURSE 0x0010
typedef struct WidgetClassT {
uint32_t flags;
void (*paint)(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors);
void (*paintOverlay)(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors);
void (*calcMinSize)(WidgetT *w, const BitmapFontT *font);
void (*layout)(WidgetT *w, const BitmapFontT *font);
void (*onMouse)(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy);
void (*onKey)(WidgetT *w, int32_t key, int32_t mod);
void (*destroy)(WidgetT *w);
const char *(*getText)(const WidgetT *w);
void (*setText)(WidgetT *w, const char *text);
} WidgetClassT;
// Global vtable array -- one entry per WidgetTypeE value. Defined in
// widgetCore.c. Must stay in sync with the WidgetTypeE enum order.
extern const WidgetClassT *widgetClassTable[];
// Global vtable array -- sized WGT_MAX_TYPES. Built-in entries
// (0..WGT_TYPE_BUILTIN_COUNT-1) are initialized at startup. Dynamic
// entries (WGT_TYPE_DYNAMIC_BASE..WGT_MAX_TYPES-1) are filled by
// wgtRegisterClass(). Defined in widgetClass.c.
extern const WidgetClassT *widgetClassTable[WGT_MAX_TYPES];
// ============================================================
// Validation macros
@ -194,6 +161,15 @@ extern WidgetT *sDragSplitter; // splitter being dragged
extern int32_t sDragSplitStart; // mouse position at start of splitter drag
extern WidgetT *sDragReorder; // listbox/treeview item being drag-reordered
extern WidgetT *sDragScrollbar; // widget whose scrollbar thumb is being dragged
// Hook for cursor shape change over ListView column borders.
// Set by wgtListViewRegister(); NULL if ListView DXE not loaded.
extern bool (*sListViewColBorderHitFn)(const WidgetT *w, int32_t vx, int32_t vy);
// Hook for splitter divider clamping during drag.
// Set by wgtSplitterRegister(); NULL if Splitter DXE not loaded.
extern void (*sSplitterClampPosFn)(WidgetT *w, int32_t *pos);
// Hook for TreeView visible item iteration during drag-reorder.
// Set by wgtTreeViewRegister(); NULL if TreeView DXE not loaded.
extern WidgetT *(*sTreeViewNextVisibleFn)(WidgetT *item, WidgetT *treeView);
extern int32_t sDragScrollbarOff; // mouse offset within thumb at drag start
extern int32_t sDragScrollbarOrient; // 0=vertical, 1=horizontal

View file

@ -1,140 +0,0 @@
# DVX GUI Library Makefile for DJGPP cross-compilation
DJGPP_PREFIX = $(HOME)/djgpp/djgpp
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
DJGPP_LIBPATH = $(HOME)/claude/windriver/tools/lib
AR = LD_LIBRARY_PATH=$(DJGPP_LIBPATH) $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-ar
RANLIB = LD_LIBRARY_PATH=$(DJGPP_LIBPATH) $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-ranlib
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../tasks
OBJDIR = ../obj/dvx
WOBJDIR = ../obj/dvx/widgets
POBJDIR = ../obj/dvx/platform
LIBDIR = ../lib
SRCS = dvxVideo.c dvxDraw.c dvxComp.c dvxWm.c dvxIcon.c dvxImageWrite.c dvxApp.c dvxDialog.c dvxPrefs.c
PSRCS = platform/dvxPlatformDos.c
WSRCS = widgets/widgetAnsiTerm.c \
widgets/widgetClass.c \
widgets/widgetCore.c \
widgets/widgetScrollbar.c \
widgets/widgetLayout.c \
widgets/widgetEvent.c \
widgets/widgetOps.c \
widgets/widgetBox.c \
widgets/widgetButton.c \
widgets/widgetCheckbox.c \
widgets/widgetComboBox.c \
widgets/widgetDropdown.c \
widgets/widgetCanvas.c \
widgets/widgetImage.c \
widgets/widgetImageButton.c \
widgets/widgetLabel.c \
widgets/widgetListBox.c \
widgets/widgetListView.c \
widgets/widgetProgressBar.c \
widgets/widgetRadio.c \
widgets/widgetScrollPane.c \
widgets/widgetSeparator.c \
widgets/widgetSplitter.c \
widgets/widgetSlider.c \
widgets/widgetSpacer.c \
widgets/widgetSpinner.c \
widgets/widgetStatusBar.c \
widgets/widgetTabControl.c \
widgets/widgetTextInput.c \
widgets/widgetTimer.c \
widgets/widgetToolbar.c \
widgets/widgetTreeView.c
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
POBJS = $(patsubst platform/%.c,$(POBJDIR)/%.o,$(PSRCS))
WOBJS = $(patsubst widgets/%.c,$(WOBJDIR)/%.o,$(WSRCS))
TARGET = $(LIBDIR)/libdvx.a
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJS) $(POBJS) $(WOBJS) | $(LIBDIR)
$(AR) rcs $@ $(OBJS) $(POBJS) $(WOBJS)
$(RANLIB) $@
$(OBJDIR)/%.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS) -c -o $@ $<
$(POBJDIR)/%.o: platform/%.c | $(POBJDIR)
$(CC) $(CFLAGS) -c -o $@ $<
$(WOBJDIR)/%.o: widgets/%.c | $(WOBJDIR)
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJDIR):
mkdir -p $(OBJDIR)
$(POBJDIR):
mkdir -p $(POBJDIR)
$(WOBJDIR):
mkdir -p $(WOBJDIR)
$(LIBDIR):
mkdir -p $(LIBDIR)
# Dependencies
$(OBJDIR)/dvxVideo.o: dvxVideo.c dvxVideo.h platform/dvxPlatform.h dvxTypes.h dvxPalette.h
$(OBJDIR)/dvxDraw.o: dvxDraw.c dvxDraw.h platform/dvxPlatform.h dvxTypes.h
$(OBJDIR)/dvxComp.o: dvxComp.c dvxComp.h platform/dvxPlatform.h dvxTypes.h
$(OBJDIR)/dvxWm.o: dvxWm.c dvxWm.h dvxTypes.h dvxDraw.h dvxComp.h dvxVideo.h dvxWidget.h thirdparty/stb_image.h
$(OBJDIR)/dvxIcon.o: dvxIcon.c thirdparty/stb_image.h
$(OBJDIR)/dvxImageWrite.o: dvxImageWrite.c thirdparty/stb_image_write.h
$(OBJDIR)/dvxApp.o: dvxApp.c dvxApp.h platform/dvxPlatform.h dvxTypes.h dvxVideo.h dvxDraw.h dvxComp.h dvxWm.h dvxFont.h dvxCursor.h
$(OBJDIR)/dvxDialog.o: dvxDialog.c dvxDialog.h platform/dvxPlatform.h dvxApp.h dvxWidget.h widgets/widgetInternal.h dvxTypes.h dvxDraw.h
$(OBJDIR)/dvxPrefs.o: dvxPrefs.c dvxPrefs.h
# Platform file dependencies
$(POBJDIR)/dvxPlatformDos.o: platform/dvxPlatformDos.c platform/dvxPlatform.h dvxTypes.h dvxPalette.h
# Thirdparty file dependencies
# Widget file dependencies
WIDGET_DEPS = widgets/widgetInternal.h dvxWidget.h dvxTypes.h dvxApp.h dvxDraw.h dvxWm.h dvxVideo.h
$(WOBJDIR)/widgetClass.o: widgets/widgetClass.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetAnsiTerm.o: widgets/widgetAnsiTerm.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetCore.o: widgets/widgetCore.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetLayout.o: widgets/widgetLayout.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetEvent.o: widgets/widgetEvent.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetOps.o: widgets/widgetOps.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetBox.o: widgets/widgetBox.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetCanvas.o: widgets/widgetCanvas.c $(WIDGET_DEPS) thirdparty/stb_image.h thirdparty/stb_image_write.h
$(WOBJDIR)/widgetButton.o: widgets/widgetButton.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetCheckbox.o: widgets/widgetCheckbox.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetComboBox.o: widgets/widgetComboBox.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetDropdown.o: widgets/widgetDropdown.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetImage.o: widgets/widgetImage.c $(WIDGET_DEPS) thirdparty/stb_image.h
$(WOBJDIR)/widgetImageButton.o: widgets/widgetImageButton.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetLabel.o: widgets/widgetLabel.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetListBox.o: widgets/widgetListBox.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetListView.o: widgets/widgetListView.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetProgressBar.o: widgets/widgetProgressBar.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetRadio.o: widgets/widgetRadio.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetScrollPane.o: widgets/widgetScrollPane.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetSeparator.o: widgets/widgetSeparator.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetSplitter.o: widgets/widgetSplitter.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetSlider.o: widgets/widgetSlider.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetSpacer.o: widgets/widgetSpacer.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetSpinner.o: widgets/widgetSpinner.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetStatusBar.o: widgets/widgetStatusBar.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetTabControl.o: widgets/widgetTabControl.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetTextInput.o: widgets/widgetTextInput.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetTimer.o: widgets/widgetTimer.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetToolbar.o: widgets/widgetToolbar.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetScrollbar.o: widgets/widgetScrollbar.c $(WIDGET_DEPS)
$(WOBJDIR)/widgetTreeView.o: widgets/widgetTreeView.c $(WIDGET_DEPS)
clean:
rm -f $(OBJS) $(POBJS) $(WOBJS) $(TARGET)

View file

@ -1,536 +0,0 @@
// widgetClass.c -- Widget class vtable definitions
//
// This file implements a C vtable pattern for the widget type system.
// Each widget type has a static const WidgetClassT struct that defines
// its behavior -- paint, layout, mouse handling, keyboard handling, etc.
// A master table (widgetClassTable[]) maps WidgetTypeE enum values to
// their class definitions, enabling O(1) dispatch.
//
// Why a vtable approach instead of switch statements:
// 1. Adding a new widget type is purely additive -- define a new class
// struct and add one entry to the table. No existing switch
// statements need modification, reducing the risk of forgetting
// a case.
// 2. NULL function pointers indicate "no behavior" naturally. A box
// container has no paint function because the framework paints
// its children directly. A label has no onKey handler because
// it's not interactive. This is cleaner than empty case blocks.
// 3. The flags field combines multiple boolean properties into a
// single bitmask, avoiding per-type if-chains in hot paths
// like hit testing and layout.
//
// Class flags:
// WCLASS_FOCUSABLE -- widget can receive keyboard focus (Tab order)
// WCLASS_BOX_CONTAINER -- uses the generic box layout engine (VBox/HBox)
// WCLASS_HORIZ_CONTAINER -- lays out children horizontally (HBox variant)
// WCLASS_PAINTS_CHILDREN -- widget handles its own child painting (TabControl,
// TreeView, ScrollPane, Splitter). The default paint
// walker skips children for these widgets.
// WCLASS_NO_HIT_RECURSE -- hit testing stops at this widget instead of
// recursing into children. Used by widgets that
// manage their own internal click regions (TreeView,
// ScrollPane, ListView, Splitter).
#include "widgetInternal.h"
// ============================================================
// Per-type class definitions
// ============================================================
//
// Containers (VBox, HBox, RadioGroup, TabPage, StatusBar, Toolbar)
// typically have NULL for most function pointers because their
// behavior is handled generically by the box layout engine and
// the default child-walking paint logic.
//
// Leaf widgets (Button, Label, TextInput, etc.) override paint,
// calcMinSize, and usually onMouse/onKey for interactivity.
static const WidgetClassT sClassVBox = {
.flags = WCLASS_BOX_CONTAINER,
.paint = NULL,
.paintOverlay = NULL,
.calcMinSize = NULL,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassHBox = {
.flags = WCLASS_BOX_CONTAINER | WCLASS_HORIZ_CONTAINER,
.paint = NULL,
.paintOverlay = NULL,
.calcMinSize = NULL,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassLabel = {
.flags = 0,
.paint = widgetLabelPaint,
.paintOverlay = NULL,
.calcMinSize = widgetLabelCalcMinSize,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = widgetLabelGetText,
.setText = widgetLabelSetText
};
static const WidgetClassT sClassButton = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetButtonPaint,
.paintOverlay = NULL,
.calcMinSize = widgetButtonCalcMinSize,
.layout = NULL,
.onMouse = widgetButtonOnMouse,
.onKey = widgetButtonOnKey,
.destroy = NULL,
.getText = widgetButtonGetText,
.setText = widgetButtonSetText
};
static const WidgetClassT sClassCheckbox = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetCheckboxPaint,
.paintOverlay = NULL,
.calcMinSize = widgetCheckboxCalcMinSize,
.layout = NULL,
.onMouse = widgetCheckboxOnMouse,
.onKey = widgetCheckboxOnKey,
.destroy = NULL,
.getText = widgetCheckboxGetText,
.setText = widgetCheckboxSetText
};
static const WidgetClassT sClassRadioGroup = {
.flags = WCLASS_BOX_CONTAINER,
.paint = NULL,
.paintOverlay = NULL,
.calcMinSize = NULL,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassRadio = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetRadioPaint,
.paintOverlay = NULL,
.calcMinSize = widgetRadioCalcMinSize,
.layout = NULL,
.onMouse = widgetRadioOnMouse,
.onKey = widgetRadioOnKey,
.destroy = NULL,
.getText = widgetRadioGetText,
.setText = widgetRadioSetText
};
// TextInput and TextArea have destroy functions because they dynamically
// allocate their text buffer and undo buffer. Most other widgets store
// all data inline in the WidgetT union and need no cleanup.
static const WidgetClassT sClassTextInput = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetTextInputPaint,
.paintOverlay = NULL,
.calcMinSize = widgetTextInputCalcMinSize,
.layout = NULL,
.onMouse = widgetTextInputOnMouse,
.onKey = widgetTextInputOnKey,
.destroy = widgetTextInputDestroy,
.getText = widgetTextInputGetText,
.setText = widgetTextInputSetText
};
static const WidgetClassT sClassTextArea = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetTextAreaPaint,
.paintOverlay = NULL,
.calcMinSize = widgetTextAreaCalcMinSize,
.layout = NULL,
.onMouse = widgetTextAreaOnMouse,
.onKey = widgetTextAreaOnKey,
.destroy = widgetTextAreaDestroy,
.getText = widgetTextAreaGetText,
.setText = widgetTextAreaSetText
};
static const WidgetClassT sClassListBox = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetListBoxPaint,
.paintOverlay = NULL,
.calcMinSize = widgetListBoxCalcMinSize,
.layout = NULL,
.onMouse = widgetListBoxOnMouse,
.onKey = widgetListBoxOnKey,
.destroy = widgetListBoxDestroy,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassSpacer = {
.flags = 0,
.paint = NULL,
.paintOverlay = NULL,
.calcMinSize = widgetSpacerCalcMinSize,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassSeparator = {
.flags = 0,
.paint = widgetSeparatorPaint,
.paintOverlay = NULL,
.calcMinSize = widgetSeparatorCalcMinSize,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassFrame = {
.flags = WCLASS_BOX_CONTAINER,
.paint = widgetFramePaint,
.paintOverlay = NULL,
.calcMinSize = NULL,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
// Dropdown and ComboBox use paintOverlay to draw their popup lists
// on top of the entire widget tree. This is rendered in a separate
// pass after the main paint, so the popup can overlap sibling widgets.
static const WidgetClassT sClassDropdown = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetDropdownPaint,
.paintOverlay = widgetDropdownPaintPopup,
.calcMinSize = widgetDropdownCalcMinSize,
.layout = NULL,
.onMouse = widgetDropdownOnMouse,
.onKey = widgetDropdownOnKey,
.destroy = NULL,
.getText = widgetDropdownGetText,
.setText = NULL
};
static const WidgetClassT sClassComboBox = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetComboBoxPaint,
.paintOverlay = widgetComboBoxPaintPopup,
.calcMinSize = widgetComboBoxCalcMinSize,
.layout = NULL,
.onMouse = widgetComboBoxOnMouse,
.onKey = widgetComboBoxOnKey,
.destroy = widgetComboBoxDestroy,
.getText = widgetComboBoxGetText,
.setText = widgetComboBoxSetText
};
static const WidgetClassT sClassProgressBar = {
.flags = 0,
.paint = widgetProgressBarPaint,
.paintOverlay = NULL,
.calcMinSize = widgetProgressBarCalcMinSize,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassSlider = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetSliderPaint,
.paintOverlay = NULL,
.calcMinSize = widgetSliderCalcMinSize,
.layout = NULL,
.onMouse = widgetSliderOnMouse,
.onKey = widgetSliderOnKey,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
// TabControl has both PAINTS_CHILDREN and a custom layout function
// because it needs to show only the active tab page's children and
// position them inside the tab content area (below the tab strip).
static const WidgetClassT sClassTabControl = {
.flags = WCLASS_FOCUSABLE | WCLASS_PAINTS_CHILDREN,
.paint = widgetTabControlPaint,
.paintOverlay = NULL,
.calcMinSize = widgetTabControlCalcMinSize,
.layout = widgetTabControlLayout,
.onMouse = widgetTabControlOnMouse,
.onKey = widgetTabControlOnKey,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassTabPage = {
.flags = WCLASS_BOX_CONTAINER,
.paint = NULL,
.paintOverlay = NULL,
.calcMinSize = NULL,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassStatusBar = {
.flags = WCLASS_BOX_CONTAINER | WCLASS_HORIZ_CONTAINER,
.paint = widgetStatusBarPaint,
.paintOverlay = NULL,
.calcMinSize = NULL,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassToolbar = {
.flags = WCLASS_BOX_CONTAINER | WCLASS_HORIZ_CONTAINER,
.paint = widgetToolbarPaint,
.paintOverlay = NULL,
.calcMinSize = NULL,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
// TreeView uses all three special flags:
// PAINTS_CHILDREN -- it renders tree items itself (with indentation,
// expand/collapse buttons, and selection highlighting)
// NO_HIT_RECURSE -- mouse clicks go to the TreeView widget, which
// figures out which tree item was clicked based on scroll position
// and item Y coordinates, rather than letting the hit tester
// recurse into child TreeItem widgets
static const WidgetClassT sClassTreeView = {
.flags = WCLASS_FOCUSABLE | WCLASS_PAINTS_CHILDREN | WCLASS_NO_HIT_RECURSE,
.paint = widgetTreeViewPaint,
.paintOverlay = NULL,
.calcMinSize = widgetTreeViewCalcMinSize,
.layout = widgetTreeViewLayout,
.onMouse = widgetTreeViewOnMouse,
.onKey = widgetTreeViewOnKey,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
// TreeItem has no paint/mouse/key handlers -- it's a data-only node.
// The TreeView parent widget handles all rendering and interaction.
// TreeItem exists as a WidgetT so it can participate in the tree
// structure (parent/child/sibling links) for hierarchical data.
static const WidgetClassT sClassTreeItem = {
.flags = 0,
.paint = NULL,
.paintOverlay = NULL,
.calcMinSize = NULL,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = widgetTreeItemGetText,
.setText = widgetTreeItemSetText
};
static const WidgetClassT sClassImage = {
.flags = 0,
.paint = widgetImagePaint,
.paintOverlay = NULL,
.calcMinSize = widgetImageCalcMinSize,
.layout = NULL,
.onMouse = widgetImageOnMouse,
.onKey = NULL,
.destroy = widgetImageDestroy,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassImageButton = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetImageButtonPaint,
.paintOverlay = NULL,
.calcMinSize = widgetImageButtonCalcMinSize,
.layout = NULL,
.onMouse = widgetImageButtonOnMouse,
.onKey = widgetImageButtonOnKey,
.destroy = widgetImageButtonDestroy,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassCanvas = {
.flags = 0,
.paint = widgetCanvasPaint,
.paintOverlay = NULL,
.calcMinSize = widgetCanvasCalcMinSize,
.layout = NULL,
.onMouse = widgetCanvasOnMouse,
.onKey = NULL,
.destroy = widgetCanvasDestroy,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassListView = {
.flags = WCLASS_FOCUSABLE | WCLASS_NO_HIT_RECURSE,
.paint = widgetListViewPaint,
.paintOverlay = NULL,
.calcMinSize = widgetListViewCalcMinSize,
.layout = NULL,
.onMouse = widgetListViewOnMouse,
.onKey = widgetListViewOnKey,
.destroy = widgetListViewDestroy,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassAnsiTerm = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetAnsiTermPaint,
.paintOverlay = NULL,
.calcMinSize = widgetAnsiTermCalcMinSize,
.layout = NULL,
.onMouse = widgetAnsiTermOnMouse,
.onKey = widgetAnsiTermOnKey,
.destroy = widgetAnsiTermDestroy,
.getText = NULL,
.setText = NULL
};
// ScrollPane, Splitter: PAINTS_CHILDREN because they clip/position
// children in a custom way; NO_HIT_RECURSE because they manage their
// own scrollbar/divider hit regions.
static const WidgetClassT sClassScrollPane = {
.flags = WCLASS_PAINTS_CHILDREN | WCLASS_NO_HIT_RECURSE,
.paint = widgetScrollPanePaint,
.paintOverlay = NULL,
.calcMinSize = widgetScrollPaneCalcMinSize,
.layout = widgetScrollPaneLayout,
.onMouse = widgetScrollPaneOnMouse,
.onKey = widgetScrollPaneOnKey,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassSplitter = {
.flags = WCLASS_PAINTS_CHILDREN | WCLASS_NO_HIT_RECURSE,
.paint = widgetSplitterPaint,
.paintOverlay = NULL,
.calcMinSize = widgetSplitterCalcMinSize,
.layout = widgetSplitterLayout,
.onMouse = widgetSplitterOnMouse,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassTimer = {
.flags = 0,
.paint = NULL,
.paintOverlay = NULL,
.calcMinSize = widgetTimerCalcMinSize,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = widgetTimerDestroy,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassSpinner = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetSpinnerPaint,
.paintOverlay = NULL,
.calcMinSize = widgetSpinnerCalcMinSize,
.layout = NULL,
.onMouse = widgetSpinnerOnMouse,
.onKey = widgetSpinnerOnKey,
.destroy = NULL,
.getText = widgetSpinnerGetText,
.setText = widgetSpinnerSetText
};
// ============================================================
// Class table -- indexed by WidgetTypeE
// ============================================================
//
// This array is the central dispatch table for the widget system.
// Indexed by the WidgetTypeE enum, it provides O(1) lookup of any
// widget type's class definition. Every WidgetT stores a pointer
// to its class (w->wclass) set at allocation time, so per-widget
// dispatch doesn't even need to index this table -- it's a direct
// pointer dereference through the vtable.
//
// Using C99 designated initializers ensures the array slots match
// the enum values even if the enum is reordered. If a new enum
// value is added without a table entry, it will be NULL, which
// callers check for before dispatching.
const WidgetClassT *widgetClassTable[] = {
[WidgetVBoxE] = &sClassVBox,
[WidgetHBoxE] = &sClassHBox,
[WidgetLabelE] = &sClassLabel,
[WidgetButtonE] = &sClassButton,
[WidgetCheckboxE] = &sClassCheckbox,
[WidgetRadioGroupE] = &sClassRadioGroup,
[WidgetRadioE] = &sClassRadio,
[WidgetTextInputE] = &sClassTextInput,
[WidgetTextAreaE] = &sClassTextArea,
[WidgetListBoxE] = &sClassListBox,
[WidgetSpacerE] = &sClassSpacer,
[WidgetSeparatorE] = &sClassSeparator,
[WidgetFrameE] = &sClassFrame,
[WidgetDropdownE] = &sClassDropdown,
[WidgetComboBoxE] = &sClassComboBox,
[WidgetProgressBarE] = &sClassProgressBar,
[WidgetSliderE] = &sClassSlider,
[WidgetTabControlE] = &sClassTabControl,
[WidgetTabPageE] = &sClassTabPage,
[WidgetStatusBarE] = &sClassStatusBar,
[WidgetToolbarE] = &sClassToolbar,
[WidgetTreeViewE] = &sClassTreeView,
[WidgetTreeItemE] = &sClassTreeItem,
[WidgetImageE] = &sClassImage,
[WidgetImageButtonE] = &sClassImageButton,
[WidgetCanvasE] = &sClassCanvas,
[WidgetAnsiTermE] = &sClassAnsiTerm,
[WidgetListViewE] = &sClassListView,
[WidgetSpinnerE] = &sClassSpinner,
[WidgetScrollPaneE] = &sClassScrollPane,
[WidgetSplitterE] = &sClassSplitter,
[WidgetTimerE] = &sClassTimer
};

View file

@ -1,746 +0,0 @@
// widgetCore.c -- Core widget infrastructure (alloc, tree ops, helpers)
//
// This file provides the foundation for the widget tree: allocation,
// parent-child linking, focus management, hit testing, and shared
// utility functions used across multiple widget types.
//
// Widgets form a tree using intrusive linked lists (firstChild/lastChild/
// nextSibling pointers inside each WidgetT). This is a singly-linked
// child list with a tail pointer for O(1) append. The tree is owned
// by its root, which is attached to a WindowT. Destroying the root
// recursively destroys all descendants.
//
// Memory allocation is plain malloc/free rather than an arena or pool.
// The widget count per window is typically small (tens to low hundreds),
// so the allocation overhead is negligible on target hardware. An arena
// approach was considered but rejected because widgets can be individually
// created and destroyed at runtime (dialog dynamics, tree item insertion),
// which doesn't map cleanly to an arena pattern.
#include "widgetInternal.h"
// ============================================================
// Global state for drag and popup tracking
// ============================================================
//
// These module-level pointers track ongoing UI interactions that span
// multiple mouse events (drags, popups, button presses). They are global
// rather than per-window because the DOS GUI is single-threaded and only
// one interaction can be active at a time.
//
// Each pointer is set when an interaction begins (e.g. mouse-down on a
// slider) and cleared when it ends (mouse-up). The event dispatcher in
// widgetEvent.c checks these before normal hit testing -- active drags
// take priority over everything else.
//
// All of these must be NULLed when the pointed-to widget is destroyed,
// otherwise dangling pointers would cause crashes. widgetDestroyChildren()
// and wgtDestroy() handle this cleanup.
clock_t sDblClickTicks = 0; // set from ctx->dblClickTicks during first paint
bool sDebugLayout = false;
WidgetT *sFocusedWidget = NULL; // currently focused widget (O(1) access, avoids tree walk)
WidgetT *sOpenPopup = NULL; // dropdown/combobox with open popup list
WidgetT *sPressedButton = NULL; // button being held down (tracks mouse in/out)
WidgetT *sDragSlider = NULL; // slider being dragged
WidgetT *sDrawingCanvas = NULL; // canvas receiving paint strokes
WidgetT *sDragTextSelect = NULL; // text widget in drag-select mode
int32_t sDragOffset = 0; // pixel offset from drag start to thumb center
WidgetT *sResizeListView = NULL; // ListView undergoing column resize
int32_t sResizeCol = -1; // which column is being resized
int32_t sResizeStartX = 0; // mouse X at resize start
int32_t sResizeOrigW = 0; // column width at resize start
bool sResizeDragging = false; // true once mouse moves during column resize
WidgetT *sDragSplitter = NULL; // splitter being dragged
int32_t sDragSplitStart = 0; // mouse offset from splitter edge at drag start
WidgetT *sDragReorder = NULL; // list/tree widget in drag-reorder mode
WidgetT *sDragScrollbar = NULL; // widget whose scrollbar thumb is being dragged
int32_t sDragScrollbarOff = 0; // mouse offset within thumb at drag start
int32_t sDragScrollbarOrient = 0; // 0=vertical, 1=horizontal
// ============================================================
// widgetAddChild
// ============================================================
//
// Appends a child to the end of the parent's child list. O(1)
// thanks to the lastChild tail pointer. The child list is singly-
// linked (nextSibling), which saves 4 bytes per widget vs doubly-
// linked and is sufficient because child removal is infrequent.
void widgetAddChild(WidgetT *parent, WidgetT *child) {
child->parent = parent;
child->nextSibling = NULL;
if (parent->lastChild) {
parent->lastChild->nextSibling = child;
parent->lastChild = child;
} else {
parent->firstChild = child;
parent->lastChild = child;
}
}
// ============================================================
// widgetAlloc
// ============================================================
//
// Allocates and zero-initializes a new widget, links it to its
// class vtable via widgetClassTable[], and optionally adds it as
// a child of the given parent.
//
// The memset to 0 is intentional -- it establishes sane defaults
// for all fields: NULL pointers, zero coordinates, no focus,
// no accel key, etc. Only visible and enabled default to true.
//
// The window pointer is inherited from the parent so that any
// widget in the tree can find its owning window without walking
// to the root.
WidgetT *widgetAlloc(WidgetT *parent, WidgetTypeE type) {
WidgetT *w = (WidgetT *)malloc(sizeof(WidgetT));
if (!w) {
return NULL;
}
memset(w, 0, sizeof(*w));
w->type = type;
w->wclass = widgetClassTable[type];
w->visible = true;
w->enabled = true;
if (parent) {
w->window = parent->window;
widgetAddChild(parent, w);
}
return w;
}
// ============================================================
// widgetClearFocus
// ============================================================
void widgetClearFocus(WidgetT *root) {
if (!root) {
return;
}
root->focused = false;
for (WidgetT *c = root->firstChild; c; c = c->nextSibling) {
widgetClearFocus(c);
}
}
// ============================================================
// widgetCountVisibleChildren
// ============================================================
int32_t widgetCountVisibleChildren(const WidgetT *w) {
int32_t count = 0;
for (const WidgetT *c = w->firstChild; c; c = c->nextSibling) {
if (c->visible) {
count++;
}
}
return count;
}
// ============================================================
// widgetDestroyChildren
// ============================================================
//
// Recursively destroys all descendants of a widget. Processes
// children depth-first (destroy grandchildren before the child
// itself) so that per-widget destroy callbacks see a consistent
// tree state.
//
// Critically, this function clears all global state pointers that
// reference destroyed widgets. Without this, any pending drag or
// focus state would become a dangling pointer. Each global is
// checked individually rather than cleared unconditionally to
// avoid disrupting unrelated ongoing interactions.
void widgetDestroyChildren(WidgetT *w) {
WidgetT *child = w->firstChild;
while (child) {
WidgetT *next = child->nextSibling;
widgetDestroyChildren(child);
if (child->wclass && child->wclass->destroy) {
child->wclass->destroy(child);
}
// Clear static references if they point to destroyed widgets
if (sFocusedWidget == child) {
sFocusedWidget = NULL;
}
if (sOpenPopup == child) {
sOpenPopup = NULL;
}
if (sPressedButton == child) {
sPressedButton = NULL;
}
if (sDragSlider == child) {
sDragSlider = NULL;
}
if (sDrawingCanvas == child) {
sDrawingCanvas = NULL;
}
if (sResizeListView == child) {
sResizeListView = NULL;
sResizeCol = -1;
sResizeDragging = false;
}
if (sDragScrollbar == child) {
sDragScrollbar = NULL;
}
free(child);
child = next;
}
w->firstChild = NULL;
w->lastChild = NULL;
}
// ============================================================
// widgetDropdownPopupRect
// ============================================================
//
// Calculates the screen rectangle for a dropdown/combobox popup list.
// Shared between Dropdown and ComboBox since they have identical
// popup positioning logic.
//
// The popup tries to open below the widget first. If there isn't
// enough room (popup would extend past the content area bottom),
// it flips to open above instead. This ensures the popup is always
// visible, even for dropdowns near the bottom of a window.
//
// Popup height is capped at DROPDOWN_MAX_VISIBLE items to prevent
// huge popups from dominating the screen.
void widgetDropdownPopupRect(WidgetT *w, const BitmapFontT *font, int32_t contentH, int32_t *popX, int32_t *popY, int32_t *popW, int32_t *popH) {
int32_t itemCount = 0;
if (w->type == WidgetDropdownE) {
itemCount = w->as.dropdown.itemCount;
} else if (w->type == WidgetComboBoxE) {
itemCount = w->as.comboBox.itemCount;
}
int32_t visibleItems = itemCount;
if (visibleItems > DROPDOWN_MAX_VISIBLE) {
visibleItems = DROPDOWN_MAX_VISIBLE;
}
if (visibleItems < 1) {
visibleItems = 1;
}
*popX = w->x;
*popW = w->w;
*popH = visibleItems * font->charHeight + 4; // 2px border each side
// Try below first, then above if no room
if (w->y + w->h + *popH <= contentH) {
*popY = w->y + w->h;
} else {
*popY = w->y - *popH;
if (*popY < 0) {
*popY = 0;
}
}
}
// ============================================================
// widgetDrawDropdownArrow
// ============================================================
//
// Draws a small downward-pointing filled triangle (7, 5, 3, 1 pixels
// wide across 4 rows) centered at the given position. Used by both
// Dropdown and ComboBox for the drop button arrow glyph.
void widgetDrawDropdownArrow(DisplayT *d, const BlitOpsT *ops, int32_t centerX, int32_t centerY, uint32_t color) {
for (int32_t i = 0; i < 4; i++) {
drawHLine(d, ops, centerX - 3 + i, centerY + i, 7 - i * 2, color);
}
}
// ============================================================
// widgetFindByAccel
// ============================================================
//
// Finds a widget with the given Alt+key accelerator. Recurses the
// tree depth-first, respecting visibility and enabled state.
//
// Special case for TabPage widgets: even if the tab page itself is
// not visible (inactive tab), its accelKey is still checked. This
// allows Alt+key to switch to a different tab. However, children
// of invisible tab pages are NOT searched -- their accelerators
// should not be active when the tab is hidden.
WidgetT *widgetFindByAccel(WidgetT *root, char key) {
if (!root || !root->enabled) {
return NULL;
}
// Invisible tab pages: match the page itself (for tab switching)
// but don't recurse into children (their accels shouldn't be active)
if (!root->visible) {
if (root->type == WidgetTabPageE && root->accelKey == key) {
return root;
}
return NULL;
}
if (root->accelKey == key) {
return root;
}
for (WidgetT *c = root->firstChild; c; c = c->nextSibling) {
WidgetT *found = widgetFindByAccel(c, key);
if (found) {
return found;
}
}
return NULL;
}
// ============================================================
// widgetFindNextFocusable
// ============================================================
//
// Implements Tab-order navigation: finds the next focusable widget
// after 'after' in depth-first tree order. The two-pass approach
// (search from 'after' to end, then wrap to start) ensures circular
// tabbing -- Tab on the last focusable widget wraps to the first.
//
// The pastAfter flag tracks whether we've passed the 'after' widget
// during traversal. Once past it, the next focusable widget is the
// answer. This avoids collecting all focusable widgets into an array
// just to find the next one -- the common case returns quickly.
static WidgetT *findNextFocusableImpl(WidgetT *w, WidgetT *after, bool *pastAfter) {
if (!w->visible || !w->enabled) {
return NULL;
}
if (after == NULL) {
*pastAfter = true;
}
if (w == after) {
*pastAfter = true;
} else if (*pastAfter && widgetIsFocusable(w->type)) {
return w;
}
for (WidgetT *c = w->firstChild; c; c = c->nextSibling) {
WidgetT *found = findNextFocusableImpl(c, after, pastAfter);
if (found) {
return found;
}
}
return NULL;
}
WidgetT *widgetFindNextFocusable(WidgetT *root, WidgetT *after) {
bool pastAfter = false;
WidgetT *found = findNextFocusableImpl(root, after, &pastAfter);
if (found) {
return found;
}
// Wrap around -- search from the beginning
pastAfter = true;
return findNextFocusableImpl(root, NULL, &pastAfter);
}
// ============================================================
// widgetFindPrevFocusable
// ============================================================
//
// Shift+Tab navigation: finds the previous focusable widget.
// Unlike findNextFocusable which can short-circuit during traversal,
// finding the PREVIOUS widget requires knowing the full order.
// So this collects all focusable widgets into an array, finds the
// target's index, and returns index-1 (with wraparound).
//
// The explicit stack-based DFS (rather than recursion) is used here
// because we need to push children in reverse order to get the same
// left-to-right depth-first ordering as the recursive version.
// Fixed-size arrays (128 widgets, 64 stack depth) are adequate for
// any reasonable dialog layout and avoid dynamic allocation.
WidgetT *widgetFindPrevFocusable(WidgetT *root, WidgetT *before) {
WidgetT *list[128];
int32_t count = 0;
// Collect all focusable widgets via depth-first traversal
WidgetT *stack[64];
int32_t top = 0;
stack[top++] = root;
while (top > 0) {
WidgetT *w = stack[--top];
if (!w->visible || !w->enabled) {
continue;
}
if (widgetIsFocusable(w->type) && count < 128) {
list[count++] = w;
}
// Push children in reverse order so first child is processed first
WidgetT *children[64];
int32_t childCount = 0;
for (WidgetT *c = w->firstChild; c; c = c->nextSibling) {
if (childCount < 64) {
children[childCount++] = c;
}
}
for (int32_t i = childCount - 1; i >= 0; i--) {
if (top < 64) {
stack[top++] = children[i];
}
}
}
if (count == 0) {
return NULL;
}
// Find 'before' in the list
int32_t idx = -1;
for (int32_t i = 0; i < count; i++) {
if (list[i] == before) {
idx = i;
break;
}
}
if (idx <= 0) {
return list[count - 1]; // Wrap to last
}
return list[idx - 1];
}
// ============================================================
// widgetFrameBorderWidth
// ============================================================
int32_t widgetFrameBorderWidth(const WidgetT *w) {
if (w->type != WidgetFrameE) {
return 0;
}
if (w->as.frame.style == FrameFlatE) {
return FRAME_FLAT_BORDER;
}
return FRAME_BEVEL_BORDER;
}
// ============================================================
// widgetHitTest
// ============================================================
//
// Recursive hit testing: finds the deepest (most specific) widget
// under the given coordinates. Returns the widget itself if no
// child is hit, or NULL if the point is outside this widget.
//
// Children are iterated front-to-back (first to last in the linked
// list), but the LAST match wins. This gives later siblings higher
// Z-order, which matches the painting order (later children paint
// on top of earlier ones). This is important for overlapping widgets,
// though in practice the layout engine rarely produces overlap.
//
// Widgets with WCLASS_NO_HIT_RECURSE stop the recursion -- the parent
// widget handles all mouse events for its children. This is used by
// TreeView, ScrollPane, ListView, and Splitter, which need to manage
// their own internal regions (scrollbars, column headers, tree
// expand buttons) that don't correspond to child widgets.
WidgetT *widgetHitTest(WidgetT *w, int32_t x, int32_t y) {
if (!w->visible) {
return NULL;
}
if (x < w->x || x >= w->x + w->w || y < w->y || y >= w->y + w->h) {
return NULL;
}
// Widgets with WCLASS_NO_HIT_RECURSE manage their own children
if (w->wclass && (w->wclass->flags & WCLASS_NO_HIT_RECURSE)) {
return w;
}
// Check children -- take the last match (topmost in Z-order)
WidgetT *hit = NULL;
for (WidgetT *c = w->firstChild; c; c = c->nextSibling) {
WidgetT *childHit = widgetHitTest(c, x, y);
if (childHit) {
hit = childHit;
}
}
return hit ? hit : w;
}
// ============================================================
// widgetIsFocusable
// ============================================================
bool widgetIsFocusable(WidgetTypeE type) {
return (widgetClassTable[type]->flags & WCLASS_FOCUSABLE) != 0;
}
// ============================================================
// widgetIsBoxContainer
// ============================================================
//
// Returns true for widget types that use the generic box layout.
bool widgetIsBoxContainer(WidgetTypeE type) {
return (widgetClassTable[type]->flags & WCLASS_BOX_CONTAINER) != 0;
}
// ============================================================
// widgetIsHorizContainer
// ============================================================
//
// Returns true for container types that lay out children horizontally.
bool widgetIsHorizContainer(WidgetTypeE type) {
return (widgetClassTable[type]->flags & WCLASS_HORIZ_CONTAINER) != 0;
}
// ============================================================
// widgetMaxItemLen
// ============================================================
//
// Scans an array of string items and returns the maximum strlen.
// Shared by ListBox, Dropdown, and ComboBox to cache the widest
// item length for calcMinSize without duplicating the loop.
int32_t widgetMaxItemLen(const char **items, int32_t count) {
int32_t maxLen = 0;
for (int32_t i = 0; i < count; i++) {
int32_t slen = (int32_t)strlen(items[i]);
if (slen > maxLen) {
maxLen = slen;
}
}
return maxLen;
}
// ============================================================
// widgetNavigateIndex
// ============================================================
//
// Shared keyboard navigation for list-like widgets (ListBox, Dropdown,
// ListView, etc.). Encapsulates the Up/Down/Home/End/PgUp/PgDn logic
// so each widget doesn't have to reimplement index clamping.
//
// Key values use the 0x100 flag to mark extended scan codes (arrow
// keys, Home, End, etc.) -- this is the DVX convention for passing
// scan codes through the same int32_t channel as ASCII values.
//
// Returns -1 for unrecognized keys so callers can check whether the
// key was consumed.
int32_t widgetNavigateIndex(int32_t key, int32_t current, int32_t count, int32_t pageSize) {
if (key == (0x50 | 0x100)) {
// Down arrow
if (current < count - 1) {
return current + 1;
}
return current < 0 ? 0 : current;
}
if (key == (0x48 | 0x100)) {
// Up arrow
if (current > 0) {
return current - 1;
}
return current < 0 ? 0 : current;
}
if (key == (0x47 | 0x100)) {
// Home
return 0;
}
if (key == (0x4F | 0x100)) {
// End
return count - 1;
}
if (key == (0x51 | 0x100)) {
// Page Down
int32_t n = current + pageSize;
return n >= count ? count - 1 : n;
}
if (key == (0x49 | 0x100)) {
// Page Up
int32_t n = current - pageSize;
return n < 0 ? 0 : n;
}
return -1;
}
// ============================================================
// widgetPaintPopupList
// ============================================================
//
// Shared popup list painting for Dropdown and ComboBox.
void widgetPaintPopupList(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, int32_t popX, int32_t popY, int32_t popW, int32_t popH, const char **items, int32_t itemCount, int32_t hoverIdx, int32_t scrollPos) {
// Draw popup border
BevelStyleT bevel;
bevel.highlight = colors->windowHighlight;
bevel.shadow = colors->windowShadow;
bevel.face = colors->contentBg;
bevel.width = 2;
drawBevel(d, ops, popX, popY, popW, popH, &bevel);
// Draw items
int32_t visibleItems = popH / font->charHeight;
int32_t textX = popX + TEXT_INPUT_PAD;
int32_t textY = popY + 2;
int32_t textW = popW - TEXT_INPUT_PAD * 2 - 4;
for (int32_t i = 0; i < visibleItems && (scrollPos + i) < itemCount; i++) {
int32_t idx = scrollPos + i;
int32_t iy = textY + i * font->charHeight;
uint32_t ifg = colors->contentFg;
uint32_t ibg = colors->contentBg;
if (idx == hoverIdx) {
ifg = colors->menuHighlightFg;
ibg = colors->menuHighlightBg;
rectFill(d, ops, popX + 2, iy, textW + TEXT_INPUT_PAD * 2, font->charHeight, ibg);
}
drawText(d, ops, font, textX, iy, items[idx], ifg, ibg, false);
}
}
// ============================================================
// widgetScrollbarThumb
// ============================================================
//
// Calculates thumb position and size for a scrollbar track.
// Used by both the WM-level scrollbars and widget-internal scrollbars
// (ListBox, TreeView, etc.) to maintain consistent scrollbar behavior.
//
// The thumb size is proportional to visibleSize/totalSize -- a larger
// visible area means a larger thumb, giving visual feedback about how
// much content is scrollable. SB_MIN_THUMB prevents the thumb from
// becoming too small to grab with a mouse.
void widgetScrollbarThumb(int32_t trackLen, int32_t totalSize, int32_t visibleSize, int32_t scrollPos, int32_t *thumbPos, int32_t *thumbSize) {
*thumbSize = (trackLen * visibleSize) / totalSize;
if (*thumbSize < SB_MIN_THUMB) {
*thumbSize = SB_MIN_THUMB;
}
if (*thumbSize > trackLen) {
*thumbSize = trackLen;
}
int32_t maxScroll = totalSize - visibleSize;
if (maxScroll > 0) {
*thumbPos = ((trackLen - *thumbSize) * scrollPos) / maxScroll;
} else {
*thumbPos = 0;
}
}
// ============================================================
// widgetRemoveChild
// ============================================================
//
// Unlinks a child from its parent's child list. O(n) in the number
// of children because the singly-linked list requires walking to
// find the predecessor. This is acceptable because child removal
// is infrequent (widget destruction, tree item reordering).
void widgetRemoveChild(WidgetT *parent, WidgetT *child) {
WidgetT *prev = NULL;
for (WidgetT *c = parent->firstChild; c; c = c->nextSibling) {
if (c == child) {
if (prev) {
prev->nextSibling = c->nextSibling;
} else {
parent->firstChild = c->nextSibling;
}
if (parent->lastChild == child) {
parent->lastChild = prev;
}
child->nextSibling = NULL;
child->parent = NULL;
return;
}
prev = c;
}
}

View file

@ -1,74 +0,0 @@
# DVX Shell Makefile for DJGPP cross-compilation
DJGPP_PREFIX = $(HOME)/djgpp/djgpp
DJGPP_LIBPATH = $(HOME)/claude/windriver/tools/lib
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
EXE2COFF = $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/exe2coff
CWSDSTUB = $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/CWSDSTUB.EXE
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../dvx -I../tasks
LDFLAGS = -L../lib -ldvx -ltasks -lm
OBJDIR = ../obj/dvxshell
BINDIR = ../bin
CONFIGDIR = ../bin/config
THEMEDIR = ../bin/config/themes
WPAPERDIR = ../bin/config/wpaper
LIBDIR = ../lib
SRCS = shellMain.c shellApp.c shellExport.c shellInfo.c shellTaskMgr.c
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
TARGET = $(BINDIR)/dvx.exe
.PHONY: all clean libs
THEMES = $(THEMEDIR)/geos.thm $(THEMEDIR)/win31.thm $(THEMEDIR)/cde.thm
WPAPERS = $(WPAPERDIR)/blueglow.jpg $(WPAPERDIR)/swoop.jpg $(WPAPERDIR)/triangle.jpg
all: libs $(TARGET) $(CONFIGDIR)/dvx.ini $(THEMES) $(WPAPERS)
libs:
$(MAKE) -C ../dvx
$(MAKE) -C ../tasks
$(TARGET): $(OBJS) $(LIBDIR)/libdvx.a $(LIBDIR)/libtasks.a | $(BINDIR)
$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) -Wl,-Map=$(BINDIR)/dvx.map
$(EXE2COFF) $@
cat $(CWSDSTUB) $(BINDIR)/dvx > $@
rm -f $(BINDIR)/dvx
$(CONFIGDIR)/dvx.ini: ../dvx.ini | $(CONFIGDIR)
sed 's/$$/\r/' $< > $@
$(OBJDIR)/%.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJDIR):
mkdir -p $(OBJDIR)
$(BINDIR):
mkdir -p $(BINDIR)
$(CONFIGDIR):
mkdir -p $(CONFIGDIR)
$(THEMEDIR):
mkdir -p $(THEMEDIR)
$(THEMEDIR)/%.thm: ../themes/%.thm | $(THEMEDIR)
sed 's/$$/\r/' $< > $@
$(WPAPERDIR):
mkdir -p $(WPAPERDIR)
$(WPAPERDIR)/%.jpg: ../wpaper/%.jpg | $(WPAPERDIR)
cp $< $@
# Dependencies
$(OBJDIR)/shellMain.o: shellMain.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../tasks/taskswitch.h
$(OBJDIR)/shellApp.o: shellApp.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../tasks/taskswitch.h
$(OBJDIR)/shellExport.o: shellExport.c shellApp.h shellInfo.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxDraw.h ../dvx/dvxVideo.h ../dvx/dvxWm.h ../tasks/taskswitch.h
$(OBJDIR)/shellInfo.o: shellInfo.c shellInfo.h shellApp.h ../dvx/dvxApp.h ../dvx/platform/dvxPlatform.h
$(OBJDIR)/shellTaskMgr.o: shellTaskMgr.c shellTaskMgr.h shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvx/platform/dvxPlatform.h
clean:
rm -f $(OBJS) $(TARGET) $(BINDIR)/dvx.map $(BINDIR)/dvx.log
rm -rf $(WPAPERDIR) $(THEMEDIR) $(CONFIGDIR)

View file

@ -1,733 +0,0 @@
// shellExport.c -- DXE export table and wrapper functions for DVX Shell
//
// Exports all dvx*/wgt*/ts* symbols that DXE apps need. A few functions
// are wrapped for resource tracking (window ownership via appId).
//
// DXE3 is DJGPP's dynamic linking mechanism. Unlike ELF shared libraries,
// DXE modules have no implicit access to the host's symbol table. Every
// function or variable the DXE needs must be explicitly listed in an
// export table registered via dlregsym() BEFORE any dlopen() call. If a
// symbol is missing, dlopen() returns NULL with a "symbol not found" error.
//
// This file is essentially the ABI contract between the shell and apps.
// Three categories of exports:
//
// 1. Wrapped functions: dvxCreateWindow, dvxCreateWindowCentered,
// dvxDestroyWindow. These are intercepted to stamp win->appId for
// resource ownership tracking. The DXE sees them under their original
// names -- the app code calls dvxCreateWindow() normally and gets our
// wrapper transparently.
//
// 2. Direct exports: all other dvx/wgt/wm/ts functions. These are safe
// to call without shell-side interception.
//
// 3. libc functions: DXE modules are statically linked against DJGPP's
// libc, but DJGPP's DXE3 loader requires explicit re-export of any
// libc symbols the module references. Without these entries, the DXE
// would fail to load with unresolved symbol errors. This is a DXE3
// design limitation -- there's no automatic fallback to the host's libc.
#include "shellApp.h"
#include "shellInfo.h"
#include "shellTaskMgr.h"
#include "dvxApp.h"
#include "dvxDialog.h"
#include "dvxWidget.h"
#include "dvxDraw.h"
#include "dvxPrefs.h"
#include "platform/dvxPlatform.h"
#include "dvxVideo.h"
#include "dvxWm.h"
#include "taskswitch.h"
#include <sys/dxe.h>
#include <sys/stat.h>
#include <ctype.h>
#include <dirent.h>
#include <math.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
// stb headers (no IMPLEMENTATION -- symbols are in libdvx.a / libtasks.a)
#include "thirdparty/stb_image.h"
#include "thirdparty/stb_image_write.h"
#include "thirdparty/stb_ds.h"
// DJGPP stdio internals -- the stdin/stdout/stderr macros dereference
// these pointers. Without exporting them, any DXE that uses printf
// or fprintf gets an unresolved symbol.
extern FILE __dj_stdin;
extern FILE __dj_stdout;
extern FILE __dj_stderr;
// libgcc 64-bit integer math helpers (no header declares these)
extern long long __divdi3(long long, long long);
extern long long __moddi3(long long, long long);
extern long long __muldi3(long long, long long);
extern unsigned long long __udivdi3(unsigned long long, unsigned long long);
extern unsigned long long __udivmoddi4(unsigned long long, unsigned long long, unsigned long long *);
extern unsigned long long __umoddi3(unsigned long long, unsigned long long);
// ============================================================
// Prototypes
// ============================================================
static void shellRegisterExports(void);
static WindowT *shellWrapCreateWindow(AppContextT *ctx, const char *title, int32_t x, int32_t y, int32_t w, int32_t h, bool resizable);
static WindowT *shellWrapCreateWindowCentered(AppContextT *ctx, const char *title, int32_t w, int32_t h, bool resizable);
static void shellWrapDestroyWindow(AppContextT *ctx, WindowT *win);
// ============================================================
// Wrapper: dvxCreateWindow -- stamps win->appId
// ============================================================
// The wrapper calls the real dvxCreateWindow, then tags the result with
// sCurrentAppId. This is how the shell knows which app owns which window,
// enabling per-app window cleanup on crash/termination. The app never
// sees the difference -- the wrapper has the same signature and is
// exported under the same name as the original function.
static WindowT *shellWrapCreateWindow(AppContextT *ctx, const char *title, int32_t x, int32_t y, int32_t w, int32_t h, bool resizable) {
WindowT *win = dvxCreateWindow(ctx, title, x, y, w, h, resizable);
if (win) {
win->appId = sCurrentAppId;
}
return win;
}
// ============================================================
// Wrapper: dvxCreateWindowCentered -- stamps win->appId
// ============================================================
static WindowT *shellWrapCreateWindowCentered(AppContextT *ctx, const char *title, int32_t w, int32_t h, bool resizable) {
WindowT *win = dvxCreateWindowCentered(ctx, title, w, h, resizable);
if (win) {
win->appId = sCurrentAppId;
}
return win;
}
// ============================================================
// Wrapper: dvxDestroyWindow -- checks for last-window reap
// ============================================================
// Beyond just destroying the window, this wrapper implements the lifecycle
// rule for callback-only apps: when their last window closes, they're done.
// Main-loop apps manage their own lifetime (their task returns from
// appMain), so this check only applies to callback-only apps.
// The appId is captured before destruction because the window struct is
// freed by dvxDestroyWindow.
static void shellWrapDestroyWindow(AppContextT *ctx, WindowT *win) {
int32_t appId = win->appId;
dvxDestroyWindow(ctx, win);
// If this was a callback-only app's last window, mark for reaping
if (appId > 0) {
ShellAppT *app = shellGetApp(appId);
if (app && !app->hasMainLoop && app->state == AppStateRunningE) {
// Check if app still has any windows
bool hasWindows = false;
for (int32_t i = 0; i < ctx->stack.count; i++) {
if (ctx->stack.windows[i]->appId == appId) {
hasWindows = true;
break;
}
}
if (!hasWindows) {
app->state = AppStateTerminatingE;
}
}
}
}
// ============================================================
// Export table
// ============================================================
// DXE_EXPORT_TABLE generates a DXE symbol table array. DXE_EXPORT(fn)
// expands to { "_fn", (void *)fn } -- the underscore prefix matches COFF
// symbol naming. For wrapped functions we use raw entries with explicit
// names so the DXE sees "_dvxCreateWindow" but gets our wrapper's address.
DXE_EXPORT_TABLE(shellExportTable)
// Wrapped functions (exported under original names, but pointing to
// our wrappers that add resource tracking)
{ "_dvxCreateWindow", (void *)shellWrapCreateWindow },
{ "_dvxDestroyWindow", (void *)shellWrapDestroyWindow },
// dvxPlatform.h -- platform abstraction
DXE_EXPORT(platformLineEnding)
DXE_EXPORT(platformChdir)
DXE_EXPORT(platformGetMemoryInfo)
DXE_EXPORT(platformMkdirRecursive)
DXE_EXPORT(platformMouseSetAccel)
DXE_EXPORT(platformMouseWarp)
DXE_EXPORT(platformPathDirEnd)
DXE_EXPORT(platformStripLineEndings)
DXE_EXPORT(platformValidateFilename)
DXE_EXPORT(platformVideoEnumModes)
// dvxPrefs.h -- preferences
DXE_EXPORT(prefsGetBool)
DXE_EXPORT(prefsGetInt)
DXE_EXPORT(prefsGetString)
DXE_EXPORT(prefsLoad)
DXE_EXPORT(prefsRemove)
DXE_EXPORT(prefsSave)
DXE_EXPORT(prefsSaveAs)
DXE_EXPORT(prefsSetBool)
DXE_EXPORT(prefsSetInt)
DXE_EXPORT(prefsSetString)
// dvxApp.h -- direct exports
DXE_EXPORT(dvxApplyColorScheme)
DXE_EXPORT(dvxChangeVideoMode)
DXE_EXPORT(dvxColorLabel)
DXE_EXPORT(dvxColorName)
DXE_EXPORT(dvxGetColor)
DXE_EXPORT(dvxInit)
DXE_EXPORT(dvxLoadTheme)
DXE_EXPORT(dvxResetColorScheme)
DXE_EXPORT(dvxSaveTheme)
DXE_EXPORT(dvxSetColor)
DXE_EXPORT(dvxSetMouseConfig)
DXE_EXPORT(dvxSetWallpaper)
DXE_EXPORT(dvxSetWallpaperMode)
DXE_EXPORT(dvxShutdown)
DXE_EXPORT(dvxUpdate)
{ "_dvxCreateWindowCentered", (void *)shellWrapCreateWindowCentered },
DXE_EXPORT(dvxFitWindow)
DXE_EXPORT(dvxInvalidateRect)
DXE_EXPORT(dvxInvalidateWindow)
DXE_EXPORT(dvxMinimizeWindow)
DXE_EXPORT(dvxMaximizeWindow)
DXE_EXPORT(dvxQuit)
DXE_EXPORT(dvxSetTitle)
DXE_EXPORT(dvxGetFont)
DXE_EXPORT(dvxGetColors)
DXE_EXPORT(dvxGetDisplay)
DXE_EXPORT(dvxGetVideoModes)
DXE_EXPORT(dvxGetBlitOps)
DXE_EXPORT(dvxSetWindowIcon)
DXE_EXPORT(dvxLoadImage)
DXE_EXPORT(dvxFreeImage)
DXE_EXPORT(dvxSaveImage)
DXE_EXPORT(dvxScreenshot)
DXE_EXPORT(dvxWindowScreenshot)
DXE_EXPORT(dvxCreateAccelTable)
DXE_EXPORT(dvxFreeAccelTable)
DXE_EXPORT(dvxAddAccel)
DXE_EXPORT(dvxCascadeWindows)
DXE_EXPORT(dvxTileWindows)
DXE_EXPORT(dvxTileWindowsH)
DXE_EXPORT(dvxTileWindowsV)
DXE_EXPORT(dvxClipboardCopy)
DXE_EXPORT(dvxClipboardGet)
// dvxDialog.h
DXE_EXPORT(dvxMessageBox)
DXE_EXPORT(dvxFileDialog)
// dvxDraw.h
DXE_EXPORT(rectFill)
DXE_EXPORT(rectCopy)
DXE_EXPORT(drawBevel)
DXE_EXPORT(drawChar)
DXE_EXPORT(drawText)
DXE_EXPORT(drawTextN)
DXE_EXPORT(textWidth)
DXE_EXPORT(drawTextAccel)
DXE_EXPORT(textWidthAccel)
DXE_EXPORT(drawFocusRect)
DXE_EXPORT(drawHLine)
DXE_EXPORT(drawVLine)
// dvxVideo.h
DXE_EXPORT(packColor)
DXE_EXPORT(resetClipRect)
DXE_EXPORT(setClipRect)
// dvxWm.h
DXE_EXPORT(wmAddMenuBar)
DXE_EXPORT(wmAddMenu)
DXE_EXPORT(wmAddMenuItem)
DXE_EXPORT(wmAddMenuCheckItem)
DXE_EXPORT(wmAddMenuRadioItem)
DXE_EXPORT(wmAddMenuSeparator)
DXE_EXPORT(wmAddSubMenu)
DXE_EXPORT(wmAddVScrollbar)
DXE_EXPORT(wmAddHScrollbar)
DXE_EXPORT(wmMinimizedIconPos)
DXE_EXPORT(wmMinimizedIconRect)
DXE_EXPORT(wmSetTitle)
DXE_EXPORT(wmSetIcon)
DXE_EXPORT(wmCreateMenu)
DXE_EXPORT(wmFreeMenu)
DXE_EXPORT(wmUpdateContentRect)
DXE_EXPORT(wmReallocContentBuf)
// dvxWidget.h -- window integration
DXE_EXPORT(wgtInitWindow)
// dvxWidget.h -- containers
DXE_EXPORT(wgtVBox)
DXE_EXPORT(wgtHBox)
DXE_EXPORT(wgtFrame)
// dvxWidget.h -- basic widgets
DXE_EXPORT(wgtLabel)
DXE_EXPORT(wgtLabelSetAlign)
DXE_EXPORT(wgtButton)
DXE_EXPORT(wgtCheckbox)
DXE_EXPORT(wgtTextInput)
DXE_EXPORT(wgtPasswordInput)
DXE_EXPORT(wgtMaskedInput)
// dvxWidget.h -- radio buttons
DXE_EXPORT(wgtRadioGroup)
DXE_EXPORT(wgtRadio)
// dvxWidget.h -- spacing
DXE_EXPORT(wgtSpacer)
DXE_EXPORT(wgtHSeparator)
DXE_EXPORT(wgtVSeparator)
// dvxWidget.h -- complex widgets
DXE_EXPORT(wgtListBox)
DXE_EXPORT(wgtTextArea)
// dvxWidget.h -- dropdown/combo
DXE_EXPORT(wgtDropdown)
DXE_EXPORT(wgtDropdownSetItems)
DXE_EXPORT(wgtDropdownGetSelected)
DXE_EXPORT(wgtDropdownSetSelected)
DXE_EXPORT(wgtComboBox)
DXE_EXPORT(wgtComboBoxSetItems)
DXE_EXPORT(wgtComboBoxGetSelected)
DXE_EXPORT(wgtComboBoxSetSelected)
// dvxWidget.h -- progress bar
DXE_EXPORT(wgtProgressBar)
DXE_EXPORT(wgtProgressBarV)
DXE_EXPORT(wgtProgressBarSetValue)
DXE_EXPORT(wgtProgressBarGetValue)
// dvxWidget.h -- slider
DXE_EXPORT(wgtSlider)
DXE_EXPORT(wgtSliderSetValue)
DXE_EXPORT(wgtSliderGetValue)
// dvxWidget.h -- spinner
DXE_EXPORT(wgtSpinner)
DXE_EXPORT(wgtSpinnerSetValue)
DXE_EXPORT(wgtSpinnerGetValue)
DXE_EXPORT(wgtSpinnerSetRange)
DXE_EXPORT(wgtSpinnerSetStep)
// dvxWidget.h -- tab control
DXE_EXPORT(wgtTabControl)
DXE_EXPORT(wgtTabPage)
DXE_EXPORT(wgtTabControlSetActive)
DXE_EXPORT(wgtTabControlGetActive)
// dvxWidget.h -- status bar / toolbar
DXE_EXPORT(wgtStatusBar)
DXE_EXPORT(wgtToolbar)
// dvxWidget.h -- tree view
DXE_EXPORT(wgtTreeView)
DXE_EXPORT(wgtTreeViewGetSelected)
DXE_EXPORT(wgtTreeViewSetSelected)
DXE_EXPORT(wgtTreeViewSetMultiSelect)
DXE_EXPORT(wgtTreeViewSetReorderable)
DXE_EXPORT(wgtTreeItem)
DXE_EXPORT(wgtTreeItemSetExpanded)
DXE_EXPORT(wgtTreeItemIsExpanded)
DXE_EXPORT(wgtTreeItemIsSelected)
DXE_EXPORT(wgtTreeItemSetSelected)
// dvxWidget.h -- timer
DXE_EXPORT(wgtTimer)
DXE_EXPORT(wgtTimerIsRunning)
DXE_EXPORT(wgtTimerSetInterval)
DXE_EXPORT(wgtTimerStart)
DXE_EXPORT(wgtTimerStop)
// dvxWidget.h -- list view
DXE_EXPORT(wgtListView)
DXE_EXPORT(wgtListViewSetColumns)
DXE_EXPORT(wgtListViewSetData)
DXE_EXPORT(wgtListViewGetSelected)
DXE_EXPORT(wgtListViewSetSelected)
DXE_EXPORT(wgtListViewSetSort)
DXE_EXPORT(wgtListViewSetHeaderClickCallback)
DXE_EXPORT(wgtListViewSetMultiSelect)
DXE_EXPORT(wgtListViewIsItemSelected)
DXE_EXPORT(wgtListViewSetItemSelected)
DXE_EXPORT(wgtListViewSelectAll)
DXE_EXPORT(wgtListViewClearSelection)
DXE_EXPORT(wgtListViewSetReorderable)
// dvxWidget.h -- scroll pane / splitter
DXE_EXPORT(wgtScrollPane)
DXE_EXPORT(wgtSplitter)
DXE_EXPORT(wgtSplitterSetPos)
DXE_EXPORT(wgtSplitterGetPos)
// dvxWidget.h -- image / image button
DXE_EXPORT(wgtImageButton)
DXE_EXPORT(wgtImageButtonFromFile)
DXE_EXPORT(wgtImageButtonSetData)
DXE_EXPORT(wgtImage)
DXE_EXPORT(wgtImageFromFile)
DXE_EXPORT(wgtImageSetData)
// dvxWidget.h -- canvas
DXE_EXPORT(wgtCanvas)
DXE_EXPORT(wgtCanvasClear)
DXE_EXPORT(wgtCanvasSetMouseCallback)
DXE_EXPORT(wgtCanvasSetPenColor)
DXE_EXPORT(wgtCanvasSetPenSize)
DXE_EXPORT(wgtCanvasSave)
DXE_EXPORT(wgtCanvasLoad)
DXE_EXPORT(wgtCanvasDrawLine)
DXE_EXPORT(wgtCanvasDrawRect)
DXE_EXPORT(wgtCanvasFillRect)
DXE_EXPORT(wgtCanvasFillCircle)
DXE_EXPORT(wgtCanvasSetPixel)
DXE_EXPORT(wgtCanvasGetPixel)
// dvxWidget.h -- ANSI terminal
DXE_EXPORT(wgtAnsiTerm)
DXE_EXPORT(wgtAnsiTermWrite)
DXE_EXPORT(wgtAnsiTermClear)
DXE_EXPORT(wgtAnsiTermSetComm)
DXE_EXPORT(wgtAnsiTermSetScrollback)
DXE_EXPORT(wgtAnsiTermPoll)
DXE_EXPORT(wgtAnsiTermRepaint)
// dvxWidget.h -- operations
DXE_EXPORT(wgtInvalidate)
DXE_EXPORT(wgtInvalidatePaint)
DXE_EXPORT(wgtSetDebugLayout)
DXE_EXPORT(wgtSetText)
DXE_EXPORT(wgtSetTooltip)
DXE_EXPORT(wgtGetText)
DXE_EXPORT(wgtGetFocused)
DXE_EXPORT(wgtSetEnabled)
DXE_EXPORT(wgtSetFocused)
DXE_EXPORT(wgtSetReadOnly)
DXE_EXPORT(wgtSetVisible)
DXE_EXPORT(wgtGetContext)
DXE_EXPORT(wgtSetName)
DXE_EXPORT(wgtFind)
DXE_EXPORT(wgtDestroy)
// dvxWidget.h -- list box ops
DXE_EXPORT(wgtListBoxSetItems)
DXE_EXPORT(wgtListBoxGetSelected)
DXE_EXPORT(wgtListBoxSetSelected)
DXE_EXPORT(wgtListBoxSetMultiSelect)
DXE_EXPORT(wgtListBoxIsItemSelected)
DXE_EXPORT(wgtListBoxSetItemSelected)
DXE_EXPORT(wgtListBoxSelectAll)
DXE_EXPORT(wgtListBoxClearSelection)
DXE_EXPORT(wgtListBoxSetReorderable)
// dvxWidget.h -- layout
DXE_EXPORT(wgtResolveSize)
DXE_EXPORT(wgtLayout)
DXE_EXPORT(wgtPaint)
// taskswitch.h -- only yield and query functions are exported.
// tsCreate/tsKill/etc. are NOT exported because apps should not
// manipulate the task system directly -- the shell manages task
// lifecycle through shellLoadApp/shellForceKillApp.
DXE_EXPORT(tsActiveCount)
DXE_EXPORT(tsCreate)
DXE_EXPORT(tsCurrentId)
DXE_EXPORT(tsGetName)
DXE_EXPORT(tsGetPriority)
DXE_EXPORT(tsGetState)
DXE_EXPORT(tsKill)
DXE_EXPORT(tsPause)
DXE_EXPORT(tsResume)
DXE_EXPORT(tsSetPriority)
DXE_EXPORT(tsYield)
// dvxWm.h -- direct window management
DXE_EXPORT(wmRaiseWindow)
DXE_EXPORT(wmSetFocus)
DXE_EXPORT(wmRestoreMinimized)
// Shell API
DXE_EXPORT(shellConfigPath)
DXE_EXPORT(shellEnsureConfigDir)
DXE_EXPORT(shellGetApp)
DXE_EXPORT(shellLoadApp)
DXE_EXPORT(shellLog)
DXE_EXPORT(shellTaskMgrOpen)
DXE_EXPORT(shellForceKillApp)
DXE_EXPORT(shellRunningAppCount)
DXE_EXPORT(shellRegisterDesktopUpdate)
DXE_EXPORT(shellUnregisterDesktopUpdate)
DXE_EXPORT(shellGetSystemInfo)
// ================================================================
// libc / libm exports. DXE3 modules are relocatable objects, not
// fully linked executables. Every C library function a DXE calls
// must appear here so the loader can resolve it at dlopen time.
// This is intentionally comprehensive to avoid "unresolved symbol"
// surprises when apps use standard functions.
// ================================================================
// --- memory ---
DXE_EXPORT(calloc)
DXE_EXPORT(free)
DXE_EXPORT(malloc)
DXE_EXPORT(realloc)
// --- string / memory ops ---
DXE_EXPORT(memchr)
DXE_EXPORT(memcmp)
DXE_EXPORT(memcpy)
DXE_EXPORT(memmove)
DXE_EXPORT(memset)
DXE_EXPORT(strcasecmp)
DXE_EXPORT(strcat)
DXE_EXPORT(strchr)
DXE_EXPORT(strcmp)
DXE_EXPORT(strcpy)
DXE_EXPORT(strcspn)
DXE_EXPORT(strdup)
DXE_EXPORT(strerror)
DXE_EXPORT(strlen)
DXE_EXPORT(strncasecmp)
DXE_EXPORT(strncat)
DXE_EXPORT(strncmp)
DXE_EXPORT(strncpy)
DXE_EXPORT(strpbrk)
DXE_EXPORT(strrchr)
DXE_EXPORT(strspn)
DXE_EXPORT(strstr)
DXE_EXPORT(strtok)
// --- ctype ---
DXE_EXPORT(isalnum)
DXE_EXPORT(isalpha)
DXE_EXPORT(isdigit)
DXE_EXPORT(islower)
DXE_EXPORT(isprint)
DXE_EXPORT(ispunct)
DXE_EXPORT(isspace)
DXE_EXPORT(isupper)
DXE_EXPORT(isxdigit)
DXE_EXPORT(tolower)
DXE_EXPORT(toupper)
// --- conversion ---
DXE_EXPORT(abs)
DXE_EXPORT(atof)
DXE_EXPORT(atoi)
DXE_EXPORT(atol)
DXE_EXPORT(labs)
DXE_EXPORT(strtod)
DXE_EXPORT(strtol)
DXE_EXPORT(strtoul)
// --- formatted I/O ---
DXE_EXPORT(fprintf)
DXE_EXPORT(fputs)
DXE_EXPORT(fscanf)
DXE_EXPORT(printf)
DXE_EXPORT(puts)
DXE_EXPORT(snprintf)
DXE_EXPORT(sprintf)
DXE_EXPORT(sscanf)
DXE_EXPORT(vfprintf)
DXE_EXPORT(vprintf)
DXE_EXPORT(vsnprintf)
DXE_EXPORT(vsprintf)
// --- character I/O ---
DXE_EXPORT(fgetc)
DXE_EXPORT(fgets)
DXE_EXPORT(fputc)
DXE_EXPORT(getc)
DXE_EXPORT(putc)
DXE_EXPORT(putchar)
DXE_EXPORT(ungetc)
// --- file I/O ---
DXE_EXPORT(fclose)
DXE_EXPORT(feof)
DXE_EXPORT(ferror)
DXE_EXPORT(fflush)
DXE_EXPORT(fopen)
DXE_EXPORT(fread)
DXE_EXPORT(freopen)
DXE_EXPORT(fseek)
DXE_EXPORT(ftell)
DXE_EXPORT(fwrite)
DXE_EXPORT(remove)
DXE_EXPORT(rename)
DXE_EXPORT(rewind)
DXE_EXPORT(tmpfile)
DXE_EXPORT(tmpnam)
// --- directory ---
DXE_EXPORT(closedir)
DXE_EXPORT(mkdir)
DXE_EXPORT(opendir)
DXE_EXPORT(readdir)
DXE_EXPORT(rmdir)
// --- filesystem ---
DXE_EXPORT(access)
DXE_EXPORT(chdir)
DXE_EXPORT(getcwd)
DXE_EXPORT(stat)
DXE_EXPORT(unlink)
// --- time ---
DXE_EXPORT(clock)
DXE_EXPORT(difftime)
DXE_EXPORT(gmtime)
DXE_EXPORT(localtime)
DXE_EXPORT(mktime)
DXE_EXPORT(strftime)
DXE_EXPORT(time)
// --- process / environment ---
DXE_EXPORT(abort)
DXE_EXPORT(atexit)
DXE_EXPORT(exit)
DXE_EXPORT(getenv)
DXE_EXPORT(system)
// --- sorting / searching ---
DXE_EXPORT(bsearch)
DXE_EXPORT(qsort)
// --- random ---
DXE_EXPORT(rand)
DXE_EXPORT(srand)
// --- setjmp / signal ---
DXE_EXPORT(longjmp)
DXE_EXPORT(setjmp)
DXE_EXPORT(signal)
// --- libm ---
DXE_EXPORT(acos)
DXE_EXPORT(asin)
DXE_EXPORT(atan)
DXE_EXPORT(atan2)
DXE_EXPORT(ceil)
DXE_EXPORT(cos)
DXE_EXPORT(exp)
DXE_EXPORT(fabs)
DXE_EXPORT(floor)
DXE_EXPORT(fmod)
DXE_EXPORT(frexp)
DXE_EXPORT(ldexp)
DXE_EXPORT(log)
DXE_EXPORT(log10)
DXE_EXPORT(modf)
DXE_EXPORT(pow)
DXE_EXPORT(sin)
DXE_EXPORT(sqrt)
DXE_EXPORT(tan)
// --- errno ---
DXE_EXPORT(errno)
// --- libgcc 64-bit integer math helpers ---
// GCC emits calls to these for int64_t division/modulo on 32-bit targets.
// Without them, any DXE using 64-bit arithmetic gets unresolved symbols.
DXE_EXPORT(__divdi3)
DXE_EXPORT(__moddi3)
DXE_EXPORT(__muldi3)
DXE_EXPORT(__udivdi3)
DXE_EXPORT(__udivmoddi4)
DXE_EXPORT(__umoddi3)
// --- DJGPP stdio internals ---
// The stdin/stdout/stderr macros in DJGPP expand to pointers to
// these FILE structs. Without them, any DXE that does printf()
// or fprintf(stderr, ...) gets an unresolved symbol at load time.
DXE_EXPORT(__dj_stdin)
DXE_EXPORT(__dj_stdout)
DXE_EXPORT(__dj_stderr)
// --- stb_ds (dynamic arrays / hashmaps) ---
// Internal functions called by the arrput/arrfree/hm* macros.
// Implementation lives in libtasks.a.
DXE_EXPORT(stbds_arrfreef)
DXE_EXPORT(stbds_arrgrowf)
DXE_EXPORT(stbds_hash_bytes)
DXE_EXPORT(stbds_hash_string)
DXE_EXPORT(stbds_hmdel_key)
DXE_EXPORT(stbds_hmfree_func)
DXE_EXPORT(stbds_hmget_key)
DXE_EXPORT(stbds_hmget_key_ts)
DXE_EXPORT(stbds_hmput_default)
DXE_EXPORT(stbds_hmput_key)
DXE_EXPORT(stbds_rand_seed)
DXE_EXPORT(stbds_shmode_func)
DXE_EXPORT(stbds_stralloc)
DXE_EXPORT(stbds_strreset)
// --- stb_image (image loading) ---
DXE_EXPORT(stbi_failure_reason)
DXE_EXPORT(stbi_image_free)
DXE_EXPORT(stbi_info)
DXE_EXPORT(stbi_info_from_memory)
DXE_EXPORT(stbi_load)
DXE_EXPORT(stbi_load_from_memory)
DXE_EXPORT(stbi_set_flip_vertically_on_load)
// --- stb_image_write ---
DXE_EXPORT(stbi_write_bmp)
DXE_EXPORT(stbi_write_png)
DXE_EXPORT(stbi_write_jpg)
DXE_EXPORT(stbi_write_tga)
DXE_EXPORT_END
// ============================================================
// shellRegisterExports
// ============================================================
// dlregsym registers our export table with DJGPP's DXE3 runtime.
// Must be called once before any dlopen -- subsequent dlopen calls
// will search this table to resolve DXE symbol references.
static void shellRegisterExports(void) {
dlregsym(shellExportTable);
}
// ============================================================
// Public init function
// ============================================================
void shellExportInit(void) {
shellRegisterExports();
}

54
loader/Makefile Normal file
View file

@ -0,0 +1,54 @@
# DVX Loader Makefile for DJGPP cross-compilation
#
# Builds the bootstrap loader (dvx.exe) that loads DXE modules.
# Links dvxPlatformDos.c directly -- the platform layer provides
# the DXE export table via platformRegisterDxeExports().
DJGPP_PREFIX = $(HOME)/djgpp/djgpp
DJGPP_LIBPATH = $(HOME)/claude/windriver/tools/lib
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
EXE2COFF = $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/exe2coff
CWSDSTUB = $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/CWSDSTUB.EXE
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../core -I../core/platform -I../tasks -I../tasks/thirdparty
LDFLAGS = -lm
OBJDIR = ../obj/loader
POBJDIR = ../obj/loader/platform
BINDIR = ../bin
SRCS = loaderMain.c
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
POBJS = $(POBJDIR)/dvxPlatformDos.o
TARGET = $(BINDIR)/dvx.exe
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJS) $(POBJS) | $(BINDIR)
$(CC) $(CFLAGS) -o $@ $(OBJS) $(POBJS) $(LDFLAGS) -Wl,-Map=$(BINDIR)/dvx.map
$(EXE2COFF) $@
cat $(CWSDSTUB) $(BINDIR)/dvx > $@
rm -f $(BINDIR)/dvx
$(OBJDIR)/%.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS) -c -o $@ $<
$(POBJDIR)/dvxPlatformDos.o: ../core/platform/dvxPlatformDos.c | $(POBJDIR)
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJDIR):
mkdir -p $(OBJDIR)
$(POBJDIR):
mkdir -p $(POBJDIR)
$(BINDIR):
mkdir -p $(BINDIR)
# Dependencies
$(OBJDIR)/loaderMain.o: loaderMain.c ../core/platform/dvxPlatform.h ../core/dvxTypes.h
$(POBJDIR)/dvxPlatformDos.o: ../core/platform/dvxPlatformDos.c ../core/platform/dvxPlatform.h ../core/dvxTypes.h ../core/dvxPalette.h
clean:
rm -f $(OBJS) $(POBJS) $(TARGET) $(BINDIR)/dvx.map

394
loader/loaderMain.c Normal file
View file

@ -0,0 +1,394 @@
// loaderMain.c -- DVX bootstrap loader entry point
//
// Loads all DXE modules from two directories:
// libs/ *.lib -- core libraries (libtasks, libdvx, dvxshell)
// widgets/ *.wgt -- widget type plugins (box, button, listview, etc.)
//
// Each module may have a .dep file (same base name, .dep extension)
// listing base names of modules that must be loaded before it.
// The loader resolves the dependency graph and loads in topological
// order. After loading, any module that exports wgtRegister() has
// it called. Finally, the loader finds and calls shellMain().
#include "dvxPlatform.h"
#include <ctype.h>
#include <dirent.h>
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#define STB_DS_IMPLEMENTATION
#include "stb_ds.h"
// ============================================================
// Constants
// ============================================================
#define LIBS_DIR "LIBS"
#define WIDGET_DIR "WIDGETS"
// ============================================================
// Module entry for dependency resolution
// ============================================================
typedef struct {
char path[260];
char baseName[16];
char **deps;
bool loaded;
void *handle;
} ModuleT;
// ============================================================
// Prototypes
// ============================================================
static bool allDepsLoaded(const ModuleT *mod, const ModuleT *mods);
static void extractBaseName(const char *path, const char *ext, char *out, int32_t outSize);
static void *findSymbol(void **handles, const char *symbol);
static void **loadAllModules(void);
static void loadInOrder(ModuleT *mods);
static void readDeps(ModuleT *mod);
static void scanDir(const char *dirPath, const char *ext, ModuleT **mods);
// ============================================================
// extractBaseName -- strip directory and extension, lowercase
// ============================================================
static void extractBaseName(const char *path, const char *ext, char *out, int32_t outSize) {
// Find last directory separator
const char *start = path;
const char *p = path;
while (*p) {
if (*p == '/' || *p == '\\') {
start = p + 1;
}
p++;
}
// Copy up to the extension
int32_t extLen = strlen(ext);
int32_t len = strlen(start);
if (len > extLen && strcasecmp(start + len - extLen, ext) == 0) {
len -= extLen;
}
if (len >= outSize) {
len = outSize - 1;
}
for (int32_t i = 0; i < len; i++) {
out[i] = tolower((unsigned char)start[i]);
}
out[len] = '\0';
}
// ============================================================
// readDeps -- parse .dep file for a module
// ============================================================
//
// The .dep file has the same path as the module but with a .dep
// extension. Each line is a dependency base name. Empty lines
// and lines starting with # are ignored.
static void readDeps(ModuleT *mod) {
// Build dep file path: replace extension with .dep
char depPath[260];
strncpy(depPath, mod->path, sizeof(depPath) - 1);
depPath[sizeof(depPath) - 1] = '\0';
char *dot = strrchr(depPath, '.');
if (!dot) {
return;
}
strcpy(dot, ".dep");
FILE *f = fopen(depPath, "r");
if (!f) {
return;
}
char line[64];
while (fgets(line, sizeof(line), f)) {
// Strip \r and \n
int32_t len = strlen(line);
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) {
line[--len] = '\0';
}
// Skip empty lines and comments
if (len == 0 || line[0] == '#') {
continue;
}
// Lowercase the dep name for case-insensitive matching
for (int32_t i = 0; i < len; i++) {
line[i] = tolower((unsigned char)line[i]);
}
arrput(mod->deps, strdup(line));
}
fclose(f);
}
// ============================================================
// scanDir -- recursively find modules with a given extension
// ============================================================
static void scanDir(const char *dirPath, const char *ext, ModuleT **mods) {
DIR *dir = opendir(dirPath);
if (!dir) {
return;
}
struct dirent *ent;
while ((ent = readdir(dir)) != NULL) {
const char *name = ent->d_name;
// Skip . and ..
if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) {
continue;
}
char path[260];
snprintf(path, sizeof(path), "%s/%s", dirPath, name);
// Check for matching extension
int32_t nameLen = strlen(name);
int32_t extLen = strlen(ext);
if (nameLen > extLen && strcasecmp(name + nameLen - extLen, ext) == 0) {
ModuleT mod;
memset(&mod, 0, sizeof(mod));
strncpy(mod.path, path, sizeof(mod.path) - 1);
extractBaseName(path, ext, mod.baseName, sizeof(mod.baseName));
arrput(*mods, mod);
continue;
}
// Recurse into subdirectories
struct stat st;
if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
scanDir(path, ext, mods);
}
}
closedir(dir);
}
// ============================================================
// allDepsLoaded -- check if a module's dependencies are satisfied
// ============================================================
//
// A dep is satisfied if either:
// 1. No module with that base name exists (external, assumed OK)
// 2. The module with that base name is already loaded
static bool allDepsLoaded(const ModuleT *mod, const ModuleT *mods) {
for (int32_t d = 0; d < arrlen(mod->deps); d++) {
for (int32_t j = 0; j < arrlen(mods); j++) {
if (strcasecmp(mods[j].baseName, mod->deps[d]) == 0) {
if (!mods[j].loaded) {
return false;
}
break;
}
}
}
return true;
}
// ============================================================
// loadInOrder -- topological sort and load
// ============================================================
//
// Repeatedly scans the module list for entries whose dependencies
// are all satisfied, loads them, and marks them done. Stops when
// all modules are loaded or no progress can be made (circular dep).
static void loadInOrder(ModuleT *mods) {
typedef void (*RegFnT)(void);
int32_t total = arrlen(mods);
int32_t loaded = 0;
bool progress;
do {
progress = false;
for (int32_t i = 0; i < total; i++) {
if (mods[i].loaded) {
continue;
}
if (!allDepsLoaded(&mods[i], mods)) {
continue;
}
mods[i].handle = dlopen(mods[i].path, RTLD_GLOBAL);
if (mods[i].handle) {
RegFnT regFn = (RegFnT)dlsym(mods[i].handle, "_wgtRegister");
if (regFn) {
regFn();
}
}
mods[i].loaded = true;
loaded++;
progress = true;
}
} while (progress && loaded < total);
if (loaded < total) {
fprintf(stderr, "Module loader: %d of %d modules could not be loaded (circular deps or missing deps)\n", total - loaded, total);
for (int32_t i = 0; i < total; i++) {
if (!mods[i].loaded) {
fprintf(stderr, " %s\n", mods[i].path);
}
}
}
}
// ============================================================
// loadAllModules -- scan libs/ and widgets/, load in dep order
// ============================================================
//
// Returns a stb_ds dynamic array of dlopen handles.
static void **loadAllModules(void) {
ModuleT *mods = NULL;
// Discover all modules
scanDir(LIBS_DIR, ".lib", &mods);
scanDir(WIDGET_DIR, ".wgt", &mods);
// Read dependency files
for (int32_t i = 0; i < arrlen(mods); i++) {
readDeps(&mods[i]);
}
// Load in dependency order
loadInOrder(mods);
// Collect handles
void **handles = NULL;
for (int32_t i = 0; i < arrlen(mods); i++) {
if (mods[i].handle) {
arrput(handles, mods[i].handle);
}
}
// Free dep arrays
for (int32_t i = 0; i < arrlen(mods); i++) {
for (int32_t d = 0; d < arrlen(mods[i].deps); d++) {
free(mods[i].deps[d]);
}
arrfree(mods[i].deps);
}
arrfree(mods);
return handles;
}
// ============================================================
// findSymbol -- search loaded handles for a symbol
// ============================================================
static void *findSymbol(void **handles, const char *symbol) {
for (int32_t i = 0; i < arrlen(handles); i++) {
void *sym = dlsym(handles[i], symbol);
if (sym) {
return sym;
}
}
return NULL;
}
// ============================================================
// main
// ============================================================
int main(int argc, char *argv[]) {
// Change to the directory containing the executable so relative
// paths (LIBS/, WIDGETS/, APPS/, CONFIG/) resolve correctly.
char exeDir[260];
strncpy(exeDir, argv[0], sizeof(exeDir) - 1);
exeDir[sizeof(exeDir) - 1] = '\0';
char *sep = platformPathDirEnd(exeDir);
if (sep) {
*sep = '\0';
platformChdir(exeDir);
}
// Register platform + libc/libm/runtime symbols for DXE resolution
platformRegisterDxeExports();
// Load all modules from libs/ and widgets/ in dependency order.
// Each module may have a .dep file specifying load-before deps.
// Widget modules that export wgtRegister() get it called.
void **handles = loadAllModules();
if (!handles || arrlen(handles) == 0) {
fprintf(stderr, "No modules loaded from %s/ or %s/\n", LIBS_DIR, WIDGET_DIR);
arrfree(handles);
return 1;
}
// Find and call shellMain from whichever module exports it
typedef int (*ShellMainFnT)(int, char **);
ShellMainFnT shellMain = (ShellMainFnT)findSymbol(handles, "_shellMain");
if (!shellMain) {
fprintf(stderr, "No module exports shellMain\n");
for (int32_t i = arrlen(handles) - 1; i >= 0; i--) {
dlclose(handles[i]);
}
arrfree(handles);
return 1;
}
int result = shellMain(argc, argv);
// Clean up in reverse load order
for (int32_t i = arrlen(handles) - 1; i >= 0; i--) {
dlclose(handles[i]);
}
arrfree(handles);
return result;
}

19
mkcd.sh
View file

@ -20,11 +20,20 @@ ISO_PATH="$ISO_DIR/dvx.iso"
echo "Building DVX..."
make -C "$SCRIPT_DIR" all
# Verify build output exists
if [ ! -f "$SCRIPT_DIR/bin/dvx.exe" ]; then
echo "ERROR: bin/dvx.exe not found -- build failed?"
exit 1
fi
# Verify core build output exists
for f in dvx.exe libs/libtasks.lib libs/libdvx.lib libs/dvxshell.lib; do
if [ ! -f "$SCRIPT_DIR/bin/$f" ]; then
echo "ERROR: bin/$f not found -- build failed?"
exit 1
fi
done
# Verify widget DXEs exist
WGT_COUNT=0
for f in "$SCRIPT_DIR"/bin/widgets/*.wgt; do
[ -f "$f" ] && WGT_COUNT=$((WGT_COUNT + 1))
done
echo "$WGT_COUNT widget modules found in bin/widgets/."
# Create the ISO image
# -iso-level 1: strict 8.3 filenames (DOS compatibility)

69
shell/Makefile Normal file
View file

@ -0,0 +1,69 @@
# DVX Shell Makefile for DJGPP cross-compilation
#
# Builds dvxshell.lib -- the shell module loaded by the DVX loader.
DJGPP_PREFIX = $(HOME)/djgpp/djgpp
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
DXE3GEN = PATH=$(DJGPP_PREFIX)/bin:$(PATH) DJDIR=$(DJGPP_PREFIX)/i586-pc-msdosdjgpp $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/dxe3gen
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../core -I../core/platform -I../tasks -I../tasks/thirdparty
OBJDIR = ../obj/shell
LIBSDIR = ../bin/libs
CONFIGDIR = ../bin/config
THEMEDIR = ../bin/config/themes
WPAPERDIR = ../bin/config/wpaper
SRCS = shellMain.c shellApp.c shellExport.c shellInfo.c shellTaskMgr.c
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
TARGET = $(LIBSDIR)/dvxshell.lib
.PHONY: all clean
THEMES = $(THEMEDIR)/geos.thm $(THEMEDIR)/win31.thm $(THEMEDIR)/cde.thm
WPAPERS = $(WPAPERDIR)/blueglow.jpg $(WPAPERDIR)/swoop.jpg $(WPAPERDIR)/triangle.jpg
all: $(TARGET) $(LIBSDIR)/dvxshell.dep $(CONFIGDIR)/dvx.ini $(THEMES) $(WPAPERS)
$(LIBSDIR)/dvxshell.dep: ../config/dvxshell.dep | $(LIBSDIR)
sed 's/$$/\r/' $< > $@
$(TARGET): $(OBJS) | $(LIBSDIR)
$(DXE3GEN) -o $@ -E _shell -U $(OBJS)
$(CONFIGDIR)/dvx.ini: ../config/dvx.ini | $(CONFIGDIR)
sed 's/$$/\r/' $< > $@
$(OBJDIR)/%.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJDIR):
mkdir -p $(OBJDIR)
$(LIBSDIR):
mkdir -p $(LIBSDIR)
$(CONFIGDIR):
mkdir -p $(CONFIGDIR)
$(THEMEDIR):
mkdir -p $(THEMEDIR)
$(THEMEDIR)/%.thm: ../config/themes/%.thm | $(THEMEDIR)
sed 's/$$/\r/' $< > $@
$(WPAPERDIR):
mkdir -p $(WPAPERDIR)
$(WPAPERDIR)/%.jpg: ../config/wpaper/%.jpg | $(WPAPERDIR)
cp $< $@
# Dependencies
$(OBJDIR)/shellMain.o: shellMain.c shellApp.h
$(OBJDIR)/shellApp.o: shellApp.c shellApp.h
$(OBJDIR)/shellExport.o: shellExport.c shellApp.h
$(OBJDIR)/shellInfo.o: shellInfo.c shellInfo.h shellApp.h
$(OBJDIR)/shellTaskMgr.o: shellTaskMgr.c shellTaskMgr.h shellApp.h
clean:
rm -f $(OBJS) $(TARGET) $(LIBSDIR)/dvxshell.dep
rm -rf $(WPAPERDIR) $(THEMEDIR) $(CONFIGDIR)

View file

@ -5,7 +5,7 @@
#include "shellApp.h"
#include "dvxDialog.h"
#include "platform/dvxPlatform.h"
#include "dvxPlatform.h"
#include <dlfcn.h>
#include <errno.h>

126
shell/shellExport.c Normal file
View file

@ -0,0 +1,126 @@
// shellExport.c -- DXE wrapper overrides for resource tracking
//
// Registers three wrapper functions via dlregsym that override the
// real dvxCreateWindow, dvxCreateWindowCentered, and dvxDestroyWindow
// for all subsequently loaded app DXEs.
//
// The key mechanic: dlregsym takes precedence over RTLD_GLOBAL exports.
// Since libdvx.dxe (which has the real functions) was loaded before
// shellExportInit() registers these wrappers, libdvx.dxe keeps the
// real implementations. But any app DXE loaded afterward gets the
// wrappers, which add resource tracking (appId stamping, last-window
// reaping) transparently.
//
// All other symbol exports (dvx*, wgt*, platform*, libc) are handled
// by the DXE modules and the loader's dlregsym table -- they no longer
// need to be listed here.
#include "shellApp.h"
#include "dvxApp.h"
#include "dvxWm.h"
#include <sys/dxe.h>
// ============================================================
// Prototypes
// ============================================================
static WindowT *shellWrapCreateWindow(AppContextT *ctx, const char *title, int32_t x, int32_t y, int32_t w, int32_t h, bool resizable);
static WindowT *shellWrapCreateWindowCentered(AppContextT *ctx, const char *title, int32_t w, int32_t h, bool resizable);
static void shellWrapDestroyWindow(AppContextT *ctx, WindowT *win);
// ============================================================
// Wrapper: dvxCreateWindow -- stamps win->appId
// ============================================================
// The wrapper calls the real dvxCreateWindow (resolved from libdvx.dxe
// at shellcore load time), then tags the result with sCurrentAppId.
// This is how the shell knows which app owns which window, enabling
// per-app window cleanup on crash/termination.
static WindowT *shellWrapCreateWindow(AppContextT *ctx, const char *title, int32_t x, int32_t y, int32_t w, int32_t h, bool resizable) {
WindowT *win = dvxCreateWindow(ctx, title, x, y, w, h, resizable);
if (win) {
win->appId = sCurrentAppId;
}
return win;
}
// ============================================================
// Wrapper: dvxCreateWindowCentered -- stamps win->appId
// ============================================================
static WindowT *shellWrapCreateWindowCentered(AppContextT *ctx, const char *title, int32_t w, int32_t h, bool resizable) {
WindowT *win = dvxCreateWindowCentered(ctx, title, w, h, resizable);
if (win) {
win->appId = sCurrentAppId;
}
return win;
}
// ============================================================
// Wrapper: dvxDestroyWindow -- checks for last-window reap
// ============================================================
// Beyond just destroying the window, this wrapper implements the lifecycle
// rule for callback-only apps: when their last window closes, they're done.
// Main-loop apps manage their own lifetime (their task returns from
// appMain), so this check only applies to callback-only apps.
// The appId is captured before destruction because the window struct is
// freed by dvxDestroyWindow.
static void shellWrapDestroyWindow(AppContextT *ctx, WindowT *win) {
int32_t appId = win->appId;
dvxDestroyWindow(ctx, win);
// If this was a callback-only app's last window, mark for reaping
if (appId > 0) {
ShellAppT *app = shellGetApp(appId);
if (app && !app->hasMainLoop && app->state == AppStateRunningE) {
// Check if app still has any windows
bool hasWindows = false;
for (int32_t i = 0; i < ctx->stack.count; i++) {
if (ctx->stack.windows[i]->appId == appId) {
hasWindows = true;
break;
}
}
if (!hasWindows) {
app->state = AppStateTerminatingE;
}
}
}
}
// ============================================================
// Wrapper export table
// ============================================================
// Only three entries: the resource-tracking wrappers exported under
// the original function names. All other symbols come from the
// loaded DXE modules (via RTLD_GLOBAL) and the loader's dlregsym.
DXE_EXPORT_TABLE(sWrapperTable)
{ "_dvxCreateWindow", (void *)shellWrapCreateWindow },
{ "_dvxCreateWindowCentered", (void *)shellWrapCreateWindowCentered },
{ "_dvxDestroyWindow", (void *)shellWrapDestroyWindow },
DXE_EXPORT_END
// ============================================================
// shellExportInit
// ============================================================
// Register the wrapper overrides. Must be called before any
// shellLoadApp() -- wrappers only affect subsequently loaded DXEs.
void shellExportInit(void) {
dlregsym(sWrapperTable);
}

View file

@ -7,7 +7,7 @@
#include "shellInfo.h"
#include "shellApp.h"
#include "platform/dvxPlatform.h"
#include "dvxPlatform.h"
#include <string.h>

View file

@ -29,8 +29,8 @@
#include "shellTaskMgr.h"
#include "dvxDialog.h"
#include "dvxPrefs.h"
#include "platform/dvxPlatform.h"
#include "thirdparty/stb_ds.h"
#include "dvxPlatform.h"
#include "stb_ds.h"
#include <stdarg.h>
#include <setjmp.h>
@ -261,25 +261,12 @@ void shellUnregisterDesktopUpdate(void (*updateFn)(void)) {
// ============================================================
// main
// shellMain -- entry point called by the DVX loader
// ============================================================
int main(int argc, char *argv[]) {
int shellMain(int argc, char *argv[]) {
(void)argc;
// Change to the directory containing the executable so that relative
// paths (CONFIG/, APPS/, etc.) resolve correctly regardless of where
// the user launched from.
char exeDir[260];
strncpy(exeDir, argv[0], sizeof(exeDir) - 1);
exeDir[sizeof(exeDir) - 1] = '\0';
char *sep = platformPathDirEnd(exeDir);
if (sep) {
*sep = '\0';
platformChdir(exeDir);
}
(void)argv;
// Truncate the log file, then use append-per-write so the file
// isn't held open (allows Notepad to read it while the shell runs).

View file

@ -9,8 +9,8 @@
#include "dvxDialog.h"
#include "dvxWidget.h"
#include "dvxWm.h"
#include "platform/dvxPlatform.h"
#include "thirdparty/stb_ds.h"
#include "dvxPlatform.h"
#include "stb_ds.h"
#include <stdio.h>
#include <string.h>

View file

@ -1,41 +1,25 @@
# Cooperative Task Switching Library Makefile for DJGPP cross-compilation
#
# Builds libtasks.lib -- the task switching module loaded by the DVX loader.
DJGPP_PREFIX = $(HOME)/djgpp/djgpp
DJGPP_LIBPATH = $(HOME)/claude/windriver/tools/lib
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
AR = LD_LIBRARY_PATH=$(DJGPP_LIBPATH) $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-ar
RANLIB = LD_LIBRARY_PATH=$(DJGPP_LIBPATH) $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-ranlib
EXE2COFF = $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/exe2coff
CWSDSTUB = $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/CWSDSTUB.EXE
DXE3GEN = PATH=$(DJGPP_PREFIX)/bin:$(PATH) DJDIR=$(DJGPP_PREFIX)/i586-pc-msdosdjgpp $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/dxe3gen
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586
OBJDIR = ../obj/tasks
LIBDIR = ../lib
BINDIR = ../bin
OBJDIR = ../obj/tasks
LIBSDIR = ../bin/libs
SRCS = taskswitch.c
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
TARGET = $(LIBDIR)/libtasks.a
DEMO_SRCS = demo.c
DEMO_OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(DEMO_SRCS))
DEMO_TARGET = $(BINDIR)/tsdemo.exe
TARGET = $(LIBSDIR)/libtasks.lib
.PHONY: all clean
all: $(TARGET)
demo: $(DEMO_TARGET)
$(TARGET): $(OBJS) | $(LIBDIR)
$(AR) rcs $@ $(OBJS)
$(RANLIB) $@
$(DEMO_TARGET): $(DEMO_OBJS) $(TARGET) | $(BINDIR)
$(CC) $(CFLAGS) -o $@ $(DEMO_OBJS) -L$(LIBDIR) -ltasks
$(EXE2COFF) $@
cat $(CWSDSTUB) $(BINDIR)/tsdemo > $@
rm -f $(BINDIR)/tsdemo
$(TARGET): $(OBJS) | $(LIBSDIR)
$(DXE3GEN) -o $@ -E _ts -E _stbds_ -U $(OBJS)
$(OBJDIR)/%.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS) -c -o $@ $<
@ -43,15 +27,11 @@ $(OBJDIR)/%.o: %.c | $(OBJDIR)
$(OBJDIR):
mkdir -p $(OBJDIR)
$(LIBDIR):
mkdir -p $(LIBDIR)
$(BINDIR):
mkdir -p $(BINDIR)
$(LIBSDIR):
mkdir -p $(LIBSDIR)
# Dependencies
$(OBJDIR)/taskswitch.o: taskswitch.c taskswitch.h thirdparty/stb_ds.h
$(OBJDIR)/demo.o: demo.c taskswitch.h
clean:
rm -f $(OBJS) $(DEMO_OBJS) $(TARGET) $(DEMO_TARGET)
rm -f $(OBJS) $(TARGET)

147
widgets/Makefile Normal file
View file

@ -0,0 +1,147 @@
# DVX Widget Modules Makefile for DJGPP cross-compilation
#
# Builds individual .wgt modules from widget source files.
# Each .wgt is a DXE loaded by the loader at startup.
DJGPP_PREFIX = $(HOME)/djgpp/djgpp
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
DXE3GEN = PATH=$(DJGPP_PREFIX)/bin:$(PATH) DJDIR=$(DJGPP_PREFIX)/i586-pc-msdosdjgpp $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/dxe3gen
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../core -I../core/platform -I../tasks -I../tasks/thirdparty
OBJDIR = ../obj/widgets
WGTDIR = ../bin/widgets
SRCS = widgetAnsiTerm.c \
widgetBox.c \
widgetButton.c \
widgetCanvas.c \
widgetCheckbox.c \
widgetComboBox.c \
widgetDropdown.c \
widgetImage.c \
widgetImageButton.c \
widgetLabel.c \
widgetListBox.c \
widgetListView.c \
widgetProgressBar.c \
widgetRadio.c \
widgetScrollPane.c \
widgetSeparator.c \
widgetSlider.c \
widgetSpacer.c \
widgetSpinner.c \
widgetSplitter.c \
widgetStatusBar.c \
widgetTabControl.c \
widgetTextInput.c \
widgetTimer.c \
widgetToolbar.c \
widgetTreeView.c
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
WGT_MODS = $(WGTDIR)/box.wgt \
$(WGTDIR)/button.wgt \
$(WGTDIR)/canvas.wgt \
$(WGTDIR)/checkbox.wgt \
$(WGTDIR)/combobox.wgt \
$(WGTDIR)/dropdown.wgt \
$(WGTDIR)/imgbtn.wgt \
$(WGTDIR)/image.wgt \
$(WGTDIR)/label.wgt \
$(WGTDIR)/listbox.wgt \
$(WGTDIR)/listview.wgt \
$(WGTDIR)/progress.wgt \
$(WGTDIR)/radio.wgt \
$(WGTDIR)/scrlpane.wgt \
$(WGTDIR)/separatr.wgt \
$(WGTDIR)/slider.wgt \
$(WGTDIR)/spacer.wgt \
$(WGTDIR)/spinner.wgt \
$(WGTDIR)/splitter.wgt \
$(WGTDIR)/statbar.wgt \
$(WGTDIR)/tabctrl.wgt \
$(WGTDIR)/terminal.wgt \
$(WGTDIR)/textinpt.wgt \
$(WGTDIR)/timer.wgt \
$(WGTDIR)/toolbar.wgt \
$(WGTDIR)/treeview.wgt
.PHONY: all clean
all: $(WGT_MODS)
# Generic widget module rule: -E _wgt exports all public API + wgtRegister
$(WGTDIR)/%.wgt: | $(WGTDIR)
$(DXE3GEN) -o $@ -E _wgt -U $<
# Map .wgt name to .o file
$(WGTDIR)/box.wgt: $(OBJDIR)/widgetBox.o
$(WGTDIR)/button.wgt: $(OBJDIR)/widgetButton.o
$(WGTDIR)/canvas.wgt: $(OBJDIR)/widgetCanvas.o
$(WGTDIR)/checkbox.wgt: $(OBJDIR)/widgetCheckbox.o
$(WGTDIR)/combobox.wgt: $(OBJDIR)/widgetComboBox.o
$(WGTDIR)/dropdown.wgt: $(OBJDIR)/widgetDropdown.o
$(WGTDIR)/imgbtn.wgt: $(OBJDIR)/widgetImageButton.o
$(WGTDIR)/image.wgt: $(OBJDIR)/widgetImage.o
$(WGTDIR)/label.wgt: $(OBJDIR)/widgetLabel.o
$(WGTDIR)/listbox.wgt: $(OBJDIR)/widgetListBox.o
$(WGTDIR)/listview.wgt: $(OBJDIR)/widgetListView.o
$(WGTDIR)/progress.wgt: $(OBJDIR)/widgetProgressBar.o
$(WGTDIR)/radio.wgt: $(OBJDIR)/widgetRadio.o
$(WGTDIR)/scrlpane.wgt: $(OBJDIR)/widgetScrollPane.o
$(WGTDIR)/separatr.wgt: $(OBJDIR)/widgetSeparator.o
$(WGTDIR)/slider.wgt: $(OBJDIR)/widgetSlider.o
$(WGTDIR)/spacer.wgt: $(OBJDIR)/widgetSpacer.o
$(WGTDIR)/spinner.wgt: $(OBJDIR)/widgetSpinner.o
$(WGTDIR)/splitter.wgt: $(OBJDIR)/widgetSplitter.o
$(WGTDIR)/statbar.wgt: $(OBJDIR)/widgetStatusBar.o
$(WGTDIR)/tabctrl.wgt: $(OBJDIR)/widgetTabControl.o
$(WGTDIR)/terminal.wgt: $(OBJDIR)/widgetAnsiTerm.o
$(WGTDIR)/textinpt.wgt: $(OBJDIR)/widgetTextInput.o
$(WGTDIR)/timer.wgt: $(OBJDIR)/widgetTimer.o
$(WGTDIR)/toolbar.wgt: $(OBJDIR)/widgetToolbar.o
$(WGTDIR)/treeview.wgt: $(OBJDIR)/widgetTreeView.o
# Compile
$(OBJDIR)/%.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJDIR):
mkdir -p $(OBJDIR)
$(WGTDIR):
mkdir -p $(WGTDIR)
# Dependencies
WIDGET_DEPS = ../core/widgetInternal.h ../core/dvxWidget.h ../core/dvxTypes.h ../core/dvxApp.h ../core/dvxDraw.h ../core/dvxWm.h ../core/dvxVideo.h
$(OBJDIR)/widgetAnsiTerm.o: widgetAnsiTerm.c $(WIDGET_DEPS)
$(OBJDIR)/widgetBox.o: widgetBox.c $(WIDGET_DEPS)
$(OBJDIR)/widgetButton.o: widgetButton.c $(WIDGET_DEPS)
$(OBJDIR)/widgetCanvas.o: widgetCanvas.c $(WIDGET_DEPS) ../core/thirdparty/stb_image.h ../core/thirdparty/stb_image_write.h
$(OBJDIR)/widgetCheckbox.o: widgetCheckbox.c $(WIDGET_DEPS)
$(OBJDIR)/widgetComboBox.o: widgetComboBox.c $(WIDGET_DEPS)
$(OBJDIR)/widgetDropdown.o: widgetDropdown.c $(WIDGET_DEPS)
$(OBJDIR)/widgetImage.o: widgetImage.c $(WIDGET_DEPS) ../core/thirdparty/stb_image.h
$(OBJDIR)/widgetImageButton.o: widgetImageButton.c $(WIDGET_DEPS)
$(OBJDIR)/widgetLabel.o: widgetLabel.c $(WIDGET_DEPS)
$(OBJDIR)/widgetListBox.o: widgetListBox.c $(WIDGET_DEPS)
$(OBJDIR)/widgetListView.o: widgetListView.c $(WIDGET_DEPS)
$(OBJDIR)/widgetProgressBar.o: widgetProgressBar.c $(WIDGET_DEPS)
$(OBJDIR)/widgetRadio.o: widgetRadio.c $(WIDGET_DEPS)
$(OBJDIR)/widgetScrollPane.o: widgetScrollPane.c $(WIDGET_DEPS)
$(OBJDIR)/widgetSeparator.o: widgetSeparator.c $(WIDGET_DEPS)
$(OBJDIR)/widgetSlider.o: widgetSlider.c $(WIDGET_DEPS)
$(OBJDIR)/widgetSpacer.o: widgetSpacer.c $(WIDGET_DEPS)
$(OBJDIR)/widgetSpinner.o: widgetSpinner.c $(WIDGET_DEPS)
$(OBJDIR)/widgetSplitter.o: widgetSplitter.c $(WIDGET_DEPS)
$(OBJDIR)/widgetStatusBar.o: widgetStatusBar.c $(WIDGET_DEPS)
$(OBJDIR)/widgetTabControl.o: widgetTabControl.c $(WIDGET_DEPS)
$(OBJDIR)/widgetTextInput.o: widgetTextInput.c $(WIDGET_DEPS)
$(OBJDIR)/widgetTimer.o: widgetTimer.c $(WIDGET_DEPS)
$(OBJDIR)/widgetToolbar.o: widgetToolbar.c $(WIDGET_DEPS)
$(OBJDIR)/widgetTreeView.o: widgetTreeView.c $(WIDGET_DEPS)
clean:
rm -f $(OBJS) $(WGT_MODS)
-rmdir $(WGTDIR) 2>/dev/null

View file

@ -2141,3 +2141,26 @@ void widgetAnsiTermPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
drawFocusRect(d, ops, w->x + 1, w->y + 1, w->w - 2, w->h - 2, colors->contentFg);
}
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassAnsiTerm = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetAnsiTermPaint,
.paintOverlay = NULL,
.calcMinSize = widgetAnsiTermCalcMinSize,
.layout = NULL,
.onMouse = widgetAnsiTermOnMouse,
.onKey = widgetAnsiTermOnKey,
.destroy = widgetAnsiTermDestroy,
.getText = NULL,
.setText = NULL
};
void wgtRegister(void) {
widgetClassTable[WidgetAnsiTermE] = &sClassAnsiTerm;
}

View file

@ -131,3 +131,54 @@ WidgetT *wgtHBox(WidgetT *parent) {
WidgetT *wgtVBox(WidgetT *parent) {
return widgetAlloc(parent, WidgetVBoxE);
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassVBox = {
.flags = WCLASS_BOX_CONTAINER,
.paint = NULL,
.paintOverlay = NULL,
.calcMinSize = NULL,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassHBox = {
.flags = WCLASS_BOX_CONTAINER | WCLASS_HORIZ_CONTAINER,
.paint = NULL,
.paintOverlay = NULL,
.calcMinSize = NULL,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassFrame = {
.flags = WCLASS_BOX_CONTAINER,
.paint = widgetFramePaint,
.paintOverlay = NULL,
.calcMinSize = NULL,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
void wgtRegister(void) {
widgetClassTable[WidgetVBoxE] = &sClassVBox;
widgetClassTable[WidgetHBoxE] = &sClassHBox;
widgetClassTable[WidgetFrameE] = &sClassFrame;
}

View file

@ -141,3 +141,26 @@ void widgetButtonPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitma
drawFocusRect(d, ops, w->x + BUTTON_FOCUS_INSET + off, w->y + BUTTON_FOCUS_INSET + off, w->w - BUTTON_FOCUS_INSET * 2, w->h - BUTTON_FOCUS_INSET * 2, fg);
}
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassButton = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetButtonPaint,
.paintOverlay = NULL,
.calcMinSize = widgetButtonCalcMinSize,
.layout = NULL,
.onMouse = widgetButtonOnMouse,
.onKey = widgetButtonOnKey,
.destroy = NULL,
.getText = widgetButtonGetText,
.setText = widgetButtonSetText
};
void wgtRegister(void) {
widgetClassTable[WidgetButtonE] = &sClassButton;
}

View file

@ -724,3 +724,26 @@ void widgetCanvasPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitma
w->as.canvas.data, w->as.canvas.canvasPitch,
0, 0, imgW, imgH);
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassCanvas = {
.flags = 0,
.paint = widgetCanvasPaint,
.paintOverlay = NULL,
.calcMinSize = widgetCanvasCalcMinSize,
.layout = NULL,
.onMouse = widgetCanvasOnMouse,
.onKey = NULL,
.destroy = widgetCanvasDestroy,
.getText = NULL,
.setText = NULL
};
void wgtRegister(void) {
widgetClassTable[WidgetCanvasE] = &sClassCanvas;
}

View file

@ -150,3 +150,26 @@ void widgetCheckboxPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
drawFocusRect(d, ops, labelX - 1, labelY - 1, labelW + 2, font->charHeight + 2, fg);
}
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassCheckbox = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetCheckboxPaint,
.paintOverlay = NULL,
.calcMinSize = widgetCheckboxCalcMinSize,
.layout = NULL,
.onMouse = widgetCheckboxOnMouse,
.onKey = widgetCheckboxOnKey,
.destroy = NULL,
.getText = widgetCheckboxGetText,
.setText = widgetCheckboxSetText
};
void wgtRegister(void) {
widgetClassTable[WidgetCheckboxE] = &sClassCheckbox;
}

View file

@ -370,3 +370,26 @@ void widgetComboBoxSetText(WidgetT *w, const char *text) {
w->as.comboBox.selEnd = -1;
}
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassComboBox = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetComboBoxPaint,
.paintOverlay = widgetComboBoxPaintPopup,
.calcMinSize = widgetComboBoxCalcMinSize,
.layout = NULL,
.onMouse = widgetComboBoxOnMouse,
.onKey = widgetComboBoxOnKey,
.destroy = widgetComboBoxDestroy,
.getText = widgetComboBoxGetText,
.setText = widgetComboBoxSetText
};
void wgtRegister(void) {
widgetClassTable[WidgetComboBoxE] = &sClassComboBox;
}

View file

@ -274,3 +274,26 @@ void widgetDropdownPaintPopup(WidgetT *w, DisplayT *d, const BlitOpsT *ops, cons
widgetDropdownPopupRect(w, font, d->clipH, &popX, &popY, &popW, &popH);
widgetPaintPopupList(d, ops, font, colors, popX, popY, popW, popH, w->as.dropdown.items, w->as.dropdown.itemCount, w->as.dropdown.hoverIdx, w->as.dropdown.scrollPos);
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassDropdown = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetDropdownPaint,
.paintOverlay = widgetDropdownPaintPopup,
.calcMinSize = widgetDropdownCalcMinSize,
.layout = NULL,
.onMouse = widgetDropdownOnMouse,
.onKey = widgetDropdownOnKey,
.destroy = NULL,
.getText = widgetDropdownGetText,
.setText = NULL
};
void wgtRegister(void) {
widgetClassTable[WidgetDropdownE] = &sClassDropdown;
}

View file

@ -160,3 +160,26 @@ void widgetImagePaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitmap
w->as.image.data, w->as.image.imgPitch,
0, 0, imgW, imgH);
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassImage = {
.flags = 0,
.paint = widgetImagePaint,
.paintOverlay = NULL,
.calcMinSize = widgetImageCalcMinSize,
.layout = NULL,
.onMouse = widgetImageOnMouse,
.onKey = NULL,
.destroy = widgetImageDestroy,
.getText = NULL,
.setText = NULL
};
void wgtRegister(void) {
widgetClassTable[WidgetImageE] = &sClassImage;
}

View file

@ -179,3 +179,26 @@ void widgetImageButtonPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const
drawFocusRect(d, ops, w->x + 3 + off, w->y + 3 + off, w->w - 6, w->h - 6, fg);
}
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassImageButton = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetImageButtonPaint,
.paintOverlay = NULL,
.calcMinSize = widgetImageButtonCalcMinSize,
.layout = NULL,
.onMouse = widgetImageButtonOnMouse,
.onKey = widgetImageButtonOnKey,
.destroy = widgetImageButtonDestroy,
.getText = NULL,
.setText = NULL
};
void wgtRegister(void) {
widgetClassTable[WidgetImageButtonE] = &sClassImageButton;
}

View file

@ -99,3 +99,26 @@ void wgtLabelSetAlign(WidgetT *w, WidgetAlignE align) {
w->as.label.textAlign = align;
}
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassLabel = {
.flags = 0,
.paint = widgetLabelPaint,
.paintOverlay = NULL,
.calcMinSize = widgetLabelCalcMinSize,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = widgetLabelGetText,
.setText = widgetLabelSetText
};
void wgtRegister(void) {
widgetClassTable[WidgetLabelE] = &sClassLabel;
}

View file

@ -636,3 +636,26 @@ void widgetListBoxPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitm
drawFocusRect(d, ops, w->x + 1, w->y + 1, w->w - 2, w->h - 2, fg);
}
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassListBox = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetListBoxPaint,
.paintOverlay = NULL,
.calcMinSize = widgetListBoxCalcMinSize,
.layout = NULL,
.onMouse = widgetListBoxOnMouse,
.onKey = widgetListBoxOnKey,
.destroy = widgetListBoxDestroy,
.getText = NULL,
.setText = NULL
};
void wgtRegister(void) {
widgetClassTable[WidgetListBoxE] = &sClassListBox;
}

View file

@ -1380,3 +1380,27 @@ void widgetListViewPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
drawFocusRect(d, ops, w->x + 1, w->y + 1, w->w - 2, w->h - 2, fg);
}
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassListView = {
.flags = WCLASS_FOCUSABLE | WCLASS_NO_HIT_RECURSE,
.paint = widgetListViewPaint,
.paintOverlay = NULL,
.calcMinSize = widgetListViewCalcMinSize,
.layout = NULL,
.onMouse = widgetListViewOnMouse,
.onKey = widgetListViewOnKey,
.destroy = widgetListViewDestroy,
.getText = NULL,
.setText = NULL
};
void wgtRegister(void) {
widgetClassTable[WidgetListViewE] = &sClassListView;
sListViewColBorderHitFn = widgetListViewColBorderHit;
}

View file

@ -159,3 +159,26 @@ void widgetProgressBarPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const
}
}
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassProgressBar = {
.flags = 0,
.paint = widgetProgressBarPaint,
.paintOverlay = NULL,
.calcMinSize = widgetProgressBarCalcMinSize,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
void wgtRegister(void) {
widgetClassTable[WidgetProgressBarE] = &sClassProgressBar;
}

View file

@ -280,3 +280,40 @@ void widgetRadioPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitmap
drawFocusRect(d, ops, labelX - 1, labelY - 1, labelW + 2, font->charHeight + 2, fg);
}
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassRadioGroup = {
.flags = WCLASS_BOX_CONTAINER,
.paint = NULL,
.paintOverlay = NULL,
.calcMinSize = NULL,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassRadio = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetRadioPaint,
.paintOverlay = NULL,
.calcMinSize = widgetRadioCalcMinSize,
.layout = NULL,
.onMouse = widgetRadioOnMouse,
.onKey = widgetRadioOnKey,
.destroy = NULL,
.getText = widgetRadioGetText,
.setText = widgetRadioSetText
};
void wgtRegister(void) {
widgetClassTable[WidgetRadioGroupE] = &sClassRadioGroup;
widgetClassTable[WidgetRadioE] = &sClassRadio;
}

View file

@ -698,3 +698,26 @@ WidgetT *wgtScrollPane(WidgetT *parent) {
return w;
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassScrollPane = {
.flags = WCLASS_PAINTS_CHILDREN | WCLASS_NO_HIT_RECURSE,
.paint = widgetScrollPanePaint,
.paintOverlay = NULL,
.calcMinSize = widgetScrollPaneCalcMinSize,
.layout = widgetScrollPaneLayout,
.onMouse = widgetScrollPaneOnMouse,
.onKey = widgetScrollPaneOnKey,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
void wgtRegister(void) {
widgetClassTable[WidgetScrollPaneE] = &sClassScrollPane;
}

View file

@ -84,3 +84,26 @@ void widgetSeparatorPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bi
drawHLine(d, ops, w->x, cy + 1, w->w, colors->windowHighlight);
}
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassSeparator = {
.flags = 0,
.paint = widgetSeparatorPaint,
.paintOverlay = NULL,
.calcMinSize = widgetSeparatorCalcMinSize,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
void wgtRegister(void) {
widgetClassTable[WidgetSeparatorE] = &sClassSeparator;
}

View file

@ -304,3 +304,26 @@ void widgetSliderPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitma
drawFocusRect(d, ops, w->x, w->y, w->w, w->h, fg);
}
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassSlider = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetSliderPaint,
.paintOverlay = NULL,
.calcMinSize = widgetSliderCalcMinSize,
.layout = NULL,
.onMouse = widgetSliderOnMouse,
.onKey = widgetSliderOnKey,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
void wgtRegister(void) {
widgetClassTable[WidgetSliderE] = &sClassSlider;
}

View file

@ -39,3 +39,26 @@ void widgetSpacerCalcMinSize(WidgetT *w, const BitmapFontT *font) {
w->calcMinW = 0;
w->calcMinH = 0;
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassSpacer = {
.flags = 0,
.paint = NULL,
.paintOverlay = NULL,
.calcMinSize = widgetSpacerCalcMinSize,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
void wgtRegister(void) {
widgetClassTable[WidgetSpacerE] = &sClassSpacer;
}

View file

@ -519,3 +519,26 @@ void wgtSpinnerSetValue(WidgetT *w, int32_t value) {
spinnerClampAndFormat(w);
wgtInvalidate(w);
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassSpinner = {
.flags = WCLASS_FOCUSABLE,
.paint = widgetSpinnerPaint,
.paintOverlay = NULL,
.calcMinSize = widgetSpinnerCalcMinSize,
.layout = NULL,
.onMouse = widgetSpinnerOnMouse,
.onKey = widgetSpinnerOnKey,
.destroy = NULL,
.getText = widgetSpinnerGetText,
.setText = widgetSpinnerSetText
};
void wgtRegister(void) {
widgetClassTable[WidgetSpinnerE] = &sClassSpinner;
}

View file

@ -387,3 +387,27 @@ void wgtSplitterSetPos(WidgetT *w, int32_t pos) {
w->as.splitter.dividerPos = pos;
wgtInvalidate(w);
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassSplitter = {
.flags = WCLASS_PAINTS_CHILDREN | WCLASS_NO_HIT_RECURSE,
.paint = widgetSplitterPaint,
.paintOverlay = NULL,
.calcMinSize = widgetSplitterCalcMinSize,
.layout = widgetSplitterLayout,
.onMouse = widgetSplitterOnMouse,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
void wgtRegister(void) {
widgetClassTable[WidgetSplitterE] = &sClassSplitter;
sSplitterClampPosFn = widgetSplitterClampPos;
}

View file

@ -61,3 +61,26 @@ void widgetStatusBarPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bi
drawBevel(d, ops, c->x - 1, c->y - 1, c->w + 2, c->h + 2, &bevel);
}
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassStatusBar = {
.flags = WCLASS_BOX_CONTAINER | WCLASS_HORIZ_CONTAINER,
.paint = widgetStatusBarPaint,
.paintOverlay = NULL,
.calcMinSize = NULL,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
void wgtRegister(void) {
widgetClassTable[WidgetStatusBarE] = &sClassStatusBar;
}

View file

@ -558,3 +558,40 @@ void widgetTabControlPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const B
tabIdx++;
}
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassTabControl = {
.flags = WCLASS_FOCUSABLE | WCLASS_PAINTS_CHILDREN,
.paint = widgetTabControlPaint,
.paintOverlay = NULL,
.calcMinSize = widgetTabControlCalcMinSize,
.layout = widgetTabControlLayout,
.onMouse = widgetTabControlOnMouse,
.onKey = widgetTabControlOnKey,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassTabPage = {
.flags = WCLASS_BOX_CONTAINER,
.paint = NULL,
.paintOverlay = NULL,
.calcMinSize = NULL,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
void wgtRegister(void) {
widgetClassTable[WidgetTabControlE] = &sClassTabControl;
widgetClassTable[WidgetTabPageE] = &sClassTabPage;
}

File diff suppressed because it is too large Load diff

View file

@ -12,7 +12,7 @@
#include <time.h>
#include "thirdparty/stb_ds.h"
#include "stb_ds.h"
// Active timer list -- avoids walking the widget tree per frame
@ -182,3 +182,26 @@ void wgtUpdateTimers(void) {
}
}
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassTimer = {
.flags = 0,
.paint = NULL,
.paintOverlay = NULL,
.calcMinSize = widgetTimerCalcMinSize,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = widgetTimerDestroy,
.getText = NULL,
.setText = NULL
};
void wgtRegister(void) {
widgetClassTable[WidgetTimerE] = &sClassTimer;
}

View file

@ -52,3 +52,26 @@ void widgetToolbarPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitm
bevel.width = 1;
drawBevel(d, ops, w->x, w->y, w->w, w->h, &bevel);
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassToolbar = {
.flags = WCLASS_BOX_CONTAINER | WCLASS_HORIZ_CONTAINER,
.paint = widgetToolbarPaint,
.paintOverlay = NULL,
.calcMinSize = NULL,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
void wgtRegister(void) {
widgetClassTable[WidgetToolbarE] = &sClassToolbar;
}

View file

@ -1386,3 +1386,41 @@ void widgetTreeViewPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
drawFocusRect(d, ops, w->x + 1, w->y + 1, w->w - 2, w->h - 2, fg);
}
}
// ============================================================
// DXE registration
// ============================================================
static const WidgetClassT sClassTreeView = {
.flags = WCLASS_FOCUSABLE | WCLASS_PAINTS_CHILDREN | WCLASS_NO_HIT_RECURSE,
.paint = widgetTreeViewPaint,
.paintOverlay = NULL,
.calcMinSize = widgetTreeViewCalcMinSize,
.layout = widgetTreeViewLayout,
.onMouse = widgetTreeViewOnMouse,
.onKey = widgetTreeViewOnKey,
.destroy = NULL,
.getText = NULL,
.setText = NULL
};
static const WidgetClassT sClassTreeItem = {
.flags = 0,
.paint = NULL,
.paintOverlay = NULL,
.calcMinSize = NULL,
.layout = NULL,
.onMouse = NULL,
.onKey = NULL,
.destroy = NULL,
.getText = widgetTreeItemGetText,
.setText = widgetTreeItemSetText
};
void wgtRegister(void) {
widgetClassTable[WidgetTreeViewE] = &sClassTreeView;
widgetClassTable[WidgetTreeItemE] = &sClassTreeItem;
sTreeViewNextVisibleFn = widgetTreeViewNextVisible;
}