Data safety (VFS):
- Copies write a temporary sibling and replace the destination only
once it is complete (as writeAll() already did): a failed overwrite
kept nothing of the old destination, and copying onto a hard link to
the source truncated the source itself. A destination symlink is
written through, and one that resolves to the source fails as the
same file.
- Moving a symlink to another file system is refused: the fallback copy
followed it and then removed the copied entries through the link,
emptying its target.
- Saves keep the exact mode of the file they replace: the temporary was
created with it, but filtered through the umask.
Deadlocks and blocking:
- writeAll( createParents ) waited on makeDirs() queued on its own
orchestration pool: one thread deadlocked at once, two with two
concurrent saves. Orchestration jobs now create parents directly.
- Copies and moves run on their own pool (Config::transferThreads), so
long transfers no longer delay saves, reads and recursive removals.
- Local watches are registered and removed on the I/O pool: efsw and the
directory checks touch the filesystem, which blocked the UI thread on
slow mounts.
SSH security:
- "@revoked" known_hosts keys are rejected; they were skipped as
unknown, so StrictHostKeyChecking accept-new trusted them.
- Sessions are shared only between configurations with the same
security policy (known hosts, host key checking, algorithms,
authentication settings), not just the same destination: a strict
alias reused a session a permissive one had accepted.
- A configured algorithm restriction that permits nothing this libssh2
supports fails the connection instead of using libssh2's defaults.
SSH liveness:
- Operations submitted to a failed or closed connection fail with
ConnectionLost instead of staying pending forever.
- ssh-agent requests run on a worker thread (SSHAgent, Unix-domain
sockets), with libssh2_userauth_publickey() and a sign callback that
waits without blocking: an agent waiting for a hardware key touch no
longer stalls every other session. Windows keeps libssh2's agent.
- Connecting no longer holds a helper thread while the user answers a
host key or password prompt: the setup continues from the session's
ready task, one ProxyJump hop at a time. Jump hosts only need to
authenticate, not to offer SFTP.
- Idle connections close after SFTPProviderConfig::idleTimeout. The old
check compared use_count() with 1 and never passed: connections now
track their users (open handles, pumps, jump children).
- A remote watch agent that never answers falls back to polling after
5 seconds; the directory listing waited for it indefinitely.
Core:
- CancellationSource::cancel() keeps the shared state alive while its
callbacks run: a callback destroying the source freed it mid-cancel
(heap use after free under AddressSanitizer).
- MainThreadExecutor::shutdown() detaches the executor from its
dispatcher node and drops queued and later functions.
- String::icompare(): case-insensitive comparison without allocations.
VFSDirectoryModel (UI thread):
- Names compare with String::icompare() instead of lowercased copies,
which made every sort, merge and insertion allocate (about 2x slower
when sorting).
- Sorting by type asks the type provider once per entry instead of
twice per comparison.
- The case-insensitive plain text filter keeps a search table instead
of lowercasing every name on each row rebuild.
- Watch events update the visible rows in place instead of rebuilding
them from every entry per event.
Drag and drop:
- The X11 drag source showed the pending badge over the source window
for every application, which broke the X11 drag and drop integration
tests in CI. DragOffer::sourceHandlesDrops now asks for it; eefm sets
it, since it completes those drops as its own drag.
eefm:
- Shutdown destroyed the main thread executor before the VFS and the
thread pool while continuations still held it: closing during a
transfer could dispatch through freed memory. Transfers are cancelled,
the VFS is destroyed, the executor is detached, and only then are the
scene, its pool and the executor destroyed.
- Remote files opened locally were downloaded to
/tmp/eefm/<scheme>/<host>/<name>: files with the same name from other
folders, users or ports overwrote each other, in a directory shared by
every user. They now go to a per-file folder (a hash of the complete
URI) in the user's cache.
Every fix comes with a regression test.
feat(eefm): drag files to other applications
X11 never offers a native drag to its source window, so a release over
it ended as a plain cancellation. DragResult::releasedOverSource now
tells the application, which can complete it as its own internal drop.
That lets applications start the native drag as soon as their own drag
begins (as file managers do: the files can then go to any window, even
one switched to with Alt+Tab mid-drag) and still handle drops on
themselves. The preview shows the pending badge over the source window
instead of the rejected one.
A drag of local files starts the system drag at once, along with eefm's
own, so the files can be dropped on any window, also after switching to
it with Alt+Tab mid-drag (starting it only once the window had lost
focus was refused on X11). eefm's drag keeps highlighting targets while
it sees the pointer, and waits for the system drag to end instead of
dropping on release:
- Released over eefm on X11 (releasedOverSource), it drops as eefm's
drag: panes, folders, tabs and places, with the Move/Copy menu.
- On Wayland, macOS and Windows that drop arrives as a file drop; being
eefm's own drag, it is resolved the same way (Move/Copy menu).
- Escape cancels both. Remote files keep the internal drag only.
The system drag shows eefm's drag label, which now has an icon (folders
tinted as in the rows) and the name or the item count. For several items
the icon is that of their most common type, the pressed row's on a tie. It
is rendered alone into the back buffer once, read back and handed to the
system as the preview image. It is cleared with the theme's background,
so the label's rounded corners match it where the preview is opaque
(X11).
Drops from other applications are still copied, now skipping files
already in the destination folder instead of duplicating them as
"(copy)".
Replace the 50 ms sleep and 200 ms deadline with a synchronized
condition-wait handshake and a generous transfer deadline.
Add a scoped budget-wait hook to keep fallback polling beyond the test
deadline. Use the budget change counter as the wait predicate so spurious
wakeups cannot hide a missing notification.
Confirmed the test fails when the idle-socket notification is removed.
- Run X11 and Weston tests at 1x and 2x scale.
- Accept both libwayland object ID log formats.
- Preserve test failures through logging pipelines.
- Add shell32 to Windows dependencies in both Premake scripts.
- Update SDK platform support and linking documentation.
- Load Wayland APIs dynamically and track genuine pointer press serials.
- Serve file URI lists on a dedicated queue and transfer thread.
- Render native drag icons with Copy, rejected and pending feedback.
- Use memfd_create on Linux with a portable temporary-file fallback.
- Suppress synthetic gesture releases and clean up on cancellation,
window destruction and process exit.
- Add isolated Weston integration tests and document API requirements.
eefm:
- Tabs can be dragged between the panes. When a pane's last tab moves
out, the pane gets a new tab on the same folder, so the split view
stays.
- A middle click on a folder opens it in a new background tab; with
Ctrl (Cmd on macOS) the new tab is selected and focused.
- Item context menus list Create New after the open actions.
- Open Terminal Here prefers eterm (in PATH or next to eefm), else the
default terminal: $TERMINAL, xdg-terminal-exec, the desktop's own
(KDE's setting, GNOME, Xfce, MATE, LXQt), x-terminal-emulator, then
the common ones. On Windows it opens PowerShell (else cmd.exe) in a
new console, so it shows in the default terminal; it used to start
as a detached process with no console.
- Tab close buttons work: the focus handoff to the files took the
focus from them, and the click goes to the focused node. Their hover
color shows again.
- Visual polish: long cell text ends in an ellipsis, dates are shorter,
and the name gets more room. Hover and selection are soft accent
tints with an outline; the inactive pane's view is a little dimmer,
with a grey selection. The location bar highlights on hover. The
filter bar is one framed field and starts hidden (Ctrl+I). Places
share the row tints, usage bars are softer, scrollbars are grey until
used, unselected tabs dim their close button, and folders are tinted
when eefm's own icons are used.
- Crashes print a stack trace (backward).
eefm and eterm:
- The font picker opens centered and preselects the font in use when
none is configured. A picked fallback font replaces the previous one,
so it applies at once. The UI scene gets a thread pool, as in ecode,
so the picker lists fonts in the background.
eepp:
- UIPushButton: with an expanded text view, the content size is the
whole text, so auto sized table columns still fit their content.
- VFSDirectoryModel: folder name cells get the vfs_folder class.
build:
- backward stack traces for more tools (texture atlas editor, map
editor, UI editor, eterm, texture packer, eeiv, eproc, eefm); libdw
is linked by the backward module.
Requests that hit a connection limit used to wait inside their worker,
polling every 10 ms, and an async request's timeout included that wait.
On a page with many resources from one origin, later requests timed out
while queued, and waiting requests tied up shared thread-pool workers
that other origins needed.
Connection queue:
- Serve blocking and async requests from one first-come, first-served
queue per pool. Requests waiting on one origin's limit never block
requests to other origins.
- In shared thread-pool mode, an async request runs on a worker only
after it has been granted a connection. Grants are handed to the
executor from a service thread, never from the thread releasing a
connection, which could be inside the executor's own lock.
- Requests hold their executor only weakly, so a request can never be
the last owner of the thread pool running it.
- Start an async request's timeout once it holds a connection. A
blocking call's timeout still covers the wait plus the transfer, and
a resumed download keeps its deadline across the HEAD probe.
- Wait on a condition variable instead of polling. Cancelling by ID,
shutdown() and Pool::close() release queued requests immediately.
- Let running requests and blocking callers take over a slot reserved
for queued work that has not started; that work queues again, first
in line. Without this, a redirect, retry or resumed download on a
busy executor could wait forever on slots reserved behind it.
- When queued work starts, switch to a connection that went idle while
it waited, instead of opening a new one.
- Replace an idle connection the server has closed in place, keeping
its slot.
Process-wide limit and idle expiry:
- Limit open sockets across all pools with
Http::setMaxOpenConnections() (default 256). At the limit, close the
least recently used idle socket in any pool, then a socket held for
queued work, and only wait while every socket is in active use.
- Wake waiters on the limit when a socket goes idle, and recheck a
change counter so a wake-up just before waiting is never missed.
- Only inspect a socket for reclaim once its connection is known to be
idle or reserved, never while a request is using it.
- Close idle connections from a background timer when idleTimeout
elapses, instead of only when a connection is next requested in the
same pool.
- The budget and its service thread are intentionally never destroyed,
so the thread is never joined during static destruction, which would
deadlock under the Windows loader lock.
Shutdown:
- Add Http::cancelAllRequests(), which cancels every queued and running
request in every pool and closes idle connections. A per-pool
generation counter makes running requests stop at their next check,
and the pools stay usable afterwards.
- Add Http::waitForConnectionsToClose().
- Engine cancels all requests as it starts shutting down. Once its
windows are closed, it waits up to 100 ms for sockets to close before
ending TLS.
- Check for cancellation every 25 ms instead of 100 ms while reading, so
requests stop within that wait.
TLS:
- SSLSocket::end() now waits while TLS sockets exist: the last socket
destroyed releases the backend, and a socket created in the meantime
keeps it.
- Set up the backend and register a new socket a
lock, so a concurrent end() cannot release the backend in between.
- Add SSLSocket::isInitialized() and a testing h
socket construction.
Skip versioned SDL2 and SDL3 SDK directories before recursing,
preventing bundled libraries and debug symbols from being treated
as project text files.
The full windows.h pulls in the RPC headers, whose UUID clashed with
EE::System::UUID brought into the global namespace by eterm's headers
(MSVC C2872). The terminal only needs kernel32 and the toolhelp API, so
define WIN32_LEAN_AND_MEAN as trash.cpp and apps.cpp already do.
FSEvents reports events under the watched directory's real path
(/private/var/... for a root under /var, such as the temporary
directory), so local watch events kept absolute paths instead of paths
relative to the watched root. Keep the resolved root when it differs and
strip whichever prefix the event path starts with. Update efsw for the
same fix in efsw-agent.
Replace thread-local connections with owned pools shared across workers.
Bound total and per-origin connections, expire idle sockets, and check
idle connections before reuse. Retry safe requests after stale-connection
races without replaying POST requests.
Fix response framing, chunk trailers, decompression completion, empty
compressed bodies, and handling of bytes beyond completed responses.
Give each WebResourceCache partition its own transport, preserving
connections across navigation and shared tabs. Close session transports
without blocking the UI thread. This substantially improves browsing
performance in the ui_html example.
Preserve every Set-Cookie field and attribute redirect cookies to the
client that received the response. Prevent rejected redirects from
assigning cookies to their destination.
Add regression coverage for pooling, framing, cancellation, TLS policy
isolation, browser sessions, and redirect cookie attribution
MSVC instantiates the implicit copy operations of dllexport classes, and
FreedesktopIconLookup's unique_ptr theme list cannot be copied (C2280).
The lookup is never copied, so delete them explicitly.
- UITableCell: leave Enter to the model view instead of clicking the cell.
With focus on the selected row's cell, single-click navigation opened
the row twice: once from the cell click and once from the view's Enter
handler. Affected UITreeView, and UITableView/UIListView since they
follow focus-on-selection.
- Add a regression test for list, table and tree views in both click
modes.
- Link CoreFoundation and CoreServices in eepp_module_vfs_add(): efsw's
FSEvents watcher needs them, which broke the eefm link on macOS.
UI:
- Add UISystemIconTheme: colored file, folder and place icons from the
freedesktop icon theme, the Windows shell or macOS, loaded asynchronously
with glyph fallbacks. Platform helpers live in src/eepp/ui/icons/.
- UITableView: rubber band selection (tableview::rubberband, styled in
breeze.css), Shift range selection from an anchor, type-to-select by a
configurable column, and focus following the selection.
- UISpinBox: keep partial text while typing; clamp and normalize on Enter or
focus loss, and detach the input listeners on destruction.
- InputEvent: report the window position of file drops (SDL3).
- Image: detect SVG documents preceded by comments, a DOCTYPE or XML
declarations.
eefm:
- Add a settings window (theme, fonts, rendering, files, terminal),
persisted in config.cpp.
- Add a terminal panel (F4) whose shell follows the active pane while it
waits at its prompt.
- Show platform icons for files and places, cached per MIME type.
- Rename in place, select created or renamed items, and copy files
dropped from other applications into the pane or folder under the drop.
- Fix MIME descriptions and icons for exact names, multi-dot extensions and
globs; cache unknown types negatively.
eterm:
- Apply UI font, size, hinting and anti-aliasing settings live.
Include only the selected backend in Engine and guard both platform helper
classes and the SDL2 implementation. This prevents MSVC SDL3 builds from
exporting SDL2 vtables and failing with unresolved helper methods.
- Implement a Cocoa drag source on NSDraggingSession for SDL2 and SDL3.
- Use the offered snapshot as the drag image, or each file's Finder icon,
with AppKit's own count badge and copy cursor.
- Offer Generic alongside Copy only within the application, so SDL2's
Cocoa windows accept drops while other apps still see a copy-only offer.
- Report cancellation immediately and refuse the drop, since AppKit cannot
abort a running session.
- Fix macOS SDL3 builds: use pkg-config instead of the removed sdl3-config
and keep EE_SDL3_FROM_ROOTPATH for framework builds only.
- Build the native drag tests on macOS, add the Android drag sources, and
avoid the windows.h max macro in the Win32 source.
- Document macOS behavior and platform support.
- Warm the notification workload before timing one million calls
- Collect ten fixed enabled/disabled pairs with alternating order
- Preserve the 1.01x ratio-of-medians guard without conditional retries
- Validate reported measurements and retain JSON diagnostics on failure
- Prevent D-Bus service output from contaminating benchmark artifacts
- Run native AT-SPI checks after timing failures without masking CI failure
- Add deterministic runner tests and document the measurement protocol
- Introduce DragAndDrop with capability queries, copy-only file offers,
image previews, cancellation, and completion callbacks.
- Implement XDND and OLE sources with safe input recovery and window teardown.
- Keep SDL2 and SDL3 presentation and input handling in their own backends.
- Load X11 dynamically without adding a link dependency.
- Reuse Image, URI, and timing APIs; fix Image::fillWithColor().
- Add a manual playground, integration tests, and consumer documentation.
- Migrate SDK builds and packaging to SDL3 and fix bundled runtime lookup.
Only trim zeros from fractional digits, preserving integers and
scientific notation. Fixes the universal locator calculator displaying
14 instead of 140 for 86504-86364.
Use an 80×24 grid when a terminal view has fewer than two rows or
columns, preventing placeholder and collapsed layouts from creating
a one-row PTY. Resize normally once usable layout dimensions exist.
Add regression coverage for startup sizing and resizing after layout.
Refs: SpartanJ/ecode#969
Async core (EE namespace, documented in docs/articles/async_core.md):
- Add Result<T, E>, Task<T>/TaskSource<T> with continuations and
abandonment, Executor, CancellationToken and SmallUniqueFunction
- Make ThreadPool an Executor with submit() returning a Task; work items
are move-only
- Add Scene::MainThreadExecutor to resume continuations on the UI thread
- Move URI to EE core (eepp/core/uri.hpp) with filesystem helpers
(fromFSPath, getFSPath, getParent); EE::Network::URI remains an alias
- Add System::SHA1 (streaming, modeled on MD5)
- Rename the pack path index to System::PackFileIndex; VirtualFileSystem
is kept as a deprecated alias
eepp-vfs module (src/modules/vfs, documented in docs/articles/vfs.md):
- URI-addressed VirtualFileSystem with pluggable providers: file://,
mem://, res:// (packs) and sftp://
- Async file and directory handles, metadata, space info, capabilities
and IOStream adapters
- Copy/move transfers with progress, cancellation and conflict options
- Change watching through efsw locally, efsw-agent over SSH for sftp://,
and a polling fallback
- VFSDirectoryModel for table views: sorting, name filtering (plain,
glob, regex), type and item-count columns, per-column CSS classes and
lifetime-safe executor delivery
SFTP:
- SSH sessions over libssh2 with ssh_config parsing (Host/Match,
ProxyJump, ProxyCommand), known_hosts verification, ssh-agent, key and
keyboard-interactive authentication, and connection pooling
- Built by default; opt out with --without-vfs-sftp
- Vendor libssh2 1.11.1 with the mbedTLS backend, and add Ed25519 host
and user keys to it through Monocypher 4.0.2 (mbedTLS has no Ed25519)
eefm (src/tools/eefm), a Dolphin-like file manager on eepp-vfs:
- Split panes, each with its own tabs, location bar, history and
filter bar
- Places panel with Places, Remote (saved locations and network mounts)
and Devices (with usage), editable, hideable and reorderable by drag
- Details view with drag and drop, clipboard interoperable with other
file managers, rename, new folder/file, trash (freedesktop, macOS,
Windows) and properties
- Open and Open With from shared-mime-info and desktop entries
- Persisted window, panes, tabs, columns, sort and places
UI and window fixes:
- Keep table column max widths across model updates and give the main
column the remaining width
- Count left margins when laying out push button icon and text
- Add Clipboard::setMimeData, getDataString and getTextMimeTypes
- Report Zip::close/addFile failures and free archives correctly
Tests and tooling:
- Unit tests for the async core, VFS providers, transfers, watching,
the directory model, ssh_config, SFTP (local sshd), the efsw agent
protocol, SHA1 and URI
- Add a VFS benchmark
- Update efsw with efsw-agent and its wire protocol
Move Context and makeCurrent() from UISceneNode to SceneNode, allowing
scene drawing, updates, and input/IME dispatch to use the same scoped
binding without UI-specific branches.
Replace SceneManager's cached UI scene and scoped scene pointers with
one persistent SceneNode binding. Add setCurrentSceneNode() and make
getUISceneNode() return nullptr when the current scene is not a UI scene,
removing implicit fallback searches.
Establish the default binding during initialization and select a
surviving application scene when the current native window closes.
Use scoped binding in UICodeEditor::showFindReplace() and migrate
existing callers of the UI-specific setter.
Add regression coverage for nested and moved contexts, strict UI lookup,
cross-window restoration, scene removal, find/replace, and window closing.
Remove pending layout, style and style-state entries from the old
scene during rebinding without invalidating shared accessibility
identities. Requeue dirty layouts in the destination scene while
preserving their invalidation reasons.
Fix a use-after-free exposed by TabFocus.WidgetCreatorDefaultsAreIntentional
after deleting a WebView.
Add regression coverage for WebView deletion, subtree and reverse
rebinding, and coalesced layout work.
- Expose source text, heading levels, links, lists/items and image alt text
- Keep formatting wrappers transparent and nested controls accessible
- Route link activation through existing navigation and interception
- Project passive fragment focus to links while preserving control focus
- Notify clients of text, href, alt text and semantic role changes
- Map HTML semantics to AT-SPI, UIA and macOS accessibility APIs
- Avoid advertising selection capabilities for static HTML lists
- Preserve dormant allocation behavior without new per-widget caches
- Fix MinGW harness compatibility with both TreeScope enum spellings
- Add unit/native regression coverage and update accessibility docs
- Use the native AX navigation-order attribute name on older AppKit SDKs
- Supply missing UIA declarations with compatible COM signatures and UUID
- Generate missing x86/x64 UIAutomationCore import libraries for older MinGW
- Keep fallback library paths consistent across Premake 4 and 5
- Add native navigation-order and UIA event-advice ABI regression coverage
- focusable now targets the native control, without making the host another Tab stop.
- Overrides survive type changes, including transitions through hidden.
- Added regression coverage and updated documentation.
- Make UIWidget accessibility queries and actions virtual while retaining
centralized resolver defaults for built-in widgets.
- Honor custom semantics in hierarchy, focus, auditing, action validation,
and relation names while preserving one-hop relation resolution.
- Remove obsolete parent-change forwarding methods and test notifications
through actual scene operations.
- Restrict SceneNode's hierarchy observer to the internal accessibility bridge.
- Add custom-widget and dormant-construction regression tests and update docs.
- Add a protected UINode rebinding hook with a no-op default.
- Move widget identity invalidation into the UIWidget override.
- Remove the UINode self-downcast and AccessibilityManager dependency.
- Preserve child-first cleanup before updating the local scene pointer.
- Extend regression coverage to verify rebinding and invalidation order
- Verify semantic references survive same-scene reparenting through plain
nodes and are invalidated when moving between scene owners.
- Check text references and semantic hierarchy without native clients.
- Verify indexed insertion reports the final semantic index when a
child-count callback reorders destination siblings.
- Assert stable identities and exactly one ordered removal/arrival pair.
- Report semantic subtree removals and arrivals through plain nodes,
preserving identities and avoiding duplicate embedded-scene notifications.
- Add regression coverage for direction changes, reparenting, focus,
selection, event ordering and mirrored-layout hit testing.
- Extend macOS password-mode tests to verify retained element identity,
protected text access and native transition notifications.
- Qualify the event-loop wake-up call to resolve MSVC's Window ambiguity.
- Document accessibility notifications for plain-node subtree moves.