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
|
A windowed GUI compositor and desktop shell for DOS, built with
|
||||||
DJGPP/DPMI. DVX combines a Motif-style window manager, dirty-rectangle
|
DJGPP/DPMI. DVX combines a Motif-style window manager, dirty-rectangle
|
||||||
compositor, cooperative task switcher, and DXE3 dynamic application
|
compositor, cooperative task switcher, and a modular DXE3 architecture
|
||||||
loading into a multitasking desktop environment where applications are
|
into a multitasking desktop environment. The bootstrap loader
|
||||||
`.app` shared libraries loaded at runtime.
|
dynamically discovers and loads core libraries, widget plugins, and
|
||||||
|
applications at runtime.
|
||||||
|
|
||||||
Targets real and emulated 486+ hardware with VESA VBE 2.0+ linear
|
Targets real and emulated 486+ hardware with VESA VBE 2.0+ linear
|
||||||
framebuffer. No bank switching -- LFB or fail.
|
framebuffer. No bank switching -- LFB or fail.
|
||||||
|
|
@ -16,9 +17,10 @@ framebuffer. No bank switching -- LFB or fail.
|
||||||
maximize, and restore
|
maximize, and restore
|
||||||
- Dirty-rectangle compositor -- only changed regions are flushed to video
|
- Dirty-rectangle compositor -- only changed regions are flushed to video
|
||||||
memory, critical for acceptable frame rates on 486/Pentium hardware
|
memory, critical for acceptable frame rates on 486/Pentium hardware
|
||||||
- 32 widget types: buttons, text inputs, list boxes, tree views, list
|
- 32 widget types across 26 DXE modules: buttons, text inputs, list
|
||||||
views, tab controls, sliders, spinners, progress bars, dropdowns,
|
boxes, tree views, list views, tab controls, sliders, spinners,
|
||||||
combo boxes, splitters, scroll panes, ANSI terminal emulator, and more
|
progress bars, dropdowns, combo boxes, splitters, scroll panes, ANSI
|
||||||
|
terminal emulator, and more
|
||||||
- Flexbox-style automatic layout engine (VBox/HBox containers with
|
- Flexbox-style automatic layout engine (VBox/HBox containers with
|
||||||
weighted space distribution)
|
weighted space distribution)
|
||||||
- Dropdown menus with cascading submenus, checkbox and radio items,
|
- 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)
|
- Encrypted serial networking stack (DH key exchange, XTEA-CTR cipher)
|
||||||
- Platform abstraction layer -- DOS/DJGPP production target, Linux/SDL2
|
- Platform abstraction layer -- DOS/DJGPP production target, Linux/SDL2
|
||||||
development target
|
development target
|
||||||
|
- Modular DXE architecture with dependency resolution -- core libraries,
|
||||||
|
widget plugins, and applications are all separate loadable modules
|
||||||
|
|
||||||
|
|
||||||
## Target Hardware
|
## Target Hardware
|
||||||
|
|
@ -53,30 +57,68 @@ framebuffer. No bank switching -- LFB or fail.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
The shell runs as a single DOS executable (`dvx.exe`) that loads
|
DVX uses a modular DXE3 architecture. The bootstrap loader (`dvx.exe`)
|
||||||
applications dynamically via DJGPP's DXE3 shared library system.
|
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 (bootstrap loader)
|
||||||
| dvx.exe (Task 0) |
|
|
|
||||||
| +-------------+ +-----------+ +------------+ |
|
+-- platformRegisterDxeExports() -- platform + libc/libm symbols
|
||||||
| | shellMain | | shellApp | | shellExport| |
|
|
|
||||||
| | (event loop)| | (lifecycle| | (DXE symbol| |
|
+-- libs/ (scanned, dependency-ordered)
|
||||||
| | | | + reaper)| | export) | |
|
| libtasks.lib -- cooperative task switching, stb_ds
|
||||||
| +-------------+ +-----------+ +------------+ |
|
| libdvx.lib -- draw, comp, wm, app, widget infrastructure
|
||||||
| | | | |
|
| dvxshell.lib -- shell, app lifecycle, crash recovery
|
||||||
| +------+-------+ +----+-----+ +----+----+ |
|
|
|
||||||
| | libdvx.a | |libtasks.a| | libdxe | |
|
+-- widgets/ (scanned, each calls wgtRegister)
|
||||||
| | (GUI/widgets)| |(scheduler)| | (DJGPP) | |
|
| box.wgt, button.wgt, label.wgt, ... (26 modules)
|
||||||
| +--------------+ +----------+ +---------+ |
|
|
|
||||||
+-------------------------------------------------------------------+
|
+-- shellMain() -- found via dlsym, enters main loop
|
||||||
| |
|
|
|
||||||
+---------+ +---------+
|
+-- apps/ (loaded on demand by shell)
|
||||||
| app.app | | app.app |
|
progman.app, notepad.app, clock.app, ...
|
||||||
| (Task N)| | (Task M)|
|
|
||||||
+---------+ +---------+
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 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
|
### App Types
|
||||||
|
|
||||||
**Callback-only** (`hasMainLoop = false`): `appMain` creates windows,
|
**Callback-only** (`hasMainLoop = false`): `appMain` creates windows,
|
||||||
|
|
@ -98,33 +140,93 @@ Diagnostic information (registers, faulting EIP) is logged to `dvx.log`.
|
||||||
|
|
||||||
## Directory Structure
|
## Directory Structure
|
||||||
|
|
||||||
|
### Source Tree
|
||||||
|
|
||||||
```
|
```
|
||||||
dvxgui/
|
dvxgui/
|
||||||
dvx/ GUI compositor library (libdvx.a)
|
core/ Core GUI infrastructure (-> libs/libdvx.lib)
|
||||||
platform/ Platform abstraction (DOS/DJGPP, Linux/SDL2)
|
platform/ Platform abstraction (dvxPlatform.h, dvxPlatformDos.c)
|
||||||
widgets/ Widget system (32 types, one file per type)
|
thirdparty/ stb_image.h, stb_image_write.h
|
||||||
thirdparty/ stb_image.h, stb_image_write.h
|
tasks/ Cooperative task switcher (-> libs/libtasks.lib)
|
||||||
tasks/ Cooperative task switcher (libtasks.a)
|
thirdparty/ stb_ds.h
|
||||||
thirdparty/ stb_ds.h
|
shell/ Desktop shell (-> libs/dvxshell.lib)
|
||||||
dvxshell/ Desktop shell (dvx.exe)
|
widgets/ Widget modules (-> widgets/*.wgt, 26 files)
|
||||||
apps/ DXE app modules (.app files)
|
loader/ Bootstrap loader (-> dvx.exe)
|
||||||
progman/ Program Manager -- app launcher grid
|
apps/ Application modules (-> apps/*.app)
|
||||||
notepad/ Text editor with file I/O
|
progman/ Program Manager -- app launcher grid
|
||||||
clock/ Digital clock (multi-instance, main-loop)
|
notepad/ Text editor with file I/O
|
||||||
dvxdemo/ Widget system showcase / demo app
|
clock/ Digital clock (multi-instance, main-loop)
|
||||||
cpanel/ Control Panel -- themes, wallpaper, video, mouse
|
dvxdemo/ Widget system showcase / demo app
|
||||||
imgview/ Image Viewer -- BMP/PNG/JPEG/GIF display
|
cpanel/ Control Panel -- themes, wallpaper, video, mouse
|
||||||
rs232/ ISR-driven UART serial driver (librs232.a)
|
imgview/ Image Viewer -- BMP/PNG/JPEG/GIF display
|
||||||
packet/ HDLC framing, CRC-16, Go-Back-N (libpacket.a)
|
config/ Themes, wallpapers, dvx.ini, dependency files
|
||||||
security/ DH key exchange, XTEA-CTR cipher (libsecurity.a)
|
rs232/ ISR-driven UART serial driver (librs232.a)
|
||||||
seclink/ Secure serial link wrapper (libseclink.a)
|
packet/ HDLC framing, CRC-16, Go-Back-N (libpacket.a)
|
||||||
proxy/ Linux SecLink-to-telnet proxy (secproxy)
|
security/ DH key exchange, XTEA-CTR cipher (libsecurity.a)
|
||||||
termdemo/ Encrypted ANSI terminal demo (termdemo.exe)
|
seclink/ Secure serial link wrapper (libseclink.a)
|
||||||
themes/ Color theme files (.thm)
|
proxy/ Linux serial-to-telnet proxy (secproxy)
|
||||||
wpaper/ Bundled wallpaper images
|
termdemo/ Encrypted ANSI terminal demo (termdemo.exe)
|
||||||
bin/ Build output (dvx.exe, apps/, config/)
|
bin/ Build output
|
||||||
lib/ Build output (static libraries)
|
lib/ Build output (static libraries)
|
||||||
releases/ Release archives
|
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`).
|
Requires the DJGPP cross-compiler (`i586-pc-msdosdjgpp-gcc`).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build everything (dvx lib, tasks lib, shell, all apps)
|
# Build everything (loader, core, tasks, shell, widgets, all apps)
|
||||||
make
|
make
|
||||||
|
|
||||||
# Build individual components
|
# Build individual components
|
||||||
make -C dvx # builds lib/libdvx.a
|
make -C loader # builds dvx.exe
|
||||||
make -C tasks # builds lib/libtasks.a
|
make -C core # builds libs/libdvx.lib + widgets/*.wgt
|
||||||
make -C dvxshell # builds bin/dvx.exe
|
make -C tasks # builds libs/libtasks.lib
|
||||||
make -C apps # builds bin/apps/*/*.app
|
make -C shell # builds libs/dvxshell.lib + config
|
||||||
|
make -C widgets # builds widgets/*.wgt
|
||||||
|
make -C apps # builds apps/*/*.app
|
||||||
|
|
||||||
# Clean all build artifacts
|
# Clean all build artifacts
|
||||||
make clean
|
make clean
|
||||||
|
|
@ -272,8 +376,8 @@ external dependencies:
|
||||||
|
|
||||||
| Library | Location | Purpose |
|
| Library | Location | Purpose |
|
||||||
|---------|----------|---------|
|
|---------|----------|---------|
|
||||||
| stb_image.h | `dvx/thirdparty/` | Image loading (BMP, PNG, JPEG, GIF) |
|
| stb_image.h | `core/thirdparty/` | Image loading (BMP, PNG, JPEG, GIF) |
|
||||||
| stb_image_write.h | `dvx/thirdparty/` | Image writing (PNG export for screenshots) |
|
| 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) |
|
| 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:
|
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
|
- [`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
|
- [`apps/README.md`](apps/README.md) -- Writing DXE applications
|
||||||
- [`rs232/README.md`](rs232/README.md) -- Serial port driver
|
- [`rs232/README.md`](rs232/README.md) -- Serial port driver
|
||||||
- [`packet/README.md`](packet/README.md) -- Packet transport protocol
|
- [`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
|
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
|
Each app is compiled to an object file with the DJGPP cross-compiler, then
|
||||||
packaged into a `.app` via `dxe3gen`:
|
packaged into a `.app` via `dxe3gen`:
|
||||||
|
|
||||||
|
|
@ -66,11 +68,26 @@ $(BINDIR)/myapp/myapp.app: $(OBJDIR)/myapp.o | $(BINDIR)/myapp
|
||||||
underscore prefix).
|
underscore prefix).
|
||||||
- `-E _appShutdown` is added for apps that export a shutdown hook (e.g.,
|
- `-E _appShutdown` is added for apps that export a shutdown hook (e.g.,
|
||||||
Clock).
|
Clock).
|
||||||
- `-U` marks all other symbols as unresolved imports to be resolved from the
|
- `-U` marks all other symbols as unresolved imports to be resolved at
|
||||||
shell's export table at dlopen time.
|
`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
|
Requires the DJGPP cross-compiler toolchain and the DXE3 tools (`dxe3gen`).
|
||||||
toolchain.
|
|
||||||
|
## 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
|
## Directory Structure
|
||||||
|
|
||||||
|
|
@ -418,8 +435,8 @@ hook.
|
||||||
simultaneously. Each instance gets independent globals and statics.
|
simultaneously. Each instance gets independent globals and statics.
|
||||||
- Avoid `static inline` functions in shared headers. Code inlined into the
|
- Avoid `static inline` functions in shared headers. Code inlined into the
|
||||||
DXE binary cannot be updated without recompiling the app. Use macros for
|
DXE binary cannot be updated without recompiling the app. Use macros for
|
||||||
trivial expressions or regular functions exported through the shell's DXE
|
trivial expressions or regular functions exported through the DXE module
|
||||||
table.
|
system.
|
||||||
- Use `ctx->appDir` for loading app-relative resources (icons, data files).
|
- Use `ctx->appDir` for loading app-relative resources (icons, data files).
|
||||||
The working directory is shared by all apps and belongs to the shell.
|
The working directory is shared by all apps and belongs to the shell.
|
||||||
- Use `shellEnsureConfigDir()` + `shellConfigPath()` for persistent settings.
|
- 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,
|
The core GUI compositor library for DVX, built as a DXE module
|
||||||
2D drawing primitives, dirty-rectangle compositing, a full window
|
(libdvx.lib). Provides VESA video setup, 2D drawing primitives,
|
||||||
manager with Motif-style chrome, and a 32-type widget toolkit with
|
dirty-rectangle compositing, a full window manager with Motif-style
|
||||||
automatic layout. Applications include `dvxApp.h` (which pulls in all
|
chrome, and the widget infrastructure (layout engine, event dispatch,
|
||||||
lower layers) and optionally `dvxWidget.h` for the widget system.
|
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
|
## 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 3 dvxComp Dirty rectangle list, merge, LFB flush
|
||||||
Layer 2 dvxDraw Spans, rects, bevels, text, bitmaps (asm inner loops)
|
Layer 2 dvxDraw Spans, rects, bevels, text, bitmaps (asm inner loops)
|
||||||
Layer 1 dvxVideo VESA init, LFB mapping, backbuffer, pixel format
|
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
|
## File Structure
|
||||||
|
|
||||||
|
|
@ -39,7 +56,7 @@ Layer 1 dvxVideo VESA init, LFB mapping, backbuffer, pixel format
|
||||||
| File | Purpose |
|
| File | Purpose |
|
||||||
|------|---------|
|
|------|---------|
|
||||||
| `dvxTypes.h` | Shared type definitions used by all layers (PixelFormatT, DisplayT, WindowT, ColorSchemeT, etc.) |
|
| `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) |
|
| `dvxDialog.h/.c` | Modal dialogs (message box, file open/save) |
|
||||||
| `dvxPrefs.h/.c` | INI-based preferences system (read/write with typed accessors) |
|
| `dvxPrefs.h/.c` | INI-based preferences system (read/write with typed accessors) |
|
||||||
| `dvxFont.h` | Embedded 8x16 VGA bitmap font glyph data (CP437, 256 glyphs) |
|
| `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) |
|
| `dvxIcon.c` | stb_image implementation unit (BMP/PNG/JPEG/GIF loading) |
|
||||||
| `dvxImageWrite.c` | stb_image_write implementation unit (PNG export) |
|
| `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
|
### Platform Abstraction
|
||||||
|
|
||||||
| File | Purpose |
|
| File | Purpose |
|
||||||
|------|---------|
|
|------|---------|
|
||||||
| `platform/dvxPlatform.h` | OS/CPU-neutral interface: video, input, span ops, filesystem |
|
| `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`
|
To port DVX to a new platform, implement a new `dvxPlatformXxx.c`
|
||||||
against `platform/dvxPlatform.h` and swap it in the Makefile. No other
|
against `platform/dvxPlatform.h` and swap it in the Makefile. No other
|
||||||
files need modification.
|
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
|
### Third Party
|
||||||
|
|
||||||
| File | Purpose |
|
| File | Purpose |
|
||||||
|
|
@ -90,10 +107,16 @@ the class table.
|
||||||
Requires the DJGPP cross-compiler (`i586-pc-msdosdjgpp-gcc`).
|
Requires the DJGPP cross-compiler (`i586-pc-msdosdjgpp-gcc`).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make # builds ../lib/libdvx.a
|
make # builds ../bin/libs/libdvx.lib and ../bin/libs/libdvx.dep
|
||||||
make clean # removes obj/ and lib/
|
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
|
Set `DJGPP_PREFIX` in the Makefile if your toolchain is installed
|
||||||
somewhere other than `~/djgpp/djgpp`.
|
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
|
Widgets form a tree rooted at a per-window VBox container. Layout is
|
||||||
automatic via a flexbox-like algorithm with weighted space distribution.
|
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
|
#### 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) |
|
| 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
|
### Widget Event Model
|
||||||
|
|
||||||
All widget types share a universal set of event callbacks set directly
|
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
|
Type-specific handlers (button press animation, listbox selection
|
||||||
highlight) run first, then these universal callbacks fire.
|
highlight) run first, then these universal callbacks fire.
|
||||||
|
|
||||||
Additional per-widget fields: `userData` (void pointer), `tooltip`
|
Additional per-widget fields: `userData` (void pointer), `customData`
|
||||||
(hover text), `contextMenu` (right-click menu).
|
(DXE plugin private data), `tooltip` (hover text), `contextMenu`
|
||||||
|
(right-click menu).
|
||||||
|
|
||||||
### Initialization
|
### 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
|
# DVX Shell
|
||||||
|
|
||||||
The DVX Shell (`dvx.exe`) is the host process for the DVX desktop environment.
|
The DVX Shell (`dvxshell.lib`) is a DXE module loaded by the DVX loader at
|
||||||
It initializes the GUI subsystem, loads DXE3 application modules at runtime,
|
startup. It initializes the GUI subsystem, loads DXE3 application modules at
|
||||||
runs the cooperative main loop, and provides crash recovery so a faulting app
|
runtime, runs the cooperative main loop, and provides crash recovery so a
|
||||||
does not take down the entire system. Think of it as the Windows 3.1 Program
|
faulting app does not take down the entire system.
|
||||||
Manager and kernel combined into one executable.
|
|
||||||
|
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
|
## Files
|
||||||
|
|
||||||
| File | Purpose |
|
| File | Purpose |
|
||||||
|------|---------|
|
|------|---------|
|
||||||
| `shellMain.c` | Entry point, main loop, crash recovery, logging, desktop update callbacks |
|
| `shellMain.c` | Entry point (`shellMain`), main loop, crash recovery, logging |
|
||||||
| `shellApp.c` | App loading (dlopen/dlsym), lifecycle state machine, reaping, resource tracking, config dirs |
|
| `shellApp.c` | App loading via `dlopen`/`dlsym`, lifecycle, reaping |
|
||||||
| `shellApp.h` | `ShellAppT`, `AppDescriptorT`, `AppStateE`, `DxeAppContextT`, public shell API |
|
| `shellApp.h` | `ShellAppT`, `AppDescriptorT`, `AppStateE`, `DxeAppContextT`, API |
|
||||||
| `shellExport.c` | DXE export table (400+ symbols), wrapper functions for resource tracking |
|
| `shellExport.c` | 3 DXE wrapper overrides for window resource tracking |
|
||||||
| `shellInfo.c` | System information gathering (delegates to platform layer), caches result |
|
| `shellInfo.c` | System info gathering |
|
||||||
| `shellInfo.h` | `shellInfoInit()`, `shellGetSystemInfo()` |
|
| `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()` |
|
| `shellTaskMgr.h` | `shellTaskMgrOpen()`, `shellTaskMgrRefresh()` |
|
||||||
| `Makefile` | Cross-compile rules, links `-ldvx -ltasks -lm` |
|
| `Makefile` | Cross-compile rules, produces `dvxshell.lib` and `dvxshell.dep` |
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
```
|
```
|
||||||
make # builds ../bin/dvx.exe (also builds libdvx.a, libtasks.a)
|
make # builds ../bin/libs/dvxshell.lib and ../bin/libs/dvxshell.dep
|
||||||
make clean # removes objects and binary
|
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`).
|
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
|
## 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`
|
1. **Truncate log** -- open `dvx.log` for write to clear it, then close.
|
||||||
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.
|
|
||||||
All subsequent writes use append-per-write (the file is never held open).
|
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.
|
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
|
manager, font, cursor, input subsystems. Reads video width/height/bpp from
|
||||||
preferences (default 640x480x16).
|
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()`.
|
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()`.
|
`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()`.
|
(stretch/tile/center), load via `dvxSetWallpaper()`.
|
||||||
8. **Video mode log** -- enumerate all available VESA modes to `dvx.log`.
|
7. **Video mode log** -- enumerate all available VESA modes to `dvx.log`.
|
||||||
9. **Task system** -- `tsInit()`, set shell task (task 0) to
|
8. **Task system** -- `tsInit()`, set shell task (task 0) to
|
||||||
`TS_PRIORITY_HIGH` so the UI stays responsive under load.
|
`TS_PRIORITY_HIGH` so the UI stays responsive under load.
|
||||||
10. **System info** -- `shellInfoInit()` gathers CPU, memory, drive info via
|
9. **System info** -- `shellInfoInit()` gathers CPU, memory, drive info via
|
||||||
the platform layer and logs it.
|
the platform layer and logs it.
|
||||||
11. **DXE exports** -- `shellExportInit()` calls `dlregsym()` to register the
|
10. **DXE exports** -- `shellExportInit()` calls `dlregsym()` to register the
|
||||||
export table. Must happen before any `dlopen()`.
|
3 wrapper overrides. Must happen before any `dlopen()` of app DXEs.
|
||||||
12. **App slot table** -- `shellAppInit()` zeroes the 32-slot fixed array.
|
11. **App slot table** -- `shellAppInit()` zeroes the 32-slot fixed array.
|
||||||
13. **Idle/hotkey callbacks** -- wire up `idleYield`, `ctrlEscHandler`,
|
12. **Idle/hotkey callbacks** -- wire up `idleYield`, `ctrlEscHandler`,
|
||||||
`titleChangeHandler` on the `AppContextT`.
|
`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.
|
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
|
for SIGSEGV, SIGFPE, SIGILL. Installed last so initialization crashes
|
||||||
get the default DJGPP abort-with-register-dump instead of our recovery
|
get the default DJGPP abort-with-register-dump instead of our recovery
|
||||||
path.
|
path.
|
||||||
|
|
@ -100,7 +134,7 @@ The shell provides Windows 3.1-style fault tolerance using `setjmp`/`longjmp`:
|
||||||
|
|
||||||
1. `installCrashHandler()` registers `crashHandler` for SIGSEGV, SIGFPE,
|
1. `installCrashHandler()` registers `crashHandler` for SIGSEGV, SIGFPE,
|
||||||
SIGILL.
|
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
|
3. If a signal fires (in any task), `crashHandler` logs the crash details
|
||||||
(signal, app name, full register dump from `__djgpp_exception_state_ptr`),
|
(signal, app name, full register dump from `__djgpp_exception_state_ptr`),
|
||||||
re-installs the handler (DJGPP uses SysV semantics -- handler resets to
|
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 Loading
|
||||||
|
|
||||||
DXE3 is DJGPP's dynamic linking mechanism. Each `.app` file is a DXE3 shared
|
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()`:
|
`dlregsym()`. The load sequence in `shellLoadApp()`:
|
||||||
|
|
||||||
1. Allocate a slot from the 32-entry fixed array (slot 0 is the shell).
|
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
|
`sCurrentAppId` is a simple global (not thread-local) because cooperative
|
||||||
multitasking means only one task runs at a time.
|
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
|
## Task Manager
|
||||||
|
|
||||||
`shellTaskMgr.c` implements a shell-level Task Manager accessible via
|
`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.
|
is running. It is owned by the shell (appId = 0), not by any DXE app.
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
|
|
@ -262,38 +318,6 @@ API:
|
||||||
Apps use the standard preferences system (`prefsLoad`/`prefsSave`) pointed at
|
Apps use the standard preferences system (`prefsLoad`/`prefsSave`) pointed at
|
||||||
their config directory for persistent settings.
|
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
|
## System Hotkeys
|
||||||
|
|
||||||
These are always active regardless of which app is focused:
|
These are always active regardless of which app is focused:
|
||||||
|
|
@ -341,6 +365,7 @@ full register dumps.
|
||||||
|
|
||||||
| Function | Description |
|
| Function | Description |
|
||||||
|----------|-------------|
|
|----------|-------------|
|
||||||
|
| `shellMain(argc, argv)` | Entry point called by the DVX loader |
|
||||||
| `shellAppInit()` | Zero the 32-slot app table |
|
| `shellAppInit()` | Zero the 32-slot app table |
|
||||||
| `shellLoadApp(ctx, path)` | Load and start a DXE app. Returns app ID (>= 1) or -1 |
|
| `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 |
|
| `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 |
|
| `shellLog(fmt, ...)` | Append to dvx.log |
|
||||||
| `shellEnsureConfigDir(ctx)` | Create an app's config directory tree |
|
| `shellEnsureConfigDir(ctx)` | Create an app's config directory tree |
|
||||||
| `shellConfigPath(ctx, name, buf, size)` | Build path to file in app's config dir |
|
| `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 |
|
| `shellRegisterDesktopUpdate(fn)` | Register callback for app state changes |
|
||||||
| `shellUnregisterDesktopUpdate(fn)` | Remove a previously registered callback |
|
| `shellUnregisterDesktopUpdate(fn)` | Remove a previously registered callback |
|
||||||
| `shellDesktopUpdate()` | Notify all registered listeners |
|
| `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
|
## Files
|
||||||
|
|
||||||
| File | Description |
|
| File | Purpose |
|
||||||
|-----------------------|----------------------------------------------------|
|
|------|---------|
|
||||||
| `taskswitch.h` | Public API -- types, constants, function prototypes |
|
| `taskswitch.h` | Public API -- types, constants, function prototypes |
|
||||||
| `taskswitch.c` | Implementation (scheduler, context switch, slots) |
|
| `taskswitch.c` | Implementation (scheduler, context switch, slots, includes stb_ds implementation) |
|
||||||
| `demo.c` | Standalone test harness exercising all features |
|
| `thirdparty/stb_ds.h` | stb dynamic array/hashmap library (third-party) |
|
||||||
| `thirdparty/stb_ds.h` | stb dynamic array/hashmap library (third-party) |
|
| `Makefile` | DJGPP cross-compilation build rules |
|
||||||
| `Makefile` | DJGPP cross-compilation build rules |
|
|
||||||
|
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
@ -39,18 +38,25 @@ lets each task yield at safe points, avoiding synchronization entirely.
|
||||||
Cross-compile from Linux:
|
Cross-compile from Linux:
|
||||||
|
|
||||||
```
|
```
|
||||||
make # builds ../lib/libtasks.a
|
make # builds ../bin/libs/libtasks.lib
|
||||||
make demo # also builds ../bin/tsdemo.exe
|
make clean # removes objects and library
|
||||||
make clean # removes objects, library, and demo binary
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
|
|
||||||
| Path | Description |
|
| Path | Description |
|
||||||
|---------------------|----------------------|
|
|------|-------------|
|
||||||
| `../lib/libtasks.a` | Static library |
|
| `../bin/libs/libtasks.lib` | DXE module |
|
||||||
| `../obj/tasks/` | Object files |
|
| `../obj/tasks/` | Object files |
|
||||||
| `../bin/tsdemo.exe` | Demo executable |
|
|
||||||
|
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
|
## Quick Start
|
||||||
|
|
@ -109,75 +115,75 @@ available for reuse by the next `tsCreate()` call.
|
||||||
|
|
||||||
### Initialization and Teardown
|
### Initialization and Teardown
|
||||||
|
|
||||||
| Function | Signature | Description |
|
| Function | Signature | Description |
|
||||||
|--------------|-------------------------|--------------------------------------------------------------------|
|
|----------|-----------|-------------|
|
||||||
| `tsInit` | `int32_t tsInit(void)` | Initialize the library. Returns `TS_OK` or a negative error code. |
|
| `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. |
|
| `tsShutdown` | `void tsShutdown(void)` | Free all resources. Safe to call even if `tsInit` was never called. |
|
||||||
|
|
||||||
### Task Creation and Termination
|
### 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. |
|
| `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. |
|
| `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). |
|
| `tsKill` | `int32_t tsKill(uint32_t taskId)` | Forcibly terminate another task. Cannot kill main (id 0) or self (use `tsExit` instead). |
|
||||||
|
|
||||||
### Scheduling
|
### Scheduling
|
||||||
|
|
||||||
| Function | Signature | Description |
|
| Function | Signature | Description |
|
||||||
|-----------|----------------------|-----------------------------------------------------------------|
|
|----------|-----------|-------------|
|
||||||
| `tsYield` | `void tsYield(void)` | Voluntarily relinquish the CPU to the next eligible ready task. |
|
| `tsYield` | `void tsYield(void)` | Voluntarily relinquish the CPU to the next eligible ready task. |
|
||||||
|
|
||||||
### Pausing and Resuming
|
### Pausing and Resuming
|
||||||
|
|
||||||
| Function | Signature | Description |
|
| 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. |
|
| `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. |
|
| `tsResume` | `int32_t tsResume(uint32_t id)` | Resume a paused task. Credits are refilled so it is not penalized for having been paused. |
|
||||||
|
|
||||||
### Priority
|
### 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. |
|
| `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
|
### 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. |
|
| `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
|
### Query
|
||||||
|
|
||||||
| Function | Signature | Description |
|
| Function | Signature | Description |
|
||||||
|-----------------|--------------------------------------|--------------------------------------------------------|
|
|----------|-----------|-------------|
|
||||||
| `tsGetState` | `TaskStateE tsGetState(uint32_t id)` | Return the task's state enum value. |
|
| `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. |
|
| `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. |
|
| `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. |
|
| `tsActiveCount` | `uint32_t tsActiveCount(void)` | Return the number of non-terminated tasks. |
|
||||||
|
|
||||||
|
|
||||||
## Constants
|
## Constants
|
||||||
|
|
||||||
### Error Codes
|
### Error Codes
|
||||||
|
|
||||||
| Name | Value | Meaning |
|
| Name | Value | Meaning |
|
||||||
|----------------|-------|--------------------------------------------------|
|
|------|-------|---------|
|
||||||
| `TS_OK` | 0 | Success |
|
| `TS_OK` | 0 | Success |
|
||||||
| `TS_ERR_INIT` | -1 | Library not initialized |
|
| `TS_ERR_INIT` | -1 | Library not initialized |
|
||||||
| `TS_ERR_PARAM` | -2 | Invalid parameter |
|
| `TS_ERR_PARAM` | -2 | Invalid parameter |
|
||||||
| `TS_ERR_FULL` | -3 | Task table full (unused, kept for compatibility) |
|
| `TS_ERR_FULL` | -3 | Task table full (unused, kept for compatibility) |
|
||||||
| `TS_ERR_NOMEM` | -4 | Memory allocation failed |
|
| `TS_ERR_NOMEM` | -4 | Memory allocation failed |
|
||||||
| `TS_ERR_STATE` | -5 | Invalid state transition |
|
| `TS_ERR_STATE` | -5 | Invalid state transition |
|
||||||
|
|
||||||
### Priority Presets
|
### Priority Presets
|
||||||
|
|
||||||
| Name | Value | Credits per Round |
|
| Name | Value | Credits per Round |
|
||||||
|----------------------|-------|-------------------|
|
|------|-------|-------------------|
|
||||||
| `TS_PRIORITY_LOW` | 0 | 1 |
|
| `TS_PRIORITY_LOW` | 0 | 1 |
|
||||||
| `TS_PRIORITY_NORMAL` | 5 | 6 |
|
| `TS_PRIORITY_NORMAL` | 5 | 6 |
|
||||||
| `TS_PRIORITY_HIGH` | 10 | 11 |
|
| `TS_PRIORITY_HIGH` | 10 | 11 |
|
||||||
|
|
||||||
Any non-negative `int32_t` may be used as a priority. The presets are
|
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
|
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
|
### Defaults
|
||||||
|
|
||||||
| Name | Value | Description |
|
| Name | Value | Description |
|
||||||
|-------------------------|-------|------------------------|
|
|------|-------|-------------|
|
||||||
| `TS_DEFAULT_STACK_SIZE` | 32768 | Default stack per task |
|
| `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
|
## Types
|
||||||
|
|
@ -280,29 +286,29 @@ versions.
|
||||||
|
|
||||||
Six callee-saved values are saved and restored per switch:
|
Six callee-saved values are saved and restored per switch:
|
||||||
|
|
||||||
| Register | Offset | Purpose |
|
| Register | Offset | Purpose |
|
||||||
|----------|--------|------------------------------------------|
|
|----------|--------|---------|
|
||||||
| EBX | 0 | Callee-saved general purpose |
|
| EBX | 0 | Callee-saved general purpose |
|
||||||
| ESI | 4 | Callee-saved general purpose |
|
| ESI | 4 | Callee-saved general purpose |
|
||||||
| EDI | 8 | Callee-saved general purpose |
|
| EDI | 8 | Callee-saved general purpose |
|
||||||
| EBP | 12 | Frame pointer |
|
| EBP | 12 | Frame pointer |
|
||||||
| ESP | 16 | Stack pointer |
|
| ESP | 16 | Stack pointer |
|
||||||
| EIP | 20 | Resume address (captured as local label) |
|
| EIP | 20 | Resume address (captured as local label) |
|
||||||
|
|
||||||
### x86_64 (for native Linux testing)
|
### x86_64 (for native Linux testing)
|
||||||
|
|
||||||
Eight callee-saved values are saved and restored per switch:
|
Eight callee-saved values are saved and restored per switch:
|
||||||
|
|
||||||
| Register | Offset | Purpose |
|
| Register | Offset | Purpose |
|
||||||
|----------|--------|------------------------------------------|
|
|----------|--------|---------|
|
||||||
| RBX | 0 | Callee-saved general purpose |
|
| RBX | 0 | Callee-saved general purpose |
|
||||||
| R12 | 8 | Callee-saved general purpose |
|
| R12 | 8 | Callee-saved general purpose |
|
||||||
| R13 | 16 | Callee-saved general purpose |
|
| R13 | 16 | Callee-saved general purpose |
|
||||||
| R14 | 24 | Callee-saved general purpose |
|
| R14 | 24 | Callee-saved general purpose |
|
||||||
| R15 | 32 | Callee-saved general purpose |
|
| R15 | 32 | Callee-saved general purpose |
|
||||||
| RBP | 40 | Frame pointer |
|
| RBP | 40 | Frame pointer |
|
||||||
| RSP | 48 | Stack pointer |
|
| RSP | 48 | Stack pointer |
|
||||||
| RIP | 56 | Resume address (RIP-relative lea) |
|
| RIP | 56 | Resume address (RIP-relative lea) |
|
||||||
|
|
||||||
Segment registers are not saved because DJGPP runs in a flat
|
Segment registers are not saved because DJGPP runs in a flat
|
||||||
protected-mode environment where CS, DS, ES, and SS share the same
|
protected-mode environment where CS, DS, ES, and SS share the same
|
||||||
|
|
@ -326,32 +332,10 @@ and then `tsExit()`.
|
||||||
each task's needs.
|
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
|
## Third-Party Dependencies
|
||||||
|
|
||||||
- **stb_ds.h** (Sean Barrett) -- dynamic array and hashmap library.
|
- **stb_ds.h** (Sean Barrett) -- dynamic array and hashmap library.
|
||||||
Located in `thirdparty/stb_ds.h`. Used for the task control block
|
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