Skip to content

Burlak 1.3.0: drop into the other panel, from plugin panels, into another Far - #9

Draft
refaim wants to merge 22 commits into
masterfrom
features
Draft

refaim wants to merge 22 commits into
masterfrom
features

Conversation

@refaim

@refaim refaim commented Sep 13, 2026

Copy link
Copy Markdown
Owner

Draft: CI only. Not to be merged or released until Roma has tested the DLL by hand.

Feature 2 of docs/ARCHITECTURE.md §0 is in: a drop onto the other panel of the same Far replays Far's own mouse drag through the console input. Features 1 (plugin panels) and 3 (another Far) follow on this branch.

🤖 Generated with Claude Code

refaim and others added 22 commits September 14, 2026 02:30
The tool window that covers the terminal during a drag now has a real
drop target. Over the item rows of the panel that is not the source it
offers copy, or move with Shift; elsewhere nothing. On the drop Burlak
copies nothing itself: it hands the point to Far's thread, which re-reads
the panels, and replays Far's own panel-to-panel mouse drag through the
console input - a left press at the cell the gesture started on and a
release at the drop cell - so Far's KEY_DRAGCOPY / KEY_DRAGMOVE path does
the copy with its own progress and conflicts, plugin panels included.
Far's native mouse drag between panels, which Burlak had been swallowing
since 1.0, works again through this.

Version 1.3.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Before writing the two records Far's thread checks that the panels are
still the current window, that the source is the same visible file panel
with the same directory and the same selected paths, and that the
destination is a visible file panel in the directory the drop point was
mapped to; a macro or a timer can change any of that during the drag.
A partial write is completed with a release so Far's drag state is not
left armed. BURLAK_NO_DESKTOP=1 skips the tests that need the desktop.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The identity compared before the replay now includes the current and top
items of both panels, the cell mapped at the drop must map the same with
fresh geometry, the host is found from the recorded drop point rather than
the live cursor, and a drag whose paths do not all parse into the shell
namespace does not start at all, so the advertised payload and the
selection Far will copy are always the same set.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An archive, FTP or any other plugin panel has no paths on disk, so the
drag starts with zero-byte placeholders carrying the item names in a temp
folder of this run, and when the button is released over a willing target
Far's thread extracts the real files over them by calling the owning
plugin's GetFilesW the way Far itself does for a plugin-to-plugin copy
(OPM_SILENT into a temp directory), while the tool thread waits with COM
pumping. A refusal or a crash under the SEH guard cancels the drop with a
message naming the plugin. A drop onto the other panel of the same Far
still goes through Far's own copy and extracts nothing. Temp runs are
removed after the drag when the target lets go, and swept at start and
exit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The items go to GetFilesW as the buffers Far returned, every field intact;
the plan is checked against the panel again right before the call; a
rewritten DestPath cancels the drop; the waiter on the tool thread is
signalled from an RAII completer so an exception on Far's thread cannot
leave it waiting; run directories advance past leftovers; duplicate names
refuse the drag up front; placeholder drags offer no link.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Stopping the tool window waited three seconds and then destroyed the drag
state while OLE could still be unwinding through a drop target: a
use-after-free that the CI runner hit in the e2e drag. Shutdown now cancels
a pending extraction, then pumps until the thread has exited. Along the way:
absolute item names (TmpPanel) are advertised as they are, a rewritten
DestPath is copied inside the SEH guard, duplicate detection is n log n,
the sweep never throws, and BURLAK_TRACE_TESTS names each test case on
stderr so a crash on CI says where.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A drag announces itself to every other Burlak with a registered window
message; each answers with its terminal window (the console window under
conhost, its owner under Windows Terminal) and the tick of its last console
focus, so among several Fars in one terminal window the one shown last
wins. When the button comes up over such a window the source extracts
first if the files are placeholders, asks with a small menu on a
right-button drag, cancels the OLE drop so the terminal pastes nothing, and
hands the peer the paths, the point and the effect over WM_COPYDATA. The
peer maps the point to a panel of its own, copies with IFileOperation into
that directory and redraws, or says in one line why it cannot.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Equal integrity only (no UIPI allowance), a single-use nonce per drag that
Hellos and Drops must echo, a class and process check on every window a
message goes to, bounded sizes and counts before any allocation, timed
sends that abort on a hung window, a registry that lives for one drag and
holds at most 64 peers, drops queued on the receiver, and the extracted
run handed to the receiver by rename so the source can forget it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A timed-out Drop send leaves the run for the sweep; a receiver that refuses
the drop still adopts and deletes the run; a message target must match the
expected pid as well as the class; the desktop preconditions are real
doctest skips and the OLE-entry probe reports a warning.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The tool window lives from startup now, so any process can send its
WM_USER messages; the payload travels in a guarded slot of the window
state and the message itself is empty.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replayed console records now carry Far's window-mode buffer delta, as
console::WriteInput does, so a drop onto the other panel no longer lands
on row 0 and opens the F9 menu. Placeholder-backed data objects publish
CFSTR_PREFERREDDROPEFFECT = COPY so Explorer stops showing a move on the
same volume. An extracted run survives a completed drop for a ten-minute
grace period (targets such as Telegram read the files after the drop)
and is swept with the dead-owner runs at startup, before each drag and
at exit; the grace clock starts at retention, and the tool window stays
active until the run is retained.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Far is now an OLE drop target: while an external drag is over the host,
the tool window covers the panels backed by real directories and takes
the drop, copying (Shift: moving) with the shell's own progress in this
Far's window, following the optimized-move protocol so the source never
deletes what was moved. A drop from another Far is that same path, so
the private Far-to-Far protocol (announce/hello/drop over WM_COPYDATA,
the nonce registry, temp-run adoption) is gone. In a shared Windows
Terminal window the last-focused Far receives, marked by a window
property on the host. Temp runs for foreign targets are swept from a
tool-thread timer: three-minute grace, runs with a file open elsewhere
are skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The export-level drop test compared the runner's 8.3 temp spelling with
the canonical paths the shell hands back through CF_HDROP; both sides
are canonical now. The real-OLE receive test releases only once OLE's
hover has reached the overlay, warns honestly when it never does, and
collects its helper threads' exceptions on the main thread; the test
main names the test case and the active exception on abort or
terminate so a fast-fail on the runner is diagnosable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A fast-fail on the runner leaves no stack; the test job now enables
local crash dumps, reruns every failed test executable under cdb with
!analyze and a full thread dump when ctest fails, and uploads the dumps.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The class was registered under the executable's module handle with its
result ignored and never unregistered, so a copy of the code in an
unloaded DLL left a class whose window procedure no longer existed and
the next window of that name died in USER32's control-flow-guard check.
The class now belongs to the module holding the procedure, a foreign
class of the same name fails the start, and the last tool thread
unregisters it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A plugin-panel release revalidates the panel and its archive location
instead of the live selection, which is the cursor item and moves with
the drag. The drag starts after two cells sideways or one row down, so a
right-button release no longer falls back to Far's context menu. An
optimized move and every drop Burlak declined after accepting the hover
answer COPY: on a NONE answer ole32 delivers WM_DROPFILES along the owner
chain and the console pastes the path. Temp runs are kept for one
minute and swept every fifteen seconds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant