diff --git a/apps/installer.cpp b/apps/installer.cpp index fa06bfa..968231e 100644 --- a/apps/installer.cpp +++ b/apps/installer.cpp @@ -907,6 +907,17 @@ void WriteNewConfiguration() { } else if (!s.sectionClassBytes && 0 == EsStringCompareRaw(s.section, s.sectionBytes, EsLiteral("general")) && 0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("installation_state"))) { EsBufferFormat(&buffer, "installation_state=0\n"); + } else if (!s.sectionClassBytes && 0 == EsStringCompareRaw(s.section, s.sectionBytes, EsLiteral("general")) + && 0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("clock_offset_ms"))) { + EsAssert(false); + } else if (!s.sectionClassBytes && 0 == EsStringCompareRaw(s.section, s.sectionBytes, EsLiteral("general")) + && 0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("user_name"))) { + EsAssert(false); + } else if (!s.sectionClassBytes && 0 == EsStringCompareRaw(s.section, s.sectionBytes, EsLiteral("general")) && !s.keyBytes) { + size_t userNameBytes; + char *userName = EsTextboxGetContents(userNameTextbox, &userNameBytes); + EsBufferFormat(&buffer, "[general]\nclock_offset_ms=%d\nuser_name=%s\n", clockOffsetMs, userNameBytes, userName); + EsHeapFree(userName); } else { size_t lineBytes = EsINIFormat(&s, lineBuffer, lineBufferBytes); EsBufferWrite(&buffer, lineBuffer, lineBytes); diff --git a/desktop/api.cpp b/desktop/api.cpp index bd649bd..e2402b0 100644 --- a/desktop/api.cpp +++ b/desktop/api.cpp @@ -428,6 +428,8 @@ void SystemConfigurationUnload() { } void SystemConfigurationLoad(const char *file, size_t fileBytes) { + // TODO Detecting duplicate keys? + EsINIState s = {}; s.buffer = (char *) file; s.bytes = fileBytes; diff --git a/desktop/desktop.cpp b/desktop/desktop.cpp index 89a05d8..5b0710e 100644 --- a/desktop/desktop.cpp +++ b/desktop/desktop.cpp @@ -218,6 +218,8 @@ struct { bool inShutdown; bool inspectorOpen; + + EsHandle clockReady; } desktop; int TaskBarButtonMessage(EsElement *element, EsMessage *message); @@ -1217,6 +1219,40 @@ int TaskBarTasksButtonMessage(EsElement *element, EsMessage *message) { return ES_HANDLED; } +void TaskBarClockUpdateThread(EsGeneric _clock) { + EsWaitSingle(desktop.clockReady); + + EsButton *clock = (EsButton *) _clock.p; + static EsDateComponents previousTime = {}; + + while (true) { + // TODO Use the local time zone. + EsDateComponents currentTime; + EsDateNowUTC(¤tTime); + + uint8_t secondsUntilNextMinuteChange = 60 - currentTime.second; + + currentTime.second = 0; + currentTime.millisecond = 0; + + if (0 == EsMemoryCompare(¤tTime, &previousTime, sizeof(EsDateComponents))) { + // The minute or hour hasn't changed. Wait for the next change. + EsSleep((secondsUntilNextMinuteChange + 1) * 1000); + } else { + // Update the clock's label. + // TODO Proper clock formatting. + char label[32]; + size_t labelBytes = EsStringFormat(label, sizeof(label), "%d%d:%d%d", + currentTime.hour / 10, currentTime.hour % 10, currentTime.minute / 10, currentTime.minute % 10); + EsMessageMutexAcquire(); + EsButtonSetLabel(clock, label, labelBytes); + EsMessageMutexRelease(); + + EsMemoryCopy(&previousTime, ¤tTime, sizeof(EsDateComponents)); + } + } +} + void Shutdown(uintptr_t action) { // TODO This doesn't wait for Desktop instances. @@ -1771,6 +1807,7 @@ ApplicationInstance *ApplicationInstanceCreate(int64_t id, _EsApplicationStartup return instance; } else { if (!hidden) WindowTabDestroy(tab); // TODO Test this. + desktop.allApplicationInstances.FindAndDeleteSwap(instance, true); EsHeapFree(instance); return nullptr; } @@ -2513,6 +2550,14 @@ void DesktopSetup() { desktop.tasksButton = EsButtonCreate(panel, ES_ELEMENT_HIDDEN, ES_STYLE_TASK_BAR_BUTTON); desktop.tasksButton->messageUser = TaskBarTasksButtonMessage; + EsButton *clockButton = EsButtonCreate(panel, ES_BUTTON_TABULAR | ES_BUTTON_COMPACT, ES_STYLE_TASK_BAR_EXTRA); + clockButton->cName = "current time"; + EsThreadCreate(TaskBarClockUpdateThread, nullptr, clockButton); + + EsButtonOnCommand(clockButton, [] (EsInstance *, EsElement *, EsCommand *) { + ApplicationInstanceCreate(APPLICATION_ID_DESKTOP_SETTINGS, nullptr, nullptr); + }); + EsButton *shutdownButton = EsButtonCreate(panel, ES_FLAGS_DEFAULT, ES_STYLE_TASK_BAR_EXTRA); EsButtonSetIcon(shutdownButton, ES_ICON_SYSTEM_SHUTDOWN_SYMBOLIC); @@ -2948,7 +2993,10 @@ void DesktopMessage(EsMessage *message) { if (ES_SUCCESS == EsDeviceControl(handle, ES_DEVICE_CONTROL_CLOCK_READ, &reading, &linear)) { // TODO Scheduler timer is not particularly accurate, so we should periodically resynchronize with the clock. - api.global->schedulerTimeOffset = (linear ?: DateToLinear(&reading)) - api.global->schedulerTimeMs; + api.global->schedulerTimeOffset = (linear ?: DateToLinear(&reading)) + - api.global->schedulerTimeMs + + EsSystemConfigurationReadInteger(EsLiteral("general"), EsLiteral("clock_offset_ms"), 0); + EsEventSet(desktop.clockReady); } } } else if (message->type == ES_MSG_UNREGISTER_FILE_SYSTEM || message->type == ES_MSG_DEVICE_DISCONNECTED) { @@ -2993,6 +3041,8 @@ void DesktopMessage(EsMessage *message) { void DesktopEntry() { ConfigurationLoadApplications(); + desktop.clockReady = EsEventCreate(false); + EsMessage m = { MSG_SETUP_DESKTOP_UI }; EsMessagePost(nullptr, &m); diff --git a/desktop/gui.cpp b/desktop/gui.cpp index 2d911f1..3dc63e7 100644 --- a/desktop/gui.cpp +++ b/desktop/gui.cpp @@ -3926,7 +3926,8 @@ int ProcessButtonMessage(EsElement *element, EsMessage *message) { EsDrawContent(message->painter, element, ES_RECT_2S(message->painter->width, message->painter->height), button->label, button->labelBytes, button->iconID, - (button->flags & ES_BUTTON_DROPDOWN) ? ES_DRAW_CONTENT_MARKER_DOWN_ARROW : ES_FLAGS_DEFAULT); + ((button->flags & ES_BUTTON_DROPDOWN) ? ES_DRAW_CONTENT_MARKER_DOWN_ARROW : 0) + | ((button->flags & ES_BUTTON_TABULAR) ? ES_DRAW_CONTENT_TABULAR : 0)); } else if (message->type == ES_MSG_PAINT_ICON) { if (button->imageDisplay) { EsRectangle imageSize = ES_RECT_2S(button->imageDisplay->width, button->imageDisplay->height); @@ -4099,6 +4100,15 @@ EsButton *EsButtonCreate(EsElement *parent, uint64_t flags, const EsStyle *style return button; } +void EsButtonSetLabel(EsButton *button, const char *label, ptrdiff_t labelBytes) { + EsMessageMutexCheck(); + if (labelBytes == -1) labelBytes = EsCStringLength(label); + HeapDuplicate((void **) &button->label, &button->labelBytes, label, labelBytes); + button->Repaint(true); + button->state &= ~UI_STATE_USE_MEASUREMENT_CACHE; + EsElementUpdateContentSize(button->parent); +} + void EsButtonSetIcon(EsButton *button, uint32_t iconID) { EsMessageMutexCheck(); diff --git a/desktop/os.header b/desktop/os.header index 58e0c3a..24a28c3 100644 --- a/desktop/os.header +++ b/desktop/os.header @@ -514,6 +514,7 @@ define ES_BUTTON_CHECKBOX (1 << 10) define ES_BUTTON_RADIOBOX (1 << 11) define ES_BUTTON_CANCEL (1 << 12) define ES_BUTTON_PUSH (1 << 13) +define ES_BUTTON_TABULAR (1 << 14) // Use tabular text figures. define ES_MENU_ITEM_CHECKED (ES_CHECK_CHECKED) define ES_COLOR_WELL_HAS_OPACITY (1 << 0) @@ -2468,7 +2469,6 @@ private function void _EsUISetFont(EsFontFamily id); // Buttons. function EsButton *EsButtonCreate(EsElement *parent, uint64_t flags = ES_FLAGS_DEFAULT, const EsStyle *style = ES_NULL, STRING label = BLANK_STRING); - function void EsButtonSetIcon(EsButton *button, uint32_t iconID); function void EsButtonSetIconFromBits(EsButton *button, const uint32_t *bits, size_t width, size_t height, size_t stride); function void EsButtonSetCheck(EsButton *button, EsCheckState checkState = ES_CHECK_CHECKED, bool sendUpdatedMessage = true); diff --git a/desktop/theme.cpp b/desktop/theme.cpp index 6040a3a..551eb59 100644 --- a/desktop/theme.cpp +++ b/desktop/theme.cpp @@ -1883,7 +1883,7 @@ void UIStyle::PaintText(EsPainter *painter, EsElement *element, EsRectangle rect EsRectangle bounds = Translate(EsRectangleAddBorder(rectangle, insets), painter->offsetX, painter->offsetY); EsRectangle textBounds = bounds; EsRectangle oldClip = painter->clip; - EsRectangleClip(painter->clip, bounds, &painter->clip); + EsRectangleClip(painter->clip, Translate(rectangle, painter->offsetX, painter->offsetY), &painter->clip); EsRectangle iconBounds = EsRectangleSplit(&textBounds, metrics->iconSize, metrics->layoutVertical ? 't' : 'l', gapMinor); EsPainter iconPainter = *painter; diff --git a/res/Theme Source.dat b/res/Theme Source.dat index 7c8aa58..0a47fb9 100644 Binary files a/res/Theme Source.dat and b/res/Theme Source.dat differ diff --git a/res/Theme.dat b/res/Theme.dat index b1f147f..51ebe3c 100644 Binary files a/res/Theme.dat and b/res/Theme.dat differ diff --git a/shared/common.cpp b/shared/common.cpp index e31b454..5a097e0 100644 --- a/shared/common.cpp +++ b/shared/common.cpp @@ -1638,7 +1638,7 @@ void LoadImage(const void *path, ptrdiff_t pathBytes, void *destination, int des for (int i = 0; i < destinationWidth; i++, pixel++) { if (i - cx < 0 || i - cx >= width || j - cy < 0 || j - cy >= height) { - *pixel = 0x738393; + *pixel = 0x77B9F9; } else { *pixel = image[i - cx + (j - cy) * width]; } diff --git a/util/build.c b/util/build.c index 1a6ff5a..0b260c9 100644 --- a/util/build.c +++ b/util/build.c @@ -1119,6 +1119,17 @@ void DoCommand(const char *l) { fwrite(&uncompressed, 1, sizeof(uncompressed), f); fwrite(&crc64, 1, sizeof(crc64), f); fclose(f); + } else if (0 == strcmp(l, "make-installer-root")) { + DoCommand("make-installer-archive"); + CallSystem("util/uefi_compile.sh"); + CallSystem("cp bin/mbr root/mbr.dat"); + CallSystem("cp bin/stage1 root/stage1.dat"); + CallSystem("cp bin/stage2 root/stage2.dat"); + CallSystem("cp bin/uefi root/uefi1.dat"); + CallSystem("cp bin/uefi_loader root/uefi2.dat"); + CallSystem("cp LICENSE.md root/installer_licenses.txt"); + CallSystem("mv bin/installer_archive.dat root"); + CallSystem("mv bin/installer_metadata.dat root"); } else if (0 == strcmp(l, "config")) { BuildUtilities(); CallSystem("bin/config_editor"); diff --git a/util/uefi.sh b/util/uefi.sh index 533eedd..203290f 100755 --- a/util/uefi.sh +++ b/util/uefi.sh @@ -10,11 +10,7 @@ set -e -# Duplicated in uefi_to_device.sh. -CC="clang -target x86_64-unknown-windows -ffreestanding -fshort-wchar -mno-red-zone -I ports/efitoolkit/inc -c -Wall -Wextra" -LINK="clang -target x86_64-unknown-windows -nostdlib -Wl,-entry:efi_main -Wl,-subsystem:efi_application -fuse-ld=lld-link" -$CC -o bin/uefi.o boot/x86/uefi.c -$LINK -o bin/uefi bin/uefi.o +util/uefi_compile.sh mkdir -p mount sudo losetup --offset `fdisk -l bin/uefi_drive | grep 'EFI System' | awk '{print 512*$2}'` --sizelimit `fdisk -l bin/uefi_drive | grep 'EFI System' | awk '{print 512*$4}'` /dev/loop0 bin/uefi_drive diff --git a/util/uefi_compile.sh b/util/uefi_compile.sh new file mode 100755 index 0000000..fb5ad6b --- /dev/null +++ b/util/uefi_compile.sh @@ -0,0 +1,5 @@ +set -e +CC="clang -target x86_64-unknown-windows -ffreestanding -fshort-wchar -mno-red-zone -I ports/efitoolkit/inc -c -Wall -Wextra" +LINK="clang -target x86_64-unknown-windows -nostdlib -Wl,-entry:efi_main -Wl,-subsystem:efi_application -fuse-ld=lld-link" +$CC -o bin/uefi.o boot/x86/uefi.c +$LINK -o bin/uefi bin/uefi.o diff --git a/util/uefi_to_device.sh b/util/uefi_to_device.sh index c9d71d2..e621526 100755 --- a/util/uefi_to_device.sh +++ b/util/uefi_to_device.sh @@ -4,11 +4,7 @@ set -e -# Duplicated from uefi.sh. -CC="clang -target x86_64-unknown-windows -ffreestanding -fshort-wchar -mno-red-zone -I ports/efitoolkit/inc -c -Wall -Wextra" -LINK="clang -target x86_64-unknown-windows -nostdlib -Wl,-entry:efi_main -Wl,-subsystem:efi_application -fuse-ld=lld-link" -$CC -o bin/uefi.o boot/x86/uefi.c -$LINK -o bin/uefi bin/uefi.o +util/uefi_compile.sh mkdir -p mount mount $1 mount