basic file copy and paste

This commit is contained in:
nakst 2021-08-31 17:44:07 +01:00
parent d55a73dc5f
commit 35d4f727f9
15 changed files with 179 additions and 40 deletions

View File

@ -120,6 +120,9 @@ void CommandNewFolder(Instance *instance, EsElement *, EsCommand *) {
}
void CommandCopy(Instance *instance, EsElement *, EsCommand *) {
// TODO If copying a single file, copy the data of the file (as well as its path),
// so that document can be pasted into other applications.
uint8_t _buffer[4096];
EsBuffer buffer = { .out = _buffer, .bytes = sizeof(_buffer) };
buffer.fileStore = EsClipboardOpen(ES_CLIPBOARD_PRIMARY);
@ -127,9 +130,9 @@ void CommandCopy(Instance *instance, EsElement *, EsCommand *) {
for (uintptr_t i = 0; i < instance->listContents.Length() && !buffer.error; i++) {
if (instance->listContents[i].selected) {
FolderEntry *entry = instance->listContents[i].entry;
String name = entry->GetName();
EsBufferWrite(&buffer, STRING(instance->path));
EsBufferWrite(&buffer, STRING(name));
String path = instance->folder->itemHandler->getPathForChild(instance->folder, entry);
EsBufferWrite(&buffer, STRING(path));
StringDestroy(&path);
uint8_t separator = '\n';
EsBufferWrite(&buffer, &separator, 1);
}
@ -149,6 +152,66 @@ void CommandCopy(Instance *instance, EsElement *, EsCommand *) {
}
}
void CommandPaste(Instance *instance, EsElement *, EsCommand *) {
if (EsClipboardHasFormat(ES_CLIPBOARD_PRIMARY, ES_CLIPBOARD_FORMAT_PATH_LIST)) {
// TODO Background task.
// TODO Renaming.
// TODO Recursing into folders.
// TODO Reporting errors properly.
// TODO Other namespace handlers.
// TODO Selecting *all* pasted files.
// TODO Update parent folders after copy complete.
void *copyBuffer = nullptr;
size_t bytes;
char *pathList = EsClipboardReadText(ES_CLIPBOARD_PRIMARY, &bytes);
if (pathList) {
const char *position = pathList;
while (bytes) {
const char *newline = (const char *) EsCRTmemchr(position, '\n', bytes);
if (!newline) break;
String source = StringFromLiteralWithSize(position, newline - position);
String name = PathGetName(source);
String destination = StringAllocateAndFormat("%s%s", STRFMT(instance->folder->path), STRFMT(name));
EsError error = EsFileCopy(STRING(source), STRING(destination), &copyBuffer);
if (error == ES_SUCCESS) {
EsMutexAcquire(&instance->folder->modifyEntriesMutex);
EsAssert(instance->folder->doneInitialEnumeration);
EsDirectoryChild directoryChild;
if (EsPathQueryInformation(STRING(destination), &directoryChild)) {
FolderAddEntryAndUpdateInstances(instance->folder, STRING(name), &directoryChild, instance);
} else {
// File must have been deleted by the time we got here!
}
EsMutexRelease(&instance->folder->modifyEntriesMutex);
} else {
goto encounteredError;
}
position += source.bytes + 1;
bytes -= source.bytes + 1;
StringDestroy(&destination);
}
} else {
encounteredError:;
EsPoint point = EsListViewGetAnnouncementPointForSelection(instance->list);
EsAnnouncementShow(instance->window, ES_FLAGS_DEFAULT, point.x, point.y, INTERFACE_STRING(CommonAnnouncementPasteErrorOther));
}
EsHeapFree(pathList);
EsHeapFree(copyBuffer);
} else {
// TODO Paste the data into a new file.
}
}
void InstanceRegisterCommands(Instance *instance) {
uint32_t stableCommandID = 1;

View File

@ -5,9 +5,7 @@
Array<Folder *> loadedFolders;
// #define MAXIMUM_FOLDERS_WITH_NO_ATTACHED_INSTANCES (20)
// TODO Temporary.
#define MAXIMUM_FOLDERS_WITH_NO_ATTACHED_INSTANCES (0)
#define MAXIMUM_FOLDERS_WITH_NO_ATTACHED_INSTANCES (20)
Array<Folder *> foldersWithNoAttachedInstances;
/////////////////////////////////
@ -57,7 +55,6 @@ EsError FSDirRenameItem(Folder *folder, String oldName, String newName) {
};
EsError FSDirEnumerate(Folder *folder) {
// Get the initial directory children.
// TODO Recurse mode.
EsNodeType type;
@ -66,6 +63,13 @@ EsError FSDirEnumerate(Folder *folder) {
return ES_ERROR_FILE_DOES_NOT_EXIST;
}
EsVolumeInformation volume;
folder->readOnly = true;
if (EsMountPointGetVolumeInformation(STRING(folder->path), &volume)) {
folder->readOnly = volume.flags & ES_VOLUME_READ_ONLY;
}
EsDirectoryChild *buffer = nullptr;
ptrdiff_t _entryCount = EsDirectoryEnumerateChildren(STRING(folder->path), &buffer);
@ -90,8 +94,9 @@ void FSDirGetTotalSize(Folder *folder) {
}
}
String FSDirGetPathForChildFolder(Folder *folder, String item) {
return StringAllocateAndFormat("%s%s/", STRFMT(folder->path), STRFMT(item));
String FSDirGetPathForChild(Folder *folder, FolderEntry *entry) {
String item = entry->GetInternalName();
return StringAllocateAndFormat("%s%s%z", STRFMT(folder->path), STRFMT(item), entry->isFolder ? "/" : "");
}
/////////////////////////////////
@ -153,7 +158,8 @@ EsError DrivesPageEnumerate(Folder *folder) {
return ES_SUCCESS;
}
String DrivesPageGetPathForChildFolder(Folder *, String item) {
String DrivesPageGetPathForChild(Folder *, FolderEntry *entry) {
String item = entry->GetInternalName();
return StringAllocateAndFormat("%s/", STRFMT(item));
}
@ -197,7 +203,7 @@ NamespaceHandler namespaceHandlers[] = {
.getFileType = DrivesPageGetFileType,
.getVisibleName = DrivesPageGetVisibleName,
.getTotalSize = DrivesPageGetTotalSize,
.getPathForChildFolder = DrivesPageGetPathForChildFolder,
.getPathForChild = DrivesPageGetPathForChild,
.getDefaultViewSettings = DrivesPageGetDefaultViewSettings,
.enumerate = DrivesPageEnumerate,
},
@ -205,11 +211,13 @@ NamespaceHandler namespaceHandlers[] = {
{
.type = NAMESPACE_HANDLER_FILE_SYSTEM,
.rootContainerHandlerType = NAMESPACE_HANDLER_DRIVES_PAGE,
.canCopy = true,
.canPaste = true,
.handlesPath = FSDirHandlesPath,
.getFileType = NamespaceDefaultGetFileType,
.getVisibleName = NamespaceDefaultGetVisibleName,
.getTotalSize = FSDirGetTotalSize,
.getPathForChildFolder = FSDirGetPathForChildFolder,
.getPathForChild = FSDirGetPathForChild,
.createChildFolder = FSDirCreateChildFolder,
.renameItem = FSDirRenameItem,
.enumerate = FSDirEnumerate,

View File

@ -182,12 +182,14 @@ struct NamespaceHandler {
uint8_t type;
uint8_t rootContainerHandlerType;
bool canCopy, canPaste;
bool (*handlesPath)(String path);
uint32_t (*getFileType)(String path);
void (*getVisibleName)(EsBuffer *buffer, String path);
void (*getTotalSize)(Folder *folder); // Possibly called on the blocking task thread.
String (*getPathForChildFolder)(Folder *folder, String item);
String (*getPathForChild)(Folder *folder, FolderEntry *entry);
void (*getDefaultViewSettings)(Folder *folder, FolderViewSettings *settings);
// Called on the blocking task thread:
@ -207,6 +209,7 @@ struct Folder {
bool recurse;
bool refreshing;
bool driveRemoved;
bool readOnly;
bool doneInitialEnumeration;
EsMutex modifyEntriesMutex;

View File

@ -199,3 +199,10 @@ String PathRemoveTrailingSlash(String path) {
return path;
}
String PathGetName(String path) {
intptr_t i = path.bytes - 2;
while (i >= 0 && path.text[i] != '/') i--;
path.text += i + 1, path.bytes -= i + 1;
return path;
}

View File

@ -94,12 +94,12 @@ bool InstanceLoadFolder(Instance *instance, String path /* takes ownership */, i
// Update commands.
EsCommandSetDisabled(&instance->commandGoBackwards, !instance->pathBackwardHistory.Length());
EsCommandSetDisabled(&instance->commandGoForwards, !instance->pathForwardHistory.Length());
EsCommandSetDisabled(&instance->commandGoParent, PathCountSections(folder->path) == 1);
EsCommandSetDisabled(&instance->commandNewFolder, !folder->itemHandler->createChildFolder);
EsCommandSetDisabled(&instance->commandRename, true);
EsCommandSetDisabled(&instance->commandRefresh, false);
EsCommandSetEnabled(&instance->commandGoBackwards, instance->pathBackwardHistory.Length());
EsCommandSetEnabled(&instance->commandGoForwards, instance->pathForwardHistory.Length());
EsCommandSetEnabled(&instance->commandGoParent, PathCountSections(folder->path) > 1);
EsCommandSetEnabled(&instance->commandNewFolder, folder->itemHandler->createChildFolder && !folder->readOnly);
EsCommandSetEnabled(&instance->commandRename, false);
EsCommandSetEnabled(&instance->commandRefresh, true);
// Load the view settings for the folder.
@ -168,14 +168,15 @@ void InstanceRefreshViewType(Instance *instance) {
}
void InstanceUpdateItemSelectionCountCommands(Instance *instance) {
EsCommandSetEnabled(&instance->commandRename, instance->selectedItemCount == 1 && instance->folder->itemHandler->renameItem);
EsCommandSetEnabled(&instance->commandRename, instance->selectedItemCount == 1 && instance->folder->itemHandler->renameItem && !instance->folder->readOnly);
#define COMMAND_SET(id, callback, enabled) \
do { EsCommand *command = EsCommandByID(instance, id); \
EsCommandSetEnabled(command, enabled); \
EsCommandSetCallback(command, callback); } while(0)
COMMAND_SET(ES_COMMAND_COPY, CommandCopy, instance->selectedItemCount >= 1);
COMMAND_SET(ES_COMMAND_COPY, CommandCopy, instance->selectedItemCount >= 1 && instance->folder->itemHandler->canCopy);
COMMAND_SET(ES_COMMAND_PASTE, CommandPaste, instance->folder->itemHandler->canPaste && EsClipboardHasData(ES_CLIPBOARD_PRIMARY) && !instance->folder->readOnly);
}
int InstanceCompareFolderEntries(FolderEntry *left, FolderEntry *right, uint16_t sortColumn) {
@ -758,7 +759,7 @@ int ListCallback(EsElement *element, EsMessage *message) {
FolderEntry *entry = listEntry->entry;
if (entry->isFolder) {
String path = instance->folder->itemHandler->getPathForChildFolder(instance->folder, entry->GetInternalName());
String path = instance->folder->itemHandler->getPathForChild(instance->folder, entry);
InstanceLoadFolder(instance, path);
} else {
FileType *fileType = FolderEntryGetType(instance->folder, entry);

View File

@ -168,6 +168,10 @@ void InitialiseInstance(EsInstance *instance) {
EsSyscall(ES_SYSCALL_WAIT, 0x00000FFFFFFFFFFF, 1, 0, 0);
});
EsButtonOnCommand(EsButtonCreate(panel, ES_FLAGS_DEFAULT, 0, "Crash 6"), [] (EsInstance *, EsElement *, EsCommand *) {
EsMemoryCopy(nullptr, nullptr, 1);
});
EsButtonOnCommand(EsButtonCreate(panel, ES_FLAGS_DEFAULT, 0, "Announcement 1"), [] (EsInstance *, EsElement *element, EsCommand *) {
EsRectangle bounds = EsElementGetWindowBounds(element);
EsAnnouncementShow(element->window, ES_FLAGS_DEFAULT, (bounds.l + bounds.r) / 2, (bounds.t + bounds.b) / 2, "Hello, world!", -1);

View File

@ -1692,6 +1692,8 @@ struct EsListView : EsElement {
}
EsCommandSetCallback(EsCommandByID(instance, ES_COMMAND_SELECT_ALL), nullptr);
} else if (message->type == ES_MSG_MOUSE_RIGHT_DOWN) {
// Make sure that right clicking will focus the list.
} else if (message->type == ES_MSG_MOUSE_LEFT_DOWN) {
Select(-1, 0, EsKeyboardIsShiftHeld(), EsKeyboardIsCtrlHeld(), false);
} else if (message->type == ES_MSG_MOUSE_LEFT_DRAG) {

View File

@ -725,6 +725,8 @@ define ES_DRAW_BITMAP_OPAQUE (0xFFFF)
define ES_DRAW_BITMAP_XOR (0xFFFE)
define ES_DRAW_BITMAP_BLEND (0)
define ES_VOLUME_READ_ONLY (1 << 0)
include desktop/icons.header
enum EsFatalError {
@ -1497,6 +1499,7 @@ struct EsVolumeInformation {
char label[64];
uint8_t labelBytes;
uint8_t driveType;
uint32_t flags;
EsObjectID id;
EsFileOffset spaceTotal;
EsFileOffset spaceUsed;
@ -1925,6 +1928,7 @@ function EsError EsFileWriteAllFromHandle(EsHandle handle, const void *data, siz
function EsError EsFileWriteAllGather(STRING filePath, const void **data, size_t *fileSize, size_t gatherCount);
function EsError EsFileWriteAllGatherFromHandle(EsHandle handle, const void **data, size_t *fileSize, size_t gatherCount);
function void *EsFileMap(STRING filePath, size_t *fileSize, uint32_t flags);
function EsError EsFileCopy(STRING source, STRING destination, void **copyBuffer); // If you are copying lots of files, you can reuse the temporary copy buffer by storing the output copyBuffer; call EsHeapFree on it after the last copy.
function EsError EsFileControl(EsHandle file, uint32_t flags);
function EsFileInformation EsFileOpen(STRING path, uint32_t flags);
@ -2223,6 +2227,7 @@ function int EsCRTvsnprintf(char *buffer, size_t bufferSize, const char *format,
function EsError EsClipboardAddText(EsClipboard clipboard, STRING text = BLANK_STRING);
function bool EsClipboardHasFormat(EsClipboard clipboard, EsClipboardFormat format);
function bool EsClipboardHasData(EsClipboard clipboard);
function char *EsClipboardReadText(EsClipboard clipboard, size_t *bytes); // Free with EsHeapFree.
function EsFileStore *EsClipboardOpen(EsClipboard clipboard); // Open the clipboard for writing.
function EsError EsClipboardCloseAndAdd(EsClipboard clipboard, EsClipboardFormat format, EsFileStore *fileStore);

View File

@ -223,6 +223,41 @@ void *EsFileReadAll(const char *filePath, ptrdiff_t filePathLength, size_t *file
return buffer;
}
EsError EsFileCopy(const char *source, ptrdiff_t sourceBytes, const char *destination, ptrdiff_t destinationBytes, void **_copyBuffer) {
const size_t copyBufferBytes = 262144;
void *copyBuffer = _copyBuffer && *_copyBuffer ? *_copyBuffer : EsHeapAllocate(copyBufferBytes, false);
if (_copyBuffer) *_copyBuffer = copyBuffer;
if (!copyBuffer) {
return ES_ERROR_INSUFFICIENT_RESOURCES;
}
EsError error = ES_SUCCESS;
EsFileInformation sourceFile = EsFileOpen(source, sourceBytes, ES_FILE_READ | ES_NODE_FILE | ES_NODE_FAIL_IF_NOT_FOUND);
EsFileInformation destinationFile = EsFileOpen(destination, destinationBytes, ES_FILE_WRITE_EXCLUSIVE | ES_NODE_FILE | ES_NODE_FAIL_IF_FOUND);
if (sourceFile.error == ES_SUCCESS && destinationFile.error == ES_SUCCESS) {
error = EsFileResize(destinationFile.handle, sourceFile.size);
if (error == ES_SUCCESS) {
for (uintptr_t i = 0; i < sourceFile.size; i += copyBufferBytes) {
size_t bytesRead = EsFileReadSync(sourceFile.handle, i, copyBufferBytes, copyBuffer);
if (ES_CHECK_ERROR(bytesRead)) { error = bytesRead; break; }
size_t bytesWritten = EsFileWriteSync(destinationFile.handle, i, bytesRead, copyBuffer);
if (ES_CHECK_ERROR(bytesWritten)) { error = bytesWritten; break; }
}
}
} else {
error = sourceFile.error == ES_SUCCESS ? destinationFile.error : sourceFile.error;
}
if (sourceFile.error == ES_SUCCESS) EsHandleClose(sourceFile.handle);
if (destinationFile.error == ES_SUCCESS) EsHandleClose(destinationFile.handle);
if (!_copyBuffer) EsHeapFree(copyBuffer);
return error;
}
EsHandle EsMemoryOpen(size_t size, const char *name, ptrdiff_t nameLength, unsigned flags) {
if (nameLength == -1) nameLength = EsCStringLength(name);
return EsSyscall(ES_SYSCALL_MEMORY_OPEN, size, (uintptr_t) name, nameLength, flags);
@ -759,7 +794,22 @@ bool EsClipboardHasFormat(EsClipboard clipboard, EsClipboardFormat format) {
ClipboardInformation information;
ClipboardGetInformation(&file, &information);
if (file) EsHandleClose(file);
return information.error == ES_SUCCESS && information.format == format;
if (information.error != ES_SUCCESS) return false;
if (format == ES_CLIPBOARD_FORMAT_TEXT) {
return information.format == ES_CLIPBOARD_FORMAT_TEXT || information.format == ES_CLIPBOARD_FORMAT_PATH_LIST;
} else {
return information.format == format;
}
}
bool EsClipboardHasData(EsClipboard clipboard) {
(void) clipboard;
EsHandle file;
ClipboardInformation information;
ClipboardGetInformation(&file, &information);
if (file) EsHandleClose(file);
return information.error == ES_SUCCESS && information.format != ES_CLIPBOARD_FORMAT_INVALID;
}
char *EsClipboardReadText(EsClipboard clipboard, size_t *bytes) {
@ -773,7 +823,7 @@ char *EsClipboardReadText(EsClipboard clipboard, size_t *bytes) {
ClipboardGetInformation(&file, &information);
if (file) {
if (information.format == ES_CLIPBOARD_FORMAT_TEXT) {
if (information.format == ES_CLIPBOARD_FORMAT_TEXT || information.format == ES_CLIPBOARD_FORMAT_PATH_LIST) {
result = (char *) EsFileReadAllFromHandle(file, bytes);
}

View File

@ -214,8 +214,6 @@ bool AHCIController::Access(uintptr_t portIndex, uint64_t offsetBytes, size_t co
AHCIPort *port = ports + portIndex;
#if 0
// TODO Temporary.
if (operation == K_ACCESS_WRITE) {
KernelPanic("AHCIController::Access - Attempted write.\n");
}

View File

@ -293,7 +293,6 @@ bool NVMeController::IssueAdminCommand(const void *command, uint32_t *result) {
bool NVMeController::Access(struct NVMeDrive *drive, uint64_t offsetBytes, size_t countBytes, int operation,
KDMABuffer *buffer, uint64_t, KWorkGroup *dispatchGroup) {
// TODO Temporary.
// if (operation == K_ACCESS_WRITE) KernelPanic("NVMeController::Access - Attempted write.\n");
// Build the PRPs.

View File

@ -794,6 +794,7 @@ SYSCALL_IMPLEMENT(ES_SYSCALL_VOLUME_GET_INFORMATION) {
information.spaceUsed = fileSystem->spaceUsed;
information.spaceTotal = fileSystem->spaceTotal;
information.id = fileSystem->objectID;
information.flags = fileSystem->write ? ES_FLAGS_DEFAULT : ES_VOLUME_READ_ONLY;
SYSCALL_WRITE(argument1, &information, sizeof(EsVolumeInformation));
SYSCALL_RETURN(ES_SUCCESS, false);

View File

@ -939,27 +939,22 @@ extern "C" void InterruptHandler(InterruptContext *context) {
currentThread->process->cExecutableName,
context->rip, local->processorID, context->rsp, context->errorCode, context->cr2);
#ifndef __OPTIMIZE__
EsPrint("Attempting to make a stack trace...\n");
{
// TODO Temporary, may crash kernel.
// Attempt to make a stack trace.
uint64_t *rbp = (uint64_t *) context->rbp;
int i = 0;
uint64_t rbp = context->rbp;
int traceDepth = 0;
while (rbp && i < 32) {
EsPrint("\t%d: %x\n", ++i, rbp[1]);
if (!rbp[1]) break;
rbp = (uint64_t *) (rbp[0]);
while (rbp && traceDepth < 32) {
uint64_t value;
if (!MMArchSafeCopy((uintptr_t) &value, rbp + 8, sizeof(uint64_t))) break;
EsPrint("\t%d: %x\n", ++traceDepth, value);
if (!value) break;
if (!MMArchSafeCopy((uintptr_t) &rbp, rbp, sizeof(uint64_t))) break;
}
}
EsPrint("Stack trace complete.\n");
#else
EsPrint("Disable optimisations to get a stack trace.\n");
#endif
EsCrashReason crashReason;
EsMemoryZero(&crashReason, sizeof(EsCrashReason));

View File

@ -63,6 +63,7 @@ DEFINE_INTERFACE_STRING(CommonAnnouncementCopied, "Copied");
DEFINE_INTERFACE_STRING(CommonAnnouncementTextCopied, "Text copied");
DEFINE_INTERFACE_STRING(CommonAnnouncementCopyErrorResources, "There's not enough space to copy this");
DEFINE_INTERFACE_STRING(CommonAnnouncementCopyErrorOther, "Could not copy");
DEFINE_INTERFACE_STRING(CommonAnnouncementPasteErrorOther, "Could not paste");
DEFINE_INTERFACE_STRING(CommonEmpty, "empty");

View File

@ -428,3 +428,5 @@ EsListViewFixedItemFindIndex=426
EsListViewFixedItemSelect=427
EsListViewFixedItemGetSelected=428
EsClipboardHasFormat=429
EsClipboardHasData=430
EsFileCopy=431