mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-10-24 07:13:11 +03:00
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.
120 lines
4.1 KiB
C++
120 lines
4.1 KiB
C++
#ifndef EE_WINDOW_DRAGANDDROP_HPP
|
|
#define EE_WINDOW_DRAGANDDROP_HPP
|
|
|
|
#include <eepp/config.hpp>
|
|
#include <eepp/core/small_vector.hpp>
|
|
#include <eepp/math/vector2.hpp>
|
|
#include <functional>
|
|
#include <string>
|
|
|
|
namespace EE { namespace Graphics {
|
|
class Image;
|
|
}} // namespace EE::Graphics
|
|
|
|
namespace EE { namespace Window {
|
|
|
|
class Window;
|
|
|
|
enum class DragAction : Uint8 { None = 0, Copy = 1, Move = 2, Link = 4, Ask = 8 };
|
|
using DragActions = Uint32;
|
|
|
|
enum class DragEndReason : Uint8 { Dropped, Cancelled, Unsupported, Failed };
|
|
|
|
enum class DragStartStatus : Uint8 {
|
|
Started,
|
|
Busy,
|
|
InvalidOffer,
|
|
InvalidGesture,
|
|
Unsupported,
|
|
Failed
|
|
};
|
|
|
|
struct DragFile {
|
|
/** Absolute local filesystem path, encoded as UTF-8. */
|
|
std::string path;
|
|
};
|
|
|
|
struct DragPreview {
|
|
/** Optional RGB/RGBA snapshot, borrowed only during startDrag(). If absent, a file/count icon
|
|
* is generated. The native session copies the pixels; it never retains a UI widget. */
|
|
const Graphics::Image* image{ nullptr };
|
|
/** Pointer position relative to the image's top-left corner, in desktop pixels (-2048..2048).
|
|
*/
|
|
Math::Vector2i hotspot{ -12, -16 };
|
|
};
|
|
|
|
struct DragOffer {
|
|
SmallVector<DragFile, 1> files;
|
|
DragActions actions{ static_cast<DragActions>( DragAction::Copy ) };
|
|
DragPreview preview;
|
|
/** The application completes drops on its own window (DragResult::releasedOverSource on
|
|
* X11): the preview shows a pending drop there instead of a rejected one. */
|
|
bool sourceHandlesDrops{ false };
|
|
};
|
|
|
|
struct DragResult {
|
|
std::string error;
|
|
DragEndReason reason{ DragEndReason::Cancelled };
|
|
DragAction performedAction{ DragAction::None };
|
|
/** The gesture was released over its own source window, which X11 never offers as a native
|
|
* target: the session ends Cancelled, and the application can complete it as an internal
|
|
* drop. Elsewhere the source window receives ordinary FileDropped events instead. */
|
|
bool releasedOverSource{ false };
|
|
};
|
|
|
|
struct DragStartResult {
|
|
std::string error;
|
|
DragStartStatus status{ DragStartStatus::Unsupported };
|
|
};
|
|
|
|
struct DragCapabilities {
|
|
bool fileSource{ false };
|
|
bool dataSource{ false };
|
|
/** Legacy SDL file reception; does not imply MIME/action negotiation. */
|
|
bool fileTarget{ false };
|
|
bool negotiatedTarget{ false };
|
|
bool dragImage{ false };
|
|
};
|
|
|
|
using DragCompletionCallback = std::function<void( DragResult )>;
|
|
|
|
/** Native cross-application file dragging. All methods must run on the window/UI thread.
|
|
* This lightweight facade borrows its Window and must not outlive it. A started session owns its
|
|
* serialized payload and completion callback. Rejected starts never invoke completion.
|
|
* Completions run once, outside native event hooks, when input is updated; they may start another
|
|
* drag. Cancellation/closing a source window also completes the session. Callbacks must not borrow
|
|
* objects that may be destroyed before completion (use weak ownership when appropriate).
|
|
* Engine teardown cancels and releases pending sessions without invoking their callbacks.
|
|
* Initially only Copy is supported. Dropped means the native transfer finished, not that a browser
|
|
* uploaded or a file manager copied a file. Existing internal UI dragging is independent.
|
|
*/
|
|
class EE_API DragAndDrop {
|
|
public:
|
|
static DragAndDrop forWindow( Window& window );
|
|
|
|
DragCapabilities getCapabilities() const;
|
|
|
|
/** Call after a movement threshold, while the initiating left mouse button is still down.
|
|
* Preview images must be RGB/RGBA, at most 2048 pixels per side and 1,048,576 pixels total.
|
|
* The offer is read synchronously; the session retains only its own native representation. */
|
|
DragStartResult startDrag( const DragOffer& offer, DragCompletionCallback completion = {} );
|
|
|
|
void cancelDrag();
|
|
|
|
bool isDragging() const;
|
|
|
|
/** Serialize an offer as text/uri-list (CRLF). Rejects empty, relative, invalid UTF-8 and NUL
|
|
* paths. Preserves order and duplicate entries. On failure output is empty. */
|
|
static bool encodeFileUriList( const DragOffer& offer, std::string& output,
|
|
std::string* error = nullptr );
|
|
|
|
private:
|
|
explicit DragAndDrop( Window& window );
|
|
|
|
Window* mWindow;
|
|
};
|
|
|
|
}} // namespace EE::Window
|
|
|
|
#endif
|