diff --git a/apps/script_console.cpp b/apps/script_console.cpp index 602db2f..24af7d4 100644 --- a/apps/script_console.cpp +++ b/apps/script_console.cpp @@ -2,6 +2,11 @@ #include #include +// TODO: +// LogOpenGroup() +// LogOpenList() +// LogClose() + struct Instance : EsInstance { EsCommand commandClearOutput; @@ -27,6 +32,7 @@ struct Instance : EsInstance { void AddPrompt(Instance *instance); void AddOutput(Instance *instance, const char *text, size_t textBytes); +void AddLogOutput(Instance *instance, const char *text, size_t textBytes); // --------------------------------- Script engine interface. @@ -159,19 +165,34 @@ CoroutineState *ExternalCoroutineWaitAny(ExecutionContext *context) { return nullptr; } -int ExternalPrintStdErr(ExecutionContext *context, Value *returnValue) { +int ExternalLog(ExecutionContext *context, Value *returnValue) { (void) returnValue; STACK_POP_STRING(entryText, entryBytes); - AddOutput(scriptInstance, entryText, entryBytes); + AddLogOutput(scriptInstance, entryText, entryBytes); return 1; } -int ExternalPrintStdErrWarning(ExecutionContext *context, Value *returnValue) { - return ExternalPrintStdErr(context, returnValue); +int ExternalTextFormat(ExecutionContext *context, Value *returnValue, const char *mode) { + char *buffer = (char *) EsHeapAllocate(16, false); + uintptr_t index = HeapAllocate(context); + context->heap[index].type = T_STR; + context->heap[index].bytes = EsStringFormat(buffer, 16, "%z", mode); + context->heap[index].text = buffer; + returnValue->i = index; + return 3; } -int ExternalPrintStdErrHighlight(ExecutionContext *context, Value *returnValue) { - return ExternalPrintStdErr(context, returnValue); +int ExternalTextColorError (ExecutionContext *context, Value *returnValue) { return ExternalTextFormat(context, returnValue, "\a#DB002A]"); } +int ExternalTextColorHighlight(ExecutionContext *context, Value *returnValue) { return ExternalTextFormat(context, returnValue, "\a#362CB6]"); } +int ExternalTextMonospaced (ExecutionContext *context, Value *returnValue) { return ExternalTextFormat(context, returnValue, "\am]"); } +int ExternalTextPlain (ExecutionContext *context, Value *returnValue) { return ExternalTextFormat(context, returnValue, "\a]"); } + +int ExternalTextWeight(ExecutionContext *context, Value *returnValue) { + if (context->c->stackPointer < 1) return -1; + int i = context->c->stack[--context->c->stackPointer].i / 100; + if (i < 1) i = 1; else if (i > 9) i = 9; + char b[5] = { '\a', 'w', (char) (i + '0'), ']', 0 }; + return ExternalTextFormat(context, returnValue, b); } int ExternalSystemSleepMs(ExecutionContext *context, Value *returnValue) { @@ -693,12 +714,19 @@ void AddREPLResult(ExecutionContext *context, EsElement *parent, Node *type, Val uint64_t pngSignature = 0x0A1A0A0D474E5089; uint32_t jpgSignature = 0xE0FFD8FF; + bool isPNG = valueBytes > sizeof(pngSignature) && 0 == EsMemoryCompare(&pngSignature, valueText, sizeof(pngSignature)); + bool isJPG = valueBytes > sizeof(jpgSignature) && 0 == EsMemoryCompare(&jpgSignature, valueText, sizeof(jpgSignature)); - if ((valueBytes > sizeof(pngSignature) && 0 == EsMemoryCompare(&pngSignature, valueText, sizeof(pngSignature))) - || (valueBytes > sizeof(jpgSignature) && 0 == EsMemoryCompare(&jpgSignature, valueText, sizeof(jpgSignature)))) { + if (isPNG || isJPG) { EsCanvasPane *canvasPane = EsCanvasPaneCreate(parent, ES_CELL_H_FILL, EsStyleIntern(&styleImageViewerPane)); EsImageDisplay *display = EsImageDisplayCreate(canvasPane, ES_CELL_H_LEFT | ES_CELL_V_TOP); EsImageDisplayLoadFromMemory(display, valueText, valueBytes); + char *buffer = EsStringAllocateAndFormat(&bytes, "%d by %d pixels \u2027 %z", + EsImageDisplayGetImageWidth(display), + EsImageDisplayGetImageHeight(display), + isPNG ? "PNG" : isJPG ? "JPEG" : "unknown format"); + EsTextDisplayCreate(parent, ES_CELL_H_FILL, EsStyleIntern(&styleOutputParagraphItalic), buffer, bytes); + EsHeapFree(buffer); } else if (EsUTF8IsValid(valueText, valueBytes)) { char *buffer = EsStringAllocateAndFormat(&bytes, "\u201C%s\u201D", valueBytes, valueText); EsTextDisplayCreate(parent, ES_CELL_H_FILL, EsStyleIntern(&styleOutputData), buffer, bytes); @@ -858,7 +886,8 @@ void EnterCommand(Instance *instance) { EsPanel *commandLogRow = EsPanelCreate(instance->root, ES_CELL_H_FILL | ES_PANEL_STACK | ES_PANEL_HORIZONTAL, EsStyleIntern(&styleInputRow)); EsTextDisplayCreate(commandLogRow, ES_FLAGS_DEFAULT, EsStyleIntern(&stylePromptText), "\u2661"); - EsTextDisplayCreate(commandLogRow, ES_CELL_H_FILL, EsStyleIntern(&styleCommandLogText), data, dataBytes); + EsTextDisplay *inputCommand = EsTextDisplayCreate(commandLogRow, ES_CELL_H_FILL, EsStyleIntern(&styleCommandLogText), data, dataBytes); + EsTextDisplaySetupSyntaxHighlighting(inputCommand, ES_SYNTAX_HIGHLIGHTING_LANGUAGE_SCRIPT); instance->outputElements.Add(commandLogRow); EsAssert(!instance->outputPanel); @@ -891,7 +920,8 @@ void AddOutput(Instance *instance, const char *text, size_t textBytes) { if (text[i] == '\n') { if (EsUTF8IsValid(instance->outputLineBuffer, instance->outputLineBufferBytes)) { EsMessageMutexAcquire(); - EsTextDisplayCreate(instance->outputPanel, ES_CELL_H_FILL, EsStyleIntern(&styleOutputParagraph), + EsTextDisplayCreate(instance->outputPanel, ES_CELL_H_FILL | ES_TEXT_DISPLAY_RICH_TEXT, + EsStyleIntern(&styleOutputParagraph), instance->outputLineBuffer, instance->outputLineBufferBytes); EsMessageMutexRelease(); } else { @@ -900,6 +930,7 @@ void AddOutput(Instance *instance, const char *text, size_t textBytes) { EsLiteral("Encoding error.\n")); EsMessageMutexRelease(); } + instance->outputLineBufferBytes = 0; } else { if (instance->outputLineBufferBytes == instance->outputLineBufferAllocated) { @@ -913,6 +944,22 @@ void AddOutput(Instance *instance, const char *text, size_t textBytes) { } } +void AddLogOutput(Instance *instance, const char *text, size_t textBytes) { + instance->anyOutput = true; + + if (EsUTF8IsValid(text, textBytes)) { + EsMessageMutexAcquire(); + EsTextDisplayCreate(instance->outputPanel, ES_CELL_H_FILL | ES_TEXT_DISPLAY_RICH_TEXT, + EsStyleIntern(&styleOutputParagraph), text, textBytes); + EsMessageMutexRelease(); + } else { + EsMessageMutexAcquire(); + EsTextDisplayCreate(instance->outputPanel, ES_CELL_H_FILL, + EsStyleIntern(&styleOutputParagraphItalic), EsLiteral("Encoding error.\n")); + EsMessageMutexRelease(); + } +} + void AddPrompt(Instance *instance) { if (instance->outputPanel) { instance->outputElements.Add(EsElementGetLayoutParent(instance->outputPanel)); @@ -928,6 +975,7 @@ void AddPrompt(Instance *instance) { instance->inputRow = EsPanelCreate(instance->root, ES_CELL_H_FILL | ES_PANEL_STACK | ES_PANEL_HORIZONTAL, EsStyleIntern(&styleInputRow)); EsTextDisplayCreate(instance->inputRow, ES_FLAGS_DEFAULT, EsStyleIntern(&stylePromptText), "\u2665"); instance->inputTextbox = EsTextboxCreate(instance->inputRow, ES_CELL_H_FILL, EsStyleIntern(&styleInputTextbox)); + EsTextboxSetupSyntaxHighlighting(instance->inputTextbox, ES_SYNTAX_HIGHLIGHTING_LANGUAGE_SCRIPT); EsTextboxEnableSmartReplacement(instance->inputTextbox, false); instance->inputTextbox->messageUser = InputTextboxMessage; EsElementFocus(instance->inputTextbox, ES_ELEMENT_FOCUS_ENSURE_VISIBLE); diff --git a/desktop/gui.cpp b/desktop/gui.cpp index 7ed4270..57ea92f 100644 --- a/desktop/gui.cpp +++ b/desktop/gui.cpp @@ -5822,6 +5822,14 @@ int ProcessImageDisplayMessage(EsElement *element, EsMessage *message) { return 0; } +uint32_t EsImageDisplayGetImageWidth(EsImageDisplay *display) { + return display->width; +} + +uint32_t EsImageDisplayGetImageHeight(EsImageDisplay *display) { + return display->height; +} + EsImageDisplay *EsImageDisplayCreate(EsElement *parent, uint64_t flags, EsStyleID style) { EsImageDisplay *display = (EsImageDisplay *) EsHeapAllocate(sizeof(EsImageDisplay), true); if (!display) return nullptr; diff --git a/desktop/os.header b/desktop/os.header index e2d7ae2..8021190 100644 --- a/desktop/os.header +++ b/desktop/os.header @@ -694,8 +694,9 @@ define ES_ELEMENT_TRANSITION_HIDE_AFTER_COMPLETE (1 << 2) define ES_TEXT_GET_CHARACTER_AT_POINT_MIDDLE (1 << 0) -define ES_SYNTAX_HIGHLIGHTING_LANGUAGE_C (1) -define ES_SYNTAX_HIGHLIGHTING_LANGUAGE_INI (2) +define ES_SYNTAX_HIGHLIGHTING_LANGUAGE_C (1) +define ES_SYNTAX_HIGHLIGHTING_LANGUAGE_INI (2) +define ES_SYNTAX_HIGHLIGHTING_LANGUAGE_SCRIPT (3) define ES_DRAW_LINE_CAP_ROUND (1 << 0) define ES_DRAW_LINE_CAP_SQUARE (1 << 1) @@ -2569,6 +2570,8 @@ function EsImageDisplay *EsImageDisplayCreate(EsElement *parent, uint64_t flags function void EsImageDisplayLoadBits(EsImageDisplay *display, const uint32_t *bits, size_t width, size_t height, size_t stride) @matrix_in(bits, width, height); function void EsImageDisplayLoadFromMemory(EsImageDisplay *display, const void *buffer, size_t bufferBytes) @buffer_in(buffer, bufferBytes); function void EsImageDisplayPaint(EsImageDisplay *display, EsPainter *painter, EsRectangle bounds); +function uint32_t EsImageDisplayGetImageWidth(EsImageDisplay *display); +function uint32_t EsImageDisplayGetImageHeight(EsImageDisplay *display); function EsTextDisplay *EsTextDisplayCreate(EsElement *parent, uint64_t flags = ES_FLAGS_DEFAULT, EsStyleID style = 0, STRING label = BLANK_STRING); function void EsTextDisplaySetContents(EsTextDisplay *display, STRING contents = BLANK_STRING); diff --git a/desktop/text.cpp b/desktop/text.cpp index df6bc26..2bbed24 100644 --- a/desktop/text.cpp +++ b/desktop/text.cpp @@ -2548,6 +2548,9 @@ void EsDrawTextThemed(EsPainter *painter, EsElement *element, EsRectangle bounds void EsRichTextParse(const char *inString, ptrdiff_t inStringBytes, char **outString, EsTextRun **outTextRuns, size_t *outTextRunCount, EsTextStyle *baseStyle) { + // TODO Check that this can be used on untrusted input. + // Specifically, what if textRunCount/stringBytes are calculated incorrectly? + if (inStringBytes == -1) { inStringBytes = EsCStringLength(inString); } @@ -2597,15 +2600,17 @@ void EsRichTextParse(const char *inString, ptrdiff_t inStringBytes, if (c == ']') { break; } else if (c == 'w' /* weight */) { - i++; if (i >= inStringBytes || inString[i] == ']') goto parsedFormat; - textRun->style.font.weight = inString[i] - '0'; + i++; + if (i >= inStringBytes || inString[i] == ']') {} + else textRun->style.font.weight = inString[i] - '0'; } else if (c == 'i' /* italic */) { textRun->style.font.flags |= ES_FONT_ITALIC; } else if (c == 's' /* size */) { textRun->style.size = 0; while (true) { - i++; if (i >= inStringBytes || inString[i] == ']') goto parsedFormat; + i++; + if (i >= inStringBytes || inString[i] == ']') break; if (inString[i] < '0' || inString[i] > '9') { i--; break; } textRun->style.size *= 10; textRun->style.size += inString[i] - '0'; @@ -2627,12 +2632,11 @@ void EsRichTextParse(const char *inString, ptrdiff_t inStringBytes, string[bytes++] = inString[i++]; } + i--; textRun->style.color = EsColorParse(string, bytes); - goto parsedFormat; } } - parsedFormat:; textRunIndex++; } else { string[stringIndex++] = inString[i]; @@ -2650,41 +2654,84 @@ void EsRichTextParse(const char *inString, ptrdiff_t inStringBytes, // --------------------------------- Syntax highlighting. -const char *const keywords_a[] = { "auto", nullptr }; -const char *const keywords_b[] = { "bool", "break", nullptr }; -const char *const keywords_c[] = { "case", "char", "const", "continue", nullptr }; -const char *const keywords_d[] = { "default", "do", "double", nullptr }; -const char *const keywords_e[] = { "else", "enum", "extern", nullptr }; -const char *const keywords_f[] = { "float", "for", nullptr }; -const char *const keywords_g[] = { "goto", nullptr }; -const char *const keywords_h[] = { nullptr }; -const char *const keywords_i[] = { "if", "inline", "int", "int16_t", "int32_t", "int64_t", "int8_t", "intptr_t", nullptr }; -const char *const keywords_j[] = { nullptr }; -const char *const keywords_k[] = { nullptr }; -const char *const keywords_l[] = { "long", nullptr }; -const char *const keywords_m[] = { nullptr }; -const char *const keywords_n[] = { nullptr }; -const char *const keywords_o[] = { nullptr }; -const char *const keywords_p[] = { nullptr }; -const char *const keywords_q[] = { nullptr }; -const char *const keywords_r[] = { "register", "restrict", "return", nullptr }; -const char *const keywords_s[] = { "short", "signed", "sizeof", "static", "struct", "switch", nullptr }; -const char *const keywords_t[] = { "typedef", nullptr }; -const char *const keywords_u[] = { "uint16_t", "uint32_t", "uint64_t", "uint8_t", "uintptr_t", "union", "unsigned", nullptr }; -const char *const keywords_v[] = { "void", "volatile", nullptr }; -const char *const keywords_w[] = { "while", nullptr }; -const char *const keywords_x[] = { nullptr }; -const char *const keywords_y[] = { nullptr }; -const char *const keywords_z[] = { nullptr }; +const char *const keywordsC_a[] = { "auto", nullptr }; +const char *const keywordsC_b[] = { "bool", "break", nullptr }; +const char *const keywordsC_c[] = { "case", "char", "const", "continue", nullptr }; +const char *const keywordsC_d[] = { "default", "do", "double", nullptr }; +const char *const keywordsC_e[] = { "else", "enum", "extern", nullptr }; +const char *const keywordsC_f[] = { "float", "for", nullptr }; +const char *const keywordsC_g[] = { "goto", nullptr }; +const char *const keywordsC_h[] = { nullptr }; +const char *const keywordsC_i[] = { "if", "inline", "int", "int16_t", "int32_t", "int64_t", "int8_t", "intptr_t", nullptr }; +const char *const keywordsC_j[] = { nullptr }; +const char *const keywordsC_k[] = { nullptr }; +const char *const keywordsC_l[] = { "long", nullptr }; +const char *const keywordsC_m[] = { nullptr }; +const char *const keywordsC_n[] = { nullptr }; +const char *const keywordsC_o[] = { nullptr }; +const char *const keywordsC_p[] = { nullptr }; +const char *const keywordsC_q[] = { nullptr }; +const char *const keywordsC_r[] = { "register", "restrict", "return", nullptr }; +const char *const keywordsC_s[] = { "short", "signed", "sizeof", "static", "struct", "switch", nullptr }; +const char *const keywordsC_t[] = { "typedef", nullptr }; +const char *const keywordsC_u[] = { "uint16_t", "uint32_t", "uint64_t", "uint8_t", "uintptr_t", "union", "unsigned", nullptr }; +const char *const keywordsC_v[] = { "void", "volatile", nullptr }; +const char *const keywordsC_w[] = { "while", nullptr }; +const char *const keywordsC_x[] = { nullptr }; +const char *const keywordsC_y[] = { nullptr }; +const char *const keywordsC_z[] = { nullptr }; -const char *const *const keywords[] = { - keywords_a, keywords_b, keywords_c, keywords_d, - keywords_e, keywords_f, keywords_g, keywords_h, - keywords_i, keywords_j, keywords_k, keywords_l, - keywords_m, keywords_n, keywords_o, keywords_p, - keywords_q, keywords_r, keywords_s, keywords_t, - keywords_u, keywords_v, keywords_w, keywords_x, - keywords_y, keywords_z, +const char *const keywordsScript_a[] = { "assert", "await", nullptr }; +const char *const keywordsScript_b[] = { "bool", nullptr }; +const char *const keywordsScript_c[] = { nullptr }; +const char *const keywordsScript_d[] = { nullptr }; +const char *const keywordsScript_e[] = { "else", nullptr }; +const char *const keywordsScript_f[] = { "false", "float", "for", "functype", nullptr }; +const char *const keywordsScript_g[] = { nullptr }; +const char *const keywordsScript_h[] = { nullptr }; +const char *const keywordsScript_i[] = { "if", "int", nullptr }; +const char *const keywordsScript_j[] = { nullptr }; +const char *const keywordsScript_k[] = { nullptr }; +const char *const keywordsScript_l[] = { nullptr }; +const char *const keywordsScript_m[] = { nullptr }; +const char *const keywordsScript_n[] = { "new", "null", nullptr }; +const char *const keywordsScript_o[] = { nullptr }; +const char *const keywordsScript_p[] = { nullptr }; +const char *const keywordsScript_q[] = { nullptr }; +const char *const keywordsScript_r[] = { "return", nullptr }; +const char *const keywordsScript_s[] = { "str", "struct", nullptr }; +const char *const keywordsScript_t[] = { "true", "tuple", nullptr }; +const char *const keywordsScript_u[] = { nullptr }; +const char *const keywordsScript_v[] = { "void", nullptr }; +const char *const keywordsScript_w[] = { "while", nullptr }; +const char *const keywordsScript_x[] = { nullptr }; +const char *const keywordsScript_y[] = { nullptr }; +const char *const keywordsScript_z[] = { nullptr }; + +const char *const *const keywordsC[] = { + keywordsC_a, keywordsC_b, keywordsC_c, keywordsC_d, + keywordsC_e, keywordsC_f, keywordsC_g, keywordsC_h, + keywordsC_i, keywordsC_j, keywordsC_k, keywordsC_l, + keywordsC_m, keywordsC_n, keywordsC_o, keywordsC_p, + keywordsC_q, keywordsC_r, keywordsC_s, keywordsC_t, + keywordsC_u, keywordsC_v, keywordsC_w, keywordsC_x, + keywordsC_y, keywordsC_z, +}; + +const char *const *const keywordsScript[] = { + keywordsScript_a, keywordsScript_b, keywordsScript_c, keywordsScript_d, + keywordsScript_e, keywordsScript_f, keywordsScript_g, keywordsScript_h, + keywordsScript_i, keywordsScript_j, keywordsScript_k, keywordsScript_l, + keywordsScript_m, keywordsScript_n, keywordsScript_o, keywordsScript_p, + keywordsScript_q, keywordsScript_r, keywordsScript_s, keywordsScript_t, + keywordsScript_u, keywordsScript_v, keywordsScript_w, keywordsScript_x, + keywordsScript_y, keywordsScript_z, +}; + +const char *const *const *const keywords[] = { + keywordsC, + nullptr /* INI */, + keywordsScript, }; bool CharIsAlphaOrDigitOrUnderscore(char c) { @@ -2693,7 +2740,6 @@ bool CharIsAlphaOrDigitOrUnderscore(char c) { Array TextApplySyntaxHighlighting(const EsTextStyle *baseStyle, int language, uint32_t *colors, Array runs, const char *string, size_t bytes) { // TODO Make these colors customizable! - // TODO Highlight keywords. int lexState = 0; bool inComment = false, inIdentifier = false, inChar = false, startedString = false; @@ -2713,14 +2759,14 @@ Array TextApplySyntaxHighlighting(const EsTextStyle *baseStyle, int l last = 0; } - if (language == ES_SYNTAX_HIGHLIGHTING_LANGUAGE_C) { + if (language == ES_SYNTAX_HIGHLIGHTING_LANGUAGE_C || language == ES_SYNTAX_HIGHLIGHTING_LANGUAGE_SCRIPT) { if (lexState == 4) { lexState = 0; } else if (lexState == 1) { if ((last & 0xFF0000) == ('*' << 16) && (last & 0xFF00) == ('/' << 8) && inComment) { lexState = 0, inComment = false; } - } else if (lexState == 3 || lexState == 6) { + } else if (lexState == 3 || lexState == 6 || (lexState == 5 && language == ES_SYNTAX_HIGHLIGHTING_LANGUAGE_SCRIPT)) { if (!CharIsAlphaOrDigitOrUnderscore(c)) { lexState = 0; } @@ -2760,7 +2806,7 @@ Array TextApplySyntaxHighlighting(const EsTextStyle *baseStyle, int l inIdentifier = true; if (c >= 'a' && c <= 'z' && (!index || !CharIsAlphaOrDigitOrUnderscore(string[index - 1]))) { - const char *const *k = keywords[c - 'a']; + const char *const *k = keywords[language - 1][c - 'a']; for (int i = 0; k[i]; i++) { int j = 0; diff --git a/ports/port.script b/ports/port.script index 7ef1f78..3bb66f9 100644 --- a/ports/port.script +++ b/ports/port.script @@ -170,14 +170,14 @@ void PortMesa() { void PortGCC() { if buildCross { // Print instructions. - PrintStdErr("To build Essence, you need a cross compiler. This will be built for you automatically.\n"); - PrintStdErr("- You need to be connected to the internet. ~100MB will be downloaded.\n"); - PrintStdErr("- You need ~3GB of drive space available.\n"); - PrintStdErr("- You need ~8GB of RAM available.\n"); - PrintStdErr("- This should take ~20 minutes on a modern computer.\n"); - PrintStdErr("- This does *not* require root permissions.\n"); - PrintStdErr("- You must fully update your system before building.\n"); - PrintStdErr("Enter 'yes' to continue.\n"); + Log("To build Essence, you need a cross compiler. This will be built for you automatically."); + Log("- You need to be connected to the internet. ~100MB will be downloaded."); + Log("- You need ~3GB of drive space available."); + Log("- You need ~8GB of RAM available."); + Log("- This should take ~20 minutes on a modern computer."); + Log("- This does *not* require root permissions."); + Log("- You must fully update your system before building."); + Log("Enter 'yes' to continue."); assert ConsoleGetYes(); } @@ -234,8 +234,8 @@ void PortGCC() { // Ask the user if they want to resume an incomplete build. bool resumeBuild = false; if runningMakefiles { - PrintStdErr("The build system has detected a build was started, but was not completed.\n"); - PrintStdErr("Enter 'yes' to attempt to resume this build.\n"); + Log("The build system has detected a build was started, but was not completed."); + Log("Enter 'yes' to attempt to resume this build."); resumeBuild = ConsoleGetYes(); runningMakefiles = false; } @@ -429,7 +429,7 @@ void PortGCC() { PathDeleteRecursively("bin/mpc-src"); PathDeleteRecursively("bin/mpfr-src"); PathDeleteRecursively("bin/gmp-src"); - PrintStdErrHighlight("Build succeeded.\n"); + LogInfo("Build succeeded."); } void PortFreeType() { @@ -618,24 +618,24 @@ void Start() { PortCallback[] portCallbacks = [ PortFFmpeg, PortUxn, PortNasm, PortBochs, PortBusybox, PortMesa, PortGCC, PortFreeType, PortHarfBuzz, PortMusl ]; if portName == "" { - PrintStdErrHighlight("Available ports:\n"); + LogInfo("Available ports:"); for int i = 0; i < portNames:len(); i += 1 { - PrintStdErr("\t%portNames[i]%\n"); + Log("\t%portNames[i]%"); } } else { assert targetName != ""; assert toolchainPrefix != ""; - // PrintStdErrHighlight("Target name: '%targetName%'\n"); - // PrintStdErrHighlight("Toolchain prefix: '%toolchainPrefix%'\n"); - // PrintStdErrHighlight("Processors to use for compilation: '%processorCount%'\n"); - // PrintStdErrHighlight("Skip yes checks: '%skipYesChecks%'\n"); + // LogInfo("Target name: '%targetName%'"); + // LogInfo("Toolchain prefix: '%toolchainPrefix%'"); + // LogInfo("Processors to use for compilation: '%processorCount%'"); + // LogInfo("Skip yes checks: '%skipYesChecks%'"); for int i = 0; i < portNames:len(); i += 1 { if portNames[i] == portName || portName == "all" { if portName == "all" { - PrintStdErrHighlight("Building port: '%portNames[i]%'\n"); + LogInfo("Building port: '%portNames[i]%'"); } portCallbacks[i](); diff --git a/res/Sample Images/Blue.jpg b/res/Sample Images/Blue.jpg index 64de23d..e90259e 100644 Binary files a/res/Sample Images/Blue.jpg and b/res/Sample Images/Blue.jpg differ diff --git a/util/api_table.ini b/util/api_table.ini index 0e42389..86ae045 100644 --- a/util/api_table.ini +++ b/util/api_table.ini @@ -456,6 +456,7 @@ _EsUISetFont=454 EsWorkQueue=455 EsWorkIsExiting=456 EsPanelRadioGroupGetChecked=457 +EsImageDisplayGetImageWidth=458 EsBufferWriteInt8=459 EsInstanceGetStartupRequest=460 EsImageDisplayPaint=461 @@ -492,3 +493,4 @@ _EsDebugCommand=491 DateToLinear=492 EsRectangleContainsAll=493 EsListViewFixedItemSetEnumStringsForColumn=494 +EsImageDisplayGetImageHeight=495 diff --git a/util/get_source.script b/util/get_source.script index 3baa934..2a30bb8 100644 --- a/util/get_source.script +++ b/util/get_source.script @@ -25,9 +25,9 @@ void Get(str url, str directoryName, str checksum) { if checksum != "" { // If we're verifying the checksum of the file, then it should be okay to try downloading it from a non-official mirror. alternateURL = "https://github.com/nakst/cdn/raw/main/cache/" + url2; - PrintStdErr("Attempting to download from '%alternateURL%' with fallback '%url%'...\n"); + Log("Attempting to download from '%alternateURL%' with fallback '%url%'..."); } else { - PrintStdErr("Attempting to download from '%url%'...\n"); + Log("Attempting to download from '%url%'..."); } bool got = PathExists(cachePath); @@ -37,18 +37,18 @@ void Get(str url, str directoryName, str checksum) { if !got { PathDelete(cachePath); - PrintStdErrWarning("Error: Could not download the file at '%url%'. Exiting.\n"); + LogError("Could not download the file at '%url%'. Exiting.\n"); assert false; } if checksum != "" { if SystemShellEvaluate("shasum -a 256 %cachePath%") != "%checksum% %cachePath%\n" { - PrintStdErrWarning("Error: Checksum mismatch for file '%cachePath%'.\n"); + LogError("Checksum mismatch for file '%cachePath%'.\n"); PathDelete(cachePath); assert false; } - PrintStdErr("Valid checksum.\n"); + Log("Valid checksum."); } str decompressFlag = ""; @@ -60,14 +60,14 @@ void Get(str url, str directoryName, str checksum) { } else if StringContains(url, ".tar.gz") { decompressFlag = "z"; } else { - PrintStdErrWarning("Error: Unrecognised archive format extension.\n"); + LogError("Unrecognised archive format extension.\n"); assert false; } assert SystemShellExecute("tar -x%decompressFlag%f %cachePath%"); assert PathMove(directoryName, "bin/source"); - PrintStdErr("File successfully downloaded and extracted.\n"); + Log("File successfully downloaded and extracted."); } void Start() { diff --git a/util/script.c b/util/script.c index a53ebfb..af824c3 100644 --- a/util/script.c +++ b/util/script.c @@ -437,9 +437,16 @@ char baseModuleSource[] = { // Logging: - "void PrintStdErr(str x) #extcall;" - "void PrintStdErrWarning(str x) #extcall;" - "void PrintStdErrHighlight(str x) #extcall;" + "void Log(str x) #extcall;" + "void LogInfo(str x) { Log(TextColorHighlight() + x); }" + "void LogError(str x) { Log(TextColorError() + \"Error: \" + x); }" + + "str TextColorError() #extcall;" + "str TextColorHighlight() #extcall;" + "str TextWeight(int i) #extcall;" + "str TextMonospaced() #extcall;" + "str TextPlain() #extcall;" + "str TextBold() { return TextWeight(700); }" // String operations: @@ -691,9 +698,12 @@ char baseModuleSource[] = { // --------------------------------- External function calls. -int ExternalPrintStdErr(ExecutionContext *context, Value *returnValue); -int ExternalPrintStdErrWarning(ExecutionContext *context, Value *returnValue); -int ExternalPrintStdErrHighlight(ExecutionContext *context, Value *returnValue); +int ExternalLog(ExecutionContext *context, Value *returnValue); +int ExternalTextColorError(ExecutionContext *context, Value *returnValue); +int ExternalTextColorHighlight(ExecutionContext *context, Value *returnValue); +int ExternalTextWeight(ExecutionContext *context, Value *returnValue); +int ExternalTextMonospaced(ExecutionContext *context, Value *returnValue); +int ExternalTextPlain(ExecutionContext *context, Value *returnValue); int ExternalConsoleGetLine(ExecutionContext *context, Value *returnValue); int ExternalStringSlice(ExecutionContext *context, Value *returnValue); int ExternalCharacterToByte(ExecutionContext *context, Value *returnValue); @@ -730,9 +740,12 @@ int External_DirectoryInternalEndIteration(ExecutionContext *context, Value *ret int ExternalOpenDocumentEnumerate(ExecutionContext *context, Value *returnValue); ExternalFunction externalFunctions[] = { - { .cName = "PrintStdErr", .callback = ExternalPrintStdErr }, - { .cName = "PrintStdErrWarning", .callback = ExternalPrintStdErrWarning }, - { .cName = "PrintStdErrHighlight", .callback = ExternalPrintStdErrHighlight }, + { .cName = "Log", .callback = ExternalLog }, + { .cName = "TextColorError", .callback = ExternalTextColorError }, + { .cName = "TextColorHighlight", .callback = ExternalTextColorHighlight }, + { .cName = "TextWeight", .callback = ExternalTextWeight }, + { .cName = "TextMonospaced", .callback = ExternalTextMonospaced }, + { .cName = "TextPlain", .callback = ExternalTextPlain }, { .cName = "ConsoleGetLine", .callback = ExternalConsoleGetLine }, { .cName = "StringSlice", .callback = ExternalStringSlice }, { .cName = "CharacterToByte", .callback = ExternalCharacterToByte }, @@ -5363,6 +5376,7 @@ pthread_mutex_t externalCoroutineMutex; CoroutineState *externalCoroutineUnblockedList; bool systemShellLoggingEnabled = true; +bool coloredOutput; char *scriptSourceDirectory; @@ -5552,33 +5566,36 @@ int ExternalSystemShellEnableLogging(ExecutionContext *context, Value *returnVal return 1; } -int ExternalPrintStdErr(ExecutionContext *context, Value *returnValue) { - (void) returnValue; +int ExternalLog(ExecutionContext *context, Value *returnValue) { STACK_POP_STRING(entryText, entryBytes); fprintf(stderr, "%.*s", (int) entryBytes, (char *) entryText); + fprintf(stderr, coloredOutput ? "\033[0;m\n" : "\n"); return 1; } -int ExternalPrintStdErrWarning(ExecutionContext *context, Value *returnValue) { - (void) returnValue; - STACK_POP_STRING(entryText, entryBytes); - static int coloredOutput = 0; -#ifndef _WIN32 - if (!coloredOutput) coloredOutput = isatty(STDERR_FILENO) ? 2 : 1; -#endif - fprintf(stderr, coloredOutput == 2 ? "\033[0;33m%.*s\033[0;m" : "%.*s", (int) entryBytes, (char *) entryText); - return 1; +int ExternalTextFormat(ExecutionContext *context, Value *returnValue, const char *mode) { + if (coloredOutput) { + char *buffer = malloc(32); + uintptr_t index = HeapAllocate(context); + context->heap[index].type = T_STR; + context->heap[index].bytes = sprintf(buffer, "\033[0;%sm", mode); + context->heap[index].text = buffer; + returnValue->i = index; + } else { + returnValue->i = 0; + } + + return 3; } -int ExternalPrintStdErrHighlight(ExecutionContext *context, Value *returnValue) { - (void) returnValue; - STACK_POP_STRING(entryText, entryBytes); - static int coloredOutput = 0; -#ifndef _WIN32 - if (!coloredOutput) coloredOutput = isatty(STDERR_FILENO) ? 2 : 1; -#endif - fprintf(stderr, coloredOutput == 2 ? "\033[0;36m%.*s\033[0;m" : "%.*s", (int) entryBytes, (char *) entryText); - return 1; +int ExternalTextColorError (ExecutionContext *context, Value *returnValue) { return ExternalTextFormat(context, returnValue, "31"); } +int ExternalTextColorHighlight(ExecutionContext *context, Value *returnValue) { return ExternalTextFormat(context, returnValue, "36"); } +int ExternalTextMonospaced (ExecutionContext *context, Value *returnValue) { return ExternalTextFormat(context, returnValue, ""); } +int ExternalTextPlain (ExecutionContext *context, Value *returnValue) { return ExternalTextFormat(context, returnValue, ""); } + +int ExternalTextWeight(ExecutionContext *context, Value *returnValue) { + if (context->c->stackPointer < 1) return -1; + return ExternalTextFormat(context, returnValue, context->c->stack[--context->c->stackPointer].i > 500 ? "1" : ""); } int ExternalPathCreateDirectory(ExecutionContext *context, Value *returnValue) { @@ -6240,8 +6257,8 @@ int main(int argc, char **argv) { return 1; } + coloredOutput = isatty(STDERR_FILENO); srand(time(NULL)); - sem_init(&externalCoroutineSemaphore, 0, 0); char *scriptPath = NULL; diff --git a/util/start.script b/util/start.script index 87debf9..2de8963 100644 --- a/util/start.script +++ b/util/start.script @@ -13,28 +13,28 @@ void Setup(bool forAutomation) { assert PathCreateLeadingDirectories("bin/Object Files"); if SystemGetHostName() == "Cygwin" { - PrintStdErrWarning("Building on Cygwin is not supported. Use the Windows Subsystem for Linux instead.\n"); + LogError("Building on Cygwin is not supported. Use the Windows Subsystem for Linux instead."); assert false; } if SystemShellEvaluate("shasum -a 256 util/test.txt") != "2c5622dbbf2552e0e66424a302bde0918e09379afce47eef1a21ef0198990fed util/test.txt\n" { - PrintStdErrWarning("--------------------------------------------------------------------\n"); - PrintStdErrWarning(" The source has been corrupted!! \n"); - PrintStdErrWarning(" Please check that you have disabled any automatic line-ending or \n"); - PrintStdErrWarning(" encoding conversions in Git and archive extraction tools you use. \n"); - PrintStdErrWarning("--------------------------------------------------------------------\n"); + Log(TextColorError() + "--------------------------------------------------------------------"); + Log(TextColorError() + " The source has been corrupted!! "); + Log(TextColorError() + " Please check that you have disabled any automatic line-ending or "); + Log(TextColorError() + " encoding conversions in Git and archive extraction tools you use. "); + Log(TextColorError() + "--------------------------------------------------------------------"); assert false; } if StringContains(PathGetDefaultPrefix(), " ") { - PrintStdErrWarning("Error: The path to your essence directory, '%PathGetDefaultPrefix()%', contains spaces.\n"); + LogError("The path to your essence directory, '%PathGetDefaultPrefix()%', contains spaces."); assert false; } - if !SystemShellExecute("which gcc > /dev/null") { PrintStdErrWarning("Error: GCC was not found.\n"); assert false; } - if !SystemShellExecute("which nasm > /dev/null") { PrintStdErrWarning("Error: Nasm was not found.\n"); assert false; } - if !SystemShellExecute("which make > /dev/null") { PrintStdErrWarning("Error: Make was not found.\n"); assert false; } + if !SystemShellExecute("which gcc > /dev/null") { LogError("GCC was not found."); assert false; } + if !SystemShellExecute("which nasm > /dev/null") { LogError("Nasm was not found."); assert false; } + if !SystemShellExecute("which make > /dev/null") { LogError("Make was not found."); assert false; } if target == "" { target = "TARGET_X86_64"; @@ -54,7 +54,7 @@ void Setup(bool forAutomation) { void Start() { Setup(false); SystemShellExecute("bin/build %options%"); - PrintStdErrHighlight("\n"); + Log(""); } void GenerateOVF() {