Dynamic memory tracking improved. Callback app reaping fixed.
This commit is contained in:
parent
09da5f3857
commit
9b136995b7
7 changed files with 241 additions and 130 deletions
|
|
@ -90,6 +90,23 @@
|
||||||
// RGB pixel stride (bytes per pixel in 24-bit RGB)
|
// RGB pixel stride (bytes per pixel in 24-bit RGB)
|
||||||
#define RGB_CHANNELS 3
|
#define RGB_CHANNELS 3
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Window callback dispatch with app ID tracking
|
||||||
|
// ============================================================
|
||||||
|
//
|
||||||
|
// Sets currentAppId to the window's owning app before calling a
|
||||||
|
// window callback, then restores the previous value. This ensures
|
||||||
|
// allocations made during menu handlers, close handlers, paint,
|
||||||
|
// etc. are attributed to the correct app.
|
||||||
|
|
||||||
|
#define WIN_CALLBACK(ctx, win, call) do { \
|
||||||
|
int32_t _prevAppId = (ctx)->currentAppId; \
|
||||||
|
(ctx)->currentAppId = (win)->appId; \
|
||||||
|
call; \
|
||||||
|
(ctx)->currentAppId = _prevAppId; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Prototypes
|
// Prototypes
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -304,7 +321,7 @@ static bool checkAccelTable(AppContextT *ctx, WindowT *win, int32_t key, int32_t
|
||||||
AccelEntryT *e = &table->entries[i];
|
AccelEntryT *e = &table->entries[i];
|
||||||
|
|
||||||
if (e->normKey == matchKey && e->normMods == requiredMods) {
|
if (e->normKey == matchKey && e->normMods == requiredMods) {
|
||||||
win->onMenu(win, e->cmdId);
|
WIN_CALLBACK(ctx, win, win->onMenu(win, e->cmdId));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -940,7 +957,7 @@ static void dispatchEvents(AppContextT *ctx) {
|
||||||
closeAllPopups(ctx);
|
closeAllPopups(ctx);
|
||||||
|
|
||||||
if (win && win->onMenu) {
|
if (win && win->onMenu) {
|
||||||
win->onMenu(win, menuId);
|
WIN_CALLBACK(ctx, win, win->onMenu(win, menuId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1753,12 +1770,12 @@ int32_t dvxChangeVideoMode(AppContextT *ctx, int32_t requestedW, int32_t request
|
||||||
wmReallocContentBuf(win, &ctx->display);
|
wmReallocContentBuf(win, &ctx->display);
|
||||||
|
|
||||||
if (win->onResize) {
|
if (win->onResize) {
|
||||||
win->onResize(win, win->contentW, win->contentH);
|
WIN_CALLBACK(ctx, win, win->onResize(win, win->contentW, win->contentH));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (win->onPaint) {
|
if (win->onPaint) {
|
||||||
RectT fullRect = {0, 0, win->contentW, win->contentH};
|
RectT fullRect = {0, 0, win->contentW, win->contentH};
|
||||||
win->onPaint(win, &fullRect);
|
WIN_CALLBACK(ctx, win, win->onPaint(win, &fullRect));
|
||||||
win->contentDirty = true;
|
win->contentDirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2535,7 +2552,7 @@ void dvxInvalidateWindow(AppContextT *ctx, WindowT *win) {
|
||||||
// need to call dvxInvalidateWindow -- onPaint fires automatically.
|
// need to call dvxInvalidateWindow -- onPaint fires automatically.
|
||||||
if (win->onPaint) {
|
if (win->onPaint) {
|
||||||
RectT fullRect = {0, 0, win->contentW, win->contentH};
|
RectT fullRect = {0, 0, win->contentW, win->contentH};
|
||||||
win->onPaint(win, &fullRect);
|
WIN_CALLBACK(ctx, win, win->onPaint(win, &fullRect));
|
||||||
}
|
}
|
||||||
|
|
||||||
win->contentDirty = true;
|
win->contentDirty = true;
|
||||||
|
|
@ -3122,7 +3139,7 @@ static void executeSysMenuCmd(AppContextT *ctx, int32_t cmd) {
|
||||||
|
|
||||||
case SysMenuCloseE:
|
case SysMenuCloseE:
|
||||||
if (win->onClose) {
|
if (win->onClose) {
|
||||||
win->onClose(win);
|
WIN_CALLBACK(ctx, win, win->onClose(win));
|
||||||
} else {
|
} else {
|
||||||
dvxDestroyWindow(ctx, win);
|
dvxDestroyWindow(ctx, win);
|
||||||
}
|
}
|
||||||
|
|
@ -3271,7 +3288,7 @@ static void handleMouseButton(AppContextT *ctx, int32_t mx, int32_t my, int32_t
|
||||||
closeSysMenu(ctx);
|
closeSysMenu(ctx);
|
||||||
|
|
||||||
if (win->onClose) {
|
if (win->onClose) {
|
||||||
win->onClose(win);
|
WIN_CALLBACK(ctx, win, win->onClose(win));
|
||||||
} else {
|
} else {
|
||||||
dvxDestroyWindow(ctx, win);
|
dvxDestroyWindow(ctx, win);
|
||||||
}
|
}
|
||||||
|
|
@ -3775,7 +3792,7 @@ static void pollKeyboard(AppContextT *ctx) {
|
||||||
WindowT *win = ctx->stack.windows[ctx->stack.focusedIdx];
|
WindowT *win = ctx->stack.windows[ctx->stack.focusedIdx];
|
||||||
|
|
||||||
if (win->onClose) {
|
if (win->onClose) {
|
||||||
win->onClose(win);
|
WIN_CALLBACK(ctx, win, win->onClose(win));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4194,7 +4211,7 @@ static void pollKeyboard(AppContextT *ctx) {
|
||||||
closeAllPopups(ctx);
|
closeAllPopups(ctx);
|
||||||
|
|
||||||
if (win && win->onMenu) {
|
if (win && win->onMenu) {
|
||||||
win->onMenu(win, menuId);
|
WIN_CALLBACK(ctx, win, win->onMenu(win, menuId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -4225,7 +4242,7 @@ static void pollKeyboard(AppContextT *ctx) {
|
||||||
closeAllPopups(ctx);
|
closeAllPopups(ctx);
|
||||||
|
|
||||||
if (win && win->onMenu) {
|
if (win && win->onMenu) {
|
||||||
win->onMenu(win, menuId);
|
WIN_CALLBACK(ctx, win, win->onMenu(win, menuId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4342,7 +4359,7 @@ static void pollKeyboard(AppContextT *ctx) {
|
||||||
if (termFocused) {
|
if (termFocused) {
|
||||||
// Terminal has focus -- send Tab to it
|
// Terminal has focus -- send Tab to it
|
||||||
if (win->onKey) {
|
if (win->onKey) {
|
||||||
win->onKey(win, ascii ? ascii : (scancode | 0x100), shiftFlags);
|
WIN_CALLBACK(ctx, win, win->onKey(win, ascii ? ascii : (scancode | 0x100), shiftFlags));
|
||||||
}
|
}
|
||||||
|
|
||||||
arrfree(fstack);
|
arrfree(fstack);
|
||||||
|
|
@ -4416,7 +4433,7 @@ static void pollKeyboard(AppContextT *ctx) {
|
||||||
WindowT *win = ctx->stack.windows[ctx->stack.focusedIdx];
|
WindowT *win = ctx->stack.windows[ctx->stack.focusedIdx];
|
||||||
|
|
||||||
if (win->onKey) {
|
if (win->onKey) {
|
||||||
win->onKey(win, ascii ? ascii : (scancode | 0x100), shiftFlags);
|
WIN_CALLBACK(ctx, win, win->onKey(win, ascii ? ascii : (scancode | 0x100), shiftFlags));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4520,12 +4537,12 @@ static void repositionWindow(AppContextT *ctx, WindowT *win, int32_t x, int32_t
|
||||||
wmReallocContentBuf(win, &ctx->display);
|
wmReallocContentBuf(win, &ctx->display);
|
||||||
|
|
||||||
if (win->onResize) {
|
if (win->onResize) {
|
||||||
win->onResize(win, win->contentW, win->contentH);
|
WIN_CALLBACK(ctx, win, win->onResize(win, win->contentW, win->contentH));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (win->onPaint) {
|
if (win->onPaint) {
|
||||||
RectT fullRect = {0, 0, win->contentW, win->contentH};
|
RectT fullRect = {0, 0, win->contentW, win->contentH};
|
||||||
win->onPaint(win, &fullRect);
|
WIN_CALLBACK(ctx, win, win->onPaint(win, &fullRect));
|
||||||
win->contentDirty = true;
|
win->contentDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,12 @@
|
||||||
// dvxMem.h -- Per-app memory tracking for DVX
|
// dvxMem.h -- Per-app memory tracking API for DVX
|
||||||
//
|
//
|
||||||
// REQUIRED: Every .c file compiled into a DXE (library, widget, shell,
|
// Declares the tracked allocation functions. DXE code does NOT need
|
||||||
// or app) MUST include this header AFTER all system includes. This
|
// to include this header for tracking to work -- the DXE export table
|
||||||
// ensures every malloc/free/calloc/realloc call goes through the
|
// maps malloc/free/calloc/realloc/strdup to these wrappers transparently.
|
||||||
// tracking wrappers, maintaining pointer consistency.
|
|
||||||
//
|
//
|
||||||
// The implementation (dvxPlatformDos.c) does NOT include this header,
|
// This header is provided for code that needs to call the tracking
|
||||||
// so its dvxMalloc/dvxFree/dvxCalloc/dvxRealloc call the real libc
|
// functions by name (e.g. dvxMemGetAppUsage in the Task Manager) or
|
||||||
// functions with no recursion.
|
// for the dvxMemAppIdPtr declaration.
|
||||||
//
|
|
||||||
// How it works:
|
|
||||||
// 1. dvxMalloc prepends a 16-byte header (magic, appId, size) to
|
|
||||||
// each allocation and returns a pointer past the header.
|
|
||||||
// 2. dvxFree checks the magic value at ptr-16. If it matches, the
|
|
||||||
// header is valid and the allocation is tracked. If not, the
|
|
||||||
// pointer came from code outside DVX (libc internals, loader)
|
|
||||||
// and is passed through to the real free() unchanged.
|
|
||||||
// 3. The appId in the header comes from *dvxMemAppIdPtr, which the
|
|
||||||
// shell points at ctx->currentAppId.
|
|
||||||
//
|
|
||||||
// The magic check makes cross-boundary frees safe: if DXE code frees
|
|
||||||
// a pointer allocated by libc (e.g. from strdup, stb_ds internals),
|
|
||||||
// dvxFree detects the missing header and falls through to real free.
|
|
||||||
|
|
||||||
#ifndef DVX_MEM_H
|
#ifndef DVX_MEM_H
|
||||||
#define DVX_MEM_H
|
#define DVX_MEM_H
|
||||||
|
|
@ -35,14 +20,9 @@ void *dvxMalloc(size_t size);
|
||||||
void *dvxCalloc(size_t nmemb, size_t size);
|
void *dvxCalloc(size_t nmemb, size_t size);
|
||||||
void *dvxRealloc(void *ptr, size_t size);
|
void *dvxRealloc(void *ptr, size_t size);
|
||||||
void dvxFree(void *ptr);
|
void dvxFree(void *ptr);
|
||||||
|
char *dvxStrdup(const char *s);
|
||||||
void dvxMemSnapshotLoad(int32_t appId);
|
void dvxMemSnapshotLoad(int32_t appId);
|
||||||
uint32_t dvxMemGetAppUsage(int32_t appId);
|
uint32_t dvxMemGetAppUsage(int32_t appId);
|
||||||
void dvxMemResetApp(int32_t appId);
|
void dvxMemResetApp(int32_t appId);
|
||||||
|
|
||||||
// The dvxMalloc/dvxFree functions are passthrough wrappers.
|
|
||||||
// Header-based per-allocation tracking was attempted but is unsafe
|
|
||||||
// in the DXE3 environment (loader-compiled code like stb_ds uses
|
|
||||||
// libc malloc while DXE code would use the wrapped version).
|
|
||||||
// Per-app memory is tracked via DPMI snapshots instead.
|
|
||||||
|
|
||||||
#endif // DVX_MEM_H
|
#endif // DVX_MEM_H
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@
|
||||||
|
|
||||||
#include "dvxTypes.h"
|
#include "dvxTypes.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -227,22 +229,18 @@ bool platformGetMemoryInfo(uint32_t *totalKb, uint32_t *freeKb);
|
||||||
// Calls to dvxFree on non-tracked pointers (magic mismatch) fall through
|
// Calls to dvxFree on non-tracked pointers (magic mismatch) fall through
|
||||||
// to the real free() safely.
|
// to the real free() safely.
|
||||||
|
|
||||||
// Per-app memory tracking (see dvxMem.h for the full API).
|
// Per-app memory tracking (header-based).
|
||||||
// These are declared here for the platform implementation; DXE
|
// The DXE export table maps malloc/free/calloc/realloc/strdup to
|
||||||
// code should include dvxMem.h instead.
|
// these wrappers. DXE code is tracked transparently.
|
||||||
extern int32_t *dvxMemAppIdPtr;
|
extern int32_t *dvxMemAppIdPtr;
|
||||||
|
|
||||||
// Take a DPMI free-memory snapshot when an app is loaded.
|
void *dvxMalloc(size_t size);
|
||||||
// Call BEFORE the app's DXE is opened so the snapshot captures
|
void *dvxCalloc(size_t nmemb, size_t size);
|
||||||
// the free memory before the app allocates anything.
|
void *dvxRealloc(void *ptr, size_t size);
|
||||||
|
void dvxFree(void *ptr);
|
||||||
|
char *dvxStrdup(const char *s);
|
||||||
void dvxMemSnapshotLoad(int32_t appId);
|
void dvxMemSnapshotLoad(int32_t appId);
|
||||||
|
|
||||||
// Return estimated memory usage for an app (bytes).
|
|
||||||
// Computed as the difference between the snapshot at load time
|
|
||||||
// and the current DPMI free memory. Coarse but safe.
|
|
||||||
uint32_t dvxMemGetAppUsage(int32_t appId);
|
uint32_t dvxMemGetAppUsage(int32_t appId);
|
||||||
|
|
||||||
// Clear the snapshot for an app (call when reaping/killing).
|
|
||||||
void dvxMemResetApp(int32_t appId);
|
void dvxMemResetApp(int32_t appId);
|
||||||
|
|
||||||
// Create a directory and all parent directories (like mkdir -p).
|
// Create a directory and all parent directories (like mkdir -p).
|
||||||
|
|
|
||||||
|
|
@ -1113,91 +1113,170 @@ bool platformGetMemoryInfo(uint32_t *totalKb, uint32_t *freeKb) {
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Per-app memory tracking (DPMI snapshot)
|
// Per-app memory tracking (header-based)
|
||||||
// ============================================================
|
// ============================================================
|
||||||
//
|
//
|
||||||
// Tracks per-app memory by taking DPMI free-memory snapshots at
|
// Tracks every allocation made by DXE code via a 16-byte header
|
||||||
// app load time. The difference between the snapshot and current
|
// prepended to each allocation. The DXE export table maps malloc/
|
||||||
// free memory is a coarse estimate of the app's heap footprint.
|
// free/calloc/realloc/strdup to these wrappers so all DXE code
|
||||||
|
// is transparently tracked without #define macros.
|
||||||
//
|
//
|
||||||
// Header-based malloc wrapping (prepending a tracking header to
|
// stb_ds is also tracked: the loader overrides STBDS_REALLOC/FREE
|
||||||
// each allocation) was attempted but is unsafe in the DJGPP/DXE3
|
// to call dvxRealloc/dvxFree before including stb_ds.h.
|
||||||
// environment: loader-compiled code (stb_ds internals, libc
|
|
||||||
// functions like strdup/localtime) allocates with libc's malloc,
|
|
||||||
// but DXE code frees with the wrapped free. Reading 16 bytes
|
|
||||||
// before an arbitrary libc pointer to check for a tracking magic
|
|
||||||
// value is undefined behavior that causes faults on 86Box/DPMI.
|
|
||||||
//
|
//
|
||||||
// dvxMalloc/dvxFree/etc. are still exported for dvxMem.h compat
|
// Cross-boundary safety: when dvxFree receives a pointer that was
|
||||||
// but pass straight through to libc.
|
// allocated by libc (not our wrapper), the magic check at ptr-16
|
||||||
|
// fails and we fall through to libc free. The DJGPP heap is a
|
||||||
|
// contiguous sbrk region so reading 16 bytes before any heap
|
||||||
|
// pointer is always valid memory (never unmapped).
|
||||||
|
|
||||||
|
#define DVX_ALLOC_MAGIC 0xDEADBEEFUL
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t magic;
|
||||||
|
int32_t appId;
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t pad;
|
||||||
|
} DvxAllocHeaderT;
|
||||||
|
|
||||||
int32_t *dvxMemAppIdPtr = NULL;
|
int32_t *dvxMemAppIdPtr = NULL;
|
||||||
static uint32_t *sAppMemAtLoad = NULL;
|
static uint32_t *sAppMemUsed = NULL;
|
||||||
static int32_t sAppMemCap = 0;
|
static int32_t sAppMemCap = 0;
|
||||||
|
|
||||||
|
|
||||||
// Passthrough allocators (dvxMem.h #defines redirect here)
|
|
||||||
void *dvxMalloc(size_t size) {
|
|
||||||
return malloc(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void *dvxCalloc(size_t nmemb, size_t size) {
|
|
||||||
return calloc(nmemb, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void dvxFree(void *ptr) {
|
|
||||||
free(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void *dvxRealloc(void *ptr, size_t size) {
|
|
||||||
return realloc(ptr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static uint32_t dpmiGetFreeKb(void) {
|
|
||||||
__dpmi_free_mem_info memInfo;
|
|
||||||
|
|
||||||
if (__dpmi_get_free_memory_information(&memInfo) != 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (memInfo.total_number_of_free_pages == 0xFFFFFFFFUL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return memInfo.total_number_of_free_pages * 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void dvxMemGrow(int32_t appId) {
|
static void dvxMemGrow(int32_t appId) {
|
||||||
if (appId < sAppMemCap) {
|
if (appId < sAppMemCap) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t newCap = appId + 16;
|
int32_t newCap = appId + 16;
|
||||||
uint32_t *newArr = (uint32_t *)realloc(sAppMemAtLoad, newCap * sizeof(uint32_t));
|
uint32_t *newArr = (uint32_t *)realloc(sAppMemUsed, newCap * sizeof(uint32_t));
|
||||||
|
|
||||||
if (!newArr) {
|
if (!newArr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(newArr + sAppMemCap, 0, (newCap - sAppMemCap) * sizeof(uint32_t));
|
memset(newArr + sAppMemCap, 0, (newCap - sAppMemCap) * sizeof(uint32_t));
|
||||||
sAppMemAtLoad = newArr;
|
sAppMemUsed = newArr;
|
||||||
sAppMemCap = newCap;
|
sAppMemCap = newCap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void dvxMemSnapshotLoad(int32_t appId) {
|
void *dvxMalloc(size_t size) {
|
||||||
|
int32_t appId = dvxMemAppIdPtr ? *dvxMemAppIdPtr : 0;
|
||||||
|
DvxAllocHeaderT *hdr = (DvxAllocHeaderT *)malloc(sizeof(DvxAllocHeaderT) + size);
|
||||||
|
|
||||||
|
if (!hdr) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr->magic = DVX_ALLOC_MAGIC;
|
||||||
|
hdr->appId = appId;
|
||||||
|
hdr->size = (uint32_t)size;
|
||||||
|
hdr->pad = 0;
|
||||||
|
|
||||||
if (appId >= 0) {
|
if (appId >= 0) {
|
||||||
dvxMemGrow(appId);
|
dvxMemGrow(appId);
|
||||||
|
|
||||||
if (appId < sAppMemCap) {
|
if (appId < sAppMemCap) {
|
||||||
sAppMemAtLoad[appId] = dpmiGetFreeKb();
|
sAppMemUsed[appId] += (uint32_t)size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return hdr + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void *dvxCalloc(size_t nmemb, size_t size) {
|
||||||
|
size_t total = nmemb * size;
|
||||||
|
void *ptr = dvxMalloc(total);
|
||||||
|
|
||||||
|
if (ptr) {
|
||||||
|
memset(ptr, 0, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void dvxFree(void *ptr) {
|
||||||
|
if (!ptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DvxAllocHeaderT *hdr = (DvxAllocHeaderT *)ptr - 1;
|
||||||
|
|
||||||
|
if (hdr->magic != DVX_ALLOC_MAGIC) {
|
||||||
|
// Not a tracked allocation -- pass through to real free
|
||||||
|
free(ptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t appId = hdr->appId;
|
||||||
|
|
||||||
|
if (appId >= 0 && appId < sAppMemCap) {
|
||||||
|
sAppMemUsed[appId] -= hdr->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr->magic = 0;
|
||||||
|
free(hdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void *dvxRealloc(void *ptr, size_t size) {
|
||||||
|
if (!ptr) {
|
||||||
|
return dvxMalloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size == 0) {
|
||||||
|
dvxFree(ptr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
DvxAllocHeaderT *hdr = (DvxAllocHeaderT *)ptr - 1;
|
||||||
|
|
||||||
|
if (hdr->magic != DVX_ALLOC_MAGIC) {
|
||||||
|
// Not tracked -- pass through to real realloc
|
||||||
|
return realloc(ptr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t appId = hdr->appId;
|
||||||
|
uint32_t oldSize = hdr->size;
|
||||||
|
|
||||||
|
DvxAllocHeaderT *newHdr = (DvxAllocHeaderT *)realloc(hdr, sizeof(DvxAllocHeaderT) + size);
|
||||||
|
|
||||||
|
if (!newHdr) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appId >= 0 && appId < sAppMemCap) {
|
||||||
|
sAppMemUsed[appId] -= oldSize;
|
||||||
|
sAppMemUsed[appId] += (uint32_t)size;
|
||||||
|
}
|
||||||
|
|
||||||
|
newHdr->size = (uint32_t)size;
|
||||||
|
return newHdr + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *dvxStrdup(const char *s) {
|
||||||
|
if (!s) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len = strlen(s) + 1;
|
||||||
|
char *dup = (char *)dvxMalloc(len);
|
||||||
|
|
||||||
|
if (dup) {
|
||||||
|
memcpy(dup, s, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dup;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void dvxMemSnapshotLoad(int32_t appId) {
|
||||||
|
(void)appId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1206,25 +1285,13 @@ uint32_t dvxMemGetAppUsage(int32_t appId) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t atLoad = sAppMemAtLoad[appId];
|
return sAppMemUsed[appId];
|
||||||
|
|
||||||
if (atLoad == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t nowFree = dpmiGetFreeKb();
|
|
||||||
|
|
||||||
if (nowFree >= atLoad) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (atLoad - nowFree) * 1024;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void dvxMemResetApp(int32_t appId) {
|
void dvxMemResetApp(int32_t appId) {
|
||||||
if (appId >= 0 && appId < sAppMemCap) {
|
if (appId >= 0 && appId < sAppMemCap) {
|
||||||
sAppMemAtLoad[appId] = 0;
|
sAppMemUsed[appId] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2283,6 +2350,7 @@ DXE_EXPORT_TABLE(sDxeExportTable)
|
||||||
DXE_EXPORT(dvxCalloc)
|
DXE_EXPORT(dvxCalloc)
|
||||||
DXE_EXPORT(dvxRealloc)
|
DXE_EXPORT(dvxRealloc)
|
||||||
DXE_EXPORT(dvxFree)
|
DXE_EXPORT(dvxFree)
|
||||||
|
DXE_EXPORT(dvxStrdup)
|
||||||
DXE_EXPORT(dvxMemSnapshotLoad)
|
DXE_EXPORT(dvxMemSnapshotLoad)
|
||||||
DXE_EXPORT(dvxMemGetAppUsage)
|
DXE_EXPORT(dvxMemGetAppUsage)
|
||||||
DXE_EXPORT(dvxMemResetApp)
|
DXE_EXPORT(dvxMemResetApp)
|
||||||
|
|
@ -2322,10 +2390,11 @@ DXE_EXPORT_TABLE(sDxeExportTable)
|
||||||
DXE_EXPORT(dvxLog)
|
DXE_EXPORT(dvxLog)
|
||||||
|
|
||||||
// --- memory ---
|
// --- memory ---
|
||||||
DXE_EXPORT(calloc)
|
// --- memory (tracked wrappers replace libc for DXE code) ---
|
||||||
DXE_EXPORT(free)
|
{ "_calloc", (void *)dvxCalloc },
|
||||||
DXE_EXPORT(malloc)
|
{ "_free", (void *)dvxFree },
|
||||||
DXE_EXPORT(realloc)
|
{ "_malloc", (void *)dvxMalloc },
|
||||||
|
{ "_realloc", (void *)dvxRealloc },
|
||||||
|
|
||||||
// --- string / memory ops ---
|
// --- string / memory ops ---
|
||||||
DXE_EXPORT(memchr)
|
DXE_EXPORT(memchr)
|
||||||
|
|
@ -2339,7 +2408,7 @@ DXE_EXPORT_TABLE(sDxeExportTable)
|
||||||
DXE_EXPORT(strcmp)
|
DXE_EXPORT(strcmp)
|
||||||
DXE_EXPORT(strcpy)
|
DXE_EXPORT(strcpy)
|
||||||
DXE_EXPORT(strcspn)
|
DXE_EXPORT(strcspn)
|
||||||
DXE_EXPORT(strdup)
|
{ "_strdup", (void *)dvxStrdup },
|
||||||
DXE_EXPORT(strerror)
|
DXE_EXPORT(strerror)
|
||||||
DXE_EXPORT(stricmp)
|
DXE_EXPORT(stricmp)
|
||||||
DXE_EXPORT(strlen)
|
DXE_EXPORT(strlen)
|
||||||
|
|
|
||||||
|
|
@ -172,8 +172,14 @@ void widgetOnKey(WindowT *win, int32_t key, int32_t mod) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch to per-widget onKey handler via vtable
|
// Attribute allocations during event handling to the owning app
|
||||||
|
AppContextT *ctx = (AppContextT *)root->userData;
|
||||||
|
int32_t prevAppId = ctx->currentAppId;
|
||||||
|
ctx->currentAppId = win->appId;
|
||||||
|
|
||||||
wclsOnKey(focus, key, mod);
|
wclsOnKey(focus, key, mod);
|
||||||
|
|
||||||
|
ctx->currentAppId = prevAppId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -195,6 +201,8 @@ void widgetOnKey(WindowT *win, int32_t key, int32_t mod) {
|
||||||
// are also in content-buffer space (set during layout), so no
|
// are also in content-buffer space (set during layout), so no
|
||||||
// coordinate transform is needed for hit testing.
|
// coordinate transform is needed for hit testing.
|
||||||
|
|
||||||
|
static void widgetOnMouseInner(WindowT *win, WidgetT *root, int32_t x, int32_t y, int32_t buttons);
|
||||||
|
|
||||||
void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
|
void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
|
||||||
WidgetT *root = win->widgetRoot;
|
WidgetT *root = win->widgetRoot;
|
||||||
sClosedPopup = NULL;
|
sClosedPopup = NULL;
|
||||||
|
|
@ -202,6 +210,19 @@ void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) {
|
||||||
if (!root) {
|
if (!root) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attribute allocations during event handling to the owning app
|
||||||
|
AppContextT *ctx = (AppContextT *)root->userData;
|
||||||
|
int32_t prevAppId = ctx->currentAppId;
|
||||||
|
ctx->currentAppId = win->appId;
|
||||||
|
|
||||||
|
widgetOnMouseInner(win, root, x, y, buttons);
|
||||||
|
|
||||||
|
ctx->currentAppId = prevAppId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void widgetOnMouseInner(WindowT *win, WidgetT *root, int32_t x, int32_t y, int32_t buttons) {
|
||||||
// Close popups from other windows
|
// Close popups from other windows
|
||||||
if (sOpenPopup && sOpenPopup->window != win) {
|
if (sOpenPopup && sOpenPopup->window != win) {
|
||||||
wclsClosePopup(sOpenPopup);
|
wclsClosePopup(sOpenPopup);
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,12 @@
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
// Route stb_ds allocations through the tracking wrappers so that
|
||||||
|
// arrput/arrfree in DXE code is tracked per-app.
|
||||||
|
extern void *dvxRealloc(void *ptr, size_t size);
|
||||||
|
extern void dvxFree(void *ptr);
|
||||||
|
#define STBDS_REALLOC(c, p, s) dvxRealloc((p), (s))
|
||||||
|
#define STBDS_FREE(c, p) dvxFree(p)
|
||||||
#define STB_DS_IMPLEMENTATION
|
#define STB_DS_IMPLEMENTATION
|
||||||
#include "stb_ds.h"
|
#include "stb_ds.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -557,6 +557,26 @@ bool shellReapApps(AppContextT *ctx) {
|
||||||
if (sApps[i].state == AppStateTerminatingE) {
|
if (sApps[i].state == AppStateTerminatingE) {
|
||||||
shellReapApp(ctx, &sApps[i]);
|
shellReapApp(ctx, &sApps[i]);
|
||||||
reaped = true;
|
reaped = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback-only apps terminate when their last window closes.
|
||||||
|
// They have no main loop to set AppStateTerminatingE, so we
|
||||||
|
// detect termination by checking for zero remaining windows.
|
||||||
|
if (sApps[i].state == AppStateRunningE && !sApps[i].hasMainLoop) {
|
||||||
|
bool hasWindow = false;
|
||||||
|
|
||||||
|
for (int32_t w = 0; w < ctx->stack.count; w++) {
|
||||||
|
if (ctx->stack.windows[w]->appId == sApps[i].appId) {
|
||||||
|
hasWindow = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasWindow) {
|
||||||
|
shellReapApp(ctx, &sApps[i]);
|
||||||
|
reaped = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue