From b8731dc27e4e0739326628641c3b3b68dccde7e8 Mon Sep 17 00:00:00 2001 From: nakst <> Date: Wed, 25 Aug 2021 09:54:14 +0100 Subject: [PATCH] open document reference counting --- desktop/api.cpp | 3 +- desktop/desktop.cpp | 135 ++++++++++++++++++++++++++++++++++++------- desktop/os.header | 8 ++- desktop/settings.cpp | 11 +++- desktop/syscall.cpp | 2 +- 5 files changed, 129 insertions(+), 30 deletions(-) diff --git a/desktop/api.cpp b/desktop/api.cpp index eb9e0e5..554f97f 100644 --- a/desktop/api.cpp +++ b/desktop/api.cpp @@ -987,8 +987,7 @@ void EsInstanceSaveComplete(EsMessage *message, bool success) { } if (success) { - message->instanceSave.file->error = EsFileControl(message->instanceSave.file->handle, - ES_FILE_CONTROL_NOTIFY_MONITORS | ES_FILE_CONTROL_FLUSH); + message->instanceSave.file->error = EsFileControl(message->instanceSave.file->handle, ES_FILE_CONTROL_FLUSH); if (message->instanceSave.file->error != ES_SUCCESS) { success = false; diff --git a/desktop/desktop.cpp b/desktop/desktop.cpp index 35083dd..3a870b5 100644 --- a/desktop/desktop.cpp +++ b/desktop/desktop.cpp @@ -5,8 +5,6 @@ // - New tab page - search; recent files. // - Right click menu. // - Duplicate tabs. -// - If a process exits, its tabs won't close because Desktop has a handle. -// - Also clear any OpenDocument currentWriter fields it owns. // TODO Graphical issues: // - New tab button isn't flush with right border when tab band full. @@ -31,6 +29,7 @@ // TODO Only let File Manager read the file_type sections of the system configuration. // TODO Restarting Desktop if it crashes. // TODO Make sure applications can't delete |Fonts: and |Themes:. +// TODO Handle open document deletion. #define MSG_SETUP_DESKTOP_UI ((EsMessageType) (ES_MSG_USER_START + 1)) @@ -105,6 +104,7 @@ struct OpenDocument { EsHandle readHandle; EsObjectID id; EsObjectID currentWriter; + uintptr_t referenceCount; }; struct InstalledApplication { @@ -122,10 +122,14 @@ struct InstalledApplication { bool notified; // Temporary flag. }; -struct CrashedTabInstance : EsInstance { +struct CommonDesktopInstance : EsInstance { + void (*destroy)(EsInstance *); }; -struct BlankTabInstance : EsInstance { +struct CrashedTabInstance : CommonDesktopInstance { +}; + +struct BlankTabInstance : CommonDesktopInstance { }; struct ApplicationInstance { @@ -184,6 +188,8 @@ struct { EsObjectID nextClipboardProcessID; EsHandle clipboardFile; ClipboardInformation clipboardInformation; + + bool configurationModified; } desktop; int TaskBarButtonMessage(EsElement *element, EsMessage *message); @@ -192,6 +198,9 @@ bool ApplicationInstanceStart(int64_t applicationID, EsApplicationStartupInforma void ApplicationInstanceClose(ApplicationInstance *instance); ApplicationInstance *ApplicationInstanceFindByWindowID(EsObjectID windowID, bool remove = false); void EmbeddedWindowDestroyed(EsObjectID id); +void ConfigurationWriteToFile(); +void OpenDocumentOpenReference(EsObjectID id); +void OpenDocumentCloseReference(EsObjectID id); #include "settings.cpp" @@ -908,6 +917,11 @@ bool ApplicationInstanceStart(int64_t applicationID, EsApplicationStartupInforma instance->processHandle = ES_INVALID_HANDLE; } + if (instance->documentID) { + OpenDocumentCloseReference(instance->documentID); + instance->documentID = 0; + } + instance->application = application; if (application->useSingleProcess && application->singleProcessHandle) { @@ -1017,6 +1031,11 @@ bool ApplicationInstanceStart(int64_t applicationID, EsApplicationStartupInforma instance->processID = EsProcessGetID(process); instance->processHandle = EsSyscall(ES_SYSCALL_PROCESS_SHARE, process, ES_CURRENT_PROCESS, 0, 0); + if (startupInformation->documentID) { + instance->documentID = startupInformation->documentID; + OpenDocumentOpenReference(instance->documentID); + } + EsMessage m = { ES_MSG_INSTANCE_CREATE }; if (~startupInformation->flags & ES_APPLICATION_STARTUP_MANUAL_PATH) { @@ -1077,9 +1096,16 @@ ApplicationInstance *ApplicationInstanceCreate(int64_t id, EsApplicationStartupI instance->titleBytes = 1; instance->tab = tab; desktop.allApplicationInstances.Add(instance); - ApplicationInstanceStart(id, startupInformation, instance); - if (!hidden) WindowTabActivate(tab); - return instance; + + if (ApplicationInstanceStart(id, startupInformation, instance)) { + if (!hidden) WindowTabActivate(tab); + return instance; + } else { + // TODO Destroy the tab/container window. + // Or, we probably didn't want to create them in the first place. + EsHeapFree(instance); + return nullptr; + } } void ApplicationTemporaryDestroy(InstalledApplication *application) { @@ -1171,15 +1197,39 @@ void ApplicationProcessTerminated(EsObjectID pid) { break; } } + + for (uintptr_t i = 0; i < desktop.openDocuments.Count(); i++) { + OpenDocument *document = &desktop.openDocuments[i]; + + if (document->currentWriter == pid) { + document->currentWriter = 0; + } + } } ////////////////////////////////////////////////////// // Document management: ////////////////////////////////////////////////////// +void OpenDocumentCloseReference(EsObjectID id) { + OpenDocument *document = desktop.openDocuments.Get(&id); + EsAssert(document->referenceCount && document->referenceCount < 0x10000000 /* sanity check */); + document->referenceCount--; + if (document->referenceCount) return; + EsHeapFree(document->path); + EsHeapFree(document->temporarySavePath); + EsHandleClose(document->readHandle); + desktop.openDocuments.Delete(&id); +} + +void OpenDocumentOpenReference(EsObjectID id) { + OpenDocument *document = desktop.openDocuments.Get(&id); + EsAssert(document->referenceCount && document->referenceCount < 0x10000000 /* sanity check */); + document->referenceCount++; +} + void OpenDocumentWithApplication(EsApplicationStartupInformation *startupInformation) { bool foundDocument = false; - EsObjectID documentID; for (uintptr_t i = 0; i < desktop.openDocuments.Count(); i++) { OpenDocument *document = &desktop.openDocuments[i]; @@ -1188,7 +1238,8 @@ void OpenDocumentWithApplication(EsApplicationStartupInformation *startupInforma && 0 == EsMemoryCompare(document->path, startupInformation->filePath, document->pathBytes)) { foundDocument = true; startupInformation->readHandle = document->readHandle; - documentID = document->id; + startupInformation->documentID = document->id; + document->referenceCount++; break; } } @@ -1207,18 +1258,16 @@ void OpenDocumentWithApplication(EsApplicationStartupInformation *startupInforma document.pathBytes = startupInformation->filePathBytes; document.readHandle = file.handle; document.id = ++desktop.currentDocumentID; - documentID = document.id; + document.referenceCount = 1; EsMemoryCopy(document.path, startupInformation->filePath, startupInformation->filePathBytes); *desktop.openDocuments.Put(&document.id) = document; startupInformation->readHandle = document.readHandle; + startupInformation->documentID = document.id; } - ApplicationInstance *instance = ApplicationInstanceCreate(startupInformation->id, startupInformation, nullptr); - - if (instance) { - instance->documentID = documentID; - } + ApplicationInstanceCreate(startupInformation->id, startupInformation, nullptr); + OpenDocumentCloseReference(startupInformation->documentID); } EsError TemporaryFileCreate(EsHandle *handle, char **path, size_t *pathBytes) { @@ -1319,6 +1368,9 @@ void ApplicationInstanceRequestSave(ApplicationInstance *instance, const char *n if (document->currentWriter) { m.tabOperation.error = ES_ERROR_FILE_CANNOT_GET_EXCLUSIVE_USE; } else { + EsHeapFree(document->temporarySavePath); + document->temporarySavePath = nullptr; + EsHandle fileHandle; m.tabOperation.error = TemporaryFileCreate(&fileHandle, &document->temporarySavePath, &document->temporarySavePathBytes); @@ -1526,7 +1578,7 @@ void ConfigurationLoadApplications() { }, 0); } -void ConfigurationWriteSectionsToBuffer(const char *sectionClass, const char *section, EsBuffer *pipe) { +void ConfigurationWriteSectionsToBuffer(const char *sectionClass, const char *section, bool includeComments, EsBuffer *pipe) { char buffer[4096]; EsMutexAcquire(&api.systemConfigurationMutex); @@ -1547,7 +1599,7 @@ void ConfigurationWriteSectionsToBuffer(const char *sectionClass, const char *se for (uintptr_t i = 0; i < group->itemCount; i++) { EsSystemConfigurationItem *item = group->items + i; - if (!item->keyBytes || item->key[0] == ';') { + if ((!item->keyBytes || item->key[0] == ';') && !includeComments) { continue; } @@ -1561,6 +1613,37 @@ void ConfigurationWriteSectionsToBuffer(const char *sectionClass, const char *se EsMutexRelease(&api.systemConfigurationMutex); } +void ConfigurationWriteToFile() { + if (!desktop.configurationModified) { + return; + } + + EsBuffer buffer = { .canGrow = true }; + ConfigurationWriteSectionsToBuffer(nullptr, nullptr, true /* include comments */, &buffer); + + if (!buffer.error) { + if (ES_SUCCESS == EsFileWriteAll(EsLiteral(K_SYSTEM_CONFIGURATION "_"), buffer.out, buffer.position)) { + // TODO Atomic delete and move. + if (ES_SUCCESS == EsPathDelete(EsLiteral(K_SYSTEM_CONFIGURATION))) { + if (ES_SUCCESS == EsPathMove(EsLiteral(K_SYSTEM_CONFIGURATION "_"), EsLiteral(K_SYSTEM_CONFIGURATION))) { + EsPrint("ConfigurationWriteToFile - New configuration successfully written.\n"); + desktop.configurationModified = true; + } else { + EsPrint("ConfigurationWriteToFile - Error while moving to final path.\n"); + } + } else { + EsPrint("ConfigurationWriteToFile - Error while deleting old file.\n"); + } + } else { + EsPrint("ConfigurationWriteToFile - Error while writing to file.\n"); + } + } else { + EsPrint("ConfigurationWriteToFile - Error while writing to buffer.\n"); + } + + EsHeapFree(buffer.out); +} + ////////////////////////////////////////////////////// // Image utilities: ////////////////////////////////////////////////////// @@ -1885,8 +1968,8 @@ void DesktopMessage2(EsMessage *message, uint8_t *buffer, EsBuffer *pipe) { EsHandleClose(processHandle); } } else if (buffer[0] == DESKTOP_MSG_SYSTEM_CONFIGURATION_GET && pipe) { - ConfigurationWriteSectionsToBuffer("font", nullptr, pipe); - ConfigurationWriteSectionsToBuffer(nullptr, "ui", pipe); + ConfigurationWriteSectionsToBuffer("font", nullptr, false, pipe); + ConfigurationWriteSectionsToBuffer(nullptr, "ui", false, pipe); } else if (buffer[0] == DESKTOP_MSG_REQUEST_SHUTDOWN) { InstalledApplication *application = ApplicationFindByPID(message->desktop.processID); @@ -1897,7 +1980,7 @@ void DesktopMessage2(EsMessage *message, uint8_t *buffer, EsBuffer *pipe) { InstalledApplication *application = ApplicationFindByPID(message->desktop.processID); if (application && (application->permissions & APPLICATION_PERMISSION_VIEW_FILE_TYPES)) { - ConfigurationWriteSectionsToBuffer("file_type", nullptr, pipe); + ConfigurationWriteSectionsToBuffer("file_type", nullptr, false, pipe); } } else if (!instance) { // ------------------------------------------------- @@ -1965,8 +2048,6 @@ void DesktopMessage2(EsMessage *message, uint8_t *buffer, EsBuffer *pipe) { } void EmbeddedWindowDestroyed(EsObjectID id) { - // TODO Close open documents. - EsMenuCloseAll(); ApplicationInstance *instance = ApplicationInstanceFindByWindowID(id, true /* remove if found */); if (!instance) return; @@ -1974,6 +2055,10 @@ void EmbeddedWindowDestroyed(EsObjectID id) { EsHandleClose(instance->embeddedWindowHandle); if (instance->processHandle) EsHandleClose(instance->processHandle); + if (instance->documentID) { + OpenDocumentCloseReference(instance->documentID); + } + InstalledApplication *application = instance->application; if (application && application->singleInstance) { @@ -2126,6 +2211,12 @@ void DesktopMessage(EsMessage *message) { EsSyscall(ES_SYSCALL_WINDOW_MOVE, window->handle, (uintptr_t) &bounds, 0, ES_WINDOW_MOVE_ALWAYS_ON_TOP); wrapper->StartAnimating(); } + } else if (message->type == ES_MSG_INSTANCE_DESTROY) { + CommonDesktopInstance *instance = (CommonDesktopInstance *) message->instanceDestroy.instance; + + if (instance->destroy) { + instance->destroy(instance); + } } else if (message->type == MSG_SETUP_DESKTOP_UI) { DesktopSetup(); } diff --git a/desktop/os.header b/desktop/os.header index 4a466ee..56ac323 100644 --- a/desktop/os.header +++ b/desktop/os.header @@ -654,8 +654,7 @@ define ES_ECHO_REQUEST_MAX_LENGTH (48) define ES_CONNECTION_OPEN_WAIT (1 << 0) -define ES_FILE_CONTROL_NOTIFY_MONITORS (1 << 0) -define ES_FILE_CONTROL_FLUSH (1 << 1) +define ES_FILE_CONTROL_FLUSH (1 << 0) define ES_ELEMENT_UPDATE_CONTENT_WIDTH (1 << 0) define ES_ELEMENT_UPDATE_CONTENT_HEIGHT (1 << 1) @@ -1392,7 +1391,10 @@ struct EsApplicationStartupInformation { EsWindow *targetWindow; uint32_t flags; int32_t data; - EsHandle readHandle; // Internal use. + + // Internal use only. + EsHandle readHandle; + EsObjectID documentID; }; struct EsINIState { diff --git a/desktop/settings.cpp b/desktop/settings.cpp index e3a3558..c373a20 100644 --- a/desktop/settings.cpp +++ b/desktop/settings.cpp @@ -1,7 +1,6 @@ -// TODO Save system configuration file on closing the instance or going back to all settings. // TODO Undo button overlapped slightly when scrollbar shown. -struct SettingsInstance : EsInstance { +struct SettingsInstance : CommonDesktopInstance { EsPanel *switcher; EsPanel *mainPage; EsButton *undoButton; @@ -131,6 +130,7 @@ void SettingsBackButton(EsInstance *_instance, EsElement *, EsCommand *) { instance->undoButton = nullptr; instance->controls.Free(); EsPanelSwitchTo(instance->switcher, instance->mainPage, ES_TRANSITION_ZOOM_OUT, ES_PANEL_SWITCHER_DESTROY_PREVIOUS_AFTER_TRANSITION, 1.0f); + ConfigurationWriteToFile(); } void SettingsNumberBoxSetValue(EsElement *element, int32_t newValue) { @@ -156,6 +156,7 @@ void SettingsNumberBoxSetValue(EsElement *element, int32_t newValue) { if (oldValue != newValue) { SettingsUpdateGlobalAndWindowManager(); EsElementSetDisabled(instance->undoButton, false); + desktop.configurationModified = true; } } @@ -221,6 +222,7 @@ void SettingsCheckboxCommand(EsInstance *_instance, EsElement *element, EsComman if (oldValue == newValue) return; SettingsUpdateGlobalAndWindowManager(); EsElementSetDisabled(instance->undoButton, false); + desktop.configurationModified = true; } void SettingsAddCheckbox(EsElement *table, const char *string, ptrdiff_t stringBytes, char accessKey, @@ -328,6 +330,7 @@ int SettingsSliderMessage(EsElement *element, EsMessage *message) { if (oldValue != newValue) { SettingsUpdateGlobalAndWindowManager(); EsElementSetDisabled(instance->undoButton, false); + desktop.configurationModified = true; } } @@ -536,4 +539,8 @@ void InstanceSettingsCreate(EsMessage *message) { EsButtonOnCommand(button, SettingsButtonPressed); } } + + instance->destroy = [] (EsInstance *) { + ConfigurationWriteToFile(); + }; } diff --git a/desktop/syscall.cpp b/desktop/syscall.cpp index cd5932b..3d901d1 100644 --- a/desktop/syscall.cpp +++ b/desktop/syscall.cpp @@ -171,7 +171,7 @@ EsError EsFileWriteAllGatherFromHandle(EsHandle handle, const void **data, size_ offset += sizes[i]; } - error = EsFileControl(handle, ES_FILE_CONTROL_NOTIFY_MONITORS | ES_FILE_CONTROL_FLUSH); + error = EsFileControl(handle, ES_FILE_CONTROL_FLUSH); return error; }