diff --git a/desktop/api.cpp b/desktop/api.cpp index 5ae2a96..2f5a6d6 100644 --- a/desktop/api.cpp +++ b/desktop/api.cpp @@ -61,6 +61,7 @@ struct EnumString { const char *cName; int value; }; #define DESKTOP_MSG_SYSTEM_CONFIGURATION_GET (13) #define DESKTOP_MSG_FILE_TYPES_GET (14) #define DESKTOP_MSG_UNHANDLED_KEY_EVENT (15) +#define DESKTOP_MSG_START_USER_TASK (16) struct EsFileStore { #define FILE_STORE_HANDLE (1) @@ -1550,6 +1551,7 @@ const void *EsEmbeddedFileGet(const char *_name, ptrdiff_t nameBytes, size_t *by struct UserTask { EsUserTaskCallbackFunction callback; EsGeneric data; + EsHandle taskHandle; }; void UserTaskThread(EsGeneric _task) { @@ -1558,23 +1560,48 @@ void UserTaskThread(EsGeneric _task) { EsMessageMutexAcquire(); api.openInstanceCount--; // TODO Send ES_MSG_APPLICATION_EXIT if needed. - // TODO Tell Desktop the task is complete. EsMessageMutexRelease(); + EsSyscall(ES_SYSCALL_WINDOW_CLOSE, task->taskHandle, 0, 0, 0); + EsHandleClose(task->taskHandle); EsHeapFree(task); } EsError EsUserTaskStart(EsUserTaskCallbackFunction callback, EsGeneric data) { EsMessageMutexCheck(); + UserTask *task = (UserTask *) EsHeapAllocate(sizeof(UserTask), true); - if (!task) return ES_ERROR_INSUFFICIENT_RESOURCES; + + if (!task) { + return ES_ERROR_INSUFFICIENT_RESOURCES; + } + + uint8_t m = DESKTOP_MSG_START_USER_TASK; + EsBuffer response = { .canGrow = true }; + MessageDesktop(&m, 1, ES_INVALID_HANDLE, &response); + EsHandle handle; + EsBufferReadInto(&response, &handle, sizeof(handle)); + EsHeapFree(response.out); + + if (!handle) { + EsHeapFree(task); + return ES_ERROR_INSUFFICIENT_RESOURCES; + } + task->callback = callback; task->data = data; - // TODO Tell Desktop about the task. (This'll also prevent it sending ES_MSG_APPLICATION_EXIT in single process mode.) - api.openInstanceCount++; + task->taskHandle = handle; EsThreadInformation information; EsError error = EsThreadCreate(UserTaskThread, &information, task); - if (error == ES_SUCCESS) EsHandleClose(information.handle); - else EsHeapFree(task); + + if (error == ES_SUCCESS) { + EsHandleClose(information.handle); + api.openInstanceCount++; + } else { + EsSyscall(ES_SYSCALL_WINDOW_CLOSE, task->taskHandle, 0, 0, 0); + EsHandleClose(task->taskHandle); + EsHeapFree(task); + } + return error; } diff --git a/desktop/desktop.cpp b/desktop/desktop.cpp index 245f55a..fe7732f 100644 --- a/desktop/desktop.cpp +++ b/desktop/desktop.cpp @@ -137,7 +137,7 @@ struct BlankTabInstance : CommonDesktopInstance { struct ApplicationInstance { // User interface. - WindowTab *tab; // nullptr for notRespondingInstance. + WindowTab *tab; // nullptr for notRespondingInstance and user tasks. EsObjectID embeddedWindowID; EsHandle embeddedWindowHandle; @@ -145,8 +145,9 @@ struct ApplicationInstance { InstalledApplication *application; EsObjectID documentID, processID; EsHandle processHandle; + bool isUserTask; - // Tab information. + // Metadata. char title[128]; size_t titleBytes; uint32_t iconID; @@ -182,6 +183,7 @@ struct { TaskBar taskBar; EsWindow *wallpaperWindow; + EsButton *tasksButton; bool shutdownWindowOpen; bool setupDesktopUIComplete; @@ -729,12 +731,12 @@ int TaskBarMessage(EsElement *element, EsMessage *message) { return 0; } -int TaskBarTasksButtonMessage(EsElement *element, EsMessage *message) { +int TaskBarTasksButtonMessage(EsElement *, EsMessage *message) { if (message->type == ES_MSG_GET_WIDTH) { message->measure.width = GetConstantNumber("taskBarTasksButtonWidth"); return ES_HANDLED; } else if (message->type == ES_MSG_PAINT_ICON) { - float progress = 0.33f; + float progress = 0.0f; // TODO. uint32_t color1 = GetConstantNumber("taskBarTasksButtonWheelColor1"); uint32_t color2 = GetConstantNumber("taskBarTasksButtonWheelColor2"); @@ -1200,8 +1202,10 @@ void ApplicationInstanceCrashed(EsMessage *message) { ApplicationInstance *instance = desktop.allApplicationInstances[i]; if (instance->processID == message->crash.pid) { - ApplicationInstanceStart(APPLICATION_ID_DESKTOP_CRASHED, nullptr, instance); - WindowTabActivate(instance->tab, true); + if (instance->tab) { + ApplicationInstanceStart(APPLICATION_ID_DESKTOP_CRASHED, nullptr, instance); + WindowTabActivate(instance->tab, true); + } } } @@ -1884,8 +1888,8 @@ void DesktopSetup() { desktop.taskBar.taskList.Initialise(panel, ES_CELL_FILL, ReorderListMessage, nullptr); desktop.taskBar.taskList.cName = "task list"; - EsButton *tasksButton = EsButtonCreate(panel, ES_ELEMENT_HIDDEN, ES_STYLE_TASK_BAR_BUTTON, "Copying files" ELLIPSIS, -1); - tasksButton->messageUser = TaskBarTasksButtonMessage; + desktop.tasksButton = EsButtonCreate(panel, ES_ELEMENT_HIDDEN, ES_STYLE_TASK_BAR_BUTTON, "Copying files" ELLIPSIS, -1); + desktop.tasksButton->messageUser = TaskBarTasksButtonMessage; EsButton *shutdownButton = EsButtonCreate(panel, ES_FLAGS_DEFAULT, ES_STYLE_TASK_BAR_EXTRA); EsButtonSetIcon(shutdownButton, ES_ICON_SYSTEM_SHUTDOWN_SYMBOLIC); @@ -1953,7 +1957,7 @@ void DesktopSetup() { desktop.setupDesktopUIComplete = true; } -void DesktopMessage2(EsMessage *message, uint8_t *buffer, EsBuffer *pipe) { +void DesktopSyscall(EsMessage *message, uint8_t *buffer, EsBuffer *pipe) { ApplicationInstance *instance = ApplicationInstanceFindByWindowID(message->desktop.windowID); if (buffer[0] == DESKTOP_MSG_START_APPLICATION) { @@ -2045,6 +2049,57 @@ void DesktopMessage2(EsMessage *message, uint8_t *buffer, EsBuffer *pipe) { if (application && (application->permissions & APPLICATION_PERMISSION_ALL_FILES)) { InstanceAnnouncePathMoved(application, buffer, message->desktop.bytes); } + } else if (buffer[0] == DESKTOP_MSG_START_USER_TASK && pipe) { + InstalledApplication *application = ApplicationFindByPID(message->desktop.processID); + + if (!application) { + return; + } + + // HACK User tasks use an embedded window object for IPC. + // This allows us to basically treat them like other instances. + + EsHandle processHandle = EsProcessOpen(message->desktop.processID); + EsHandle windowHandle = EsSyscall(ES_SYSCALL_WINDOW_CREATE, ES_WINDOW_NORMAL, 0, 0, 0); + ApplicationInstance *instance = (ApplicationInstance *) EsHeapAllocate(sizeof(ApplicationInstance), true); + bool added = false; + + if (processHandle && windowHandle && instance) { + added = desktop.allApplicationInstances.Add(instance); + } + + if (!processHandle || !windowHandle || !instance || !added) { + if (processHandle) EsHandleClose(processHandle); + if (windowHandle) EsHandleClose(windowHandle); + if (instance) EsHeapFree(instance); + + EsHandle invalid = ES_INVALID_HANDLE; + EsBufferWrite(pipe, &invalid, sizeof(invalid)); + return; + } + + instance->title[0] = ' '; + instance->titleBytes = 1; + instance->isUserTask = true; + instance->embeddedWindowHandle = windowHandle; + instance->embeddedWindowID = EsSyscall(ES_SYSCALL_WINDOW_GET_ID, windowHandle, 0, 0, 0); + instance->processHandle = processHandle; + instance->processID = message->desktop.processID; + instance->application = application; + + if (application->singleProcessHandle) { + EsAssert(application->openInstanceCount); + application->openInstanceCount++; + } + + EsHandle targetWindowHandle = EsSyscall(ES_SYSCALL_WINDOW_SET_PROPERTY, windowHandle, processHandle, 0, ES_WINDOW_PROPERTY_EMBED_OWNER); + EsBufferWrite(pipe, &targetWindowHandle, sizeof(targetWindowHandle)); + + if (EsElementIsHidden(desktop.tasksButton)) { + EsPanelStartMovementAnimation((EsPanel *) EsElementGetLayoutParent(desktop.tasksButton), 1.5f /* duration scale */); + EsElementStartTransition(desktop.tasksButton, ES_TRANSITION_FADE_IN, ES_ELEMENT_TRANSITION_ENTRANCE, 1.5f); + EsElementSetHidden(desktop.tasksButton, false); + } } else if (!instance) { // ------------------------------------------------- // | Messages below here require a valid instance. | @@ -2059,10 +2114,12 @@ void DesktopMessage2(EsMessage *message, uint8_t *buffer, EsBuffer *pipe) { } } - instance->tab->Repaint(true); + if (instance->tab) { + instance->tab->Repaint(true); - if (instance->tab == instance->tab->container->active) { - instance->tab->container->taskBarButton->Repaint(true); + if (instance->tab == instance->tab->container->active) { + instance->tab->container->taskBarButton->Repaint(true); + } } } else if (buffer[0] == DESKTOP_MSG_REQUEST_SAVE) { ApplicationInstanceRequestSave(instance, (const char *) buffer + 1, message->desktop.bytes - 1); @@ -2201,7 +2258,7 @@ void DesktopMessage(EsMessage *message) { if (buffer) { EsConstantBufferRead(message->desktop.buffer, buffer); EsBuffer pipe = { .canGrow = true }; - DesktopMessage2(message, buffer, &pipe); + DesktopSyscall(message, buffer, &pipe); if (message->desktop.pipe) EsPipeWrite(message->desktop.pipe, pipe.out, pipe.position); EsHeapFree(pipe.out); EsHeapFree(buffer); diff --git a/desktop/gui.cpp b/desktop/gui.cpp index 84f2929..abf3a52 100644 --- a/desktop/gui.cpp +++ b/desktop/gui.cpp @@ -321,6 +321,7 @@ struct ScrollPane { struct PanelMovementItem { EsElement *element; EsRectangle oldBounds; + bool wasHidden; }; struct EsPanel : EsElement { @@ -1903,15 +1904,19 @@ void PanelMoveChild(EsElement *element, int width, int height, int offsetX, int continue; } - int oldWidth = Width(item->oldBounds); - int oldHeight = Height(item->oldBounds); - int oldOffsetX = item->oldBounds.l; - int oldOffsetY = item->oldBounds.t; + if (item->wasHidden) { + break; + } else { + int oldWidth = Width(item->oldBounds); + int oldHeight = Height(item->oldBounds); + int oldOffsetX = item->oldBounds.l; + int oldOffsetY = item->oldBounds.t; - element->InternalMove(LinearInterpolate(oldWidth, width, progress), - LinearInterpolate(oldHeight, height, progress), - LinearInterpolate(oldOffsetX, offsetX, progress), - LinearInterpolate(oldOffsetY, offsetY, progress)); + element->InternalMove(LinearInterpolate(oldWidth, width, progress), + LinearInterpolate(oldHeight, height, progress), + LinearInterpolate(oldOffsetX, offsetX, progress), + LinearInterpolate(oldOffsetY, offsetY, progress)); + } return; } @@ -3471,6 +3476,7 @@ void EsPanelStartMovementAnimation(EsPanel *panel, float timeMultiplier) { item.element = element; item.oldBounds = ES_RECT_4(element->offsetX, element->offsetX + element->width, element->offsetY, element->offsetY + element->height); + item.wasHidden = element->flags & ES_ELEMENT_HIDDEN; panel->movementItems.Add(item); } @@ -5643,6 +5649,11 @@ void EsElementSetHidden(EsElement *element, bool hidden) { UIMaybeRemoveFocusedElement(element->window); } +bool EsElementIsHidden(EsElement *element) { + EsMessageMutexCheck(); + return element->flags & ES_ELEMENT_HIDDEN; +} + void EsElementSetDisabled(EsElement *element, bool disabled) { EsMessageMutexCheck(); diff --git a/desktop/os.header b/desktop/os.header index 6f6666e..0291102 100644 --- a/desktop/os.header +++ b/desktop/os.header @@ -2285,6 +2285,7 @@ function void EsElementFocus(EsElement *element, uint32_t flags = ES_FLAGS_DEFAU function bool EsElementIsFocused(EsElement *element); function void EsElementSetDisabled(EsElement *element, bool disabled = true); function void EsElementSetHidden(EsElement *element, bool hidden = true); +function bool EsElementIsHidden(EsElement *element); function void EsElementSetCallback(EsElement *element, EsUICallbackFunction callback); function void EsElementGetSize(EsElement *element, int *width, int *height); function void EsElementRepaint(EsElement *element, const EsRectangle *region = ES_NULL); // Mark an element to be repainted. If region is null, then the whole element is repainted. diff --git a/desktop/syscall.cpp b/desktop/syscall.cpp index 97194e7..1cd2e10 100644 --- a/desktop/syscall.cpp +++ b/desktop/syscall.cpp @@ -717,7 +717,7 @@ size_t EsGameControllerStatePoll(EsGameControllerState *buffer) { return EsSyscall(ES_SYSCALL_GAME_CONTROLLER_STATE_POLL, (uintptr_t) buffer, 0, 0, 0); } -void DesktopMessage2(EsMessage *message, uint8_t *buffer, EsBuffer *pipe); +void DesktopSyscall(EsMessage *message, uint8_t *buffer, EsBuffer *pipe); void MessageDesktop(void *message, size_t messageBytes, EsHandle embeddedWindow = ES_INVALID_HANDLE, EsBuffer *responseBuffer = nullptr) { if (api.startupInformation->isDesktop) { @@ -726,7 +726,7 @@ void MessageDesktop(void *message, size_t messageBytes, EsHandle embeddedWindow m.desktop.windowID = embeddedWindow ? EsSyscall(ES_SYSCALL_WINDOW_GET_ID, embeddedWindow, 0, 0, 0) : 0; m.desktop.processID = EsProcessGetID(ES_CURRENT_PROCESS); m.desktop.bytes = messageBytes; - DesktopMessage2(&m, (uint8_t *) message, responseBuffer); + DesktopSyscall(&m, (uint8_t *) message, responseBuffer); } else { EsHandle pipeRead = ES_INVALID_HANDLE, pipeWrite = ES_INVALID_HANDLE; diff --git a/util/api_table.ini b/util/api_table.ini index 993029d..bb2f8b8 100644 --- a/util/api_table.ini +++ b/util/api_table.ini @@ -433,3 +433,4 @@ EsFileCopy=431 EsListViewSelectNone=432 EsElementIsFocused=433 EsUserTaskStart=434 +EsElementIsHidden=435