Docs.
This commit is contained in:
parent
163959a192
commit
3b2d87845e
8 changed files with 736 additions and 266 deletions
224
README.md
224
README.md
|
|
@ -2,9 +2,10 @@
|
|||
|
||||
A windowed GUI compositor and desktop shell for DOS, built with
|
||||
DJGPP/DPMI. DVX combines a Motif-style window manager, dirty-rectangle
|
||||
compositor, cooperative task switcher, and DXE3 dynamic application
|
||||
loading into a multitasking desktop environment where applications are
|
||||
`.app` shared libraries loaded at runtime.
|
||||
compositor, cooperative task switcher, and a modular DXE3 architecture
|
||||
into a multitasking desktop environment. The bootstrap loader
|
||||
dynamically discovers and loads core libraries, widget plugins, and
|
||||
applications at runtime.
|
||||
|
||||
Targets real and emulated 486+ hardware with VESA VBE 2.0+ linear
|
||||
framebuffer. No bank switching -- LFB or fail.
|
||||
|
|
@ -16,9 +17,10 @@ framebuffer. No bank switching -- LFB or fail.
|
|||
maximize, and restore
|
||||
- Dirty-rectangle compositor -- only changed regions are flushed to video
|
||||
memory, critical for acceptable frame rates on 486/Pentium hardware
|
||||
- 32 widget types: buttons, text inputs, list boxes, tree views, list
|
||||
views, tab controls, sliders, spinners, progress bars, dropdowns,
|
||||
combo boxes, splitters, scroll panes, ANSI terminal emulator, and more
|
||||
- 32 widget types across 26 DXE modules: buttons, text inputs, list
|
||||
boxes, tree views, list views, tab controls, sliders, spinners,
|
||||
progress bars, dropdowns, combo boxes, splitters, scroll panes, ANSI
|
||||
terminal emulator, and more
|
||||
- Flexbox-style automatic layout engine (VBox/HBox containers with
|
||||
weighted space distribution)
|
||||
- Dropdown menus with cascading submenus, checkbox and radio items,
|
||||
|
|
@ -40,6 +42,8 @@ framebuffer. No bank switching -- LFB or fail.
|
|||
- Encrypted serial networking stack (DH key exchange, XTEA-CTR cipher)
|
||||
- Platform abstraction layer -- DOS/DJGPP production target, Linux/SDL2
|
||||
development target
|
||||
- Modular DXE architecture with dependency resolution -- core libraries,
|
||||
widget plugins, and applications are all separate loadable modules
|
||||
|
||||
|
||||
## Target Hardware
|
||||
|
|
@ -53,30 +57,68 @@ framebuffer. No bank switching -- LFB or fail.
|
|||
|
||||
## Architecture
|
||||
|
||||
The shell runs as a single DOS executable (`dvx.exe`) that loads
|
||||
applications dynamically via DJGPP's DXE3 shared library system.
|
||||
DVX uses a modular DXE3 architecture. The bootstrap loader (`dvx.exe`)
|
||||
is a small executable that discovers, dependency-sorts, and loads all
|
||||
modules at startup. Core libraries, widget plugins, and applications
|
||||
are separate `.lib`, `.wgt`, and `.app` DXE shared libraries.
|
||||
|
||||
```
|
||||
+-------------------------------------------------------------------+
|
||||
| dvx.exe (Task 0) |
|
||||
| +-------------+ +-----------+ +------------+ |
|
||||
| | shellMain | | shellApp | | shellExport| |
|
||||
| | (event loop)| | (lifecycle| | (DXE symbol| |
|
||||
| | | | + reaper)| | export) | |
|
||||
| +-------------+ +-----------+ +------------+ |
|
||||
| | | | |
|
||||
| +------+-------+ +----+-----+ +----+----+ |
|
||||
| | libdvx.a | |libtasks.a| | libdxe | |
|
||||
| | (GUI/widgets)| |(scheduler)| | (DJGPP) | |
|
||||
| +--------------+ +----------+ +---------+ |
|
||||
+-------------------------------------------------------------------+
|
||||
| |
|
||||
+---------+ +---------+
|
||||
| app.app | | app.app |
|
||||
| (Task N)| | (Task M)|
|
||||
+---------+ +---------+
|
||||
dvx.exe (bootstrap loader)
|
||||
|
|
||||
+-- platformRegisterDxeExports() -- platform + libc/libm symbols
|
||||
|
|
||||
+-- libs/ (scanned, dependency-ordered)
|
||||
| libtasks.lib -- cooperative task switching, stb_ds
|
||||
| libdvx.lib -- draw, comp, wm, app, widget infrastructure
|
||||
| dvxshell.lib -- shell, app lifecycle, crash recovery
|
||||
|
|
||||
+-- widgets/ (scanned, each calls wgtRegister)
|
||||
| box.wgt, button.wgt, label.wgt, ... (26 modules)
|
||||
|
|
||||
+-- shellMain() -- found via dlsym, enters main loop
|
||||
|
|
||||
+-- apps/ (loaded on demand by shell)
|
||||
progman.app, notepad.app, clock.app, ...
|
||||
```
|
||||
|
||||
### Boot Sequence
|
||||
|
||||
1. `dvx.exe` starts, calls `platformRegisterDxeExports()` to register
|
||||
platform and libc/libm symbols for DXE modules
|
||||
2. Scans `libs/` recursively for `*.lib` files, reads `.dep` files,
|
||||
topologically sorts, and loads in dependency order
|
||||
3. Scans `widgets/` recursively for `*.wgt` files, loads each, and
|
||||
calls `wgtRegister()` on any module that exports it
|
||||
4. Finds `shellMain()` across loaded modules via `dlsym`
|
||||
5. Calls `shellMain()`, which initializes the GUI, loads the desktop
|
||||
app, and enters the main event loop
|
||||
|
||||
### Dependency Resolution
|
||||
|
||||
Each DXE module may have a `.dep` file (same base name, `.dep`
|
||||
extension) listing the base names of modules that must be loaded before
|
||||
it. The loader reads all `.dep` files, builds a dependency graph, and
|
||||
loads modules in topological order. For example, `libdvx.dep` contains
|
||||
`libtasks`, and `dvxshell.dep` lists `libtasks`, `libdvx`, and all
|
||||
widget modules it requires.
|
||||
|
||||
### Widget DXE Modules
|
||||
|
||||
The 32 widget types in the `WidgetTypeE` enum are implemented across 26
|
||||
`.wgt` DXE modules. Some modules register multiple widget types:
|
||||
|
||||
| Module | Widget Types |
|
||||
|--------|-------------|
|
||||
| `box.wgt` | VBox, HBox, Frame |
|
||||
| `radio.wgt` | RadioGroup, Radio |
|
||||
| `textinpt.wgt` | TextInput, TextArea |
|
||||
| `tabctrl.wgt` | TabControl, TabPage |
|
||||
| `treeview.wgt` | TreeView, TreeItem |
|
||||
|
||||
Each widget DXE exports a standard `wgtRegister()` function that
|
||||
registers its widget class(es) with the core widget infrastructure.
|
||||
Widget DXEs are discovered by directory scanning, not hardcoded.
|
||||
|
||||
### App Types
|
||||
|
||||
**Callback-only** (`hasMainLoop = false`): `appMain` creates windows,
|
||||
|
|
@ -98,33 +140,93 @@ Diagnostic information (registers, faulting EIP) is logged to `dvx.log`.
|
|||
|
||||
## Directory Structure
|
||||
|
||||
### Source Tree
|
||||
|
||||
```
|
||||
dvxgui/
|
||||
dvx/ GUI compositor library (libdvx.a)
|
||||
platform/ Platform abstraction (DOS/DJGPP, Linux/SDL2)
|
||||
widgets/ Widget system (32 types, one file per type)
|
||||
thirdparty/ stb_image.h, stb_image_write.h
|
||||
tasks/ Cooperative task switcher (libtasks.a)
|
||||
thirdparty/ stb_ds.h
|
||||
dvxshell/ Desktop shell (dvx.exe)
|
||||
apps/ DXE app modules (.app files)
|
||||
progman/ Program Manager -- app launcher grid
|
||||
notepad/ Text editor with file I/O
|
||||
clock/ Digital clock (multi-instance, main-loop)
|
||||
dvxdemo/ Widget system showcase / demo app
|
||||
cpanel/ Control Panel -- themes, wallpaper, video, mouse
|
||||
imgview/ Image Viewer -- BMP/PNG/JPEG/GIF display
|
||||
rs232/ ISR-driven UART serial driver (librs232.a)
|
||||
packet/ HDLC framing, CRC-16, Go-Back-N (libpacket.a)
|
||||
security/ DH key exchange, XTEA-CTR cipher (libsecurity.a)
|
||||
seclink/ Secure serial link wrapper (libseclink.a)
|
||||
proxy/ Linux SecLink-to-telnet proxy (secproxy)
|
||||
termdemo/ Encrypted ANSI terminal demo (termdemo.exe)
|
||||
themes/ Color theme files (.thm)
|
||||
wpaper/ Bundled wallpaper images
|
||||
bin/ Build output (dvx.exe, apps/, config/)
|
||||
lib/ Build output (static libraries)
|
||||
releases/ Release archives
|
||||
core/ Core GUI infrastructure (-> libs/libdvx.lib)
|
||||
platform/ Platform abstraction (dvxPlatform.h, dvxPlatformDos.c)
|
||||
thirdparty/ stb_image.h, stb_image_write.h
|
||||
tasks/ Cooperative task switcher (-> libs/libtasks.lib)
|
||||
thirdparty/ stb_ds.h
|
||||
shell/ Desktop shell (-> libs/dvxshell.lib)
|
||||
widgets/ Widget modules (-> widgets/*.wgt, 26 files)
|
||||
loader/ Bootstrap loader (-> dvx.exe)
|
||||
apps/ Application modules (-> apps/*.app)
|
||||
progman/ Program Manager -- app launcher grid
|
||||
notepad/ Text editor with file I/O
|
||||
clock/ Digital clock (multi-instance, main-loop)
|
||||
dvxdemo/ Widget system showcase / demo app
|
||||
cpanel/ Control Panel -- themes, wallpaper, video, mouse
|
||||
imgview/ Image Viewer -- BMP/PNG/JPEG/GIF display
|
||||
config/ Themes, wallpapers, dvx.ini, dependency files
|
||||
rs232/ ISR-driven UART serial driver (librs232.a)
|
||||
packet/ HDLC framing, CRC-16, Go-Back-N (libpacket.a)
|
||||
security/ DH key exchange, XTEA-CTR cipher (libsecurity.a)
|
||||
seclink/ Secure serial link wrapper (libseclink.a)
|
||||
proxy/ Linux serial-to-telnet proxy (secproxy)
|
||||
termdemo/ Encrypted ANSI terminal demo (termdemo.exe)
|
||||
bin/ Build output
|
||||
lib/ Build output (static libraries)
|
||||
releases/ Release archives
|
||||
```
|
||||
|
||||
### Build Output (`bin/`)
|
||||
|
||||
```
|
||||
bin/
|
||||
dvx.exe Bootstrap loader
|
||||
dvx.map Linker map
|
||||
libs/
|
||||
libtasks.lib Task switching DXE
|
||||
libdvx.lib Core GUI DXE
|
||||
libdvx.dep Dependencies: libtasks
|
||||
dvxshell.lib Shell DXE
|
||||
dvxshell.dep Dependencies: libtasks, libdvx, box, button, etc.
|
||||
widgets/
|
||||
box.wgt VBox/HBox/Frame
|
||||
button.wgt Button
|
||||
canvas.wgt Canvas
|
||||
checkbox.wgt Checkbox
|
||||
combobox.wgt ComboBox
|
||||
dropdown.wgt Dropdown
|
||||
image.wgt Image
|
||||
imgbtn.wgt ImageButton
|
||||
label.wgt Label
|
||||
listbox.wgt ListBox
|
||||
listview.wgt ListView
|
||||
progress.wgt ProgressBar
|
||||
radio.wgt RadioGroup/Radio
|
||||
scrlpane.wgt ScrollPane
|
||||
separatr.wgt Separator
|
||||
slider.wgt Slider
|
||||
spacer.wgt Spacer
|
||||
spinner.wgt Spinner
|
||||
splitter.wgt Splitter
|
||||
statbar.wgt StatusBar
|
||||
tabctrl.wgt TabControl/TabPage
|
||||
terminal.wgt AnsiTerm
|
||||
textinpt.wgt TextInput/TextArea
|
||||
timer.wgt Timer
|
||||
toolbar.wgt Toolbar
|
||||
treeview.wgt TreeView/TreeItem
|
||||
apps/
|
||||
progman/progman.app
|
||||
notepad/notepad.app
|
||||
clock/clock.app
|
||||
dvxdemo/dvxdemo.app
|
||||
cpanel/cpanel.app
|
||||
imgview/imgview.app
|
||||
config/
|
||||
dvx.ini
|
||||
themes/
|
||||
cde.thm
|
||||
geos.thm
|
||||
win31.thm
|
||||
wpaper/
|
||||
blueglow.jpg
|
||||
swoop.jpg
|
||||
triangle.jpg
|
||||
```
|
||||
|
||||
|
||||
|
|
@ -133,14 +235,16 @@ dvxgui/
|
|||
Requires the DJGPP cross-compiler (`i586-pc-msdosdjgpp-gcc`).
|
||||
|
||||
```bash
|
||||
# Build everything (dvx lib, tasks lib, shell, all apps)
|
||||
# Build everything (loader, core, tasks, shell, widgets, all apps)
|
||||
make
|
||||
|
||||
# Build individual components
|
||||
make -C dvx # builds lib/libdvx.a
|
||||
make -C tasks # builds lib/libtasks.a
|
||||
make -C dvxshell # builds bin/dvx.exe
|
||||
make -C apps # builds bin/apps/*/*.app
|
||||
make -C loader # builds dvx.exe
|
||||
make -C core # builds libs/libdvx.lib + widgets/*.wgt
|
||||
make -C tasks # builds libs/libtasks.lib
|
||||
make -C shell # builds libs/dvxshell.lib + config
|
||||
make -C widgets # builds widgets/*.wgt
|
||||
make -C apps # builds apps/*/*.app
|
||||
|
||||
# Clean all build artifacts
|
||||
make clean
|
||||
|
|
@ -272,8 +376,8 @@ external dependencies:
|
|||
|
||||
| Library | Location | Purpose |
|
||||
|---------|----------|---------|
|
||||
| stb_image.h | `dvx/thirdparty/` | Image loading (BMP, PNG, JPEG, GIF) |
|
||||
| stb_image_write.h | `dvx/thirdparty/` | Image writing (PNG export for screenshots) |
|
||||
| stb_image.h | `core/thirdparty/` | Image loading (BMP, PNG, JPEG, GIF) |
|
||||
| stb_image_write.h | `core/thirdparty/` | Image writing (PNG export for screenshots) |
|
||||
| stb_ds.h | `tasks/thirdparty/` | Dynamic array and hash map (used by task manager) |
|
||||
|
||||
|
||||
|
|
@ -281,9 +385,9 @@ external dependencies:
|
|||
|
||||
Each component directory has its own README with detailed API reference:
|
||||
|
||||
- [`dvx/README.md`](dvx/README.md) -- GUI library architecture and API
|
||||
- [`core/README.md`](core/README.md) -- Core GUI library architecture and API
|
||||
- [`tasks/README.md`](tasks/README.md) -- Task switcher API
|
||||
- [`dvxshell/README.md`](dvxshell/README.md) -- Shell internals and DXE app contract
|
||||
- [`shell/README.md`](shell/README.md) -- Shell internals and DXE app contract
|
||||
- [`apps/README.md`](apps/README.md) -- Writing DXE applications
|
||||
- [`rs232/README.md`](rs232/README.md) -- Serial port driver
|
||||
- [`packet/README.md`](packet/README.md) -- Packet transport protocol
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ make # builds all .app files into ../bin/apps/<name>/
|
|||
make clean # removes objects and binaries
|
||||
```
|
||||
|
||||
CFLAGS: `-O2 -Wall -Wextra -march=i486 -mtune=i586 -I../core -I../core/platform -I../core/thirdparty -I../tasks -I../tasks/thirdparty -I../shell`
|
||||
|
||||
Each app is compiled to an object file with the DJGPP cross-compiler, then
|
||||
packaged into a `.app` via `dxe3gen`:
|
||||
|
||||
|
|
@ -66,11 +68,26 @@ $(BINDIR)/myapp/myapp.app: $(OBJDIR)/myapp.o | $(BINDIR)/myapp
|
|||
underscore prefix).
|
||||
- `-E _appShutdown` is added for apps that export a shutdown hook (e.g.,
|
||||
Clock).
|
||||
- `-U` marks all other symbols as unresolved imports to be resolved from the
|
||||
shell's export table at dlopen time.
|
||||
- `-U` marks all other symbols as unresolved imports to be resolved at
|
||||
`dlopen` time from the loader and all `RTLD_GLOBAL` modules (libdvx,
|
||||
libtasks, widgets, and the shell's wrapper overrides).
|
||||
|
||||
Requires `lib/libdvx.a`, `lib/libtasks.a`, and the DXE3 tools from the DJGPP
|
||||
toolchain.
|
||||
Requires the DJGPP cross-compiler toolchain and the DXE3 tools (`dxe3gen`).
|
||||
|
||||
## Symbol Resolution
|
||||
|
||||
Apps do not link against static libraries. All symbols are resolved at load
|
||||
time via the DXE3 dynamic linker:
|
||||
|
||||
1. The **loader** registers libc/libm/runtime symbols via `dlregsym()`.
|
||||
2. **DXE modules** loaded with `RTLD_GLOBAL` (libdvx, libtasks, widgets)
|
||||
export their public APIs to the global symbol namespace.
|
||||
3. The **shell** registers 3 wrapper overrides (`dvxCreateWindow`,
|
||||
`dvxCreateWindowCentered`, `dvxDestroyWindow`) via `dlregsym()` for
|
||||
resource tracking.
|
||||
|
||||
When an app DXE is loaded, all its unresolved symbols are matched against
|
||||
this combined symbol table.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
|
|
@ -418,8 +435,8 @@ hook.
|
|||
simultaneously. Each instance gets independent globals and statics.
|
||||
- Avoid `static inline` functions in shared headers. Code inlined into the
|
||||
DXE binary cannot be updated without recompiling the app. Use macros for
|
||||
trivial expressions or regular functions exported through the shell's DXE
|
||||
table.
|
||||
trivial expressions or regular functions exported through the DXE module
|
||||
system.
|
||||
- Use `ctx->appDir` for loading app-relative resources (icons, data files).
|
||||
The working directory is shared by all apps and belongs to the shell.
|
||||
- Use `shellEnsureConfigDir()` + `shellConfigPath()` for persistent settings.
|
||||
|
|
|
|||
93
config/README.md
Normal file
93
config/README.md
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
# DVX Configuration Files
|
||||
|
||||
Runtime configuration, theme files, wallpaper images, and module
|
||||
dependency files. These are copied into `bin/config/` (INI, themes,
|
||||
wallpapers) or `bin/libs/` (dep files) during the build.
|
||||
|
||||
|
||||
## Files
|
||||
|
||||
| File | Destination | Purpose |
|
||||
|------|-------------|---------|
|
||||
| `dvx.ini` | `bin/config/dvx.ini` | Main configuration (video, mouse, colors, desktop) |
|
||||
| `themes/*.thm` | `bin/config/themes/` | Color theme files (INI format with `[colors]` section) |
|
||||
| `wpaper/*.jpg` | `bin/config/wpaper/` | Bundled wallpaper images |
|
||||
| `libdvx.dep` | `bin/libs/libdvx.dep` | libdvx.lib dependency list |
|
||||
| `dvxshell.dep` | `bin/libs/dvxshell.dep` | dvxshell.lib dependency list |
|
||||
|
||||
|
||||
## DVX.INI Format
|
||||
|
||||
Standard `[section]` / `key = value` INI format. Converted to DOS
|
||||
line endings (`\r\n`) during the build.
|
||||
|
||||
```ini
|
||||
[video]
|
||||
width = 640
|
||||
height = 480
|
||||
bpp = 16
|
||||
|
||||
[mouse]
|
||||
wheel = normal # normal | reversed
|
||||
doubleclick = 500 # milliseconds (200-900)
|
||||
acceleration = medium # off | low | medium | high
|
||||
|
||||
[colors]
|
||||
desktop = 0,128,128
|
||||
windowFace = 192,192,192
|
||||
# ... 20 color keys total (R,G,B triplets 0-255)
|
||||
|
||||
[desktop]
|
||||
wallpaper = CONFIG\WPAPER\SWOOP.JPG
|
||||
mode = stretch # stretch | tile | center
|
||||
```
|
||||
|
||||
|
||||
## Dependency Files (.dep)
|
||||
|
||||
Plain text, one dependency per line. Each line is the base name
|
||||
(without extension) of a module that must be loaded before this one.
|
||||
Lines starting with `#` are comments. Empty lines are ignored.
|
||||
|
||||
Example -- `dvxshell.dep`:
|
||||
```
|
||||
# Core libraries
|
||||
libtasks
|
||||
libdvx
|
||||
|
||||
# Widget modules used by the shell
|
||||
box
|
||||
button
|
||||
checkbox
|
||||
dropdown
|
||||
label
|
||||
listbox
|
||||
listview
|
||||
radio
|
||||
separator
|
||||
spacer
|
||||
statbar
|
||||
textinpt
|
||||
```
|
||||
|
||||
Dep files are read by the loader during startup to determine the
|
||||
correct load order via topological sort.
|
||||
|
||||
|
||||
## Theme Files (.thm)
|
||||
|
||||
INI format with a `[colors]` section containing the same 20 color
|
||||
keys as `dvx.ini`. Loaded via the Control Panel app or
|
||||
`dvxLoadTheme()`.
|
||||
|
||||
```ini
|
||||
[colors]
|
||||
desktop = 0,128,128
|
||||
windowFace = 192,192,192
|
||||
windowHighlight = 255,255,255
|
||||
windowShadow = 128,128,128
|
||||
; ... remaining colors
|
||||
```
|
||||
|
||||
Three themes are bundled: `geos.thm` (GEOS Ensemble), `win31.thm`
|
||||
(Windows 3.1), `cde.thm` (CDE/Motif).
|
||||
143
core/README.md
143
core/README.md
|
|
@ -1,10 +1,16 @@
|
|||
# DVX GUI Library (libdvx.a)
|
||||
# DVX Core Library (libdvx.lib)
|
||||
|
||||
The core GUI compositor library for DVX. Provides VESA video setup,
|
||||
2D drawing primitives, dirty-rectangle compositing, a full window
|
||||
manager with Motif-style chrome, and a 32-type widget toolkit with
|
||||
automatic layout. Applications include `dvxApp.h` (which pulls in all
|
||||
lower layers) and optionally `dvxWidget.h` for the widget system.
|
||||
The core GUI compositor library for DVX, built as a DXE module
|
||||
(libdvx.lib). Provides VESA video setup, 2D drawing primitives,
|
||||
dirty-rectangle compositing, a full window manager with Motif-style
|
||||
chrome, and the widget infrastructure (layout engine, event dispatch,
|
||||
class registration). Individual widget type implementations live in
|
||||
`../widgets/` as separate `.wgt` DXE modules that register themselves
|
||||
at runtime via `wgtRegisterClass()`.
|
||||
|
||||
Applications include `dvxApp.h` (which pulls in all lower layers) and
|
||||
optionally `dvxWidget.h` for the widget system. In a DXE app context,
|
||||
headers are found via `-I` flags set by the build system.
|
||||
|
||||
|
||||
## Architecture
|
||||
|
|
@ -18,9 +24,20 @@ Layer 4 dvxWm Window stack, chrome, drag, resize, focus, menus
|
|||
Layer 3 dvxComp Dirty rectangle list, merge, LFB flush
|
||||
Layer 2 dvxDraw Spans, rects, bevels, text, bitmaps (asm inner loops)
|
||||
Layer 1 dvxVideo VESA init, LFB mapping, backbuffer, pixel format
|
||||
dvxWidget Widget/layout system (optional, standalone)
|
||||
dvxWidget Widget infrastructure (layout, events, class table)
|
||||
```
|
||||
|
||||
The widget class table (`widgetClassTable[]`) starts empty. Widget DXE
|
||||
modules (`.wgt` files in `../widgets/`) fill it at runtime by calling
|
||||
registration functions (e.g. `wgtBoxRegister()`, `wgtButtonRegister()`).
|
||||
The DVX shell loader loads each widget DXE and calls its register
|
||||
function before starting the application.
|
||||
|
||||
Dynamic widget registration (Phase 2): external DXE plugins can call
|
||||
`wgtRegisterClass()` to add entirely new widget types at runtime. The
|
||||
returned type ID is >= `WGT_TYPE_DYNAMIC_BASE` (32). The maximum number
|
||||
of widget types (built-in + dynamic) is `WGT_MAX_TYPES` (64).
|
||||
|
||||
|
||||
## File Structure
|
||||
|
||||
|
|
@ -39,7 +56,7 @@ Layer 1 dvxVideo VESA init, LFB mapping, backbuffer, pixel format
|
|||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `dvxTypes.h` | Shared type definitions used by all layers (PixelFormatT, DisplayT, WindowT, ColorSchemeT, etc.) |
|
||||
| `dvxWidget.h` | Widget system public API (32 widget types, layout, events) |
|
||||
| `dvxWidget.h` | Widget system public API, WidgetClassT vtable struct, WCLASS_* flags (for DXE plugins) |
|
||||
| `dvxDialog.h/.c` | Modal dialogs (message box, file open/save) |
|
||||
| `dvxPrefs.h/.c` | INI-based preferences system (read/write with typed accessors) |
|
||||
| `dvxFont.h` | Embedded 8x16 VGA bitmap font glyph data (CP437, 256 glyphs) |
|
||||
|
|
@ -48,35 +65,35 @@ Layer 1 dvxVideo VESA init, LFB mapping, backbuffer, pixel format
|
|||
| `dvxIcon.c` | stb_image implementation unit (BMP/PNG/JPEG/GIF loading) |
|
||||
| `dvxImageWrite.c` | stb_image_write implementation unit (PNG export) |
|
||||
|
||||
### Widget Infrastructure
|
||||
|
||||
The widget infrastructure lives in this directory. It provides the
|
||||
layout engine, event dispatch, class table, scrollbar primitives, and
|
||||
shared helpers that all widget types depend on. The actual widget type
|
||||
implementations (button, checkbox, listbox, etc.) are individual `.wgt`
|
||||
DXE modules in `../widgets/`.
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `widgetInternal.h` | Internal header for widget implementation files: vtable externs, shared state, helper prototypes |
|
||||
| `widgetClass.c` | Widget class table (`widgetClassTable[]`, starts empty) and `wgtRegisterClass()` dynamic registration |
|
||||
| `widgetCore.c` | Widget allocation, tree ops, hit testing, focus management, shared helpers |
|
||||
| `widgetScrollbar.c` | Widget-internal scrollbar drawing and hit testing (shared by ListBox, TreeView, etc.) |
|
||||
| `widgetLayout.c` | Two-pass layout engine: measure (bottom-up) + arrange (top-down) |
|
||||
| `widgetEvent.c` | Mouse, keyboard, scroll, resize, and paint event dispatch to widget tree |
|
||||
| `widgetOps.c` | Paint dispatch, overlay rendering, public widget operations (wgtFind, wgtDestroy, etc.) |
|
||||
|
||||
### Platform Abstraction
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `platform/dvxPlatform.h` | OS/CPU-neutral interface: video, input, span ops, filesystem |
|
||||
| `platform/dvxPlatformDos.c` | DOS/DJGPP: VESA, DPMI, INT 16h/33h, rep stosl/movsd asm spans |
|
||||
| `platform/dvxPlatformDos.c` | DOS/DJGPP: VESA, DPMI, INT 16h/33h, rep stosl/movsd asm spans, DXE export table |
|
||||
|
||||
To port DVX to a new platform, implement a new `dvxPlatformXxx.c`
|
||||
against `platform/dvxPlatform.h` and swap it in the Makefile. No other
|
||||
files need modification.
|
||||
|
||||
### Widget System
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `widgets/widgetInternal.h` | Shared internal header: vtable type, constants, cross-widget prototypes |
|
||||
| `widgets/widgetClass.c` | Widget class table: one WidgetClassT vtable entry per type |
|
||||
| `widgets/widgetCore.c` | Allocation, tree ops, hit testing, flag-based type queries |
|
||||
| `widgets/widgetLayout.c` | Two-pass layout engine (measure + arrange) |
|
||||
| `widgets/widgetEvent.c` | Mouse, keyboard, scroll, resize, and paint event dispatch |
|
||||
| `widgets/widgetOps.c` | Paint dispatcher, public operations (wgtFind, wgtDestroy, etc.) |
|
||||
| `widgets/widget*.c` | One file per widget type (button, checkbox, slider, etc.) |
|
||||
|
||||
Each widget type is self-contained in its own `.c` file. Dispatch for
|
||||
paint, layout, mouse, keyboard, getText/setText, and destroy is driven
|
||||
by a per-type vtable (WidgetClassT) rather than switch statements.
|
||||
Adding a new widget type requires only a new `.c` file and an entry in
|
||||
the class table.
|
||||
|
||||
### Third Party
|
||||
|
||||
| File | Purpose |
|
||||
|
|
@ -90,10 +107,16 @@ the class table.
|
|||
Requires the DJGPP cross-compiler (`i586-pc-msdosdjgpp-gcc`).
|
||||
|
||||
```bash
|
||||
make # builds ../lib/libdvx.a
|
||||
make clean # removes obj/ and lib/
|
||||
make # builds ../bin/libs/libdvx.lib and ../bin/libs/libdvx.dep
|
||||
make clean
|
||||
```
|
||||
|
||||
The Makefile uses `dxe3gen` to produce the DXE module. Internal symbols
|
||||
are exported with `-E` prefixes so that widget DXE modules can link
|
||||
against them at runtime (`-E _widget`, `-E _sCursor`, `-E _dvx`,
|
||||
`-E _wgt`, `-E _wm`, `-E _draw`, etc.). The dependency file
|
||||
`libdvx.dep` is copied from `../config/libdvx.dep` to `../bin/libs/`.
|
||||
|
||||
Set `DJGPP_PREFIX` in the Makefile if your toolchain is installed
|
||||
somewhere other than `~/djgpp/djgpp`.
|
||||
|
||||
|
|
@ -854,7 +877,17 @@ A retained-mode widget toolkit layered on top of the window manager.
|
|||
Widgets form a tree rooted at a per-window VBox container. Layout is
|
||||
automatic via a flexbox-like algorithm with weighted space distribution.
|
||||
|
||||
### Widget Catalog (32 types)
|
||||
Widget type implementations are NOT compiled into libdvx. Each widget
|
||||
type is a separate `.wgt` DXE module in `../widgets/` that registers
|
||||
its `WidgetClassT` vtable into `widgetClassTable[]` at load time. The
|
||||
`WidgetClassT` struct and `WCLASS_*` flags are defined in `dvxWidget.h`
|
||||
(the public API header) so that DXE plugins can create and register new
|
||||
widget types.
|
||||
|
||||
### Widget Catalog (32 built-in types)
|
||||
|
||||
Each type below is implemented in its own `.wgt` DXE module and loaded
|
||||
by the DVX shell at startup.
|
||||
|
||||
#### Containers
|
||||
|
||||
|
|
@ -926,6 +959,51 @@ automatic via a flexbox-like algorithm with weighted space distribution.
|
|||
| Timer | `wgtTimer(parent, intervalMs, repeat)` | Invisible callback timer (one-shot or repeating) |
|
||||
|
||||
|
||||
### WidgetClassT Vtable
|
||||
|
||||
Each widget type has a `WidgetClassT` vtable that defines its behavior.
|
||||
This struct is defined in `dvxWidget.h` so that DXE plugins can create
|
||||
their own widget classes.
|
||||
|
||||
```c
|
||||
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;
|
||||
```
|
||||
|
||||
Class flags (`WCLASS_*`):
|
||||
|
||||
| Flag | Value | Meaning |
|
||||
|------|-------|---------|
|
||||
| `WCLASS_FOCUSABLE` | 0x0001 | Can receive keyboard focus (Tab navigation) |
|
||||
| `WCLASS_BOX_CONTAINER` | 0x0002 | Uses the generic VBox/HBox layout algorithm |
|
||||
| `WCLASS_HORIZ_CONTAINER` | 0x0004 | Lays out children horizontally (vs. vertical) |
|
||||
| `WCLASS_PAINTS_CHILDREN` | 0x0008 | Widget handles child rendering itself |
|
||||
| `WCLASS_NO_HIT_RECURSE` | 0x0010 | Hit testing stops here, no child recursion |
|
||||
|
||||
### Dynamic Widget Registration
|
||||
|
||||
```c
|
||||
int32_t wgtRegisterClass(const WidgetClassT *wclass);
|
||||
```
|
||||
|
||||
Register a new widget class at runtime. Returns the assigned type ID
|
||||
(>= `WGT_TYPE_DYNAMIC_BASE`, which is 32) 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).
|
||||
|
||||
The `customData` field on `WidgetT` is available for DXE widget plugins
|
||||
to store private per-widget data (NULL for built-in types).
|
||||
|
||||
### Widget Event Model
|
||||
|
||||
All widget types share a universal set of event callbacks set directly
|
||||
|
|
@ -942,8 +1020,9 @@ on the WidgetT struct:
|
|||
Type-specific handlers (button press animation, listbox selection
|
||||
highlight) run first, then these universal callbacks fire.
|
||||
|
||||
Additional per-widget fields: `userData` (void pointer), `tooltip`
|
||||
(hover text), `contextMenu` (right-click menu).
|
||||
Additional per-widget fields: `userData` (void pointer), `customData`
|
||||
(DXE plugin private data), `tooltip` (hover text), `contextMenu`
|
||||
(right-click menu).
|
||||
|
||||
### Initialization
|
||||
|
||||
|
|
|
|||
73
loader/README.md
Normal file
73
loader/README.md
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# DVX Loader
|
||||
|
||||
Bootstrap loader for the DVX desktop environment. Builds as
|
||||
`dvx.exe` -- the only native executable in the system. Everything
|
||||
else is a dynamically loaded DXE module.
|
||||
|
||||
|
||||
## What It Does
|
||||
|
||||
1. Changes to the executable's directory so relative paths resolve
|
||||
2. Calls `platformRegisterDxeExports()` to register platform functions
|
||||
and C runtime symbols (libc, libm, libgcc, DJGPP internals) with
|
||||
the DXE3 loader via `dlregsym()`
|
||||
3. Recursively scans `LIBS/` for `*.lib` modules and `WIDGETS/` for
|
||||
`*.wgt` modules
|
||||
4. Reads `.dep` files to build a dependency graph
|
||||
5. Topologically sorts all modules and loads them in order with
|
||||
`RTLD_GLOBAL` (each module's exports become available to
|
||||
subsequent modules)
|
||||
6. Calls `wgtRegister()` on any module that exports it (widget
|
||||
registration)
|
||||
7. Finds `shellMain()` across all loaded modules and calls it
|
||||
8. On return, closes all modules in reverse order
|
||||
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `loaderMain.c` | Entry point, directory scanning, dependency resolution, module loading |
|
||||
| `Makefile` | Cross-compilation rules; links `dvxPlatformDos.c` directly |
|
||||
|
||||
The DXE export table lives in `core/platform/dvxPlatformDos.c`, not
|
||||
in the loader. The loader is platform-agnostic -- all DJGPP/DXE3
|
||||
knowledge is in the platform layer.
|
||||
|
||||
|
||||
## Dependency Resolution
|
||||
|
||||
Each module may have a `.dep` file alongside it (same base name,
|
||||
`.dep` extension). The file lists base names of modules that must
|
||||
be loaded first, one per line. Lines starting with `#` are comments.
|
||||
|
||||
Example -- `dvxshell.dep`:
|
||||
```
|
||||
libtasks
|
||||
libdvx
|
||||
box
|
||||
button
|
||||
label
|
||||
listview
|
||||
```
|
||||
|
||||
A dependency is satisfied when either:
|
||||
- A module with that base name has been loaded, or
|
||||
- No module with that base name exists in the scan (external, assumed OK)
|
||||
|
||||
The loader iterates until all modules are loaded or no further
|
||||
progress can be made (circular dependency). Unloadable modules are
|
||||
reported to stderr.
|
||||
|
||||
|
||||
## Building
|
||||
|
||||
```
|
||||
make # builds ../bin/dvx.exe
|
||||
make clean
|
||||
```
|
||||
|
||||
The Makefile compiles `loaderMain.c` and links it with
|
||||
`dvxPlatformDos.o` (compiled from `../core/platform/dvxPlatformDos.c`)
|
||||
and `-lm`. The result goes through `exe2coff` + `CWSDSTUB` to
|
||||
produce a DOS executable.
|
||||
165
shell/README.md
165
shell/README.md
|
|
@ -1,67 +1,101 @@
|
|||
# DVX Shell
|
||||
|
||||
The DVX Shell (`dvx.exe`) is the host process for the DVX desktop environment.
|
||||
It initializes the GUI subsystem, loads DXE3 application modules at runtime,
|
||||
runs the cooperative main loop, and provides crash recovery so a faulting app
|
||||
does not take down the entire system. Think of it as the Windows 3.1 Program
|
||||
Manager and kernel combined into one executable.
|
||||
The DVX Shell (`dvxshell.lib`) is a DXE module loaded by the DVX loader at
|
||||
startup. It initializes the GUI subsystem, loads DXE3 application modules at
|
||||
runtime, runs the cooperative main loop, and provides crash recovery so a
|
||||
faulting app does not take down the entire system.
|
||||
|
||||
The shell is not a standalone executable. The loader maps it into memory via
|
||||
`dlopen`, resolves its entry point (`shellMain`), and calls it. All symbols
|
||||
the shell needs -- libdvx, libtasks, widgets, libc -- are resolved at load
|
||||
time from DXE modules already loaded by the loader and the loader's own
|
||||
`dlregsym` table.
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `shellMain.c` | Entry point, main loop, crash recovery, logging, desktop update callbacks |
|
||||
| `shellApp.c` | App loading (dlopen/dlsym), lifecycle state machine, reaping, resource tracking, config dirs |
|
||||
| `shellApp.h` | `ShellAppT`, `AppDescriptorT`, `AppStateE`, `DxeAppContextT`, public shell API |
|
||||
| `shellExport.c` | DXE export table (400+ symbols), wrapper functions for resource tracking |
|
||||
| `shellInfo.c` | System information gathering (delegates to platform layer), caches result |
|
||||
| `shellMain.c` | Entry point (`shellMain`), main loop, crash recovery, logging |
|
||||
| `shellApp.c` | App loading via `dlopen`/`dlsym`, lifecycle, reaping |
|
||||
| `shellApp.h` | `ShellAppT`, `AppDescriptorT`, `AppStateE`, `DxeAppContextT`, API |
|
||||
| `shellExport.c` | 3 DXE wrapper overrides for window resource tracking |
|
||||
| `shellInfo.c` | System info gathering |
|
||||
| `shellInfo.h` | `shellInfoInit()`, `shellGetSystemInfo()` |
|
||||
| `shellTaskMgr.c` | Task Manager window -- list view, Switch To / End Task / Run buttons |
|
||||
| `shellTaskMgr.c` | Task Manager window |
|
||||
| `shellTaskMgr.h` | `shellTaskMgrOpen()`, `shellTaskMgrRefresh()` |
|
||||
| `Makefile` | Cross-compile rules, links `-ldvx -ltasks -lm` |
|
||||
| `Makefile` | Cross-compile rules, produces `dvxshell.lib` and `dvxshell.dep` |
|
||||
|
||||
## Building
|
||||
|
||||
```
|
||||
make # builds ../bin/dvx.exe (also builds libdvx.a, libtasks.a)
|
||||
make clean # removes objects and binary
|
||||
make # builds ../bin/libs/dvxshell.lib and ../bin/libs/dvxshell.dep
|
||||
make clean # removes objects, binary, and deployed config files
|
||||
```
|
||||
|
||||
CFLAGS: `-O2 -Wall -Wextra -march=i486 -mtune=i586 -I../core -I../core/platform -I../tasks -I../tasks/thirdparty`
|
||||
|
||||
The shell is compiled to object files and packaged into a DXE via `dxe3gen`.
|
||||
It exports `_shell` so the loader can resolve `shellMain`. All other symbols
|
||||
are unresolved imports (`-U`) satisfied at load time.
|
||||
|
||||
Requires the DJGPP cross-compiler toolchain and the DXE3 tools (`dxe3gen`).
|
||||
|
||||
## Dependencies
|
||||
|
||||
`dvxshell.dep` lists the modules the loader must load before the shell:
|
||||
|
||||
```
|
||||
libtasks
|
||||
libdvx
|
||||
box
|
||||
button
|
||||
checkbox
|
||||
dropdown
|
||||
label
|
||||
listbox
|
||||
listview
|
||||
radio
|
||||
separator
|
||||
spacer
|
||||
statbar
|
||||
textinpt
|
||||
```
|
||||
|
||||
The loader reads this file, loads each module via `dlopen` with `RTLD_GLOBAL`,
|
||||
then loads the shell itself. This is how all symbols (dvx*, wgt*, ts*, etc.)
|
||||
become available without the shell statically linking anything.
|
||||
|
||||
## Startup Sequence
|
||||
|
||||
`main()` in `shellMain.c` performs initialization in this order:
|
||||
`shellMain()` in `shellMain.c` is called by the loader after all dependencies
|
||||
are loaded. It performs initialization in this order:
|
||||
|
||||
1. **Change to exe directory** -- resolve the directory containing `dvx.exe`
|
||||
via `argv[0]` and `platformChdir()` so that relative paths (`CONFIG/`,
|
||||
`APPS/`, etc.) work regardless of where the user launched from.
|
||||
2. **Truncate log** -- open `dvx.log` for write to clear it, then close.
|
||||
1. **Truncate log** -- open `dvx.log` for write to clear it, then close.
|
||||
All subsequent writes use append-per-write (the file is never held open).
|
||||
3. **Load preferences** -- `prefsLoad("CONFIG/DVX.INI")`. Missing file or
|
||||
2. **Load preferences** -- `prefsLoad("CONFIG/DVX.INI")`. Missing file or
|
||||
keys silently fall back to compiled-in defaults.
|
||||
4. **dvxInit** -- initialize VESA video (LFB), backbuffer, compositor, window
|
||||
3. **dvxInit** -- initialize VESA video (LFB), backbuffer, compositor, window
|
||||
manager, font, cursor, input subsystems. Reads video width/height/bpp from
|
||||
preferences (default 640x480x16).
|
||||
5. **Mouse config** -- read wheel direction, double-click speed, acceleration
|
||||
4. **Mouse config** -- read wheel direction, double-click speed, acceleration
|
||||
from `[mouse]` section and call `dvxSetMouseConfig()`.
|
||||
6. **Color scheme** -- read `[colors]` section (20 RGB triplets), apply via
|
||||
5. **Color scheme** -- read `[colors]` section (20 RGB triplets), apply via
|
||||
`dvxApplyColorScheme()`.
|
||||
7. **Wallpaper** -- read `[desktop]` section for wallpaper path and mode
|
||||
6. **Wallpaper** -- read `[desktop]` section for wallpaper path and mode
|
||||
(stretch/tile/center), load via `dvxSetWallpaper()`.
|
||||
8. **Video mode log** -- enumerate all available VESA modes to `dvx.log`.
|
||||
9. **Task system** -- `tsInit()`, set shell task (task 0) to
|
||||
7. **Video mode log** -- enumerate all available VESA modes to `dvx.log`.
|
||||
8. **Task system** -- `tsInit()`, set shell task (task 0) to
|
||||
`TS_PRIORITY_HIGH` so the UI stays responsive under load.
|
||||
10. **System info** -- `shellInfoInit()` gathers CPU, memory, drive info via
|
||||
the platform layer and logs it.
|
||||
11. **DXE exports** -- `shellExportInit()` calls `dlregsym()` to register the
|
||||
export table. Must happen before any `dlopen()`.
|
||||
12. **App slot table** -- `shellAppInit()` zeroes the 32-slot fixed array.
|
||||
13. **Idle/hotkey callbacks** -- wire up `idleYield`, `ctrlEscHandler`,
|
||||
9. **System info** -- `shellInfoInit()` gathers CPU, memory, drive info via
|
||||
the platform layer and logs it.
|
||||
10. **DXE exports** -- `shellExportInit()` calls `dlregsym()` to register the
|
||||
3 wrapper overrides. Must happen before any `dlopen()` of app DXEs.
|
||||
11. **App slot table** -- `shellAppInit()` zeroes the 32-slot fixed array.
|
||||
12. **Idle/hotkey callbacks** -- wire up `idleYield`, `ctrlEscHandler`,
|
||||
`titleChangeHandler` on the `AppContextT`.
|
||||
14. **Desktop app** -- `shellLoadApp(ctx, "apps/progman/progman.app")`. If
|
||||
13. **Desktop app** -- `shellLoadApp(ctx, "apps/progman/progman.app")`. If
|
||||
this fails, the shell exits.
|
||||
15. **Crash handlers** -- `installCrashHandler()` registers signal handlers
|
||||
14. **Crash handlers** -- `installCrashHandler()` registers signal handlers
|
||||
for SIGSEGV, SIGFPE, SIGILL. Installed last so initialization crashes
|
||||
get the default DJGPP abort-with-register-dump instead of our recovery
|
||||
path.
|
||||
|
|
@ -100,7 +134,7 @@ The shell provides Windows 3.1-style fault tolerance using `setjmp`/`longjmp`:
|
|||
|
||||
1. `installCrashHandler()` registers `crashHandler` for SIGSEGV, SIGFPE,
|
||||
SIGILL.
|
||||
2. `setjmp(sCrashJmp)` in `main()` establishes the recovery point.
|
||||
2. `setjmp(sCrashJmp)` in `shellMain()` establishes the recovery point.
|
||||
3. If a signal fires (in any task), `crashHandler` logs the crash details
|
||||
(signal, app name, full register dump from `__djgpp_exception_state_ptr`),
|
||||
re-installs the handler (DJGPP uses SysV semantics -- handler resets to
|
||||
|
|
@ -122,7 +156,8 @@ registers, and EFLAGS -- invaluable for post-mortem debugging.
|
|||
### DXE3 Loading
|
||||
|
||||
DXE3 is DJGPP's dynamic linking mechanism. Each `.app` file is a DXE3 shared
|
||||
object. The shell resolves symbols from the export table registered via
|
||||
object. Symbols are resolved from the loaded RTLD_GLOBAL modules (libdvx,
|
||||
libtasks, widgets) and the shell's 3 wrapper overrides registered via
|
||||
`dlregsym()`. The load sequence in `shellLoadApp()`:
|
||||
|
||||
1. Allocate a slot from the 32-entry fixed array (slot 0 is the shell).
|
||||
|
|
@ -216,10 +251,31 @@ to which app, enabling:
|
|||
`sCurrentAppId` is a simple global (not thread-local) because cooperative
|
||||
multitasking means only one task runs at a time.
|
||||
|
||||
## DXE Export Table
|
||||
|
||||
`shellExport.c` registers 3 wrapper functions via `dlregsym()` that override
|
||||
the real implementations for subsequently loaded app DXEs:
|
||||
|
||||
1. `dvxCreateWindow` -- stamps `win->appId` for resource ownership tracking.
|
||||
2. `dvxCreateWindowCentered` -- stamps `win->appId` for resource ownership
|
||||
tracking.
|
||||
3. `dvxDestroyWindow` -- checks if the destroyed window was a callback-only
|
||||
app's last window and marks it for reaping.
|
||||
|
||||
The key mechanic: `dlregsym` takes precedence over `RTLD_GLOBAL` exports.
|
||||
Since libdvx (which has the real functions) was loaded before
|
||||
`shellExportInit()` registers these wrappers, libdvx keeps the real
|
||||
implementations. But any app DXE loaded afterward gets the wrappers, which
|
||||
add resource tracking transparently.
|
||||
|
||||
All other symbol exports -- dvx*, wgt*, ts*, platform*, libc, libm -- come
|
||||
from the DXE modules loaded with `RTLD_GLOBAL` by the loader. They no longer
|
||||
need to be listed in the shell's export table.
|
||||
|
||||
## Task Manager
|
||||
|
||||
`shellTaskMgr.c` implements a shell-level Task Manager accessible via
|
||||
**Ctrl+Esc** regardless of which app is focused or whether the desktop app
|
||||
Ctrl+Esc regardless of which app is focused or whether the desktop app
|
||||
is running. It is owned by the shell (appId = 0), not by any DXE app.
|
||||
|
||||
Features:
|
||||
|
|
@ -262,38 +318,6 @@ API:
|
|||
Apps use the standard preferences system (`prefsLoad`/`prefsSave`) pointed at
|
||||
their config directory for persistent settings.
|
||||
|
||||
## DXE Export Table
|
||||
|
||||
`shellExport.c` contains the ABI contract between the shell and apps. Three
|
||||
categories of exports:
|
||||
|
||||
1. **Wrapped functions** (3): `dvxCreateWindow`, `dvxCreateWindowCentered`,
|
||||
`dvxDestroyWindow`. These are intercepted to stamp `win->appId` for
|
||||
resource ownership tracking. Apps see them under their original names --
|
||||
the wrapping is transparent.
|
||||
|
||||
2. **Direct exports** (200+): all other `dvx*`, `wgt*`, `wm*`, `ts*`,
|
||||
drawing, preferences, platform, and shell API functions. Safe to call
|
||||
without interception.
|
||||
|
||||
3. **libc / libm / runtime exports** (200+): DXE3 modules are relocatable
|
||||
objects, not fully linked executables. Every C library function a DXE
|
||||
calls must be explicitly listed so the loader can resolve it at dlopen
|
||||
time. This includes:
|
||||
- Memory (malloc, calloc, realloc, free)
|
||||
- String operations (str*, mem*)
|
||||
- Formatted I/O (printf, snprintf, fprintf, sscanf, etc.)
|
||||
- File I/O (fopen, fread, fwrite, fclose, etc.)
|
||||
- Directory operations (opendir, readdir, closedir, mkdir)
|
||||
- Time (time, localtime, clock, strftime)
|
||||
- Math (sin, cos, sqrt, pow, floor, ceil, etc.)
|
||||
- stb_ds internals (arrput/arrfree/hm* macro backends)
|
||||
- stb_image / stb_image_write
|
||||
- libgcc 64-bit integer helpers (__divdi3, __moddi3, etc.)
|
||||
- DJGPP stdio internals (__dj_stdin, __dj_stdout, __dj_stderr)
|
||||
|
||||
The table is registered once via `dlregsym()` before any `dlopen()`.
|
||||
|
||||
## System Hotkeys
|
||||
|
||||
These are always active regardless of which app is focused:
|
||||
|
|
@ -341,6 +365,7 @@ full register dumps.
|
|||
|
||||
| Function | Description |
|
||||
|----------|-------------|
|
||||
| `shellMain(argc, argv)` | Entry point called by the DVX loader |
|
||||
| `shellAppInit()` | Zero the 32-slot app table |
|
||||
| `shellLoadApp(ctx, path)` | Load and start a DXE app. Returns app ID (>= 1) or -1 |
|
||||
| `shellReapApps(ctx)` | Clean up terminated apps (call each frame). Returns true if any reaped |
|
||||
|
|
@ -352,7 +377,7 @@ full register dumps.
|
|||
| `shellLog(fmt, ...)` | Append to dvx.log |
|
||||
| `shellEnsureConfigDir(ctx)` | Create an app's config directory tree |
|
||||
| `shellConfigPath(ctx, name, buf, size)` | Build path to file in app's config dir |
|
||||
| `shellExportInit()` | Register DXE symbol export table via dlregsym() |
|
||||
| `shellExportInit()` | Register 3 DXE wrapper overrides via dlregsym() |
|
||||
| `shellRegisterDesktopUpdate(fn)` | Register callback for app state changes |
|
||||
| `shellUnregisterDesktopUpdate(fn)` | Remove a previously registered callback |
|
||||
| `shellDesktopUpdate()` | Notify all registered listeners |
|
||||
|
|
|
|||
180
tasks/README.md
180
tasks/README.md
|
|
@ -25,13 +25,12 @@ lets each task yield at safe points, avoiding synchronization entirely.
|
|||
|
||||
## Files
|
||||
|
||||
| File | Description |
|
||||
|-----------------------|----------------------------------------------------|
|
||||
| `taskswitch.h` | Public API -- types, constants, function prototypes |
|
||||
| `taskswitch.c` | Implementation (scheduler, context switch, slots) |
|
||||
| `demo.c` | Standalone test harness exercising all features |
|
||||
| `thirdparty/stb_ds.h` | stb dynamic array/hashmap library (third-party) |
|
||||
| `Makefile` | DJGPP cross-compilation build rules |
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `taskswitch.h` | Public API -- types, constants, function prototypes |
|
||||
| `taskswitch.c` | Implementation (scheduler, context switch, slots, includes stb_ds implementation) |
|
||||
| `thirdparty/stb_ds.h` | stb dynamic array/hashmap library (third-party) |
|
||||
| `Makefile` | DJGPP cross-compilation build rules |
|
||||
|
||||
|
||||
## Building
|
||||
|
|
@ -39,18 +38,25 @@ lets each task yield at safe points, avoiding synchronization entirely.
|
|||
Cross-compile from Linux:
|
||||
|
||||
```
|
||||
make # builds ../lib/libtasks.a
|
||||
make demo # also builds ../bin/tsdemo.exe
|
||||
make clean # removes objects, library, and demo binary
|
||||
make # builds ../bin/libs/libtasks.lib
|
||||
make clean # removes objects and library
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
| Path | Description |
|
||||
|---------------------|----------------------|
|
||||
| `../lib/libtasks.a` | Static library |
|
||||
| `../obj/tasks/` | Object files |
|
||||
| `../bin/tsdemo.exe` | Demo executable |
|
||||
| Path | Description |
|
||||
|------|-------------|
|
||||
| `../bin/libs/libtasks.lib` | DXE module |
|
||||
| `../obj/tasks/` | Object files |
|
||||
|
||||
The library is compiled to an object file and packaged into a DXE via
|
||||
`dxe3gen`. It exports all `ts*` symbols (`-E _ts`) and stb_ds internals
|
||||
(`-E _stbds_`) so that other DXE modules loaded with `RTLD_GLOBAL` can
|
||||
use both the task API and stb_ds data structures. All other symbols are
|
||||
unresolved imports (`-U`) resolved at load time from the loader's
|
||||
`dlregsym` table.
|
||||
|
||||
Requires the DJGPP cross-compiler toolchain and the DXE3 tools (`dxe3gen`).
|
||||
|
||||
|
||||
## Quick Start
|
||||
|
|
@ -109,75 +115,75 @@ available for reuse by the next `tsCreate()` call.
|
|||
|
||||
### Initialization and Teardown
|
||||
|
||||
| Function | Signature | Description |
|
||||
|--------------|-------------------------|--------------------------------------------------------------------|
|
||||
| `tsInit` | `int32_t tsInit(void)` | Initialize the library. Returns `TS_OK` or a negative error code. |
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `tsInit` | `int32_t tsInit(void)` | Initialize the library. Returns `TS_OK` or a negative error code. |
|
||||
| `tsShutdown` | `void tsShutdown(void)` | Free all resources. Safe to call even if `tsInit` was never called. |
|
||||
|
||||
### Task Creation and Termination
|
||||
|
||||
| Function | Signature | Description |
|
||||
|------------|---------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `tsCreate` | `int32_t tsCreate(const char *name, TaskEntryT entry, void *arg, uint32_t ss, int32_t pri)` | Create a ready task. Returns the task ID (>= 0) or a negative error code. Pass 0 for `ss` to use `TS_DEFAULT_STACK_SIZE`. Reuses terminated slots. |
|
||||
| `tsExit` | `void tsExit(void)` | Terminate the calling task. Must not be called from the main task. Never returns. |
|
||||
| `tsKill` | `int32_t tsKill(uint32_t taskId)` | Forcibly terminate another task. Cannot kill main (id 0) or self (use `tsExit` instead). |
|
||||
| `tsExit` | `void tsExit(void)` | Terminate the calling task. Must not be called from the main task. Never returns. |
|
||||
| `tsKill` | `int32_t tsKill(uint32_t taskId)` | Forcibly terminate another task. Cannot kill main (id 0) or self (use `tsExit` instead). |
|
||||
|
||||
### Scheduling
|
||||
|
||||
| Function | Signature | Description |
|
||||
|-----------|----------------------|-----------------------------------------------------------------|
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `tsYield` | `void tsYield(void)` | Voluntarily relinquish the CPU to the next eligible ready task. |
|
||||
|
||||
### Pausing and Resuming
|
||||
|
||||
| Function | Signature | Description |
|
||||
|------------|---------------------------------|------------------------------------------------------------------------------------------------------------|
|
||||
| `tsPause` | `int32_t tsPause(uint32_t id)` | Pause a task. Main task (id 0) cannot be paused. Self-pause triggers an implicit yield. |
|
||||
| `tsResume` | `int32_t tsResume(uint32_t id)` | Resume a paused task. Credits are refilled so it is not penalized for having been paused. |
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `tsPause` | `int32_t tsPause(uint32_t id)` | Pause a task. Main task (id 0) cannot be paused. Self-pause triggers an implicit yield. |
|
||||
| `tsResume` | `int32_t tsResume(uint32_t id)` | Resume a paused task. Credits are refilled so it is not penalized for having been paused. |
|
||||
|
||||
### Priority
|
||||
|
||||
| Function | Signature | Description |
|
||||
|-----------------|---------------------------------------------------|-----------------------------------------------------------------------------------|
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `tsSetPriority` | `int32_t tsSetPriority(uint32_t id, int32_t pri)` | Change a task's priority. Credits are reset so the change takes effect immediately. |
|
||||
| `tsGetPriority` | `int32_t tsGetPriority(uint32_t id)` | Return the task's priority, or `TS_ERR_PARAM` on an invalid ID. |
|
||||
| `tsGetPriority` | `int32_t tsGetPriority(uint32_t id)` | Return the task's priority, or `TS_ERR_PARAM` on an invalid ID. |
|
||||
|
||||
### Crash Recovery
|
||||
|
||||
| Function | Signature | Description |
|
||||
|-------------------|------------------------------|------------------------------------------------------------------------------------------------------------------|
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `tsRecoverToMain` | `void tsRecoverToMain(void)` | Reset scheduler state to task 0 after a `longjmp` from a signal handler. Call before `tsKill` on the crashed task. The crashed task's slot is NOT freed -- call `tsKill` afterward. |
|
||||
|
||||
### Query
|
||||
|
||||
| Function | Signature | Description |
|
||||
|-----------------|--------------------------------------|--------------------------------------------------------|
|
||||
| `tsGetState` | `TaskStateE tsGetState(uint32_t id)` | Return the task's state enum value. |
|
||||
| `tsCurrentId` | `uint32_t tsCurrentId(void)` | Return the ID of the currently running task. |
|
||||
| `tsGetName` | `const char *tsGetName(uint32_t id)` | Return the task's name string, or `NULL` on invalid ID. |
|
||||
| `tsActiveCount` | `uint32_t tsActiveCount(void)` | Return the number of non-terminated tasks. |
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `tsGetState` | `TaskStateE tsGetState(uint32_t id)` | Return the task's state enum value. |
|
||||
| `tsCurrentId` | `uint32_t tsCurrentId(void)` | Return the ID of the currently running task. |
|
||||
| `tsGetName` | `const char *tsGetName(uint32_t id)` | Return the task's name string, or `NULL` on invalid ID. |
|
||||
| `tsActiveCount` | `uint32_t tsActiveCount(void)` | Return the number of non-terminated tasks. |
|
||||
|
||||
|
||||
## Constants
|
||||
|
||||
### Error Codes
|
||||
|
||||
| Name | Value | Meaning |
|
||||
|----------------|-------|--------------------------------------------------|
|
||||
| `TS_OK` | 0 | Success |
|
||||
| `TS_ERR_INIT` | -1 | Library not initialized |
|
||||
| `TS_ERR_PARAM` | -2 | Invalid parameter |
|
||||
| `TS_ERR_FULL` | -3 | Task table full (unused, kept for compatibility) |
|
||||
| `TS_ERR_NOMEM` | -4 | Memory allocation failed |
|
||||
| `TS_ERR_STATE` | -5 | Invalid state transition |
|
||||
| Name | Value | Meaning |
|
||||
|------|-------|---------|
|
||||
| `TS_OK` | 0 | Success |
|
||||
| `TS_ERR_INIT` | -1 | Library not initialized |
|
||||
| `TS_ERR_PARAM` | -2 | Invalid parameter |
|
||||
| `TS_ERR_FULL` | -3 | Task table full (unused, kept for compatibility) |
|
||||
| `TS_ERR_NOMEM` | -4 | Memory allocation failed |
|
||||
| `TS_ERR_STATE` | -5 | Invalid state transition |
|
||||
|
||||
### Priority Presets
|
||||
|
||||
| Name | Value | Credits per Round |
|
||||
|----------------------|-------|-------------------|
|
||||
| `TS_PRIORITY_LOW` | 0 | 1 |
|
||||
| `TS_PRIORITY_NORMAL` | 5 | 6 |
|
||||
| `TS_PRIORITY_HIGH` | 10 | 11 |
|
||||
| Name | Value | Credits per Round |
|
||||
|------|-------|-------------------|
|
||||
| `TS_PRIORITY_LOW` | 0 | 1 |
|
||||
| `TS_PRIORITY_NORMAL` | 5 | 6 |
|
||||
| `TS_PRIORITY_HIGH` | 10 | 11 |
|
||||
|
||||
Any non-negative `int32_t` may be used as a priority. The presets are
|
||||
provided for convenience. In the DVX Shell, the main task runs at
|
||||
|
|
@ -186,10 +192,10 @@ provided for convenience. In the DVX Shell, the main task runs at
|
|||
|
||||
### Defaults
|
||||
|
||||
| Name | Value | Description |
|
||||
|-------------------------|-------|------------------------|
|
||||
| Name | Value | Description |
|
||||
|------|-------|-------------|
|
||||
| `TS_DEFAULT_STACK_SIZE` | 32768 | Default stack per task |
|
||||
| `TS_NAME_MAX` | 32 | Max task name length |
|
||||
| `TS_NAME_MAX` | 32 | Max task name length |
|
||||
|
||||
|
||||
## Types
|
||||
|
|
@ -280,29 +286,29 @@ versions.
|
|||
|
||||
Six callee-saved values are saved and restored per switch:
|
||||
|
||||
| Register | Offset | Purpose |
|
||||
|----------|--------|------------------------------------------|
|
||||
| EBX | 0 | Callee-saved general purpose |
|
||||
| ESI | 4 | Callee-saved general purpose |
|
||||
| EDI | 8 | Callee-saved general purpose |
|
||||
| EBP | 12 | Frame pointer |
|
||||
| ESP | 16 | Stack pointer |
|
||||
| EIP | 20 | Resume address (captured as local label) |
|
||||
| Register | Offset | Purpose |
|
||||
|----------|--------|---------|
|
||||
| EBX | 0 | Callee-saved general purpose |
|
||||
| ESI | 4 | Callee-saved general purpose |
|
||||
| EDI | 8 | Callee-saved general purpose |
|
||||
| EBP | 12 | Frame pointer |
|
||||
| ESP | 16 | Stack pointer |
|
||||
| EIP | 20 | Resume address (captured as local label) |
|
||||
|
||||
### x86_64 (for native Linux testing)
|
||||
|
||||
Eight callee-saved values are saved and restored per switch:
|
||||
|
||||
| Register | Offset | Purpose |
|
||||
|----------|--------|------------------------------------------|
|
||||
| RBX | 0 | Callee-saved general purpose |
|
||||
| R12 | 8 | Callee-saved general purpose |
|
||||
| R13 | 16 | Callee-saved general purpose |
|
||||
| R14 | 24 | Callee-saved general purpose |
|
||||
| R15 | 32 | Callee-saved general purpose |
|
||||
| RBP | 40 | Frame pointer |
|
||||
| RSP | 48 | Stack pointer |
|
||||
| RIP | 56 | Resume address (RIP-relative lea) |
|
||||
| Register | Offset | Purpose |
|
||||
|----------|--------|---------|
|
||||
| RBX | 0 | Callee-saved general purpose |
|
||||
| R12 | 8 | Callee-saved general purpose |
|
||||
| R13 | 16 | Callee-saved general purpose |
|
||||
| R14 | 24 | Callee-saved general purpose |
|
||||
| R15 | 32 | Callee-saved general purpose |
|
||||
| RBP | 40 | Frame pointer |
|
||||
| RSP | 48 | Stack pointer |
|
||||
| RIP | 56 | Resume address (RIP-relative lea) |
|
||||
|
||||
Segment registers are not saved because DJGPP runs in a flat
|
||||
protected-mode environment where CS, DS, ES, and SS share the same
|
||||
|
|
@ -326,32 +332,10 @@ and then `tsExit()`.
|
|||
each task's needs.
|
||||
|
||||
|
||||
## Demo
|
||||
|
||||
`demo.c` exercises five phases:
|
||||
|
||||
1. **Priority scheduling** -- creates tasks at low, normal, and high
|
||||
priority. All tasks run, but the high-priority task gets significantly
|
||||
more turns.
|
||||
2. **Pause** -- pauses one task mid-run and shows it stops being
|
||||
scheduled.
|
||||
3. **Resume** -- resumes the paused task and shows it picks up where it
|
||||
left off.
|
||||
4. **Priority boost** -- raises the low-priority task above all others
|
||||
and shows it immediately gets more turns.
|
||||
5. **Slot reuse** -- creates three waves of short-lived tasks that
|
||||
terminate and shows subsequent waves reuse the same task IDs.
|
||||
|
||||
Build and run:
|
||||
|
||||
```
|
||||
make demo
|
||||
tsdemo
|
||||
```
|
||||
|
||||
|
||||
## Third-Party Dependencies
|
||||
|
||||
- **stb_ds.h** (Sean Barrett) -- dynamic array and hashmap library.
|
||||
Located in `thirdparty/stb_ds.h`. Used for the task control block
|
||||
array. Public domain / MIT licensed.
|
||||
array. Included in the DXE build and exported via `-E _stbds_` so
|
||||
other modules can use stb_ds without bundling their own copy.
|
||||
Public domain / MIT licensed.
|
||||
|
|
|
|||
95
widgets/README.md
Normal file
95
widgets/README.md
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
# DVX Widget Modules
|
||||
|
||||
Individual widget type implementations for the DVX GUI, each built
|
||||
as a separate `.wgt` DXE module. The loader scans the `WIDGETS/`
|
||||
directory recursively at startup and loads every `.wgt` file it
|
||||
finds, calling `wgtRegister()` on each to fill the widget class
|
||||
table.
|
||||
|
||||
Drop a new `.wgt` file in the directory and it is automatically
|
||||
available -- no loader or core library changes needed.
|
||||
|
||||
|
||||
## How Widget DXEs Work
|
||||
|
||||
Each `.wgt` module contains:
|
||||
|
||||
1. The widget implementation (paint, layout, mouse/key handlers)
|
||||
2. A `static const WidgetClassT` vtable definition
|
||||
3. A `wgtRegister()` function that writes the vtable pointer into
|
||||
`widgetClassTable[]` at the widget's enum slot
|
||||
4. The public API functions (e.g. `wgtTreeView()`, `wgtTreeViewGetSelected()`)
|
||||
|
||||
At load time, the module resolves its dependencies against:
|
||||
- The loader's `dlregsym` table (platform functions, libc)
|
||||
- `libtasks.lib` exports (stb_ds dynamic arrays)
|
||||
- `libdvx.lib` exports (widget infrastructure: `widgetAlloc`,
|
||||
`widgetClassTable`, `widgetScrollbarThumb`, shared state, etc.)
|
||||
- Other widget modules loaded earlier (via `RTLD_GLOBAL`)
|
||||
|
||||
|
||||
## Widget Catalog
|
||||
|
||||
| Module | Widget Types | Description |
|
||||
|--------|-------------|-------------|
|
||||
| `box.wgt` | VBox, HBox, Frame | Containers: vertical/horizontal box layout, titled frame |
|
||||
| `button.wgt` | Button | Push button with text and keyboard accelerator |
|
||||
| `canvas.wgt` | Canvas | Freeform drawing surface with mouse callbacks |
|
||||
| `checkbox.wgt` | Checkbox | Toggle checkbox with label |
|
||||
| `combobox.wgt` | ComboBox | Editable text input with dropdown list |
|
||||
| `dropdown.wgt` | Dropdown | Non-editable dropdown selector |
|
||||
| `image.wgt` | Image | Static image display (BMP/PNG/JPEG/GIF) |
|
||||
| `imgbtn.wgt` | ImageButton | Button with icon image |
|
||||
| `label.wgt` | Label | Static text display with optional accelerator |
|
||||
| `listbox.wgt` | ListBox | Scrollable string list with single/multi select |
|
||||
| `listview.wgt` | ListView | Multi-column list with headers, sorting, column resize |
|
||||
| `progress.wgt` | ProgressBar | Horizontal or vertical progress indicator |
|
||||
| `radio.wgt` | RadioGroup, Radio | Mutually exclusive option buttons |
|
||||
| `scrlpane.wgt` | ScrollPane | Scrollable viewport for oversized content |
|
||||
| `separatr.wgt` | Separator | Horizontal or vertical divider line |
|
||||
| `slider.wgt` | Slider | Horizontal value slider with thumb |
|
||||
| `spacer.wgt` | Spacer | Invisible layout spacer |
|
||||
| `spinner.wgt` | Spinner | Numeric input with up/down buttons |
|
||||
| `splitter.wgt` | Splitter | Resizable two-pane divider |
|
||||
| `statbar.wgt` | StatusBar | Horizontal bar at window bottom |
|
||||
| `tabctrl.wgt` | TabControl, TabPage | Tabbed page container |
|
||||
| `terminal.wgt` | AnsiTerm | VT100 terminal emulator with scrollback |
|
||||
| `textinpt.wgt` | TextInput, TextArea | Single-line and multi-line text editing |
|
||||
| `timer.wgt` | Timer | Periodic callback (no visual) |
|
||||
| `toolbar.wgt` | Toolbar | Horizontal button/widget bar |
|
||||
| `treeview.wgt` | TreeView, TreeItem | Hierarchical tree with expand/collapse |
|
||||
|
||||
26 modules implementing 32 widget types (some modules register
|
||||
multiple related types).
|
||||
|
||||
|
||||
## Writing a New Widget
|
||||
|
||||
1. Create `widgets/myWidget.c`
|
||||
2. Include `"widgetInternal.h"` for infrastructure access
|
||||
3. Implement paint, calcMinSize, and event handlers as needed
|
||||
4. Define a `static const WidgetClassT` with your vtable
|
||||
5. Add a `wgtRegister()` function:
|
||||
```c
|
||||
void wgtRegister(void) {
|
||||
widgetClassTable[MyWidgetTypeE] = &sClassMyWidget;
|
||||
}
|
||||
```
|
||||
6. Add the new enum value to `WidgetTypeE` in `core/dvxWidget.h`
|
||||
(or use `wgtRegisterClass()` for a fully dynamic type ID)
|
||||
7. Add public API functions (e.g. `wgtMyWidget()`) that call
|
||||
`widgetAlloc(parent, MyWidgetTypeE)`
|
||||
8. Add a build rule in `widgets/Makefile`
|
||||
9. Build -- the loader discovers and loads it automatically
|
||||
|
||||
|
||||
## Building
|
||||
|
||||
```
|
||||
make # compiles all widget .c files, creates .wgt modules in ../bin/widgets/
|
||||
make clean
|
||||
```
|
||||
|
||||
Each `.wgt` is created with `dxe3gen -E _wgt -U`, which exports all
|
||||
symbols starting with `wgt` (public API + `wgtRegister`) and allows
|
||||
unresolved symbols (resolved at load time from other modules).
|
||||
Loading…
Add table
Reference in a new issue