Dynamic help recompilation. Reorganization for third-party developers.
This commit is contained in:
parent
10ba408465
commit
c43c586f2f
72 changed files with 3559 additions and 263 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -8,3 +8,4 @@ lib/
|
|||
*.SWP
|
||||
.claude/
|
||||
capture/
|
||||
just-stuff/
|
||||
|
|
|
|||
24
Makefile
24
Makefile
|
|
@ -3,9 +3,9 @@
|
|||
# Builds the full DVX stack: core library, task switcher,
|
||||
# bootstrap loader, text help library, widgets, shell, and apps.
|
||||
|
||||
.PHONY: all clean core tasks loader texthelp listhelp widgets shell taskmgr serial sql apps tools
|
||||
.PHONY: all clean core tasks loader texthelp listhelp widgets shell taskmgr serial sql apps tools deploy-helpsrc compile-help
|
||||
|
||||
all: core tasks loader texthelp listhelp tools widgets shell taskmgr serial sql apps
|
||||
all: core tasks loader texthelp listhelp tools widgets shell taskmgr serial sql apps deploy-helpsrc compile-help
|
||||
|
||||
core:
|
||||
$(MAKE) -C core
|
||||
|
|
@ -43,6 +43,26 @@ tools:
|
|||
apps: core tasks shell tools
|
||||
$(MAKE) -C apps
|
||||
|
||||
deploy-helpsrc:
|
||||
$(MAKE) -C tools deploy-helpsrc
|
||||
|
||||
HLPC = bin/dvxhlpc
|
||||
|
||||
compile-help:
|
||||
$(HLPC) -o bin/apps/kpunch/dvxhelp/dvxhelp.hlp apps/dvxhelp/help.dhs
|
||||
$(HLPC) -o bin/apps/kpunch/progman/dvxhelp.hlp \
|
||||
core/arch.dhs core/apiref.dhs \
|
||||
tasks/libtasks.dhs shell/dvxshell.dhs sql/dvxsql.dhs \
|
||||
texthelp/texthelp.dhs listhelp/listhelp.dhs \
|
||||
taskmgr/taskmgr.dhs serial/serial.dhs \
|
||||
apps/dvxbasic/basrt.dhs \
|
||||
widgets/wgtsys.dhs \
|
||||
$$(find widgets -name "*.dhs" ! -path "widgets/wgtsys.dhs" | sort)
|
||||
$(HLPC) -o bin/apps/kpunch/dvxbasic/dvxbasic.hlp \
|
||||
apps/dvxbasic/ideguide.dhs apps/dvxbasic/langref.dhs \
|
||||
apps/dvxbasic/ctrlover.dhs apps/dvxbasic/form.dhs \
|
||||
$$(find widgets -name "*.bhs" | sort)
|
||||
|
||||
clean:
|
||||
$(MAKE) -C core clean
|
||||
$(MAKE) -C tasks clean
|
||||
|
|
|
|||
|
|
@ -20,39 +20,39 @@ all: $(APPS) dvxbasic
|
|||
dvxbasic:
|
||||
$(MAKE) -C dvxbasic
|
||||
|
||||
cpanel: $(BINDIR)/cpanel/cpanel.app
|
||||
imgview: $(BINDIR)/imgview/imgview.app
|
||||
progman: $(BINDIR)/progman/progman.app
|
||||
notepad: $(BINDIR)/notepad/notepad.app
|
||||
clock: $(BINDIR)/clock/clock.app
|
||||
dvxdemo: $(BINDIR)/dvxdemo/dvxdemo.app
|
||||
dvxhelp: $(BINDIR)/dvxhelp/dvxhelp.app
|
||||
cpanel: $(BINDIR)/kpunch/cpanel/cpanel.app
|
||||
imgview: $(BINDIR)/kpunch/imgview/imgview.app
|
||||
progman: $(BINDIR)/kpunch/progman/progman.app
|
||||
notepad: $(BINDIR)/kpunch/notepad/notepad.app
|
||||
clock: $(BINDIR)/kpunch/clock/clock.app
|
||||
dvxdemo: $(BINDIR)/kpunch/dvxdemo/dvxdemo.app
|
||||
dvxhelp: $(BINDIR)/kpunch/dvxhelp/dvxhelp.app
|
||||
|
||||
$(BINDIR)/cpanel/cpanel.app: $(OBJDIR)/cpanel.o cpanel/cpanel.res cpanel/icon32.bmp | $(BINDIR)/cpanel
|
||||
$(BINDIR)/kpunch/cpanel/cpanel.app: $(OBJDIR)/cpanel.o cpanel/cpanel.res cpanel/icon32.bmp | $(BINDIR)/kpunch/cpanel
|
||||
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $<
|
||||
$(DVXRES) build $@ cpanel/cpanel.res
|
||||
|
||||
$(BINDIR)/imgview/imgview.app: $(OBJDIR)/imgview.o imgview/imgview.res imgview/icon32.bmp | $(BINDIR)/imgview
|
||||
$(BINDIR)/kpunch/imgview/imgview.app: $(OBJDIR)/imgview.o imgview/imgview.res imgview/icon32.bmp | $(BINDIR)/kpunch/imgview
|
||||
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $<
|
||||
$(DVXRES) build $@ imgview/imgview.res
|
||||
|
||||
$(BINDIR)/progman/progman.app: $(OBJDIR)/progman.o | $(BINDIR)/progman
|
||||
$(BINDIR)/kpunch/progman/progman.app: $(OBJDIR)/progman.o | $(BINDIR)/kpunch/progman
|
||||
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $<
|
||||
|
||||
$(BINDIR)/notepad/notepad.app: $(OBJDIR)/notepad.o notepad/notepad.res notepad/icon32.bmp | $(BINDIR)/notepad
|
||||
$(BINDIR)/kpunch/notepad/notepad.app: $(OBJDIR)/notepad.o notepad/notepad.res notepad/icon32.bmp | $(BINDIR)/kpunch/notepad
|
||||
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $<
|
||||
$(DVXRES) build $@ notepad/notepad.res
|
||||
|
||||
$(BINDIR)/clock/clock.app: $(OBJDIR)/clock.o clock/clock.res clock/icon32.bmp | $(BINDIR)/clock
|
||||
$(BINDIR)/kpunch/clock/clock.app: $(OBJDIR)/clock.o clock/clock.res clock/icon32.bmp | $(BINDIR)/kpunch/clock
|
||||
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -E _appShutdown -U $<
|
||||
$(DVXRES) build $@ clock/clock.res
|
||||
|
||||
DVXDEMO_BMPS = logo.bmp new.bmp open.bmp sample.bmp save.bmp
|
||||
|
||||
$(BINDIR)/dvxdemo/dvxdemo.app: $(OBJDIR)/dvxdemo.o $(addprefix dvxdemo/,$(DVXDEMO_BMPS)) dvxdemo/dvxdemo.res dvxdemo/icon32.bmp | $(BINDIR)/dvxdemo
|
||||
$(BINDIR)/kpunch/dvxdemo/dvxdemo.app: $(OBJDIR)/dvxdemo.o $(addprefix dvxdemo/,$(DVXDEMO_BMPS)) dvxdemo/dvxdemo.res dvxdemo/icon32.bmp | $(BINDIR)/kpunch/dvxdemo
|
||||
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $<
|
||||
$(DVXRES) build $@ dvxdemo/dvxdemo.res
|
||||
cp $(addprefix dvxdemo/,$(DVXDEMO_BMPS)) $(BINDIR)/dvxdemo/
|
||||
cp $(addprefix dvxdemo/,$(DVXDEMO_BMPS)) $(BINDIR)/kpunch/dvxdemo/
|
||||
|
||||
$(OBJDIR)/cpanel.o: cpanel/cpanel.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
|
@ -72,7 +72,7 @@ $(OBJDIR)/clock.o: clock/clock.c | $(OBJDIR)
|
|||
$(OBJDIR)/dvxdemo.o: dvxdemo/dvxdemo.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(BINDIR)/dvxhelp/dvxhelp.app: $(OBJDIR)/dvxhelp.o dvxhelp/dvxhelp.res dvxhelp/icon32.bmp | $(BINDIR)/dvxhelp
|
||||
$(BINDIR)/kpunch/dvxhelp/dvxhelp.app: $(OBJDIR)/dvxhelp.o dvxhelp/dvxhelp.res dvxhelp/icon32.bmp | $(BINDIR)/kpunch/dvxhelp
|
||||
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $<
|
||||
$(DVXRES) build $@ dvxhelp/dvxhelp.res
|
||||
|
||||
|
|
@ -82,26 +82,26 @@ $(OBJDIR)/dvxhelp.o: dvxhelp/dvxhelp.c dvxhelp/hlpformat.h | $(OBJDIR)
|
|||
$(OBJDIR):
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
$(BINDIR)/cpanel:
|
||||
mkdir -p $(BINDIR)/cpanel
|
||||
$(BINDIR)/kpunch/cpanel:
|
||||
mkdir -p $(BINDIR)/kpunch/cpanel
|
||||
|
||||
$(BINDIR)/imgview:
|
||||
mkdir -p $(BINDIR)/imgview
|
||||
$(BINDIR)/kpunch/imgview:
|
||||
mkdir -p $(BINDIR)/kpunch/imgview
|
||||
|
||||
$(BINDIR)/progman:
|
||||
mkdir -p $(BINDIR)/progman
|
||||
$(BINDIR)/kpunch/progman:
|
||||
mkdir -p $(BINDIR)/kpunch/progman
|
||||
|
||||
$(BINDIR)/notepad:
|
||||
mkdir -p $(BINDIR)/notepad
|
||||
$(BINDIR)/kpunch/notepad:
|
||||
mkdir -p $(BINDIR)/kpunch/notepad
|
||||
|
||||
$(BINDIR)/clock:
|
||||
mkdir -p $(BINDIR)/clock
|
||||
$(BINDIR)/kpunch/clock:
|
||||
mkdir -p $(BINDIR)/kpunch/clock
|
||||
|
||||
$(BINDIR)/dvxdemo:
|
||||
mkdir -p $(BINDIR)/dvxdemo
|
||||
$(BINDIR)/kpunch/dvxdemo:
|
||||
mkdir -p $(BINDIR)/kpunch/dvxdemo
|
||||
|
||||
$(BINDIR)/dvxhelp:
|
||||
mkdir -p $(BINDIR)/dvxhelp
|
||||
$(BINDIR)/kpunch/dvxhelp:
|
||||
mkdir -p $(BINDIR)/kpunch/dvxhelp
|
||||
|
||||
# Dependencies
|
||||
$(OBJDIR)/imgview.o: imgview/imgview.c ../core/dvxApp.h ../core/dvxDialog.h ../core/dvxWidget.h ../core/dvxWm.h ../core/dvxVideo.h ../shell/shellApp.h
|
||||
|
|
@ -113,10 +113,10 @@ $(OBJDIR)/dvxdemo.o: dvxdemo/dvxdemo.c ../core/dvxApp.h ../core/dvxDialog.h ../c
|
|||
|
||||
clean:
|
||||
rm -f $(OBJDIR)/cpanel.o $(OBJDIR)/imgview.o $(OBJDIR)/progman.o $(OBJDIR)/notepad.o $(OBJDIR)/clock.o $(OBJDIR)/dvxdemo.o
|
||||
rm -f $(BINDIR)/cpanel/cpanel.app
|
||||
rm -f $(BINDIR)/imgview/imgview.app
|
||||
rm -f $(BINDIR)/progman/progman.app
|
||||
rm -f $(BINDIR)/notepad/notepad.app
|
||||
rm -f $(BINDIR)/clock/clock.app
|
||||
rm -f $(BINDIR)/dvxdemo/dvxdemo.app $(addprefix $(BINDIR)/dvxdemo/,$(DVXDEMO_BMPS))
|
||||
rm -f $(BINDIR)/kpunch/cpanel/cpanel.app
|
||||
rm -f $(BINDIR)/kpunch/imgview/imgview.app
|
||||
rm -f $(BINDIR)/kpunch/progman/progman.app
|
||||
rm -f $(BINDIR)/kpunch/notepad/notepad.app
|
||||
rm -f $(BINDIR)/kpunch/clock/clock.app
|
||||
rm -f $(BINDIR)/kpunch/dvxdemo/dvxdemo.app $(addprefix $(BINDIR)/kpunch/dvxdemo/,$(DVXDEMO_BMPS))
|
||||
$(MAKE) -C dvxbasic clean
|
||||
|
|
|
|||
|
|
@ -16,13 +16,14 @@ CFLAGS = -O2 -Wall -Wextra -Werror -Wno-type-limits -Wno-sign-compare -Wn
|
|||
|
||||
OBJDIR = ../../obj/dvxbasic
|
||||
LIBSDIR = ../../bin/libs
|
||||
APPDIR = ../../bin/apps/dvxbasic
|
||||
APPDIR = ../../bin/apps/kpunch/dvxbasic
|
||||
DVXRES = ../../bin/dvxres
|
||||
SAMPLES = samples/hello.bas samples/formtest.bas samples/clickme.bas samples/clickme.frm samples/input.bas
|
||||
|
||||
# Runtime library objects (VM + values)
|
||||
RT_OBJS = $(OBJDIR)/vm.o $(OBJDIR)/values.o
|
||||
RT_TARGET = $(LIBSDIR)/basrt.lib
|
||||
RT_TARGETDIR = $(LIBSDIR)/kpunch/basrt
|
||||
RT_TARGET = $(RT_TARGETDIR)/basrt.lib
|
||||
|
||||
# Compiler objects (only needed by the IDE)
|
||||
COMP_OBJS = $(OBJDIR)/lexer.o $(OBJDIR)/parser.o $(OBJDIR)/codegen.o $(OBJDIR)/symtab.o
|
||||
|
|
@ -53,7 +54,7 @@ TEST_QUICK_SRCS = test_quick.c compiler/lexer.c compiler/parser.c compiler/co
|
|||
|
||||
.PHONY: all clean tests
|
||||
|
||||
all: $(RT_TARGET) $(LIBSDIR)/basrt.dep $(APP_TARGET) install-samples
|
||||
all: $(RT_TARGET) $(RT_TARGETDIR)/basrt.dep $(APP_TARGET) install-samples
|
||||
|
||||
tests: $(TEST_COMPILER) $(TEST_VM) $(TEST_LEX) $(TEST_QUICK)
|
||||
|
||||
|
|
@ -70,13 +71,13 @@ $(TEST_QUICK): $(TEST_QUICK_SRCS) | $(BINDIR)
|
|||
$(HOSTCC) $(HOSTCFLAGS) -o $@ $(TEST_QUICK_SRCS) -lm
|
||||
|
||||
# Runtime library DXE (exports symbols via dlregsym constructor)
|
||||
$(RT_TARGET): $(RT_OBJS) | $(LIBSDIR)
|
||||
$(DXE3GEN) -o $(LIBSDIR)/basrt.dxe \
|
||||
$(RT_TARGET): $(RT_OBJS) | $(RT_TARGETDIR)
|
||||
$(DXE3GEN) -o $(RT_TARGETDIR)/basrt.dxe \
|
||||
-E _bas -E _BAS \
|
||||
-U $(RT_OBJS)
|
||||
mv $(LIBSDIR)/basrt.dxe $@
|
||||
mv $(RT_TARGETDIR)/basrt.dxe $@
|
||||
|
||||
$(LIBSDIR)/basrt.dep: ../../config/basrt.dep | $(LIBSDIR)
|
||||
$(RT_TARGETDIR)/basrt.dep: ../../config/basrt.dep | $(RT_TARGETDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
|
||||
# IDE app DXE (compiler linked in, runtime from basrt.lib)
|
||||
|
|
@ -134,6 +135,9 @@ $(OBJDIR):
|
|||
$(LIBSDIR):
|
||||
mkdir -p $(LIBSDIR)
|
||||
|
||||
$(RT_TARGETDIR):
|
||||
mkdir -p $(RT_TARGETDIR)
|
||||
|
||||
$(APPDIR):
|
||||
mkdir -p $(APPDIR)
|
||||
|
||||
|
|
@ -141,5 +145,5 @@ $(BINDIR):
|
|||
mkdir -p $(BINDIR)
|
||||
|
||||
clean:
|
||||
rm -f $(RT_OBJS) $(COMP_OBJS) $(FORMRT_OBJS) $(IDE_OBJS) $(RT_TARGET) $(APP_TARGET) $(LIBSDIR)/basrt.dep $(OBJDIR)/basrt_init.o
|
||||
rm -rf $(RT_OBJS) $(COMP_OBJS) $(FORMRT_OBJS) $(IDE_OBJS) $(RT_TARGET) $(APP_TARGET) $(RT_TARGETDIR)/basrt.dep $(RT_TARGETDIR) $(OBJDIR)/basrt_init.o
|
||||
rm -f $(TEST_COMPILER) $(TEST_VM) $(TEST_LEX) $(TEST_QUICK)
|
||||
|
|
|
|||
535
apps/dvxbasic/basrt.dhs
Normal file
535
apps/dvxbasic/basrt.dhs
Normal file
|
|
@ -0,0 +1,535 @@
|
|||
.topic lib.basrt
|
||||
.title BASIC Runtime Library
|
||||
.toc 1 BASIC Runtime Library
|
||||
.index BASIC Runtime
|
||||
.index BasVmT
|
||||
.index BasValueT
|
||||
.index BasStringT
|
||||
.index BasArrayT
|
||||
.index BasModuleT
|
||||
|
||||
.h1 BASIC Runtime Library
|
||||
|
||||
Stack-based p-code virtual machine and value system for DVX BASIC. Embeddable: the host provides I/O and UI callbacks. No DVX GUI dependencies in the core runtime.
|
||||
|
||||
Headers: apps/dvxbasic/runtime/vm.h, apps/dvxbasic/runtime/values.h
|
||||
|
||||
.link lib.basrt.values Value System (values.h)
|
||||
.link lib.basrt.vm Virtual Machine (vm.h)
|
||||
|
||||
.topic lib.basrt.values
|
||||
.title Value System
|
||||
.toc 1 Value System
|
||||
.index BasValueT
|
||||
.index BasStringT
|
||||
.index BasArrayT
|
||||
.index BasUdtT
|
||||
.index basStringNew
|
||||
.index basArrayNew
|
||||
.index basValCopy
|
||||
.index basValRelease
|
||||
|
||||
.h1 Value System
|
||||
|
||||
Tagged union value type for the VM evaluation stack, variables, and array elements. Strings, arrays, and UDT instances are reference-counted for automatic memory management without a garbage collector.
|
||||
|
||||
Header: apps/dvxbasic/runtime/values.h
|
||||
|
||||
.h2 Type Tags
|
||||
|
||||
.table
|
||||
Constant Value C Union Field Description
|
||||
-------- ----- ------------- -----------
|
||||
BAS_TYPE_INTEGER 0 intVal 16-bit signed integer.
|
||||
BAS_TYPE_LONG 1 longVal 32-bit signed integer.
|
||||
BAS_TYPE_SINGLE 2 sngVal 32-bit float.
|
||||
BAS_TYPE_DOUBLE 3 dblVal 64-bit float.
|
||||
BAS_TYPE_STRING 4 strVal Reference-counted dynamic string.
|
||||
BAS_TYPE_BOOLEAN 5 boolVal True (-1) or False (0).
|
||||
BAS_TYPE_ARRAY 6 arrVal Reference-counted array.
|
||||
BAS_TYPE_UDT 7 udtVal Reference-counted user-defined type.
|
||||
BAS_TYPE_OBJECT 8 objVal Opaque host pointer (form, control).
|
||||
BAS_TYPE_REF 9 refVal ByRef pointer to a BasValueT slot.
|
||||
.endtable
|
||||
|
||||
.h2 BasValueT
|
||||
|
||||
Tagged union holding any BASIC value.
|
||||
|
||||
.code
|
||||
struct BasValueTag {
|
||||
uint8_t type; // BAS_TYPE_*
|
||||
union {
|
||||
int16_t intVal;
|
||||
int32_t longVal;
|
||||
float sngVal;
|
||||
double dblVal;
|
||||
BasStringT *strVal;
|
||||
int16_t boolVal;
|
||||
BasArrayT *arrVal;
|
||||
BasUdtT *udtVal;
|
||||
void *objVal;
|
||||
BasValueT *refVal;
|
||||
};
|
||||
};
|
||||
.endcode
|
||||
|
||||
.h2 Value Constructors
|
||||
|
||||
.code
|
||||
BasValueT basValInteger(int16_t v);
|
||||
BasValueT basValLong(int32_t v);
|
||||
BasValueT basValSingle(float v);
|
||||
BasValueT basValDouble(double v);
|
||||
BasValueT basValString(BasStringT *s);
|
||||
BasValueT basValStringFromC(const char *text);
|
||||
BasValueT basValBool(bool v);
|
||||
BasValueT basValObject(void *obj);
|
||||
.endcode
|
||||
|
||||
Each returns a BasValueT with the appropriate type tag set. basValString increments the string's reference count.
|
||||
|
||||
.h2 Value Lifetime
|
||||
|
||||
.code
|
||||
BasValueT basValCopy(BasValueT v);
|
||||
void basValRelease(BasValueT *v);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Function Description
|
||||
-------- -----------
|
||||
basValCopy Copy a value. Increments reference count for strings, arrays, and UDTs.
|
||||
basValRelease Release a value. Decrements reference count and frees if it reaches zero.
|
||||
.endtable
|
||||
|
||||
.h2 Type Conversion
|
||||
|
||||
.code
|
||||
BasValueT basValToInteger(BasValueT v);
|
||||
BasValueT basValToLong(BasValueT v);
|
||||
BasValueT basValToSingle(BasValueT v);
|
||||
BasValueT basValToDouble(BasValueT v);
|
||||
BasValueT basValToString(BasValueT v);
|
||||
BasValueT basValToBool(BasValueT v);
|
||||
.endcode
|
||||
|
||||
Each returns a new value of the target type. The original is not released; the caller manages both lifetimes.
|
||||
|
||||
.h2 Value Utilities
|
||||
|
||||
.table
|
||||
Function Description
|
||||
-------- -----------
|
||||
basValToNumber(v) Convert any numeric value to double.
|
||||
basValFormatString(v) Return a new ref-counted string representation of v.
|
||||
basValIsTruthy(v) True if non-zero number or non-empty string.
|
||||
basValCompare(a, b) Compare two values. Returns -1, 0, or 1.
|
||||
basValCompareCI(a, b) Case-insensitive comparison (OPTION COMPARE TEXT).
|
||||
basValPromoteType(a, b) Determine common type for binary ops (e.g. Integer + Single -> Single).
|
||||
.endtable
|
||||
|
||||
.h2 BasStringT
|
||||
|
||||
Reference-counted string with flexible array member for inline storage.
|
||||
|
||||
.code
|
||||
typedef struct {
|
||||
int32_t refCount;
|
||||
int32_t len;
|
||||
int32_t cap;
|
||||
char data[];
|
||||
} BasStringT;
|
||||
.endcode
|
||||
|
||||
.h3 String Functions
|
||||
|
||||
.table
|
||||
Function Description
|
||||
-------- -----------
|
||||
basStringNew(text, len) Allocate from a C string. refCount starts at 1.
|
||||
basStringAlloc(cap) Allocate an empty string with given capacity.
|
||||
basStringRef(s) Increment reference count. Returns s.
|
||||
basStringUnref(s) Decrement reference count. Frees when it reaches zero.
|
||||
basStringConcat(a, b) Concatenate two strings. Returns a new string (refCount 1).
|
||||
basStringSub(s, start, len) Extract a substring. Returns a new string (refCount 1).
|
||||
basStringCompare(a, b) Compare. Returns <0, 0, >0 (like strcmp).
|
||||
basStringCompareCI(a, b) Case-insensitive compare.
|
||||
basStringSystemInit() Initialize the string system and empty string singleton.
|
||||
basStringSystemShutdown() Shut down the string system.
|
||||
.endtable
|
||||
|
||||
The global basEmptyString is a singleton that is never freed.
|
||||
|
||||
.h2 BasArrayT
|
||||
|
||||
Reference-counted multi-dimensional array (up to BAS_ARRAY_MAX_DIMS = 8 dimensions).
|
||||
|
||||
.code
|
||||
typedef struct {
|
||||
int32_t refCount;
|
||||
uint8_t elementType;
|
||||
int32_t dims;
|
||||
int32_t lbound[BAS_ARRAY_MAX_DIMS];
|
||||
int32_t ubound[BAS_ARRAY_MAX_DIMS];
|
||||
int32_t totalElements;
|
||||
BasValueT *elements;
|
||||
} BasArrayT;
|
||||
.endcode
|
||||
|
||||
.h3 Array Functions
|
||||
|
||||
.table
|
||||
Function Description
|
||||
-------- -----------
|
||||
basArrayNew(dims, lbounds, ubounds, type) Allocate an array. refCount starts at 1.
|
||||
basArrayFree(arr) Free all elements and release the array.
|
||||
basArrayRef(arr) Increment reference count.
|
||||
basArrayUnref(arr) Decrement reference count. Frees at zero.
|
||||
basArrayIndex(arr, indices, ndims) Compute flat index from multi-dimensional indices. Returns -1 if out of bounds.
|
||||
.endtable
|
||||
|
||||
.h2 BasUdtT
|
||||
|
||||
Reference-counted user-defined type instance.
|
||||
|
||||
.code
|
||||
typedef struct {
|
||||
int32_t refCount;
|
||||
int32_t typeId;
|
||||
int32_t fieldCount;
|
||||
BasValueT *fields;
|
||||
} BasUdtT;
|
||||
.endcode
|
||||
|
||||
.h3 UDT Functions
|
||||
|
||||
.table
|
||||
Function Description
|
||||
-------- -----------
|
||||
basUdtNew(typeId, fieldCount) Allocate a UDT instance. refCount starts at 1.
|
||||
basUdtFree(udt) Free all fields and release.
|
||||
basUdtRef(udt) Increment reference count.
|
||||
basUdtUnref(udt) Decrement reference count. Frees at zero.
|
||||
.endtable
|
||||
|
||||
.topic lib.basrt.vm
|
||||
.title Virtual Machine
|
||||
.toc 1 Virtual Machine
|
||||
.index BasVmT
|
||||
.index BasModuleT
|
||||
.index BasVmResultE
|
||||
.index basVmCreate
|
||||
.index basVmRun
|
||||
.index basVmStep
|
||||
.index basVmDestroy
|
||||
.index basVmCallSub
|
||||
|
||||
.h1 Virtual Machine
|
||||
|
||||
Stack-based p-code interpreter. Executes compiled BASIC bytecode modules. The host provides I/O, UI, SQL, and external library callbacks. The VM has no DVX dependencies; it can be embedded in any C program.
|
||||
|
||||
Header: apps/dvxbasic/runtime/vm.h
|
||||
|
||||
.h2 VM Limits
|
||||
|
||||
.table
|
||||
Constant Value Description
|
||||
-------- ----- -----------
|
||||
BAS_VM_STACK_SIZE 256 Evaluation stack depth.
|
||||
BAS_VM_CALL_STACK_SIZE 64 Maximum call nesting depth.
|
||||
BAS_VM_MAX_GLOBALS 512 Global variable slots.
|
||||
BAS_VM_MAX_LOCALS 64 Local variables per stack frame.
|
||||
BAS_VM_MAX_FOR_DEPTH 32 Maximum nested FOR loop depth.
|
||||
BAS_VM_MAX_FILES 16 Open file channels (1-based).
|
||||
.endtable
|
||||
|
||||
.h2 BasVmResultE
|
||||
|
||||
Result codes returned by basVmRun and basVmStep.
|
||||
|
||||
.table
|
||||
Code Value Description
|
||||
---- ----- -----------
|
||||
BAS_VM_OK 0 Program completed normally.
|
||||
BAS_VM_HALTED 1 HALT instruction reached.
|
||||
BAS_VM_YIELDED 2 DoEvents yielded control.
|
||||
BAS_VM_ERROR 3 Runtime error.
|
||||
BAS_VM_STACK_OVERFLOW 4 Evaluation stack overflow.
|
||||
BAS_VM_STACK_UNDERFLOW 5 Evaluation stack underflow.
|
||||
BAS_VM_CALL_OVERFLOW 6 Call stack overflow.
|
||||
BAS_VM_DIV_BY_ZERO 7 Division by zero.
|
||||
BAS_VM_TYPE_MISMATCH 8 Type mismatch in operation.
|
||||
BAS_VM_OUT_OF_MEMORY 9 Memory allocation failed.
|
||||
BAS_VM_BAD_OPCODE 10 Unknown opcode encountered.
|
||||
BAS_VM_FILE_ERROR 11 File I/O error.
|
||||
BAS_VM_SUBSCRIPT_RANGE 12 Array subscript out of range.
|
||||
BAS_VM_USER_ERROR 13 ON ERROR raised by program.
|
||||
BAS_VM_STEP_LIMIT 14 Step limit reached (not an error).
|
||||
BAS_VM_BREAKPOINT 15 Breakpoint or step completed (not an error).
|
||||
.endtable
|
||||
|
||||
.h2 Lifecycle
|
||||
|
||||
.code
|
||||
BasVmT *basVmCreate(void);
|
||||
void basVmDestroy(BasVmT *vm);
|
||||
void basVmLoadModule(BasVmT *vm, BasModuleT *module);
|
||||
void basVmReset(BasVmT *vm);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Function Description
|
||||
-------- -----------
|
||||
basVmCreate Allocate and initialize a new VM instance.
|
||||
basVmDestroy Destroy the VM and free all resources.
|
||||
basVmLoadModule Load a compiled module (BasModuleT) into the VM.
|
||||
basVmReset Reset to initial state (clear stack, globals, PC).
|
||||
.endtable
|
||||
|
||||
.h2 Execution
|
||||
|
||||
.code
|
||||
BasVmResultE basVmRun(BasVmT *vm);
|
||||
BasVmResultE basVmStep(BasVmT *vm);
|
||||
void basVmSetStepLimit(BasVmT *vm, int32_t limit);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Function Description
|
||||
-------- -----------
|
||||
basVmRun Execute the loaded module until it ends, halts, yields, errors, or hits a breakpoint/step limit.
|
||||
basVmStep Execute a single instruction and return. Useful for debugger stepping.
|
||||
basVmSetStepLimit Set maximum instructions per basVmRun call. 0 = unlimited (default). Returns BAS_VM_STEP_LIMIT when reached.
|
||||
.endtable
|
||||
|
||||
.h2 I/O Callbacks
|
||||
|
||||
The host provides these callbacks for PRINT, INPUT, and DoEvents statements.
|
||||
|
||||
.code
|
||||
void basVmSetPrintCallback(BasVmT *vm, BasPrintFnT fn, void *ctx);
|
||||
void basVmSetInputCallback(BasVmT *vm, BasInputFnT fn, void *ctx);
|
||||
void basVmSetDoEventsCallback(BasVmT *vm, BasDoEventsFnT fn, void *ctx);
|
||||
.endcode
|
||||
|
||||
.h3 Callback Types
|
||||
|
||||
.code
|
||||
typedef void (*BasPrintFnT)(void *ctx, const char *text, bool newline);
|
||||
typedef bool (*BasInputFnT)(void *ctx, const char *prompt,
|
||||
char *buf, int32_t bufSize);
|
||||
typedef bool (*BasDoEventsFnT)(void *ctx);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Type Description
|
||||
---- -----------
|
||||
BasPrintFnT Called for PRINT output. text is null-terminated. newline indicates line advance.
|
||||
BasInputFnT Called for INPUT. Fill buf (up to bufSize-1 chars). Return true on success, false on cancel.
|
||||
BasDoEventsFnT Called for DoEvents. Process pending events and return. Return false to stop the program.
|
||||
.endtable
|
||||
|
||||
.h2 UI Callbacks
|
||||
|
||||
For form and control integration. The VM resolves all UI operations through these callbacks, keeping it independent of any specific GUI toolkit.
|
||||
|
||||
.code
|
||||
void basVmSetUiCallbacks(BasVmT *vm, const BasUiCallbacksT *ui);
|
||||
.endcode
|
||||
|
||||
.h3 BasUiCallbacksT
|
||||
|
||||
.table
|
||||
Field Signature Description
|
||||
----- --------- -----------
|
||||
getProp BasValueT (*)(ctx, ctrlRef, propName) Get a control property value.
|
||||
setProp void (*)(ctx, ctrlRef, propName, value) Set a control property.
|
||||
callMethod BasValueT (*)(ctx, ctrlRef, methodName, args, argc) Call a method on a control.
|
||||
createCtrl void *(*)(ctx, formRef, typeName, ctrlName) Create a control on a form.
|
||||
findCtrl void *(*)(ctx, formRef, ctrlName) Find a control by name.
|
||||
findCtrlIdx void *(*)(ctx, formRef, ctrlName, index) Find a control array element.
|
||||
loadForm void *(*)(ctx, formName) Load a form by name.
|
||||
unloadForm void (*)(ctx, formRef) Unload a form.
|
||||
showForm void (*)(ctx, formRef, modal) Show a form (modal or modeless).
|
||||
hideForm void (*)(ctx, formRef) Hide a form (keep in memory).
|
||||
msgBox int32_t (*)(ctx, message, flags) Display a message box.
|
||||
inputBox BasStringT *(*)(ctx, prompt, title, defaultText) Display an input box.
|
||||
ctx void * User pointer passed to all callbacks.
|
||||
.endtable
|
||||
|
||||
.h2 SQL Callbacks
|
||||
|
||||
For database integration.
|
||||
|
||||
.code
|
||||
void basVmSetSqlCallbacks(BasVmT *vm, const BasSqlCallbacksT *sql);
|
||||
.endcode
|
||||
|
||||
.h3 BasSqlCallbacksT
|
||||
|
||||
.table
|
||||
Field Signature Description
|
||||
----- --------- -----------
|
||||
sqlOpen int32_t (*)(path) Open a database. Returns handle.
|
||||
sqlClose void (*)(db) Close a database.
|
||||
sqlExec bool (*)(db, sql) Execute a non-query SQL statement.
|
||||
sqlError const char *(*)(db) Get last error message.
|
||||
sqlQuery int32_t (*)(db, sql) Execute a query. Returns result set handle.
|
||||
sqlNext bool (*)(rs) Advance to next row.
|
||||
sqlEof bool (*)(rs) Check if at end of result set.
|
||||
sqlFieldCount int32_t (*)(rs) Get number of columns.
|
||||
sqlFieldName const char *(*)(rs, col) Get column name by index.
|
||||
sqlFieldText const char *(*)(rs, col) Get column value as text by index.
|
||||
sqlFieldByName const char *(*)(rs, name) Get column value as text by name.
|
||||
sqlFieldInt int32_t (*)(rs, col) Get column value as integer.
|
||||
sqlFieldDbl double (*)(rs, col) Get column value as double.
|
||||
sqlFreeResult void (*)(rs) Free a result set.
|
||||
sqlAffectedRows int32_t (*)(db) Get number of rows affected by last statement.
|
||||
.endtable
|
||||
|
||||
.h2 External Library Callbacks
|
||||
|
||||
For DECLARE LIBRARY support. The VM resolves external functions at runtime via the host.
|
||||
|
||||
.code
|
||||
void basVmSetExternCallbacks(BasVmT *vm,
|
||||
const BasExternCallbacksT *ext);
|
||||
.endcode
|
||||
|
||||
.h3 BasExternCallbacksT
|
||||
|
||||
.table
|
||||
Field Signature Description
|
||||
----- --------- -----------
|
||||
resolveExtern void *(*)(ctx, libName, funcName) Resolve a native function by library and symbol name. Cached after first call.
|
||||
callExtern BasValueT (*)(ctx, funcPtr, funcName, args, argc, retType) Call a resolved native function, marshalling arguments and return value.
|
||||
ctx void * User pointer passed to both callbacks.
|
||||
.endtable
|
||||
|
||||
.h2 Form Context
|
||||
|
||||
Set the active form context during event dispatch.
|
||||
|
||||
.code
|
||||
void basVmSetCurrentForm(BasVmT *vm, void *formRef);
|
||||
void basVmSetCurrentFormVars(BasVmT *vm,
|
||||
BasValueT *vars, int32_t count);
|
||||
.endcode
|
||||
|
||||
.h2 Stack Access
|
||||
|
||||
Push and pop values on the evaluation stack for host integration.
|
||||
|
||||
.code
|
||||
bool basVmPush(BasVmT *vm, BasValueT val);
|
||||
bool basVmPop(BasVmT *vm, BasValueT *val);
|
||||
.endcode
|
||||
|
||||
Both return true on success, false on stack overflow/underflow.
|
||||
|
||||
.h2 Error Reporting
|
||||
|
||||
.code
|
||||
const char *basVmGetError(const BasVmT *vm);
|
||||
.endcode
|
||||
|
||||
Returns the current error message string. Valid after basVmRun returns BAS_VM_ERROR.
|
||||
|
||||
.h2 Sub/Function Calls from Host
|
||||
|
||||
Call a SUB or FUNCTION by its code address from host code.
|
||||
|
||||
.code
|
||||
bool basVmCallSub(BasVmT *vm, int32_t codeAddr);
|
||||
bool basVmCallSubWithArgs(BasVmT *vm, int32_t codeAddr,
|
||||
const BasValueT *args, int32_t argCount);
|
||||
bool basVmCallSubWithArgsOut(BasVmT *vm, int32_t codeAddr,
|
||||
const BasValueT *args, int32_t argCount,
|
||||
BasValueT *outArgs, int32_t outCount);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Function Description
|
||||
-------- -----------
|
||||
basVmCallSub Call a SUB with no arguments.
|
||||
basVmCallSubWithArgs Call a SUB with arguments pushed onto the stack frame.
|
||||
basVmCallSubWithArgsOut Call a SUB and read back modified argument values after it returns.
|
||||
.endtable
|
||||
|
||||
All three push a call frame, execute until the SUB returns, then restore the previous execution state. Return true on normal completion, false on error or if the VM is not idle.
|
||||
|
||||
.h2 Debugger API
|
||||
|
||||
.code
|
||||
void basVmSetBreakpoints(BasVmT *vm,
|
||||
int32_t *lines, int32_t count);
|
||||
void basVmStepInto(BasVmT *vm);
|
||||
void basVmStepOver(BasVmT *vm);
|
||||
void basVmStepOut(BasVmT *vm);
|
||||
void basVmRunToCursor(BasVmT *vm, int32_t line);
|
||||
int32_t basVmGetCurrentLine(const BasVmT *vm);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Function Description
|
||||
-------- -----------
|
||||
basVmSetBreakpoints Set the breakpoint list (sorted array of source line numbers, host-owned memory).
|
||||
basVmStepInto Break at the next OP_LINE instruction.
|
||||
basVmStepOver Break when call depth returns to the current level.
|
||||
basVmStepOut Break when call depth drops below the current level.
|
||||
basVmRunToCursor Break when execution reaches the specified source line.
|
||||
basVmGetCurrentLine Get the current source line number (from the last OP_LINE instruction).
|
||||
.endtable
|
||||
|
||||
The breakpoint callback notifies the host when a breakpoint fires during nested sub calls:
|
||||
|
||||
.code
|
||||
typedef void (*BasBreakpointFnT)(void *ctx, int32_t line);
|
||||
.endcode
|
||||
|
||||
.h2 BasModuleT
|
||||
|
||||
Compiled module produced by the BASIC compiler and loaded into the VM.
|
||||
|
||||
.code
|
||||
typedef struct {
|
||||
uint8_t *code;
|
||||
int32_t codeLen;
|
||||
BasStringT **constants;
|
||||
int32_t constCount;
|
||||
int32_t globalCount;
|
||||
int32_t entryPoint;
|
||||
BasValueT *dataPool;
|
||||
int32_t dataCount;
|
||||
BasProcEntryT *procs;
|
||||
int32_t procCount;
|
||||
BasFormVarInfoT *formVarInfo;
|
||||
int32_t formVarInfoCount;
|
||||
BasDebugVarT *debugVars;
|
||||
int32_t debugVarCount;
|
||||
BasDebugUdtDefT *debugUdtDefs;
|
||||
int32_t debugUdtDefCount;
|
||||
} BasModuleT;
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Field Description
|
||||
----- -----------
|
||||
code P-code bytecode array.
|
||||
codeLen Length of bytecode in bytes.
|
||||
constants String constant pool.
|
||||
constCount Number of string constants.
|
||||
globalCount Number of global variable slots needed.
|
||||
entryPoint PC of the first instruction (module-level code).
|
||||
dataPool DATA statement value pool (for READ).
|
||||
dataCount Number of values in the data pool.
|
||||
procs Procedure table (SUBs and FUNCTIONs).
|
||||
procCount Number of procedures.
|
||||
formVarInfo Per-form variable counts and init code addresses.
|
||||
formVarInfoCount Number of forms with form-scoped variables.
|
||||
debugVars Variable names and metadata for the debugger.
|
||||
debugVarCount Number of debug variable entries.
|
||||
debugUdtDefs UDT type definitions for the debugger.
|
||||
debugUdtDefCount Number of debug UDT definitions.
|
||||
.endtable
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
.topic ctrl.frame
|
||||
.title Frame
|
||||
.toc 1 Frame
|
||||
.index Frame
|
||||
.index Container
|
||||
|
||||
.h1 Frame
|
||||
|
||||
VB Equivalent: Frame -- DVX Widget: frame (titled VBox container)
|
||||
|
||||
A container with a titled border. Child controls are placed inside the frame using VBox layout. In the .frm file, nest Begin/End blocks inside the Frame block.
|
||||
|
||||
.h2 Type-Specific Properties
|
||||
|
||||
.table
|
||||
Property Type Description
|
||||
-------- ------ -------------------------------------------
|
||||
Caption String The title displayed in the frame border.
|
||||
.endtable
|
||||
|
||||
Container: Yes
|
||||
|
||||
Default Event: Click
|
||||
|
||||
.h2 Example
|
||||
|
||||
.code
|
||||
Begin Frame Frame1
|
||||
Caption = "Options"
|
||||
Begin CheckBox Check1
|
||||
Caption = "Option A"
|
||||
End
|
||||
Begin CheckBox Check2
|
||||
Caption = "Option B"
|
||||
End
|
||||
End
|
||||
.endcode
|
||||
|
||||
.link ctrl.common.props Common Properties, Events, and Methods
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
.topic ctrl.hbox
|
||||
.title HBox
|
||||
.toc 1 HBox
|
||||
.index HBox
|
||||
.index Horizontal Layout
|
||||
|
||||
.h1 HBox
|
||||
|
||||
DVX Extension -- DVX Widget: hbox (horizontal layout container)
|
||||
|
||||
A container that arranges its children horizontally, left to right. Use Weight on children to distribute extra space.
|
||||
|
||||
Container: Yes
|
||||
|
||||
Default Event: Click
|
||||
|
||||
No type-specific properties.
|
||||
|
||||
.link ctrl.common.props Common Properties, Events, and Methods
|
||||
.link ctrl.vbox VBox
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
.topic ctrl.textarea
|
||||
.title TextArea
|
||||
.toc 1 TextArea
|
||||
.index TextArea
|
||||
|
||||
.h1 TextArea
|
||||
|
||||
VB Equivalent: TextArea (DVX extension) -- DVX Widget: textarea (multi-line text input, max 4096 chars)
|
||||
|
||||
A multi-line text editing area. This is a DVX extension with no direct VB3 equivalent (VB uses a TextBox with MultiLine=True). Supports syntax colorization, line numbers, auto-indent, and find/replace via the C API.
|
||||
|
||||
.h2 Type-Specific Properties
|
||||
|
||||
.table
|
||||
Property Type Description
|
||||
-------- ------ -------------------------------------------
|
||||
Text String The full text content.
|
||||
.endtable
|
||||
|
||||
Default Event: Change
|
||||
|
||||
.link ctrl.common.props Common Properties, Events, and Methods
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
.topic ctrl.vbox
|
||||
.title VBox
|
||||
.toc 1 VBox
|
||||
.index VBox
|
||||
.index Vertical Layout
|
||||
|
||||
.h1 VBox
|
||||
|
||||
DVX Extension -- DVX Widget: vbox (vertical layout container)
|
||||
|
||||
A container that arranges its children vertically, top to bottom. No title or border. Use Weight on children to distribute extra space.
|
||||
|
||||
Container: Yes
|
||||
|
||||
Default Event: Click
|
||||
|
||||
No type-specific properties.
|
||||
|
||||
.link ctrl.common.props Common Properties, Events, and Methods
|
||||
.link ctrl.hbox HBox
|
||||
5
apps/dvxbasic/dvxbasic.hcf
Normal file
5
apps/dvxbasic/dvxbasic.hcf
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# DVX BASIC Help configuration
|
||||
# Compiled into DVXBASIC.HLP in this directory
|
||||
output = DVXBASIC.HLP
|
||||
source = APPS/KPUNCH/DVXBASIC/*.DHS
|
||||
source = WIDGETS/*.BHS
|
||||
|
|
@ -605,7 +605,7 @@ int32_t appMain(DxeAppContextT *ctx) {
|
|||
sOutputLen = 0;
|
||||
|
||||
// Auto-load project for development/testing
|
||||
if (prjLoad(&sProject, "C:\\BIN\\APPS\\DVXBASIC\\MULTI.DBP")) {
|
||||
if (prjLoad(&sProject, "C:\\BIN\\APPS\\KPUNCH\\DVXBASIC\\MULTI.DBP")) {
|
||||
prjLoadAllFiles(&sProject, sAc);
|
||||
sProjectWin = prjCreateWindow(sAc, &sProject, onPrjFileDblClick, updateProjectMenuState);
|
||||
|
||||
|
|
@ -4549,15 +4549,15 @@ static void onMenu(WindowT *win, int32_t menuId) {
|
|||
char hlpPath[DVX_MAX_PATH];
|
||||
char viewerPath[DVX_MAX_PATH];
|
||||
snprintf(hlpPath, sizeof(hlpPath), "%s%c%s", sCtx->appDir, DVX_PATH_SEP, "dvxbasic.hlp");
|
||||
snprintf(viewerPath, sizeof(viewerPath), "APPS%cDVXHELP%cDVXHELP.APP", DVX_PATH_SEP, DVX_PATH_SEP);
|
||||
snprintf(viewerPath, sizeof(viewerPath), "APPS%cKPUNCH%cDVXHELP%cDVXHELP.APP", DVX_PATH_SEP, DVX_PATH_SEP, DVX_PATH_SEP);
|
||||
shellLoadAppWithArgs(sAc, viewerPath, hlpPath);
|
||||
}
|
||||
|
||||
if (menuId == CMD_HELP_API) {
|
||||
char viewerPath[DVX_MAX_PATH];
|
||||
char sysHlp[DVX_MAX_PATH];
|
||||
snprintf(viewerPath, sizeof(viewerPath), "APPS%cDVXHELP%cDVXHELP.APP", DVX_PATH_SEP, DVX_PATH_SEP);
|
||||
snprintf(sysHlp, sizeof(sysHlp), "APPS%cPROGMAN%cDVXHELP.HLP", DVX_PATH_SEP, DVX_PATH_SEP);
|
||||
snprintf(viewerPath, sizeof(viewerPath), "APPS%cKPUNCH%cDVXHELP%cDVXHELP.APP", DVX_PATH_SEP, DVX_PATH_SEP, DVX_PATH_SEP);
|
||||
snprintf(sysHlp, sizeof(sysHlp), "APPS%cKPUNCH%cPROGMAN%cDVXHELP.HLP", DVX_PATH_SEP, DVX_PATH_SEP, DVX_PATH_SEP);
|
||||
shellLoadAppWithArgs(sAc, viewerPath, sysHlp);
|
||||
}
|
||||
|
||||
|
|
|
|||
5
apps/progman/dvxhelp.hcf
Normal file
5
apps/progman/dvxhelp.hcf
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# DVX System Help configuration
|
||||
# Compiled into DVXHELP.HLP in this directory
|
||||
output = DVXHELP.HLP
|
||||
source = LIBS/*.DHS
|
||||
source = WIDGETS/*.DHS
|
||||
|
|
@ -366,7 +366,7 @@ static void onPmMenu(WindowT *win, int32_t menuId) {
|
|||
case CMD_HELP: {
|
||||
char viewerPath[DVX_MAX_PATH];
|
||||
char sysHlp[DVX_MAX_PATH];
|
||||
snprintf(viewerPath, sizeof(viewerPath), "APPS%cDVXHELP%cDVXHELP.APP", DVX_PATH_SEP, DVX_PATH_SEP);
|
||||
snprintf(viewerPath, sizeof(viewerPath), "APPS%cKPUNCH%cDVXHELP%cDVXHELP.APP", DVX_PATH_SEP, DVX_PATH_SEP, DVX_PATH_SEP);
|
||||
snprintf(sysHlp, sizeof(sysHlp), "%s%c%s", sCtx->appDir, DVX_PATH_SEP, "dvxhelp.hlp");
|
||||
shellLoadAppWithArgs(sAc, viewerPath, sysHlp);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -23,4 +23,4 @@ acceleration = medium
|
|||
; desktop: path to the desktop app loaded at startup
|
||||
|
||||
[shell]
|
||||
desktop = apps/progman/progman.app
|
||||
desktop = apps/kpunch/progman/progman.app
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ SRCS = dvxVideo.c dvxDraw.c dvxComp.c dvxWm.c dvxImage.c dvxImageWrite.c \
|
|||
widgetLayout.c widgetEvent.c widgetOps.c
|
||||
|
||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||
TARGET = $(LIBSDIR)/libdvx.lib
|
||||
TARGETDIR = $(LIBSDIR)/kpunch/libdvx
|
||||
TARGET = $(TARGETDIR)/libdvx.lib
|
||||
|
||||
# libdvx.lib export prefixes
|
||||
DVX_EXPORTS = -E _dvx -E _wgt -E _wm -E _prefs -E _rect -E _draw -E _pack -E _text \
|
||||
|
|
@ -32,14 +33,14 @@ DVX_EXPORTS = -E _dvx -E _wgt -E _wm -E _prefs -E _rect -E _draw -E _pack -E _te
|
|||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET) $(LIBSDIR)/libdvx.dep
|
||||
all: $(TARGET) $(TARGETDIR)/libdvx.dep
|
||||
|
||||
$(LIBSDIR)/libdvx.dep: ../config/libdvx.dep | $(LIBSDIR)
|
||||
$(TARGETDIR)/libdvx.dep: ../config/libdvx.dep | $(TARGETDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
|
||||
$(TARGET): $(OBJS) | $(LIBSDIR)
|
||||
$(DXE3GEN) -o $(LIBSDIR)/libdvx.dxe $(DVX_EXPORTS) -U $(OBJS)
|
||||
mv $(LIBSDIR)/libdvx.dxe $@
|
||||
$(TARGET): $(OBJS) | $(TARGETDIR)
|
||||
$(DXE3GEN) -o $(TARGETDIR)/libdvx.dxe $(DVX_EXPORTS) -U $(OBJS)
|
||||
mv $(TARGETDIR)/libdvx.dxe $@
|
||||
|
||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
|
@ -50,6 +51,9 @@ $(OBJDIR):
|
|||
$(LIBSDIR):
|
||||
mkdir -p $(LIBSDIR)
|
||||
|
||||
$(TARGETDIR):
|
||||
mkdir -p $(TARGETDIR)
|
||||
|
||||
# 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
|
||||
|
|
@ -71,4 +75,4 @@ $(OBJDIR)/widgetEvent.o: widgetEvent.c $(WIDGET_DEPS)
|
|||
$(OBJDIR)/widgetOps.o: widgetOps.c $(WIDGET_DEPS)
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(TARGET) $(LIBSDIR)/libdvx.dep
|
||||
rm -rf $(OBJS) $(TARGET) $(TARGETDIR)/libdvx.dep $(TARGETDIR)
|
||||
|
|
|
|||
|
|
@ -269,6 +269,10 @@ char *platformPathDirEnd(const char *path);
|
|||
// '/' on DJGPP (which accepts both '/' and '\\').
|
||||
#define DVX_PATH_SEP '/'
|
||||
|
||||
// Simple glob pattern matching for filenames. Case-insensitive.
|
||||
// Supports * (zero or more chars) and ? (one char).
|
||||
bool platformGlobMatch(const char *pattern, const char *name);
|
||||
|
||||
// Return the platform's native line ending string.
|
||||
// "\r\n" on DOS.
|
||||
const char *platformLineEnding(void);
|
||||
|
|
|
|||
|
|
@ -2466,6 +2466,7 @@ DXE_EXPORT_TABLE(sDxeExportTable)
|
|||
DXE_EXPORT(platformMouseWheelInit)
|
||||
DXE_EXPORT(platformMouseWheelPoll)
|
||||
DXE_EXPORT(platformPathDirEnd)
|
||||
DXE_EXPORT(platformGlobMatch)
|
||||
DXE_EXPORT(platformRegisterDxeExports)
|
||||
DXE_EXPORT(platformRegisterSymOverrides)
|
||||
DXE_EXPORT(platformSpanCopy8)
|
||||
|
|
|
|||
|
|
@ -13,18 +13,19 @@ LIBSDIR = ../bin/libs
|
|||
|
||||
SRCS = listHelp.c
|
||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||
TARGET = $(LIBSDIR)/listhelp.lib
|
||||
TARGETDIR = $(LIBSDIR)/kpunch/listhelp
|
||||
TARGET = $(TARGETDIR)/listhelp.lib
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET) $(LIBSDIR)/listhelp.dep
|
||||
all: $(TARGET) $(TARGETDIR)/listhelp.dep
|
||||
|
||||
$(LIBSDIR)/listhelp.dep: ../config/listhelp.dep | $(LIBSDIR)
|
||||
$(TARGETDIR)/listhelp.dep: ../config/listhelp.dep | $(TARGETDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
|
||||
$(TARGET): $(OBJS) | $(LIBSDIR)
|
||||
$(DXE3GEN) -o $(LIBSDIR)/listhelp.dxe -E _widgetDraw -E _widgetDropdown -E _widgetMax -E _widgetNavigate -E _widgetPaint -U $(OBJS)
|
||||
mv $(LIBSDIR)/listhelp.dxe $@
|
||||
$(TARGET): $(OBJS) | $(TARGETDIR)
|
||||
$(DXE3GEN) -o $(TARGETDIR)/listhelp.dxe -E _widgetDraw -E _widgetDropdown -E _widgetMax -E _widgetNavigate -E _widgetPaint -U $(OBJS)
|
||||
mv $(TARGETDIR)/listhelp.dxe $@
|
||||
|
||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
|
@ -35,8 +36,11 @@ $(OBJDIR):
|
|||
$(LIBSDIR):
|
||||
mkdir -p $(LIBSDIR)
|
||||
|
||||
$(TARGETDIR):
|
||||
mkdir -p $(TARGETDIR)
|
||||
|
||||
# Dependencies
|
||||
$(OBJDIR)/listHelp.o: listHelp.c listHelp.h ../core/dvxWidgetPlugin.h ../core/dvxWidget.h ../core/dvxTypes.h ../core/dvxApp.h ../core/dvxDraw.h ../core/dvxWm.h ../core/dvxVideo.h
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(TARGET) $(LIBSDIR)/listhelp.dep
|
||||
rm -rf $(OBJS) $(TARGET) $(TARGETDIR)/listhelp.dep $(TARGETDIR)
|
||||
|
|
|
|||
134
listhelp/listhelp.dhs
Normal file
134
listhelp/listhelp.dhs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
.topic lib.listhelp
|
||||
.title List Helper Library
|
||||
.toc 1 List Helper Library
|
||||
.index listhelp
|
||||
.index widgetDrawDropdownArrow
|
||||
.index widgetMaxItemLen
|
||||
.index widgetNavigateIndex
|
||||
.index widgetDropdownPopupRect
|
||||
.index widgetPaintPopupList
|
||||
|
||||
.h1 List Helper Library
|
||||
|
||||
Shared helper routines for dropdown and list-based widget DXEs (ListBox, Dropdown, ComboBox, ListView, TreeView). Provides dropdown arrow rendering, item measurement, keyboard navigation, popup geometry calculation, and popup list painting.
|
||||
|
||||
Header: listhelp/listHelp.h
|
||||
|
||||
.h2 Constants
|
||||
|
||||
.table
|
||||
Constant Value Description
|
||||
-------- ----- -----------
|
||||
DROPDOWN_BTN_WIDTH 16 Width of the dropdown arrow button in pixels.
|
||||
DROPDOWN_MAX_VISIBLE 8 Maximum number of items visible in a popup list.
|
||||
.endtable
|
||||
|
||||
.h2 widgetDrawDropdownArrow
|
||||
|
||||
Draw the triangular dropdown arrow glyph centered at a given position.
|
||||
|
||||
.code
|
||||
void widgetDrawDropdownArrow(DisplayT *d, const BlitOpsT *ops,
|
||||
int32_t centerX, int32_t centerY, uint32_t color);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
d Display context.
|
||||
ops Blit operations vtable for the active pixel depth.
|
||||
centerX Horizontal center of the arrow in backbuffer coordinates.
|
||||
centerY Vertical center of the arrow in backbuffer coordinates.
|
||||
color Pre-packed pixel color for the arrow.
|
||||
.endtable
|
||||
|
||||
.h2 widgetMaxItemLen
|
||||
|
||||
Scan an array of strings and return the length (in characters) of the longest item.
|
||||
|
||||
.code
|
||||
int32_t widgetMaxItemLen(const char **items, int32_t count);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
items Array of null-terminated string pointers.
|
||||
count Number of items in the array.
|
||||
.endtable
|
||||
|
||||
Returns the character length of the longest item, or 0 if count is zero.
|
||||
|
||||
.h2 widgetNavigateIndex
|
||||
|
||||
Compute a new selected index from a navigation key press. Handles Up, Down, Home, End, Page Up, and Page Down.
|
||||
|
||||
.code
|
||||
int32_t widgetNavigateIndex(int32_t key, int32_t current,
|
||||
int32_t count, int32_t pageSize);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
key Keyboard scancode (Up, Down, Home, End, PgUp, PgDn).
|
||||
current Currently selected index.
|
||||
count Total number of items in the list.
|
||||
pageSize Number of visible items (used for PgUp/PgDn step size).
|
||||
.endtable
|
||||
|
||||
Returns the new index, clamped to [0, count-1].
|
||||
|
||||
.h2 widgetDropdownPopupRect
|
||||
|
||||
Calculate the screen rectangle for a dropdown popup list, positioning it below the owning widget and clamping to screen bounds.
|
||||
|
||||
.code
|
||||
void widgetDropdownPopupRect(WidgetT *w, const BitmapFontT *font,
|
||||
int32_t contentH, int32_t itemCount,
|
||||
int32_t *popX, int32_t *popY, int32_t *popW, int32_t *popH);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
w The widget that owns the popup (Dropdown, ComboBox, etc.).
|
||||
font Bitmap font used for item text (determines row height).
|
||||
contentH Height of the widget's content area in pixels.
|
||||
itemCount Total number of items in the list.
|
||||
popX Output: popup X position (screen coordinates).
|
||||
popY Output: popup Y position (screen coordinates).
|
||||
popW Output: popup width in pixels.
|
||||
popH Output: popup height in pixels.
|
||||
.endtable
|
||||
|
||||
The popup is sized to show up to DROPDOWN_MAX_VISIBLE items.
|
||||
|
||||
.h2 widgetPaintPopupList
|
||||
|
||||
Render a popup item list with highlight, scrolling, and beveled border.
|
||||
|
||||
.code
|
||||
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);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
d Display context.
|
||||
ops Blit operations vtable.
|
||||
font Bitmap font for rendering item text.
|
||||
colors Color scheme for background, text, and highlight colors.
|
||||
popX Popup X position (from widgetDropdownPopupRect).
|
||||
popY Popup Y position.
|
||||
popW Popup width.
|
||||
popH Popup height.
|
||||
items Array of null-terminated item strings.
|
||||
itemCount Total number of items.
|
||||
hoverIdx Index of the highlighted (hovered/selected) item, or -1 for none.
|
||||
scrollPos Index of the first visible item (scroll offset).
|
||||
.endtable
|
||||
|
|
@ -18,7 +18,7 @@ BINDIR = ../bin
|
|||
|
||||
SRCS = loaderMain.c
|
||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||
POBJS = $(POBJDIR)/dvxPlatformDos.o
|
||||
POBJS = $(POBJDIR)/dvxPlatformDos.o $(POBJDIR)/dvxPrefs.o
|
||||
TARGET = $(BINDIR)/dvx.exe
|
||||
|
||||
.PHONY: all clean
|
||||
|
|
@ -37,6 +37,9 @@ $(OBJDIR)/%.o: %.c | $(OBJDIR)
|
|||
$(POBJDIR)/dvxPlatformDos.o: ../core/platform/dvxPlatformDos.c | $(POBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(POBJDIR)/dvxPrefs.o: ../core/dvxPrefs.c | $(POBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR):
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
|
|
@ -49,6 +52,7 @@ $(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
|
||||
$(POBJDIR)/dvxPrefs.o: ../core/dvxPrefs.c ../core/dvxPrefs.h
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(POBJS) $(TARGET) $(BINDIR)/dvx.map
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
// it called. Finally, the loader finds and calls shellMain().
|
||||
|
||||
#include "dvxPlatform.h"
|
||||
#include "dvxPrefs.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
|
|
@ -35,9 +36,11 @@ extern void dvxFree(void *ptr);
|
|||
// Constants
|
||||
// ============================================================
|
||||
|
||||
#define LIBS_DIR "LIBS"
|
||||
#define WIDGET_DIR "WIDGETS"
|
||||
#define LOG_PATH "dvx.log"
|
||||
#define LIBS_DIR "LIBS"
|
||||
#define WIDGET_DIR "WIDGETS"
|
||||
#define LOG_PATH "dvx.log"
|
||||
#define INI_PATH "CONFIG/DVX.INI"
|
||||
#define HLPC_PATH "SYSTEM/DVXHLPC.EXE"
|
||||
|
||||
// ============================================================
|
||||
// Splash screen (delegates to platformSplash* in dvxPlatformDos.c)
|
||||
|
|
@ -60,7 +63,7 @@ static int32_t sSplashLoaded = 0;
|
|||
|
||||
|
||||
static void splashDrawScreen(void) {
|
||||
if (platformSplashLoadRaw("CONFIG/SPLASH.RAW")) {
|
||||
if (platformSplashLoadRaw("SYSTEM/SPLASH.RAW")) {
|
||||
platformSplashFillRect(PBAR_X - 1, PBAR_Y - 1, PBAR_W + 2, PBAR_H + 2, SPLASH_BAR_OUT);
|
||||
platformSplashFillRect(PBAR_X, PBAR_Y, PBAR_W, PBAR_H, SPLASH_BAR_BG);
|
||||
}
|
||||
|
|
@ -120,12 +123,14 @@ typedef struct {
|
|||
// ============================================================
|
||||
|
||||
static bool allDepsLoaded(const ModuleT *mod, const ModuleT *mods);
|
||||
static int32_t countHcfFilesRecurse(const char *dirPath);
|
||||
static void extractBaseName(const char *path, const char *ext, char *out, int32_t outSize);
|
||||
static void *findSymbol(void **handles, const char *symbol);
|
||||
static void freeMods(ModuleT *mods);
|
||||
static void **loadAllModules(void);
|
||||
static void loadInOrder(ModuleT *mods);
|
||||
static void logAndReadDeps(ModuleT *mods);
|
||||
static void processHcfDir(const char *dirPath);
|
||||
static void readDeps(ModuleT *mod);
|
||||
static void scanDir(const char *dirPath, const char *ext, ModuleT **mods);
|
||||
|
||||
|
|
@ -428,6 +433,404 @@ static void freeMods(ModuleT *mods) {
|
|||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// Help recompilation
|
||||
// ============================================================
|
||||
|
||||
static int32_t sLibCount = 0;
|
||||
static int32_t sWidgetCount = 0;
|
||||
|
||||
// ============================================================
|
||||
// Glob matching
|
||||
// ============================================================
|
||||
//
|
||||
// Simple glob pattern matching for filenames. Supports:
|
||||
// * matches zero or more characters
|
||||
// ? matches exactly one character
|
||||
// !pat at the start means "exclude files matching pat"
|
||||
// Case-insensitive. Exported for use by DXE modules.
|
||||
|
||||
bool platformGlobMatch(const char *pattern, const char *name) {
|
||||
while (*pattern && *name) {
|
||||
if (*pattern == '*') {
|
||||
pattern++;
|
||||
|
||||
if (!*pattern) {
|
||||
return true;
|
||||
}
|
||||
|
||||
while (*name) {
|
||||
if (platformGlobMatch(pattern, name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
name++;
|
||||
}
|
||||
|
||||
return false;
|
||||
} else if (*pattern == '?' || tolower((unsigned char)*pattern) == tolower((unsigned char)*name)) {
|
||||
pattern++;
|
||||
name++;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip trailing *
|
||||
while (*pattern == '*') {
|
||||
pattern++;
|
||||
}
|
||||
|
||||
return *pattern == '\0' && *name == '\0';
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// HCF-driven help recompilation
|
||||
// ============================================================
|
||||
//
|
||||
// Scans APPS/*/ for .hcf (help config) files. Each .hcf defines
|
||||
// an output .hlp file and source patterns:
|
||||
//
|
||||
// output = DVXHELP.HLP
|
||||
// source = LIBS/*.DHS
|
||||
// source = WIDGETS/*.DHS
|
||||
//
|
||||
// Source lines specify a directory/glob pattern. An optional !pattern
|
||||
// excludes matching files. The loader expands these by scanning
|
||||
// directories and matching filenames.
|
||||
|
||||
// Recursively count .hcf files under the given directory
|
||||
static int32_t countHcfFilesRecurse(const char *dirPath) {
|
||||
int32_t count = 0;
|
||||
DIR *dir = opendir(dirPath);
|
||||
|
||||
if (!dir) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dirent *ent;
|
||||
|
||||
while ((ent = readdir(dir)) != NULL) {
|
||||
if (ent->d_name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
char fullPath[DVX_MAX_PATH];
|
||||
snprintf(fullPath, sizeof(fullPath), "%s%c%s", dirPath, DVX_PATH_SEP, ent->d_name);
|
||||
|
||||
struct stat st;
|
||||
|
||||
if (stat(fullPath, &st) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
count += countHcfFilesRecurse(fullPath);
|
||||
} else {
|
||||
int32_t nameLen = (int32_t)strlen(ent->d_name);
|
||||
|
||||
if (nameLen > 4 && strcasecmp(ent->d_name + nameLen - 4, ".hcf") == 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
// Count .hcf files under APPS/
|
||||
static int32_t countHcfFiles(void) {
|
||||
return countHcfFilesRecurse("APPS");
|
||||
}
|
||||
|
||||
|
||||
// Process a single .hcf file: parse it and invoke the compiler.
|
||||
#define HELP_RESP_FILE "SYSTEM/HLPFILES.TMP"
|
||||
|
||||
// Write matching filenames to the response file (one per line).
|
||||
static void writeGlobToResp(FILE *resp, const char *pattern, const char *excludePattern) {
|
||||
// Split pattern into directory and filename glob
|
||||
char dirPart[DVX_MAX_PATH];
|
||||
const char *globPart = NULL;
|
||||
|
||||
snprintf(dirPart, sizeof(dirPart), "%s", pattern);
|
||||
char *lastSep = strrchr(dirPart, '/');
|
||||
|
||||
if (!lastSep) {
|
||||
lastSep = strrchr(dirPart, '\\');
|
||||
}
|
||||
|
||||
if (lastSep) {
|
||||
*lastSep = '\0';
|
||||
globPart = lastSep + 1;
|
||||
} else {
|
||||
globPart = pattern;
|
||||
dirPart[0] = '.';
|
||||
dirPart[1] = '\0';
|
||||
}
|
||||
|
||||
DIR *d = opendir(dirPart);
|
||||
|
||||
if (!d) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct dirent *ent;
|
||||
|
||||
while ((ent = readdir(d)) != NULL) {
|
||||
if (ent->d_name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
char fullPath[DVX_MAX_PATH];
|
||||
snprintf(fullPath, sizeof(fullPath), "%s%c%s", dirPart, DVX_PATH_SEP, ent->d_name);
|
||||
|
||||
struct stat st;
|
||||
|
||||
if (stat(fullPath, &st) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
char subPattern[DVX_MAX_PATH];
|
||||
snprintf(subPattern, sizeof(subPattern), "%s%c%s", fullPath, DVX_PATH_SEP, globPart);
|
||||
writeGlobToResp(resp, subPattern, excludePattern);
|
||||
} else if (platformGlobMatch(globPart, ent->d_name)) {
|
||||
if (excludePattern && platformGlobMatch(excludePattern, ent->d_name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(resp, "%s\n", fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
|
||||
static void processHcf(const char *hcfPath, const char *hcfDir) {
|
||||
FILE *f = fopen(hcfPath, "r");
|
||||
|
||||
if (!f) {
|
||||
return;
|
||||
}
|
||||
|
||||
char outputFile[DVX_MAX_PATH] = {0};
|
||||
|
||||
// Write matching source files to a response file
|
||||
FILE *resp = fopen(HELP_RESP_FILE, "w");
|
||||
|
||||
if (!resp) {
|
||||
fclose(f);
|
||||
dvxLog("helpRecompile: cannot create %s", HELP_RESP_FILE);
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasFiles = false;
|
||||
char line[512];
|
||||
|
||||
while (fgets(line, (int)sizeof(line), f)) {
|
||||
int32_t len = (int32_t)strlen(line);
|
||||
|
||||
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r' || line[len - 1] == ' ')) {
|
||||
line[--len] = '\0';
|
||||
}
|
||||
|
||||
if (line[0] == '#' || line[0] == '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strncasecmp(line, "output", 6) == 0) {
|
||||
const char *p = line + 6;
|
||||
|
||||
while (*p == ' ' || *p == '=') {
|
||||
p++;
|
||||
}
|
||||
|
||||
snprintf(outputFile, sizeof(outputFile), "%s%c%s", hcfDir, DVX_PATH_SEP, p);
|
||||
} else if (strncasecmp(line, "source", 6) == 0) {
|
||||
const char *p = line + 6;
|
||||
|
||||
while (*p == ' ' || *p == '=') {
|
||||
p++;
|
||||
}
|
||||
|
||||
char pattern[DVX_MAX_PATH] = {0};
|
||||
char exclude[DVX_MAX_PATH] = {0};
|
||||
|
||||
const char *bang = strchr(p, '!');
|
||||
|
||||
if (bang) {
|
||||
int32_t patLen = (int32_t)(bang - p);
|
||||
|
||||
while (patLen > 0 && p[patLen - 1] == ' ') {
|
||||
patLen--;
|
||||
}
|
||||
|
||||
memcpy(pattern, p, patLen);
|
||||
pattern[patLen] = '\0';
|
||||
bang++;
|
||||
|
||||
while (*bang == ' ') {
|
||||
bang++;
|
||||
}
|
||||
|
||||
snprintf(exclude, sizeof(exclude), "%s", bang);
|
||||
} else {
|
||||
snprintf(pattern, sizeof(pattern), "%s", p);
|
||||
}
|
||||
|
||||
long before = ftell(resp);
|
||||
writeGlobToResp(resp, pattern, exclude[0] ? exclude : NULL);
|
||||
|
||||
if (ftell(resp) > before) {
|
||||
hasFiles = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
fclose(resp);
|
||||
|
||||
if (!outputFile[0] || !hasFiles) {
|
||||
remove(HELP_RESP_FILE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Short command: compiler + output + response file
|
||||
char cmd[DVX_MAX_PATH * 2];
|
||||
snprintf(cmd, sizeof(cmd), "%s --quiet -o %s @%s", HLPC_PATH, outputFile, HELP_RESP_FILE);
|
||||
|
||||
dvxLog("helpRecompile: %s -> %s", hcfPath, outputFile);
|
||||
int rc = system(cmd);
|
||||
|
||||
if (rc != 0) {
|
||||
dvxLog("helpRecompile: FAILED (rc=%d)", rc);
|
||||
}
|
||||
|
||||
remove(HELP_RESP_FILE);
|
||||
}
|
||||
|
||||
|
||||
// Recursively scan a directory for .hcf files and process each one.
|
||||
static void processHcfDir(const char *dirPath) {
|
||||
DIR *dir = opendir(dirPath);
|
||||
|
||||
if (!dir) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct dirent *ent;
|
||||
|
||||
while ((ent = readdir(dir)) != NULL) {
|
||||
if (ent->d_name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
char fullPath[DVX_MAX_PATH];
|
||||
snprintf(fullPath, sizeof(fullPath), "%s%c%s", dirPath, DVX_PATH_SEP, ent->d_name);
|
||||
|
||||
struct stat st;
|
||||
|
||||
if (stat(fullPath, &st) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
processHcfDir(fullPath);
|
||||
} else {
|
||||
int32_t nameLen = (int32_t)strlen(ent->d_name);
|
||||
|
||||
if (nameLen > 4 && strcasecmp(ent->d_name + nameLen - 4, ".hcf") == 0) {
|
||||
processHcf(fullPath, dirPath);
|
||||
sSplashLoaded++;
|
||||
splashUpdateProgress();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
|
||||
static void helpRecompileIfNeeded(void) {
|
||||
PrefsHandleT *ini = prefsLoad(INI_PATH);
|
||||
|
||||
if (!ini) {
|
||||
dvxLog("helpRecompile: cannot load INI");
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t storedLibs = prefsGetInt(ini, "help", "libCount", -1);
|
||||
int32_t storedWidgets = prefsGetInt(ini, "help", "widgetCount", -1);
|
||||
|
||||
// First startup: no [help] section. Save counts, skip recompile.
|
||||
if (storedLibs == -1 && storedWidgets == -1) {
|
||||
dvxLog("helpRecompile: first startup, saving initial counts (libs=%d widgets=%d)",
|
||||
(int)sLibCount, (int)sWidgetCount);
|
||||
prefsSetInt(ini, "help", "libCount", sLibCount);
|
||||
prefsSetInt(ini, "help", "widgetCount", sWidgetCount);
|
||||
prefsSave(ini);
|
||||
prefsClose(ini);
|
||||
return;
|
||||
}
|
||||
|
||||
// Counts unchanged: no recompile needed
|
||||
if (storedLibs == sLibCount && storedWidgets == sWidgetCount) {
|
||||
prefsClose(ini);
|
||||
return;
|
||||
}
|
||||
|
||||
dvxLog("helpRecompile: counts changed (libs: %d->%d, widgets: %d->%d)",
|
||||
(int)storedLibs, (int)sLibCount, (int)storedWidgets, (int)sWidgetCount);
|
||||
|
||||
// Check if compiler exists
|
||||
FILE *test = fopen(HLPC_PATH, "rb");
|
||||
|
||||
if (!test) {
|
||||
dvxLog("helpRecompile: %s not found, skipping", HLPC_PATH);
|
||||
prefsSetInt(ini, "help", "libCount", sLibCount);
|
||||
prefsSetInt(ini, "help", "widgetCount", sWidgetCount);
|
||||
prefsSave(ini);
|
||||
prefsClose(ini);
|
||||
return;
|
||||
}
|
||||
|
||||
fclose(test);
|
||||
|
||||
// Count .hcf files for progress bar
|
||||
int32_t hcfCount = countHcfFiles();
|
||||
|
||||
if (hcfCount == 0) {
|
||||
dvxLog("helpRecompile: no .hcf files found");
|
||||
prefsSetInt(ini, "help", "libCount", sLibCount);
|
||||
prefsSetInt(ini, "help", "widgetCount", sWidgetCount);
|
||||
prefsSave(ini);
|
||||
prefsClose(ini);
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset progress bar for recompile phase
|
||||
sSplashLoaded = 0;
|
||||
sSplashTotal = hcfCount;
|
||||
platformSplashFillRect(PBAR_X, PBAR_Y, PBAR_W, PBAR_H, SPLASH_BAR_BG);
|
||||
|
||||
// Recursively scan APPS/ for .hcf files and process each
|
||||
processHcfDir("APPS");
|
||||
|
||||
// Save new counts
|
||||
prefsSetInt(ini, "help", "libCount", sLibCount);
|
||||
prefsSetInt(ini, "help", "widgetCount", sWidgetCount);
|
||||
prefsSave(ini);
|
||||
prefsClose(ini);
|
||||
|
||||
dvxLog("helpRecompile: done");
|
||||
}
|
||||
|
||||
|
||||
static void **loadAllModules(void) {
|
||||
void **handles = NULL;
|
||||
|
||||
|
|
@ -440,7 +843,9 @@ static void **loadAllModules(void) {
|
|||
scanDir(WIDGET_DIR, ".wgt", &widgets);
|
||||
logAndReadDeps(widgets);
|
||||
|
||||
sSplashTotal = (int32_t)arrlen(libs) + (int32_t)arrlen(widgets);
|
||||
sLibCount = (int32_t)arrlen(libs);
|
||||
sWidgetCount = (int32_t)arrlen(widgets);
|
||||
sSplashTotal = sLibCount + sWidgetCount;
|
||||
|
||||
// Phase 1: load libraries in dependency order
|
||||
loadInOrder(libs);
|
||||
|
|
@ -535,6 +940,9 @@ int main(int argc, char *argv[]) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
// Check if help files need recompilation (module count changed)
|
||||
helpRecompileIfNeeded();
|
||||
|
||||
// Find and call shellMain from whichever module exports it
|
||||
typedef int (*ShellMainFnT)(int, char **);
|
||||
ShellMainFnT shellMain = (ShellMainFnT)findSymbol(handles, "_shellMain");
|
||||
|
|
|
|||
11
mkcd.sh
11
mkcd.sh
|
|
@ -21,7 +21,7 @@ echo "Building DVX..."
|
|||
make -C "$SCRIPT_DIR" all
|
||||
|
||||
# Verify core build output exists
|
||||
for f in dvx.exe libs/libtasks.lib libs/libdvx.lib libs/dvxshell.lib; do
|
||||
for f in dvx.exe libs/kpunch/libtasks/libtasks.lib libs/kpunch/libdvx/libdvx.lib libs/kpunch/dvxshell/dvxshell.lib; do
|
||||
if [ ! -f "$SCRIPT_DIR/bin/$f" ]; then
|
||||
echo "ERROR: bin/$f not found -- build failed?"
|
||||
exit 1
|
||||
|
|
@ -30,11 +30,14 @@ 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
|
||||
while IFS= read -r f; do
|
||||
WGT_COUNT=$((WGT_COUNT + 1))
|
||||
done < <(find "$SCRIPT_DIR/bin/widgets/" -name "*.wgt" -type f 2>/dev/null)
|
||||
echo "$WGT_COUNT widget modules found in bin/widgets/."
|
||||
|
||||
# Clean DOSBox-X local filesystem artifacts
|
||||
find "$SCRIPT_DIR/bin/" -name ".DBLOCALFILE*" -delete 2>/dev/null
|
||||
|
||||
# Create the ISO image
|
||||
# -iso-level 1: strict 8.3 filenames (DOS compatibility)
|
||||
# -J: Joliet extensions (long names for Windows/Linux)
|
||||
|
|
|
|||
|
|
@ -13,20 +13,21 @@ LIBSDIR = ../bin/libs
|
|||
|
||||
SRCS = ../rs232/rs232.c ../packet/packet.c ../security/security.c ../seclink/secLink.c
|
||||
OBJS = $(OBJDIR)/rs232.o $(OBJDIR)/packet.o $(OBJDIR)/security.o $(OBJDIR)/secLink.o
|
||||
TARGET = $(LIBSDIR)/serial.lib
|
||||
TARGETDIR = $(LIBSDIR)/kpunch/serial
|
||||
TARGET = $(TARGETDIR)/serial.lib
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET) $(LIBSDIR)/serial.dep
|
||||
all: $(TARGET) $(TARGETDIR)/serial.dep
|
||||
|
||||
$(LIBSDIR)/serial.dep: ../config/serial.dep | $(LIBSDIR)
|
||||
$(TARGETDIR)/serial.dep: ../config/serial.dep | $(TARGETDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
|
||||
$(TARGET): $(OBJS) | $(LIBSDIR)
|
||||
$(DXE3GEN) -o $(LIBSDIR)/serial.dxe \
|
||||
$(TARGET): $(OBJS) | $(TARGETDIR)
|
||||
$(DXE3GEN) -o $(TARGETDIR)/serial.dxe \
|
||||
-E _rs232 -E _pkt -E _secLink -E _secDh -E _secCipher -E _secRng \
|
||||
-U $(OBJS)
|
||||
mv $(LIBSDIR)/serial.dxe $@
|
||||
mv $(TARGETDIR)/serial.dxe $@
|
||||
|
||||
$(OBJDIR)/rs232.o: ../rs232/rs232.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
|
@ -46,6 +47,9 @@ $(OBJDIR):
|
|||
$(LIBSDIR):
|
||||
mkdir -p $(LIBSDIR)
|
||||
|
||||
$(TARGETDIR):
|
||||
mkdir -p $(TARGETDIR)
|
||||
|
||||
# Dependencies
|
||||
$(OBJDIR)/rs232.o: ../rs232/rs232.c ../rs232/rs232.h
|
||||
$(OBJDIR)/packet.o: ../packet/packet.c ../packet/packet.h ../rs232/rs232.h
|
||||
|
|
@ -53,4 +57,4 @@ $(OBJDIR)/security.o: ../security/security.c ../security/security.h
|
|||
$(OBJDIR)/secLink.o: ../seclink/secLink.c ../seclink/secLink.h ../rs232/rs232.h ../packet/packet.h ../security/security.h
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(TARGET) $(LIBSDIR)/serial.dep
|
||||
rm -rf $(OBJS) $(TARGET) $(TARGETDIR)/serial.dep $(TARGETDIR)
|
||||
|
|
|
|||
644
serial/serial.dhs
Normal file
644
serial/serial.dhs
Normal file
|
|
@ -0,0 +1,644 @@
|
|||
.topic lib.serial
|
||||
.title Serial Stack
|
||||
.toc 1 Serial Stack
|
||||
.index Serial
|
||||
.index RS-232
|
||||
.index HDLC
|
||||
.index XTEA
|
||||
.index Diffie-Hellman
|
||||
.index secLink
|
||||
|
||||
.h1 Serial Stack
|
||||
|
||||
The DVX serial/networking stack provides reliable, optionally encrypted communication over RS-232 serial ports. It is composed of four layers, each building on the one below:
|
||||
|
||||
.list
|
||||
.item rs232 -- ISR-driven UART driver with ring buffers and flow control
|
||||
.item packet -- HDLC framing, CRC-16, Go-Back-N ARQ (reliable delivery)
|
||||
.item security -- 1024-bit Diffie-Hellman key exchange, XTEA-CTR cipher, DRBG RNG
|
||||
.item secLink -- Convenience wrapper: channel multiplexing, per-packet encryption
|
||||
.endlist
|
||||
|
||||
Loaded as: bin/libs/kpunch/serial/serial.lib
|
||||
|
||||
.link lib.serial.rs232 Layer 1: RS-232 UART Driver
|
||||
.link lib.serial.packet Layer 2: Packet Transport
|
||||
.link lib.serial.security Layer 3: Security (DH + XTEA)
|
||||
.link lib.serial.seclink Layer 4: Secure Link
|
||||
|
||||
.topic lib.serial.rs232
|
||||
.title RS-232 UART Driver
|
||||
.toc 1 RS-232 UART Driver
|
||||
.index rs232
|
||||
.index UART
|
||||
.index COM Port
|
||||
.index rs232Open
|
||||
.index rs232Close
|
||||
.index rs232Read
|
||||
.index rs232Write
|
||||
|
||||
.h1 RS-232 UART Driver
|
||||
|
||||
ISR-driven serial port driver supporting up to 4 simultaneous COM ports. A shared ISR drains UART FIFOs into per-port ring buffers (2048 bytes, power-of-2 for fast index wrapping). Flow control (XON/XOFF, RTS/CTS, DTR/DSR) operates within the ISR using watermark thresholds. All ISR data structures are DPMI-locked to prevent page faults.
|
||||
|
||||
Header: rs232/rs232.h
|
||||
|
||||
.h2 Port Constants
|
||||
|
||||
.table
|
||||
Constant Value Description
|
||||
-------- ----- -----------
|
||||
RS232_COM1 0 First serial port.
|
||||
RS232_COM2 1 Second serial port.
|
||||
RS232_COM3 2 Third serial port.
|
||||
RS232_COM4 3 Fourth serial port.
|
||||
.endtable
|
||||
|
||||
.h2 Handshake Modes
|
||||
|
||||
.table
|
||||
Constant Value Description
|
||||
-------- ----- -----------
|
||||
RS232_HANDSHAKE_NONE 0 No flow control.
|
||||
RS232_HANDSHAKE_XONXOFF 1 Software flow control (XON/XOFF characters).
|
||||
RS232_HANDSHAKE_RTSCTS 2 Hardware flow control via RTS/CTS lines.
|
||||
RS232_HANDSHAKE_DTRDSR 3 Hardware flow control via DTR/DSR lines.
|
||||
.endtable
|
||||
|
||||
.h2 UART Types
|
||||
|
||||
Detected automatically by probing the scratch register and FIFO capability.
|
||||
|
||||
.table
|
||||
Constant Value Description
|
||||
-------- ----- -----------
|
||||
RS232_UART_UNKNOWN 0 Detection failed.
|
||||
RS232_UART_8250 1 Original IBM PC UART. No FIFO.
|
||||
RS232_UART_16450 2 Has scratch register, no FIFO.
|
||||
RS232_UART_16550 3 Has FIFO but buggy (rare).
|
||||
RS232_UART_16550A 4 Working 16-byte FIFO. Most common in 486+ hardware.
|
||||
.endtable
|
||||
|
||||
.h2 Error Codes
|
||||
|
||||
.table
|
||||
Constant Value Description
|
||||
-------- ----- -----------
|
||||
RS232_SUCCESS 0 Operation succeeded.
|
||||
RS232_ERR_UNKNOWN -1 Unknown error.
|
||||
RS232_ERR_NOT_OPEN -2 Port is not open.
|
||||
RS232_ERR_ALREADY_OPEN -3 Port is already open.
|
||||
RS232_ERR_NO_UART -4 No UART detected at port address.
|
||||
RS232_ERR_INVALID_PORT -5 Invalid port number.
|
||||
RS232_ERR_INVALID_BASE -6 Invalid base I/O address.
|
||||
RS232_ERR_INVALID_IRQ -7 Invalid IRQ number.
|
||||
RS232_ERR_INVALID_BPS -8 Invalid baud rate.
|
||||
RS232_ERR_INVALID_DATA -9 Invalid data bits value.
|
||||
RS232_ERR_INVALID_PARITY -10 Invalid parity character.
|
||||
RS232_ERR_INVALID_STOP -11 Invalid stop bits value.
|
||||
RS232_ERR_INVALID_HANDSHAKE -12 Invalid handshake mode.
|
||||
RS232_ERR_INVALID_FIFO -13 Invalid FIFO threshold.
|
||||
RS232_ERR_NULL_PTR -14 NULL pointer argument.
|
||||
RS232_ERR_IRQ_NOT_FOUND -15 Could not detect IRQ for port.
|
||||
RS232_ERR_LOCK_MEM -16 DPMI memory lock failed.
|
||||
.endtable
|
||||
|
||||
.h2 rs232Open
|
||||
|
||||
Open a COM port with the specified line parameters.
|
||||
|
||||
.code
|
||||
int rs232Open(int com, int32_t bps, int dataBits,
|
||||
char parity, int stopBits, int handshake);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
com Port index (RS232_COM1..RS232_COM4).
|
||||
bps Baud rate (e.g. 9600, 19200, 38400, 57600, 115200).
|
||||
dataBits Data bits per character (5, 6, 7, or 8).
|
||||
parity Parity mode: 'N' (none), 'O' (odd), 'E' (even), 'M' (mark), 'S' (space).
|
||||
stopBits Stop bits (1 or 2).
|
||||
handshake Flow control mode (RS232_HANDSHAKE_*).
|
||||
.endtable
|
||||
|
||||
Returns RS232_SUCCESS or a negative error code.
|
||||
|
||||
.h2 rs232Close
|
||||
|
||||
Close a COM port and release its ISR resources.
|
||||
|
||||
.code
|
||||
int rs232Close(int com);
|
||||
.endcode
|
||||
|
||||
.h2 rs232Read
|
||||
|
||||
Non-blocking read from the receive ring buffer.
|
||||
|
||||
.code
|
||||
int rs232Read(int com, char *data, int len);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
com Port index.
|
||||
data Buffer to receive data.
|
||||
len Maximum bytes to read.
|
||||
.endtable
|
||||
|
||||
Returns the number of bytes actually read (may be less than len).
|
||||
|
||||
.h2 rs232Write
|
||||
|
||||
Blocking polled write directly to UART transmit holding register. Bypasses the TX ring buffer. Use for small, immediate writes.
|
||||
|
||||
.code
|
||||
int rs232Write(int com, const char *data, int len);
|
||||
.endcode
|
||||
|
||||
Returns RS232_SUCCESS or a negative error code.
|
||||
|
||||
.h2 rs232WriteBuf
|
||||
|
||||
Non-blocking write to the transmit ring buffer. The ISR drains buffered data to the UART asynchronously.
|
||||
|
||||
.code
|
||||
int rs232WriteBuf(int com, const char *data, int len);
|
||||
.endcode
|
||||
|
||||
Returns the number of bytes actually queued (may be less than len if the buffer is full).
|
||||
|
||||
.h2 rs232Set
|
||||
|
||||
Change all line parameters on an open port.
|
||||
|
||||
.code
|
||||
int rs232Set(int com, int32_t bps, int dataBits,
|
||||
char parity, int stopBits, int handshake);
|
||||
.endcode
|
||||
|
||||
.h2 Configuration Getters
|
||||
|
||||
.table
|
||||
Function Returns
|
||||
-------- -------
|
||||
rs232GetBase(com) I/O base address.
|
||||
rs232GetBps(com) Current baud rate.
|
||||
rs232GetData(com) Data bits setting.
|
||||
rs232GetParity(com) Parity character ('N', 'O', 'E', 'M', 'S').
|
||||
rs232GetStop(com) Stop bits setting.
|
||||
rs232GetHandshake(com) Handshake mode.
|
||||
rs232GetIrq(com) IRQ number.
|
||||
rs232GetUartType(com) Detected UART type (RS232_UART_*).
|
||||
.endtable
|
||||
|
||||
.h2 Status Getters
|
||||
|
||||
.table
|
||||
Function Returns
|
||||
-------- -------
|
||||
rs232GetRxBuffered(com) Bytes waiting in the receive ring buffer.
|
||||
rs232GetTxBuffered(com) Bytes waiting in the transmit ring buffer.
|
||||
rs232GetCts(com) CTS line state (0 or 1).
|
||||
rs232GetDsr(com) DSR line state (0 or 1).
|
||||
rs232GetDtr(com) DTR line state (0 or 1).
|
||||
rs232GetRts(com) RTS line state (0 or 1).
|
||||
rs232GetLsr(com) Line status register value.
|
||||
rs232GetMcr(com) Modem control register value.
|
||||
rs232GetMsr(com) Modem status register value.
|
||||
.endtable
|
||||
|
||||
.h2 Configuration Setters
|
||||
|
||||
.table
|
||||
Function Description
|
||||
-------- -----------
|
||||
rs232SetBase(com, base) Set I/O base address (before open).
|
||||
rs232SetBps(com, bps) Change baud rate.
|
||||
rs232SetData(com, dataBits) Change data bits.
|
||||
rs232SetParity(com, parity) Change parity mode.
|
||||
rs232SetStop(com, stopBits) Change stop bits.
|
||||
rs232SetHandshake(com, handshake) Change flow control mode.
|
||||
rs232SetIrq(com, irq) Set IRQ number (before open).
|
||||
rs232SetMcr(com, mcr) Set modem control register directly.
|
||||
rs232SetDtr(com, dtr) Set DTR line state.
|
||||
rs232SetRts(com, rts) Set RTS line state.
|
||||
rs232SetFifoThreshold(com, thr) Set FIFO trigger level (1, 4, 8, or 14 bytes).
|
||||
.endtable
|
||||
|
||||
.h2 Buffer Management
|
||||
|
||||
.table
|
||||
Function Description
|
||||
-------- -----------
|
||||
rs232ClearRxBuffer(com) Discard all data in the receive ring buffer.
|
||||
rs232ClearTxBuffer(com) Discard all data in the transmit ring buffer.
|
||||
.endtable
|
||||
|
||||
.topic lib.serial.packet
|
||||
.title Packet Transport
|
||||
.toc 1 Packet Transport
|
||||
.index packet
|
||||
.index HDLC
|
||||
.index CRC-16
|
||||
.index Go-Back-N
|
||||
.index pktOpen
|
||||
.index pktClose
|
||||
.index pktSend
|
||||
.index pktPoll
|
||||
|
||||
.h1 Packet Transport
|
||||
|
||||
Reliable, ordered packet delivery over an RS-232 serial link. Uses HDLC-style byte-stuffed framing for frame delimiting, CRC-16-CCITT for error detection, and Go-Back-N ARQ with a configurable sliding window for retransmission.
|
||||
|
||||
Header: packet/packet.h
|
||||
|
||||
.h2 Constants
|
||||
|
||||
.table
|
||||
Constant Value Description
|
||||
-------- ----- -----------
|
||||
PKT_MAX_PAYLOAD 255 Maximum bytes per packet payload.
|
||||
PKT_DEFAULT_WINDOW 4 Default sliding window size (unacknowledged frames in flight).
|
||||
PKT_MAX_WINDOW 8 Maximum sliding window size.
|
||||
.endtable
|
||||
|
||||
.h2 Error Codes
|
||||
|
||||
.table
|
||||
Constant Value Description
|
||||
-------- ----- -----------
|
||||
PKT_SUCCESS 0 Operation succeeded.
|
||||
PKT_ERR_INVALID_PORT -1 Invalid COM port index.
|
||||
PKT_ERR_NOT_OPEN -2 Connection is not open.
|
||||
PKT_ERR_ALREADY_OPEN -3 Connection is already open.
|
||||
PKT_ERR_WOULD_BLOCK -4 Operation would block.
|
||||
PKT_ERR_OVERFLOW -5 Buffer overflow.
|
||||
PKT_ERR_INVALID_PARAM -6 Invalid parameter.
|
||||
PKT_ERR_TX_FULL -7 Transmit window is full.
|
||||
PKT_ERR_NO_DATA -8 No data available.
|
||||
PKT_ERR_DISCONNECTED -9 Remote side disconnected.
|
||||
.endtable
|
||||
|
||||
.h2 PktRecvCallbackT
|
||||
|
||||
Callback type for received packets.
|
||||
|
||||
.code
|
||||
typedef void (*PktRecvCallbackT)(void *ctx,
|
||||
const uint8_t *data, int len);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
ctx User context pointer (passed to pktOpen).
|
||||
data Payload bytes (valid only during the callback).
|
||||
len Payload length.
|
||||
.endtable
|
||||
|
||||
.h2 pktOpen
|
||||
|
||||
Open a packetized connection over an already-open COM port.
|
||||
|
||||
.code
|
||||
PktConnT *pktOpen(int com, int windowSize,
|
||||
PktRecvCallbackT callback, void *callbackCtx);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
com COM port index (RS232_COM1..RS232_COM4). Must already be open via rs232Open.
|
||||
windowSize Sliding window size (1..PKT_MAX_WINDOW). Pass 0 for PKT_DEFAULT_WINDOW.
|
||||
callback Called when a complete, CRC-verified packet is received.
|
||||
callbackCtx User pointer passed to the callback.
|
||||
.endtable
|
||||
|
||||
Returns an opaque PktConnT handle, or NULL on failure.
|
||||
|
||||
.h2 pktClose
|
||||
|
||||
Close a packetized connection. Does not close the underlying COM port.
|
||||
|
||||
.code
|
||||
void pktClose(PktConnT *conn);
|
||||
.endcode
|
||||
|
||||
.h2 pktSend
|
||||
|
||||
Send a data packet.
|
||||
|
||||
.code
|
||||
int pktSend(PktConnT *conn, const uint8_t *data,
|
||||
int len, bool block);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
conn Connection handle from pktOpen.
|
||||
data Payload bytes (up to PKT_MAX_PAYLOAD).
|
||||
len Payload length.
|
||||
block If true, block until transmit window has space. If false, return PKT_ERR_TX_FULL when the window is full.
|
||||
.endtable
|
||||
|
||||
Returns PKT_SUCCESS or a negative error code.
|
||||
|
||||
.h2 pktPoll
|
||||
|
||||
Poll for incoming data, process received frames, and handle retransmits. Must be called frequently in the main loop.
|
||||
|
||||
.code
|
||||
int pktPoll(PktConnT *conn);
|
||||
.endcode
|
||||
|
||||
Returns the number of valid data packets delivered to the callback.
|
||||
|
||||
.h2 pktReset
|
||||
|
||||
Reset the connection state (sequence numbers, buffers) and send a RST frame to the remote side.
|
||||
|
||||
.code
|
||||
int pktReset(PktConnT *conn);
|
||||
.endcode
|
||||
|
||||
.h2 pktCanSend
|
||||
|
||||
Check whether there is room in the transmit window for another packet.
|
||||
|
||||
.code
|
||||
bool pktCanSend(PktConnT *conn);
|
||||
.endcode
|
||||
|
||||
.h2 pktGetPending
|
||||
|
||||
Get the number of unacknowledged packets currently in the transmit window.
|
||||
|
||||
.code
|
||||
int pktGetPending(PktConnT *conn);
|
||||
.endcode
|
||||
|
||||
.topic lib.serial.security
|
||||
.title Security (DH + XTEA)
|
||||
.toc 1 Security (DH + XTEA)
|
||||
.index Security
|
||||
.index Diffie-Hellman
|
||||
.index XTEA
|
||||
.index XTEA-CTR
|
||||
.index DRBG
|
||||
.index secDhCreate
|
||||
.index secCipherCreate
|
||||
.index secRngBytes
|
||||
|
||||
.h1 Security (DH + XTEA)
|
||||
|
||||
Cryptographic primitives for the serial stack: 1024-bit Diffie-Hellman key exchange (RFC 2409 Group 2), XTEA block cipher in CTR mode, and an XTEA-CTR DRBG random number generator.
|
||||
|
||||
XTEA requires no lookup tables and compiles to approximately 20 instructions per round, making it suitable for 486-class hardware where AES S-box tables would thrash the 8KB cache.
|
||||
|
||||
Header: security/security.h
|
||||
|
||||
.h2 Constants
|
||||
|
||||
.table
|
||||
Constant Value Description
|
||||
-------- ----- -----------
|
||||
SEC_DH_KEY_SIZE 128 Size of a DH public key in bytes (1024 bits).
|
||||
SEC_XTEA_KEY_SIZE 16 Size of an XTEA key in bytes (128 bits).
|
||||
.endtable
|
||||
|
||||
.h2 Error Codes
|
||||
|
||||
.table
|
||||
Constant Value Description
|
||||
-------- ----- -----------
|
||||
SEC_SUCCESS 0 Operation succeeded.
|
||||
SEC_ERR_PARAM -1 Invalid parameter.
|
||||
SEC_ERR_NOT_READY -2 DH context not ready (keys not generated or secret not computed).
|
||||
SEC_ERR_ALLOC -3 Memory allocation failed.
|
||||
.endtable
|
||||
|
||||
.h2 RNG Functions
|
||||
|
||||
The RNG uses XTEA-CTR as a DRBG. Hardware entropy from PIT timer jitter is weak (~20 bits); supplement with keyboard timing or mouse jitter before generating DH keys.
|
||||
|
||||
.code
|
||||
void secRngSeed(const uint8_t *entropy, int len);
|
||||
int secRngGatherEntropy(uint8_t *buf, int len);
|
||||
void secRngAddEntropy(const uint8_t *data, int len);
|
||||
void secRngBytes(uint8_t *buf, int len);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Function Description
|
||||
-------- -----------
|
||||
secRngSeed Seed the DRBG from an entropy buffer.
|
||||
secRngGatherEntropy Gather hardware entropy (PIT jitter, BIOS tick count) into a buffer.
|
||||
secRngAddEntropy Mix additional entropy into the DRBG state.
|
||||
secRngBytes Generate pseudorandom bytes.
|
||||
.endtable
|
||||
|
||||
.h2 Diffie-Hellman Key Exchange
|
||||
|
||||
1024-bit DH with 256-bit private exponents using the RFC 2409 Group 2 safe prime.
|
||||
|
||||
.code
|
||||
SecDhT *secDhCreate(void);
|
||||
int secDhGenerateKeys(SecDhT *dh);
|
||||
int secDhGetPublicKey(SecDhT *dh, uint8_t *buf, int *len);
|
||||
int secDhComputeSecret(SecDhT *dh,
|
||||
const uint8_t *remotePub, int len);
|
||||
int secDhDeriveKey(SecDhT *dh, uint8_t *key, int keyLen);
|
||||
void secDhDestroy(SecDhT *dh);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Function Description
|
||||
-------- -----------
|
||||
secDhCreate Allocate a new DH context.
|
||||
secDhGenerateKeys Generate a private exponent and public key. RNG must be seeded first.
|
||||
secDhGetPublicKey Copy the local public key into buf. *len receives the key size.
|
||||
secDhComputeSecret Compute the shared secret from the remote public key.
|
||||
secDhDeriveKey Derive a symmetric key (XTEA key) from the shared secret via hashing.
|
||||
secDhDestroy Free the DH context and all associated memory.
|
||||
.endtable
|
||||
|
||||
Typical usage order: secDhCreate, secDhGenerateKeys, exchange public keys, secDhComputeSecret, secDhDeriveKey, secDhDestroy.
|
||||
|
||||
.h2 XTEA-CTR Cipher
|
||||
|
||||
XTEA in counter mode. Encrypt and decrypt are the same operation (XOR with keystream). The counter must never repeat with the same key.
|
||||
|
||||
.code
|
||||
SecCipherT *secCipherCreate(const uint8_t *key);
|
||||
void secCipherSetNonce(SecCipherT *c,
|
||||
uint32_t nonceLo, uint32_t nonceHi);
|
||||
void secCipherCrypt(SecCipherT *c,
|
||||
uint8_t *data, int len);
|
||||
void secCipherDestroy(SecCipherT *c);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Function Description
|
||||
-------- -----------
|
||||
secCipherCreate Allocate a cipher context with a 128-bit key.
|
||||
secCipherSetNonce Set the initial counter value (nonce). Must be unique per session.
|
||||
secCipherCrypt Encrypt or decrypt data in place. The counter increments automatically.
|
||||
secCipherDestroy Free the cipher context.
|
||||
.endtable
|
||||
|
||||
.topic lib.serial.seclink
|
||||
.title Secure Link
|
||||
.toc 1 Secure Link
|
||||
.index secLink
|
||||
.index secLinkOpen
|
||||
.index secLinkClose
|
||||
.index secLinkHandshake
|
||||
.index secLinkSend
|
||||
.index secLinkPoll
|
||||
|
||||
.h1 Secure Link
|
||||
|
||||
Top-level API composing all three lower layers (rs232, packet, security) into a single interface. Provides channel multiplexing (0-127) and per-packet encryption control over a reliable serial link.
|
||||
|
||||
Each packet carries a one-byte header: bit 7 is the encrypted flag, bits 6..0 are the channel number. Unencrypted packets can be sent before the handshake completes (e.g., for version negotiation).
|
||||
|
||||
Header: seclink/secLink.h
|
||||
|
||||
.h2 Constants
|
||||
|
||||
.table
|
||||
Constant Value Description
|
||||
-------- ----- -----------
|
||||
SECLINK_MAX_PAYLOAD 254 Maximum plaintext bytes per send (PKT_MAX_PAYLOAD minus 1-byte channel header).
|
||||
SECLINK_MAX_CHANNEL 127 Highest valid channel number.
|
||||
.endtable
|
||||
|
||||
.h2 Error Codes
|
||||
|
||||
.table
|
||||
Constant Value Description
|
||||
-------- ----- -----------
|
||||
SECLINK_SUCCESS 0 Operation succeeded.
|
||||
SECLINK_ERR_PARAM -1 Invalid parameter.
|
||||
SECLINK_ERR_SERIAL -2 RS-232 layer error.
|
||||
SECLINK_ERR_ALLOC -3 Memory allocation failed.
|
||||
SECLINK_ERR_HANDSHAKE -4 DH handshake failed.
|
||||
SECLINK_ERR_NOT_READY -5 Handshake not yet completed (encrypted send attempted).
|
||||
SECLINK_ERR_SEND -6 Packet send failed.
|
||||
.endtable
|
||||
|
||||
.h2 SecLinkRecvT
|
||||
|
||||
Receive callback type. Delivers decrypted plaintext with the channel number.
|
||||
|
||||
.code
|
||||
typedef void (*SecLinkRecvT)(void *ctx,
|
||||
const uint8_t *data, int len, uint8_t channel);
|
||||
.endcode
|
||||
|
||||
.h2 secLinkOpen
|
||||
|
||||
Open a secure serial link. Opens the COM port and packet layer internally.
|
||||
|
||||
.code
|
||||
SecLinkT *secLinkOpen(int com, int32_t bps, int dataBits,
|
||||
char parity, int stopBits, int handshake,
|
||||
SecLinkRecvT callback, void *ctx);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
com COM port index (RS232_COM1..RS232_COM4).
|
||||
bps Baud rate.
|
||||
dataBits Data bits per character (5-8).
|
||||
parity Parity mode ('N', 'O', 'E', 'M', 'S').
|
||||
stopBits Stop bits (1 or 2).
|
||||
handshake Flow control mode (RS232_HANDSHAKE_*).
|
||||
callback Called when a packet is received (plaintext, with channel).
|
||||
ctx User pointer passed to the callback.
|
||||
.endtable
|
||||
|
||||
Returns an opaque SecLinkT handle, or NULL on failure.
|
||||
|
||||
.h2 secLinkClose
|
||||
|
||||
Close the link, free all resources, and close the COM port.
|
||||
|
||||
.code
|
||||
void secLinkClose(SecLinkT *link);
|
||||
.endcode
|
||||
|
||||
.h2 secLinkHandshake
|
||||
|
||||
Perform Diffie-Hellman key exchange. Blocks until both sides complete. The RNG must be seeded before calling this.
|
||||
|
||||
.code
|
||||
int secLinkHandshake(SecLinkT *link);
|
||||
.endcode
|
||||
|
||||
Returns SECLINK_SUCCESS or SECLINK_ERR_HANDSHAKE.
|
||||
|
||||
.h2 secLinkSend
|
||||
|
||||
Send data on a channel, optionally encrypted.
|
||||
|
||||
.code
|
||||
int secLinkSend(SecLinkT *link, const uint8_t *data,
|
||||
int len, uint8_t channel, bool encrypt, bool block);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
link Connection handle from secLinkOpen.
|
||||
data Plaintext payload (1..SECLINK_MAX_PAYLOAD bytes).
|
||||
len Payload length.
|
||||
channel Logical channel number (0..SECLINK_MAX_CHANNEL).
|
||||
encrypt If true, encrypt before sending (requires completed handshake).
|
||||
block If true, block until the transmit window has space.
|
||||
.endtable
|
||||
|
||||
Returns SECLINK_SUCCESS or a negative error code.
|
||||
|
||||
.h2 secLinkSendBuf
|
||||
|
||||
Send an arbitrarily large buffer by splitting into SECLINK_MAX_PAYLOAD chunks. Always blocks until all data is sent.
|
||||
|
||||
.code
|
||||
int secLinkSendBuf(SecLinkT *link, const uint8_t *data,
|
||||
int len, uint8_t channel, bool encrypt);
|
||||
.endcode
|
||||
|
||||
Returns SECLINK_SUCCESS or the first error encountered.
|
||||
|
||||
.h2 secLinkPoll
|
||||
|
||||
Poll for incoming data. Decrypts encrypted packets and delivers plaintext to the receive callback.
|
||||
|
||||
.code
|
||||
int secLinkPoll(SecLinkT *link);
|
||||
.endcode
|
||||
|
||||
Returns the number of packets delivered, or a negative error code.
|
||||
|
||||
.h2 secLinkIsReady
|
||||
|
||||
Check whether the handshake is complete and the link is ready for encrypted data.
|
||||
|
||||
.code
|
||||
bool secLinkIsReady(SecLinkT *link);
|
||||
.endcode
|
||||
|
||||
.h2 secLinkGetPending
|
||||
|
||||
Get the number of unacknowledged packets in the transmit window.
|
||||
|
||||
.code
|
||||
int secLinkGetPending(SecLinkT *link);
|
||||
.endcode
|
||||
|
|
@ -15,21 +15,22 @@ WPAPERDIR = ../bin/config/wpaper
|
|||
|
||||
SRCS = shellMain.c shellApp.c shellInfo.c
|
||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||
TARGET = $(LIBSDIR)/dvxshell.lib
|
||||
TARGETDIR = $(LIBSDIR)/kpunch/dvxshell
|
||||
TARGET = $(TARGETDIR)/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)
|
||||
all: $(TARGET) $(TARGETDIR)/dvxshell.dep $(CONFIGDIR)/dvx.ini $(THEMES) $(WPAPERS)
|
||||
|
||||
$(LIBSDIR)/dvxshell.dep: ../config/dvxshell.dep | $(LIBSDIR)
|
||||
$(TARGETDIR)/dvxshell.dep: ../config/dvxshell.dep | $(TARGETDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
|
||||
$(TARGET): $(OBJS) | $(LIBSDIR)
|
||||
$(DXE3GEN) -o $(LIBSDIR)/dvxshell.dxe -E _shell -U $(OBJS)
|
||||
mv $(LIBSDIR)/dvxshell.dxe $@
|
||||
$(TARGET): $(OBJS) | $(TARGETDIR)
|
||||
$(DXE3GEN) -o $(TARGETDIR)/dvxshell.dxe -E _shell -U $(OBJS)
|
||||
mv $(TARGETDIR)/dvxshell.dxe $@
|
||||
|
||||
$(CONFIGDIR)/dvx.ini: ../config/dvx.ini | $(CONFIGDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
|
|
@ -43,6 +44,9 @@ $(OBJDIR):
|
|||
$(LIBSDIR):
|
||||
mkdir -p $(LIBSDIR)
|
||||
|
||||
$(TARGETDIR):
|
||||
mkdir -p $(TARGETDIR)
|
||||
|
||||
$(CONFIGDIR):
|
||||
mkdir -p $(CONFIGDIR)
|
||||
|
||||
|
|
@ -65,5 +69,5 @@ $(OBJDIR)/shellApp.o: shellApp.c $(SHELL_DEPS)
|
|||
$(OBJDIR)/shellInfo.o: shellInfo.c shellInfo.h $(SHELL_DEPS)
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(TARGET) $(LIBSDIR)/dvxshell.dep
|
||||
rm -rf $(OBJS) $(TARGET) $(TARGETDIR)/dvxshell.dep $(TARGETDIR)
|
||||
rm -rf $(WPAPERDIR) $(THEMEDIR) $(CONFIGDIR)
|
||||
|
|
|
|||
359
shell/dvxshell.dhs
Normal file
359
shell/dvxshell.dhs
Normal file
|
|
@ -0,0 +1,359 @@
|
|||
.topic shell.overview
|
||||
.title DVX Shell Library
|
||||
.toc 0 DVX Shell Library
|
||||
.default
|
||||
.index DVX Shell
|
||||
.index shellApp.h
|
||||
.index DXE Application Loading
|
||||
|
||||
.h1 DVX Shell Library
|
||||
|
||||
The DVX shell library manages the lifecycle of DXE applications: loading, launching, tracking, and reaping. It provides the bridge between the DVX GUI compositor and dynamically loaded DXE3 application modules.
|
||||
|
||||
Header: shell/shellApp.h
|
||||
|
||||
.h2 App Model
|
||||
|
||||
The shell supports two kinds of DXE apps:
|
||||
|
||||
.list
|
||||
.item Callback-only (hasMainLoop = false) -- appMain() runs in the shell's task 0, creates windows, registers event callbacks, and returns immediately. The app lives through GUI callbacks. Lifecycle ends when the last window is closed. Simpler and cheaper (no extra stack/task).
|
||||
.item Main-loop (hasMainLoop = true) -- A dedicated cooperative task is created. appMain() runs in that task and can do its own polling loop, calling tsYield() to share CPU. Lifecycle ends when appMain() returns or the task is killed. Needed for terminal emulators, games, or long computations.
|
||||
.endlist
|
||||
|
||||
Both types use the same DXE interface: an exported appDescriptor and appMain function.
|
||||
|
||||
.h2 DXE Interface
|
||||
|
||||
Every .app DXE module must export these symbols (COFF convention uses leading underscore):
|
||||
|
||||
.table
|
||||
Symbol Type Required
|
||||
------ ---- --------
|
||||
_appDescriptor AppDescriptorT Yes
|
||||
_appMain int32_t (*)(DxeAppContextT *) Yes
|
||||
_appShutdown void (*)(void) No
|
||||
.endtable
|
||||
|
||||
.h2 State Machine
|
||||
|
||||
App slots progress through four states:
|
||||
|
||||
.code
|
||||
Free -> Loaded -> Running -> Terminating -> Free
|
||||
.endcode
|
||||
|
||||
LoadedE is transient (only during shellLoadApp before the entry point is called). TerminatingE means the app's task has exited but cleanup has not yet occurred. The shell's main loop reaps terminating apps each frame via shellReapApps().
|
||||
|
||||
.h2 Contents
|
||||
|
||||
.link shell.types Types and Constants
|
||||
.link shell.lifecycle Lifecycle API
|
||||
.link shell.query Query API
|
||||
.link shell.config Configuration API
|
||||
.link shell.desktop Desktop Callbacks
|
||||
.link shell.info System Information
|
||||
|
||||
.topic shell.types
|
||||
.title Shell Types and Constants
|
||||
.toc 1 Types and Constants
|
||||
.index AppDescriptorT
|
||||
.index DxeAppContextT
|
||||
.index ShellAppT
|
||||
.index AppStateE
|
||||
.index SHELL_APP_NAME_MAX
|
||||
.index SHELL_STACK_DEFAULT
|
||||
.index SHELL_DESKTOP_APP
|
||||
|
||||
.h1 Types and Constants
|
||||
|
||||
.h2 Constants
|
||||
|
||||
.table
|
||||
Constant Value Description
|
||||
-------- ----- -----------
|
||||
SHELL_APP_NAME_MAX 64 Maximum length of an app name string.
|
||||
SHELL_STACK_DEFAULT 0 Use in AppDescriptorT.stackSize to get the default task stack size.
|
||||
SHELL_DESKTOP_APP "apps/kpunch/progman/progman.app" Default desktop application path.
|
||||
.endtable
|
||||
|
||||
.h2 AppDescriptorT
|
||||
|
||||
Exported by every DXE app as a global named appDescriptor. The shell reads it at load time to determine how to launch the app.
|
||||
|
||||
.table
|
||||
Field Type Description
|
||||
----- ---- -----------
|
||||
name[SHELL_APP_NAME_MAX] char[] Display name of the application.
|
||||
hasMainLoop bool false = callback-only, true = dedicated task.
|
||||
multiInstance bool true = allow multiple simultaneous instances via temp copy.
|
||||
stackSize int32_t SHELL_STACK_DEFAULT or explicit byte count for the task stack.
|
||||
priority int32_t TS_PRIORITY_* value or custom priority for the task.
|
||||
.endtable
|
||||
|
||||
.h2 DxeAppContextT
|
||||
|
||||
Passed as the sole argument to appMain(). Gives the app access to the shell's GUI context and its own identity.
|
||||
|
||||
.table
|
||||
Field Type Description
|
||||
----- ---- -----------
|
||||
shellCtx AppContextT * The shell's GUI context (for creating windows, drawing, etc.).
|
||||
appId int32_t This app's unique ID (slot index, 1-based).
|
||||
appPath[DVX_MAX_PATH] char[] Full path to the .app DXE file.
|
||||
appDir[DVX_MAX_PATH] char[] Directory containing the .app file (for loading resources).
|
||||
configDir[DVX_MAX_PATH] char[] Writable config directory (e.g. CONFIG/APPS/KPUNCH/DVXBASIC/).
|
||||
args[1024] char[] Launch arguments (empty string if none).
|
||||
helpFile[DVX_MAX_PATH] char[] Help file path (for F1 context help).
|
||||
helpTopic[128] char[] Current help topic ID (updated by the app at runtime).
|
||||
.endtable
|
||||
|
||||
The appDir field is derived from the .app file path at load time so apps can find their own resources via relative paths. This is necessary because the working directory is shared by all apps in DOS.
|
||||
|
||||
.h2 AppStateE
|
||||
|
||||
.table
|
||||
Value Description
|
||||
----- -----------
|
||||
AppStateFreeE Slot is available for reuse.
|
||||
AppStateLoadedE DXE loaded, entry point not yet called (transient).
|
||||
AppStateRunningE Entry point called, app is active.
|
||||
AppStateTerminatingE Shutdown in progress, awaiting reap.
|
||||
.endtable
|
||||
|
||||
.h2 ShellAppT
|
||||
|
||||
Per-app slot in the shell's app table. Slot 0 is reserved for the shell itself; apps use slots 1 and above.
|
||||
|
||||
.table
|
||||
Field Type Description
|
||||
----- ---- -----------
|
||||
appId int32_t Unique ID (slot index, 1-based; 0 = shell).
|
||||
name[SHELL_APP_NAME_MAX] char[] Display name from AppDescriptorT.
|
||||
path[DVX_MAX_PATH] char[] Original DXE file path.
|
||||
tempPath[DVX_MAX_PATH] char[] Temp copy path for multi-instance (empty if not a copy).
|
||||
dxeHandle void * dlopen() handle for the DXE module.
|
||||
state AppStateE Current lifecycle state.
|
||||
hasMainLoop bool Whether this app has a dedicated task.
|
||||
mainTaskId uint32_t Task ID if hasMainLoop, else 0.
|
||||
entryFn int32_t (*)(DxeAppContextT *) Pointer to appMain.
|
||||
shutdownFn void (*)(void) Pointer to appShutdown (may be NULL).
|
||||
dxeCtx DxeAppContextT * Heap-allocated context (address stable across realloc).
|
||||
.endtable
|
||||
|
||||
.topic shell.lifecycle
|
||||
.title App Lifecycle API
|
||||
.toc 1 Lifecycle API
|
||||
.index shellAppInit
|
||||
.index shellLoadApp
|
||||
.index shellLoadAppWithArgs
|
||||
.index shellReapApps
|
||||
.index shellReapApp
|
||||
.index shellForceKillApp
|
||||
.index shellTerminateAllApps
|
||||
|
||||
.h1 App Lifecycle API
|
||||
|
||||
.h2 shellAppInit
|
||||
|
||||
.code
|
||||
void shellAppInit(void);
|
||||
.endcode
|
||||
|
||||
Initialize the app slot table. Seeds slot 0 (reserved for the shell). Must be called once at startup before any other shell API function.
|
||||
|
||||
.h2 shellLoadApp
|
||||
|
||||
.code
|
||||
int32_t shellLoadApp(AppContextT *ctx, const char *path);
|
||||
.endcode
|
||||
|
||||
Load and start an app from a DXE file. Returns the app ID (>= 1) on success, or -1 on error.
|
||||
|
||||
For callback-only apps, appMain() runs synchronously and returns before shellLoadApp returns. For main-loop apps, a cooperative task is created and the app begins running on the next tsYield().
|
||||
|
||||
If multiInstance is false in the app's descriptor and the same DXE is already loaded, the call fails with an error dialog. If multiInstance is true, the DXE is copied to a temp file so dlopen gets an independent code and data image.
|
||||
|
||||
.h2 shellLoadAppWithArgs
|
||||
|
||||
.code
|
||||
int32_t shellLoadAppWithArgs(AppContextT *ctx, const char *path, const char *args);
|
||||
.endcode
|
||||
|
||||
Load and run an app with arguments. The args string is copied into DxeAppContextT.args before appMain() is called. Otherwise identical to shellLoadApp().
|
||||
|
||||
.h2 shellReapApps
|
||||
|
||||
.code
|
||||
bool shellReapApps(AppContextT *ctx);
|
||||
.endcode
|
||||
|
||||
Scan for and reap finished apps. Call once per frame from the main loop.
|
||||
|
||||
Returns true if any apps were reaped, so the caller can trigger a desktop refresh. For main-loop apps, termination is detected by the AppStateTerminatingE state (set when appMain returns). For callback-only apps, termination is detected when no windows remain for that app.
|
||||
|
||||
.h2 shellReapApp
|
||||
|
||||
.code
|
||||
void shellReapApp(AppContextT *ctx, ShellAppT *app);
|
||||
.endcode
|
||||
|
||||
Gracefully shut down a single app. Calls the app's shutdownFn (if present), destroys all windows belonging to the app, kills its task (if any), closes the DXE handle, and frees the context. The slot returns to AppStateFreeE.
|
||||
|
||||
.h2 shellForceKillApp
|
||||
|
||||
.code
|
||||
void shellForceKillApp(AppContextT *ctx, ShellAppT *app);
|
||||
.endcode
|
||||
|
||||
Forcibly kill an app without calling its shutdown hook. Used by the Task Manager "End Task" or when an app has crashed and cannot be trusted to run cleanup code.
|
||||
|
||||
Cleanup order: windows first (removes from compositor), then task (frees stack), then DXE handle (unmaps code). Closing the DXE before destroying windows would cause callbacks into unmapped code.
|
||||
|
||||
.h2 shellTerminateAllApps
|
||||
|
||||
.code
|
||||
void shellTerminateAllApps(AppContextT *ctx);
|
||||
.endcode
|
||||
|
||||
Force-kill all running apps. Called during shell shutdown. Iterates all slots and calls shellForceKillApp() on each active app.
|
||||
|
||||
.topic shell.query
|
||||
.title Query API
|
||||
.toc 1 Query API
|
||||
.index shellGetApp
|
||||
.index shellAppSlotCount
|
||||
.index shellRunningAppCount
|
||||
|
||||
.h1 Query API
|
||||
|
||||
.h2 shellGetApp
|
||||
|
||||
.code
|
||||
ShellAppT *shellGetApp(int32_t appId);
|
||||
.endcode
|
||||
|
||||
Look up an app slot by ID. Returns a pointer to the ShellAppT, or NULL if the ID is out of range or the slot is free. Valid app IDs are 1 through shellAppSlotCount() - 1.
|
||||
|
||||
.h2 shellAppSlotCount
|
||||
|
||||
.code
|
||||
int32_t shellAppSlotCount(void);
|
||||
.endcode
|
||||
|
||||
Return the total number of app slots (including slot 0). Use as the iteration bound when scanning all slots. Slot 0 is the shell itself; app slots start at 1.
|
||||
|
||||
.h2 shellRunningAppCount
|
||||
|
||||
.code
|
||||
int32_t shellRunningAppCount(void);
|
||||
.endcode
|
||||
|
||||
Count running apps, not counting the shell itself. Includes apps in both AppStateLoadedE and AppStateRunningE states.
|
||||
|
||||
.topic shell.config
|
||||
.title Configuration API
|
||||
.toc 1 Configuration API
|
||||
.index shellEnsureConfigDir
|
||||
.index shellConfigPath
|
||||
|
||||
.h1 Configuration API
|
||||
|
||||
Each app has a per-app configuration directory derived from its DXE path, mirrored under CONFIG/. For example, an app at APPS/KPUNCH/DVXBASIC/dvxbasic.app gets the config directory CONFIG/APPS/KPUNCH/DVXBASIC/.
|
||||
|
||||
.h2 shellEnsureConfigDir
|
||||
|
||||
.code
|
||||
int32_t shellEnsureConfigDir(const DxeAppContextT *ctx);
|
||||
.endcode
|
||||
|
||||
Ensure the app's config directory exists, creating all parent directories as needed. Returns 0 on success, -1 on failure.
|
||||
|
||||
Call this before writing any config files. The directory path comes from ctx->configDir.
|
||||
|
||||
.h2 shellConfigPath
|
||||
|
||||
.code
|
||||
void shellConfigPath(const DxeAppContextT *ctx, const char *filename, char *outPath, int32_t outSize);
|
||||
.endcode
|
||||
|
||||
Build a full path to a file in the app's config directory by joining ctx->configDir with the given filename.
|
||||
|
||||
.code
|
||||
// Example:
|
||||
char path[DVX_MAX_PATH];
|
||||
shellConfigPath(ctx, "settings.ini", path, sizeof(path));
|
||||
// -> "CONFIG/APPS/KPUNCH/PROGMAN/settings.ini"
|
||||
.endcode
|
||||
|
||||
.topic shell.desktop
|
||||
.title Desktop Callbacks
|
||||
.toc 1 Desktop Callbacks
|
||||
.index shellRegisterDesktopUpdate
|
||||
.index shellUnregisterDesktopUpdate
|
||||
.index shellDesktopUpdate
|
||||
.index shellCtrlEscFn
|
||||
|
||||
.h1 Desktop Callbacks
|
||||
|
||||
The shell provides a notification mechanism for app state changes (load, reap, crash). Desktop managers register a callback to refresh their display when apps come and go.
|
||||
|
||||
.h2 shellRegisterDesktopUpdate
|
||||
|
||||
.code
|
||||
void shellRegisterDesktopUpdate(void (*updateFn)(void));
|
||||
.endcode
|
||||
|
||||
Register a callback for app state change notifications. Multiple callbacks are supported. Apps typically call this during appMain() to receive notifications.
|
||||
|
||||
.h2 shellUnregisterDesktopUpdate
|
||||
|
||||
.code
|
||||
void shellUnregisterDesktopUpdate(void (*updateFn)(void));
|
||||
.endcode
|
||||
|
||||
Remove a previously registered callback. Call this before app shutdown to avoid dangling function pointers.
|
||||
|
||||
.h2 shellDesktopUpdate
|
||||
|
||||
.code
|
||||
void shellDesktopUpdate(void);
|
||||
.endcode
|
||||
|
||||
Notify all registered desktop callbacks that app state has changed. Called internally by the shell after loading or reaping an app. Can also be called by apps that need to trigger a desktop refresh.
|
||||
|
||||
.h2 shellCtrlEscFn
|
||||
|
||||
.code
|
||||
extern void (*shellCtrlEscFn)(AppContextT *ctx);
|
||||
.endcode
|
||||
|
||||
Function pointer set by the taskmgr DXE's constructor. The shell calls this when Ctrl+Esc is pressed. NULL if the task manager is not loaded.
|
||||
|
||||
.topic shell.info
|
||||
.title System Information
|
||||
.toc 1 System Information
|
||||
.index shellInfoInit
|
||||
.index shellGetSystemInfo
|
||||
|
||||
.h1 System Information
|
||||
|
||||
Header: shell/shellInfo.h
|
||||
|
||||
Thin wrapper around the platform layer's hardware detection. Gathers system information at startup, logs it, and caches the result for display in dialogs.
|
||||
|
||||
.h2 shellInfoInit
|
||||
|
||||
.code
|
||||
void shellInfoInit(AppContextT *ctx);
|
||||
.endcode
|
||||
|
||||
Gather all hardware information via the platform layer, log each line to DVX.LOG, and store the result for later retrieval. Call once after dvxInit().
|
||||
|
||||
.h2 shellGetSystemInfo
|
||||
|
||||
.code
|
||||
const char *shellGetSystemInfo(void);
|
||||
.endcode
|
||||
|
||||
Return the formatted system information text. The returned pointer is valid for the lifetime of the process (static buffer in the platform layer).
|
||||
|
|
@ -459,7 +459,7 @@ static int32_t shellLoadAppInternal(AppContextT *ctx, const char *path, const ch
|
|||
}
|
||||
|
||||
// Derive config directory: mirror the app directory under CONFIG/.
|
||||
// e.g. "APPS/DVXBASIC" -> "CONFIG/APPS/DVXBASIC"
|
||||
// e.g. "APPS/KPUNCH/DVXBASIC" -> "CONFIG/APPS/KPUNCH/DVXBASIC"
|
||||
// If the path doesn't start with a recognized prefix, fall back to
|
||||
// "CONFIG/APPS/<appname>" using the descriptor name.
|
||||
const char *appDirStr = app->dxeCtx->appDir;
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ void shellConfigPath(const DxeAppContextT *ctx, const char *filename, char *outP
|
|||
// ============================================================
|
||||
|
||||
// Default desktop app path
|
||||
#define SHELL_DESKTOP_APP "apps/progman/progman.app"
|
||||
#define SHELL_DESKTOP_APP "apps/kpunch/progman/progman.app"
|
||||
|
||||
// Register a callback for app state changes (load, reap, crash).
|
||||
// Apps call this during appMain to receive notifications when app state
|
||||
|
|
|
|||
|
|
@ -118,10 +118,10 @@ static void f1HelpHandler(void *ctx) {
|
|||
|
||||
// Fall back to system help if no app-specific help
|
||||
char viewerPath[DVX_MAX_PATH];
|
||||
snprintf(viewerPath, sizeof(viewerPath), "APPS%cDVXHELP%cDVXHELP.APP", DVX_PATH_SEP, DVX_PATH_SEP);
|
||||
snprintf(viewerPath, sizeof(viewerPath), "APPS%cKPUNCH%cDVXHELP%cDVXHELP.APP", DVX_PATH_SEP, DVX_PATH_SEP, DVX_PATH_SEP);
|
||||
|
||||
if (!args[0]) {
|
||||
snprintf(args, sizeof(args), "APPS%cPROGMAN%cDVXHELP.HLP", DVX_PATH_SEP, DVX_PATH_SEP);
|
||||
snprintf(args, sizeof(args), "APPS%cKPUNCH%cPROGMAN%cDVXHELP.HLP", DVX_PATH_SEP, DVX_PATH_SEP, DVX_PATH_SEP);
|
||||
}
|
||||
|
||||
shellLoadAppWithArgs(ac, viewerPath, args);
|
||||
|
|
|
|||
22
sql/Makefile
22
sql/Makefile
|
|
@ -22,9 +22,10 @@ CFLAGS = -O2 -Wall -Werror -Wno-type-limits -Wno-sign-compare -Wno-format
|
|||
-DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
-I$(SQLITE_SRC) -I$(SQLITE_DIR)/examples -I$(OBJDIR)
|
||||
|
||||
OBJDIR = ../obj/sql
|
||||
LIBSDIR = ../bin/libs
|
||||
TARGET = $(LIBSDIR)/dvxsql.lib
|
||||
OBJDIR = ../obj/sql
|
||||
LIBSDIR = ../bin/libs
|
||||
TARGETDIR = $(LIBSDIR)/kpunch/dvxsql
|
||||
TARGET = $(TARGETDIR)/dvxsql.lib
|
||||
|
||||
# SQLite library source files (no test, shell, tcl, server, md5, os_test)
|
||||
SQLITE_SRCS = \
|
||||
|
|
@ -54,9 +55,9 @@ OBJS = $(OBJDIR)/dvxSql.o $(SQLITE_OBJS) $(OBJDIR)/sqlite_parse.o $(OBJDIR)/sqli
|
|||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET) $(LIBSDIR)/dvxsql.dep
|
||||
all: $(TARGET) $(TARGETDIR)/dvxsql.dep
|
||||
|
||||
$(LIBSDIR)/dvxsql.dep: ../config/dvxsql.dep | $(LIBSDIR)
|
||||
$(TARGETDIR)/dvxsql.dep: ../config/dvxsql.dep | $(TARGETDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
|
||||
# ---- Phase 1: Native tools and generated files ----
|
||||
|
|
@ -97,11 +98,11 @@ $(OBJDIR)/sqlite_parse.o: $(GEN_PARSE_C) $(GEN_OPCODES_H) | $(OBJDIR)
|
|||
$(OBJDIR)/sqlite_opcodes.o: $(GEN_OPCODES_C) | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(TARGET): $(OBJS) | $(LIBSDIR)
|
||||
$(DXE3GEN) -o $(LIBSDIR)/dvxsql.dxe \
|
||||
$(TARGET): $(OBJS) | $(TARGETDIR)
|
||||
$(DXE3GEN) -o $(TARGETDIR)/dvxsql.dxe \
|
||||
-E _dvxSql -E _sqlite3 \
|
||||
-U $(OBJS)
|
||||
mv $(LIBSDIR)/dvxsql.dxe $@
|
||||
mv $(TARGETDIR)/dvxsql.dxe $@
|
||||
|
||||
$(OBJDIR):
|
||||
mkdir -p $(OBJDIR)
|
||||
|
|
@ -109,8 +110,11 @@ $(OBJDIR):
|
|||
$(LIBSDIR):
|
||||
mkdir -p $(LIBSDIR)
|
||||
|
||||
$(TARGETDIR):
|
||||
mkdir -p $(TARGETDIR)
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(TARGET) $(LIBSDIR)/dvxsql.dep
|
||||
rm -rf $(OBJS) $(TARGET) $(TARGETDIR)/dvxsql.dep $(TARGETDIR)
|
||||
rm -f $(LEMON) $(MKKEYWORDHASH)
|
||||
rm -f $(GEN_PARSE_C) $(GEN_PARSE_H) $(GEN_OPCODES_H) $(GEN_OPCODES_C) $(GEN_KEYWORDHASH)
|
||||
rm -f $(OBJDIR)/parse.y $(OBJDIR)/parse.out
|
||||
|
|
|
|||
388
sql/dvxsql.dhs
Normal file
388
sql/dvxsql.dhs
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
.topic sql.overview
|
||||
.title dvxSql -- SQL Database Interface
|
||||
.toc 1 dvxSql -- SQL Database Interface
|
||||
.default
|
||||
.index dvxSql
|
||||
.index SQL
|
||||
.index SQLite
|
||||
.index Database
|
||||
|
||||
.h2 dvxSql -- SQL Database Interface
|
||||
|
||||
High-level wrapper around SQLite3 for DVX applications. Manages database connections and result set cursors via integer handles so BASIC code never touches raw pointers. All handles are 1-based; 0 indicates an error or invalid handle.
|
||||
|
||||
Header: sql/dvxSql.h
|
||||
|
||||
.h3 Limits
|
||||
|
||||
.table
|
||||
Constant Value Description
|
||||
-------- ----- -----------
|
||||
MAX_DBS 16 Maximum number of simultaneously open databases.
|
||||
MAX_CURSORS 64 Maximum number of simultaneously open result set cursors.
|
||||
.endtable
|
||||
|
||||
.h3 Handle Model
|
||||
|
||||
Database and cursor handles are int32_t values. A successful open or query returns a handle greater than zero. Handle 0 is reserved as the invalid/error sentinel. Closing a database automatically finalizes all cursors that belong to it.
|
||||
|
||||
.link sql.db Database Operations
|
||||
.link sql.cursor Cursor Operations
|
||||
.link sql.utility Utility Functions
|
||||
.link sql.example Example
|
||||
|
||||
.topic sql.db
|
||||
.title Database Operations
|
||||
.toc 1 Database Operations
|
||||
.index dvxSqlOpen
|
||||
.index dvxSqlClose
|
||||
.index dvxSqlExec
|
||||
.index dvxSqlError
|
||||
.index dvxSqlAffectedRows
|
||||
|
||||
.h2 Database Operations
|
||||
|
||||
.h3 dvxSqlOpen
|
||||
|
||||
.code
|
||||
int32_t dvxSqlOpen(const char *path);
|
||||
.endcode
|
||||
|
||||
Open a SQLite database file. Creates the file if it does not exist.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
path Path to the database file.
|
||||
.endtable
|
||||
|
||||
Returns a database handle greater than 0 on success, or 0 on error (null path, open failure, or no free slots).
|
||||
|
||||
.h3 dvxSqlClose
|
||||
|
||||
.code
|
||||
void dvxSqlClose(int32_t db);
|
||||
.endcode
|
||||
|
||||
Close a database and free all associated resources. Any open cursors belonging to this database are automatically finalized.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
db Database handle returned by dvxSqlOpen.
|
||||
.endtable
|
||||
|
||||
Safe to call with an invalid handle (no-op).
|
||||
|
||||
.h3 dvxSqlExec
|
||||
|
||||
.code
|
||||
bool dvxSqlExec(int32_t db, const char *sql);
|
||||
.endcode
|
||||
|
||||
Execute one or more SQL statements that return no result rows. Suitable for DDL (CREATE TABLE, DROP TABLE, etc.) and DML (INSERT, UPDATE, DELETE).
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
db Database handle.
|
||||
sql SQL statement(s) to execute.
|
||||
.endtable
|
||||
|
||||
Returns true on success, false on error. On failure, the error message is available via dvxSqlError. On success, the affected row count is available via dvxSqlAffectedRows.
|
||||
|
||||
.h3 dvxSqlError
|
||||
|
||||
.code
|
||||
const char *dvxSqlError(int32_t db);
|
||||
.endcode
|
||||
|
||||
Return the last error message for a database handle. The returned string is stored internally and valid until the next operation on the same handle.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
db Database handle.
|
||||
.endtable
|
||||
|
||||
Returns the error string, or "Invalid database handle" if the handle is invalid.
|
||||
|
||||
.h3 dvxSqlAffectedRows
|
||||
|
||||
.code
|
||||
int32_t dvxSqlAffectedRows(int32_t db);
|
||||
.endcode
|
||||
|
||||
Return the number of rows inserted, updated, or deleted by the last dvxSqlExec call on this handle.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
db Database handle.
|
||||
.endtable
|
||||
|
||||
Returns the row count, or 0 if the handle is invalid.
|
||||
|
||||
.topic sql.cursor
|
||||
.title Cursor Operations
|
||||
.toc 1 Cursor Operations
|
||||
.index dvxSqlQuery
|
||||
.index dvxSqlNext
|
||||
.index dvxSqlEof
|
||||
.index dvxSqlFieldCount
|
||||
.index dvxSqlFieldName
|
||||
.index dvxSqlFieldText
|
||||
.index dvxSqlFieldByName
|
||||
.index dvxSqlFieldInt
|
||||
.index dvxSqlFieldDbl
|
||||
.index dvxSqlFreeResult
|
||||
|
||||
.h2 Cursor Operations
|
||||
|
||||
Result set cursors are created by dvxSqlQuery and must be freed with dvxSqlFreeResult when no longer needed. A new cursor is positioned before the first row; call dvxSqlNext to advance to the first row before reading field values.
|
||||
|
||||
.h3 dvxSqlQuery
|
||||
|
||||
.code
|
||||
int32_t dvxSqlQuery(int32_t db, const char *sql);
|
||||
.endcode
|
||||
|
||||
Execute a SELECT query and return a cursor handle for iterating the results. The cursor is positioned before the first row.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
db Database handle.
|
||||
sql SQL SELECT statement.
|
||||
.endtable
|
||||
|
||||
Returns a cursor handle greater than 0 on success, or 0 on error (invalid handle, null SQL, SQL syntax error, or no free cursor slots). On failure, the error message is available via dvxSqlError on the database handle.
|
||||
|
||||
.h3 dvxSqlNext
|
||||
|
||||
.code
|
||||
bool dvxSqlNext(int32_t rs);
|
||||
.endcode
|
||||
|
||||
Advance the cursor to the next row.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
rs Cursor handle returned by dvxSqlQuery.
|
||||
.endtable
|
||||
|
||||
Returns true if a row is now available for reading, false if the cursor has reached the end or the handle is invalid.
|
||||
|
||||
.h3 dvxSqlEof
|
||||
|
||||
.code
|
||||
bool dvxSqlEof(int32_t rs);
|
||||
.endcode
|
||||
|
||||
Test whether the cursor is past the last row.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
rs Cursor handle.
|
||||
.endtable
|
||||
|
||||
Returns true if the cursor is exhausted or the handle is invalid, false otherwise.
|
||||
|
||||
.h3 dvxSqlFieldCount
|
||||
|
||||
.code
|
||||
int32_t dvxSqlFieldCount(int32_t rs);
|
||||
.endcode
|
||||
|
||||
Return the number of columns in the result set.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
rs Cursor handle.
|
||||
.endtable
|
||||
|
||||
Returns the column count, or 0 if the handle is invalid.
|
||||
|
||||
.h3 dvxSqlFieldName
|
||||
|
||||
.code
|
||||
const char *dvxSqlFieldName(int32_t rs, int32_t col);
|
||||
.endcode
|
||||
|
||||
Return the name of a column by index.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
rs Cursor handle.
|
||||
col Column index (0-based).
|
||||
.endtable
|
||||
|
||||
Returns the column name, or "" if the handle or index is invalid.
|
||||
|
||||
.h3 dvxSqlFieldText
|
||||
|
||||
.code
|
||||
const char *dvxSqlFieldText(int32_t rs, int32_t col);
|
||||
.endcode
|
||||
|
||||
Return the value of a column as a string.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
rs Cursor handle.
|
||||
col Column index (0-based).
|
||||
.endtable
|
||||
|
||||
Returns the text value, or "" if the handle or index is invalid or the value is NULL.
|
||||
|
||||
.h3 dvxSqlFieldByName
|
||||
|
||||
.code
|
||||
const char *dvxSqlFieldByName(int32_t rs, const char *name);
|
||||
.endcode
|
||||
|
||||
Return the value of a column identified by name as a string. The name match is case-insensitive.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
rs Cursor handle.
|
||||
name Column name (case-insensitive).
|
||||
.endtable
|
||||
|
||||
Returns the text value, or "" if the handle is invalid, the name is NULL, or no column with that name exists.
|
||||
|
||||
.h3 dvxSqlFieldInt
|
||||
|
||||
.code
|
||||
int32_t dvxSqlFieldInt(int32_t rs, int32_t col);
|
||||
.endcode
|
||||
|
||||
Return the value of a column as a 32-bit integer.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
rs Cursor handle.
|
||||
col Column index (0-based).
|
||||
.endtable
|
||||
|
||||
Returns the integer value, or 0 if the handle or index is invalid.
|
||||
|
||||
.h3 dvxSqlFieldDbl
|
||||
|
||||
.code
|
||||
double dvxSqlFieldDbl(int32_t rs, int32_t col);
|
||||
.endcode
|
||||
|
||||
Return the value of a column as a double-precision floating point number.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
rs Cursor handle.
|
||||
col Column index (0-based).
|
||||
.endtable
|
||||
|
||||
Returns the double value, or 0.0 if the handle or index is invalid.
|
||||
|
||||
.h3 dvxSqlFreeResult
|
||||
|
||||
.code
|
||||
void dvxSqlFreeResult(int32_t rs);
|
||||
.endcode
|
||||
|
||||
Close a result set cursor and free its resources. Must be called for every cursor returned by dvxSqlQuery.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
rs Cursor handle.
|
||||
.endtable
|
||||
|
||||
Safe to call with an invalid handle (no-op).
|
||||
|
||||
.topic sql.utility
|
||||
.title Utility Functions
|
||||
.toc 1 Utility Functions
|
||||
.index dvxSqlEscape
|
||||
|
||||
.h2 Utility Functions
|
||||
|
||||
.h3 dvxSqlEscape
|
||||
|
||||
.code
|
||||
int32_t dvxSqlEscape(const char *src, char *dst, int32_t dstSize);
|
||||
.endcode
|
||||
|
||||
Escape a string for safe inclusion in SQL string literals. Doubles single quotes so that O'Brien becomes O''Brien.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
src Source string to escape.
|
||||
dst Destination buffer for the escaped string.
|
||||
dstSize Size of the destination buffer in bytes.
|
||||
.endtable
|
||||
|
||||
Returns the length of the escaped string (not including the null terminator), or -1 if the buffer was too small or any parameter is NULL/invalid.
|
||||
|
||||
.topic sql.example
|
||||
.title Example Usage
|
||||
.toc 1 Example Usage
|
||||
.index SQL Example
|
||||
|
||||
.h2 Example Usage
|
||||
|
||||
.h3 Creating a Table and Inserting Data
|
||||
|
||||
.code
|
||||
int32_t db = dvxSqlOpen("mydata.db");
|
||||
if (!db) {
|
||||
// handle error
|
||||
}
|
||||
|
||||
dvxSqlExec(db, "CREATE TABLE IF NOT EXISTS contacts ("
|
||||
"id INTEGER PRIMARY KEY, "
|
||||
"name TEXT, "
|
||||
"phone TEXT)");
|
||||
|
||||
dvxSqlExec(db, "INSERT INTO contacts (name, phone) "
|
||||
"VALUES ('Alice', '555-0100')");
|
||||
.endcode
|
||||
|
||||
.h3 Querying and Iterating Results
|
||||
|
||||
.code
|
||||
int32_t rs = dvxSqlQuery(db, "SELECT id, name, phone FROM contacts");
|
||||
if (!rs) {
|
||||
printf("Error: %s\n", dvxSqlError(db));
|
||||
}
|
||||
|
||||
while (dvxSqlNext(rs)) {
|
||||
int32_t id = dvxSqlFieldInt(rs, 0);
|
||||
const char *name = dvxSqlFieldText(rs, 1);
|
||||
const char *phone = dvxSqlFieldText(rs, 2);
|
||||
printf("%d: %s -- %s\n", id, name, phone);
|
||||
}
|
||||
|
||||
dvxSqlFreeResult(rs);
|
||||
dvxSqlClose(db);
|
||||
.endcode
|
||||
|
||||
.h3 Escaping User Input
|
||||
|
||||
.code
|
||||
char escaped[512];
|
||||
dvxSqlEscape(userInput, escaped, sizeof(escaped));
|
||||
|
||||
char sql[1024];
|
||||
snprintf(sql, sizeof(sql),
|
||||
"INSERT INTO notes (text) VALUES ('%s')", escaped);
|
||||
dvxSqlExec(db, sql);
|
||||
.endcode
|
||||
|
|
@ -12,18 +12,19 @@ LIBSDIR = ../bin/libs
|
|||
|
||||
SRCS = shellTaskMgr.c
|
||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||
TARGET = $(LIBSDIR)/taskmgr.lib
|
||||
TARGETDIR = $(LIBSDIR)/kpunch/taskmgr
|
||||
TARGET = $(TARGETDIR)/taskmgr.lib
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET) $(LIBSDIR)/taskmgr.dep
|
||||
all: $(TARGET) $(TARGETDIR)/taskmgr.dep
|
||||
|
||||
$(LIBSDIR)/taskmgr.dep: ../config/taskmgr.dep | $(LIBSDIR)
|
||||
$(TARGETDIR)/taskmgr.dep: ../config/taskmgr.dep | $(TARGETDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
|
||||
$(TARGET): $(OBJS) | $(LIBSDIR)
|
||||
$(DXE3GEN) -o $(LIBSDIR)/taskmgr.dxe -U $(OBJS)
|
||||
mv $(LIBSDIR)/taskmgr.dxe $@
|
||||
$(TARGET): $(OBJS) | $(TARGETDIR)
|
||||
$(DXE3GEN) -o $(TARGETDIR)/taskmgr.dxe -U $(OBJS)
|
||||
mv $(TARGETDIR)/taskmgr.dxe $@
|
||||
|
||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
|
@ -34,8 +35,11 @@ $(OBJDIR):
|
|||
$(LIBSDIR):
|
||||
mkdir -p $(LIBSDIR)
|
||||
|
||||
$(TARGETDIR):
|
||||
mkdir -p $(TARGETDIR)
|
||||
|
||||
# Dependencies
|
||||
$(OBJDIR)/shellTaskMgr.o: shellTaskMgr.c shellTaskMgr.h ../shell/shellApp.h ../core/dvxWidget.h ../core/dvxApp.h
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(TARGET) $(LIBSDIR)/taskmgr.dep
|
||||
rm -rf $(OBJS) $(TARGET) $(TARGETDIR)/taskmgr.dep $(TARGETDIR)
|
||||
|
|
|
|||
40
taskmgr/taskmgr.dhs
Normal file
40
taskmgr/taskmgr.dhs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
.topic lib.taskmgr
|
||||
.title Task Manager
|
||||
.toc 1 Task Manager
|
||||
.index Task Manager
|
||||
.index shellTaskMgrOpen
|
||||
.index shellTaskMgrRefresh
|
||||
|
||||
.h1 Task Manager
|
||||
|
||||
System-level task manager for DVX. Displays running applications and per-app memory usage. Always accessible via Ctrl+Esc regardless of which application is focused. Persists independently of the desktop app (Program Manager).
|
||||
|
||||
Header: taskmgr/shellTaskMgr.h
|
||||
|
||||
Loaded as: bin/libs/taskmgr.lib
|
||||
|
||||
.h2 shellTaskMgrOpen
|
||||
|
||||
Open the Task Manager window, or raise it to the front if already open.
|
||||
|
||||
.code
|
||||
void shellTaskMgrOpen(AppContextT *ctx);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
ctx Application context (from dvxInit). Required for window creation and event registration.
|
||||
.endtable
|
||||
|
||||
Called by the shell's global Ctrl+Esc handler.
|
||||
|
||||
.h2 shellTaskMgrRefresh
|
||||
|
||||
Refresh the task list display. Call this when applications are launched or terminated so the Task Manager reflects the current state.
|
||||
|
||||
.code
|
||||
void shellTaskMgrRefresh(void);
|
||||
.endcode
|
||||
|
||||
Takes no parameters. Safe to call even if the Task Manager window is not currently open (the call is a no-op in that case).
|
||||
|
|
@ -12,15 +12,16 @@ LIBSDIR = ../bin/libs
|
|||
|
||||
SRCS = taskswitch.c
|
||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||
TARGET = $(LIBSDIR)/libtasks.lib
|
||||
TARGETDIR = $(LIBSDIR)/kpunch/libtasks
|
||||
TARGET = $(TARGETDIR)/libtasks.lib
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS) | $(LIBSDIR)
|
||||
$(DXE3GEN) -o $(LIBSDIR)/libtasks.dxe -E _ts -U $(OBJS)
|
||||
mv $(LIBSDIR)/libtasks.dxe $@
|
||||
$(TARGET): $(OBJS) | $(TARGETDIR)
|
||||
$(DXE3GEN) -o $(TARGETDIR)/libtasks.dxe -E _ts -U $(OBJS)
|
||||
mv $(TARGETDIR)/libtasks.dxe $@
|
||||
|
||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
|
@ -31,8 +32,11 @@ $(OBJDIR):
|
|||
$(LIBSDIR):
|
||||
mkdir -p $(LIBSDIR)
|
||||
|
||||
$(TARGETDIR):
|
||||
mkdir -p $(TARGETDIR)
|
||||
|
||||
# Dependencies
|
||||
$(OBJDIR)/taskswitch.o: taskswitch.c taskswitch.h ../core/thirdparty/stb_ds.h
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(TARGET)
|
||||
rm -rf $(OBJS) $(TARGET) $(TARGETDIR)
|
||||
|
|
|
|||
260
tasks/libtasks.dhs
Normal file
260
tasks/libtasks.dhs
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
.topic libtasks
|
||||
.title libtasks -- Cooperative Task Switching
|
||||
.toc 1 libtasks -- Cooperative Task Switching
|
||||
.index libtasks
|
||||
.index Task Switching
|
||||
.index Cooperative Multitasking
|
||||
.index taskswitch.h
|
||||
|
||||
.h1 libtasks -- Cooperative Task Switching
|
||||
|
||||
Credit-based cooperative (non-preemptive) multitasking library for DOS protected mode (DJGPP/DPMI). Each task receives (priority + 1) credits per scheduling round. Tasks run round-robin, consuming one credit per turn. When all credits are exhausted, every ready task is refilled. Higher-priority tasks run proportionally more often but never starve lower ones.
|
||||
|
||||
Header: tasks/taskswitch.h
|
||||
|
||||
.h2 Why Cooperative
|
||||
|
||||
DOS is single-threaded with no kernel scheduler. DPMI provides no preemption primitives. The DVX GUI event model is inherently single-threaded: one compositor, one input queue, one window stack. Cooperative switching lets each task yield at safe points, avoiding synchronization primitives entirely.
|
||||
|
||||
.h2 Error Codes
|
||||
|
||||
.table
|
||||
Constant Value Description
|
||||
-------- ----- -----------
|
||||
TS_OK 0 Success
|
||||
TS_ERR_INIT -1 Task system not initialized
|
||||
TS_ERR_PARAM -2 Invalid parameter (bad task ID, etc.)
|
||||
TS_ERR_FULL -3 Task array full (should not occur; array grows)
|
||||
TS_ERR_NOMEM -4 Memory allocation failed
|
||||
TS_ERR_STATE -5 Invalid state transition
|
||||
.endtable
|
||||
|
||||
.h2 Constants
|
||||
|
||||
.table
|
||||
Constant Value Description
|
||||
-------- ----- -----------
|
||||
TS_DEFAULT_STACK_SIZE 32768 Default stack size per task (32 KB)
|
||||
TS_NAME_MAX 32 Maximum task name length including NUL
|
||||
TS_PRIORITY_LOW 0 Low priority (1 credit per round)
|
||||
TS_PRIORITY_NORMAL 5 Normal priority (6 credits per round)
|
||||
TS_PRIORITY_HIGH 10 High priority (11 credits per round)
|
||||
.endtable
|
||||
|
||||
.h2 Types
|
||||
|
||||
.h3 TaskStateE
|
||||
|
||||
Task scheduling state. Only Ready tasks participate in scheduling.
|
||||
|
||||
.table
|
||||
Value Description
|
||||
----- -----------
|
||||
TaskStateReady Eligible for scheduling
|
||||
TaskStateRunning Currently executing (cosmetic; marks active task)
|
||||
TaskStatePaused Skipped by scheduler until explicitly resumed
|
||||
TaskStateTerminated Slot available for reuse
|
||||
.endtable
|
||||
|
||||
.h3 TaskEntryT
|
||||
|
||||
.code
|
||||
typedef void (*TaskEntryT)(void *arg);
|
||||
.endcode
|
||||
|
||||
Task entry point function signature. The void* argument lets the caller pass arbitrary context (e.g. a shell app descriptor).
|
||||
|
||||
.h2 tsInit
|
||||
|
||||
.code
|
||||
int32_t tsInit(void);
|
||||
.endcode
|
||||
|
||||
Initialize the task system. The calling context becomes task 0 (the main task). Task 0 is special: it cannot be killed or paused, and tsRecoverToMain() always returns control here. No separate stack is allocated for task 0; it uses the process stack directly.
|
||||
|
||||
Returns: TS_OK on success, TS_ERR_INIT if already initialized.
|
||||
|
||||
.h2 tsShutdown
|
||||
|
||||
.code
|
||||
void tsShutdown(void);
|
||||
.endcode
|
||||
|
||||
Shut down the task system and free all task stacks and internal storage. Safe to call even if tsInit() was never called.
|
||||
|
||||
.h2 tsCreate
|
||||
|
||||
.code
|
||||
int32_t tsCreate(const char *name, TaskEntryT entry, void *arg, uint32_t stackSize, int32_t priority);
|
||||
.endcode
|
||||
|
||||
Create a new task. Terminated task slots are recycled to avoid unbounded array growth.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
name Task name (truncated to TS_NAME_MAX - 1 characters)
|
||||
entry Task entry point function
|
||||
arg Opaque argument passed to entry
|
||||
stackSize Stack size in bytes (pass 0 for TS_DEFAULT_STACK_SIZE)
|
||||
priority Scheduling priority (0..10; use TS_PRIORITY_* constants)
|
||||
.endtable
|
||||
|
||||
Returns: Task ID (>= 0) on success, or a negative error code (TS_ERR_INIT, TS_ERR_PARAM, TS_ERR_NOMEM).
|
||||
|
||||
.h2 tsYield
|
||||
|
||||
.code
|
||||
void tsYield(void);
|
||||
.endcode
|
||||
|
||||
Yield CPU to the next eligible ready task using credit-based round-robin. This is the sole mechanism for task switching. Every task must call this (or a GUI function that calls it) periodically, or it will monopolize the CPU.
|
||||
|
||||
.h2 tsPause
|
||||
|
||||
.code
|
||||
int32_t tsPause(uint32_t taskId);
|
||||
.endcode
|
||||
|
||||
Pause a task, removing it from scheduling. Cannot pause the main task (ID 0). If a task pauses itself, an implicit yield occurs.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
taskId ID of the task to pause
|
||||
.endtable
|
||||
|
||||
Returns: TS_OK on success, TS_ERR_PARAM on invalid ID, TS_ERR_STATE if the task is not in a pausable state.
|
||||
|
||||
.h2 tsResume
|
||||
|
||||
.code
|
||||
int32_t tsResume(uint32_t taskId);
|
||||
.endcode
|
||||
|
||||
Resume a paused task. Credits are refilled so the task gets a fair share of CPU time immediately rather than waiting for the next scheduling round.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
taskId ID of the task to resume
|
||||
.endtable
|
||||
|
||||
Returns: TS_OK on success, TS_ERR_PARAM on invalid ID, TS_ERR_STATE if the task is not paused.
|
||||
|
||||
.h2 tsSetPriority
|
||||
|
||||
.code
|
||||
int32_t tsSetPriority(uint32_t taskId, int32_t priority);
|
||||
.endcode
|
||||
|
||||
Set a task's scheduling priority. Also refills credits so the change takes effect immediately.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
taskId ID of the task to modify
|
||||
priority New priority level (0..10)
|
||||
.endtable
|
||||
|
||||
Returns: TS_OK on success, TS_ERR_PARAM on invalid ID or out-of-range priority.
|
||||
|
||||
.h2 tsGetPriority
|
||||
|
||||
.code
|
||||
int32_t tsGetPriority(uint32_t taskId);
|
||||
.endcode
|
||||
|
||||
Get a task's current scheduling priority.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
taskId ID of the task to query
|
||||
.endtable
|
||||
|
||||
Returns: Priority value (0..10) on success, TS_ERR_PARAM on invalid ID.
|
||||
|
||||
.h2 tsGetState
|
||||
|
||||
.code
|
||||
TaskStateE tsGetState(uint32_t taskId);
|
||||
.endcode
|
||||
|
||||
Get a task's current scheduling state.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
taskId ID of the task to query
|
||||
.endtable
|
||||
|
||||
Returns: TaskStateE value. Returns TaskStateTerminated for invalid IDs.
|
||||
|
||||
.h2 tsCurrentId
|
||||
|
||||
.code
|
||||
uint32_t tsCurrentId(void);
|
||||
.endcode
|
||||
|
||||
Get the ID of the currently executing task. Always valid while the task system is initialized.
|
||||
|
||||
Returns: Current task ID.
|
||||
|
||||
.h2 tsGetName
|
||||
|
||||
.code
|
||||
const char *tsGetName(uint32_t taskId);
|
||||
.endcode
|
||||
|
||||
Get a task's name string.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
taskId ID of the task to query
|
||||
.endtable
|
||||
|
||||
Returns: Pointer to the task's name, or NULL on invalid ID. The pointer remains valid until the task slot is reused.
|
||||
|
||||
.h2 tsExit
|
||||
|
||||
.code
|
||||
void tsExit(void);
|
||||
.endcode
|
||||
|
||||
Terminate the calling task. Must not be called from the main task (ID 0). The stack is freed immediately and the slot is marked for reuse. This function never returns; it performs an internal context switch to the next ready task.
|
||||
|
||||
.h2 tsKill
|
||||
|
||||
.code
|
||||
int32_t tsKill(uint32_t taskId);
|
||||
.endcode
|
||||
|
||||
Forcibly terminate another task. Cannot kill the main task (ID 0) or the currently running task (use tsExit() for self-termination). Safe because cooperative scheduling guarantees the target is suspended at a yield point.
|
||||
|
||||
.table
|
||||
Parameter Description
|
||||
--------- -----------
|
||||
taskId ID of the task to terminate
|
||||
.endtable
|
||||
|
||||
Returns: TS_OK on success, TS_ERR_PARAM on invalid ID or illegal target (main task, self).
|
||||
|
||||
.h2 tsRecoverToMain
|
||||
|
||||
.code
|
||||
void tsRecoverToMain(void);
|
||||
.endcode
|
||||
|
||||
Crash recovery: force the scheduler back to the main task (ID 0). Call after longjmp from a signal handler that fired in a non-main task. The crashed task is NOT cleaned up by this call; call tsKill() afterward to free its resources. This exists because longjmp unwinds the crashed task's stack but the scheduler state still points to it.
|
||||
|
||||
.h2 tsActiveCount
|
||||
|
||||
.code
|
||||
uint32_t tsActiveCount(void);
|
||||
.endcode
|
||||
|
||||
Get the number of non-terminated tasks currently in the system.
|
||||
|
||||
Returns: Count of tasks in Ready, Running, or Paused state.
|
||||
|
|
@ -13,18 +13,19 @@ LIBSDIR = ../bin/libs
|
|||
|
||||
SRCS = textHelp.c
|
||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||
TARGET = $(LIBSDIR)/texthelp.lib
|
||||
TARGETDIR = $(LIBSDIR)/kpunch/texthelp
|
||||
TARGET = $(TARGETDIR)/texthelp.lib
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET) $(LIBSDIR)/texthelp.dep
|
||||
all: $(TARGET) $(TARGETDIR)/texthelp.dep
|
||||
|
||||
$(LIBSDIR)/texthelp.dep: ../config/texthelp.dep | $(LIBSDIR)
|
||||
$(TARGETDIR)/texthelp.dep: ../config/texthelp.dep | $(TARGETDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
|
||||
$(TARGET): $(OBJS) | $(LIBSDIR)
|
||||
$(DXE3GEN) -o $(LIBSDIR)/texthelp.dxe -E _clipboard -E _clearOther -E _isWordChar -E _multiClick -E _widgetText -E _wordStart -E _wordEnd -U $(OBJS)
|
||||
mv $(LIBSDIR)/texthelp.dxe $@
|
||||
$(TARGET): $(OBJS) | $(TARGETDIR)
|
||||
$(DXE3GEN) -o $(TARGETDIR)/texthelp.dxe -E _clipboard -E _clearOther -E _isWordChar -E _multiClick -E _widgetText -E _wordStart -E _wordEnd -U $(OBJS)
|
||||
mv $(TARGETDIR)/texthelp.dxe $@
|
||||
|
||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
|
@ -35,8 +36,11 @@ $(OBJDIR):
|
|||
$(LIBSDIR):
|
||||
mkdir -p $(LIBSDIR)
|
||||
|
||||
$(TARGETDIR):
|
||||
mkdir -p $(TARGETDIR)
|
||||
|
||||
# Dependencies
|
||||
$(OBJDIR)/textHelp.o: textHelp.c textHelp.h ../core/dvxWidgetPlugin.h ../core/dvxWidget.h ../core/dvxTypes.h ../core/dvxApp.h ../core/dvxDraw.h ../core/dvxWm.h ../core/dvxVideo.h
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(TARGET) $(LIBSDIR)/texthelp.dep
|
||||
rm -rf $(OBJS) $(TARGET) $(TARGETDIR)/texthelp.dep $(TARGETDIR)
|
||||
|
|
|
|||
313
texthelp/texthelp.dhs
Normal file
313
texthelp/texthelp.dhs
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
.topic lib.texthelp
|
||||
.title Text Help Library
|
||||
.toc 0 Text Help Library
|
||||
.default
|
||||
.index textHelp
|
||||
.index Text Editing
|
||||
.index Shared Library
|
||||
|
||||
.h1 Text Help Library
|
||||
|
||||
Shared text editing infrastructure library for DVX widget DXEs. Provides cursor blink management, cross-widget selection clearing, word boundary logic, and a complete single-line text editing engine. Used by TextInput, Spinner, ComboBox, AnsiTerm, and other widgets that need text editing behavior.
|
||||
|
||||
Header: texthelp/textHelp.h
|
||||
|
||||
.h2 Constants
|
||||
|
||||
.table
|
||||
Constant Value Description
|
||||
-------- ----- -----------
|
||||
TEXT_INPUT_PAD 3 Pixel padding inside text editing fields.
|
||||
.endtable
|
||||
|
||||
.topic lib.texthelp.cursorblink
|
||||
.title wgtUpdateCursorBlink
|
||||
.toc 1 wgtUpdateCursorBlink
|
||||
.index wgtUpdateCursorBlink
|
||||
.index Cursor Blink
|
||||
|
||||
.h2 wgtUpdateCursorBlink
|
||||
|
||||
Advances the cursor blink timer. When the blink interval (250 ms) elapses, toggles the global sCursorBlinkOn flag and invalidates the currently focused widget for repaint. Registered automatically at library load time via a constructor function that sets the sCursorBlinkFn pointer.
|
||||
|
||||
.code
|
||||
void wgtUpdateCursorBlink(void);
|
||||
.endcode
|
||||
|
||||
Takes no arguments and returns nothing. Called by the core event loop.
|
||||
|
||||
.topic lib.texthelp.clearother
|
||||
.title clearOtherSelections
|
||||
.toc 1 clearOtherSelections
|
||||
.index clearOtherSelections
|
||||
.index Selection
|
||||
|
||||
.h2 clearOtherSelections
|
||||
|
||||
Clears the text selection on whichever widget previously had one, unless that widget is the one passed as the argument. Tracks the last selected widget in a static pointer for O(1) lookup instead of walking the widget tree. Validates that the previous widget's window is still in the window stack before touching it, so stale pointers from closed windows are handled safely. If the previous selection was in a different window, that window gets a full repaint to clear the stale highlight.
|
||||
|
||||
.code
|
||||
void clearOtherSelections(WidgetT *except);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Type Description
|
||||
--------- ---- -----------
|
||||
except WidgetT * The widget that now owns the selection. Its selection is preserved.
|
||||
.endtable
|
||||
|
||||
.topic lib.texthelp.iswordchar
|
||||
.title isWordChar
|
||||
.toc 1 isWordChar
|
||||
.index isWordChar
|
||||
|
||||
.h2 isWordChar
|
||||
|
||||
Returns true if the character is alphanumeric or underscore. Used by word boundary functions to define what constitutes a "word" for double-click selection and Ctrl+arrow navigation.
|
||||
|
||||
.code
|
||||
bool isWordChar(char c);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Type Description
|
||||
--------- ---- -----------
|
||||
c char The character to test.
|
||||
.endtable
|
||||
|
||||
Returns true if c is alphanumeric (a-z, A-Z, 0-9) or underscore.
|
||||
|
||||
.topic lib.texthelp.wordend
|
||||
.title wordEnd
|
||||
.toc 1 wordEnd
|
||||
.index wordEnd
|
||||
|
||||
.h2 wordEnd
|
||||
|
||||
Scans forward from the given position, returning the index of the first non-word character. Used to find the right boundary of a word for double-click word selection.
|
||||
|
||||
.code
|
||||
int32_t wordEnd(const char *buf, int32_t len, int32_t pos);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Type Description
|
||||
--------- ---- -----------
|
||||
buf const char * The text buffer to scan.
|
||||
len int32_t Length of the text in the buffer.
|
||||
pos int32_t Starting position (character index).
|
||||
.endtable
|
||||
|
||||
Returns the index one past the last word character.
|
||||
|
||||
.topic lib.texthelp.wordstart
|
||||
.title wordStart
|
||||
.toc 1 wordStart
|
||||
.index wordStart
|
||||
|
||||
.h2 wordStart
|
||||
|
||||
Scans backward from the given position, returning the index of the first character of the current word. Used to find the left boundary of a word for double-click word selection.
|
||||
|
||||
.code
|
||||
int32_t wordStart(const char *buf, int32_t pos);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Type Description
|
||||
--------- ---- -----------
|
||||
buf const char * The text buffer to scan.
|
||||
pos int32_t Starting position (character index).
|
||||
.endtable
|
||||
|
||||
Returns the index of the first character of the word containing pos.
|
||||
|
||||
.topic lib.texthelp.dragupdate
|
||||
.title widgetTextEditDragUpdateLine
|
||||
.toc 1 widgetTextEditDragUpdateLine
|
||||
.index widgetTextEditDragUpdateLine
|
||||
.index Drag Select
|
||||
|
||||
.h2 widgetTextEditDragUpdateLine
|
||||
|
||||
Called during mouse drag to extend the selection in a single-line text field. Converts a viewport-relative pixel x coordinate to a character position and updates the cursor and selection end accordingly. Auto-scrolls the text when the mouse moves past the left or right visible edge.
|
||||
|
||||
.code
|
||||
void widgetTextEditDragUpdateLine(
|
||||
int32_t vx, int32_t leftEdge, int32_t maxChars,
|
||||
const BitmapFontT *font, int32_t len,
|
||||
int32_t *pCursorPos, int32_t *pScrollOff,
|
||||
int32_t *pSelEnd);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Type Description
|
||||
--------- ---- -----------
|
||||
vx int32_t Mouse x position in viewport coordinates.
|
||||
leftEdge int32_t Pixel x of the left edge of the text area.
|
||||
maxChars int32_t Maximum visible characters in the field.
|
||||
font const BitmapFontT * Bitmap font used for character width.
|
||||
len int32_t Total length of the text buffer.
|
||||
pCursorPos int32_t * [in/out] Current cursor position.
|
||||
pScrollOff int32_t * [in/out] Horizontal scroll offset in characters.
|
||||
pSelEnd int32_t * [out] Updated selection end position.
|
||||
.endtable
|
||||
|
||||
.topic lib.texthelp.mouseclick
|
||||
.title widgetTextEditMouseClick
|
||||
.toc 1 widgetTextEditMouseClick
|
||||
.index widgetTextEditMouseClick
|
||||
.index Mouse Click
|
||||
.index Multi-Click
|
||||
.index Word Select
|
||||
|
||||
.h2 widgetTextEditMouseClick
|
||||
|
||||
Handles mouse click events for single-line text widgets. Converts pixel coordinates to a character position using the font's character width. Detects multi-click sequences via the core multiClickDetect() function:
|
||||
|
||||
.list
|
||||
.item Single click -- places the cursor and begins a potential drag selection.
|
||||
.item Double click -- selects the word under the cursor (if wordSelect is true), or selects all text.
|
||||
.item Triple click -- selects all text in the field.
|
||||
.endlist
|
||||
|
||||
.code
|
||||
void widgetTextEditMouseClick(
|
||||
WidgetT *w, int32_t vx, int32_t vy,
|
||||
int32_t textLeftX, const BitmapFontT *font,
|
||||
const char *buf, int32_t len, int32_t scrollOff,
|
||||
int32_t *pCursorPos, int32_t *pSelStart,
|
||||
int32_t *pSelEnd, bool wordSelect,
|
||||
bool dragSelect);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Type Description
|
||||
--------- ---- -----------
|
||||
w WidgetT * The widget receiving the click.
|
||||
vx int32_t Mouse x in viewport coordinates.
|
||||
vy int32_t Mouse y in viewport coordinates.
|
||||
textLeftX int32_t Pixel x of the left edge of the text area.
|
||||
font const BitmapFontT * Bitmap font for character width calculation.
|
||||
buf const char * Text buffer contents.
|
||||
len int32_t Length of text in the buffer.
|
||||
scrollOff int32_t Current horizontal scroll offset.
|
||||
pCursorPos int32_t * [out] Computed cursor position.
|
||||
pSelStart int32_t * [out] Selection start.
|
||||
pSelEnd int32_t * [out] Selection end.
|
||||
wordSelect bool If true, double-click selects word; if false, selects all.
|
||||
dragSelect bool If true, single-click begins drag selection.
|
||||
.endtable
|
||||
|
||||
.topic lib.texthelp.onkey
|
||||
.title widgetTextEditOnKey
|
||||
.toc 1 widgetTextEditOnKey
|
||||
.index widgetTextEditOnKey
|
||||
.index Key Handling
|
||||
.index Text Editing
|
||||
.index Undo
|
||||
.index Clipboard
|
||||
|
||||
.h2 widgetTextEditOnKey
|
||||
|
||||
The core single-line text editing engine. Processes a single keystroke and updates the buffer, cursor, scroll offset, selection, and undo state. All state is passed by pointer so the function is reusable across TextInput, Spinner, ComboBox, and any other single-line widget.
|
||||
|
||||
.code
|
||||
void widgetTextEditOnKey(
|
||||
WidgetT *w, int32_t key, int32_t mod,
|
||||
char *buf, int32_t bufSize,
|
||||
int32_t *pLen, int32_t *pCursor,
|
||||
int32_t *pScrollOff,
|
||||
int32_t *pSelStart, int32_t *pSelEnd,
|
||||
char *undoBuf, int32_t *pUndoLen,
|
||||
int32_t *pUndoCursor, int32_t fieldWidth);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Type Description
|
||||
--------- ---- -----------
|
||||
w WidgetT * The widget receiving the key event.
|
||||
key int32_t Key code (ASCII for printable, control code or scan|0x100 for extended).
|
||||
mod int32_t Modifier flags (KEY_MOD_SHIFT, etc.).
|
||||
buf char * [in/out] The text buffer.
|
||||
bufSize int32_t Total size of the buffer including null terminator.
|
||||
pLen int32_t * [in/out] Current text length.
|
||||
pCursor int32_t * [in/out] Cursor position (character index).
|
||||
pScrollOff int32_t * [in/out] Horizontal scroll offset.
|
||||
pSelStart int32_t * [in/out] Selection start (-1 if no selection).
|
||||
pSelEnd int32_t * [in/out] Selection end (-1 if no selection).
|
||||
undoBuf char * Undo buffer (same size as buf). NULL to disable undo.
|
||||
pUndoLen int32_t * [in/out] Length of undo buffer contents.
|
||||
pUndoCursor int32_t * [in/out] Cursor position saved in undo state.
|
||||
fieldWidth int32_t Visible field width in pixels. 0 = use widget width.
|
||||
.endtable
|
||||
|
||||
.h3 Supported Keys
|
||||
|
||||
.table
|
||||
Key Action
|
||||
--- ------
|
||||
Printable Insert character at cursor (replaces selection if active).
|
||||
Backspace Delete character before cursor or delete selection.
|
||||
Delete Delete character after cursor or delete selection.
|
||||
Left Move cursor left. With Shift, extend selection.
|
||||
Right Move cursor right. With Shift, extend selection.
|
||||
Ctrl+Left Move cursor to previous word boundary. With Shift, extend selection.
|
||||
Ctrl+Right Move cursor to next word boundary. With Shift, extend selection.
|
||||
Home Move cursor to start. With Shift, extend selection.
|
||||
End Move cursor to end. With Shift, extend selection.
|
||||
Ctrl+A Select all text.
|
||||
Ctrl+C Copy selection to clipboard.
|
||||
Ctrl+V Paste from clipboard (newlines stripped).
|
||||
Ctrl+X Cut selection to clipboard.
|
||||
Ctrl+Z Undo (swaps current and undo buffers).
|
||||
.endtable
|
||||
|
||||
Fires w->onChange after any mutation (insert, delete, paste, cut, undo). Automatically adjusts scroll offset to keep cursor visible after every operation.
|
||||
|
||||
.topic lib.texthelp.paintline
|
||||
.title widgetTextEditPaintLine
|
||||
.toc 1 widgetTextEditPaintLine
|
||||
.index widgetTextEditPaintLine
|
||||
.index Text Rendering
|
||||
.index Selection Highlight
|
||||
|
||||
.h2 widgetTextEditPaintLine
|
||||
|
||||
Renders a single line of text with optional selection highlighting and a blinking cursor. Splits the visible text into up to three segments (pre-selection, selection, post-selection) and draws each with appropriate colors. The selection highlight uses the menu highlight colors from the color scheme. The cursor is drawn as a one-pixel vertical line when sCursorBlinkOn is true.
|
||||
|
||||
.code
|
||||
void widgetTextEditPaintLine(
|
||||
DisplayT *d, const BlitOpsT *ops,
|
||||
const BitmapFontT *font,
|
||||
const ColorSchemeT *colors,
|
||||
int32_t textX, int32_t textY,
|
||||
const char *buf, int32_t visLen,
|
||||
int32_t scrollOff, int32_t cursorPos,
|
||||
int32_t selStart, int32_t selEnd,
|
||||
uint32_t fg, uint32_t bg,
|
||||
bool showCursor,
|
||||
int32_t cursorMinX, int32_t cursorMaxX);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
Parameter Type Description
|
||||
--------- ---- -----------
|
||||
d DisplayT * Display context for drawing.
|
||||
ops const BlitOpsT * Blit operations table for the current pixel format.
|
||||
font const BitmapFontT * Bitmap font for text rendering.
|
||||
colors const ColorSchemeT * Color scheme (used for selection highlight colors).
|
||||
textX int32_t Pixel x of the start of visible text.
|
||||
textY int32_t Pixel y of the text baseline.
|
||||
buf const char * Visible portion of the text buffer (buf + scrollOff).
|
||||
visLen int32_t Number of visible characters to draw.
|
||||
scrollOff int32_t Scroll offset (used to map selection to visible range).
|
||||
cursorPos int32_t Absolute cursor position in the full buffer.
|
||||
selStart int32_t Selection start (-1 if no selection).
|
||||
selEnd int32_t Selection end (-1 if no selection).
|
||||
fg uint32_t Foreground (text) color.
|
||||
bg uint32_t Background color.
|
||||
showCursor bool Whether to draw the cursor (false when widget lacks focus).
|
||||
cursorMinX int32_t Left clipping bound for cursor drawing.
|
||||
cursorMaxX int32_t Right clipping bound for cursor drawing.
|
||||
.endtable
|
||||
|
|
@ -1,18 +1,23 @@
|
|||
# DVX Tools Makefile
|
||||
#
|
||||
# Builds native (host) utilities. These run on the development machine
|
||||
# (Linux or DOS), not on the target. No cross-compilation needed.
|
||||
# Builds native (host) utilities and cross-compiled DOS tools.
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -O2 -Wall -Wextra -Werror -Wno-format-truncation -I../core -I../core/platform
|
||||
|
||||
BINDIR = ../bin
|
||||
|
||||
.PHONY: all clean
|
||||
DJGPP_PREFIX = $(HOME)/djgpp/djgpp
|
||||
DOSCC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
|
||||
EXE2COFF = $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/exe2coff
|
||||
CWSDSTUB = $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/CWSDSTUB.EXE
|
||||
DOSCFLAGS = -O2 -Wall -Wextra -Werror -Wno-format-truncation -march=i486 -mtune=i586 -I../core -I../core/platform
|
||||
|
||||
BINDIR = ../bin
|
||||
CONFIGDIR = ../bin/config
|
||||
SYSTEMDIR = ../bin/system
|
||||
|
||||
all: $(BINDIR)/dvxres $(BINDIR)/mkicon $(BINDIR)/mktbicon $(BINDIR)/mkwgticon $(BINDIR)/bmp2raw $(BINDIR)/dvxhlpc $(CONFIGDIR)/SPLASH.RAW
|
||||
.PHONY: all clean deploy-helpsrc
|
||||
|
||||
all: $(BINDIR)/dvxres $(BINDIR)/mkicon $(BINDIR)/mktbicon $(BINDIR)/mkwgticon $(BINDIR)/bmp2raw $(BINDIR)/dvxhlpc $(SYSTEMDIR)/SPLASH.RAW $(SYSTEMDIR)/DVXHLPC.EXE $(SYSTEMDIR)/DVXRES.EXE
|
||||
|
||||
$(BINDIR)/dvxres: dvxres.c ../core/dvxResource.c ../core/dvxResource.h | $(BINDIR)
|
||||
$(CC) $(CFLAGS) -o $@ dvxres.c ../core/dvxResource.c
|
||||
|
|
@ -32,7 +37,7 @@ $(BINDIR)/bmp2raw: bmp2raw.c | $(BINDIR)
|
|||
$(BINDIR)/dvxhlpc: dvxhlpc.c ../apps/dvxhelp/hlpformat.h | $(BINDIR)
|
||||
$(CC) $(CFLAGS) -o $@ dvxhlpc.c
|
||||
|
||||
$(CONFIGDIR)/SPLASH.RAW: $(BINDIR)/bmp2raw ../assets/splash.bmp | $(CONFIGDIR)
|
||||
$(SYSTEMDIR)/SPLASH.RAW: $(BINDIR)/bmp2raw ../assets/splash.bmp | $(SYSTEMDIR)
|
||||
$(BINDIR)/bmp2raw ../assets/splash.bmp $@
|
||||
|
||||
$(BINDIR):
|
||||
|
|
@ -41,5 +46,78 @@ $(BINDIR):
|
|||
$(CONFIGDIR):
|
||||
mkdir -p $(CONFIGDIR)
|
||||
|
||||
$(SYSTEMDIR)/DVXHLPC.EXE: dvxhlpc.c ../apps/dvxhelp/hlpformat.h | $(SYSTEMDIR)
|
||||
$(DOSCC) $(DOSCFLAGS) -o $(SYSTEMDIR)/dvxhlpc.exe dvxhlpc.c
|
||||
$(EXE2COFF) $(SYSTEMDIR)/dvxhlpc.exe
|
||||
cat $(CWSDSTUB) $(SYSTEMDIR)/dvxhlpc > $@
|
||||
rm -f $(SYSTEMDIR)/dvxhlpc $(SYSTEMDIR)/dvxhlpc.exe
|
||||
|
||||
$(SYSTEMDIR)/DVXRES.EXE: dvxres.c ../core/dvxResource.c ../core/dvxResource.h | $(SYSTEMDIR)
|
||||
$(DOSCC) $(DOSCFLAGS) -o $(SYSTEMDIR)/dvxres.exe dvxres.c ../core/dvxResource.c
|
||||
$(EXE2COFF) $(SYSTEMDIR)/dvxres.exe
|
||||
cat $(CWSDSTUB) $(SYSTEMDIR)/dvxres > $@
|
||||
rm -f $(SYSTEMDIR)/dvxres $(SYSTEMDIR)/dvxres.exe
|
||||
|
||||
$(SYSTEMDIR):
|
||||
mkdir -p $(SYSTEMDIR)
|
||||
|
||||
deploy-helpsrc:
|
||||
@echo "Deploying help source files..."
|
||||
@for pair in \
|
||||
"../core/*.dhs:$(BINDIR)/libs/kpunch/libdvx" \
|
||||
"../tasks/*.dhs:$(BINDIR)/libs/kpunch/libtasks" \
|
||||
"../shell/*.dhs:$(BINDIR)/libs/kpunch/dvxshell" \
|
||||
"../sql/*.dhs:$(BINDIR)/libs/kpunch/dvxsql" \
|
||||
"../texthelp/*.dhs:$(BINDIR)/libs/kpunch/texthelp" \
|
||||
"../listhelp/*.dhs:$(BINDIR)/libs/kpunch/listhelp" \
|
||||
"../taskmgr/*.dhs:$(BINDIR)/libs/kpunch/taskmgr" \
|
||||
"../serial/*.dhs:$(BINDIR)/libs/kpunch/serial" \
|
||||
"../apps/dvxbasic/basrt.dhs:$(BINDIR)/libs/kpunch/basrt" \
|
||||
; do \
|
||||
src=$${pair%%:*}; dst=$${pair##*:}; \
|
||||
for f in $$src; do \
|
||||
[ -f "$$f" ] || continue; \
|
||||
name=$$(basename "$$f" | tr a-z A-Z); \
|
||||
cp "$$f" "$$dst"/"$$name"; \
|
||||
done; \
|
||||
done
|
||||
@for f in ../widgets/*.dhs; do \
|
||||
[ -f "$$f" ] || continue; \
|
||||
name=$$(basename "$$f" | tr a-z A-Z); \
|
||||
cp "$$f" $(BINDIR)/widgets/kpunch/"$$name"; \
|
||||
done
|
||||
@for d in ../widgets/*/; do \
|
||||
for f in "$$d"*.dhs "$$d"*.bhs; do \
|
||||
[ -f "$$f" ] || continue; \
|
||||
name=$$(basename "$$f" | tr a-z A-Z); \
|
||||
base=$$(basename "$$f" | tr A-Z a-z); \
|
||||
base=$${base%.dhs}; \
|
||||
base=$${base%.bhs}; \
|
||||
if [ -d $(BINDIR)/widgets/kpunch/"$$base" ]; then \
|
||||
cp "$$f" $(BINDIR)/widgets/kpunch/"$$base"/"$$name"; \
|
||||
fi; \
|
||||
done; \
|
||||
done
|
||||
@for f in ../apps/dvxbasic/*.dhs; do \
|
||||
[ -f "$$f" ] || continue; \
|
||||
name=$$(basename "$$f" | tr a-z A-Z); \
|
||||
cp "$$f" $(BINDIR)/apps/kpunch/dvxbasic/"$$name"; \
|
||||
done
|
||||
@for f in ../apps/dvxhelp/*.dhs; do \
|
||||
[ -f "$$f" ] || continue; \
|
||||
name=$$(basename "$$f" | tr a-z A-Z); \
|
||||
cp "$$f" $(BINDIR)/apps/kpunch/dvxhelp/"$$name"; \
|
||||
done
|
||||
@echo "Deploying help config files..."
|
||||
@for d in ../apps/*/; do \
|
||||
for f in "$$d"*.hcf; do \
|
||||
[ -f "$$f" ] || continue; \
|
||||
appdir=$$(basename "$$d"); \
|
||||
name=$$(basename "$$f" | tr a-z A-Z); \
|
||||
cp "$$f" $(BINDIR)/apps/kpunch/"$$appdir"/"$$name"; \
|
||||
done; \
|
||||
done
|
||||
|
||||
clean:
|
||||
rm -f $(BINDIR)/dvxres
|
||||
rm -rf $(SYSTEMDIR)
|
||||
|
|
|
|||
|
|
@ -128,8 +128,9 @@ static TrigramT trigrams[MAX_TRIGRAMS];
|
|||
static int32_t trigramCount = 0;
|
||||
|
||||
static char imageDir[260] = ".";
|
||||
static const char *htmlPath = NULL;
|
||||
static const char *htmlPath = NULL;
|
||||
static int32_t errorCount = 0;
|
||||
static bool quietMode = false;
|
||||
|
||||
// Parse state
|
||||
static const char *currentFile = NULL;
|
||||
|
|
@ -155,6 +156,7 @@ static void emitWarning(const char *fmt, ...);
|
|||
static int32_t findImage(const char *filename);
|
||||
static void flushParagraph(TopicT *topic, char *para, int32_t paraLen, uint8_t type, uint8_t flags);
|
||||
static void freeAll(void);
|
||||
static void hlpcInfo(const char *fmt, ...);
|
||||
static void parseDirective(const char *line, TopicT **curTopic, bool *inList, bool *inTable, bool *inCode, bool *inNote, uint8_t *noteFlags, char *para, int32_t *paraLen, int32_t includeDepth);
|
||||
static void parseFile(const char *path, TopicT **curTopic, bool *inList, bool *inTable, bool *inCode, bool *inNote, uint8_t *noteFlags, char *para, int32_t *paraLen, int32_t includeDepth);
|
||||
static void pass1Parse(int32_t fileCount, char **files);
|
||||
|
|
@ -174,7 +176,7 @@ static void usage(void);
|
|||
static void emitError(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "error: %s:%d: ", currentFile ? currentFile : "<unknown>", currentLine);
|
||||
fprintf(stderr, "error: %s:%d: ", currentFile ? currentFile : "<unknown>", (int)currentLine);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(ap);
|
||||
|
|
@ -185,19 +187,30 @@ static void emitError(const char *fmt, ...) {
|
|||
static void emitWarning(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "warning: %s:%d: ", currentFile ? currentFile : "<unknown>", currentLine);
|
||||
fprintf(stderr, "warning: %s:%d: ", currentFile ? currentFile : "<unknown>", (int)currentLine);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
static void hlpcInfo(const char *fmt, ...) {
|
||||
if (quietMode) {
|
||||
return;
|
||||
}
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// usage
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void usage(void) {
|
||||
fprintf(stderr, "Usage: dvxhlpc -o output.hlp [-i imagedir] [--html out.html] input.dhs [...]\n");
|
||||
fprintf(stderr, "Usage: dvxhlpc -o output.hlp [-i imagedir] [--html out.html] [--quiet] input.dhs [@filelist] [...]\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
|
@ -621,7 +634,7 @@ static void parseFile(const char *path, TopicT **curTopic, bool *inList, bool *i
|
|||
currentFile = path;
|
||||
currentLine = 0;
|
||||
|
||||
fprintf(stderr, " parsing %s\n", path);
|
||||
hlpcInfo(" parsing %s\n", path);
|
||||
|
||||
char line[MAX_LINE_LEN];
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
|
|
@ -714,7 +727,7 @@ static void parseFile(const char *path, TopicT **curTopic, bool *inList, bool *i
|
|||
|
||||
|
||||
static void pass1Parse(int32_t fileCount, char **files) {
|
||||
fprintf(stderr, "Pass 1: Parsing %d input file(s)...\n", fileCount);
|
||||
hlpcInfo("Pass 1: Parsing %d input file(s)...\n", fileCount);
|
||||
|
||||
TopicT *curTopic = NULL;
|
||||
bool inList = false;
|
||||
|
|
@ -748,7 +761,7 @@ static void pass1Parse(int32_t fileCount, char **files) {
|
|||
flushParagraph(curTopic, para, paraLen, type, flags);
|
||||
}
|
||||
|
||||
fprintf(stderr, " %d topic(s), %d TOC entries, %d index entries, %d image(s)\n",
|
||||
hlpcInfo(" %d topic(s), %d TOC entries, %d index entries, %d image(s)\n",
|
||||
topicCount, tocCount, indexCount, imageCount);
|
||||
}
|
||||
|
||||
|
|
@ -758,7 +771,7 @@ static void pass1Parse(int32_t fileCount, char **files) {
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
static void pass2Wrap(void) {
|
||||
fprintf(stderr, "Pass 2: Joining paragraph lines...\n");
|
||||
hlpcInfo("Pass 2: Joining paragraph lines...\n");
|
||||
|
||||
// TEXT, LIST_ITEM, and NOTE records are stored as flowing text.
|
||||
// The viewer wraps them at display time based on actual window width.
|
||||
|
|
@ -824,7 +837,7 @@ static int32_t strTableAdd(const char *str) {
|
|||
|
||||
|
||||
static void pass3StringTable(void) {
|
||||
fprintf(stderr, "Pass 3: Building string table...\n");
|
||||
hlpcInfo("Pass 3: Building string table...\n");
|
||||
|
||||
strTabCap = INITIAL_STRTAB_SIZE;
|
||||
strTabSize = 0;
|
||||
|
|
@ -853,7 +866,7 @@ static void pass3StringTable(void) {
|
|||
strTableAdd(indexEntries[i].keyword);
|
||||
}
|
||||
|
||||
fprintf(stderr, " %d unique strings, %d bytes\n", strEntryCount, strTabSize);
|
||||
hlpcInfo(" %d unique strings, %d bytes\n", strEntryCount, strTabSize);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -961,14 +974,14 @@ static void buildSearchIndex(void) {
|
|||
|
||||
|
||||
static void pass4SearchIndex(void) {
|
||||
fprintf(stderr, "Pass 4: Building search index...\n");
|
||||
hlpcInfo("Pass 4: Building search index...\n");
|
||||
|
||||
buildSearchIndex();
|
||||
|
||||
// Sort trigrams
|
||||
qsort(trigrams, trigramCount, sizeof(TrigramT), compareTrigrams);
|
||||
|
||||
fprintf(stderr, " %d trigrams\n", trigramCount);
|
||||
hlpcInfo(" %d trigrams\n", trigramCount);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1315,7 +1328,7 @@ static int emitHtml(const char *outputPath) {
|
|||
|
||||
|
||||
static int pass5Serialize(const char *outputPath) {
|
||||
fprintf(stderr, "Pass 5: Serializing to %s...\n", outputPath);
|
||||
hlpcInfo("Pass 5: Serializing to %s...\n", outputPath);
|
||||
|
||||
FILE *f = fopen(outputPath, "wb");
|
||||
if (!f) {
|
||||
|
|
@ -1367,7 +1380,7 @@ static int pass5Serialize(const char *outputPath) {
|
|||
offset += img->fileSize;
|
||||
free(imgData);
|
||||
fclose(imgFile);
|
||||
fprintf(stderr, " image: %s (%d bytes)\n", img->path, img->fileSize);
|
||||
hlpcInfo(" image: %s (%d bytes)\n", img->path, img->fileSize);
|
||||
}
|
||||
hdr.imagePoolSize = offset - hdr.imagePoolOffset;
|
||||
|
||||
|
|
@ -1526,7 +1539,7 @@ static int pass5Serialize(const char *outputPath) {
|
|||
free(topicContentOffsets);
|
||||
free(topicContentSizes);
|
||||
|
||||
fprintf(stderr, " wrote %u bytes\n", offset);
|
||||
hlpcInfo(" wrote %u bytes\n", offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1577,9 +1590,47 @@ int main(int argc, char **argv) {
|
|||
usage();
|
||||
}
|
||||
htmlPath = argv[i];
|
||||
} else if (strcmp(argv[i], "--quiet") == 0) {
|
||||
quietMode = true;
|
||||
} else if (argv[i][0] == '-') {
|
||||
fprintf(stderr, "error: unknown option '%s'\n", argv[i]);
|
||||
usage();
|
||||
} else if (argv[i][0] == '@') {
|
||||
// Response file: read filenames from the file, one per line
|
||||
FILE *rf = fopen(argv[i] + 1, "r");
|
||||
|
||||
if (!rf) {
|
||||
fprintf(stderr, "error: cannot open response file '%s'\n", argv[i] + 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define RESP_LINE_MAX 260
|
||||
static char respLines[256][RESP_LINE_MAX];
|
||||
char rline[RESP_LINE_MAX];
|
||||
|
||||
while (fgets(rline, (int)sizeof(rline), rf)) {
|
||||
int32_t rlen = (int32_t)strlen(rline);
|
||||
|
||||
while (rlen > 0 && (rline[rlen - 1] == '\n' || rline[rlen - 1] == '\r' || rline[rlen - 1] == ' ')) {
|
||||
rline[--rlen] = '\0';
|
||||
}
|
||||
|
||||
if (rlen == 0 || rline[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inputCount >= 256) {
|
||||
fprintf(stderr, "error: too many input files\n");
|
||||
fclose(rf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
snprintf(respLines[inputCount], sizeof(respLines[inputCount]), "%s", rline);
|
||||
inputFiles[inputCount] = respLines[inputCount];
|
||||
inputCount++;
|
||||
}
|
||||
|
||||
fclose(rf);
|
||||
} else {
|
||||
if (inputCount >= 256) {
|
||||
fprintf(stderr, "error: too many input files\n");
|
||||
|
|
@ -1593,11 +1644,11 @@ int main(int argc, char **argv) {
|
|||
usage();
|
||||
}
|
||||
|
||||
fprintf(stderr, "dvxhlpc: DVX Help Compiler\n");
|
||||
hlpcInfo("dvxhlpc: DVX Help Compiler\n");
|
||||
|
||||
pass1Parse(inputCount, inputFiles);
|
||||
if (errorCount > 0) {
|
||||
fprintf(stderr, "Aborting due to %d error(s).\n", errorCount);
|
||||
fprintf(stderr, "Aborting due to %d error(s).\n", (int)errorCount);
|
||||
freeAll();
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -1607,7 +1658,7 @@ int main(int argc, char **argv) {
|
|||
// Emit HTML if requested (uses wrapped text, before binary passes)
|
||||
if (htmlPath) {
|
||||
if (emitHtml(htmlPath) == 0) {
|
||||
fprintf(stderr, "HTML: wrote %s\n", htmlPath);
|
||||
hlpcInfo("HTML: wrote %s\n", htmlPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1616,7 +1667,7 @@ int main(int argc, char **argv) {
|
|||
|
||||
int result = pass5Serialize(outputPath);
|
||||
if (result == 0) {
|
||||
fprintf(stderr, "Done. %d topic(s), %d TOC entries, %d index keywords, %d trigrams.\n",
|
||||
hlpcInfo("Done. %d topic(s), %d TOC entries, %d index keywords, %d trigrams.\n",
|
||||
topicCount, tocCount, indexCount, trigramCount);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -386,7 +386,7 @@ static int cmdAdd(const char *dxePath, const char *name, const char *typeStr, co
|
|||
return 1;
|
||||
}
|
||||
|
||||
printf("Added '%s' (%s, %u bytes) to %s\n", name, typeStr, newSize, dxePath);
|
||||
printf("Added '%s' (%s, %u bytes) to %s\n", name, typeStr, (unsigned)newSize, dxePath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -553,7 +553,7 @@ static int cmdBuild(const char *dxePath, const char *manifestPath) {
|
|||
data[count] = newData;
|
||||
count++;
|
||||
|
||||
printf(" %s (%s, %u bytes)\n", name, typeStr, newSize);
|
||||
printf(" %s (%s, %u bytes)\n", name, typeStr, (unsigned)newSize);
|
||||
}
|
||||
|
||||
fclose(mf);
|
||||
|
|
@ -579,7 +579,7 @@ static int cmdBuild(const char *dxePath, const char *manifestPath) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
printf("Built %u resource(s) into %s\n", count, dxePath);
|
||||
printf("Built %u resource(s) into %s\n", (unsigned)count, dxePath);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
|
|
@ -618,7 +618,7 @@ static int cmdList(const char *dxePath) {
|
|||
|
||||
fseek(f, footer.dirOffset, SEEK_SET);
|
||||
|
||||
printf("Resources in %s (%u entries):\n", dxePath, footer.entryCount);
|
||||
printf("Resources in %s (%u entries):\n", dxePath, (unsigned)footer.entryCount);
|
||||
printf(" %-31s %-8s %s\n", "Name", "Type", "Size");
|
||||
printf(" %-31s %-8s %s\n", "-------------------------------", "--------", "--------");
|
||||
|
||||
|
|
@ -639,7 +639,7 @@ static int cmdList(const char *dxePath) {
|
|||
typeStr = "binary";
|
||||
}
|
||||
|
||||
printf(" %-31s %-8s %u\n", entry.name, typeStr, entry.size);
|
||||
printf(" %-31s %-8s %u\n", entry.name, typeStr, (unsigned)entry.size);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
|
@ -680,7 +680,7 @@ static int cmdGet(const char *dxePath, const char *name, const char *outPath) {
|
|||
|
||||
fwrite(buf, 1, size, f);
|
||||
fclose(f);
|
||||
printf("Extracted '%s' (%u bytes) to %s\n", name, size, outPath);
|
||||
printf("Extracted '%s' (%u bytes) to %s\n", name, (unsigned)size, outPath);
|
||||
} else {
|
||||
// Write to stdout
|
||||
fwrite(buf, 1, size, stdout);
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ static void drawTimer(void) {
|
|||
clear(192, 192, 192);
|
||||
// Clock face
|
||||
for (int a = 0; a < 12; a++) {
|
||||
int cx = 12, cy = 12, r = 8;
|
||||
int cx = 12, cy = 12;
|
||||
// Approximate circle points
|
||||
static const int dx[] = {0, 4, 7, 8, 7, 4, 0, -4, -7, -8, -7, -4};
|
||||
static const int dy[] = {-8, -7, -4, 0, 4, 7, 8, 7, 4, 0, -4, -7};
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ CFLAGS = -O2 -Wall -Wextra -Werror -Wno-type-limits -Wno-sign-compare -Wn
|
|||
DVXRES = ../bin/dvxres
|
||||
|
||||
OBJDIR = ../obj/widgets
|
||||
WGTDIR = ../bin/widgets
|
||||
WGTDIR = ../bin/widgets/kpunch
|
||||
|
||||
# Widget name -> directory, source, short name for .wgt/.res
|
||||
# Format: wgtShortName:dirName:srcFile:resFile
|
||||
|
|
@ -47,21 +47,25 @@ WIDGETS = \
|
|||
|
||||
# Extract lists
|
||||
WGT_NAMES = $(foreach w,$(WIDGETS),$(word 1,$(subst :, ,$w)))
|
||||
WGT_MODS = $(WGT_NAMES:%=$(WGTDIR)/%.wgt)
|
||||
WGT_MODS = $(foreach n,$(WGT_NAMES),$(WGTDIR)/$(n)/$(n).wgt)
|
||||
OBJS = $(foreach w,$(WIDGETS),$(OBJDIR)/$(word 3,$(subst :, ,$w)).o)
|
||||
|
||||
# Per-widget extra DXE3GEN flags (e.g. additional -E exports for dlsym)
|
||||
EXTRA_DXE_FLAGS_datactrl = -E _dataCtrl
|
||||
|
||||
DEPFILES = textinpt combobox spinner terminal listbox dropdown listview treeview
|
||||
WGT_DEPS = $(DEPFILES:%=$(WGTDIR)/%.dep)
|
||||
WGT_DEPS = $(foreach d,$(DEPFILES),$(WGTDIR)/$(d)/$(d).dep)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(WGT_MODS) $(WGT_DEPS)
|
||||
|
||||
$(WGTDIR)/%.dep: ../config/%.dep | $(WGTDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
define DEP_RULE
|
||||
$(WGTDIR)/$(1)/$(1).dep: ../config/$(1).dep | $(WGTDIR)/$(1)
|
||||
cp $$< $$@
|
||||
endef
|
||||
|
||||
$(foreach d,$(DEPFILES),$(eval $(call DEP_RULE,$(d))))
|
||||
|
||||
# Compile: source is in subdirectory
|
||||
WIDGET_DEPS_H = ../core/dvxWidgetPlugin.h ../core/dvxWidget.h ../core/dvxTypes.h ../core/dvxApp.h ../core/dvxDraw.h ../core/dvxWm.h ../core/dvxVideo.h ../core/platform/dvxPlatform.h
|
||||
|
|
@ -70,12 +74,15 @@ define WIDGET_RULES
|
|||
$(OBJDIR)/$(word 3,$(subst :, ,$1)).o: $(word 2,$(subst :, ,$1))/$(word 3,$(subst :, ,$1)).c $$(WIDGET_DEPS_H) | $(OBJDIR)
|
||||
$$(CC) $$(CFLAGS) -c -o $$@ $$<
|
||||
|
||||
$(WGTDIR)/$(word 1,$(subst :, ,$1)).wgt: $(OBJDIR)/$(word 3,$(subst :, ,$1)).o | $(WGTDIR)
|
||||
$$(DXE3GEN) -o $(WGTDIR)/$(word 1,$(subst :, ,$1)).dxe -E _wgtRegister $$(EXTRA_DXE_FLAGS_$(word 1,$(subst :, ,$1))) -U $$<
|
||||
mv $(WGTDIR)/$(word 1,$(subst :, ,$1)).dxe $$@
|
||||
$(WGTDIR)/$(word 1,$(subst :, ,$1))/$(word 1,$(subst :, ,$1)).wgt: $(OBJDIR)/$(word 3,$(subst :, ,$1)).o | $(WGTDIR)/$(word 1,$(subst :, ,$1))
|
||||
$$(DXE3GEN) -o $(WGTDIR)/$(word 1,$(subst :, ,$1))/$(word 1,$(subst :, ,$1)).dxe -E _wgtRegister $$(EXTRA_DXE_FLAGS_$(word 1,$(subst :, ,$1))) -U $$<
|
||||
mv $(WGTDIR)/$(word 1,$(subst :, ,$1))/$(word 1,$(subst :, ,$1)).dxe $$@
|
||||
@if [ -f $(word 2,$(subst :, ,$1))/$(word 4,$(subst :, ,$1)).res ]; then \
|
||||
cd $(word 2,$(subst :, ,$1)) && ../$(DVXRES) build ../$$@ $(word 4,$(subst :, ,$1)).res; \
|
||||
fi
|
||||
|
||||
$(WGTDIR)/$(word 1,$(subst :, ,$1)):
|
||||
mkdir -p $$@
|
||||
endef
|
||||
|
||||
$(foreach w,$(WIDGETS),$(eval $(call WIDGET_RULES,$w)))
|
||||
|
|
|
|||
79
widgets/box/box.bhs
Normal file
79
widgets/box/box.bhs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
.topic ctrl.frame
|
||||
.title Frame
|
||||
.toc 1 Frame
|
||||
.index Frame
|
||||
.index Container
|
||||
|
||||
.h1 Frame
|
||||
|
||||
VB Equivalent: Frame -- DVX Widget: frame (titled VBox container)
|
||||
|
||||
A container with a titled border. Child controls are placed inside the frame using VBox layout. In the .frm file, nest Begin/End blocks inside the Frame block.
|
||||
|
||||
.h2 Type-Specific Properties
|
||||
|
||||
.table
|
||||
Property Type Description
|
||||
-------- ------ -------------------------------------------
|
||||
Caption String The title displayed in the frame border.
|
||||
.endtable
|
||||
|
||||
Container: Yes
|
||||
|
||||
Default Event: Click
|
||||
|
||||
.h2 Example
|
||||
|
||||
.code
|
||||
Begin Frame Frame1
|
||||
Caption = "Options"
|
||||
Begin CheckBox Check1
|
||||
Caption = "Option A"
|
||||
End
|
||||
Begin CheckBox Check2
|
||||
Caption = "Option B"
|
||||
End
|
||||
End
|
||||
.endcode
|
||||
|
||||
.link ctrl.common.props Common Properties, Events, and Methods
|
||||
.topic ctrl.hbox
|
||||
.title HBox
|
||||
.toc 1 HBox
|
||||
.index HBox
|
||||
.index Horizontal Layout
|
||||
|
||||
.h1 HBox
|
||||
|
||||
DVX Extension -- DVX Widget: hbox (horizontal layout container)
|
||||
|
||||
A container that arranges its children horizontally, left to right. Use Weight on children to distribute extra space.
|
||||
|
||||
Container: Yes
|
||||
|
||||
Default Event: Click
|
||||
|
||||
No type-specific properties.
|
||||
|
||||
.link ctrl.common.props Common Properties, Events, and Methods
|
||||
.link ctrl.vbox VBox
|
||||
.topic ctrl.vbox
|
||||
.title VBox
|
||||
.toc 1 VBox
|
||||
.index VBox
|
||||
.index Vertical Layout
|
||||
|
||||
.h1 VBox
|
||||
|
||||
DVX Extension -- DVX Widget: vbox (vertical layout container)
|
||||
|
||||
A container that arranges its children vertically, top to bottom. No title or border. Use Weight on children to distribute extra space.
|
||||
|
||||
Container: Yes
|
||||
|
||||
Default Event: Click
|
||||
|
||||
No type-specific properties.
|
||||
|
||||
.link ctrl.common.props Common Properties, Events, and Methods
|
||||
.link ctrl.hbox HBox
|
||||
|
|
@ -38,3 +38,25 @@ End Sub
|
|||
|
||||
.link ctrl.common.props Common Properties, Events, and Methods
|
||||
.link ctrl.databinding Data Binding
|
||||
.topic ctrl.textarea
|
||||
.title TextArea
|
||||
.toc 1 TextArea
|
||||
.index TextArea
|
||||
|
||||
.h1 TextArea
|
||||
|
||||
VB Equivalent: TextArea (DVX extension) -- DVX Widget: textarea (multi-line text input, max 4096 chars)
|
||||
|
||||
A multi-line text editing area. This is a DVX extension with no direct VB3 equivalent (VB uses a TextBox with MultiLine=True). Supports syntax colorization, line numbers, auto-indent, and find/replace via the C API.
|
||||
|
||||
.h2 Type-Specific Properties
|
||||
|
||||
.table
|
||||
Property Type Description
|
||||
-------- ------ -------------------------------------------
|
||||
Text String The full text content.
|
||||
.endtable
|
||||
|
||||
Default Event: Change
|
||||
|
||||
.link ctrl.common.props Common Properties, Events, and Methods
|
||||
Loading…
Add table
Reference in a new issue