diff --git a/apps/file_manager/commands.cpp b/apps/file_manager/commands.cpp index 73a1e80..99844a6 100644 --- a/apps/file_manager/commands.cpp +++ b/apps/file_manager/commands.cpp @@ -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), ©Buffer); + + 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; diff --git a/apps/file_manager/folder.cpp b/apps/file_manager/folder.cpp index 2f129f1..6700457 100644 --- a/apps/file_manager/folder.cpp +++ b/apps/file_manager/folder.cpp @@ -5,9 +5,7 @@ Array 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 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, diff --git a/apps/file_manager/main.cpp b/apps/file_manager/main.cpp index 1120184..6eb8c93 100644 --- a/apps/file_manager/main.cpp +++ b/apps/file_manager/main.cpp @@ -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; diff --git a/apps/file_manager/string.cpp b/apps/file_manager/string.cpp index d321641..c4db1b6 100644 --- a/apps/file_manager/string.cpp +++ b/apps/file_manager/string.cpp @@ -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; +} diff --git a/apps/file_manager/ui.cpp b/apps/file_manager/ui.cpp index 2496d07..87c0510 100644 --- a/apps/file_manager/ui.cpp +++ b/apps/file_manager/ui.cpp @@ -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); diff --git a/apps/test.cpp b/apps/test.cpp index b55fe60..2205491 100644 --- a/apps/test.cpp +++ b/apps/test.cpp @@ -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); diff --git a/desktop/list_view.cpp b/desktop/list_view.cpp index eff4162..0495cf0 100644 --- a/desktop/list_view.cpp +++ b/desktop/list_view.cpp @@ -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) { diff --git a/desktop/os.header b/desktop/os.header index faaba35..961dd59 100644 --- a/desktop/os.header +++ b/desktop/os.header @@ -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); diff --git a/desktop/syscall.cpp b/desktop/syscall.cpp index e0b6232..1137dde 100644 --- a/desktop/syscall.cpp +++ b/desktop/syscall.cpp @@ -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); } diff --git a/drivers/ahci.cpp b/drivers/ahci.cpp index b841256..7dfc914 100644 --- a/drivers/ahci.cpp +++ b/drivers/ahci.cpp @@ -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"); } diff --git a/drivers/nvme.cpp b/drivers/nvme.cpp index 980f2f3..fe067df 100644 --- a/drivers/nvme.cpp +++ b/drivers/nvme.cpp @@ -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. diff --git a/kernel/syscall.cpp b/kernel/syscall.cpp index 5a6f761..1d5533e 100644 --- a/kernel/syscall.cpp +++ b/kernel/syscall.cpp @@ -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); diff --git a/kernel/x86_64.cpp b/kernel/x86_64.cpp index 3ec5012..354c6b3 100644 --- a/kernel/x86_64.cpp +++ b/kernel/x86_64.cpp @@ -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)); diff --git a/shared/strings.cpp b/shared/strings.cpp index 2b11c1c..f1607b8 100644 --- a/shared/strings.cpp +++ b/shared/strings.cpp @@ -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"); diff --git a/util/api_table.ini b/util/api_table.ini index 19e6f27..261a706 100644 --- a/util/api_table.ini +++ b/util/api_table.ini @@ -428,3 +428,5 @@ EsListViewFixedItemFindIndex=426 EsListViewFixedItemSelect=427 EsListViewFixedItemGetSelected=428 EsClipboardHasFormat=429 +EsClipboardHasData=430 +EsFileCopy=431