Start of major refactor for dynamic library and widget loading.
This commit is contained in:
parent
7ae7ea1a97
commit
163959a192
95 changed files with 3885 additions and 3292 deletions
35
Makefile
35
Makefile
|
|
@ -1,28 +1,37 @@
|
|||
# DVX GUI -- Top-level Makefile
|
||||
#
|
||||
# Builds the full DVX stack: library, task switcher, shell, and apps.
|
||||
# Builds the full DVX stack: core library, task switcher,
|
||||
# bootstrap loader, widgets, shell, and apps.
|
||||
|
||||
.PHONY: all clean dvx tasks dvxshell apps
|
||||
.PHONY: all clean core tasks loader widgets shell apps
|
||||
|
||||
all: dvx tasks dvxshell apps
|
||||
all: core tasks loader widgets shell apps
|
||||
|
||||
dvx:
|
||||
$(MAKE) -C dvx
|
||||
core:
|
||||
$(MAKE) -C core
|
||||
|
||||
tasks:
|
||||
$(MAKE) -C tasks
|
||||
|
||||
dvxshell: dvx tasks
|
||||
$(MAKE) -C dvxshell
|
||||
loader: core tasks
|
||||
$(MAKE) -C loader
|
||||
|
||||
apps: dvx tasks dvxshell
|
||||
widgets: core tasks
|
||||
$(MAKE) -C widgets
|
||||
|
||||
shell: core tasks
|
||||
$(MAKE) -C shell
|
||||
|
||||
apps: core tasks shell
|
||||
$(MAKE) -C apps
|
||||
|
||||
clean:
|
||||
$(MAKE) -C dvx clean
|
||||
$(MAKE) -C core clean
|
||||
$(MAKE) -C tasks clean
|
||||
$(MAKE) -C dvxshell clean
|
||||
$(MAKE) -C loader clean
|
||||
$(MAKE) -C widgets clean
|
||||
$(MAKE) -C shell clean
|
||||
$(MAKE) -C apps clean
|
||||
-rmdir obj/dvx/widgets obj/dvx/platform obj/dvx/thirdparty obj/dvx obj/tasks obj/dvxshell obj/apps obj 2>/dev/null
|
||||
-rm -rf bin/config
|
||||
-rmdir bin/apps/cpanel bin/apps/imgview bin/apps/progman bin/apps/notepad bin/apps/clock bin/apps/dvxdemo bin/apps bin lib 2>/dev/null
|
||||
-rmdir obj 2>/dev/null
|
||||
-rm -rf bin/config bin/widgets bin/libs
|
||||
-rmdir bin/apps/cpanel bin/apps/imgview bin/apps/progman bin/apps/notepad bin/apps/clock bin/apps/dvxdemo bin/apps bin 2>/dev/null
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ DJGPP_PREFIX = $(HOME)/djgpp/djgpp
|
|||
DJGPP_LIBPATH = $(HOME)/claude/windriver/tools/lib
|
||||
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
|
||||
DXE3GEN = PATH=$(DJGPP_PREFIX)/bin:$(PATH) DJDIR=$(DJGPP_PREFIX)/i586-pc-msdosdjgpp $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/dxe3gen
|
||||
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../dvx -I../tasks -I../dvxshell
|
||||
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../core -I../core/platform -I../core/thirdparty -I../tasks -I../tasks/thirdparty -I../shell
|
||||
|
||||
OBJDIR = ../obj/apps
|
||||
BINDIR = ../bin/apps
|
||||
|
|
@ -84,12 +84,12 @@ $(BINDIR)/dvxdemo:
|
|||
mkdir -p $(BINDIR)/dvxdemo
|
||||
|
||||
# Dependencies
|
||||
$(OBJDIR)/imgview.o: imgview/imgview.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvx/dvxVideo.h ../dvxshell/shellApp.h
|
||||
$(OBJDIR)/cpanel.o: cpanel/cpanel.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxPrefs.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvx/platform/dvxPlatform.h ../dvxshell/shellApp.h
|
||||
$(OBJDIR)/progman.o: progman/progman.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvxshell/shellApp.h ../dvxshell/shellInfo.h
|
||||
$(OBJDIR)/notepad.o: notepad/notepad.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvxshell/shellApp.h
|
||||
$(OBJDIR)/clock.o: clock/clock.c ../dvx/dvxApp.h ../dvx/dvxWidget.h ../dvx/dvxDraw.h ../dvx/dvxVideo.h ../dvxshell/shellApp.h ../tasks/taskswitch.h
|
||||
$(OBJDIR)/dvxdemo.o: dvxdemo/dvxdemo.c ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvx/dvxVideo.h ../dvxshell/shellApp.h
|
||||
$(OBJDIR)/imgview.o: imgview/imgview.c ../core/dvxApp.h ../core/dvxDialog.h ../core/dvxWidget.h ../core/dvxWm.h ../core/dvxVideo.h ../shell/shellApp.h
|
||||
$(OBJDIR)/cpanel.o: cpanel/cpanel.c ../core/dvxApp.h ../core/dvxDialog.h ../core/dvxPrefs.h ../core/dvxWidget.h ../core/dvxWm.h ../core/platform/dvxPlatform.h ../shell/shellApp.h
|
||||
$(OBJDIR)/progman.o: progman/progman.c ../core/dvxApp.h ../core/dvxDialog.h ../core/dvxWidget.h ../core/dvxWm.h ../shell/shellApp.h ../shell/shellInfo.h
|
||||
$(OBJDIR)/notepad.o: notepad/notepad.c ../core/dvxApp.h ../core/dvxDialog.h ../core/dvxWidget.h ../core/dvxWm.h ../shell/shellApp.h
|
||||
$(OBJDIR)/clock.o: clock/clock.c ../core/dvxApp.h ../core/dvxWidget.h ../core/dvxDraw.h ../core/dvxVideo.h ../shell/shellApp.h ../tasks/taskswitch.h
|
||||
$(OBJDIR)/dvxdemo.o: dvxdemo/dvxdemo.c ../core/dvxApp.h ../core/dvxDialog.h ../core/dvxWidget.h ../core/dvxWm.h ../core/dvxVideo.h ../shell/shellApp.h
|
||||
|
||||
clean:
|
||||
rm -f $(OBJDIR)/cpanel.o $(OBJDIR)/imgview.o $(OBJDIR)/progman.o $(OBJDIR)/notepad.o $(OBJDIR)/clock.o $(OBJDIR)/dvxdemo.o
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@
|
|||
#include "dvxPrefs.h"
|
||||
#include "dvxWidget.h"
|
||||
#include "dvxWm.h"
|
||||
#include "platform/dvxPlatform.h"
|
||||
#include "dvxPlatform.h"
|
||||
#include "shellApp.h"
|
||||
#include "thirdparty/stb_ds.h"
|
||||
#include "stb_ds.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
#include "dvxWidget.h"
|
||||
#include "dvxWm.h"
|
||||
#include "shellApp.h"
|
||||
#include "thirdparty/stb_image.h"
|
||||
#include "stb_image.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
#include "dvxDialog.h"
|
||||
#include "dvxWidget.h"
|
||||
#include "dvxWm.h"
|
||||
#include "platform/dvxPlatform.h"
|
||||
#include "dvxPlatform.h"
|
||||
#include "shellApp.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
#include "dvxPrefs.h"
|
||||
#include "dvxWidget.h"
|
||||
#include "dvxWm.h"
|
||||
#include "platform/dvxPlatform.h"
|
||||
#include "dvxPlatform.h"
|
||||
#include "shellApp.h"
|
||||
#include "shellTaskMgr.h"
|
||||
#include "shellInfo.h"
|
||||
|
|
|
|||
14
config/dvxshell.dep
Normal file
14
config/dvxshell.dep
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
libtasks
|
||||
libdvx
|
||||
box
|
||||
button
|
||||
checkbox
|
||||
dropdown
|
||||
label
|
||||
listbox
|
||||
listview
|
||||
radio
|
||||
separator
|
||||
spacer
|
||||
statbar
|
||||
textinpt
|
||||
1
config/libdvx.dep
Normal file
1
config/libdvx.dep
Normal file
|
|
@ -0,0 +1 @@
|
|||
libtasks
|
||||
74
core/Makefile
Normal file
74
core/Makefile
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# DVX Core Library Makefile for DJGPP cross-compilation
|
||||
#
|
||||
# Builds libdvx.lib -- core GUI infrastructure (draw, compositor,
|
||||
# window manager, event dispatch, layout engine, widget infrastructure).
|
||||
# Zero widget implementations -- those are in ../widgets/ as .wgt modules.
|
||||
|
||||
DJGPP_PREFIX = $(HOME)/djgpp/djgpp
|
||||
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
|
||||
DXE3GEN = PATH=$(DJGPP_PREFIX)/bin:$(PATH) DJDIR=$(DJGPP_PREFIX)/i586-pc-msdosdjgpp $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/dxe3gen
|
||||
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I. -Iplatform -I../tasks
|
||||
|
||||
OBJDIR = ../obj/core
|
||||
LIBSDIR = ../bin/libs
|
||||
|
||||
# Core sources
|
||||
SRCS = dvxVideo.c dvxDraw.c dvxComp.c dvxWm.c dvxIcon.c dvxImageWrite.c \
|
||||
dvxApp.c dvxDialog.c dvxPrefs.c \
|
||||
widgetClass.c widgetCore.c widgetScrollbar.c \
|
||||
widgetLayout.c widgetEvent.c widgetOps.c
|
||||
|
||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||
TARGET = $(LIBSDIR)/libdvx.lib
|
||||
|
||||
# libdvx.lib export prefixes
|
||||
DVX_EXPORTS = -E _dvx -E _wgt -E _wm -E _prefs -E _rect -E _draw -E _pack -E _text \
|
||||
-E _setClip -E _resetClip -E _stbi_ -E _stbi_write -E _dirtyList \
|
||||
-E _widget \
|
||||
-E _sCursor -E _sDbl -E _sDebug -E _sClosed -E _sFocused -E _sKey \
|
||||
-E _sOpen -E _sPressed -E _sDrag -E _sDrawing -E _sResize \
|
||||
-E _sListView -E _sSplitter -E _sTreeView \
|
||||
-E _accelParse \
|
||||
-E _clearOther -E _clipboard -E _isWord -E _multiClick -E _wordStart -E _wordEnd
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET) $(LIBSDIR)/libdvx.dep
|
||||
|
||||
$(LIBSDIR)/libdvx.dep: ../config/libdvx.dep | $(LIBSDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
|
||||
$(TARGET): $(OBJS) | $(LIBSDIR)
|
||||
$(DXE3GEN) -o $@ $(DVX_EXPORTS) -U $(OBJS)
|
||||
|
||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR):
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
$(LIBSDIR):
|
||||
mkdir -p $(LIBSDIR)
|
||||
|
||||
# Dependencies
|
||||
CORE_HDRS = dvxTypes.h dvxApp.h dvxDraw.h dvxWm.h dvxVideo.h dvxWidget.h platform/dvxPlatform.h
|
||||
$(OBJDIR)/dvxVideo.o: dvxVideo.c dvxVideo.h platform/dvxPlatform.h dvxTypes.h dvxPalette.h
|
||||
$(OBJDIR)/dvxDraw.o: dvxDraw.c dvxDraw.h platform/dvxPlatform.h dvxTypes.h
|
||||
$(OBJDIR)/dvxComp.o: dvxComp.c dvxComp.h platform/dvxPlatform.h dvxTypes.h
|
||||
$(OBJDIR)/dvxWm.o: dvxWm.c dvxWm.h dvxTypes.h dvxDraw.h dvxComp.h dvxVideo.h dvxWidget.h thirdparty/stb_image.h
|
||||
$(OBJDIR)/dvxIcon.o: dvxIcon.c thirdparty/stb_image.h
|
||||
$(OBJDIR)/dvxImageWrite.o: dvxImageWrite.c thirdparty/stb_image_write.h
|
||||
$(OBJDIR)/dvxApp.o: dvxApp.c dvxApp.h platform/dvxPlatform.h dvxTypes.h dvxVideo.h dvxDraw.h dvxComp.h dvxWm.h dvxFont.h dvxCursor.h
|
||||
$(OBJDIR)/dvxDialog.o: dvxDialog.c dvxDialog.h platform/dvxPlatform.h dvxApp.h dvxWidget.h widgetInternal.h dvxTypes.h dvxDraw.h
|
||||
$(OBJDIR)/dvxPrefs.o: dvxPrefs.c dvxPrefs.h
|
||||
|
||||
WIDGET_DEPS = widgetInternal.h dvxWidget.h dvxTypes.h dvxApp.h dvxDraw.h dvxWm.h dvxVideo.h
|
||||
$(OBJDIR)/widgetClass.o: widgetClass.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetCore.o: widgetCore.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetScrollbar.o: widgetScrollbar.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetLayout.o: widgetLayout.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetEvent.o: widgetEvent.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetOps.o: widgetOps.c $(WIDGET_DEPS)
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(TARGET) $(LIBSDIR)/libdvx.dep
|
||||
|
|
@ -31,11 +31,11 @@
|
|||
#include "dvxApp.h"
|
||||
#include "dvxDialog.h"
|
||||
#include "dvxWidget.h"
|
||||
#include "widgets/widgetInternal.h"
|
||||
#include "widgetInternal.h"
|
||||
#include "dvxFont.h"
|
||||
#include "dvxCursor.h"
|
||||
|
||||
#include "platform/dvxPlatform.h"
|
||||
#include "dvxPlatform.h"
|
||||
#include "thirdparty/stb_ds.h"
|
||||
|
||||
#include <string.h>
|
||||
|
|
@ -726,14 +726,18 @@ static bool dispatchAccelKey(AppContextT *ctx, char key) {
|
|||
|
||||
case WidgetCheckboxE:
|
||||
if (sFocusedWidget) { sFocusedWidget->focused = false; }
|
||||
widgetCheckboxOnMouse(target, win->widgetRoot, 0, 0);
|
||||
if (target->wclass && target->wclass->onMouse) {
|
||||
target->wclass->onMouse(target, win->widgetRoot, 0, 0);
|
||||
}
|
||||
sFocusedWidget = target;
|
||||
wgtInvalidate(target);
|
||||
return true;
|
||||
|
||||
case WidgetRadioE:
|
||||
if (sFocusedWidget) { sFocusedWidget->focused = false; }
|
||||
widgetRadioOnMouse(target, win->widgetRoot, 0, 0);
|
||||
if (target->wclass && target->wclass->onMouse) {
|
||||
target->wclass->onMouse(target, win->widgetRoot, 0, 0);
|
||||
}
|
||||
sFocusedWidget = target;
|
||||
wgtInvalidate(target);
|
||||
return true;
|
||||
|
|
@ -4628,7 +4632,7 @@ static void updateCursorShape(AppContextT *ctx) {
|
|||
|
||||
WidgetT *hit = widgetHitTest(win->widgetRoot, vx, vy);
|
||||
|
||||
if (hit && hit->type == WidgetListViewE && widgetListViewColBorderHit(hit, vx, vy)) {
|
||||
if (hit && hit->type == WidgetListViewE && sListViewColBorderHitFn && sListViewColBorderHitFn(hit, vx, vy)) {
|
||||
newCursor = CURSOR_RESIZE_H;
|
||||
}
|
||||
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
// exactly once per pixel per frame.
|
||||
|
||||
#include "dvxComp.h"
|
||||
#include "platform/dvxPlatform.h"
|
||||
#include "dvxPlatform.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -21,9 +21,9 @@
|
|||
// context pointers through every widget callback.
|
||||
|
||||
#include "dvxDialog.h"
|
||||
#include "platform/dvxPlatform.h"
|
||||
#include "dvxPlatform.h"
|
||||
#include "dvxWidget.h"
|
||||
#include "widgets/widgetInternal.h"
|
||||
#include "widgetInternal.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
|
|
@ -50,7 +50,7 @@
|
|||
// unlikely, helping the branch predictor on Pentium and later.
|
||||
|
||||
#include "dvxDraw.h"
|
||||
#include "platform/dvxPlatform.h"
|
||||
#include "dvxPlatform.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
// shuffling.
|
||||
|
||||
#include "dvxVideo.h"
|
||||
#include "platform/dvxPlatform.h"
|
||||
#include "dvxPlatform.h"
|
||||
#include "dvxPalette.h"
|
||||
|
||||
#include <string.h>
|
||||
|
|
@ -67,10 +67,10 @@ struct WidgetClassT;
|
|||
// Widget type enum
|
||||
// ============================================================
|
||||
//
|
||||
// Used as the index into widgetClassTable[] (in widgetInternal.h) to
|
||||
// look up the vtable for each widget type. Adding a new widget type
|
||||
// requires adding an enum value here, a corresponding union member in
|
||||
// WidgetT, and a WidgetClassT entry in widgetClassTable[].
|
||||
// Used as the index into widgetClassTable[] to look up the vtable for
|
||||
// each widget type. Built-in types occupy slots 0..WGT_TYPE_BUILTIN_COUNT-1.
|
||||
// Dynamic types registered via wgtRegisterClass() start at
|
||||
// WGT_TYPE_DYNAMIC_BASE.
|
||||
|
||||
typedef enum {
|
||||
WidgetVBoxE,
|
||||
|
|
@ -104,9 +104,15 @@ typedef enum {
|
|||
WidgetSpinnerE,
|
||||
WidgetScrollPaneE,
|
||||
WidgetSplitterE,
|
||||
WidgetTimerE
|
||||
WidgetTimerE,
|
||||
WGT_TYPE_BUILTIN_COUNT // must be last built-in entry
|
||||
} WidgetTypeE;
|
||||
|
||||
// Dynamic widget type IDs start here. External DXEs register new
|
||||
// widget types via wgtRegisterClass() and receive IDs >= this value.
|
||||
#define WGT_TYPE_DYNAMIC_BASE 32
|
||||
#define WGT_MAX_TYPES 64
|
||||
|
||||
// ============================================================
|
||||
// ListView types
|
||||
// ============================================================
|
||||
|
|
@ -272,6 +278,41 @@ typedef struct {
|
|||
void (*onHeaderClick)(struct WidgetT *w, int32_t col, ListViewSortE dir);
|
||||
} ListViewDataT;
|
||||
|
||||
// ============================================================
|
||||
// Widget class vtable
|
||||
// ============================================================
|
||||
//
|
||||
// Each widget type has a WidgetClassT that defines its behavior.
|
||||
// Built-in classes are static const; dynamically registered classes
|
||||
// (from DXE plugins) are stored in the mutable region of
|
||||
// widgetClassTable[].
|
||||
//
|
||||
// Flags encode static properties checked by the framework:
|
||||
// FOCUSABLE -- can receive keyboard focus (Tab navigation)
|
||||
// BOX_CONTAINER -- uses the generic VBox/HBox layout algorithm
|
||||
// HORIZ_CONTAINER -- lays out children horizontally (vs. vertical)
|
||||
// PAINTS_CHILDREN -- widget handles child rendering itself
|
||||
// NO_HIT_RECURSE -- hit testing stops here, no child recursion
|
||||
|
||||
#define WCLASS_FOCUSABLE 0x0001
|
||||
#define WCLASS_BOX_CONTAINER 0x0002
|
||||
#define WCLASS_HORIZ_CONTAINER 0x0004
|
||||
#define WCLASS_PAINTS_CHILDREN 0x0008
|
||||
#define WCLASS_NO_HIT_RECURSE 0x0010
|
||||
|
||||
typedef struct WidgetClassT {
|
||||
uint32_t flags;
|
||||
void (*paint)(struct WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors);
|
||||
void (*paintOverlay)(struct WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors);
|
||||
void (*calcMinSize)(struct WidgetT *w, const BitmapFontT *font);
|
||||
void (*layout)(struct WidgetT *w, const BitmapFontT *font);
|
||||
void (*onMouse)(struct WidgetT *w, struct WidgetT *root, int32_t vx, int32_t vy);
|
||||
void (*onKey)(struct WidgetT *w, int32_t key, int32_t mod);
|
||||
void (*destroy)(struct WidgetT *w);
|
||||
const char *(*getText)(const struct WidgetT *w);
|
||||
void (*setText)(struct WidgetT *w, const char *text);
|
||||
} WidgetClassT;
|
||||
|
||||
// ============================================================
|
||||
// Widget structure
|
||||
// ============================================================
|
||||
|
|
@ -348,6 +389,7 @@ typedef struct WidgetT {
|
|||
// Type-specific handlers (e.g. button press animation, listbox
|
||||
// selection) run first, then these universal callbacks fire.
|
||||
void *userData;
|
||||
void *customData; // DXE widget plugin private data (NULL for built-in types)
|
||||
const char *tooltip; // tooltip text (NULL = none, caller owns string)
|
||||
MenuT *contextMenu; // right-click context menu (NULL = none, caller owns)
|
||||
void (*onClick)(struct WidgetT *w);
|
||||
|
|
@ -987,6 +1029,16 @@ void wgtSetTooltip(WidgetT *w, const char *text);
|
|||
// Draw borders around layout containers in ugly colors
|
||||
void wgtSetDebugLayout(struct AppContextT *ctx, bool enabled);
|
||||
|
||||
// ============================================================
|
||||
// Dynamic widget registration
|
||||
// ============================================================
|
||||
|
||||
// Register a new widget class at runtime. Returns the assigned type ID
|
||||
// (>= WGT_TYPE_DYNAMIC_BASE) on success, or -1 if the table is full.
|
||||
// The WidgetClassT must remain valid for the lifetime of the process
|
||||
// (typically a static const in the registering DXE).
|
||||
int32_t wgtRegisterClass(const WidgetClassT *wclass);
|
||||
|
||||
// ============================================================
|
||||
// Layout (called internally; available for manual trigger)
|
||||
// ============================================================
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
#ifndef DVX_PLATFORM_H
|
||||
#define DVX_PLATFORM_H
|
||||
|
||||
#include "../dvxTypes.h"
|
||||
#include "dvxTypes.h"
|
||||
|
||||
// ============================================================
|
||||
// Keyboard event
|
||||
|
|
@ -231,4 +231,14 @@ const char *platformLineEnding(void);
|
|||
// On DOS this removes '\r' from CR+LF pairs. Returns the new length.
|
||||
int32_t platformStripLineEndings(char *buf, int32_t len);
|
||||
|
||||
// ============================================================
|
||||
// DXE module support
|
||||
// ============================================================
|
||||
|
||||
// Register platform and C runtime symbols with the dynamic module
|
||||
// loader so that DXE modules can resolve them at load time. On DOS
|
||||
// this calls dlregsym() with the full DJGPP libc/libm/libgcc/platform
|
||||
// export table. On platforms without DXE (Linux/SDL), this is a no-op.
|
||||
void platformRegisterDxeExports(void);
|
||||
|
||||
#endif // DVX_PLATFORM_H
|
||||
|
|
@ -27,17 +27,24 @@
|
|||
// of installing a real-mode callback for mouse events.
|
||||
|
||||
#include "dvxPlatform.h"
|
||||
#include "../dvxPalette.h"
|
||||
#include "dvxPalette.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dir.h>
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <sys/dxe.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// DJGPP-specific headers -- this is the ONLY file that includes these
|
||||
|
|
@ -1917,3 +1924,276 @@ static int32_t setVesaMode(uint16_t mode) {
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE3 export table
|
||||
// ============================================================
|
||||
//
|
||||
// DJGPP's DXE3 modules have no implicit access to the host's symbol
|
||||
// table. Every function or variable a DXE needs must be explicitly
|
||||
// listed here and registered via dlregsym() before any dlopen().
|
||||
//
|
||||
// Three categories:
|
||||
// 1. Platform functions (all platform*() from this file)
|
||||
// 2. C runtime (libc, libm, libgcc, DJGPP internals)
|
||||
// 3. DXE3 runtime (dlopen/dlsym/dlclose/dlerror/dlregsym)
|
||||
|
||||
// DJGPP stdio internals
|
||||
extern FILE __dj_stdin;
|
||||
extern FILE __dj_stdout;
|
||||
extern FILE __dj_stderr;
|
||||
|
||||
// libgcc 64-bit integer math helpers
|
||||
extern long long __divdi3(long long, long long);
|
||||
extern long long __moddi3(long long, long long);
|
||||
extern long long __muldi3(long long, long long);
|
||||
extern unsigned long long __udivdi3(unsigned long long, unsigned long long);
|
||||
extern unsigned long long __udivmoddi4(unsigned long long, unsigned long long, unsigned long long *);
|
||||
extern unsigned long long __umoddi3(unsigned long long, unsigned long long);
|
||||
|
||||
// DJGPP runtime internals
|
||||
extern void *__djgpp_exception_state_ptr;
|
||||
extern void __dj_assert(const char *, const char *, int);
|
||||
extern unsigned short __dj_ctype_flags[];
|
||||
extern unsigned char __dj_ctype_tolower[];
|
||||
|
||||
// GCC emulated thread-local storage
|
||||
extern void *__emutls_get_address(void *);
|
||||
|
||||
DXE_EXPORT_TABLE(sDxeExportTable)
|
||||
|
||||
// --- platform ---
|
||||
DXE_EXPORT(platformAltScanToChar)
|
||||
DXE_EXPORT(platformChdir)
|
||||
DXE_EXPORT(platformFlushRect)
|
||||
DXE_EXPORT(platformGetMemoryInfo)
|
||||
DXE_EXPORT(platformGetSystemInfo)
|
||||
DXE_EXPORT(platformInit)
|
||||
DXE_EXPORT(platformKeyboardGetModifiers)
|
||||
DXE_EXPORT(platformKeyboardRead)
|
||||
DXE_EXPORT(platformLineEnding)
|
||||
DXE_EXPORT(platformMkdirRecursive)
|
||||
DXE_EXPORT(platformMouseInit)
|
||||
DXE_EXPORT(platformMousePoll)
|
||||
DXE_EXPORT(platformMouseSetAccel)
|
||||
DXE_EXPORT(platformMouseWarp)
|
||||
DXE_EXPORT(platformMouseWheelInit)
|
||||
DXE_EXPORT(platformMouseWheelPoll)
|
||||
DXE_EXPORT(platformPathDirEnd)
|
||||
DXE_EXPORT(platformRegisterDxeExports)
|
||||
DXE_EXPORT(platformSpanCopy8)
|
||||
DXE_EXPORT(platformSpanCopy16)
|
||||
DXE_EXPORT(platformSpanCopy32)
|
||||
DXE_EXPORT(platformSpanFill8)
|
||||
DXE_EXPORT(platformSpanFill16)
|
||||
DXE_EXPORT(platformSpanFill32)
|
||||
DXE_EXPORT(platformStripLineEndings)
|
||||
DXE_EXPORT(platformValidateFilename)
|
||||
DXE_EXPORT(platformVideoEnumModes)
|
||||
DXE_EXPORT(platformVideoFreeBuffers)
|
||||
DXE_EXPORT(platformVideoInit)
|
||||
DXE_EXPORT(platformVideoSetPalette)
|
||||
DXE_EXPORT(platformVideoShutdown)
|
||||
DXE_EXPORT(platformYield)
|
||||
|
||||
// --- memory ---
|
||||
DXE_EXPORT(calloc)
|
||||
DXE_EXPORT(free)
|
||||
DXE_EXPORT(malloc)
|
||||
DXE_EXPORT(realloc)
|
||||
|
||||
// --- string / memory ops ---
|
||||
DXE_EXPORT(memchr)
|
||||
DXE_EXPORT(memcmp)
|
||||
DXE_EXPORT(memcpy)
|
||||
DXE_EXPORT(memmove)
|
||||
DXE_EXPORT(memset)
|
||||
DXE_EXPORT(strcasecmp)
|
||||
DXE_EXPORT(strcat)
|
||||
DXE_EXPORT(strchr)
|
||||
DXE_EXPORT(strcmp)
|
||||
DXE_EXPORT(strcpy)
|
||||
DXE_EXPORT(strcspn)
|
||||
DXE_EXPORT(strdup)
|
||||
DXE_EXPORT(strerror)
|
||||
DXE_EXPORT(stricmp)
|
||||
DXE_EXPORT(strlen)
|
||||
DXE_EXPORT(strncasecmp)
|
||||
DXE_EXPORT(strncat)
|
||||
DXE_EXPORT(strncmp)
|
||||
DXE_EXPORT(strncpy)
|
||||
DXE_EXPORT(strpbrk)
|
||||
DXE_EXPORT(strrchr)
|
||||
DXE_EXPORT(strspn)
|
||||
DXE_EXPORT(strstr)
|
||||
DXE_EXPORT(strtok)
|
||||
|
||||
// --- ctype ---
|
||||
DXE_EXPORT(isalnum)
|
||||
DXE_EXPORT(isalpha)
|
||||
DXE_EXPORT(isdigit)
|
||||
DXE_EXPORT(islower)
|
||||
DXE_EXPORT(isprint)
|
||||
DXE_EXPORT(ispunct)
|
||||
DXE_EXPORT(isspace)
|
||||
DXE_EXPORT(isupper)
|
||||
DXE_EXPORT(isxdigit)
|
||||
DXE_EXPORT(tolower)
|
||||
DXE_EXPORT(toupper)
|
||||
|
||||
// --- conversion ---
|
||||
DXE_EXPORT(abs)
|
||||
DXE_EXPORT(atof)
|
||||
DXE_EXPORT(atoi)
|
||||
DXE_EXPORT(atol)
|
||||
DXE_EXPORT(labs)
|
||||
DXE_EXPORT(strtod)
|
||||
DXE_EXPORT(strtol)
|
||||
DXE_EXPORT(strtoul)
|
||||
|
||||
// --- formatted I/O ---
|
||||
DXE_EXPORT(fprintf)
|
||||
DXE_EXPORT(fputs)
|
||||
DXE_EXPORT(fscanf)
|
||||
DXE_EXPORT(printf)
|
||||
DXE_EXPORT(puts)
|
||||
DXE_EXPORT(snprintf)
|
||||
DXE_EXPORT(sprintf)
|
||||
DXE_EXPORT(sscanf)
|
||||
DXE_EXPORT(vfprintf)
|
||||
DXE_EXPORT(vprintf)
|
||||
DXE_EXPORT(vsnprintf)
|
||||
DXE_EXPORT(vsprintf)
|
||||
|
||||
// --- character I/O ---
|
||||
DXE_EXPORT(fgetc)
|
||||
DXE_EXPORT(fgets)
|
||||
DXE_EXPORT(fputc)
|
||||
DXE_EXPORT(getc)
|
||||
DXE_EXPORT(putc)
|
||||
DXE_EXPORT(putchar)
|
||||
DXE_EXPORT(ungetc)
|
||||
|
||||
// --- file I/O ---
|
||||
DXE_EXPORT(fclose)
|
||||
DXE_EXPORT(feof)
|
||||
DXE_EXPORT(ferror)
|
||||
DXE_EXPORT(fflush)
|
||||
DXE_EXPORT(fopen)
|
||||
DXE_EXPORT(fread)
|
||||
DXE_EXPORT(freopen)
|
||||
DXE_EXPORT(fseek)
|
||||
DXE_EXPORT(ftell)
|
||||
DXE_EXPORT(fwrite)
|
||||
DXE_EXPORT(remove)
|
||||
DXE_EXPORT(rename)
|
||||
DXE_EXPORT(rewind)
|
||||
DXE_EXPORT(tmpfile)
|
||||
DXE_EXPORT(tmpnam)
|
||||
|
||||
// --- directory ---
|
||||
DXE_EXPORT(closedir)
|
||||
DXE_EXPORT(mkdir)
|
||||
DXE_EXPORT(opendir)
|
||||
DXE_EXPORT(readdir)
|
||||
DXE_EXPORT(rmdir)
|
||||
|
||||
// --- filesystem ---
|
||||
DXE_EXPORT(access)
|
||||
DXE_EXPORT(chdir)
|
||||
DXE_EXPORT(getcwd)
|
||||
DXE_EXPORT(realpath)
|
||||
DXE_EXPORT(stat)
|
||||
DXE_EXPORT(unlink)
|
||||
|
||||
// --- time ---
|
||||
DXE_EXPORT(clock)
|
||||
DXE_EXPORT(difftime)
|
||||
DXE_EXPORT(gmtime)
|
||||
DXE_EXPORT(localtime)
|
||||
DXE_EXPORT(mktime)
|
||||
DXE_EXPORT(strftime)
|
||||
DXE_EXPORT(time)
|
||||
|
||||
// --- process / environment ---
|
||||
DXE_EXPORT(abort)
|
||||
DXE_EXPORT(atexit)
|
||||
DXE_EXPORT(exit)
|
||||
DXE_EXPORT(getenv)
|
||||
DXE_EXPORT(system)
|
||||
|
||||
// --- sorting / searching ---
|
||||
DXE_EXPORT(bsearch)
|
||||
DXE_EXPORT(qsort)
|
||||
|
||||
// --- random ---
|
||||
DXE_EXPORT(rand)
|
||||
DXE_EXPORT(srand)
|
||||
|
||||
// --- setjmp / signal ---
|
||||
DXE_EXPORT(longjmp)
|
||||
DXE_EXPORT(setjmp)
|
||||
DXE_EXPORT(signal)
|
||||
|
||||
// --- libm ---
|
||||
DXE_EXPORT(acos)
|
||||
DXE_EXPORT(asin)
|
||||
DXE_EXPORT(atan)
|
||||
DXE_EXPORT(atan2)
|
||||
DXE_EXPORT(ceil)
|
||||
DXE_EXPORT(cos)
|
||||
DXE_EXPORT(exp)
|
||||
DXE_EXPORT(fabs)
|
||||
DXE_EXPORT(floor)
|
||||
DXE_EXPORT(fmod)
|
||||
DXE_EXPORT(frexp)
|
||||
DXE_EXPORT(ldexp)
|
||||
DXE_EXPORT(log)
|
||||
DXE_EXPORT(log10)
|
||||
DXE_EXPORT(modf)
|
||||
DXE_EXPORT(pow)
|
||||
DXE_EXPORT(sin)
|
||||
DXE_EXPORT(sqrt)
|
||||
DXE_EXPORT(tan)
|
||||
|
||||
// --- errno ---
|
||||
DXE_EXPORT(errno)
|
||||
|
||||
// --- libgcc 64-bit integer math ---
|
||||
DXE_EXPORT(__divdi3)
|
||||
DXE_EXPORT(__moddi3)
|
||||
DXE_EXPORT(__muldi3)
|
||||
DXE_EXPORT(__udivdi3)
|
||||
DXE_EXPORT(__udivmoddi4)
|
||||
DXE_EXPORT(__umoddi3)
|
||||
|
||||
// --- DJGPP internals ---
|
||||
DXE_EXPORT(__dj_stdin)
|
||||
DXE_EXPORT(__dj_stdout)
|
||||
DXE_EXPORT(__dj_stderr)
|
||||
DXE_EXPORT(__dj_assert)
|
||||
DXE_EXPORT(__dj_ctype_flags)
|
||||
DXE_EXPORT(__dj_ctype_tolower)
|
||||
DXE_EXPORT(__djgpp_exception_state_ptr)
|
||||
|
||||
// --- GCC internals ---
|
||||
DXE_EXPORT(__emutls_get_address)
|
||||
|
||||
// --- DXE3 runtime ---
|
||||
DXE_EXPORT(dlopen)
|
||||
DXE_EXPORT(dlsym)
|
||||
DXE_EXPORT(dlclose)
|
||||
DXE_EXPORT(dlerror)
|
||||
DXE_EXPORT(dlregsym)
|
||||
|
||||
DXE_EXPORT_END
|
||||
|
||||
|
||||
// ============================================================
|
||||
// platformRegisterDxeExports
|
||||
// ============================================================
|
||||
|
||||
void platformRegisterDxeExports(void) {
|
||||
dlregsym(sDxeExportTable);
|
||||
}
|
||||
49
core/widgetClass.c
Normal file
49
core/widgetClass.c
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// widgetClass.c -- Widget class table and dynamic registration
|
||||
//
|
||||
// The widgetClassTable[] is the central dispatch table for the widget
|
||||
// system. All entries start NULL and are filled at runtime by widget
|
||||
// DXE registration functions (wgtBoxRegister, wgtButtonRegister, etc.).
|
||||
// The loader loads each widget DXE and calls its register function
|
||||
// before starting the shell.
|
||||
//
|
||||
// This file contains only the table and the dynamic registration API.
|
||||
// All class definitions live in their respective widget .c files.
|
||||
|
||||
#include "widgetInternal.h"
|
||||
|
||||
// ============================================================
|
||||
// Class table -- indexed by WidgetTypeE
|
||||
// ============================================================
|
||||
//
|
||||
// Every entry is NULL until the corresponding widget DXE calls its
|
||||
// registration function (e.g. wgtBoxRegister sets VBox/HBox/Frame).
|
||||
// Sized WGT_MAX_TYPES to accommodate both built-in widget types
|
||||
// (0..WGT_TYPE_BUILTIN_COUNT-1) and dynamically registered types
|
||||
// (WGT_TYPE_DYNAMIC_BASE..WGT_MAX_TYPES-1).
|
||||
|
||||
const WidgetClassT *widgetClassTable[WGT_MAX_TYPES];
|
||||
|
||||
|
||||
// ============================================================
|
||||
// wgtRegisterClass -- register a dynamic widget type at runtime
|
||||
// ============================================================
|
||||
//
|
||||
// External DXE plugins call this to add new widget types without
|
||||
// modifying the core library. Returns the assigned type ID
|
||||
// (>= WGT_TYPE_DYNAMIC_BASE) so the caller can use it with
|
||||
// widgetAlloc(). Returns -1 if the table is full.
|
||||
|
||||
int32_t wgtRegisterClass(const WidgetClassT *wclass) {
|
||||
if (!wclass) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int32_t i = WGT_TYPE_DYNAMIC_BASE; i < WGT_MAX_TYPES; i++) {
|
||||
if (!widgetClassTable[i]) {
|
||||
widgetClassTable[i] = wclass;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
1805
core/widgetCore.c
Normal file
1805
core/widgetCore.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -258,7 +258,9 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
|
|||
|
||||
// Handle canvas drawing (mouse move while pressed)
|
||||
if (sDrawingCanvas && (buttons & MOUSE_LEFT)) {
|
||||
widgetCanvasOnMouse(sDrawingCanvas, root, x, y);
|
||||
if (sDrawingCanvas->wclass && sDrawingCanvas->wclass->onMouse) {
|
||||
sDrawingCanvas->wclass->onMouse(sDrawingCanvas, root, x, y);
|
||||
}
|
||||
wgtInvalidatePaint(root);
|
||||
return;
|
||||
}
|
||||
|
|
@ -387,7 +389,9 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
|
|||
pos = y - sDragSplitter->y - sDragSplitStart;
|
||||
}
|
||||
|
||||
widgetSplitterClampPos(sDragSplitter, &pos);
|
||||
if (sSplitterClampPosFn) {
|
||||
sSplitterClampPosFn(sDragSplitter, &pos);
|
||||
}
|
||||
|
||||
if (pos != sDragSplitter->as.splitter.dividerPos) {
|
||||
sDragSplitter->as.splitter.dividerPos = pos;
|
||||
|
|
@ -1088,7 +1092,7 @@ void widgetReorderUpdate(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
|
|||
WidgetT *target = NULL;
|
||||
bool after = false;
|
||||
|
||||
for (WidgetT *v = first; v; v = widgetTreeViewNextVisible(v, w)) {
|
||||
for (WidgetT *v = first; v; v = sTreeViewNextVisibleFn ? sTreeViewNextVisibleFn(v, w) : NULL) {
|
||||
int32_t itemBot = curY + font->charHeight;
|
||||
int32_t mid = curY + font->charHeight / 2;
|
||||
|
||||
|
|
@ -1101,7 +1105,7 @@ void widgetReorderUpdate(WidgetT *w, WidgetT *root, int32_t x, int32_t y) {
|
|||
curY = itemBot;
|
||||
|
||||
// Check if mouse is between this item and next
|
||||
WidgetT *next = widgetTreeViewNextVisible(v, w);
|
||||
WidgetT *next = sTreeViewNextVisibleFn ? sTreeViewNextVisibleFn(v, w) : NULL;
|
||||
|
||||
if (!next || y < itemBot) {
|
||||
target = v;
|
||||
|
|
@ -14,61 +14,28 @@
|
|||
#ifndef WIDGET_INTERNAL_H
|
||||
#define WIDGET_INTERNAL_H
|
||||
|
||||
#include "../dvxWidget.h"
|
||||
#include "../dvxApp.h"
|
||||
#include "../dvxDraw.h"
|
||||
#include "../dvxWm.h"
|
||||
#include "../dvxVideo.h"
|
||||
#include "dvxWidget.h"
|
||||
#include "dvxApp.h"
|
||||
#include "dvxDraw.h"
|
||||
#include "dvxWm.h"
|
||||
#include "dvxVideo.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// ============================================================
|
||||
// Widget class vtable
|
||||
// Widget class table
|
||||
// ============================================================
|
||||
//
|
||||
// Each widget type has a WidgetClassT entry in widgetClassTable[], indexed
|
||||
// by WidgetTypeE. This is a C implementation of virtual dispatch: instead
|
||||
// of a switch(type) in every operation, the code calls w->wclass->paint()
|
||||
// etc. The vtable pointer is set once at widget creation and never changes.
|
||||
// WidgetClassT and WCLASS_* flags are now in dvxWidget.h (public API)
|
||||
// so that DXE plugins can register new widget types.
|
||||
//
|
||||
// Flags encode static properties that the framework needs without calling
|
||||
// into the vtable:
|
||||
// FOCUSABLE -- can receive keyboard focus (Tab navigation)
|
||||
// BOX_CONTAINER -- uses the generic VBox/HBox layout algorithm
|
||||
// HORIZ_CONTAINER -- lays out children horizontally (vs. default vertical)
|
||||
// PAINTS_CHILDREN -- the widget's paint function handles child rendering
|
||||
// (e.g. TabControl only paints the active tab page)
|
||||
// NO_HIT_RECURSE -- hit testing stops at this widget and doesn't recurse
|
||||
// into children (e.g. ListBox handles its own items)
|
||||
//
|
||||
// NULL function pointers are valid and mean "no-op" for that operation.
|
||||
// paintOverlay is used by widgets that need to draw outside their bounds
|
||||
// (dropdown popup lists), rendered in a separate pass after all widgets.
|
||||
|
||||
#define WCLASS_FOCUSABLE 0x0001
|
||||
#define WCLASS_BOX_CONTAINER 0x0002
|
||||
#define WCLASS_HORIZ_CONTAINER 0x0004
|
||||
#define WCLASS_PAINTS_CHILDREN 0x0008
|
||||
#define WCLASS_NO_HIT_RECURSE 0x0010
|
||||
|
||||
typedef struct WidgetClassT {
|
||||
uint32_t flags;
|
||||
void (*paint)(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors);
|
||||
void (*paintOverlay)(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors);
|
||||
void (*calcMinSize)(WidgetT *w, const BitmapFontT *font);
|
||||
void (*layout)(WidgetT *w, const BitmapFontT *font);
|
||||
void (*onMouse)(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy);
|
||||
void (*onKey)(WidgetT *w, int32_t key, int32_t mod);
|
||||
void (*destroy)(WidgetT *w);
|
||||
const char *(*getText)(const WidgetT *w);
|
||||
void (*setText)(WidgetT *w, const char *text);
|
||||
} WidgetClassT;
|
||||
|
||||
// Global vtable array -- one entry per WidgetTypeE value. Defined in
|
||||
// widgetCore.c. Must stay in sync with the WidgetTypeE enum order.
|
||||
extern const WidgetClassT *widgetClassTable[];
|
||||
// Global vtable array -- sized WGT_MAX_TYPES. Built-in entries
|
||||
// (0..WGT_TYPE_BUILTIN_COUNT-1) are initialized at startup. Dynamic
|
||||
// entries (WGT_TYPE_DYNAMIC_BASE..WGT_MAX_TYPES-1) are filled by
|
||||
// wgtRegisterClass(). Defined in widgetClass.c.
|
||||
extern const WidgetClassT *widgetClassTable[WGT_MAX_TYPES];
|
||||
|
||||
// ============================================================
|
||||
// Validation macros
|
||||
|
|
@ -194,6 +161,15 @@ extern WidgetT *sDragSplitter; // splitter being dragged
|
|||
extern int32_t sDragSplitStart; // mouse position at start of splitter drag
|
||||
extern WidgetT *sDragReorder; // listbox/treeview item being drag-reordered
|
||||
extern WidgetT *sDragScrollbar; // widget whose scrollbar thumb is being dragged
|
||||
// Hook for cursor shape change over ListView column borders.
|
||||
// Set by wgtListViewRegister(); NULL if ListView DXE not loaded.
|
||||
extern bool (*sListViewColBorderHitFn)(const WidgetT *w, int32_t vx, int32_t vy);
|
||||
// Hook for splitter divider clamping during drag.
|
||||
// Set by wgtSplitterRegister(); NULL if Splitter DXE not loaded.
|
||||
extern void (*sSplitterClampPosFn)(WidgetT *w, int32_t *pos);
|
||||
// Hook for TreeView visible item iteration during drag-reorder.
|
||||
// Set by wgtTreeViewRegister(); NULL if TreeView DXE not loaded.
|
||||
extern WidgetT *(*sTreeViewNextVisibleFn)(WidgetT *item, WidgetT *treeView);
|
||||
extern int32_t sDragScrollbarOff; // mouse offset within thumb at drag start
|
||||
extern int32_t sDragScrollbarOrient; // 0=vertical, 1=horizontal
|
||||
|
||||
140
dvx/Makefile
140
dvx/Makefile
|
|
@ -1,140 +0,0 @@
|
|||
# DVX GUI Library Makefile for DJGPP cross-compilation
|
||||
|
||||
DJGPP_PREFIX = $(HOME)/djgpp/djgpp
|
||||
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
|
||||
DJGPP_LIBPATH = $(HOME)/claude/windriver/tools/lib
|
||||
AR = LD_LIBRARY_PATH=$(DJGPP_LIBPATH) $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-ar
|
||||
RANLIB = LD_LIBRARY_PATH=$(DJGPP_LIBPATH) $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-ranlib
|
||||
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../tasks
|
||||
|
||||
OBJDIR = ../obj/dvx
|
||||
WOBJDIR = ../obj/dvx/widgets
|
||||
POBJDIR = ../obj/dvx/platform
|
||||
LIBDIR = ../lib
|
||||
|
||||
SRCS = dvxVideo.c dvxDraw.c dvxComp.c dvxWm.c dvxIcon.c dvxImageWrite.c dvxApp.c dvxDialog.c dvxPrefs.c
|
||||
|
||||
PSRCS = platform/dvxPlatformDos.c
|
||||
|
||||
|
||||
WSRCS = widgets/widgetAnsiTerm.c \
|
||||
widgets/widgetClass.c \
|
||||
widgets/widgetCore.c \
|
||||
widgets/widgetScrollbar.c \
|
||||
widgets/widgetLayout.c \
|
||||
widgets/widgetEvent.c \
|
||||
widgets/widgetOps.c \
|
||||
widgets/widgetBox.c \
|
||||
widgets/widgetButton.c \
|
||||
widgets/widgetCheckbox.c \
|
||||
widgets/widgetComboBox.c \
|
||||
widgets/widgetDropdown.c \
|
||||
widgets/widgetCanvas.c \
|
||||
widgets/widgetImage.c \
|
||||
widgets/widgetImageButton.c \
|
||||
widgets/widgetLabel.c \
|
||||
widgets/widgetListBox.c \
|
||||
widgets/widgetListView.c \
|
||||
widgets/widgetProgressBar.c \
|
||||
widgets/widgetRadio.c \
|
||||
widgets/widgetScrollPane.c \
|
||||
widgets/widgetSeparator.c \
|
||||
widgets/widgetSplitter.c \
|
||||
widgets/widgetSlider.c \
|
||||
widgets/widgetSpacer.c \
|
||||
widgets/widgetSpinner.c \
|
||||
widgets/widgetStatusBar.c \
|
||||
widgets/widgetTabControl.c \
|
||||
widgets/widgetTextInput.c \
|
||||
widgets/widgetTimer.c \
|
||||
widgets/widgetToolbar.c \
|
||||
widgets/widgetTreeView.c
|
||||
|
||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||
POBJS = $(patsubst platform/%.c,$(POBJDIR)/%.o,$(PSRCS))
|
||||
WOBJS = $(patsubst widgets/%.c,$(WOBJDIR)/%.o,$(WSRCS))
|
||||
TARGET = $(LIBDIR)/libdvx.a
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS) $(POBJS) $(WOBJS) | $(LIBDIR)
|
||||
$(AR) rcs $@ $(OBJS) $(POBJS) $(WOBJS)
|
||||
$(RANLIB) $@
|
||||
|
||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(POBJDIR)/%.o: platform/%.c | $(POBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(WOBJDIR)/%.o: widgets/%.c | $(WOBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR):
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
$(POBJDIR):
|
||||
mkdir -p $(POBJDIR)
|
||||
|
||||
$(WOBJDIR):
|
||||
mkdir -p $(WOBJDIR)
|
||||
|
||||
$(LIBDIR):
|
||||
mkdir -p $(LIBDIR)
|
||||
|
||||
# Dependencies
|
||||
$(OBJDIR)/dvxVideo.o: dvxVideo.c dvxVideo.h platform/dvxPlatform.h dvxTypes.h dvxPalette.h
|
||||
$(OBJDIR)/dvxDraw.o: dvxDraw.c dvxDraw.h platform/dvxPlatform.h dvxTypes.h
|
||||
$(OBJDIR)/dvxComp.o: dvxComp.c dvxComp.h platform/dvxPlatform.h dvxTypes.h
|
||||
$(OBJDIR)/dvxWm.o: dvxWm.c dvxWm.h dvxTypes.h dvxDraw.h dvxComp.h dvxVideo.h dvxWidget.h thirdparty/stb_image.h
|
||||
$(OBJDIR)/dvxIcon.o: dvxIcon.c thirdparty/stb_image.h
|
||||
$(OBJDIR)/dvxImageWrite.o: dvxImageWrite.c thirdparty/stb_image_write.h
|
||||
$(OBJDIR)/dvxApp.o: dvxApp.c dvxApp.h platform/dvxPlatform.h dvxTypes.h dvxVideo.h dvxDraw.h dvxComp.h dvxWm.h dvxFont.h dvxCursor.h
|
||||
$(OBJDIR)/dvxDialog.o: dvxDialog.c dvxDialog.h platform/dvxPlatform.h dvxApp.h dvxWidget.h widgets/widgetInternal.h dvxTypes.h dvxDraw.h
|
||||
|
||||
$(OBJDIR)/dvxPrefs.o: dvxPrefs.c dvxPrefs.h
|
||||
|
||||
# Platform file dependencies
|
||||
$(POBJDIR)/dvxPlatformDos.o: platform/dvxPlatformDos.c platform/dvxPlatform.h dvxTypes.h dvxPalette.h
|
||||
|
||||
# Thirdparty file dependencies
|
||||
|
||||
# Widget file dependencies
|
||||
WIDGET_DEPS = widgets/widgetInternal.h dvxWidget.h dvxTypes.h dvxApp.h dvxDraw.h dvxWm.h dvxVideo.h
|
||||
$(WOBJDIR)/widgetClass.o: widgets/widgetClass.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetAnsiTerm.o: widgets/widgetAnsiTerm.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetCore.o: widgets/widgetCore.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetLayout.o: widgets/widgetLayout.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetEvent.o: widgets/widgetEvent.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetOps.o: widgets/widgetOps.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetBox.o: widgets/widgetBox.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetCanvas.o: widgets/widgetCanvas.c $(WIDGET_DEPS) thirdparty/stb_image.h thirdparty/stb_image_write.h
|
||||
$(WOBJDIR)/widgetButton.o: widgets/widgetButton.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetCheckbox.o: widgets/widgetCheckbox.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetComboBox.o: widgets/widgetComboBox.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetDropdown.o: widgets/widgetDropdown.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetImage.o: widgets/widgetImage.c $(WIDGET_DEPS) thirdparty/stb_image.h
|
||||
$(WOBJDIR)/widgetImageButton.o: widgets/widgetImageButton.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetLabel.o: widgets/widgetLabel.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetListBox.o: widgets/widgetListBox.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetListView.o: widgets/widgetListView.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetProgressBar.o: widgets/widgetProgressBar.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetRadio.o: widgets/widgetRadio.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetScrollPane.o: widgets/widgetScrollPane.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetSeparator.o: widgets/widgetSeparator.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetSplitter.o: widgets/widgetSplitter.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetSlider.o: widgets/widgetSlider.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetSpacer.o: widgets/widgetSpacer.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetSpinner.o: widgets/widgetSpinner.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetStatusBar.o: widgets/widgetStatusBar.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetTabControl.o: widgets/widgetTabControl.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetTextInput.o: widgets/widgetTextInput.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetTimer.o: widgets/widgetTimer.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetToolbar.o: widgets/widgetToolbar.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetScrollbar.o: widgets/widgetScrollbar.c $(WIDGET_DEPS)
|
||||
$(WOBJDIR)/widgetTreeView.o: widgets/widgetTreeView.c $(WIDGET_DEPS)
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(POBJS) $(WOBJS) $(TARGET)
|
||||
|
|
@ -1,536 +0,0 @@
|
|||
// widgetClass.c -- Widget class vtable definitions
|
||||
//
|
||||
// This file implements a C vtable pattern for the widget type system.
|
||||
// Each widget type has a static const WidgetClassT struct that defines
|
||||
// its behavior -- paint, layout, mouse handling, keyboard handling, etc.
|
||||
// A master table (widgetClassTable[]) maps WidgetTypeE enum values to
|
||||
// their class definitions, enabling O(1) dispatch.
|
||||
//
|
||||
// Why a vtable approach instead of switch statements:
|
||||
// 1. Adding a new widget type is purely additive -- define a new class
|
||||
// struct and add one entry to the table. No existing switch
|
||||
// statements need modification, reducing the risk of forgetting
|
||||
// a case.
|
||||
// 2. NULL function pointers indicate "no behavior" naturally. A box
|
||||
// container has no paint function because the framework paints
|
||||
// its children directly. A label has no onKey handler because
|
||||
// it's not interactive. This is cleaner than empty case blocks.
|
||||
// 3. The flags field combines multiple boolean properties into a
|
||||
// single bitmask, avoiding per-type if-chains in hot paths
|
||||
// like hit testing and layout.
|
||||
//
|
||||
// Class flags:
|
||||
// WCLASS_FOCUSABLE -- widget can receive keyboard focus (Tab order)
|
||||
// WCLASS_BOX_CONTAINER -- uses the generic box layout engine (VBox/HBox)
|
||||
// WCLASS_HORIZ_CONTAINER -- lays out children horizontally (HBox variant)
|
||||
// WCLASS_PAINTS_CHILDREN -- widget handles its own child painting (TabControl,
|
||||
// TreeView, ScrollPane, Splitter). The default paint
|
||||
// walker skips children for these widgets.
|
||||
// WCLASS_NO_HIT_RECURSE -- hit testing stops at this widget instead of
|
||||
// recursing into children. Used by widgets that
|
||||
// manage their own internal click regions (TreeView,
|
||||
// ScrollPane, ListView, Splitter).
|
||||
|
||||
#include "widgetInternal.h"
|
||||
|
||||
// ============================================================
|
||||
// Per-type class definitions
|
||||
// ============================================================
|
||||
//
|
||||
// Containers (VBox, HBox, RadioGroup, TabPage, StatusBar, Toolbar)
|
||||
// typically have NULL for most function pointers because their
|
||||
// behavior is handled generically by the box layout engine and
|
||||
// the default child-walking paint logic.
|
||||
//
|
||||
// Leaf widgets (Button, Label, TextInput, etc.) override paint,
|
||||
// calcMinSize, and usually onMouse/onKey for interactivity.
|
||||
|
||||
static const WidgetClassT sClassVBox = {
|
||||
.flags = WCLASS_BOX_CONTAINER,
|
||||
.paint = NULL,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = NULL,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassHBox = {
|
||||
.flags = WCLASS_BOX_CONTAINER | WCLASS_HORIZ_CONTAINER,
|
||||
.paint = NULL,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = NULL,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassLabel = {
|
||||
.flags = 0,
|
||||
.paint = widgetLabelPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetLabelCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = widgetLabelGetText,
|
||||
.setText = widgetLabelSetText
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassButton = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetButtonPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetButtonCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetButtonOnMouse,
|
||||
.onKey = widgetButtonOnKey,
|
||||
.destroy = NULL,
|
||||
.getText = widgetButtonGetText,
|
||||
.setText = widgetButtonSetText
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassCheckbox = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetCheckboxPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetCheckboxCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetCheckboxOnMouse,
|
||||
.onKey = widgetCheckboxOnKey,
|
||||
.destroy = NULL,
|
||||
.getText = widgetCheckboxGetText,
|
||||
.setText = widgetCheckboxSetText
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassRadioGroup = {
|
||||
.flags = WCLASS_BOX_CONTAINER,
|
||||
.paint = NULL,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = NULL,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassRadio = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetRadioPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetRadioCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetRadioOnMouse,
|
||||
.onKey = widgetRadioOnKey,
|
||||
.destroy = NULL,
|
||||
.getText = widgetRadioGetText,
|
||||
.setText = widgetRadioSetText
|
||||
};
|
||||
|
||||
// TextInput and TextArea have destroy functions because they dynamically
|
||||
// allocate their text buffer and undo buffer. Most other widgets store
|
||||
// all data inline in the WidgetT union and need no cleanup.
|
||||
static const WidgetClassT sClassTextInput = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetTextInputPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetTextInputCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetTextInputOnMouse,
|
||||
.onKey = widgetTextInputOnKey,
|
||||
.destroy = widgetTextInputDestroy,
|
||||
.getText = widgetTextInputGetText,
|
||||
.setText = widgetTextInputSetText
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassTextArea = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetTextAreaPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetTextAreaCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetTextAreaOnMouse,
|
||||
.onKey = widgetTextAreaOnKey,
|
||||
.destroy = widgetTextAreaDestroy,
|
||||
.getText = widgetTextAreaGetText,
|
||||
.setText = widgetTextAreaSetText
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassListBox = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetListBoxPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetListBoxCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetListBoxOnMouse,
|
||||
.onKey = widgetListBoxOnKey,
|
||||
.destroy = widgetListBoxDestroy,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassSpacer = {
|
||||
.flags = 0,
|
||||
.paint = NULL,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetSpacerCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassSeparator = {
|
||||
.flags = 0,
|
||||
.paint = widgetSeparatorPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetSeparatorCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassFrame = {
|
||||
.flags = WCLASS_BOX_CONTAINER,
|
||||
.paint = widgetFramePaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = NULL,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
// Dropdown and ComboBox use paintOverlay to draw their popup lists
|
||||
// on top of the entire widget tree. This is rendered in a separate
|
||||
// pass after the main paint, so the popup can overlap sibling widgets.
|
||||
static const WidgetClassT sClassDropdown = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetDropdownPaint,
|
||||
.paintOverlay = widgetDropdownPaintPopup,
|
||||
.calcMinSize = widgetDropdownCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetDropdownOnMouse,
|
||||
.onKey = widgetDropdownOnKey,
|
||||
.destroy = NULL,
|
||||
.getText = widgetDropdownGetText,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassComboBox = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetComboBoxPaint,
|
||||
.paintOverlay = widgetComboBoxPaintPopup,
|
||||
.calcMinSize = widgetComboBoxCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetComboBoxOnMouse,
|
||||
.onKey = widgetComboBoxOnKey,
|
||||
.destroy = widgetComboBoxDestroy,
|
||||
.getText = widgetComboBoxGetText,
|
||||
.setText = widgetComboBoxSetText
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassProgressBar = {
|
||||
.flags = 0,
|
||||
.paint = widgetProgressBarPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetProgressBarCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassSlider = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetSliderPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetSliderCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetSliderOnMouse,
|
||||
.onKey = widgetSliderOnKey,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
// TabControl has both PAINTS_CHILDREN and a custom layout function
|
||||
// because it needs to show only the active tab page's children and
|
||||
// position them inside the tab content area (below the tab strip).
|
||||
static const WidgetClassT sClassTabControl = {
|
||||
.flags = WCLASS_FOCUSABLE | WCLASS_PAINTS_CHILDREN,
|
||||
.paint = widgetTabControlPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetTabControlCalcMinSize,
|
||||
.layout = widgetTabControlLayout,
|
||||
.onMouse = widgetTabControlOnMouse,
|
||||
.onKey = widgetTabControlOnKey,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassTabPage = {
|
||||
.flags = WCLASS_BOX_CONTAINER,
|
||||
.paint = NULL,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = NULL,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassStatusBar = {
|
||||
.flags = WCLASS_BOX_CONTAINER | WCLASS_HORIZ_CONTAINER,
|
||||
.paint = widgetStatusBarPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = NULL,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassToolbar = {
|
||||
.flags = WCLASS_BOX_CONTAINER | WCLASS_HORIZ_CONTAINER,
|
||||
.paint = widgetToolbarPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = NULL,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
// TreeView uses all three special flags:
|
||||
// PAINTS_CHILDREN -- it renders tree items itself (with indentation,
|
||||
// expand/collapse buttons, and selection highlighting)
|
||||
// NO_HIT_RECURSE -- mouse clicks go to the TreeView widget, which
|
||||
// figures out which tree item was clicked based on scroll position
|
||||
// and item Y coordinates, rather than letting the hit tester
|
||||
// recurse into child TreeItem widgets
|
||||
static const WidgetClassT sClassTreeView = {
|
||||
.flags = WCLASS_FOCUSABLE | WCLASS_PAINTS_CHILDREN | WCLASS_NO_HIT_RECURSE,
|
||||
.paint = widgetTreeViewPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetTreeViewCalcMinSize,
|
||||
.layout = widgetTreeViewLayout,
|
||||
.onMouse = widgetTreeViewOnMouse,
|
||||
.onKey = widgetTreeViewOnKey,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
// TreeItem has no paint/mouse/key handlers -- it's a data-only node.
|
||||
// The TreeView parent widget handles all rendering and interaction.
|
||||
// TreeItem exists as a WidgetT so it can participate in the tree
|
||||
// structure (parent/child/sibling links) for hierarchical data.
|
||||
static const WidgetClassT sClassTreeItem = {
|
||||
.flags = 0,
|
||||
.paint = NULL,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = NULL,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = widgetTreeItemGetText,
|
||||
.setText = widgetTreeItemSetText
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassImage = {
|
||||
.flags = 0,
|
||||
.paint = widgetImagePaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetImageCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetImageOnMouse,
|
||||
.onKey = NULL,
|
||||
.destroy = widgetImageDestroy,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassImageButton = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetImageButtonPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetImageButtonCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetImageButtonOnMouse,
|
||||
.onKey = widgetImageButtonOnKey,
|
||||
.destroy = widgetImageButtonDestroy,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassCanvas = {
|
||||
.flags = 0,
|
||||
.paint = widgetCanvasPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetCanvasCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetCanvasOnMouse,
|
||||
.onKey = NULL,
|
||||
.destroy = widgetCanvasDestroy,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassListView = {
|
||||
.flags = WCLASS_FOCUSABLE | WCLASS_NO_HIT_RECURSE,
|
||||
.paint = widgetListViewPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetListViewCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetListViewOnMouse,
|
||||
.onKey = widgetListViewOnKey,
|
||||
.destroy = widgetListViewDestroy,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassAnsiTerm = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetAnsiTermPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetAnsiTermCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetAnsiTermOnMouse,
|
||||
.onKey = widgetAnsiTermOnKey,
|
||||
.destroy = widgetAnsiTermDestroy,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
// ScrollPane, Splitter: PAINTS_CHILDREN because they clip/position
|
||||
// children in a custom way; NO_HIT_RECURSE because they manage their
|
||||
// own scrollbar/divider hit regions.
|
||||
static const WidgetClassT sClassScrollPane = {
|
||||
.flags = WCLASS_PAINTS_CHILDREN | WCLASS_NO_HIT_RECURSE,
|
||||
.paint = widgetScrollPanePaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetScrollPaneCalcMinSize,
|
||||
.layout = widgetScrollPaneLayout,
|
||||
.onMouse = widgetScrollPaneOnMouse,
|
||||
.onKey = widgetScrollPaneOnKey,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassSplitter = {
|
||||
.flags = WCLASS_PAINTS_CHILDREN | WCLASS_NO_HIT_RECURSE,
|
||||
.paint = widgetSplitterPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetSplitterCalcMinSize,
|
||||
.layout = widgetSplitterLayout,
|
||||
.onMouse = widgetSplitterOnMouse,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassTimer = {
|
||||
.flags = 0,
|
||||
.paint = NULL,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetTimerCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = widgetTimerDestroy,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassSpinner = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetSpinnerPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetSpinnerCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetSpinnerOnMouse,
|
||||
.onKey = widgetSpinnerOnKey,
|
||||
.destroy = NULL,
|
||||
.getText = widgetSpinnerGetText,
|
||||
.setText = widgetSpinnerSetText
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// Class table -- indexed by WidgetTypeE
|
||||
// ============================================================
|
||||
//
|
||||
// This array is the central dispatch table for the widget system.
|
||||
// Indexed by the WidgetTypeE enum, it provides O(1) lookup of any
|
||||
// widget type's class definition. Every WidgetT stores a pointer
|
||||
// to its class (w->wclass) set at allocation time, so per-widget
|
||||
// dispatch doesn't even need to index this table -- it's a direct
|
||||
// pointer dereference through the vtable.
|
||||
//
|
||||
// Using C99 designated initializers ensures the array slots match
|
||||
// the enum values even if the enum is reordered. If a new enum
|
||||
// value is added without a table entry, it will be NULL, which
|
||||
// callers check for before dispatching.
|
||||
|
||||
const WidgetClassT *widgetClassTable[] = {
|
||||
[WidgetVBoxE] = &sClassVBox,
|
||||
[WidgetHBoxE] = &sClassHBox,
|
||||
[WidgetLabelE] = &sClassLabel,
|
||||
[WidgetButtonE] = &sClassButton,
|
||||
[WidgetCheckboxE] = &sClassCheckbox,
|
||||
[WidgetRadioGroupE] = &sClassRadioGroup,
|
||||
[WidgetRadioE] = &sClassRadio,
|
||||
[WidgetTextInputE] = &sClassTextInput,
|
||||
[WidgetTextAreaE] = &sClassTextArea,
|
||||
[WidgetListBoxE] = &sClassListBox,
|
||||
[WidgetSpacerE] = &sClassSpacer,
|
||||
[WidgetSeparatorE] = &sClassSeparator,
|
||||
[WidgetFrameE] = &sClassFrame,
|
||||
[WidgetDropdownE] = &sClassDropdown,
|
||||
[WidgetComboBoxE] = &sClassComboBox,
|
||||
[WidgetProgressBarE] = &sClassProgressBar,
|
||||
[WidgetSliderE] = &sClassSlider,
|
||||
[WidgetTabControlE] = &sClassTabControl,
|
||||
[WidgetTabPageE] = &sClassTabPage,
|
||||
[WidgetStatusBarE] = &sClassStatusBar,
|
||||
[WidgetToolbarE] = &sClassToolbar,
|
||||
[WidgetTreeViewE] = &sClassTreeView,
|
||||
[WidgetTreeItemE] = &sClassTreeItem,
|
||||
[WidgetImageE] = &sClassImage,
|
||||
[WidgetImageButtonE] = &sClassImageButton,
|
||||
[WidgetCanvasE] = &sClassCanvas,
|
||||
[WidgetAnsiTermE] = &sClassAnsiTerm,
|
||||
[WidgetListViewE] = &sClassListView,
|
||||
[WidgetSpinnerE] = &sClassSpinner,
|
||||
[WidgetScrollPaneE] = &sClassScrollPane,
|
||||
[WidgetSplitterE] = &sClassSplitter,
|
||||
[WidgetTimerE] = &sClassTimer
|
||||
};
|
||||
|
|
@ -1,746 +0,0 @@
|
|||
// widgetCore.c -- Core widget infrastructure (alloc, tree ops, helpers)
|
||||
//
|
||||
// This file provides the foundation for the widget tree: allocation,
|
||||
// parent-child linking, focus management, hit testing, and shared
|
||||
// utility functions used across multiple widget types.
|
||||
//
|
||||
// Widgets form a tree using intrusive linked lists (firstChild/lastChild/
|
||||
// nextSibling pointers inside each WidgetT). This is a singly-linked
|
||||
// child list with a tail pointer for O(1) append. The tree is owned
|
||||
// by its root, which is attached to a WindowT. Destroying the root
|
||||
// recursively destroys all descendants.
|
||||
//
|
||||
// Memory allocation is plain malloc/free rather than an arena or pool.
|
||||
// The widget count per window is typically small (tens to low hundreds),
|
||||
// so the allocation overhead is negligible on target hardware. An arena
|
||||
// approach was considered but rejected because widgets can be individually
|
||||
// created and destroyed at runtime (dialog dynamics, tree item insertion),
|
||||
// which doesn't map cleanly to an arena pattern.
|
||||
|
||||
#include "widgetInternal.h"
|
||||
|
||||
// ============================================================
|
||||
// Global state for drag and popup tracking
|
||||
// ============================================================
|
||||
//
|
||||
// These module-level pointers track ongoing UI interactions that span
|
||||
// multiple mouse events (drags, popups, button presses). They are global
|
||||
// rather than per-window because the DOS GUI is single-threaded and only
|
||||
// one interaction can be active at a time.
|
||||
//
|
||||
// Each pointer is set when an interaction begins (e.g. mouse-down on a
|
||||
// slider) and cleared when it ends (mouse-up). The event dispatcher in
|
||||
// widgetEvent.c checks these before normal hit testing -- active drags
|
||||
// take priority over everything else.
|
||||
//
|
||||
// All of these must be NULLed when the pointed-to widget is destroyed,
|
||||
// otherwise dangling pointers would cause crashes. widgetDestroyChildren()
|
||||
// and wgtDestroy() handle this cleanup.
|
||||
|
||||
clock_t sDblClickTicks = 0; // set from ctx->dblClickTicks during first paint
|
||||
bool sDebugLayout = false;
|
||||
WidgetT *sFocusedWidget = NULL; // currently focused widget (O(1) access, avoids tree walk)
|
||||
WidgetT *sOpenPopup = NULL; // dropdown/combobox with open popup list
|
||||
WidgetT *sPressedButton = NULL; // button being held down (tracks mouse in/out)
|
||||
WidgetT *sDragSlider = NULL; // slider being dragged
|
||||
WidgetT *sDrawingCanvas = NULL; // canvas receiving paint strokes
|
||||
WidgetT *sDragTextSelect = NULL; // text widget in drag-select mode
|
||||
int32_t sDragOffset = 0; // pixel offset from drag start to thumb center
|
||||
WidgetT *sResizeListView = NULL; // ListView undergoing column resize
|
||||
int32_t sResizeCol = -1; // which column is being resized
|
||||
int32_t sResizeStartX = 0; // mouse X at resize start
|
||||
int32_t sResizeOrigW = 0; // column width at resize start
|
||||
bool sResizeDragging = false; // true once mouse moves during column resize
|
||||
WidgetT *sDragSplitter = NULL; // splitter being dragged
|
||||
int32_t sDragSplitStart = 0; // mouse offset from splitter edge at drag start
|
||||
WidgetT *sDragReorder = NULL; // list/tree widget in drag-reorder mode
|
||||
WidgetT *sDragScrollbar = NULL; // widget whose scrollbar thumb is being dragged
|
||||
int32_t sDragScrollbarOff = 0; // mouse offset within thumb at drag start
|
||||
int32_t sDragScrollbarOrient = 0; // 0=vertical, 1=horizontal
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetAddChild
|
||||
// ============================================================
|
||||
//
|
||||
// Appends a child to the end of the parent's child list. O(1)
|
||||
// thanks to the lastChild tail pointer. The child list is singly-
|
||||
// linked (nextSibling), which saves 4 bytes per widget vs doubly-
|
||||
// linked and is sufficient because child removal is infrequent.
|
||||
|
||||
void widgetAddChild(WidgetT *parent, WidgetT *child) {
|
||||
child->parent = parent;
|
||||
child->nextSibling = NULL;
|
||||
|
||||
if (parent->lastChild) {
|
||||
parent->lastChild->nextSibling = child;
|
||||
parent->lastChild = child;
|
||||
} else {
|
||||
parent->firstChild = child;
|
||||
parent->lastChild = child;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetAlloc
|
||||
// ============================================================
|
||||
//
|
||||
// Allocates and zero-initializes a new widget, links it to its
|
||||
// class vtable via widgetClassTable[], and optionally adds it as
|
||||
// a child of the given parent.
|
||||
//
|
||||
// The memset to 0 is intentional -- it establishes sane defaults
|
||||
// for all fields: NULL pointers, zero coordinates, no focus,
|
||||
// no accel key, etc. Only visible and enabled default to true.
|
||||
//
|
||||
// The window pointer is inherited from the parent so that any
|
||||
// widget in the tree can find its owning window without walking
|
||||
// to the root.
|
||||
|
||||
WidgetT *widgetAlloc(WidgetT *parent, WidgetTypeE type) {
|
||||
WidgetT *w = (WidgetT *)malloc(sizeof(WidgetT));
|
||||
|
||||
if (!w) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(w, 0, sizeof(*w));
|
||||
w->type = type;
|
||||
w->wclass = widgetClassTable[type];
|
||||
w->visible = true;
|
||||
w->enabled = true;
|
||||
|
||||
if (parent) {
|
||||
w->window = parent->window;
|
||||
widgetAddChild(parent, w);
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetClearFocus
|
||||
// ============================================================
|
||||
|
||||
void widgetClearFocus(WidgetT *root) {
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
root->focused = false;
|
||||
|
||||
for (WidgetT *c = root->firstChild; c; c = c->nextSibling) {
|
||||
widgetClearFocus(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetCountVisibleChildren
|
||||
// ============================================================
|
||||
|
||||
int32_t widgetCountVisibleChildren(const WidgetT *w) {
|
||||
int32_t count = 0;
|
||||
|
||||
for (const WidgetT *c = w->firstChild; c; c = c->nextSibling) {
|
||||
if (c->visible) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetDestroyChildren
|
||||
// ============================================================
|
||||
//
|
||||
// Recursively destroys all descendants of a widget. Processes
|
||||
// children depth-first (destroy grandchildren before the child
|
||||
// itself) so that per-widget destroy callbacks see a consistent
|
||||
// tree state.
|
||||
//
|
||||
// Critically, this function clears all global state pointers that
|
||||
// reference destroyed widgets. Without this, any pending drag or
|
||||
// focus state would become a dangling pointer. Each global is
|
||||
// checked individually rather than cleared unconditionally to
|
||||
// avoid disrupting unrelated ongoing interactions.
|
||||
|
||||
void widgetDestroyChildren(WidgetT *w) {
|
||||
WidgetT *child = w->firstChild;
|
||||
|
||||
while (child) {
|
||||
WidgetT *next = child->nextSibling;
|
||||
widgetDestroyChildren(child);
|
||||
|
||||
if (child->wclass && child->wclass->destroy) {
|
||||
child->wclass->destroy(child);
|
||||
}
|
||||
|
||||
// Clear static references if they point to destroyed widgets
|
||||
if (sFocusedWidget == child) {
|
||||
sFocusedWidget = NULL;
|
||||
}
|
||||
|
||||
if (sOpenPopup == child) {
|
||||
sOpenPopup = NULL;
|
||||
}
|
||||
|
||||
if (sPressedButton == child) {
|
||||
sPressedButton = NULL;
|
||||
}
|
||||
|
||||
if (sDragSlider == child) {
|
||||
sDragSlider = NULL;
|
||||
}
|
||||
|
||||
if (sDrawingCanvas == child) {
|
||||
sDrawingCanvas = NULL;
|
||||
}
|
||||
|
||||
if (sResizeListView == child) {
|
||||
sResizeListView = NULL;
|
||||
sResizeCol = -1;
|
||||
sResizeDragging = false;
|
||||
}
|
||||
|
||||
if (sDragScrollbar == child) {
|
||||
sDragScrollbar = NULL;
|
||||
}
|
||||
|
||||
free(child);
|
||||
child = next;
|
||||
}
|
||||
|
||||
w->firstChild = NULL;
|
||||
w->lastChild = NULL;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetDropdownPopupRect
|
||||
// ============================================================
|
||||
//
|
||||
// Calculates the screen rectangle for a dropdown/combobox popup list.
|
||||
// Shared between Dropdown and ComboBox since they have identical
|
||||
// popup positioning logic.
|
||||
//
|
||||
// The popup tries to open below the widget first. If there isn't
|
||||
// enough room (popup would extend past the content area bottom),
|
||||
// it flips to open above instead. This ensures the popup is always
|
||||
// visible, even for dropdowns near the bottom of a window.
|
||||
//
|
||||
// Popup height is capped at DROPDOWN_MAX_VISIBLE items to prevent
|
||||
// huge popups from dominating the screen.
|
||||
|
||||
void widgetDropdownPopupRect(WidgetT *w, const BitmapFontT *font, int32_t contentH, int32_t *popX, int32_t *popY, int32_t *popW, int32_t *popH) {
|
||||
int32_t itemCount = 0;
|
||||
|
||||
if (w->type == WidgetDropdownE) {
|
||||
itemCount = w->as.dropdown.itemCount;
|
||||
} else if (w->type == WidgetComboBoxE) {
|
||||
itemCount = w->as.comboBox.itemCount;
|
||||
}
|
||||
|
||||
int32_t visibleItems = itemCount;
|
||||
|
||||
if (visibleItems > DROPDOWN_MAX_VISIBLE) {
|
||||
visibleItems = DROPDOWN_MAX_VISIBLE;
|
||||
}
|
||||
|
||||
if (visibleItems < 1) {
|
||||
visibleItems = 1;
|
||||
}
|
||||
|
||||
*popX = w->x;
|
||||
*popW = w->w;
|
||||
*popH = visibleItems * font->charHeight + 4; // 2px border each side
|
||||
|
||||
// Try below first, then above if no room
|
||||
if (w->y + w->h + *popH <= contentH) {
|
||||
*popY = w->y + w->h;
|
||||
} else {
|
||||
*popY = w->y - *popH;
|
||||
|
||||
if (*popY < 0) {
|
||||
*popY = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetDrawDropdownArrow
|
||||
// ============================================================
|
||||
//
|
||||
// Draws a small downward-pointing filled triangle (7, 5, 3, 1 pixels
|
||||
// wide across 4 rows) centered at the given position. Used by both
|
||||
// Dropdown and ComboBox for the drop button arrow glyph.
|
||||
|
||||
void widgetDrawDropdownArrow(DisplayT *d, const BlitOpsT *ops, int32_t centerX, int32_t centerY, uint32_t color) {
|
||||
for (int32_t i = 0; i < 4; i++) {
|
||||
drawHLine(d, ops, centerX - 3 + i, centerY + i, 7 - i * 2, color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetFindByAccel
|
||||
// ============================================================
|
||||
//
|
||||
// Finds a widget with the given Alt+key accelerator. Recurses the
|
||||
// tree depth-first, respecting visibility and enabled state.
|
||||
//
|
||||
// Special case for TabPage widgets: even if the tab page itself is
|
||||
// not visible (inactive tab), its accelKey is still checked. This
|
||||
// allows Alt+key to switch to a different tab. However, children
|
||||
// of invisible tab pages are NOT searched -- their accelerators
|
||||
// should not be active when the tab is hidden.
|
||||
|
||||
WidgetT *widgetFindByAccel(WidgetT *root, char key) {
|
||||
if (!root || !root->enabled) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Invisible tab pages: match the page itself (for tab switching)
|
||||
// but don't recurse into children (their accels shouldn't be active)
|
||||
if (!root->visible) {
|
||||
if (root->type == WidgetTabPageE && root->accelKey == key) {
|
||||
return root;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (root->accelKey == key) {
|
||||
return root;
|
||||
}
|
||||
|
||||
for (WidgetT *c = root->firstChild; c; c = c->nextSibling) {
|
||||
WidgetT *found = widgetFindByAccel(c, key);
|
||||
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetFindNextFocusable
|
||||
// ============================================================
|
||||
//
|
||||
// Implements Tab-order navigation: finds the next focusable widget
|
||||
// after 'after' in depth-first tree order. The two-pass approach
|
||||
// (search from 'after' to end, then wrap to start) ensures circular
|
||||
// tabbing -- Tab on the last focusable widget wraps to the first.
|
||||
//
|
||||
// The pastAfter flag tracks whether we've passed the 'after' widget
|
||||
// during traversal. Once past it, the next focusable widget is the
|
||||
// answer. This avoids collecting all focusable widgets into an array
|
||||
// just to find the next one -- the common case returns quickly.
|
||||
|
||||
static WidgetT *findNextFocusableImpl(WidgetT *w, WidgetT *after, bool *pastAfter) {
|
||||
if (!w->visible || !w->enabled) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (after == NULL) {
|
||||
*pastAfter = true;
|
||||
}
|
||||
|
||||
if (w == after) {
|
||||
*pastAfter = true;
|
||||
} else if (*pastAfter && widgetIsFocusable(w->type)) {
|
||||
return w;
|
||||
}
|
||||
|
||||
for (WidgetT *c = w->firstChild; c; c = c->nextSibling) {
|
||||
WidgetT *found = findNextFocusableImpl(c, after, pastAfter);
|
||||
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
WidgetT *widgetFindNextFocusable(WidgetT *root, WidgetT *after) {
|
||||
bool pastAfter = false;
|
||||
WidgetT *found = findNextFocusableImpl(root, after, &pastAfter);
|
||||
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
|
||||
// Wrap around -- search from the beginning
|
||||
pastAfter = true;
|
||||
return findNextFocusableImpl(root, NULL, &pastAfter);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetFindPrevFocusable
|
||||
// ============================================================
|
||||
//
|
||||
// Shift+Tab navigation: finds the previous focusable widget.
|
||||
// Unlike findNextFocusable which can short-circuit during traversal,
|
||||
// finding the PREVIOUS widget requires knowing the full order.
|
||||
// So this collects all focusable widgets into an array, finds the
|
||||
// target's index, and returns index-1 (with wraparound).
|
||||
//
|
||||
// The explicit stack-based DFS (rather than recursion) is used here
|
||||
// because we need to push children in reverse order to get the same
|
||||
// left-to-right depth-first ordering as the recursive version.
|
||||
// Fixed-size arrays (128 widgets, 64 stack depth) are adequate for
|
||||
// any reasonable dialog layout and avoid dynamic allocation.
|
||||
|
||||
WidgetT *widgetFindPrevFocusable(WidgetT *root, WidgetT *before) {
|
||||
WidgetT *list[128];
|
||||
int32_t count = 0;
|
||||
|
||||
// Collect all focusable widgets via depth-first traversal
|
||||
WidgetT *stack[64];
|
||||
int32_t top = 0;
|
||||
stack[top++] = root;
|
||||
|
||||
while (top > 0) {
|
||||
WidgetT *w = stack[--top];
|
||||
|
||||
if (!w->visible || !w->enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (widgetIsFocusable(w->type) && count < 128) {
|
||||
list[count++] = w;
|
||||
}
|
||||
|
||||
// Push children in reverse order so first child is processed first
|
||||
WidgetT *children[64];
|
||||
int32_t childCount = 0;
|
||||
|
||||
for (WidgetT *c = w->firstChild; c; c = c->nextSibling) {
|
||||
if (childCount < 64) {
|
||||
children[childCount++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = childCount - 1; i >= 0; i--) {
|
||||
if (top < 64) {
|
||||
stack[top++] = children[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Find 'before' in the list
|
||||
int32_t idx = -1;
|
||||
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
if (list[i] == before) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx <= 0) {
|
||||
return list[count - 1]; // Wrap to last
|
||||
}
|
||||
|
||||
return list[idx - 1];
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetFrameBorderWidth
|
||||
// ============================================================
|
||||
|
||||
int32_t widgetFrameBorderWidth(const WidgetT *w) {
|
||||
if (w->type != WidgetFrameE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (w->as.frame.style == FrameFlatE) {
|
||||
return FRAME_FLAT_BORDER;
|
||||
}
|
||||
|
||||
return FRAME_BEVEL_BORDER;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetHitTest
|
||||
// ============================================================
|
||||
//
|
||||
// Recursive hit testing: finds the deepest (most specific) widget
|
||||
// under the given coordinates. Returns the widget itself if no
|
||||
// child is hit, or NULL if the point is outside this widget.
|
||||
//
|
||||
// Children are iterated front-to-back (first to last in the linked
|
||||
// list), but the LAST match wins. This gives later siblings higher
|
||||
// Z-order, which matches the painting order (later children paint
|
||||
// on top of earlier ones). This is important for overlapping widgets,
|
||||
// though in practice the layout engine rarely produces overlap.
|
||||
//
|
||||
// Widgets with WCLASS_NO_HIT_RECURSE stop the recursion -- the parent
|
||||
// widget handles all mouse events for its children. This is used by
|
||||
// TreeView, ScrollPane, ListView, and Splitter, which need to manage
|
||||
// their own internal regions (scrollbars, column headers, tree
|
||||
// expand buttons) that don't correspond to child widgets.
|
||||
|
||||
WidgetT *widgetHitTest(WidgetT *w, int32_t x, int32_t y) {
|
||||
if (!w->visible) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (x < w->x || x >= w->x + w->w || y < w->y || y >= w->y + w->h) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Widgets with WCLASS_NO_HIT_RECURSE manage their own children
|
||||
if (w->wclass && (w->wclass->flags & WCLASS_NO_HIT_RECURSE)) {
|
||||
return w;
|
||||
}
|
||||
|
||||
// Check children -- take the last match (topmost in Z-order)
|
||||
WidgetT *hit = NULL;
|
||||
|
||||
for (WidgetT *c = w->firstChild; c; c = c->nextSibling) {
|
||||
WidgetT *childHit = widgetHitTest(c, x, y);
|
||||
|
||||
if (childHit) {
|
||||
hit = childHit;
|
||||
}
|
||||
}
|
||||
|
||||
return hit ? hit : w;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetIsFocusable
|
||||
// ============================================================
|
||||
|
||||
bool widgetIsFocusable(WidgetTypeE type) {
|
||||
return (widgetClassTable[type]->flags & WCLASS_FOCUSABLE) != 0;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetIsBoxContainer
|
||||
// ============================================================
|
||||
//
|
||||
// Returns true for widget types that use the generic box layout.
|
||||
|
||||
bool widgetIsBoxContainer(WidgetTypeE type) {
|
||||
return (widgetClassTable[type]->flags & WCLASS_BOX_CONTAINER) != 0;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetIsHorizContainer
|
||||
// ============================================================
|
||||
//
|
||||
// Returns true for container types that lay out children horizontally.
|
||||
|
||||
bool widgetIsHorizContainer(WidgetTypeE type) {
|
||||
return (widgetClassTable[type]->flags & WCLASS_HORIZ_CONTAINER) != 0;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetMaxItemLen
|
||||
// ============================================================
|
||||
//
|
||||
// Scans an array of string items and returns the maximum strlen.
|
||||
// Shared by ListBox, Dropdown, and ComboBox to cache the widest
|
||||
// item length for calcMinSize without duplicating the loop.
|
||||
|
||||
int32_t widgetMaxItemLen(const char **items, int32_t count) {
|
||||
int32_t maxLen = 0;
|
||||
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
int32_t slen = (int32_t)strlen(items[i]);
|
||||
|
||||
if (slen > maxLen) {
|
||||
maxLen = slen;
|
||||
}
|
||||
}
|
||||
|
||||
return maxLen;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetNavigateIndex
|
||||
// ============================================================
|
||||
//
|
||||
// Shared keyboard navigation for list-like widgets (ListBox, Dropdown,
|
||||
// ListView, etc.). Encapsulates the Up/Down/Home/End/PgUp/PgDn logic
|
||||
// so each widget doesn't have to reimplement index clamping.
|
||||
//
|
||||
// Key values use the 0x100 flag to mark extended scan codes (arrow
|
||||
// keys, Home, End, etc.) -- this is the DVX convention for passing
|
||||
// scan codes through the same int32_t channel as ASCII values.
|
||||
//
|
||||
// Returns -1 for unrecognized keys so callers can check whether the
|
||||
// key was consumed.
|
||||
|
||||
int32_t widgetNavigateIndex(int32_t key, int32_t current, int32_t count, int32_t pageSize) {
|
||||
if (key == (0x50 | 0x100)) {
|
||||
// Down arrow
|
||||
if (current < count - 1) {
|
||||
return current + 1;
|
||||
}
|
||||
|
||||
return current < 0 ? 0 : current;
|
||||
}
|
||||
|
||||
if (key == (0x48 | 0x100)) {
|
||||
// Up arrow
|
||||
if (current > 0) {
|
||||
return current - 1;
|
||||
}
|
||||
|
||||
return current < 0 ? 0 : current;
|
||||
}
|
||||
|
||||
if (key == (0x47 | 0x100)) {
|
||||
// Home
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (key == (0x4F | 0x100)) {
|
||||
// End
|
||||
return count - 1;
|
||||
}
|
||||
|
||||
if (key == (0x51 | 0x100)) {
|
||||
// Page Down
|
||||
int32_t n = current + pageSize;
|
||||
return n >= count ? count - 1 : n;
|
||||
}
|
||||
|
||||
if (key == (0x49 | 0x100)) {
|
||||
// Page Up
|
||||
int32_t n = current - pageSize;
|
||||
return n < 0 ? 0 : n;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetPaintPopupList
|
||||
// ============================================================
|
||||
//
|
||||
// Shared popup list painting for Dropdown and ComboBox.
|
||||
|
||||
void widgetPaintPopupList(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, int32_t popX, int32_t popY, int32_t popW, int32_t popH, const char **items, int32_t itemCount, int32_t hoverIdx, int32_t scrollPos) {
|
||||
// Draw popup border
|
||||
BevelStyleT bevel;
|
||||
bevel.highlight = colors->windowHighlight;
|
||||
bevel.shadow = colors->windowShadow;
|
||||
bevel.face = colors->contentBg;
|
||||
bevel.width = 2;
|
||||
drawBevel(d, ops, popX, popY, popW, popH, &bevel);
|
||||
|
||||
// Draw items
|
||||
int32_t visibleItems = popH / font->charHeight;
|
||||
int32_t textX = popX + TEXT_INPUT_PAD;
|
||||
int32_t textY = popY + 2;
|
||||
int32_t textW = popW - TEXT_INPUT_PAD * 2 - 4;
|
||||
|
||||
for (int32_t i = 0; i < visibleItems && (scrollPos + i) < itemCount; i++) {
|
||||
int32_t idx = scrollPos + i;
|
||||
int32_t iy = textY + i * font->charHeight;
|
||||
uint32_t ifg = colors->contentFg;
|
||||
uint32_t ibg = colors->contentBg;
|
||||
|
||||
if (idx == hoverIdx) {
|
||||
ifg = colors->menuHighlightFg;
|
||||
ibg = colors->menuHighlightBg;
|
||||
rectFill(d, ops, popX + 2, iy, textW + TEXT_INPUT_PAD * 2, font->charHeight, ibg);
|
||||
}
|
||||
|
||||
drawText(d, ops, font, textX, iy, items[idx], ifg, ibg, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetScrollbarThumb
|
||||
// ============================================================
|
||||
//
|
||||
// Calculates thumb position and size for a scrollbar track.
|
||||
// Used by both the WM-level scrollbars and widget-internal scrollbars
|
||||
// (ListBox, TreeView, etc.) to maintain consistent scrollbar behavior.
|
||||
//
|
||||
// The thumb size is proportional to visibleSize/totalSize -- a larger
|
||||
// visible area means a larger thumb, giving visual feedback about how
|
||||
// much content is scrollable. SB_MIN_THUMB prevents the thumb from
|
||||
// becoming too small to grab with a mouse.
|
||||
|
||||
void widgetScrollbarThumb(int32_t trackLen, int32_t totalSize, int32_t visibleSize, int32_t scrollPos, int32_t *thumbPos, int32_t *thumbSize) {
|
||||
*thumbSize = (trackLen * visibleSize) / totalSize;
|
||||
|
||||
if (*thumbSize < SB_MIN_THUMB) {
|
||||
*thumbSize = SB_MIN_THUMB;
|
||||
}
|
||||
|
||||
if (*thumbSize > trackLen) {
|
||||
*thumbSize = trackLen;
|
||||
}
|
||||
|
||||
int32_t maxScroll = totalSize - visibleSize;
|
||||
|
||||
if (maxScroll > 0) {
|
||||
*thumbPos = ((trackLen - *thumbSize) * scrollPos) / maxScroll;
|
||||
} else {
|
||||
*thumbPos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// widgetRemoveChild
|
||||
// ============================================================
|
||||
//
|
||||
// Unlinks a child from its parent's child list. O(n) in the number
|
||||
// of children because the singly-linked list requires walking to
|
||||
// find the predecessor. This is acceptable because child removal
|
||||
// is infrequent (widget destruction, tree item reordering).
|
||||
|
||||
void widgetRemoveChild(WidgetT *parent, WidgetT *child) {
|
||||
WidgetT *prev = NULL;
|
||||
|
||||
for (WidgetT *c = parent->firstChild; c; c = c->nextSibling) {
|
||||
if (c == child) {
|
||||
if (prev) {
|
||||
prev->nextSibling = c->nextSibling;
|
||||
} else {
|
||||
parent->firstChild = c->nextSibling;
|
||||
}
|
||||
|
||||
if (parent->lastChild == child) {
|
||||
parent->lastChild = prev;
|
||||
}
|
||||
|
||||
child->nextSibling = NULL;
|
||||
child->parent = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
prev = c;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
# DVX Shell Makefile for DJGPP cross-compilation
|
||||
|
||||
DJGPP_PREFIX = $(HOME)/djgpp/djgpp
|
||||
DJGPP_LIBPATH = $(HOME)/claude/windriver/tools/lib
|
||||
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
|
||||
EXE2COFF = $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/exe2coff
|
||||
CWSDSTUB = $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/CWSDSTUB.EXE
|
||||
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../dvx -I../tasks
|
||||
LDFLAGS = -L../lib -ldvx -ltasks -lm
|
||||
|
||||
OBJDIR = ../obj/dvxshell
|
||||
BINDIR = ../bin
|
||||
CONFIGDIR = ../bin/config
|
||||
THEMEDIR = ../bin/config/themes
|
||||
WPAPERDIR = ../bin/config/wpaper
|
||||
LIBDIR = ../lib
|
||||
|
||||
SRCS = shellMain.c shellApp.c shellExport.c shellInfo.c shellTaskMgr.c
|
||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||
TARGET = $(BINDIR)/dvx.exe
|
||||
|
||||
.PHONY: all clean libs
|
||||
|
||||
THEMES = $(THEMEDIR)/geos.thm $(THEMEDIR)/win31.thm $(THEMEDIR)/cde.thm
|
||||
WPAPERS = $(WPAPERDIR)/blueglow.jpg $(WPAPERDIR)/swoop.jpg $(WPAPERDIR)/triangle.jpg
|
||||
|
||||
all: libs $(TARGET) $(CONFIGDIR)/dvx.ini $(THEMES) $(WPAPERS)
|
||||
|
||||
libs:
|
||||
$(MAKE) -C ../dvx
|
||||
$(MAKE) -C ../tasks
|
||||
|
||||
$(TARGET): $(OBJS) $(LIBDIR)/libdvx.a $(LIBDIR)/libtasks.a | $(BINDIR)
|
||||
$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) -Wl,-Map=$(BINDIR)/dvx.map
|
||||
$(EXE2COFF) $@
|
||||
cat $(CWSDSTUB) $(BINDIR)/dvx > $@
|
||||
rm -f $(BINDIR)/dvx
|
||||
|
||||
$(CONFIGDIR)/dvx.ini: ../dvx.ini | $(CONFIGDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
|
||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR):
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
$(BINDIR):
|
||||
mkdir -p $(BINDIR)
|
||||
|
||||
$(CONFIGDIR):
|
||||
mkdir -p $(CONFIGDIR)
|
||||
|
||||
$(THEMEDIR):
|
||||
mkdir -p $(THEMEDIR)
|
||||
|
||||
$(THEMEDIR)/%.thm: ../themes/%.thm | $(THEMEDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
|
||||
$(WPAPERDIR):
|
||||
mkdir -p $(WPAPERDIR)
|
||||
|
||||
$(WPAPERDIR)/%.jpg: ../wpaper/%.jpg | $(WPAPERDIR)
|
||||
cp $< $@
|
||||
|
||||
# Dependencies
|
||||
$(OBJDIR)/shellMain.o: shellMain.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../tasks/taskswitch.h
|
||||
$(OBJDIR)/shellApp.o: shellApp.c shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../tasks/taskswitch.h
|
||||
$(OBJDIR)/shellExport.o: shellExport.c shellApp.h shellInfo.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxDraw.h ../dvx/dvxVideo.h ../dvx/dvxWm.h ../tasks/taskswitch.h
|
||||
$(OBJDIR)/shellInfo.o: shellInfo.c shellInfo.h shellApp.h ../dvx/dvxApp.h ../dvx/platform/dvxPlatform.h
|
||||
$(OBJDIR)/shellTaskMgr.o: shellTaskMgr.c shellTaskMgr.h shellApp.h ../dvx/dvxApp.h ../dvx/dvxDialog.h ../dvx/dvxWidget.h ../dvx/dvxWm.h ../dvx/platform/dvxPlatform.h
|
||||
clean:
|
||||
rm -f $(OBJS) $(TARGET) $(BINDIR)/dvx.map $(BINDIR)/dvx.log
|
||||
rm -rf $(WPAPERDIR) $(THEMEDIR) $(CONFIGDIR)
|
||||
|
|
@ -1,733 +0,0 @@
|
|||
// shellExport.c -- DXE export table and wrapper functions for DVX Shell
|
||||
//
|
||||
// Exports all dvx*/wgt*/ts* symbols that DXE apps need. A few functions
|
||||
// are wrapped for resource tracking (window ownership via appId).
|
||||
//
|
||||
// DXE3 is DJGPP's dynamic linking mechanism. Unlike ELF shared libraries,
|
||||
// DXE modules have no implicit access to the host's symbol table. Every
|
||||
// function or variable the DXE needs must be explicitly listed in an
|
||||
// export table registered via dlregsym() BEFORE any dlopen() call. If a
|
||||
// symbol is missing, dlopen() returns NULL with a "symbol not found" error.
|
||||
//
|
||||
// This file is essentially the ABI contract between the shell and apps.
|
||||
// Three categories of exports:
|
||||
//
|
||||
// 1. Wrapped functions: dvxCreateWindow, dvxCreateWindowCentered,
|
||||
// dvxDestroyWindow. These are intercepted to stamp win->appId for
|
||||
// resource ownership tracking. The DXE sees them under their original
|
||||
// names -- the app code calls dvxCreateWindow() normally and gets our
|
||||
// wrapper transparently.
|
||||
//
|
||||
// 2. Direct exports: all other dvx/wgt/wm/ts functions. These are safe
|
||||
// to call without shell-side interception.
|
||||
//
|
||||
// 3. libc functions: DXE modules are statically linked against DJGPP's
|
||||
// libc, but DJGPP's DXE3 loader requires explicit re-export of any
|
||||
// libc symbols the module references. Without these entries, the DXE
|
||||
// would fail to load with unresolved symbol errors. This is a DXE3
|
||||
// design limitation -- there's no automatic fallback to the host's libc.
|
||||
|
||||
#include "shellApp.h"
|
||||
#include "shellInfo.h"
|
||||
#include "shellTaskMgr.h"
|
||||
#include "dvxApp.h"
|
||||
#include "dvxDialog.h"
|
||||
#include "dvxWidget.h"
|
||||
#include "dvxDraw.h"
|
||||
#include "dvxPrefs.h"
|
||||
#include "platform/dvxPlatform.h"
|
||||
#include "dvxVideo.h"
|
||||
#include "dvxWm.h"
|
||||
#include "taskswitch.h"
|
||||
|
||||
#include <sys/dxe.h>
|
||||
#include <sys/stat.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <math.h>
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
// stb headers (no IMPLEMENTATION -- symbols are in libdvx.a / libtasks.a)
|
||||
#include "thirdparty/stb_image.h"
|
||||
#include "thirdparty/stb_image_write.h"
|
||||
#include "thirdparty/stb_ds.h"
|
||||
|
||||
// DJGPP stdio internals -- the stdin/stdout/stderr macros dereference
|
||||
// these pointers. Without exporting them, any DXE that uses printf
|
||||
// or fprintf gets an unresolved symbol.
|
||||
extern FILE __dj_stdin;
|
||||
extern FILE __dj_stdout;
|
||||
extern FILE __dj_stderr;
|
||||
|
||||
// libgcc 64-bit integer math helpers (no header declares these)
|
||||
extern long long __divdi3(long long, long long);
|
||||
extern long long __moddi3(long long, long long);
|
||||
extern long long __muldi3(long long, long long);
|
||||
extern unsigned long long __udivdi3(unsigned long long, unsigned long long);
|
||||
extern unsigned long long __udivmoddi4(unsigned long long, unsigned long long, unsigned long long *);
|
||||
extern unsigned long long __umoddi3(unsigned long long, unsigned long long);
|
||||
|
||||
// ============================================================
|
||||
// Prototypes
|
||||
// ============================================================
|
||||
|
||||
static void shellRegisterExports(void);
|
||||
static WindowT *shellWrapCreateWindow(AppContextT *ctx, const char *title, int32_t x, int32_t y, int32_t w, int32_t h, bool resizable);
|
||||
static WindowT *shellWrapCreateWindowCentered(AppContextT *ctx, const char *title, int32_t w, int32_t h, bool resizable);
|
||||
static void shellWrapDestroyWindow(AppContextT *ctx, WindowT *win);
|
||||
|
||||
// ============================================================
|
||||
// Wrapper: dvxCreateWindow -- stamps win->appId
|
||||
// ============================================================
|
||||
|
||||
// The wrapper calls the real dvxCreateWindow, then tags the result with
|
||||
// sCurrentAppId. This is how the shell knows which app owns which window,
|
||||
// enabling per-app window cleanup on crash/termination. The app never
|
||||
// sees the difference -- the wrapper has the same signature and is
|
||||
// exported under the same name as the original function.
|
||||
static WindowT *shellWrapCreateWindow(AppContextT *ctx, const char *title, int32_t x, int32_t y, int32_t w, int32_t h, bool resizable) {
|
||||
WindowT *win = dvxCreateWindow(ctx, title, x, y, w, h, resizable);
|
||||
|
||||
if (win) {
|
||||
win->appId = sCurrentAppId;
|
||||
}
|
||||
|
||||
return win;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// Wrapper: dvxCreateWindowCentered -- stamps win->appId
|
||||
// ============================================================
|
||||
|
||||
static WindowT *shellWrapCreateWindowCentered(AppContextT *ctx, const char *title, int32_t w, int32_t h, bool resizable) {
|
||||
WindowT *win = dvxCreateWindowCentered(ctx, title, w, h, resizable);
|
||||
|
||||
if (win) {
|
||||
win->appId = sCurrentAppId;
|
||||
}
|
||||
|
||||
return win;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// Wrapper: dvxDestroyWindow -- checks for last-window reap
|
||||
// ============================================================
|
||||
|
||||
// Beyond just destroying the window, this wrapper implements the lifecycle
|
||||
// rule for callback-only apps: when their last window closes, they're done.
|
||||
// Main-loop apps manage their own lifetime (their task returns from
|
||||
// appMain), so this check only applies to callback-only apps.
|
||||
// The appId is captured before destruction because the window struct is
|
||||
// freed by dvxDestroyWindow.
|
||||
static void shellWrapDestroyWindow(AppContextT *ctx, WindowT *win) {
|
||||
int32_t appId = win->appId;
|
||||
|
||||
dvxDestroyWindow(ctx, win);
|
||||
|
||||
// If this was a callback-only app's last window, mark for reaping
|
||||
if (appId > 0) {
|
||||
ShellAppT *app = shellGetApp(appId);
|
||||
|
||||
if (app && !app->hasMainLoop && app->state == AppStateRunningE) {
|
||||
// Check if app still has any windows
|
||||
bool hasWindows = false;
|
||||
|
||||
for (int32_t i = 0; i < ctx->stack.count; i++) {
|
||||
if (ctx->stack.windows[i]->appId == appId) {
|
||||
hasWindows = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasWindows) {
|
||||
app->state = AppStateTerminatingE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// Export table
|
||||
// ============================================================
|
||||
|
||||
// DXE_EXPORT_TABLE generates a DXE symbol table array. DXE_EXPORT(fn)
|
||||
// expands to { "_fn", (void *)fn } -- the underscore prefix matches COFF
|
||||
// symbol naming. For wrapped functions we use raw entries with explicit
|
||||
// names so the DXE sees "_dvxCreateWindow" but gets our wrapper's address.
|
||||
|
||||
DXE_EXPORT_TABLE(shellExportTable)
|
||||
// Wrapped functions (exported under original names, but pointing to
|
||||
// our wrappers that add resource tracking)
|
||||
{ "_dvxCreateWindow", (void *)shellWrapCreateWindow },
|
||||
{ "_dvxDestroyWindow", (void *)shellWrapDestroyWindow },
|
||||
|
||||
// dvxPlatform.h -- platform abstraction
|
||||
DXE_EXPORT(platformLineEnding)
|
||||
DXE_EXPORT(platformChdir)
|
||||
DXE_EXPORT(platformGetMemoryInfo)
|
||||
DXE_EXPORT(platformMkdirRecursive)
|
||||
DXE_EXPORT(platformMouseSetAccel)
|
||||
DXE_EXPORT(platformMouseWarp)
|
||||
DXE_EXPORT(platformPathDirEnd)
|
||||
DXE_EXPORT(platformStripLineEndings)
|
||||
DXE_EXPORT(platformValidateFilename)
|
||||
DXE_EXPORT(platformVideoEnumModes)
|
||||
|
||||
// dvxPrefs.h -- preferences
|
||||
DXE_EXPORT(prefsGetBool)
|
||||
DXE_EXPORT(prefsGetInt)
|
||||
DXE_EXPORT(prefsGetString)
|
||||
DXE_EXPORT(prefsLoad)
|
||||
DXE_EXPORT(prefsRemove)
|
||||
DXE_EXPORT(prefsSave)
|
||||
DXE_EXPORT(prefsSaveAs)
|
||||
DXE_EXPORT(prefsSetBool)
|
||||
DXE_EXPORT(prefsSetInt)
|
||||
DXE_EXPORT(prefsSetString)
|
||||
|
||||
// dvxApp.h -- direct exports
|
||||
DXE_EXPORT(dvxApplyColorScheme)
|
||||
DXE_EXPORT(dvxChangeVideoMode)
|
||||
DXE_EXPORT(dvxColorLabel)
|
||||
DXE_EXPORT(dvxColorName)
|
||||
DXE_EXPORT(dvxGetColor)
|
||||
DXE_EXPORT(dvxInit)
|
||||
DXE_EXPORT(dvxLoadTheme)
|
||||
DXE_EXPORT(dvxResetColorScheme)
|
||||
DXE_EXPORT(dvxSaveTheme)
|
||||
DXE_EXPORT(dvxSetColor)
|
||||
DXE_EXPORT(dvxSetMouseConfig)
|
||||
DXE_EXPORT(dvxSetWallpaper)
|
||||
DXE_EXPORT(dvxSetWallpaperMode)
|
||||
DXE_EXPORT(dvxShutdown)
|
||||
DXE_EXPORT(dvxUpdate)
|
||||
{ "_dvxCreateWindowCentered", (void *)shellWrapCreateWindowCentered },
|
||||
DXE_EXPORT(dvxFitWindow)
|
||||
DXE_EXPORT(dvxInvalidateRect)
|
||||
DXE_EXPORT(dvxInvalidateWindow)
|
||||
DXE_EXPORT(dvxMinimizeWindow)
|
||||
DXE_EXPORT(dvxMaximizeWindow)
|
||||
DXE_EXPORT(dvxQuit)
|
||||
DXE_EXPORT(dvxSetTitle)
|
||||
DXE_EXPORT(dvxGetFont)
|
||||
DXE_EXPORT(dvxGetColors)
|
||||
DXE_EXPORT(dvxGetDisplay)
|
||||
DXE_EXPORT(dvxGetVideoModes)
|
||||
DXE_EXPORT(dvxGetBlitOps)
|
||||
DXE_EXPORT(dvxSetWindowIcon)
|
||||
DXE_EXPORT(dvxLoadImage)
|
||||
DXE_EXPORT(dvxFreeImage)
|
||||
DXE_EXPORT(dvxSaveImage)
|
||||
DXE_EXPORT(dvxScreenshot)
|
||||
DXE_EXPORT(dvxWindowScreenshot)
|
||||
DXE_EXPORT(dvxCreateAccelTable)
|
||||
DXE_EXPORT(dvxFreeAccelTable)
|
||||
DXE_EXPORT(dvxAddAccel)
|
||||
DXE_EXPORT(dvxCascadeWindows)
|
||||
DXE_EXPORT(dvxTileWindows)
|
||||
DXE_EXPORT(dvxTileWindowsH)
|
||||
DXE_EXPORT(dvxTileWindowsV)
|
||||
DXE_EXPORT(dvxClipboardCopy)
|
||||
DXE_EXPORT(dvxClipboardGet)
|
||||
|
||||
// dvxDialog.h
|
||||
DXE_EXPORT(dvxMessageBox)
|
||||
DXE_EXPORT(dvxFileDialog)
|
||||
|
||||
// dvxDraw.h
|
||||
DXE_EXPORT(rectFill)
|
||||
DXE_EXPORT(rectCopy)
|
||||
DXE_EXPORT(drawBevel)
|
||||
DXE_EXPORT(drawChar)
|
||||
DXE_EXPORT(drawText)
|
||||
DXE_EXPORT(drawTextN)
|
||||
DXE_EXPORT(textWidth)
|
||||
DXE_EXPORT(drawTextAccel)
|
||||
DXE_EXPORT(textWidthAccel)
|
||||
DXE_EXPORT(drawFocusRect)
|
||||
DXE_EXPORT(drawHLine)
|
||||
DXE_EXPORT(drawVLine)
|
||||
|
||||
// dvxVideo.h
|
||||
DXE_EXPORT(packColor)
|
||||
DXE_EXPORT(resetClipRect)
|
||||
DXE_EXPORT(setClipRect)
|
||||
|
||||
// dvxWm.h
|
||||
DXE_EXPORT(wmAddMenuBar)
|
||||
DXE_EXPORT(wmAddMenu)
|
||||
DXE_EXPORT(wmAddMenuItem)
|
||||
DXE_EXPORT(wmAddMenuCheckItem)
|
||||
DXE_EXPORT(wmAddMenuRadioItem)
|
||||
DXE_EXPORT(wmAddMenuSeparator)
|
||||
DXE_EXPORT(wmAddSubMenu)
|
||||
DXE_EXPORT(wmAddVScrollbar)
|
||||
DXE_EXPORT(wmAddHScrollbar)
|
||||
DXE_EXPORT(wmMinimizedIconPos)
|
||||
DXE_EXPORT(wmMinimizedIconRect)
|
||||
DXE_EXPORT(wmSetTitle)
|
||||
DXE_EXPORT(wmSetIcon)
|
||||
DXE_EXPORT(wmCreateMenu)
|
||||
DXE_EXPORT(wmFreeMenu)
|
||||
DXE_EXPORT(wmUpdateContentRect)
|
||||
DXE_EXPORT(wmReallocContentBuf)
|
||||
|
||||
// dvxWidget.h -- window integration
|
||||
DXE_EXPORT(wgtInitWindow)
|
||||
|
||||
// dvxWidget.h -- containers
|
||||
DXE_EXPORT(wgtVBox)
|
||||
DXE_EXPORT(wgtHBox)
|
||||
DXE_EXPORT(wgtFrame)
|
||||
|
||||
// dvxWidget.h -- basic widgets
|
||||
DXE_EXPORT(wgtLabel)
|
||||
DXE_EXPORT(wgtLabelSetAlign)
|
||||
DXE_EXPORT(wgtButton)
|
||||
DXE_EXPORT(wgtCheckbox)
|
||||
DXE_EXPORT(wgtTextInput)
|
||||
DXE_EXPORT(wgtPasswordInput)
|
||||
DXE_EXPORT(wgtMaskedInput)
|
||||
|
||||
// dvxWidget.h -- radio buttons
|
||||
DXE_EXPORT(wgtRadioGroup)
|
||||
DXE_EXPORT(wgtRadio)
|
||||
|
||||
// dvxWidget.h -- spacing
|
||||
DXE_EXPORT(wgtSpacer)
|
||||
DXE_EXPORT(wgtHSeparator)
|
||||
DXE_EXPORT(wgtVSeparator)
|
||||
|
||||
// dvxWidget.h -- complex widgets
|
||||
DXE_EXPORT(wgtListBox)
|
||||
DXE_EXPORT(wgtTextArea)
|
||||
|
||||
// dvxWidget.h -- dropdown/combo
|
||||
DXE_EXPORT(wgtDropdown)
|
||||
DXE_EXPORT(wgtDropdownSetItems)
|
||||
DXE_EXPORT(wgtDropdownGetSelected)
|
||||
DXE_EXPORT(wgtDropdownSetSelected)
|
||||
DXE_EXPORT(wgtComboBox)
|
||||
DXE_EXPORT(wgtComboBoxSetItems)
|
||||
DXE_EXPORT(wgtComboBoxGetSelected)
|
||||
DXE_EXPORT(wgtComboBoxSetSelected)
|
||||
|
||||
// dvxWidget.h -- progress bar
|
||||
DXE_EXPORT(wgtProgressBar)
|
||||
DXE_EXPORT(wgtProgressBarV)
|
||||
DXE_EXPORT(wgtProgressBarSetValue)
|
||||
DXE_EXPORT(wgtProgressBarGetValue)
|
||||
|
||||
// dvxWidget.h -- slider
|
||||
DXE_EXPORT(wgtSlider)
|
||||
DXE_EXPORT(wgtSliderSetValue)
|
||||
DXE_EXPORT(wgtSliderGetValue)
|
||||
|
||||
// dvxWidget.h -- spinner
|
||||
DXE_EXPORT(wgtSpinner)
|
||||
DXE_EXPORT(wgtSpinnerSetValue)
|
||||
DXE_EXPORT(wgtSpinnerGetValue)
|
||||
DXE_EXPORT(wgtSpinnerSetRange)
|
||||
DXE_EXPORT(wgtSpinnerSetStep)
|
||||
|
||||
// dvxWidget.h -- tab control
|
||||
DXE_EXPORT(wgtTabControl)
|
||||
DXE_EXPORT(wgtTabPage)
|
||||
DXE_EXPORT(wgtTabControlSetActive)
|
||||
DXE_EXPORT(wgtTabControlGetActive)
|
||||
|
||||
// dvxWidget.h -- status bar / toolbar
|
||||
DXE_EXPORT(wgtStatusBar)
|
||||
DXE_EXPORT(wgtToolbar)
|
||||
|
||||
// dvxWidget.h -- tree view
|
||||
DXE_EXPORT(wgtTreeView)
|
||||
DXE_EXPORT(wgtTreeViewGetSelected)
|
||||
DXE_EXPORT(wgtTreeViewSetSelected)
|
||||
DXE_EXPORT(wgtTreeViewSetMultiSelect)
|
||||
DXE_EXPORT(wgtTreeViewSetReorderable)
|
||||
DXE_EXPORT(wgtTreeItem)
|
||||
DXE_EXPORT(wgtTreeItemSetExpanded)
|
||||
DXE_EXPORT(wgtTreeItemIsExpanded)
|
||||
DXE_EXPORT(wgtTreeItemIsSelected)
|
||||
DXE_EXPORT(wgtTreeItemSetSelected)
|
||||
|
||||
// dvxWidget.h -- timer
|
||||
DXE_EXPORT(wgtTimer)
|
||||
DXE_EXPORT(wgtTimerIsRunning)
|
||||
DXE_EXPORT(wgtTimerSetInterval)
|
||||
DXE_EXPORT(wgtTimerStart)
|
||||
DXE_EXPORT(wgtTimerStop)
|
||||
|
||||
// dvxWidget.h -- list view
|
||||
DXE_EXPORT(wgtListView)
|
||||
DXE_EXPORT(wgtListViewSetColumns)
|
||||
DXE_EXPORT(wgtListViewSetData)
|
||||
DXE_EXPORT(wgtListViewGetSelected)
|
||||
DXE_EXPORT(wgtListViewSetSelected)
|
||||
DXE_EXPORT(wgtListViewSetSort)
|
||||
DXE_EXPORT(wgtListViewSetHeaderClickCallback)
|
||||
DXE_EXPORT(wgtListViewSetMultiSelect)
|
||||
DXE_EXPORT(wgtListViewIsItemSelected)
|
||||
DXE_EXPORT(wgtListViewSetItemSelected)
|
||||
DXE_EXPORT(wgtListViewSelectAll)
|
||||
DXE_EXPORT(wgtListViewClearSelection)
|
||||
DXE_EXPORT(wgtListViewSetReorderable)
|
||||
|
||||
// dvxWidget.h -- scroll pane / splitter
|
||||
DXE_EXPORT(wgtScrollPane)
|
||||
DXE_EXPORT(wgtSplitter)
|
||||
DXE_EXPORT(wgtSplitterSetPos)
|
||||
DXE_EXPORT(wgtSplitterGetPos)
|
||||
|
||||
// dvxWidget.h -- image / image button
|
||||
DXE_EXPORT(wgtImageButton)
|
||||
DXE_EXPORT(wgtImageButtonFromFile)
|
||||
DXE_EXPORT(wgtImageButtonSetData)
|
||||
DXE_EXPORT(wgtImage)
|
||||
DXE_EXPORT(wgtImageFromFile)
|
||||
DXE_EXPORT(wgtImageSetData)
|
||||
|
||||
// dvxWidget.h -- canvas
|
||||
DXE_EXPORT(wgtCanvas)
|
||||
DXE_EXPORT(wgtCanvasClear)
|
||||
DXE_EXPORT(wgtCanvasSetMouseCallback)
|
||||
DXE_EXPORT(wgtCanvasSetPenColor)
|
||||
DXE_EXPORT(wgtCanvasSetPenSize)
|
||||
DXE_EXPORT(wgtCanvasSave)
|
||||
DXE_EXPORT(wgtCanvasLoad)
|
||||
DXE_EXPORT(wgtCanvasDrawLine)
|
||||
DXE_EXPORT(wgtCanvasDrawRect)
|
||||
DXE_EXPORT(wgtCanvasFillRect)
|
||||
DXE_EXPORT(wgtCanvasFillCircle)
|
||||
DXE_EXPORT(wgtCanvasSetPixel)
|
||||
DXE_EXPORT(wgtCanvasGetPixel)
|
||||
|
||||
// dvxWidget.h -- ANSI terminal
|
||||
DXE_EXPORT(wgtAnsiTerm)
|
||||
DXE_EXPORT(wgtAnsiTermWrite)
|
||||
DXE_EXPORT(wgtAnsiTermClear)
|
||||
DXE_EXPORT(wgtAnsiTermSetComm)
|
||||
DXE_EXPORT(wgtAnsiTermSetScrollback)
|
||||
DXE_EXPORT(wgtAnsiTermPoll)
|
||||
DXE_EXPORT(wgtAnsiTermRepaint)
|
||||
|
||||
// dvxWidget.h -- operations
|
||||
DXE_EXPORT(wgtInvalidate)
|
||||
DXE_EXPORT(wgtInvalidatePaint)
|
||||
DXE_EXPORT(wgtSetDebugLayout)
|
||||
DXE_EXPORT(wgtSetText)
|
||||
DXE_EXPORT(wgtSetTooltip)
|
||||
DXE_EXPORT(wgtGetText)
|
||||
DXE_EXPORT(wgtGetFocused)
|
||||
DXE_EXPORT(wgtSetEnabled)
|
||||
DXE_EXPORT(wgtSetFocused)
|
||||
DXE_EXPORT(wgtSetReadOnly)
|
||||
DXE_EXPORT(wgtSetVisible)
|
||||
DXE_EXPORT(wgtGetContext)
|
||||
DXE_EXPORT(wgtSetName)
|
||||
DXE_EXPORT(wgtFind)
|
||||
DXE_EXPORT(wgtDestroy)
|
||||
|
||||
// dvxWidget.h -- list box ops
|
||||
DXE_EXPORT(wgtListBoxSetItems)
|
||||
DXE_EXPORT(wgtListBoxGetSelected)
|
||||
DXE_EXPORT(wgtListBoxSetSelected)
|
||||
DXE_EXPORT(wgtListBoxSetMultiSelect)
|
||||
DXE_EXPORT(wgtListBoxIsItemSelected)
|
||||
DXE_EXPORT(wgtListBoxSetItemSelected)
|
||||
DXE_EXPORT(wgtListBoxSelectAll)
|
||||
DXE_EXPORT(wgtListBoxClearSelection)
|
||||
DXE_EXPORT(wgtListBoxSetReorderable)
|
||||
|
||||
// dvxWidget.h -- layout
|
||||
DXE_EXPORT(wgtResolveSize)
|
||||
DXE_EXPORT(wgtLayout)
|
||||
DXE_EXPORT(wgtPaint)
|
||||
|
||||
// taskswitch.h -- only yield and query functions are exported.
|
||||
// tsCreate/tsKill/etc. are NOT exported because apps should not
|
||||
// manipulate the task system directly -- the shell manages task
|
||||
// lifecycle through shellLoadApp/shellForceKillApp.
|
||||
DXE_EXPORT(tsActiveCount)
|
||||
DXE_EXPORT(tsCreate)
|
||||
DXE_EXPORT(tsCurrentId)
|
||||
DXE_EXPORT(tsGetName)
|
||||
DXE_EXPORT(tsGetPriority)
|
||||
DXE_EXPORT(tsGetState)
|
||||
DXE_EXPORT(tsKill)
|
||||
DXE_EXPORT(tsPause)
|
||||
DXE_EXPORT(tsResume)
|
||||
DXE_EXPORT(tsSetPriority)
|
||||
DXE_EXPORT(tsYield)
|
||||
|
||||
// dvxWm.h -- direct window management
|
||||
DXE_EXPORT(wmRaiseWindow)
|
||||
DXE_EXPORT(wmSetFocus)
|
||||
DXE_EXPORT(wmRestoreMinimized)
|
||||
|
||||
// Shell API
|
||||
DXE_EXPORT(shellConfigPath)
|
||||
DXE_EXPORT(shellEnsureConfigDir)
|
||||
DXE_EXPORT(shellGetApp)
|
||||
DXE_EXPORT(shellLoadApp)
|
||||
DXE_EXPORT(shellLog)
|
||||
DXE_EXPORT(shellTaskMgrOpen)
|
||||
DXE_EXPORT(shellForceKillApp)
|
||||
DXE_EXPORT(shellRunningAppCount)
|
||||
DXE_EXPORT(shellRegisterDesktopUpdate)
|
||||
DXE_EXPORT(shellUnregisterDesktopUpdate)
|
||||
DXE_EXPORT(shellGetSystemInfo)
|
||||
|
||||
// ================================================================
|
||||
// libc / libm exports. DXE3 modules are relocatable objects, not
|
||||
// fully linked executables. Every C library function a DXE calls
|
||||
// must appear here so the loader can resolve it at dlopen time.
|
||||
// This is intentionally comprehensive to avoid "unresolved symbol"
|
||||
// surprises when apps use standard functions.
|
||||
// ================================================================
|
||||
|
||||
// --- memory ---
|
||||
DXE_EXPORT(calloc)
|
||||
DXE_EXPORT(free)
|
||||
DXE_EXPORT(malloc)
|
||||
DXE_EXPORT(realloc)
|
||||
|
||||
// --- string / memory ops ---
|
||||
DXE_EXPORT(memchr)
|
||||
DXE_EXPORT(memcmp)
|
||||
DXE_EXPORT(memcpy)
|
||||
DXE_EXPORT(memmove)
|
||||
DXE_EXPORT(memset)
|
||||
DXE_EXPORT(strcasecmp)
|
||||
DXE_EXPORT(strcat)
|
||||
DXE_EXPORT(strchr)
|
||||
DXE_EXPORT(strcmp)
|
||||
DXE_EXPORT(strcpy)
|
||||
DXE_EXPORT(strcspn)
|
||||
DXE_EXPORT(strdup)
|
||||
DXE_EXPORT(strerror)
|
||||
DXE_EXPORT(strlen)
|
||||
DXE_EXPORT(strncasecmp)
|
||||
DXE_EXPORT(strncat)
|
||||
DXE_EXPORT(strncmp)
|
||||
DXE_EXPORT(strncpy)
|
||||
DXE_EXPORT(strpbrk)
|
||||
DXE_EXPORT(strrchr)
|
||||
DXE_EXPORT(strspn)
|
||||
DXE_EXPORT(strstr)
|
||||
DXE_EXPORT(strtok)
|
||||
|
||||
// --- ctype ---
|
||||
DXE_EXPORT(isalnum)
|
||||
DXE_EXPORT(isalpha)
|
||||
DXE_EXPORT(isdigit)
|
||||
DXE_EXPORT(islower)
|
||||
DXE_EXPORT(isprint)
|
||||
DXE_EXPORT(ispunct)
|
||||
DXE_EXPORT(isspace)
|
||||
DXE_EXPORT(isupper)
|
||||
DXE_EXPORT(isxdigit)
|
||||
DXE_EXPORT(tolower)
|
||||
DXE_EXPORT(toupper)
|
||||
|
||||
// --- conversion ---
|
||||
DXE_EXPORT(abs)
|
||||
DXE_EXPORT(atof)
|
||||
DXE_EXPORT(atoi)
|
||||
DXE_EXPORT(atol)
|
||||
DXE_EXPORT(labs)
|
||||
DXE_EXPORT(strtod)
|
||||
DXE_EXPORT(strtol)
|
||||
DXE_EXPORT(strtoul)
|
||||
|
||||
// --- formatted I/O ---
|
||||
DXE_EXPORT(fprintf)
|
||||
DXE_EXPORT(fputs)
|
||||
DXE_EXPORT(fscanf)
|
||||
DXE_EXPORT(printf)
|
||||
DXE_EXPORT(puts)
|
||||
DXE_EXPORT(snprintf)
|
||||
DXE_EXPORT(sprintf)
|
||||
DXE_EXPORT(sscanf)
|
||||
DXE_EXPORT(vfprintf)
|
||||
DXE_EXPORT(vprintf)
|
||||
DXE_EXPORT(vsnprintf)
|
||||
DXE_EXPORT(vsprintf)
|
||||
|
||||
// --- character I/O ---
|
||||
DXE_EXPORT(fgetc)
|
||||
DXE_EXPORT(fgets)
|
||||
DXE_EXPORT(fputc)
|
||||
DXE_EXPORT(getc)
|
||||
DXE_EXPORT(putc)
|
||||
DXE_EXPORT(putchar)
|
||||
DXE_EXPORT(ungetc)
|
||||
|
||||
// --- file I/O ---
|
||||
DXE_EXPORT(fclose)
|
||||
DXE_EXPORT(feof)
|
||||
DXE_EXPORT(ferror)
|
||||
DXE_EXPORT(fflush)
|
||||
DXE_EXPORT(fopen)
|
||||
DXE_EXPORT(fread)
|
||||
DXE_EXPORT(freopen)
|
||||
DXE_EXPORT(fseek)
|
||||
DXE_EXPORT(ftell)
|
||||
DXE_EXPORT(fwrite)
|
||||
DXE_EXPORT(remove)
|
||||
DXE_EXPORT(rename)
|
||||
DXE_EXPORT(rewind)
|
||||
DXE_EXPORT(tmpfile)
|
||||
DXE_EXPORT(tmpnam)
|
||||
|
||||
// --- directory ---
|
||||
DXE_EXPORT(closedir)
|
||||
DXE_EXPORT(mkdir)
|
||||
DXE_EXPORT(opendir)
|
||||
DXE_EXPORT(readdir)
|
||||
DXE_EXPORT(rmdir)
|
||||
|
||||
// --- filesystem ---
|
||||
DXE_EXPORT(access)
|
||||
DXE_EXPORT(chdir)
|
||||
DXE_EXPORT(getcwd)
|
||||
DXE_EXPORT(stat)
|
||||
DXE_EXPORT(unlink)
|
||||
|
||||
// --- time ---
|
||||
DXE_EXPORT(clock)
|
||||
DXE_EXPORT(difftime)
|
||||
DXE_EXPORT(gmtime)
|
||||
DXE_EXPORT(localtime)
|
||||
DXE_EXPORT(mktime)
|
||||
DXE_EXPORT(strftime)
|
||||
DXE_EXPORT(time)
|
||||
|
||||
// --- process / environment ---
|
||||
DXE_EXPORT(abort)
|
||||
DXE_EXPORT(atexit)
|
||||
DXE_EXPORT(exit)
|
||||
DXE_EXPORT(getenv)
|
||||
DXE_EXPORT(system)
|
||||
|
||||
// --- sorting / searching ---
|
||||
DXE_EXPORT(bsearch)
|
||||
DXE_EXPORT(qsort)
|
||||
|
||||
// --- random ---
|
||||
DXE_EXPORT(rand)
|
||||
DXE_EXPORT(srand)
|
||||
|
||||
// --- setjmp / signal ---
|
||||
DXE_EXPORT(longjmp)
|
||||
DXE_EXPORT(setjmp)
|
||||
DXE_EXPORT(signal)
|
||||
|
||||
// --- libm ---
|
||||
DXE_EXPORT(acos)
|
||||
DXE_EXPORT(asin)
|
||||
DXE_EXPORT(atan)
|
||||
DXE_EXPORT(atan2)
|
||||
DXE_EXPORT(ceil)
|
||||
DXE_EXPORT(cos)
|
||||
DXE_EXPORT(exp)
|
||||
DXE_EXPORT(fabs)
|
||||
DXE_EXPORT(floor)
|
||||
DXE_EXPORT(fmod)
|
||||
DXE_EXPORT(frexp)
|
||||
DXE_EXPORT(ldexp)
|
||||
DXE_EXPORT(log)
|
||||
DXE_EXPORT(log10)
|
||||
DXE_EXPORT(modf)
|
||||
DXE_EXPORT(pow)
|
||||
DXE_EXPORT(sin)
|
||||
DXE_EXPORT(sqrt)
|
||||
DXE_EXPORT(tan)
|
||||
|
||||
// --- errno ---
|
||||
DXE_EXPORT(errno)
|
||||
|
||||
// --- libgcc 64-bit integer math helpers ---
|
||||
// GCC emits calls to these for int64_t division/modulo on 32-bit targets.
|
||||
// Without them, any DXE using 64-bit arithmetic gets unresolved symbols.
|
||||
DXE_EXPORT(__divdi3)
|
||||
DXE_EXPORT(__moddi3)
|
||||
DXE_EXPORT(__muldi3)
|
||||
DXE_EXPORT(__udivdi3)
|
||||
DXE_EXPORT(__udivmoddi4)
|
||||
DXE_EXPORT(__umoddi3)
|
||||
|
||||
// --- DJGPP stdio internals ---
|
||||
// The stdin/stdout/stderr macros in DJGPP expand to pointers to
|
||||
// these FILE structs. Without them, any DXE that does printf()
|
||||
// or fprintf(stderr, ...) gets an unresolved symbol at load time.
|
||||
DXE_EXPORT(__dj_stdin)
|
||||
DXE_EXPORT(__dj_stdout)
|
||||
DXE_EXPORT(__dj_stderr)
|
||||
|
||||
// --- stb_ds (dynamic arrays / hashmaps) ---
|
||||
// Internal functions called by the arrput/arrfree/hm* macros.
|
||||
// Implementation lives in libtasks.a.
|
||||
DXE_EXPORT(stbds_arrfreef)
|
||||
DXE_EXPORT(stbds_arrgrowf)
|
||||
DXE_EXPORT(stbds_hash_bytes)
|
||||
DXE_EXPORT(stbds_hash_string)
|
||||
DXE_EXPORT(stbds_hmdel_key)
|
||||
DXE_EXPORT(stbds_hmfree_func)
|
||||
DXE_EXPORT(stbds_hmget_key)
|
||||
DXE_EXPORT(stbds_hmget_key_ts)
|
||||
DXE_EXPORT(stbds_hmput_default)
|
||||
DXE_EXPORT(stbds_hmput_key)
|
||||
DXE_EXPORT(stbds_rand_seed)
|
||||
DXE_EXPORT(stbds_shmode_func)
|
||||
DXE_EXPORT(stbds_stralloc)
|
||||
DXE_EXPORT(stbds_strreset)
|
||||
|
||||
// --- stb_image (image loading) ---
|
||||
DXE_EXPORT(stbi_failure_reason)
|
||||
DXE_EXPORT(stbi_image_free)
|
||||
DXE_EXPORT(stbi_info)
|
||||
DXE_EXPORT(stbi_info_from_memory)
|
||||
DXE_EXPORT(stbi_load)
|
||||
DXE_EXPORT(stbi_load_from_memory)
|
||||
DXE_EXPORT(stbi_set_flip_vertically_on_load)
|
||||
|
||||
// --- stb_image_write ---
|
||||
DXE_EXPORT(stbi_write_bmp)
|
||||
DXE_EXPORT(stbi_write_png)
|
||||
DXE_EXPORT(stbi_write_jpg)
|
||||
DXE_EXPORT(stbi_write_tga)
|
||||
DXE_EXPORT_END
|
||||
|
||||
|
||||
// ============================================================
|
||||
// shellRegisterExports
|
||||
// ============================================================
|
||||
|
||||
// dlregsym registers our export table with DJGPP's DXE3 runtime.
|
||||
// Must be called once before any dlopen -- subsequent dlopen calls
|
||||
// will search this table to resolve DXE symbol references.
|
||||
static void shellRegisterExports(void) {
|
||||
dlregsym(shellExportTable);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// Public init function
|
||||
// ============================================================
|
||||
|
||||
void shellExportInit(void) {
|
||||
shellRegisterExports();
|
||||
}
|
||||
54
loader/Makefile
Normal file
54
loader/Makefile
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# DVX Loader Makefile for DJGPP cross-compilation
|
||||
#
|
||||
# Builds the bootstrap loader (dvx.exe) that loads DXE modules.
|
||||
# Links dvxPlatformDos.c directly -- the platform layer provides
|
||||
# the DXE export table via platformRegisterDxeExports().
|
||||
|
||||
DJGPP_PREFIX = $(HOME)/djgpp/djgpp
|
||||
DJGPP_LIBPATH = $(HOME)/claude/windriver/tools/lib
|
||||
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
|
||||
EXE2COFF = $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/exe2coff
|
||||
CWSDSTUB = $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/CWSDSTUB.EXE
|
||||
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../core -I../core/platform -I../tasks -I../tasks/thirdparty
|
||||
LDFLAGS = -lm
|
||||
|
||||
OBJDIR = ../obj/loader
|
||||
POBJDIR = ../obj/loader/platform
|
||||
BINDIR = ../bin
|
||||
|
||||
SRCS = loaderMain.c
|
||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||
POBJS = $(POBJDIR)/dvxPlatformDos.o
|
||||
TARGET = $(BINDIR)/dvx.exe
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS) $(POBJS) | $(BINDIR)
|
||||
$(CC) $(CFLAGS) -o $@ $(OBJS) $(POBJS) $(LDFLAGS) -Wl,-Map=$(BINDIR)/dvx.map
|
||||
$(EXE2COFF) $@
|
||||
cat $(CWSDSTUB) $(BINDIR)/dvx > $@
|
||||
rm -f $(BINDIR)/dvx
|
||||
|
||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(POBJDIR)/dvxPlatformDos.o: ../core/platform/dvxPlatformDos.c | $(POBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR):
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
$(POBJDIR):
|
||||
mkdir -p $(POBJDIR)
|
||||
|
||||
$(BINDIR):
|
||||
mkdir -p $(BINDIR)
|
||||
|
||||
# Dependencies
|
||||
$(OBJDIR)/loaderMain.o: loaderMain.c ../core/platform/dvxPlatform.h ../core/dvxTypes.h
|
||||
$(POBJDIR)/dvxPlatformDos.o: ../core/platform/dvxPlatformDos.c ../core/platform/dvxPlatform.h ../core/dvxTypes.h ../core/dvxPalette.h
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(POBJS) $(TARGET) $(BINDIR)/dvx.map
|
||||
394
loader/loaderMain.c
Normal file
394
loader/loaderMain.c
Normal file
|
|
@ -0,0 +1,394 @@
|
|||
// loaderMain.c -- DVX bootstrap loader entry point
|
||||
//
|
||||
// Loads all DXE modules from two directories:
|
||||
// libs/ *.lib -- core libraries (libtasks, libdvx, dvxshell)
|
||||
// widgets/ *.wgt -- widget type plugins (box, button, listview, etc.)
|
||||
//
|
||||
// Each module may have a .dep file (same base name, .dep extension)
|
||||
// listing base names of modules that must be loaded before it.
|
||||
// The loader resolves the dependency graph and loads in topological
|
||||
// order. After loading, any module that exports wgtRegister() has
|
||||
// it called. Finally, the loader finds and calls shellMain().
|
||||
|
||||
#include "dvxPlatform.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define STB_DS_IMPLEMENTATION
|
||||
#include "stb_ds.h"
|
||||
|
||||
// ============================================================
|
||||
// Constants
|
||||
// ============================================================
|
||||
|
||||
#define LIBS_DIR "LIBS"
|
||||
#define WIDGET_DIR "WIDGETS"
|
||||
|
||||
// ============================================================
|
||||
// Module entry for dependency resolution
|
||||
// ============================================================
|
||||
|
||||
typedef struct {
|
||||
char path[260];
|
||||
char baseName[16];
|
||||
char **deps;
|
||||
bool loaded;
|
||||
void *handle;
|
||||
} ModuleT;
|
||||
|
||||
// ============================================================
|
||||
// Prototypes
|
||||
// ============================================================
|
||||
|
||||
static bool allDepsLoaded(const ModuleT *mod, const ModuleT *mods);
|
||||
static void extractBaseName(const char *path, const char *ext, char *out, int32_t outSize);
|
||||
static void *findSymbol(void **handles, const char *symbol);
|
||||
static void **loadAllModules(void);
|
||||
static void loadInOrder(ModuleT *mods);
|
||||
static void readDeps(ModuleT *mod);
|
||||
static void scanDir(const char *dirPath, const char *ext, ModuleT **mods);
|
||||
|
||||
// ============================================================
|
||||
// extractBaseName -- strip directory and extension, lowercase
|
||||
// ============================================================
|
||||
|
||||
static void extractBaseName(const char *path, const char *ext, char *out, int32_t outSize) {
|
||||
// Find last directory separator
|
||||
const char *start = path;
|
||||
const char *p = path;
|
||||
|
||||
while (*p) {
|
||||
if (*p == '/' || *p == '\\') {
|
||||
start = p + 1;
|
||||
}
|
||||
|
||||
p++;
|
||||
}
|
||||
|
||||
// Copy up to the extension
|
||||
int32_t extLen = strlen(ext);
|
||||
int32_t len = strlen(start);
|
||||
|
||||
if (len > extLen && strcasecmp(start + len - extLen, ext) == 0) {
|
||||
len -= extLen;
|
||||
}
|
||||
|
||||
if (len >= outSize) {
|
||||
len = outSize - 1;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < len; i++) {
|
||||
out[i] = tolower((unsigned char)start[i]);
|
||||
}
|
||||
|
||||
out[len] = '\0';
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// readDeps -- parse .dep file for a module
|
||||
// ============================================================
|
||||
//
|
||||
// The .dep file has the same path as the module but with a .dep
|
||||
// extension. Each line is a dependency base name. Empty lines
|
||||
// and lines starting with # are ignored.
|
||||
|
||||
static void readDeps(ModuleT *mod) {
|
||||
// Build dep file path: replace extension with .dep
|
||||
char depPath[260];
|
||||
strncpy(depPath, mod->path, sizeof(depPath) - 1);
|
||||
depPath[sizeof(depPath) - 1] = '\0';
|
||||
|
||||
char *dot = strrchr(depPath, '.');
|
||||
|
||||
if (!dot) {
|
||||
return;
|
||||
}
|
||||
|
||||
strcpy(dot, ".dep");
|
||||
|
||||
FILE *f = fopen(depPath, "r");
|
||||
|
||||
if (!f) {
|
||||
return;
|
||||
}
|
||||
|
||||
char line[64];
|
||||
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
// Strip \r and \n
|
||||
int32_t len = strlen(line);
|
||||
|
||||
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) {
|
||||
line[--len] = '\0';
|
||||
}
|
||||
|
||||
// Skip empty lines and comments
|
||||
if (len == 0 || line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Lowercase the dep name for case-insensitive matching
|
||||
for (int32_t i = 0; i < len; i++) {
|
||||
line[i] = tolower((unsigned char)line[i]);
|
||||
}
|
||||
|
||||
arrput(mod->deps, strdup(line));
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// scanDir -- recursively find modules with a given extension
|
||||
// ============================================================
|
||||
|
||||
static void scanDir(const char *dirPath, const char *ext, ModuleT **mods) {
|
||||
DIR *dir = opendir(dirPath);
|
||||
|
||||
if (!dir) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct dirent *ent;
|
||||
|
||||
while ((ent = readdir(dir)) != NULL) {
|
||||
const char *name = ent->d_name;
|
||||
|
||||
// Skip . and ..
|
||||
if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char path[260];
|
||||
snprintf(path, sizeof(path), "%s/%s", dirPath, name);
|
||||
|
||||
// Check for matching extension
|
||||
int32_t nameLen = strlen(name);
|
||||
int32_t extLen = strlen(ext);
|
||||
|
||||
if (nameLen > extLen && strcasecmp(name + nameLen - extLen, ext) == 0) {
|
||||
ModuleT mod;
|
||||
memset(&mod, 0, sizeof(mod));
|
||||
strncpy(mod.path, path, sizeof(mod.path) - 1);
|
||||
extractBaseName(path, ext, mod.baseName, sizeof(mod.baseName));
|
||||
arrput(*mods, mod);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Recurse into subdirectories
|
||||
struct stat st;
|
||||
|
||||
if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||
scanDir(path, ext, mods);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// allDepsLoaded -- check if a module's dependencies are satisfied
|
||||
// ============================================================
|
||||
//
|
||||
// A dep is satisfied if either:
|
||||
// 1. No module with that base name exists (external, assumed OK)
|
||||
// 2. The module with that base name is already loaded
|
||||
|
||||
static bool allDepsLoaded(const ModuleT *mod, const ModuleT *mods) {
|
||||
for (int32_t d = 0; d < arrlen(mod->deps); d++) {
|
||||
for (int32_t j = 0; j < arrlen(mods); j++) {
|
||||
if (strcasecmp(mods[j].baseName, mod->deps[d]) == 0) {
|
||||
if (!mods[j].loaded) {
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// loadInOrder -- topological sort and load
|
||||
// ============================================================
|
||||
//
|
||||
// Repeatedly scans the module list for entries whose dependencies
|
||||
// are all satisfied, loads them, and marks them done. Stops when
|
||||
// all modules are loaded or no progress can be made (circular dep).
|
||||
|
||||
static void loadInOrder(ModuleT *mods) {
|
||||
typedef void (*RegFnT)(void);
|
||||
int32_t total = arrlen(mods);
|
||||
int32_t loaded = 0;
|
||||
bool progress;
|
||||
|
||||
do {
|
||||
progress = false;
|
||||
|
||||
for (int32_t i = 0; i < total; i++) {
|
||||
if (mods[i].loaded) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!allDepsLoaded(&mods[i], mods)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mods[i].handle = dlopen(mods[i].path, RTLD_GLOBAL);
|
||||
|
||||
if (mods[i].handle) {
|
||||
RegFnT regFn = (RegFnT)dlsym(mods[i].handle, "_wgtRegister");
|
||||
|
||||
if (regFn) {
|
||||
regFn();
|
||||
}
|
||||
}
|
||||
|
||||
mods[i].loaded = true;
|
||||
loaded++;
|
||||
progress = true;
|
||||
}
|
||||
} while (progress && loaded < total);
|
||||
|
||||
if (loaded < total) {
|
||||
fprintf(stderr, "Module loader: %d of %d modules could not be loaded (circular deps or missing deps)\n", total - loaded, total);
|
||||
|
||||
for (int32_t i = 0; i < total; i++) {
|
||||
if (!mods[i].loaded) {
|
||||
fprintf(stderr, " %s\n", mods[i].path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// loadAllModules -- scan libs/ and widgets/, load in dep order
|
||||
// ============================================================
|
||||
//
|
||||
// Returns a stb_ds dynamic array of dlopen handles.
|
||||
|
||||
static void **loadAllModules(void) {
|
||||
ModuleT *mods = NULL;
|
||||
|
||||
// Discover all modules
|
||||
scanDir(LIBS_DIR, ".lib", &mods);
|
||||
scanDir(WIDGET_DIR, ".wgt", &mods);
|
||||
|
||||
// Read dependency files
|
||||
for (int32_t i = 0; i < arrlen(mods); i++) {
|
||||
readDeps(&mods[i]);
|
||||
}
|
||||
|
||||
// Load in dependency order
|
||||
loadInOrder(mods);
|
||||
|
||||
// Collect handles
|
||||
void **handles = NULL;
|
||||
|
||||
for (int32_t i = 0; i < arrlen(mods); i++) {
|
||||
if (mods[i].handle) {
|
||||
arrput(handles, mods[i].handle);
|
||||
}
|
||||
}
|
||||
|
||||
// Free dep arrays
|
||||
for (int32_t i = 0; i < arrlen(mods); i++) {
|
||||
for (int32_t d = 0; d < arrlen(mods[i].deps); d++) {
|
||||
free(mods[i].deps[d]);
|
||||
}
|
||||
|
||||
arrfree(mods[i].deps);
|
||||
}
|
||||
|
||||
arrfree(mods);
|
||||
return handles;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// findSymbol -- search loaded handles for a symbol
|
||||
// ============================================================
|
||||
|
||||
static void *findSymbol(void **handles, const char *symbol) {
|
||||
for (int32_t i = 0; i < arrlen(handles); i++) {
|
||||
void *sym = dlsym(handles[i], symbol);
|
||||
|
||||
if (sym) {
|
||||
return sym;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// main
|
||||
// ============================================================
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Change to the directory containing the executable so relative
|
||||
// paths (LIBS/, WIDGETS/, APPS/, CONFIG/) resolve correctly.
|
||||
char exeDir[260];
|
||||
strncpy(exeDir, argv[0], sizeof(exeDir) - 1);
|
||||
exeDir[sizeof(exeDir) - 1] = '\0';
|
||||
|
||||
char *sep = platformPathDirEnd(exeDir);
|
||||
|
||||
if (sep) {
|
||||
*sep = '\0';
|
||||
platformChdir(exeDir);
|
||||
}
|
||||
|
||||
// Register platform + libc/libm/runtime symbols for DXE resolution
|
||||
platformRegisterDxeExports();
|
||||
|
||||
// Load all modules from libs/ and widgets/ in dependency order.
|
||||
// Each module may have a .dep file specifying load-before deps.
|
||||
// Widget modules that export wgtRegister() get it called.
|
||||
void **handles = loadAllModules();
|
||||
|
||||
if (!handles || arrlen(handles) == 0) {
|
||||
fprintf(stderr, "No modules loaded from %s/ or %s/\n", LIBS_DIR, WIDGET_DIR);
|
||||
arrfree(handles);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Find and call shellMain from whichever module exports it
|
||||
typedef int (*ShellMainFnT)(int, char **);
|
||||
ShellMainFnT shellMain = (ShellMainFnT)findSymbol(handles, "_shellMain");
|
||||
|
||||
if (!shellMain) {
|
||||
fprintf(stderr, "No module exports shellMain\n");
|
||||
|
||||
for (int32_t i = arrlen(handles) - 1; i >= 0; i--) {
|
||||
dlclose(handles[i]);
|
||||
}
|
||||
|
||||
arrfree(handles);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int result = shellMain(argc, argv);
|
||||
|
||||
// Clean up in reverse load order
|
||||
for (int32_t i = arrlen(handles) - 1; i >= 0; i--) {
|
||||
dlclose(handles[i]);
|
||||
}
|
||||
|
||||
arrfree(handles);
|
||||
return result;
|
||||
}
|
||||
19
mkcd.sh
19
mkcd.sh
|
|
@ -20,11 +20,20 @@ ISO_PATH="$ISO_DIR/dvx.iso"
|
|||
echo "Building DVX..."
|
||||
make -C "$SCRIPT_DIR" all
|
||||
|
||||
# Verify build output exists
|
||||
if [ ! -f "$SCRIPT_DIR/bin/dvx.exe" ]; then
|
||||
echo "ERROR: bin/dvx.exe not found -- build failed?"
|
||||
exit 1
|
||||
fi
|
||||
# Verify core build output exists
|
||||
for f in dvx.exe libs/libtasks.lib libs/libdvx.lib libs/dvxshell.lib; do
|
||||
if [ ! -f "$SCRIPT_DIR/bin/$f" ]; then
|
||||
echo "ERROR: bin/$f not found -- build failed?"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Verify widget DXEs exist
|
||||
WGT_COUNT=0
|
||||
for f in "$SCRIPT_DIR"/bin/widgets/*.wgt; do
|
||||
[ -f "$f" ] && WGT_COUNT=$((WGT_COUNT + 1))
|
||||
done
|
||||
echo "$WGT_COUNT widget modules found in bin/widgets/."
|
||||
|
||||
# Create the ISO image
|
||||
# -iso-level 1: strict 8.3 filenames (DOS compatibility)
|
||||
|
|
|
|||
69
shell/Makefile
Normal file
69
shell/Makefile
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
# DVX Shell Makefile for DJGPP cross-compilation
|
||||
#
|
||||
# Builds dvxshell.lib -- the shell module loaded by the DVX loader.
|
||||
|
||||
DJGPP_PREFIX = $(HOME)/djgpp/djgpp
|
||||
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
|
||||
DXE3GEN = PATH=$(DJGPP_PREFIX)/bin:$(PATH) DJDIR=$(DJGPP_PREFIX)/i586-pc-msdosdjgpp $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/dxe3gen
|
||||
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../core -I../core/platform -I../tasks -I../tasks/thirdparty
|
||||
|
||||
OBJDIR = ../obj/shell
|
||||
LIBSDIR = ../bin/libs
|
||||
CONFIGDIR = ../bin/config
|
||||
THEMEDIR = ../bin/config/themes
|
||||
WPAPERDIR = ../bin/config/wpaper
|
||||
|
||||
SRCS = shellMain.c shellApp.c shellExport.c shellInfo.c shellTaskMgr.c
|
||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||
TARGET = $(LIBSDIR)/dvxshell.lib
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
THEMES = $(THEMEDIR)/geos.thm $(THEMEDIR)/win31.thm $(THEMEDIR)/cde.thm
|
||||
WPAPERS = $(WPAPERDIR)/blueglow.jpg $(WPAPERDIR)/swoop.jpg $(WPAPERDIR)/triangle.jpg
|
||||
|
||||
all: $(TARGET) $(LIBSDIR)/dvxshell.dep $(CONFIGDIR)/dvx.ini $(THEMES) $(WPAPERS)
|
||||
|
||||
$(LIBSDIR)/dvxshell.dep: ../config/dvxshell.dep | $(LIBSDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
|
||||
$(TARGET): $(OBJS) | $(LIBSDIR)
|
||||
$(DXE3GEN) -o $@ -E _shell -U $(OBJS)
|
||||
|
||||
$(CONFIGDIR)/dvx.ini: ../config/dvx.ini | $(CONFIGDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
|
||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR):
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
$(LIBSDIR):
|
||||
mkdir -p $(LIBSDIR)
|
||||
|
||||
$(CONFIGDIR):
|
||||
mkdir -p $(CONFIGDIR)
|
||||
|
||||
$(THEMEDIR):
|
||||
mkdir -p $(THEMEDIR)
|
||||
|
||||
$(THEMEDIR)/%.thm: ../config/themes/%.thm | $(THEMEDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
|
||||
$(WPAPERDIR):
|
||||
mkdir -p $(WPAPERDIR)
|
||||
|
||||
$(WPAPERDIR)/%.jpg: ../config/wpaper/%.jpg | $(WPAPERDIR)
|
||||
cp $< $@
|
||||
|
||||
# Dependencies
|
||||
$(OBJDIR)/shellMain.o: shellMain.c shellApp.h
|
||||
$(OBJDIR)/shellApp.o: shellApp.c shellApp.h
|
||||
$(OBJDIR)/shellExport.o: shellExport.c shellApp.h
|
||||
$(OBJDIR)/shellInfo.o: shellInfo.c shellInfo.h shellApp.h
|
||||
$(OBJDIR)/shellTaskMgr.o: shellTaskMgr.c shellTaskMgr.h shellApp.h
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(TARGET) $(LIBSDIR)/dvxshell.dep
|
||||
rm -rf $(WPAPERDIR) $(THEMEDIR) $(CONFIGDIR)
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#include "shellApp.h"
|
||||
#include "dvxDialog.h"
|
||||
#include "platform/dvxPlatform.h"
|
||||
#include "dvxPlatform.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
126
shell/shellExport.c
Normal file
126
shell/shellExport.c
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
// shellExport.c -- DXE wrapper overrides for resource tracking
|
||||
//
|
||||
// Registers three wrapper functions via dlregsym that override the
|
||||
// real dvxCreateWindow, dvxCreateWindowCentered, and dvxDestroyWindow
|
||||
// for all subsequently loaded app DXEs.
|
||||
//
|
||||
// The key mechanic: dlregsym takes precedence over RTLD_GLOBAL exports.
|
||||
// Since libdvx.dxe (which has the real functions) was loaded before
|
||||
// shellExportInit() registers these wrappers, libdvx.dxe keeps the
|
||||
// real implementations. But any app DXE loaded afterward gets the
|
||||
// wrappers, which add resource tracking (appId stamping, last-window
|
||||
// reaping) transparently.
|
||||
//
|
||||
// All other symbol exports (dvx*, wgt*, platform*, libc) are handled
|
||||
// by the DXE modules and the loader's dlregsym table -- they no longer
|
||||
// need to be listed here.
|
||||
|
||||
#include "shellApp.h"
|
||||
#include "dvxApp.h"
|
||||
#include "dvxWm.h"
|
||||
|
||||
#include <sys/dxe.h>
|
||||
|
||||
// ============================================================
|
||||
// Prototypes
|
||||
// ============================================================
|
||||
|
||||
static WindowT *shellWrapCreateWindow(AppContextT *ctx, const char *title, int32_t x, int32_t y, int32_t w, int32_t h, bool resizable);
|
||||
static WindowT *shellWrapCreateWindowCentered(AppContextT *ctx, const char *title, int32_t w, int32_t h, bool resizable);
|
||||
static void shellWrapDestroyWindow(AppContextT *ctx, WindowT *win);
|
||||
|
||||
// ============================================================
|
||||
// Wrapper: dvxCreateWindow -- stamps win->appId
|
||||
// ============================================================
|
||||
|
||||
// The wrapper calls the real dvxCreateWindow (resolved from libdvx.dxe
|
||||
// at shellcore load time), then tags the result with sCurrentAppId.
|
||||
// This is how the shell knows which app owns which window, enabling
|
||||
// per-app window cleanup on crash/termination.
|
||||
static WindowT *shellWrapCreateWindow(AppContextT *ctx, const char *title, int32_t x, int32_t y, int32_t w, int32_t h, bool resizable) {
|
||||
WindowT *win = dvxCreateWindow(ctx, title, x, y, w, h, resizable);
|
||||
|
||||
if (win) {
|
||||
win->appId = sCurrentAppId;
|
||||
}
|
||||
|
||||
return win;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// Wrapper: dvxCreateWindowCentered -- stamps win->appId
|
||||
// ============================================================
|
||||
|
||||
static WindowT *shellWrapCreateWindowCentered(AppContextT *ctx, const char *title, int32_t w, int32_t h, bool resizable) {
|
||||
WindowT *win = dvxCreateWindowCentered(ctx, title, w, h, resizable);
|
||||
|
||||
if (win) {
|
||||
win->appId = sCurrentAppId;
|
||||
}
|
||||
|
||||
return win;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// Wrapper: dvxDestroyWindow -- checks for last-window reap
|
||||
// ============================================================
|
||||
|
||||
// Beyond just destroying the window, this wrapper implements the lifecycle
|
||||
// rule for callback-only apps: when their last window closes, they're done.
|
||||
// Main-loop apps manage their own lifetime (their task returns from
|
||||
// appMain), so this check only applies to callback-only apps.
|
||||
// The appId is captured before destruction because the window struct is
|
||||
// freed by dvxDestroyWindow.
|
||||
static void shellWrapDestroyWindow(AppContextT *ctx, WindowT *win) {
|
||||
int32_t appId = win->appId;
|
||||
|
||||
dvxDestroyWindow(ctx, win);
|
||||
|
||||
// If this was a callback-only app's last window, mark for reaping
|
||||
if (appId > 0) {
|
||||
ShellAppT *app = shellGetApp(appId);
|
||||
|
||||
if (app && !app->hasMainLoop && app->state == AppStateRunningE) {
|
||||
// Check if app still has any windows
|
||||
bool hasWindows = false;
|
||||
|
||||
for (int32_t i = 0; i < ctx->stack.count; i++) {
|
||||
if (ctx->stack.windows[i]->appId == appId) {
|
||||
hasWindows = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasWindows) {
|
||||
app->state = AppStateTerminatingE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// Wrapper export table
|
||||
// ============================================================
|
||||
|
||||
// Only three entries: the resource-tracking wrappers exported under
|
||||
// the original function names. All other symbols come from the
|
||||
// loaded DXE modules (via RTLD_GLOBAL) and the loader's dlregsym.
|
||||
DXE_EXPORT_TABLE(sWrapperTable)
|
||||
{ "_dvxCreateWindow", (void *)shellWrapCreateWindow },
|
||||
{ "_dvxCreateWindowCentered", (void *)shellWrapCreateWindowCentered },
|
||||
{ "_dvxDestroyWindow", (void *)shellWrapDestroyWindow },
|
||||
DXE_EXPORT_END
|
||||
|
||||
|
||||
// ============================================================
|
||||
// shellExportInit
|
||||
// ============================================================
|
||||
|
||||
// Register the wrapper overrides. Must be called before any
|
||||
// shellLoadApp() -- wrappers only affect subsequently loaded DXEs.
|
||||
void shellExportInit(void) {
|
||||
dlregsym(sWrapperTable);
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#include "shellInfo.h"
|
||||
#include "shellApp.h"
|
||||
#include "platform/dvxPlatform.h"
|
||||
#include "dvxPlatform.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -29,8 +29,8 @@
|
|||
#include "shellTaskMgr.h"
|
||||
#include "dvxDialog.h"
|
||||
#include "dvxPrefs.h"
|
||||
#include "platform/dvxPlatform.h"
|
||||
#include "thirdparty/stb_ds.h"
|
||||
#include "dvxPlatform.h"
|
||||
#include "stb_ds.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <setjmp.h>
|
||||
|
|
@ -261,25 +261,12 @@ void shellUnregisterDesktopUpdate(void (*updateFn)(void)) {
|
|||
|
||||
|
||||
// ============================================================
|
||||
// main
|
||||
// shellMain -- entry point called by the DVX loader
|
||||
// ============================================================
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int shellMain(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
|
||||
// Change to the directory containing the executable so that relative
|
||||
// paths (CONFIG/, APPS/, etc.) resolve correctly regardless of where
|
||||
// the user launched from.
|
||||
char exeDir[260];
|
||||
strncpy(exeDir, argv[0], sizeof(exeDir) - 1);
|
||||
exeDir[sizeof(exeDir) - 1] = '\0';
|
||||
|
||||
char *sep = platformPathDirEnd(exeDir);
|
||||
|
||||
if (sep) {
|
||||
*sep = '\0';
|
||||
platformChdir(exeDir);
|
||||
}
|
||||
(void)argv;
|
||||
|
||||
// Truncate the log file, then use append-per-write so the file
|
||||
// isn't held open (allows Notepad to read it while the shell runs).
|
||||
|
|
@ -9,8 +9,8 @@
|
|||
#include "dvxDialog.h"
|
||||
#include "dvxWidget.h"
|
||||
#include "dvxWm.h"
|
||||
#include "platform/dvxPlatform.h"
|
||||
#include "thirdparty/stb_ds.h"
|
||||
#include "dvxPlatform.h"
|
||||
#include "stb_ds.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
|
@ -1,41 +1,25 @@
|
|||
# Cooperative Task Switching Library Makefile for DJGPP cross-compilation
|
||||
#
|
||||
# Builds libtasks.lib -- the task switching module loaded by the DVX loader.
|
||||
|
||||
DJGPP_PREFIX = $(HOME)/djgpp/djgpp
|
||||
DJGPP_LIBPATH = $(HOME)/claude/windriver/tools/lib
|
||||
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
|
||||
AR = LD_LIBRARY_PATH=$(DJGPP_LIBPATH) $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-ar
|
||||
RANLIB = LD_LIBRARY_PATH=$(DJGPP_LIBPATH) $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-ranlib
|
||||
EXE2COFF = $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/exe2coff
|
||||
CWSDSTUB = $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/CWSDSTUB.EXE
|
||||
DXE3GEN = PATH=$(DJGPP_PREFIX)/bin:$(PATH) DJDIR=$(DJGPP_PREFIX)/i586-pc-msdosdjgpp $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/dxe3gen
|
||||
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586
|
||||
|
||||
OBJDIR = ../obj/tasks
|
||||
LIBDIR = ../lib
|
||||
BINDIR = ../bin
|
||||
OBJDIR = ../obj/tasks
|
||||
LIBSDIR = ../bin/libs
|
||||
|
||||
SRCS = taskswitch.c
|
||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||
TARGET = $(LIBDIR)/libtasks.a
|
||||
|
||||
DEMO_SRCS = demo.c
|
||||
DEMO_OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(DEMO_SRCS))
|
||||
DEMO_TARGET = $(BINDIR)/tsdemo.exe
|
||||
TARGET = $(LIBSDIR)/libtasks.lib
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
demo: $(DEMO_TARGET)
|
||||
|
||||
$(TARGET): $(OBJS) | $(LIBDIR)
|
||||
$(AR) rcs $@ $(OBJS)
|
||||
$(RANLIB) $@
|
||||
|
||||
$(DEMO_TARGET): $(DEMO_OBJS) $(TARGET) | $(BINDIR)
|
||||
$(CC) $(CFLAGS) -o $@ $(DEMO_OBJS) -L$(LIBDIR) -ltasks
|
||||
$(EXE2COFF) $@
|
||||
cat $(CWSDSTUB) $(BINDIR)/tsdemo > $@
|
||||
rm -f $(BINDIR)/tsdemo
|
||||
$(TARGET): $(OBJS) | $(LIBSDIR)
|
||||
$(DXE3GEN) -o $@ -E _ts -E _stbds_ -U $(OBJS)
|
||||
|
||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
|
@ -43,15 +27,11 @@ $(OBJDIR)/%.o: %.c | $(OBJDIR)
|
|||
$(OBJDIR):
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
$(LIBDIR):
|
||||
mkdir -p $(LIBDIR)
|
||||
|
||||
$(BINDIR):
|
||||
mkdir -p $(BINDIR)
|
||||
$(LIBSDIR):
|
||||
mkdir -p $(LIBSDIR)
|
||||
|
||||
# Dependencies
|
||||
$(OBJDIR)/taskswitch.o: taskswitch.c taskswitch.h thirdparty/stb_ds.h
|
||||
$(OBJDIR)/demo.o: demo.c taskswitch.h
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(DEMO_OBJS) $(TARGET) $(DEMO_TARGET)
|
||||
rm -f $(OBJS) $(TARGET)
|
||||
|
|
|
|||
147
widgets/Makefile
Normal file
147
widgets/Makefile
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
# DVX Widget Modules Makefile for DJGPP cross-compilation
|
||||
#
|
||||
# Builds individual .wgt modules from widget source files.
|
||||
# Each .wgt is a DXE loaded by the loader at startup.
|
||||
|
||||
DJGPP_PREFIX = $(HOME)/djgpp/djgpp
|
||||
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
|
||||
DXE3GEN = PATH=$(DJGPP_PREFIX)/bin:$(PATH) DJDIR=$(DJGPP_PREFIX)/i586-pc-msdosdjgpp $(DJGPP_PREFIX)/i586-pc-msdosdjgpp/bin/dxe3gen
|
||||
CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../core -I../core/platform -I../tasks -I../tasks/thirdparty
|
||||
|
||||
OBJDIR = ../obj/widgets
|
||||
WGTDIR = ../bin/widgets
|
||||
|
||||
SRCS = widgetAnsiTerm.c \
|
||||
widgetBox.c \
|
||||
widgetButton.c \
|
||||
widgetCanvas.c \
|
||||
widgetCheckbox.c \
|
||||
widgetComboBox.c \
|
||||
widgetDropdown.c \
|
||||
widgetImage.c \
|
||||
widgetImageButton.c \
|
||||
widgetLabel.c \
|
||||
widgetListBox.c \
|
||||
widgetListView.c \
|
||||
widgetProgressBar.c \
|
||||
widgetRadio.c \
|
||||
widgetScrollPane.c \
|
||||
widgetSeparator.c \
|
||||
widgetSlider.c \
|
||||
widgetSpacer.c \
|
||||
widgetSpinner.c \
|
||||
widgetSplitter.c \
|
||||
widgetStatusBar.c \
|
||||
widgetTabControl.c \
|
||||
widgetTextInput.c \
|
||||
widgetTimer.c \
|
||||
widgetToolbar.c \
|
||||
widgetTreeView.c
|
||||
|
||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||
|
||||
WGT_MODS = $(WGTDIR)/box.wgt \
|
||||
$(WGTDIR)/button.wgt \
|
||||
$(WGTDIR)/canvas.wgt \
|
||||
$(WGTDIR)/checkbox.wgt \
|
||||
$(WGTDIR)/combobox.wgt \
|
||||
$(WGTDIR)/dropdown.wgt \
|
||||
$(WGTDIR)/imgbtn.wgt \
|
||||
$(WGTDIR)/image.wgt \
|
||||
$(WGTDIR)/label.wgt \
|
||||
$(WGTDIR)/listbox.wgt \
|
||||
$(WGTDIR)/listview.wgt \
|
||||
$(WGTDIR)/progress.wgt \
|
||||
$(WGTDIR)/radio.wgt \
|
||||
$(WGTDIR)/scrlpane.wgt \
|
||||
$(WGTDIR)/separatr.wgt \
|
||||
$(WGTDIR)/slider.wgt \
|
||||
$(WGTDIR)/spacer.wgt \
|
||||
$(WGTDIR)/spinner.wgt \
|
||||
$(WGTDIR)/splitter.wgt \
|
||||
$(WGTDIR)/statbar.wgt \
|
||||
$(WGTDIR)/tabctrl.wgt \
|
||||
$(WGTDIR)/terminal.wgt \
|
||||
$(WGTDIR)/textinpt.wgt \
|
||||
$(WGTDIR)/timer.wgt \
|
||||
$(WGTDIR)/toolbar.wgt \
|
||||
$(WGTDIR)/treeview.wgt
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(WGT_MODS)
|
||||
|
||||
# Generic widget module rule: -E _wgt exports all public API + wgtRegister
|
||||
$(WGTDIR)/%.wgt: | $(WGTDIR)
|
||||
$(DXE3GEN) -o $@ -E _wgt -U $<
|
||||
|
||||
# Map .wgt name to .o file
|
||||
$(WGTDIR)/box.wgt: $(OBJDIR)/widgetBox.o
|
||||
$(WGTDIR)/button.wgt: $(OBJDIR)/widgetButton.o
|
||||
$(WGTDIR)/canvas.wgt: $(OBJDIR)/widgetCanvas.o
|
||||
$(WGTDIR)/checkbox.wgt: $(OBJDIR)/widgetCheckbox.o
|
||||
$(WGTDIR)/combobox.wgt: $(OBJDIR)/widgetComboBox.o
|
||||
$(WGTDIR)/dropdown.wgt: $(OBJDIR)/widgetDropdown.o
|
||||
$(WGTDIR)/imgbtn.wgt: $(OBJDIR)/widgetImageButton.o
|
||||
$(WGTDIR)/image.wgt: $(OBJDIR)/widgetImage.o
|
||||
$(WGTDIR)/label.wgt: $(OBJDIR)/widgetLabel.o
|
||||
$(WGTDIR)/listbox.wgt: $(OBJDIR)/widgetListBox.o
|
||||
$(WGTDIR)/listview.wgt: $(OBJDIR)/widgetListView.o
|
||||
$(WGTDIR)/progress.wgt: $(OBJDIR)/widgetProgressBar.o
|
||||
$(WGTDIR)/radio.wgt: $(OBJDIR)/widgetRadio.o
|
||||
$(WGTDIR)/scrlpane.wgt: $(OBJDIR)/widgetScrollPane.o
|
||||
$(WGTDIR)/separatr.wgt: $(OBJDIR)/widgetSeparator.o
|
||||
$(WGTDIR)/slider.wgt: $(OBJDIR)/widgetSlider.o
|
||||
$(WGTDIR)/spacer.wgt: $(OBJDIR)/widgetSpacer.o
|
||||
$(WGTDIR)/spinner.wgt: $(OBJDIR)/widgetSpinner.o
|
||||
$(WGTDIR)/splitter.wgt: $(OBJDIR)/widgetSplitter.o
|
||||
$(WGTDIR)/statbar.wgt: $(OBJDIR)/widgetStatusBar.o
|
||||
$(WGTDIR)/tabctrl.wgt: $(OBJDIR)/widgetTabControl.o
|
||||
$(WGTDIR)/terminal.wgt: $(OBJDIR)/widgetAnsiTerm.o
|
||||
$(WGTDIR)/textinpt.wgt: $(OBJDIR)/widgetTextInput.o
|
||||
$(WGTDIR)/timer.wgt: $(OBJDIR)/widgetTimer.o
|
||||
$(WGTDIR)/toolbar.wgt: $(OBJDIR)/widgetToolbar.o
|
||||
$(WGTDIR)/treeview.wgt: $(OBJDIR)/widgetTreeView.o
|
||||
|
||||
# Compile
|
||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR):
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
$(WGTDIR):
|
||||
mkdir -p $(WGTDIR)
|
||||
|
||||
# Dependencies
|
||||
WIDGET_DEPS = ../core/widgetInternal.h ../core/dvxWidget.h ../core/dvxTypes.h ../core/dvxApp.h ../core/dvxDraw.h ../core/dvxWm.h ../core/dvxVideo.h
|
||||
$(OBJDIR)/widgetAnsiTerm.o: widgetAnsiTerm.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetBox.o: widgetBox.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetButton.o: widgetButton.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetCanvas.o: widgetCanvas.c $(WIDGET_DEPS) ../core/thirdparty/stb_image.h ../core/thirdparty/stb_image_write.h
|
||||
$(OBJDIR)/widgetCheckbox.o: widgetCheckbox.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetComboBox.o: widgetComboBox.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetDropdown.o: widgetDropdown.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetImage.o: widgetImage.c $(WIDGET_DEPS) ../core/thirdparty/stb_image.h
|
||||
$(OBJDIR)/widgetImageButton.o: widgetImageButton.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetLabel.o: widgetLabel.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetListBox.o: widgetListBox.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetListView.o: widgetListView.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetProgressBar.o: widgetProgressBar.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetRadio.o: widgetRadio.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetScrollPane.o: widgetScrollPane.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetSeparator.o: widgetSeparator.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetSlider.o: widgetSlider.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetSpacer.o: widgetSpacer.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetSpinner.o: widgetSpinner.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetSplitter.o: widgetSplitter.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetStatusBar.o: widgetStatusBar.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetTabControl.o: widgetTabControl.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetTextInput.o: widgetTextInput.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetTimer.o: widgetTimer.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetToolbar.o: widgetToolbar.c $(WIDGET_DEPS)
|
||||
$(OBJDIR)/widgetTreeView.o: widgetTreeView.c $(WIDGET_DEPS)
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(WGT_MODS)
|
||||
-rmdir $(WGTDIR) 2>/dev/null
|
||||
|
|
@ -2141,3 +2141,26 @@ void widgetAnsiTermPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
|
|||
drawFocusRect(d, ops, w->x + 1, w->y + 1, w->w - 2, w->h - 2, colors->contentFg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
static const WidgetClassT sClassAnsiTerm = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetAnsiTermPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetAnsiTermCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetAnsiTermOnMouse,
|
||||
.onKey = widgetAnsiTermOnKey,
|
||||
.destroy = widgetAnsiTermDestroy,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetAnsiTermE] = &sClassAnsiTerm;
|
||||
}
|
||||
|
|
@ -131,3 +131,54 @@ WidgetT *wgtHBox(WidgetT *parent) {
|
|||
WidgetT *wgtVBox(WidgetT *parent) {
|
||||
return widgetAlloc(parent, WidgetVBoxE);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
|
||||
static const WidgetClassT sClassVBox = {
|
||||
.flags = WCLASS_BOX_CONTAINER,
|
||||
.paint = NULL,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = NULL,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassHBox = {
|
||||
.flags = WCLASS_BOX_CONTAINER | WCLASS_HORIZ_CONTAINER,
|
||||
.paint = NULL,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = NULL,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassFrame = {
|
||||
.flags = WCLASS_BOX_CONTAINER,
|
||||
.paint = widgetFramePaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = NULL,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetVBoxE] = &sClassVBox;
|
||||
widgetClassTable[WidgetHBoxE] = &sClassHBox;
|
||||
widgetClassTable[WidgetFrameE] = &sClassFrame;
|
||||
}
|
||||
|
|
@ -141,3 +141,26 @@ void widgetButtonPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitma
|
|||
drawFocusRect(d, ops, w->x + BUTTON_FOCUS_INSET + off, w->y + BUTTON_FOCUS_INSET + off, w->w - BUTTON_FOCUS_INSET * 2, w->h - BUTTON_FOCUS_INSET * 2, fg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
|
||||
static const WidgetClassT sClassButton = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetButtonPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetButtonCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetButtonOnMouse,
|
||||
.onKey = widgetButtonOnKey,
|
||||
.destroy = NULL,
|
||||
.getText = widgetButtonGetText,
|
||||
.setText = widgetButtonSetText
|
||||
};
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetButtonE] = &sClassButton;
|
||||
}
|
||||
|
|
@ -724,3 +724,26 @@ void widgetCanvasPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitma
|
|||
w->as.canvas.data, w->as.canvas.canvasPitch,
|
||||
0, 0, imgW, imgH);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
static const WidgetClassT sClassCanvas = {
|
||||
.flags = 0,
|
||||
.paint = widgetCanvasPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetCanvasCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetCanvasOnMouse,
|
||||
.onKey = NULL,
|
||||
.destroy = widgetCanvasDestroy,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetCanvasE] = &sClassCanvas;
|
||||
}
|
||||
|
|
@ -150,3 +150,26 @@ void widgetCheckboxPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
|
|||
drawFocusRect(d, ops, labelX - 1, labelY - 1, labelW + 2, font->charHeight + 2, fg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
|
||||
static const WidgetClassT sClassCheckbox = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetCheckboxPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetCheckboxCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetCheckboxOnMouse,
|
||||
.onKey = widgetCheckboxOnKey,
|
||||
.destroy = NULL,
|
||||
.getText = widgetCheckboxGetText,
|
||||
.setText = widgetCheckboxSetText
|
||||
};
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetCheckboxE] = &sClassCheckbox;
|
||||
}
|
||||
|
|
@ -370,3 +370,26 @@ void widgetComboBoxSetText(WidgetT *w, const char *text) {
|
|||
w->as.comboBox.selEnd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
static const WidgetClassT sClassComboBox = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetComboBoxPaint,
|
||||
.paintOverlay = widgetComboBoxPaintPopup,
|
||||
.calcMinSize = widgetComboBoxCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetComboBoxOnMouse,
|
||||
.onKey = widgetComboBoxOnKey,
|
||||
.destroy = widgetComboBoxDestroy,
|
||||
.getText = widgetComboBoxGetText,
|
||||
.setText = widgetComboBoxSetText
|
||||
};
|
||||
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetComboBoxE] = &sClassComboBox;
|
||||
}
|
||||
|
|
@ -274,3 +274,26 @@ void widgetDropdownPaintPopup(WidgetT *w, DisplayT *d, const BlitOpsT *ops, cons
|
|||
widgetDropdownPopupRect(w, font, d->clipH, &popX, &popY, &popW, &popH);
|
||||
widgetPaintPopupList(d, ops, font, colors, popX, popY, popW, popH, w->as.dropdown.items, w->as.dropdown.itemCount, w->as.dropdown.hoverIdx, w->as.dropdown.scrollPos);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
|
||||
static const WidgetClassT sClassDropdown = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetDropdownPaint,
|
||||
.paintOverlay = widgetDropdownPaintPopup,
|
||||
.calcMinSize = widgetDropdownCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetDropdownOnMouse,
|
||||
.onKey = widgetDropdownOnKey,
|
||||
.destroy = NULL,
|
||||
.getText = widgetDropdownGetText,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetDropdownE] = &sClassDropdown;
|
||||
}
|
||||
|
|
@ -160,3 +160,26 @@ void widgetImagePaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitmap
|
|||
w->as.image.data, w->as.image.imgPitch,
|
||||
0, 0, imgW, imgH);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
static const WidgetClassT sClassImage = {
|
||||
.flags = 0,
|
||||
.paint = widgetImagePaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetImageCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetImageOnMouse,
|
||||
.onKey = NULL,
|
||||
.destroy = widgetImageDestroy,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetImageE] = &sClassImage;
|
||||
}
|
||||
|
|
@ -179,3 +179,26 @@ void widgetImageButtonPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const
|
|||
drawFocusRect(d, ops, w->x + 3 + off, w->y + 3 + off, w->w - 6, w->h - 6, fg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
static const WidgetClassT sClassImageButton = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetImageButtonPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetImageButtonCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetImageButtonOnMouse,
|
||||
.onKey = widgetImageButtonOnKey,
|
||||
.destroy = widgetImageButtonDestroy,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetImageButtonE] = &sClassImageButton;
|
||||
}
|
||||
|
|
@ -99,3 +99,26 @@ void wgtLabelSetAlign(WidgetT *w, WidgetAlignE align) {
|
|||
w->as.label.textAlign = align;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
|
||||
static const WidgetClassT sClassLabel = {
|
||||
.flags = 0,
|
||||
.paint = widgetLabelPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetLabelCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = widgetLabelGetText,
|
||||
.setText = widgetLabelSetText
|
||||
};
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetLabelE] = &sClassLabel;
|
||||
}
|
||||
|
|
@ -636,3 +636,26 @@ void widgetListBoxPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitm
|
|||
drawFocusRect(d, ops, w->x + 1, w->y + 1, w->w - 2, w->h - 2, fg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
|
||||
static const WidgetClassT sClassListBox = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetListBoxPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetListBoxCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetListBoxOnMouse,
|
||||
.onKey = widgetListBoxOnKey,
|
||||
.destroy = widgetListBoxDestroy,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetListBoxE] = &sClassListBox;
|
||||
}
|
||||
|
|
@ -1380,3 +1380,27 @@ void widgetListViewPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
|
|||
drawFocusRect(d, ops, w->x + 1, w->y + 1, w->w - 2, w->h - 2, fg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
static const WidgetClassT sClassListView = {
|
||||
.flags = WCLASS_FOCUSABLE | WCLASS_NO_HIT_RECURSE,
|
||||
.paint = widgetListViewPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetListViewCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetListViewOnMouse,
|
||||
.onKey = widgetListViewOnKey,
|
||||
.destroy = widgetListViewDestroy,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetListViewE] = &sClassListView;
|
||||
sListViewColBorderHitFn = widgetListViewColBorderHit;
|
||||
}
|
||||
|
|
@ -159,3 +159,26 @@ void widgetProgressBarPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
static const WidgetClassT sClassProgressBar = {
|
||||
.flags = 0,
|
||||
.paint = widgetProgressBarPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetProgressBarCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetProgressBarE] = &sClassProgressBar;
|
||||
}
|
||||
|
|
@ -280,3 +280,40 @@ void widgetRadioPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitmap
|
|||
drawFocusRect(d, ops, labelX - 1, labelY - 1, labelW + 2, font->charHeight + 2, fg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
|
||||
static const WidgetClassT sClassRadioGroup = {
|
||||
.flags = WCLASS_BOX_CONTAINER,
|
||||
.paint = NULL,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = NULL,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassRadio = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetRadioPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetRadioCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetRadioOnMouse,
|
||||
.onKey = widgetRadioOnKey,
|
||||
.destroy = NULL,
|
||||
.getText = widgetRadioGetText,
|
||||
.setText = widgetRadioSetText
|
||||
};
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetRadioGroupE] = &sClassRadioGroup;
|
||||
widgetClassTable[WidgetRadioE] = &sClassRadio;
|
||||
}
|
||||
|
|
@ -698,3 +698,26 @@ WidgetT *wgtScrollPane(WidgetT *parent) {
|
|||
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
static const WidgetClassT sClassScrollPane = {
|
||||
.flags = WCLASS_PAINTS_CHILDREN | WCLASS_NO_HIT_RECURSE,
|
||||
.paint = widgetScrollPanePaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetScrollPaneCalcMinSize,
|
||||
.layout = widgetScrollPaneLayout,
|
||||
.onMouse = widgetScrollPaneOnMouse,
|
||||
.onKey = widgetScrollPaneOnKey,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetScrollPaneE] = &sClassScrollPane;
|
||||
}
|
||||
|
|
@ -84,3 +84,26 @@ void widgetSeparatorPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bi
|
|||
drawHLine(d, ops, w->x, cy + 1, w->w, colors->windowHighlight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
|
||||
static const WidgetClassT sClassSeparator = {
|
||||
.flags = 0,
|
||||
.paint = widgetSeparatorPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetSeparatorCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetSeparatorE] = &sClassSeparator;
|
||||
}
|
||||
|
|
@ -304,3 +304,26 @@ void widgetSliderPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitma
|
|||
drawFocusRect(d, ops, w->x, w->y, w->w, w->h, fg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
|
||||
static const WidgetClassT sClassSlider = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetSliderPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetSliderCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetSliderOnMouse,
|
||||
.onKey = widgetSliderOnKey,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetSliderE] = &sClassSlider;
|
||||
}
|
||||
|
|
@ -39,3 +39,26 @@ void widgetSpacerCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
|||
w->calcMinW = 0;
|
||||
w->calcMinH = 0;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
|
||||
static const WidgetClassT sClassSpacer = {
|
||||
.flags = 0,
|
||||
.paint = NULL,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetSpacerCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetSpacerE] = &sClassSpacer;
|
||||
}
|
||||
|
|
@ -519,3 +519,26 @@ void wgtSpinnerSetValue(WidgetT *w, int32_t value) {
|
|||
spinnerClampAndFormat(w);
|
||||
wgtInvalidate(w);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
static const WidgetClassT sClassSpinner = {
|
||||
.flags = WCLASS_FOCUSABLE,
|
||||
.paint = widgetSpinnerPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetSpinnerCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = widgetSpinnerOnMouse,
|
||||
.onKey = widgetSpinnerOnKey,
|
||||
.destroy = NULL,
|
||||
.getText = widgetSpinnerGetText,
|
||||
.setText = widgetSpinnerSetText
|
||||
};
|
||||
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetSpinnerE] = &sClassSpinner;
|
||||
}
|
||||
|
|
@ -387,3 +387,27 @@ void wgtSplitterSetPos(WidgetT *w, int32_t pos) {
|
|||
w->as.splitter.dividerPos = pos;
|
||||
wgtInvalidate(w);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
static const WidgetClassT sClassSplitter = {
|
||||
.flags = WCLASS_PAINTS_CHILDREN | WCLASS_NO_HIT_RECURSE,
|
||||
.paint = widgetSplitterPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetSplitterCalcMinSize,
|
||||
.layout = widgetSplitterLayout,
|
||||
.onMouse = widgetSplitterOnMouse,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetSplitterE] = &sClassSplitter;
|
||||
sSplitterClampPosFn = widgetSplitterClampPos;
|
||||
}
|
||||
|
|
@ -61,3 +61,26 @@ void widgetStatusBarPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bi
|
|||
drawBevel(d, ops, c->x - 1, c->y - 1, c->w + 2, c->h + 2, &bevel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
|
||||
static const WidgetClassT sClassStatusBar = {
|
||||
.flags = WCLASS_BOX_CONTAINER | WCLASS_HORIZ_CONTAINER,
|
||||
.paint = widgetStatusBarPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = NULL,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetStatusBarE] = &sClassStatusBar;
|
||||
}
|
||||
|
|
@ -558,3 +558,40 @@ void widgetTabControlPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const B
|
|||
tabIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
static const WidgetClassT sClassTabControl = {
|
||||
.flags = WCLASS_FOCUSABLE | WCLASS_PAINTS_CHILDREN,
|
||||
.paint = widgetTabControlPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetTabControlCalcMinSize,
|
||||
.layout = widgetTabControlLayout,
|
||||
.onMouse = widgetTabControlOnMouse,
|
||||
.onKey = widgetTabControlOnKey,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassTabPage = {
|
||||
.flags = WCLASS_BOX_CONTAINER,
|
||||
.paint = NULL,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = NULL,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetTabControlE] = &sClassTabControl;
|
||||
widgetClassTable[WidgetTabPageE] = &sClassTabPage;
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
#include <time.h>
|
||||
|
||||
#include "thirdparty/stb_ds.h"
|
||||
#include "stb_ds.h"
|
||||
|
||||
|
||||
// Active timer list -- avoids walking the widget tree per frame
|
||||
|
|
@ -182,3 +182,26 @@ void wgtUpdateTimers(void) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
static const WidgetClassT sClassTimer = {
|
||||
.flags = 0,
|
||||
.paint = NULL,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetTimerCalcMinSize,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = widgetTimerDestroy,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetTimerE] = &sClassTimer;
|
||||
}
|
||||
|
|
@ -52,3 +52,26 @@ void widgetToolbarPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bitm
|
|||
bevel.width = 1;
|
||||
drawBevel(d, ops, w->x, w->y, w->w, w->h, &bevel);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
static const WidgetClassT sClassToolbar = {
|
||||
.flags = WCLASS_BOX_CONTAINER | WCLASS_HORIZ_CONTAINER,
|
||||
.paint = widgetToolbarPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = NULL,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetToolbarE] = &sClassToolbar;
|
||||
}
|
||||
|
|
@ -1386,3 +1386,41 @@ void widgetTreeViewPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
|
|||
drawFocusRect(d, ops, w->x + 1, w->y + 1, w->w - 2, w->h - 2, fg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// DXE registration
|
||||
// ============================================================
|
||||
|
||||
static const WidgetClassT sClassTreeView = {
|
||||
.flags = WCLASS_FOCUSABLE | WCLASS_PAINTS_CHILDREN | WCLASS_NO_HIT_RECURSE,
|
||||
.paint = widgetTreeViewPaint,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = widgetTreeViewCalcMinSize,
|
||||
.layout = widgetTreeViewLayout,
|
||||
.onMouse = widgetTreeViewOnMouse,
|
||||
.onKey = widgetTreeViewOnKey,
|
||||
.destroy = NULL,
|
||||
.getText = NULL,
|
||||
.setText = NULL
|
||||
};
|
||||
|
||||
static const WidgetClassT sClassTreeItem = {
|
||||
.flags = 0,
|
||||
.paint = NULL,
|
||||
.paintOverlay = NULL,
|
||||
.calcMinSize = NULL,
|
||||
.layout = NULL,
|
||||
.onMouse = NULL,
|
||||
.onKey = NULL,
|
||||
.destroy = NULL,
|
||||
.getText = widgetTreeItemGetText,
|
||||
.setText = widgetTreeItemSetText
|
||||
};
|
||||
|
||||
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[WidgetTreeViewE] = &sClassTreeView;
|
||||
widgetClassTable[WidgetTreeItemE] = &sClassTreeItem;
|
||||
sTreeViewNextVisibleFn = widgetTreeViewNextVisible;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue