From d3653d2958839c73561704f0ef3065221ab9a0e0 Mon Sep 17 00:00:00 2001 From: nakst <> Date: Sun, 6 Feb 2022 10:51:15 +0000 Subject: [PATCH] start script console --- apps/script_console.cpp | 621 ++++++++++++++++++++++++++++++++++++++++ apps/script_console.ini | 6 + 2 files changed, 627 insertions(+) create mode 100644 apps/script_console.cpp create mode 100644 apps/script_console.ini diff --git a/apps/script_console.cpp b/apps/script_console.cpp new file mode 100644 index 0000000..0927f6b --- /dev/null +++ b/apps/script_console.cpp @@ -0,0 +1,621 @@ +#define ES_INSTANCE_TYPE Instance +#include + +struct Instance : EsInstance { + EsThreadInformation scriptThread; + char *inputText; + size_t inputBytes; + + EsPanel *root; + EsPanel *inputRow; + EsPanel *outputPanel; + EsSpacer *outputDecoration; + EsTextbox *inputTextbox; + + char *outputLineBuffer; + size_t outputLineBufferBytes; + size_t outputLineBufferAllocated; +}; + +void AddPrompt(Instance *instance); +void AddOutput(Instance *instance, const char *text, size_t textBytes); + +// --------------------------------- Script engine interface. + +#define Assert EsAssert +#include + +void **fixedAllocationBlocks; +uint8_t *fixedAllocationCurrentBlock; +uintptr_t fixedAllocationCurrentPosition; +size_t fixedAllocationCurrentSize; + +EsDirectoryChild *directoryIterationBuffer; +uintptr_t directoryIterationBufferPosition; +size_t directoryIterationBufferCount; + +Instance *scriptInstance; + +void *AllocateFixed(size_t bytes) { + if (!bytes) { + return nullptr; + } + + bytes = (bytes + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1); + + if (bytes >= fixedAllocationCurrentSize || fixedAllocationCurrentPosition >= fixedAllocationCurrentSize - bytes) { + fixedAllocationCurrentSize = bytes > 1048576 ? bytes : 1048576; + fixedAllocationCurrentPosition = 0; + fixedAllocationCurrentBlock = (uint8_t *) EsCRTcalloc(1, fixedAllocationCurrentSize + sizeof(void *)); + + if (!fixedAllocationCurrentBlock) { + // TODO. + EsAssert(false); + } + + *(void **) fixedAllocationCurrentBlock = fixedAllocationBlocks; + fixedAllocationBlocks = (void **) fixedAllocationCurrentBlock; + fixedAllocationCurrentBlock += sizeof(void *); + } + + void *p = fixedAllocationCurrentBlock + fixedAllocationCurrentPosition; + fixedAllocationCurrentPosition += bytes; + return p; +} + +void *AllocateResize(void *old, size_t bytes) { + return EsCRTrealloc(old, bytes); +} + +int MemoryCompare(const void *a, const void *b, size_t bytes) { + return EsCRTmemcmp(a, b, bytes); +} + +void MemoryCopy(void *a, const void *b, size_t bytes) { + EsMemoryCopy(a, b, bytes); +} + +size_t PrintIntegerToBuffer(char *buffer, size_t bufferBytes, int64_t i) { + EsCRTsnprintf(buffer, bufferBytes, "%ld", i); + return EsCRTstrlen(buffer); +} + +size_t PrintFloatToBuffer(char *buffer, size_t bufferBytes, double f) { + EsCRTsnprintf(buffer, bufferBytes, "%f", f); + return EsCRTstrlen(buffer); +} + +void PrintDebug(const char *format, ...) { + va_list arguments; + va_start(arguments, format); + char buffer[256]; + EsCRTvsnprintf(buffer, sizeof(buffer), format, arguments); + AddOutput(scriptInstance, buffer, EsCStringLength(buffer)); + va_end(arguments); +} + +void PrintError(Tokenizer *tokenizer, const char *format, ...) { + PrintDebug("Error on line %d of '%s':\n", (int) tokenizer->line, tokenizer->module->path); + va_list arguments; + va_start(arguments, format); + char buffer[256]; + EsCRTvsnprintf(buffer, sizeof(buffer), format, arguments); + AddOutput(scriptInstance, buffer, EsCStringLength(buffer)); + va_end(arguments); + PrintLine(tokenizer->module, tokenizer->line); +} + +void PrintError2(Tokenizer *tokenizer, Node *node, const char *format, ...) { + PrintDebug("Error on line %d of '%s':\n", (int) node->token.line, tokenizer->module->path); + va_list arguments; + va_start(arguments, format); + char buffer[256]; + EsCRTvsnprintf(buffer, sizeof(buffer), format, arguments); + AddOutput(scriptInstance, buffer, EsCStringLength(buffer)); + va_end(arguments); + PrintLine(tokenizer->module, node->token.line); +} + +void PrintError3(const char *format, ...) { + PrintDebug("General error:\n"); + va_list arguments; + va_start(arguments, format); + char buffer[256]; + EsCRTvsnprintf(buffer, sizeof(buffer), format, arguments); + AddOutput(scriptInstance, buffer, EsCStringLength(buffer)); + va_end(arguments); +} + +void PrintError4(ExecutionContext *context, uint32_t instructionPointer, const char *format, ...) { + LineNumber lineNumber = { 0 }; + LineNumberLookup(context, instructionPointer, &lineNumber); + PrintDebug("Runtime error on line %d of '%s':\n", (int) lineNumber.lineNumber, + lineNumber.importData ? lineNumber.importData->path : "??"); + va_list arguments; + va_start(arguments, format); + char buffer[256]; + EsCRTvsnprintf(buffer, sizeof(buffer), format, arguments); + AddOutput(scriptInstance, buffer, EsCStringLength(buffer)); + PrintLine(lineNumber.importData, lineNumber.lineNumber); + PrintDebug("Back trace:\n"); + PrintBackTrace(context, instructionPointer, context->c, ""); +} + +void *FileLoad(const char *path, size_t *length) { + return EsFileReadAll(path, -1, length); +} + +CoroutineState *ExternalCoroutineWaitAny(ExecutionContext *context) { + (void) context; + EsAssert(false); // TODO. + return nullptr; +} + +int ExternalPrintStdErr(ExecutionContext *context, Value *returnValue) { + (void) returnValue; + STACK_POP_STRING(entryText, entryBytes); + AddOutput(scriptInstance, entryText, entryBytes); + return 1; +} + +int ExternalPrintStdErrWarning(ExecutionContext *context, Value *returnValue) { + return ExternalPrintStdErr(context, returnValue); +} + +int ExternalPrintStdErrHighlight(ExecutionContext *context, Value *returnValue) { + return ExternalPrintStdErr(context, returnValue); +} + +int ExternalSystemGetHostName(ExecutionContext *context, Value *returnValue) { + (void) context; + RETURN_STRING_COPY("Essence", 7); + return 3; +} + +int ExternalSystemRunningAsAdministrator(ExecutionContext *context, Value *returnValue) { + (void) context; + returnValue->i = 0; + return 2; +} + +int ExternalSystemGetProcessorCount(ExecutionContext *context, Value *returnValue) { + (void) context; + returnValue->i = EsSystemGetOptimalWorkQueueThreadCount(); + return 2; +} + +int ExternalRandomInt(ExecutionContext *context, Value *returnValue) { + if (context->c->stackPointer < 2) return -1; + int64_t min = context->c->stack[context->c->stackPointer - 1].i; + int64_t max = context->c->stack[context->c->stackPointer - 2].i; + if (max < min) { PrintError4(context, 0, "RandomInt() called with maximum limit (%ld) less than the minimum limit (%ld).\n", max, min); return 0; } + returnValue->i = EsRandomU64() % (max - min + 1) + min; + context->c->stackPointer -= 2; + return 2; +} + +int ExternalPathCreateDirectory(ExecutionContext *context, Value *returnValue) { + (void) returnValue; + STACK_POP_STRING(entryText, entryBytes); + EsError error = EsPathCreate(entryText, entryBytes, ES_NODE_DIRECTORY, false); + returnValue->i = error == ES_SUCCESS || error == ES_ERROR_FILE_ALREADY_EXISTS; + return 2; +} + +int ExternalPathExists(ExecutionContext *context, Value *returnValue) { + (void) returnValue; + STACK_POP_STRING(entryText, entryBytes); + returnValue->i = EsPathExists(entryText, entryBytes) ? 1 : 0; + return 2; +} + +int ExternalPathIsFile(ExecutionContext *context, Value *returnValue) { + (void) returnValue; + STACK_POP_STRING(entryText, entryBytes); + EsNodeType type; + returnValue->i = EsPathExists(entryText, entryBytes, &type) ? 1 : 0; + if (type != ES_NODE_FILE) returnValue->i = 0; + return 2; +} + +int ExternalPathIsDirectory(ExecutionContext *context, Value *returnValue) { + (void) returnValue; + STACK_POP_STRING(entryText, entryBytes); + EsNodeType type; + returnValue->i = EsPathExists(entryText, entryBytes, &type) ? 1 : 0; + if (type != ES_NODE_DIRECTORY) returnValue->i = 0; + return 2; +} + +int ExternalPathIsLink(ExecutionContext *context, Value *returnValue) { + (void) returnValue; + STACK_POP_STRING(entryText, entryBytes); + returnValue->i = 0; + return 2; +} + +int ExternalPathMove(ExecutionContext *context, Value *returnValue) { + (void) returnValue; + STACK_POP_STRING_2(entryText, entryBytes, entry2Text, entry2Bytes); + returnValue->i = EsPathMove(entryText, entryBytes, entry2Text, entry2Bytes) == ES_SUCCESS; + return 2; +} + +int ExternalPathDelete(ExecutionContext *context, Value *returnValue) { + (void) returnValue; + STACK_POP_STRING(entryText, entryBytes); + returnValue->i = EsPathDelete(entryText, entryBytes) == ES_SUCCESS; + return 2; +} + +int ExternalFileReadAll(ExecutionContext *context, Value *returnValue) { + STACK_POP_STRING(entryText, entryBytes); + returnValue->i = 0; + size_t length = 0; + void *data = EsFileReadAll(entryText, entryBytes, &length); // Free with EsHeapFree. + if (!data) return 3; + RETURN_STRING_NO_COPY((char *) data, length); + return 3; +} + +int ExternalFileWriteAll(ExecutionContext *context, Value *returnValue) { + STACK_POP_STRING_2(entryText, entryBytes, entry2Text, entry2Bytes); + returnValue->i = EsFileWriteAll(entryText, entryBytes, entry2Text, entry2Bytes) == ES_SUCCESS; + return 2; +} + +int ExternalFileGetSize(ExecutionContext *context, Value *returnValue) { + STACK_POP_STRING(entryText, entryBytes); + EsDirectoryChild information; + bool exists = EsPathQueryInformation(entryText, entryBytes, &information); + returnValue->i = exists && information.type == ES_NODE_FILE ? information.fileSize : -1; + return 2; +} + +int ExternalFileCopy(ExecutionContext *context, Value *returnValue) { + STACK_POP_STRING_2(entryText, entryBytes, entry2Text, entry2Bytes); + returnValue->i = EsFileCopy(entryText, entryBytes, entry2Text, entry2Bytes) == ES_SUCCESS; + return 2; +} + +int External_DirectoryInternalStartIteration(ExecutionContext *context, Value *returnValue) { + STACK_POP_STRING(entryText, entryBytes); + EsHeapFree(directoryIterationBuffer); + directoryIterationBuffer = nullptr; + ptrdiff_t count = EsDirectoryEnumerateChildren(entryText, entryBytes, &directoryIterationBuffer); + returnValue->i = count >= 0 ? 1 : 0; + directoryIterationBufferPosition = 0; + directoryIterationBufferCount = count >= 0 ? count : 0; + return 2; +} + +int External_DirectoryInternalEndIteration(ExecutionContext *context, Value *returnValue) { + (void) context; + (void) returnValue; + EsHeapFree(directoryIterationBuffer); + directoryIterationBuffer = nullptr; + directoryIterationBufferPosition = 0; + directoryIterationBufferCount = 0; + return 1; +} + +int External_DirectoryInternalNextIteration(ExecutionContext *context, Value *returnValue) { + (void) context; + + if (directoryIterationBufferPosition == directoryIterationBufferCount) { + returnValue->i = 0; + } else { + RETURN_STRING_COPY(directoryIterationBuffer[directoryIterationBufferPosition].name, + directoryIterationBuffer[directoryIterationBufferPosition].nameBytes); + directoryIterationBufferPosition++; + } + + return 3; +} + +#define EXTERNAL_STUB(name) int name(ExecutionContext *, Value *) { EsPrint("Unimplemented " #name "\n"); EsAssert(false); return -1; } + +// TODO What to do with these? +EXTERNAL_STUB(ExternalPathGetDefaultPrefix); +EXTERNAL_STUB(ExternalPathSetDefaultPrefixToScriptSourceDirectory); + +// TODO These functions should be moved to Helpers. +EXTERNAL_STUB(ExternalPersistRead); +EXTERNAL_STUB(ExternalPersistWrite); + +// TODO Functions only available in the POSIX subsystem: +EXTERNAL_STUB(ExternalConsoleGetLine); +EXTERNAL_STUB(ExternalSystemShellExecute); +EXTERNAL_STUB(ExternalSystemShellExecuteWithWorkingDirectory); +EXTERNAL_STUB(ExternalSystemShellEvaluate); +EXTERNAL_STUB(ExternalSystemShellEnableLogging); +EXTERNAL_STUB(ExternalSystemGetEnvironmentVariable); +EXTERNAL_STUB(ExternalSystemSetEnvironmentVariable); + +// --------------------------------- User interface. + +#define COLOR_BACKGROUND (0xFFFDFDFD) +#define COLOR_TEXT_MAIN (0xFF010102) +#define COLOR_TEXT_LIGHT (0xFF606062) +#define TEXT_SIZE_DEFAULT (14) +#define TEXT_SIZE_OUTPUT (12) + +const EsStyle styleBackground = { + .appearance = { + .enabled = true, + .backgroundColor = COLOR_BACKGROUND, + }, +}; + +const EsStyle styleRoot = { + .metrics = { + .mask = ES_THEME_METRICS_INSETS | ES_THEME_METRICS_GAP_ALL, + .insets = ES_RECT_4(32, 32, 32, 32), + .gapMajor = 4, + }, +}; + +const EsStyle styleInputRow = { + .metrics = { + .mask = ES_THEME_METRICS_GAP_ALL, + .gapMajor = 8, + }, +}; + +const EsStyle stylePathDefaultPrefixDisplay = { + .metrics = { + .mask = ES_THEME_METRICS_FONT_FAMILY | ES_THEME_METRICS_FONT_WEIGHT + | ES_THEME_METRICS_TEXT_SIZE | ES_THEME_METRICS_TEXT_ALIGN | ES_THEME_METRICS_TEXT_COLOR, + .textColor = COLOR_TEXT_MAIN, + .textAlign = ES_TEXT_H_LEFT | ES_TEXT_WRAP | ES_TEXT_V_TOP, + .textSize = TEXT_SIZE_DEFAULT, + .fontFamily = ES_FONT_SANS, + .fontWeight = 6, + }, +}; + +const EsStyle styleOutputParagraph = { + .metrics = { + .mask = ES_THEME_METRICS_FONT_FAMILY | ES_THEME_METRICS_FONT_WEIGHT + | ES_THEME_METRICS_TEXT_SIZE | ES_THEME_METRICS_TEXT_ALIGN | ES_THEME_METRICS_TEXT_COLOR, + .textColor = COLOR_TEXT_MAIN, + .textAlign = ES_TEXT_H_LEFT | ES_TEXT_WRAP | ES_TEXT_V_TOP, + .textSize = TEXT_SIZE_OUTPUT, + .fontFamily = ES_FONT_SANS, + .fontWeight = 4, + }, +}; + +const EsStyle stylePromptText = { + .metrics = { + .mask = ES_THEME_METRICS_FONT_FAMILY | ES_THEME_METRICS_FONT_WEIGHT + | ES_THEME_METRICS_TEXT_SIZE | ES_THEME_METRICS_TEXT_ALIGN | ES_THEME_METRICS_TEXT_COLOR, + .textColor = COLOR_TEXT_LIGHT, + .textAlign = ES_TEXT_H_LEFT | ES_TEXT_V_CENTER, + .textSize = TEXT_SIZE_DEFAULT, + .fontFamily = ES_FONT_SANS, + .fontWeight = 5, + }, +}; + +const EsStyle styleInputTextbox = { + .inherit = ES_STYLE_TEXTBOX_TRANSPARENT, + + .metrics = { + .mask = ES_THEME_METRICS_FONT_FAMILY | ES_THEME_METRICS_FONT_WEIGHT + | ES_THEME_METRICS_TEXT_SIZE | ES_THEME_METRICS_TEXT_COLOR, + .textColor = COLOR_TEXT_MAIN, + .textSize = TEXT_SIZE_DEFAULT, + .fontFamily = ES_FONT_SANS, + .fontWeight = 4, + }, +}; + +const EsStyle styleCommandLogText = { + .metrics = { + .mask = ES_THEME_METRICS_FONT_FAMILY | ES_THEME_METRICS_FONT_WEIGHT + | ES_THEME_METRICS_TEXT_SIZE | ES_THEME_METRICS_TEXT_ALIGN | ES_THEME_METRICS_TEXT_COLOR, + .textColor = COLOR_TEXT_MAIN, + .textAlign = ES_TEXT_H_LEFT | ES_TEXT_ELLIPSIS | ES_TEXT_V_TOP, + .textSize = TEXT_SIZE_DEFAULT, + .fontFamily = ES_FONT_SANS, + .fontWeight = 4, + }, +}; + +const EsStyle styleInterCommandSpacer = { + .metrics = { + .mask = ES_THEME_METRICS_PREFERRED_HEIGHT, + .preferredHeight = 14, + }, +}; + +const EsStyle styleOutputPanelWrapper = { + .metrics = { + .mask = ES_THEME_METRICS_INSETS | ES_THEME_METRICS_GAP_MAJOR, + .insets = ES_RECT_1(10), + .gapMajor = 12, + }, +}; + +const EsStyle styleOutputPanel = { + .metrics = { + .mask = ES_THEME_METRICS_INSETS | ES_THEME_METRICS_GAP_MAJOR, + .insets = ES_RECT_2(0, 4), + .gapMajor = 0, + }, +}; + +const EsStyle styleOutputDecorationInProgress = { + .metrics = { + .mask = ES_THEME_METRICS_PREFERRED_WIDTH, + .preferredWidth = 6, + }, + + .appearance = { + .enabled = true, + .backgroundColor = 0xFFFF7F00, + }, +}; + +const EsStyle styleOutputDecorationSuccess = { + .metrics = { + .mask = ES_THEME_METRICS_PREFERRED_WIDTH, + .preferredWidth = 6, + }, + + .appearance = { + .enabled = true, + .backgroundColor = 0xFF3070FF, + }, +}; + +const EsStyle styleOutputDecorationFailure = { + .metrics = { + .mask = ES_THEME_METRICS_PREFERRED_WIDTH, + .preferredWidth = 6, + }, + + .appearance = { + .enabled = true, + .backgroundColor = 0xFFFF3040, + }, +}; + +void ScriptThread(EsGeneric _instance) { + Instance *instance = (Instance *) _instance.p; + scriptInstance = instance; + int result = ScriptExecuteFromFile(EsLiteral("in"), instance->inputText, instance->inputBytes); + instance->inputText = nullptr; + + if (instance->outputLineBufferBytes) { + AddOutput(instance, EsLiteral("\n")); + } + + EsMessageMutexAcquire(); + + if (result == 0) { + EsSpacerChangeStyle(instance->outputDecoration, &styleOutputDecorationSuccess); + } else { + EsSpacerChangeStyle(instance->outputDecoration, &styleOutputDecorationFailure); + } + + EsSpacerCreate(instance->root, ES_CELL_H_FILL, &styleInterCommandSpacer); + AddPrompt(instance); + + EsMessageMutexRelease(); +} + +void EnterCommand(Instance *instance) { + EsAssert(instance->inputTextbox); + EsAssert(instance->inputRow); + size_t dataBytes; + char *data = EsTextboxGetContents(instance->inputTextbox, &dataBytes, ES_FLAGS_DEFAULT); + EsElementDestroy(instance->inputRow); + instance->inputTextbox = nullptr; + instance->inputRow = nullptr; + + EsPanel *commandLogRow = EsPanelCreate(instance->root, ES_CELL_H_FILL | ES_PANEL_STACK | ES_PANEL_HORIZONTAL, &styleInputRow); + EsTextDisplayCreate(commandLogRow, ES_FLAGS_DEFAULT, &stylePromptText, "\u2661"); + EsTextDisplayCreate(commandLogRow, ES_CELL_H_FILL, &styleCommandLogText, data, dataBytes); + + EsAssert(!instance->outputPanel); + EsAssert(!instance->outputDecoration); + EsPanel *outputPanelWrapper = EsPanelCreate(instance->root, ES_CELL_H_FILL | ES_PANEL_STACK | ES_PANEL_HORIZONTAL, &styleOutputPanelWrapper); + instance->outputDecoration = EsSpacerCreate(outputPanelWrapper, ES_CELL_V_FILL, &styleOutputDecorationInProgress); + instance->outputPanel = EsPanelCreate(outputPanelWrapper, ES_CELL_H_FILL | ES_PANEL_STACK | ES_PANEL_VERTICAL, &styleOutputPanel); + + const char *inputPrefix = "void Start() {\n"; + const char *inputSuffix = "\n}"; + char *script = (char *) EsHeapAllocate(dataBytes + EsCStringLength(inputPrefix) + EsCStringLength(inputSuffix), false); + EsMemoryCopy(script, inputPrefix, EsCStringLength(inputPrefix)); + EsMemoryCopy(script + EsCStringLength(inputPrefix), data, dataBytes); + EsMemoryCopy(script + EsCStringLength(inputPrefix) + dataBytes, inputSuffix, EsCStringLength(inputSuffix)); + EsHeapFree(data); + + instance->inputText = script; + instance->inputBytes = dataBytes + EsCStringLength(inputPrefix) + EsCStringLength(inputSuffix); + EsAssert(ES_SUCCESS == EsThreadCreate(ScriptThread, &instance->scriptThread, instance)); + EsHandleClose(instance->scriptThread.handle); +} + +int InputTextboxMessage(EsElement *element, EsMessage *message) { + if (message->type == ES_MSG_KEY_TYPED) { + if (message->keyboard.scancode == ES_SCANCODE_ENTER) { + EnterCommand(element->instance); + return ES_HANDLED; + } + } + + return 0; +} + +void AddOutput(Instance *instance, const char *text, size_t textBytes) { + for (uintptr_t i = 0; i < textBytes; i++) { + if (text[i] == '\n') { + EsMessageMutexAcquire(); + EsTextDisplayCreate(instance->outputPanel, ES_CELL_H_FILL, &styleOutputParagraph, + instance->outputLineBuffer, instance->outputLineBufferBytes); + EsMessageMutexRelease(); + instance->outputLineBufferBytes = 0; + } else { + if (instance->outputLineBufferBytes == instance->outputLineBufferAllocated) { + instance->outputLineBufferAllocated = (instance->outputLineBufferAllocated + 4) * 2; + instance->outputLineBuffer = (char *) EsHeapReallocate(instance->outputLineBuffer, + instance->outputLineBufferAllocated, false); + } + + instance->outputLineBuffer[instance->outputLineBufferBytes++] = text[i]; + } + } +} + +void AddPrompt(Instance *instance) { + EsAssert(!instance->inputTextbox); + EsAssert(!instance->inputRow); + instance->outputPanel = nullptr; + instance->outputDecoration = nullptr; + + EsTextDisplayCreate(instance->root, ES_CELL_H_FILL, &stylePathDefaultPrefixDisplay, "Essence HD (0:)"); + instance->inputRow = EsPanelCreate(instance->root, ES_CELL_H_FILL | ES_PANEL_STACK | ES_PANEL_HORIZONTAL, &styleInputRow); + EsTextDisplayCreate(instance->inputRow, ES_FLAGS_DEFAULT, &stylePromptText, "\u2665"); + instance->inputTextbox = EsTextboxCreate(instance->inputRow, ES_CELL_H_FILL, &styleInputTextbox); + EsTextboxEnableSmartReplacement(instance->inputTextbox, false); + instance->inputTextbox->messageUser = InputTextboxMessage; + EsElementFocus(instance->inputTextbox, ES_ELEMENT_FOCUS_ENSURE_VISIBLE); +} + +int InstanceCallback(Instance *instance, EsMessage *message) { + if (message->type == ES_MSG_INSTANCE_DESTROY) { + // TODO Stopping the script thread. + EsHeapFree(instance->outputLineBuffer); + } + + return 0; +} + +void _start() { + _init(); + + // TODO Proper drive mounting. + + EsDeviceEnumerate([] (EsMessageDevice device, EsGeneric) { + if (device.type == ES_DEVICE_FILE_SYSTEM && EsDeviceControl(device.handle, ES_DEVICE_CONTROL_FS_IS_BOOT, 0, nullptr)) { + EsMountPointAdd(EsLiteral("0:"), device.handle); + } + }, 0); + + while (true) { + EsMessage *message = EsMessageReceive(); + + if (message->type == ES_MSG_INSTANCE_CREATE) { + Instance *instance = EsInstanceCreate(message, "Script Console", -1); + instance->callback = InstanceCallback; + EsWindowSetIcon(instance->window, ES_ICON_UTILITIES_TERMINAL); + EsPanel *wrapper = EsPanelCreate(instance->window, ES_CELL_FILL, ES_STYLE_PANEL_WINDOW_DIVIDER); + EsPanel *background = EsPanelCreate(wrapper, ES_CELL_FILL | ES_PANEL_V_SCROLL_AUTO, &styleBackground); + instance->root = EsPanelCreate(background, ES_CELL_H_FILL | ES_PANEL_STACK | ES_PANEL_VERTICAL, &styleRoot); + AddPrompt(instance); + } + } +} diff --git a/apps/script_console.ini b/apps/script_console.ini new file mode 100644 index 0000000..d3b8407 --- /dev/null +++ b/apps/script_console.ini @@ -0,0 +1,6 @@ +[general] +name=Script Console +permission_all_files=1 + +[build] +source=apps/script_console.cpp