DVX_GUI/apps/README.md
2026-03-18 02:08:26 -05:00

141 lines
3.8 KiB
Markdown

# DVX Shell Applications
DXE3 shared library applications loaded at runtime by the DVX Shell. Each app
is a `.app` file (DXE3 format) placed under the `apps/` directory tree. The
Program Manager scans this directory recursively and displays all discovered
apps.
## Building
```
make # builds all .app files into ../bin/apps/<name>/
make clean # removes objects and binaries
```
Requires `lib/libdvx.a`, `lib/libtasks.a`, and the DXE3 tools (`dxe3gen`)
from the DJGPP toolchain.
## Applications
| App | File | Type | Description |
|-----|------|------|-------------|
| Program Manager | `progman/progman.c` | Callback | Desktop app: app launcher grid, Task Manager (Ctrl+Esc), window management |
| Notepad | `notepad/notepad.c` | Callback | Text editor with file I/O, dirty tracking via hash |
| Clock | `clock/clock.c` | Main-loop | Digital clock, multi-instance capable |
| DVX Demo | `dvxdemo/dvxdemo.c` | Callback | Widget system showcase with all widget types |
## Writing a New App
### Minimal callback-only app
```c
#include "dvxApp.h"
#include "dvxWidget.h"
#include "shellApp.h"
AppDescriptorT appDescriptor = {
.name = "My App",
.hasMainLoop = false,
.stackSize = SHELL_STACK_DEFAULT,
.priority = TS_PRIORITY_NORMAL
};
static DxeAppContextT *sCtx = NULL;
static WindowT *sWin = NULL;
static void onClose(WindowT *win) {
dvxDestroyWindow(sCtx->shellCtx, win);
sWin = NULL;
}
int32_t appMain(DxeAppContextT *ctx) {
sCtx = ctx;
AppContextT *ac = ctx->shellCtx;
sWin = dvxCreateWindow(ac, "My App", 100, 100, 300, 200, true);
if (!sWin) {
return -1;
}
sWin->onClose = onClose;
WidgetT *root = wgtInitWindow(ac, sWin);
wgtLabel(root, "Hello, DVX!");
wgtInvalidate(root);
return 0;
}
```
### Minimal main-loop app
```c
#include "dvxApp.h"
#include "dvxWidget.h"
#include "dvxWm.h"
#include "shellApp.h"
#include "taskswitch.h"
AppDescriptorT appDescriptor = {
.name = "My Task App",
.hasMainLoop = true,
.stackSize = SHELL_STACK_DEFAULT,
.priority = TS_PRIORITY_NORMAL
};
static bool sQuit = false;
static void onClose(WindowT *win) {
sQuit = true;
}
int32_t appMain(DxeAppContextT *ctx) {
AppContextT *ac = ctx->shellCtx;
WindowT *win = dvxCreateWindow(ac, "My Task App", 100, 100, 200, 100, false);
if (!win) {
return -1;
}
win->onClose = onClose;
while (!sQuit) {
// Do work, update window content
tsYield();
}
dvxDestroyWindow(ac, win);
return 0;
}
```
### Adding to the build
Add your app directory and source to `apps/Makefile`. Each app is compiled to
an object file, then linked into a `.app` via `dxe3gen`:
```makefile
$(BIN_DIR)/myapp.app: $(OBJ_DIR)/myapp/myapp.o
$(DXE3GEN) -o $@ -E _appDescriptor -E _appMain -U $<
```
The `-E` flags export the required symbols. `-U` marks unresolved symbols as
imports to be resolved from the shell's export table at load time.
## App Guidelines
- Include `shellApp.h` for `AppDescriptorT`, `DxeAppContextT`, and
`SHELL_STACK_DEFAULT`.
- Use `ctx->shellCtx` (the `AppContextT *`) for all DVX API calls.
- Callback-only apps must destroy their own windows in `onClose` via
`dvxDestroyWindow()`. The shell detects the last window closing and
reaps the app.
- Main-loop apps must call `tsYield()` regularly. A task that never yields
blocks the entire system.
- Use file-scoped `static` variables for app state. Each DXE has its own data
segment, so there is no collision between apps.
- Set `multiInstance = true` in the descriptor if the app can safely run
multiple copies simultaneously.
- 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.