mirror of https://gitlab.com/nakst/essence
native UI for build core
This commit is contained in:
parent
d66fdf114c
commit
420bdf698a
|
@ -582,6 +582,14 @@ void FolderRefresh(Folder *folder) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!folder->attachedInstances.Length()) {
|
||||
// The folder does not have any attached instances, so just destroy it.
|
||||
loadedFolders.FindAndDeleteSwap(folder, true);
|
||||
foldersWithNoAttachedInstances.FindAndDeleteSwap(folder, true);
|
||||
FolderDestroy(folder);
|
||||
return;
|
||||
}
|
||||
|
||||
folder->refreshing = true;
|
||||
|
||||
Array<Instance *> instancesToRefresh = {};
|
||||
|
@ -597,3 +605,21 @@ void FolderRefresh(Folder *folder) {
|
|||
|
||||
instancesToRefresh.Free();
|
||||
}
|
||||
|
||||
void FolderRefreshAllFrom(String prefix) {
|
||||
Array<Folder *> foldersToRefresh = {};
|
||||
|
||||
for (uintptr_t i = 0; i < loadedFolders.Length(); i++) {
|
||||
if (!loadedFolders[i]->refreshing
|
||||
&& loadedFolders[i]->itemHandler->type == NAMESPACE_HANDLER_FILE_SYSTEM
|
||||
&& PathHasPrefix(loadedFolders[i]->path, prefix)) {
|
||||
foldersToRefresh.Add(loadedFolders[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (uintptr_t i = 0; i < foldersToRefresh.Length(); i++) {
|
||||
FolderRefresh(foldersToRefresh[i]);
|
||||
}
|
||||
|
||||
foldersToRefresh.Free();
|
||||
}
|
||||
|
|
|
@ -608,9 +608,14 @@ void _start() {
|
|||
EsConstantBufferRead(message->user.context1.u, data);
|
||||
String oldPath = StringFromLiteralWithSize(paths, bytes[0]);
|
||||
String newPath = StringFromLiteralWithSize(paths + bytes[0], bytes[1]);
|
||||
FolderPathMoved(oldPath, newPath, false);
|
||||
size_t pathSectionCount;
|
||||
|
||||
if (oldPath.bytes) {
|
||||
FolderPathMoved(oldPath, newPath, false);
|
||||
} else {
|
||||
FolderRefreshAllFrom(newPath);
|
||||
}
|
||||
|
||||
pathSectionCount = PathCountSections(oldPath);
|
||||
|
||||
for (uintptr_t i = 0; i < pathSectionCount; i++) {
|
||||
|
|
|
@ -834,7 +834,7 @@ int ListCallback(EsElement *element, EsMessage *message) {
|
|||
INTERFACE_STRING(FileManagerCannotOpenSystemFile),
|
||||
ES_ICON_DIALOG_ERROR, ES_DIALOG_ALERT_OK_BUTTON);
|
||||
} else {
|
||||
EsApplicationRunTemporary(instance, STRING(path));
|
||||
EsApplicationRunTemporary(STRING(path));
|
||||
}
|
||||
|
||||
StringDestroy(&path);
|
||||
|
@ -847,7 +847,8 @@ int ListCallback(EsElement *element, EsMessage *message) {
|
|||
request.id = fileType->openHandler;
|
||||
request.filePath = path.text;
|
||||
request.filePathBytes = path.bytes;
|
||||
request.flags = EsKeyboardIsCtrlHeld() ? ES_APPLICATION_STARTUP_IN_SAME_CONTAINER : ES_FLAGS_DEFAULT;
|
||||
request.flags = EsKeyboardIsCtrlHeld() || message->chooseItem.source == ES_LIST_VIEW_CHOOSE_ITEM_MIDDLE_CLICK
|
||||
? ES_APPLICATION_STARTUP_IN_SAME_CONTAINER : ES_FLAGS_DEFAULT;
|
||||
EsApplicationStart(instance, &request);
|
||||
StringDestroy(&path);
|
||||
} else {
|
||||
|
@ -1166,7 +1167,7 @@ void InstanceCreateUI(Instance *instance) {
|
|||
EsApplicationStartupRequest startupRequest = EsInstanceGetStartupRequest(instance);
|
||||
String path;
|
||||
|
||||
if (startupRequest.flags & ES_APPLICATION_STARTUP_MANUAL_PATH) {
|
||||
if (startupRequest.filePathBytes) {
|
||||
uintptr_t directoryEnd = startupRequest.filePathBytes;
|
||||
|
||||
for (uintptr_t i = 0; i < (uintptr_t) startupRequest.filePathBytes; i++) {
|
||||
|
|
|
@ -318,7 +318,7 @@ int InstanceCallback(Instance *instance, EsMessage *message) {
|
|||
|
||||
EsPanel *titleRow = EsPanelCreate(instance->fontPreview, ES_CELL_H_CENTER | ES_PANEL_HORIZONTAL, &styleFontInformationRow);
|
||||
EsIconDisplayCreate(titleRow, ES_FLAGS_DEFAULT, ES_STYLE_ICON_DISPLAY, ES_ICON_FONT_X_GENERIC);
|
||||
EsTextDisplayCreate(titleRow, ES_FLAGS_DEFAULT, ES_STYLE_TEXT_HEADING0, message->instanceOpen.name, message->instanceOpen.nameBytes);
|
||||
EsTextDisplayCreate(titleRow, ES_FLAGS_DEFAULT, ES_STYLE_TEXT_HEADING0, message->instanceOpen.nameOrPath, message->instanceOpen.nameOrPathBytes);
|
||||
EsSpacerCreate(instance->fontPreview, ES_FLAGS_DEFAULT, 0, 0, 20);
|
||||
|
||||
int sizes[] = { 12, 18, 24, 36, 48, 60, 72, 0 };
|
||||
|
|
|
@ -712,18 +712,18 @@ int InstanceCallback(Instance *instance, EsMessage *message) {
|
|||
} else if (message->type == ES_MSG_INSTANCE_SAVE) {
|
||||
// TODO Error handling.
|
||||
|
||||
uintptr_t extensionOffset = message->instanceSave.nameBytes;
|
||||
uintptr_t extensionOffset = message->instanceSave.nameOrPathBytes;
|
||||
|
||||
while (extensionOffset) {
|
||||
if (message->instanceSave.name[extensionOffset - 1] == '.') {
|
||||
if (message->instanceSave.nameOrPath[extensionOffset - 1] == '.') {
|
||||
break;
|
||||
} else {
|
||||
extensionOffset--;
|
||||
}
|
||||
}
|
||||
|
||||
const char *extension = extensionOffset ? message->instanceSave.name + extensionOffset : "png";
|
||||
size_t extensionBytes = extensionOffset ? message->instanceSave.nameBytes - extensionOffset : 3;
|
||||
const char *extension = extensionOffset ? message->instanceSave.nameOrPath + extensionOffset : "png";
|
||||
size_t extensionBytes = extensionOffset ? message->instanceSave.nameOrPathBytes - extensionOffset : 3;
|
||||
|
||||
uint32_t *bits;
|
||||
size_t width, height, stride;
|
||||
|
|
|
@ -173,7 +173,8 @@ void MessageLoopThread(EsGeneric) {
|
|||
textboxOutput = EsTextboxCreate(panel, ES_TEXTBOX_MULTILINE | ES_CELL_FILL, &styleMonospacedTextbox);
|
||||
EsSpacerCreate(panel, ES_CELL_H_FILL, ES_STYLE_SEPARATOR_HORIZONTAL);
|
||||
textboxInput = EsTextboxCreate(panel, ES_CELL_H_FILL, &styleMonospacedTextbox);
|
||||
EsTextboxEnableSmartQuotes(textboxInput, false);
|
||||
EsTextboxEnableSmartReplacement(textboxInput, false);
|
||||
EsTextboxSetReadOnly(textboxOutput, true);
|
||||
textboxInput->messageUser = ProcessTextboxInputMessage;
|
||||
EsElementFocus(textboxInput);
|
||||
EsEventSet(commandEvent); // Ready to receive output.
|
||||
|
|
|
@ -280,6 +280,8 @@ void InitialiseInstance(EsInstance *instance) {
|
|||
void _start() {
|
||||
_init();
|
||||
|
||||
EsApplicationRunTemporary(EsLiteral("what"));
|
||||
|
||||
while (true) {
|
||||
EsMessage *message = EsMessageReceive();
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ name=Test
|
|||
icon=icon_system_software_install
|
||||
use_single_process=1
|
||||
permission_all_files=1
|
||||
permission_run_temporary_application=1
|
||||
|
||||
[build]
|
||||
source=apps/test.cpp
|
||||
|
|
|
@ -123,7 +123,7 @@ void SetLanguage(Instance *instance, uint32_t newLanguage) {
|
|||
|
||||
instance->syntaxHighlightingLanguage = newLanguage;
|
||||
EsTextboxSetupSyntaxHighlighting(instance->textboxDocument, newLanguage);
|
||||
EsTextboxEnableSmartQuotes(instance->textboxDocument, !newLanguage);
|
||||
EsTextboxEnableSmartReplacement(instance->textboxDocument, !newLanguage);
|
||||
}
|
||||
|
||||
void FormatPopupCreate(Instance *instance) {
|
||||
|
@ -285,11 +285,11 @@ int InstanceCallback(Instance *instance, EsMessage *message) {
|
|||
EsTextboxSetSelection(instance->textboxDocument, 0, 0, 0, 0);
|
||||
EsElementRelayout(instance->textboxDocument);
|
||||
|
||||
if (StringEndsWith(message->instanceOpen.name, message->instanceOpen.nameBytes, EsLiteral(".c"), true)
|
||||
|| StringEndsWith(message->instanceOpen.name, message->instanceOpen.nameBytes, EsLiteral(".cpp"), true)
|
||||
|| StringEndsWith(message->instanceOpen.name, message->instanceOpen.nameBytes, EsLiteral(".h"), true)) {
|
||||
if (StringEndsWith(message->instanceOpen.nameOrPath, message->instanceOpen.nameOrPathBytes, EsLiteral(".c"), true)
|
||||
|| StringEndsWith(message->instanceOpen.nameOrPath, message->instanceOpen.nameOrPathBytes, EsLiteral(".cpp"), true)
|
||||
|| StringEndsWith(message->instanceOpen.nameOrPath, message->instanceOpen.nameOrPathBytes, EsLiteral(".h"), true)) {
|
||||
SetLanguage(instance, ES_SYNTAX_HIGHLIGHTING_LANGUAGE_C);
|
||||
} else if (StringEndsWith(message->instanceOpen.name, message->instanceOpen.nameBytes, EsLiteral(".ini"), true)) {
|
||||
} else if (StringEndsWith(message->instanceOpen.nameOrPath, message->instanceOpen.nameOrPathBytes, EsLiteral(".ini"), true)) {
|
||||
SetLanguage(instance, ES_SYNTAX_HIGHLIGHTING_LANGUAGE_INI);
|
||||
} else {
|
||||
SetLanguage(instance, 0);
|
||||
|
|
|
@ -177,6 +177,7 @@ char *SystemConfigurationGroupReadString(EsSystemConfigurationGroup *group, cons
|
|||
int64_t SystemConfigurationGroupReadInteger(EsSystemConfigurationGroup *group, const char *key, ptrdiff_t keyBytes, int64_t defaultValue = 0);
|
||||
MountPoint *NodeFindMountPoint(const char *prefix, size_t prefixBytes);
|
||||
EsWindow *WindowFromWindowID(EsObjectID id);
|
||||
void POSIXCleanup();
|
||||
extern "C" void _init();
|
||||
|
||||
struct ProcessMessageTiming {
|
||||
|
@ -626,14 +627,14 @@ void _EsOpenDocumentEnumerate(EsBuffer *outputBuffer) {
|
|||
MessageDesktop(&m, 1, ES_INVALID_HANDLE, outputBuffer);
|
||||
}
|
||||
|
||||
void EsApplicationRunTemporary(EsInstance *instance, const char *path, ptrdiff_t pathBytes) {
|
||||
void EsApplicationRunTemporary(const char *path, ptrdiff_t pathBytes) {
|
||||
if (pathBytes == -1) pathBytes = EsCStringLength(path);
|
||||
char *buffer = (char *) EsHeapAllocate(pathBytes + 1, false);
|
||||
|
||||
if (buffer) {
|
||||
buffer[0] = DESKTOP_MSG_RUN_TEMPORARY_APPLICATION;
|
||||
EsMemoryCopy(buffer + 1, path, pathBytes);
|
||||
MessageDesktop(buffer, pathBytes + 1, instance->window->handle);
|
||||
MessageDesktop(buffer, pathBytes + 1);
|
||||
EsHeapFree(buffer);
|
||||
}
|
||||
}
|
||||
|
@ -682,8 +683,10 @@ void InstanceClose(EsInstance *instance) {
|
|||
|
||||
if (apiInstance->startupInformation->filePathBytes) {
|
||||
cTitle = interfaceString_FileCloseWithModificationsTitle;
|
||||
contentBytes = EsStringFormat(content, sizeof(content), interfaceString_FileCloseWithModificationsContent,
|
||||
apiInstance->startupInformation->filePathBytes, apiInstance->startupInformation->filePath);
|
||||
const char *name;
|
||||
ptrdiff_t nameBytes;
|
||||
PathGetName(apiInstance->startupInformation->filePath, apiInstance->startupInformation->filePathBytes, &name, &nameBytes);
|
||||
contentBytes = EsStringFormat(content, sizeof(content), interfaceString_FileCloseWithModificationsContent, nameBytes, name);
|
||||
} else {
|
||||
cTitle = interfaceString_FileCloseNewTitle;
|
||||
contentBytes = EsStringFormat(content, sizeof(content), interfaceString_FileCloseNewContent,
|
||||
|
@ -870,8 +873,12 @@ EsInstance *_EsInstanceCreate(size_t bytes, EsMessage *message, const char *appl
|
|||
EsWindowSetTitle(instance->window, nullptr, 0);
|
||||
|
||||
if (apiInstance->startupInformation && apiInstance->startupInformation->readHandle) {
|
||||
const char *name;
|
||||
ptrdiff_t nameBytes;
|
||||
PathGetName(apiInstance->startupInformation->filePath, apiInstance->startupInformation->filePathBytes, &name, &nameBytes);
|
||||
|
||||
InstanceCreateFileStore(apiInstance, apiInstance->startupInformation->readHandle);
|
||||
EsWindowSetTitle(instance->window, apiInstance->startupInformation->filePath, apiInstance->startupInformation->filePathBytes);
|
||||
EsWindowSetTitle(instance->window, name, nameBytes);
|
||||
EsCommandSetDisabled(&apiInstance->commandShowInFileManager, false);
|
||||
|
||||
// HACK Delay sending the instance open message so that application has a chance to initialise the instance.
|
||||
|
@ -989,8 +996,8 @@ void InstanceSendOpenMessage(EsInstance *instance, bool update) {
|
|||
APIInstance *apiInstance = (APIInstance *) instance->_private;
|
||||
|
||||
EsMessage m = { .type = ES_MSG_INSTANCE_OPEN };
|
||||
m.instanceOpen.name = apiInstance->startupInformation->filePath;
|
||||
m.instanceOpen.nameBytes = apiInstance->startupInformation->filePathBytes;
|
||||
m.instanceOpen.nameOrPath = apiInstance->startupInformation->filePath;
|
||||
m.instanceOpen.nameOrPathBytes = apiInstance->startupInformation->filePathBytes;
|
||||
m.instanceOpen.file = apiInstance->fileStore;
|
||||
m.instanceOpen.update = update;
|
||||
|
||||
|
@ -1061,6 +1068,9 @@ EsMessage *EsMessageReceive() {
|
|||
EsAssert(!api.workQueue.Length());
|
||||
api.workThreads.Free();
|
||||
api.workQueue.Free();
|
||||
#ifdef ENABLE_POSIX_SUBSYSTEM
|
||||
POSIXCleanup();
|
||||
#endif
|
||||
MemoryLeakDetectorCheckpoint(&heap);
|
||||
EsPrint("ES_MSG_APPLICATION_EXIT - Heap allocation count: %d (%d from malloc).\n", heap.allocationsCount, mallocCount);
|
||||
#endif
|
||||
|
@ -1147,8 +1157,8 @@ EsMessage *EsMessageReceive() {
|
|||
m.instanceSave.file->type = FILE_STORE_HANDLE;
|
||||
m.instanceSave.file->handles = 1;
|
||||
|
||||
m.instanceSave.name = instance->startupInformation->filePath;
|
||||
m.instanceSave.nameBytes = instance->startupInformation->filePathBytes;
|
||||
m.instanceSave.nameOrPath = instance->startupInformation->filePath;
|
||||
m.instanceSave.nameOrPathBytes = instance->startupInformation->filePathBytes;
|
||||
|
||||
if (m.instanceSave.file->error == ES_SUCCESS && _instance->callback && _instance->callback(_instance, &m)) {
|
||||
// The instance callback will have called EsInstanceSaveComplete.
|
||||
|
@ -1227,7 +1237,10 @@ EsMessage *EsMessageReceive() {
|
|||
instance->startupInformation->containingFolderBytes);
|
||||
instance->startupInformation->filePath = filePath;
|
||||
instance->startupInformation->containingFolder = containingFolder;
|
||||
EsWindowSetTitle(_instance->window, filePath, instance->startupInformation->filePathBytes);
|
||||
const char *name;
|
||||
ptrdiff_t nameBytes;
|
||||
PathGetName(filePath, instance->startupInformation->filePathBytes, &name, &nameBytes);
|
||||
EsWindowSetTitle(_instance->window, name, nameBytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1395,10 +1408,13 @@ void EsInstanceSaveComplete(EsInstance *instance, EsFileStore *file, bool succes
|
|||
MessageDesktop(buffer, 1, instance->window->handle);
|
||||
|
||||
if (success) {
|
||||
const char *name;
|
||||
ptrdiff_t nameBytes;
|
||||
PathGetName(apiInstance->startupInformation->filePath, apiInstance->startupInformation->filePathBytes, &name, &nameBytes);
|
||||
|
||||
EsInstanceSetModified(instance, false);
|
||||
size_t messageBytes;
|
||||
char *message = EsStringAllocateAndFormat(&messageBytes, "Saved to %s", // TODO Localization.
|
||||
apiInstance->startupInformation->filePathBytes, apiInstance->startupInformation->filePath);
|
||||
char *message = EsStringAllocateAndFormat(&messageBytes, interfaceString_FileSaveAnnouncement, nameBytes, name);
|
||||
EsAnnouncementShow(instance->window, ES_FLAGS_DEFAULT, -1, -1, message, messageBytes);
|
||||
EsHeapFree(message);
|
||||
EsCommandSetDisabled(&apiInstance->commandShowInFileManager, false);
|
||||
|
@ -1596,7 +1612,8 @@ void EsCommandAddButton(EsCommand *command, EsButton *button) {
|
|||
button->state |= UI_STATE_COMMAND_BUTTON;
|
||||
EsElementSetEnabled(button, command->enabled);
|
||||
EsButtonSetCheck(button, command->check); // Set the check before setting the callback, so that it doesn't get called.
|
||||
EsButtonOnCommand(button, command->callback, command);
|
||||
EsButtonOnCommand(button, command->callback);
|
||||
button->command = command;
|
||||
}
|
||||
|
||||
EsCommand *EsCommandRegister(EsCommand *command, EsInstance *_instance,
|
||||
|
@ -1654,7 +1671,9 @@ void EsCommandSetCallback(EsCommand *command, EsCommandCallback callback) {
|
|||
|
||||
for (uintptr_t i = 0; i < ArrayLength(command->elements); i++) {
|
||||
if (command->elements[i]->state & UI_STATE_COMMAND_BUTTON) {
|
||||
EsButtonOnCommand((EsButton *) command->elements[i], callback, command);
|
||||
EsButton *button = (EsButton *) command->elements[i];
|
||||
EsAssert(button->command == command);
|
||||
button->onCommand = callback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2156,6 +2175,9 @@ char *EsPOSIXConvertPath(const char *, size_t *, bool) {
|
|||
EsAssert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void POSIXCleanup() {
|
||||
}
|
||||
#else
|
||||
EsProcessStartupInformation *ProcessGetStartupInformation() {
|
||||
return api.startupInformation;
|
||||
|
|
|
@ -1586,10 +1586,11 @@ void ApplicationInstanceCleanup(ApplicationInstance *instance) {
|
|||
instance->application = nullptr;
|
||||
}
|
||||
|
||||
void PathGetNameAndContainingFolder(const char *path, ptrdiff_t pathBytes,
|
||||
const char **name, ptrdiff_t *nameBytes,
|
||||
const char **containingFolder, ptrdiff_t *containingFolderBytes,
|
||||
EsVolumeInformation *volumeInformation /* needs to be allocated outside */) {
|
||||
void PathGetNameAndContainingFolder(InstalledApplication *application,
|
||||
const char *path, ptrdiff_t pathBytes,
|
||||
const char **name, ptrdiff_t *nameBytes, /* the full path is returned if the application has permission_all_files */
|
||||
const char **containingFolder, ptrdiff_t *containingFolderBytes,
|
||||
EsVolumeInformation *volumeInformation /* needs to be allocated outside */) {
|
||||
if (pathBytes == -1) {
|
||||
pathBytes = EsCStringLength(path);
|
||||
}
|
||||
|
@ -1604,8 +1605,11 @@ void PathGetNameAndContainingFolder(const char *path, ptrdiff_t pathBytes,
|
|||
if (path[i - 1] == '/') {
|
||||
if (!containingFolderEnd) {
|
||||
containingFolderEnd = path + i - 1;
|
||||
*name = path + i;
|
||||
*nameBytes = pathBytes - i;
|
||||
|
||||
if (~application->permissions & APPLICATION_PERMISSION_ALL_FILES) {
|
||||
*name = path + i;
|
||||
*nameBytes = pathBytes - i;
|
||||
}
|
||||
} else {
|
||||
*containingFolder = path + i;
|
||||
*containingFolderBytes = containingFolderEnd - *containingFolder;
|
||||
|
@ -1624,11 +1628,11 @@ void PathGetNameAndContainingFolder(const char *path, ptrdiff_t pathBytes,
|
|||
}
|
||||
}
|
||||
|
||||
void *OpenDocumentGetRenameMessageData(const char *path, size_t pathBytes, size_t *bytes) {
|
||||
void *OpenDocumentGetRenameMessageData(InstalledApplication *application, const char *path, size_t pathBytes, size_t *bytes) {
|
||||
EsVolumeInformation volumeInformation;
|
||||
const char *name, *containingFolder;
|
||||
ptrdiff_t nameBytes, containingFolderBytes;
|
||||
PathGetNameAndContainingFolder(path, pathBytes, &name, &nameBytes, &containingFolder, &containingFolderBytes, &volumeInformation);
|
||||
PathGetNameAndContainingFolder(application, path, pathBytes, &name, &nameBytes, &containingFolder, &containingFolderBytes, &volumeInformation);
|
||||
*bytes = sizeof(ptrdiff_t) * 2 + nameBytes + containingFolderBytes;
|
||||
uint8_t *data = (uint8_t *) EsHeapAllocate(*bytes, false);
|
||||
EsMemoryCopy(data, &nameBytes, sizeof(ptrdiff_t));
|
||||
|
@ -1638,6 +1642,24 @@ void *OpenDocumentGetRenameMessageData(const char *path, size_t pathBytes, size_
|
|||
return data;
|
||||
}
|
||||
|
||||
void ApplicationTemporaryDestroy(InstalledApplication *application) {
|
||||
if (!application->temporary) return;
|
||||
|
||||
for (uintptr_t i = 0; i < desktop.installedApplications.Length(); i++) {
|
||||
if (desktop.installedApplications[i] == application) {
|
||||
desktop.installedApplications.Delete(i);
|
||||
EsHeapFree(application->cName);
|
||||
EsHeapFree(application->cExecutable);
|
||||
EsHeapFree(application->settingsPath);
|
||||
EsHeapFree(application);
|
||||
// TODO Delete the settings folder.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
EsAssert(false);
|
||||
}
|
||||
|
||||
bool ApplicationInstanceStart(int64_t applicationID, _EsApplicationStartupInformation *startupInformation, ApplicationInstance *instance) {
|
||||
if (desktop.inShutdown) {
|
||||
return false;
|
||||
|
@ -1706,6 +1728,7 @@ bool ApplicationInstanceStart(int64_t applicationID, _EsApplicationStartupInform
|
|||
ES_FILE_READ | ES_NODE_FAIL_IF_NOT_FOUND, &executableNode);
|
||||
|
||||
if (ES_CHECK_ERROR(error)) {
|
||||
ApplicationTemporaryDestroy(application);
|
||||
_EsApplicationStartupInformation s = {};
|
||||
s.data = CRASHED_TAB_INVALID_EXECUTABLE;
|
||||
return ApplicationInstanceStart(APPLICATION_ID_DESKTOP_CRASHED, &s, instance);
|
||||
|
@ -1817,6 +1840,7 @@ bool ApplicationInstanceStart(int64_t applicationID, _EsApplicationStartupInform
|
|||
process->application = application;
|
||||
desktop.allApplicationProcesses.Add(process);
|
||||
} else {
|
||||
ApplicationTemporaryDestroy(application);
|
||||
_EsApplicationStartupInformation s = {};
|
||||
s.data = CRASHED_TAB_INVALID_EXECUTABLE;
|
||||
return ApplicationInstanceStart(APPLICATION_ID_DESKTOP_CRASHED, &s, instance);
|
||||
|
@ -1845,12 +1869,11 @@ bool ApplicationInstanceStart(int64_t applicationID, _EsApplicationStartupInform
|
|||
|
||||
EsMessage m = { ES_MSG_INSTANCE_CREATE };
|
||||
|
||||
if (~startupInformation->flags & ES_APPLICATION_STARTUP_MANUAL_PATH) {
|
||||
PathGetNameAndContainingFolder(startupInformation->filePath, startupInformation->filePathBytes,
|
||||
&startupInformation->filePath, &startupInformation->filePathBytes,
|
||||
&startupInformation->containingFolder, &startupInformation->containingFolderBytes,
|
||||
&volumeInformation);
|
||||
}
|
||||
PathGetNameAndContainingFolder(application,
|
||||
startupInformation->filePath, startupInformation->filePathBytes,
|
||||
&startupInformation->filePath, &startupInformation->filePathBytes,
|
||||
&startupInformation->containingFolder, &startupInformation->containingFolderBytes,
|
||||
&volumeInformation);
|
||||
|
||||
// Share handles to the file and the startup information buffer.
|
||||
|
||||
|
@ -1904,24 +1927,6 @@ ApplicationInstance *ApplicationInstanceCreate(int64_t id, _EsApplicationStartup
|
|||
}
|
||||
}
|
||||
|
||||
void ApplicationTemporaryDestroy(InstalledApplication *application) {
|
||||
if (!application->temporary) return;
|
||||
|
||||
for (uintptr_t i = 0; i < desktop.installedApplications.Length(); i++) {
|
||||
if (desktop.installedApplications[i] == application) {
|
||||
desktop.installedApplications.Delete(i);
|
||||
EsHeapFree(application->cName);
|
||||
EsHeapFree(application->cExecutable);
|
||||
EsHeapFree(application->settingsPath);
|
||||
EsHeapFree(application);
|
||||
// TODO Delete the settings folder.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
EsAssert(false);
|
||||
}
|
||||
|
||||
ApplicationProcess *ApplicationProcessFindByPID(EsObjectID pid, bool removeIfFound = false) {
|
||||
for (uintptr_t i = 0; i < desktop.allApplicationProcesses.Length(); i++) {
|
||||
ApplicationProcess *process = desktop.allApplicationProcesses[i];
|
||||
|
@ -2180,7 +2185,7 @@ void ApplicationInstanceRequestSave(ApplicationInstance *instance, const char *n
|
|||
{
|
||||
// Tell the instance the chosen name and new containing folder for the document.
|
||||
EsMessage m = { ES_MSG_INSTANCE_DOCUMENT_RENAMED };
|
||||
void *data = OpenDocumentGetRenameMessageData(name, nameBytes, &m.tabOperation.bytes);
|
||||
void *data = OpenDocumentGetRenameMessageData(instance->application, name, nameBytes, &m.tabOperation.bytes);
|
||||
m.tabOperation.id = instance->embeddedWindowID;
|
||||
m.tabOperation.handle = EsConstantBufferCreate(data, m.tabOperation.bytes, instance->process->handle);
|
||||
EsMessagePostRemote(instance->process->handle, &m);
|
||||
|
@ -2219,47 +2224,47 @@ void InstanceAnnouncePathMoved(InstalledApplication *fromApplication, const char
|
|||
// TODO Update the location of installed applications and other things in the configuration.
|
||||
// TODO Replace fromApplication with something better.
|
||||
|
||||
EsObjectID documentID = 0;
|
||||
if (oldPathBytes) {
|
||||
EsObjectID documentID = 0;
|
||||
|
||||
for (uintptr_t i = 0; i < desktop.openDocuments.Count(); i++) {
|
||||
OpenDocument *document = &desktop.openDocuments[i];
|
||||
for (uintptr_t i = 0; i < desktop.openDocuments.Count(); i++) {
|
||||
OpenDocument *document = &desktop.openDocuments[i];
|
||||
|
||||
if (document->pathBytes >= oldPathBytes
|
||||
&& 0 == EsMemoryCompare(document->path, oldPath, oldPathBytes)
|
||||
&& (oldPathBytes == document->pathBytes || document->path[oldPathBytes] == '/')) {
|
||||
if (document->pathBytes == oldPathBytes) documentID = document->id;
|
||||
char *newDocumentPath = (char *) EsHeapAllocate(document->pathBytes - oldPathBytes + newPathBytes, false);
|
||||
EsMemoryCopy(newDocumentPath, newPath, newPathBytes);
|
||||
EsMemoryCopy(newDocumentPath + newPathBytes, document->path + oldPathBytes, document->pathBytes - oldPathBytes);
|
||||
document->pathBytes += newPathBytes - oldPathBytes;
|
||||
EsHeapFree(document->path);
|
||||
document->path = newDocumentPath;
|
||||
if (document->pathBytes >= oldPathBytes
|
||||
&& 0 == EsMemoryCompare(document->path, oldPath, oldPathBytes)
|
||||
&& (oldPathBytes == document->pathBytes || document->path[oldPathBytes] == '/')) {
|
||||
if (document->pathBytes == oldPathBytes) documentID = document->id;
|
||||
char *newDocumentPath = (char *) EsHeapAllocate(document->pathBytes - oldPathBytes + newPathBytes, false);
|
||||
EsMemoryCopy(newDocumentPath, newPath, newPathBytes);
|
||||
EsMemoryCopy(newDocumentPath + newPathBytes, document->path + oldPathBytes, document->pathBytes - oldPathBytes);
|
||||
document->pathBytes += newPathBytes - oldPathBytes;
|
||||
EsHeapFree(document->path);
|
||||
document->path = newDocumentPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (documentID) {
|
||||
for (uintptr_t i = 0; i < desktop.allApplicationInstances.Length(); i++) {
|
||||
ApplicationInstance *instance = desktop.allApplicationInstances[i];
|
||||
|
||||
if (instance->documentID != documentID) continue;
|
||||
if (instance->application == fromApplication) continue;
|
||||
if (!instance->process) continue;
|
||||
|
||||
size_t messageDataBytes;
|
||||
void *messageData = OpenDocumentGetRenameMessageData(instance->application, newPath, newPathBytes, &messageDataBytes);
|
||||
|
||||
EsMessage m = { ES_MSG_INSTANCE_DOCUMENT_RENAMED };
|
||||
m.tabOperation.id = instance->embeddedWindowID;
|
||||
m.tabOperation.handle = EsConstantBufferCreate(messageData, messageDataBytes, instance->process->handle);
|
||||
m.tabOperation.bytes = messageDataBytes;
|
||||
EsMessagePostRemote(instance->process->handle, &m);
|
||||
|
||||
EsHeapFree(messageData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!documentID) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t messageDataBytes;
|
||||
void *messageData = OpenDocumentGetRenameMessageData(newPath, newPathBytes, &messageDataBytes);
|
||||
|
||||
for (uintptr_t i = 0; i < desktop.allApplicationInstances.Length(); i++) {
|
||||
ApplicationInstance *instance = desktop.allApplicationInstances[i];
|
||||
|
||||
if (instance->documentID != documentID) continue;
|
||||
if (instance->application == fromApplication) continue;
|
||||
if (!instance->process) continue;
|
||||
|
||||
EsMessage m = { ES_MSG_INSTANCE_DOCUMENT_RENAMED };
|
||||
m.tabOperation.id = instance->embeddedWindowID;
|
||||
m.tabOperation.handle = EsConstantBufferCreate(messageData, messageDataBytes, instance->process->handle);
|
||||
m.tabOperation.bytes = messageDataBytes;
|
||||
EsMessagePostRemote(instance->process->handle, &m);
|
||||
}
|
||||
|
||||
EsHeapFree(messageData);
|
||||
|
||||
if (fromApplication != desktop.fileManager && desktop.fileManager && desktop.fileManager->singleProcess) {
|
||||
char *data = (char *) EsHeapAllocate(sizeof(size_t) * 2 + oldPathBytes + newPathBytes, false);
|
||||
EsMemoryCopy(data + 0, &oldPathBytes, sizeof(size_t));
|
||||
|
@ -2905,6 +2910,28 @@ void DesktopSyscall(EsMessage *message, uint8_t *buffer, EsBuffer *pipe) {
|
|||
EsBufferWrite(pipe, document->path, document->pathBytes);
|
||||
}
|
||||
}
|
||||
} else if (buffer[0] == DESKTOP_MSG_RUN_TEMPORARY_APPLICATION) {
|
||||
InstalledApplication *requestingApplication = ApplicationFindByPID(message->desktop.processID);
|
||||
|
||||
if (requestingApplication && (requestingApplication->permissions & APPLICATION_PERMISSION_RUN_TEMPORARY_APPLICATION)) {
|
||||
InstalledApplication *application = (InstalledApplication *) EsHeapAllocate(sizeof(InstalledApplication), true);
|
||||
application->temporary = true;
|
||||
application->hidden = true;
|
||||
application->useSingleProcess = true;
|
||||
application->cExecutable = (char *) EsHeapAllocate(message->desktop.bytes, false);
|
||||
EsMemoryCopy(application->cExecutable, buffer + 1, message->desktop.bytes - 1);
|
||||
application->cExecutable[message->desktop.bytes - 1] = 0;
|
||||
static int64_t nextTemporaryID = -1;
|
||||
application->id = nextTemporaryID--;
|
||||
application->cName = (char *) EsHeapAllocate(32, false);
|
||||
for (int i = 1; i < 31; i++) application->cName[i] = (EsRandomU8() % 26) + 'a';
|
||||
application->cName[0] = '_', application->cName[31] = 0;
|
||||
EsHandle handle;
|
||||
EsError error = TemporaryFileCreate(&handle, &application->settingsPath, &application->settingsPathBytes, ES_NODE_DIRECTORY);
|
||||
if (error == ES_SUCCESS) EsHandleClose(handle);
|
||||
desktop.installedApplications.Add(application);
|
||||
ApplicationInstanceCreate(application->id, nullptr, nullptr);
|
||||
}
|
||||
} else if (!instance) {
|
||||
// -------------------------------------------------
|
||||
// | Messages below here require a valid instance. |
|
||||
|
@ -2990,31 +3017,10 @@ void DesktopSyscall(EsMessage *message, uint8_t *buffer, EsBuffer *pipe) {
|
|||
|
||||
if (document) {
|
||||
_EsApplicationStartupInformation startupInformation = {};
|
||||
startupInformation.flags = ES_APPLICATION_STARTUP_MANUAL_PATH;
|
||||
startupInformation.filePath = document->path;
|
||||
startupInformation.filePathBytes = document->pathBytes;
|
||||
ApplicationInstanceCreate(desktop.fileManager->id, &startupInformation, instance->tab->container);
|
||||
}
|
||||
} else if (buffer[0] == DESKTOP_MSG_RUN_TEMPORARY_APPLICATION) {
|
||||
if (instance->application && (instance->application->permissions & APPLICATION_PERMISSION_RUN_TEMPORARY_APPLICATION)) {
|
||||
InstalledApplication *application = (InstalledApplication *) EsHeapAllocate(sizeof(InstalledApplication), true);
|
||||
application->temporary = true;
|
||||
application->hidden = true;
|
||||
application->useSingleProcess = true;
|
||||
application->cExecutable = (char *) EsHeapAllocate(message->desktop.bytes, false);
|
||||
EsMemoryCopy(application->cExecutable, buffer + 1, message->desktop.bytes - 1);
|
||||
application->cExecutable[message->desktop.bytes - 1] = 0;
|
||||
static int64_t nextTemporaryID = -1;
|
||||
application->id = nextTemporaryID--;
|
||||
application->cName = (char *) EsHeapAllocate(32, false);
|
||||
for (int i = 1; i < 31; i++) application->cName[i] = (EsRandomU8() % 26) + 'a';
|
||||
application->cName[0] = '_', application->cName[31] = 0;
|
||||
EsHandle handle;
|
||||
EsError error = TemporaryFileCreate(&handle, &application->settingsPath, &application->settingsPathBytes, ES_NODE_DIRECTORY);
|
||||
if (error == ES_SUCCESS) EsHandleClose(handle);
|
||||
desktop.installedApplications.Add(application);
|
||||
ApplicationInstanceCreate(application->id, nullptr, nullptr);
|
||||
}
|
||||
} else {
|
||||
EsPrint("DesktopSyscall - Received unhandled message %d.\n", buffer[0]);
|
||||
}
|
||||
|
|
|
@ -4446,11 +4446,9 @@ void EsButtonSetIconFromBits(EsButton *button, const uint32_t *bits, size_t widt
|
|||
}
|
||||
}
|
||||
|
||||
void EsButtonOnCommand(EsButton *button, EsCommandCallback onCommand, EsCommand *command) {
|
||||
void EsButtonOnCommand(EsButton *button, EsCommandCallback onCommand) {
|
||||
EsMessageMutexCheck();
|
||||
|
||||
button->onCommand = onCommand;
|
||||
button->command = command;
|
||||
}
|
||||
|
||||
void EsButtonSetCheckBuddy(EsButton *button, EsElement *checkBuddy) {
|
||||
|
@ -5949,8 +5947,7 @@ void FileMenuRename(EsInstance *_instance, EsElement *, EsCommand *) {
|
|||
ptrdiff_t initialNameBytes = 0;
|
||||
|
||||
if (instance->startupInformation && instance->startupInformation->filePathBytes) {
|
||||
initialName = instance->startupInformation->filePath;
|
||||
initialNameBytes = instance->startupInformation->filePathBytes;
|
||||
PathGetName(instance->startupInformation->filePath, instance->startupInformation->filePathBytes, &initialName, &initialNameBytes);
|
||||
} else {
|
||||
EsInstanceClassEditorSettings *editorSettings = &instance->editorSettings;
|
||||
initialName = editorSettings->newDocumentFileName;
|
||||
|
@ -6007,8 +6004,10 @@ void EsFileMenuCreate(EsInstance *_instance, EsElement *element, uint64_t menuFl
|
|||
EsTextDisplayCreate(panel3, ES_CELL_H_FILL, ES_STYLE_TEXT_LABEL,
|
||||
editorSettings->newDocumentTitle, editorSettings->newDocumentTitleBytes);
|
||||
} else {
|
||||
EsTextDisplayCreate(panel3, ES_CELL_H_FILL, ES_STYLE_TEXT_LABEL,
|
||||
instance->startupInformation->filePath, instance->startupInformation->filePathBytes);
|
||||
const char *name;
|
||||
ptrdiff_t nameBytes;
|
||||
PathGetName(instance->startupInformation->filePath, instance->startupInformation->filePathBytes, &name, &nameBytes);
|
||||
EsTextDisplayCreate(panel3, ES_CELL_H_FILL, ES_STYLE_TEXT_LABEL, name, nameBytes);
|
||||
}
|
||||
|
||||
EsButton *renameButton = EsButtonCreate(panel3, ES_BUTTON_TOOLBAR);
|
||||
|
@ -6484,6 +6483,10 @@ void EsElementFocus(EsElement *element, uint32_t flags) {
|
|||
|
||||
if (window->focused == element) return;
|
||||
|
||||
// If an element is already focused and the request is only to focus the element if there is no focused element, ignore the request.
|
||||
|
||||
if ((flags & ES_ELEMENT_FOCUS_ONLY_IF_NO_FOCUSED_ELEMENT) && window->focused) return;
|
||||
|
||||
// Tell the previously focused element it's no longer focused.
|
||||
|
||||
EsElement *oldFocus = window->focused;
|
||||
|
@ -6708,6 +6711,10 @@ bool EsElementIsHidden(EsElement *element) {
|
|||
void EsElementSetDisabled(EsElement *element, bool disabled) {
|
||||
EsMessageMutexCheck();
|
||||
|
||||
if (element->window->focused == element) {
|
||||
UIRemoveFocusFromElement(element);
|
||||
}
|
||||
|
||||
for (uintptr_t i = 0; i < element->GetChildCount(); i++) {
|
||||
if (element->GetChild(i)->flags & ES_ELEMENT_NON_CLIENT) continue;
|
||||
EsElementSetDisabled(element->GetChild(i), disabled);
|
||||
|
@ -7808,6 +7815,8 @@ void UIProcessWindowManagerMessage(EsWindow *window, EsMessage *message, Process
|
|||
gui.leftModifiers = message->windowActivated.leftModifiers;
|
||||
gui.rightModifiers = message->windowActivated.rightModifiers;
|
||||
|
||||
UIMouseUp(window, nullptr, false);
|
||||
|
||||
if (!window->activated) {
|
||||
AccessKeyModeExit();
|
||||
|
||||
|
|
|
@ -1320,7 +1320,7 @@ struct EsListView : EsElement {
|
|||
EsRectangle bounds = element->GetBounds();
|
||||
child->InternalMove(bounds.r - bounds.l, bounds.b - bounds.t, bounds.l, bounds.t);
|
||||
}
|
||||
} else if (message->type == ES_MSG_MOUSE_LEFT_DOWN) {
|
||||
} else if (message->type == ES_MSG_MOUSE_LEFT_DOWN || message->type == ES_MSG_MOUSE_MIDDLE_CLICK) {
|
||||
EsElementFocus(this);
|
||||
|
||||
if (hasFocusedItem) {
|
||||
|
@ -1337,14 +1337,23 @@ struct EsListView : EsElement {
|
|||
focusedItemIndex = item->index;
|
||||
element->customStyleState |= THEME_STATE_FOCUSED_ITEM;
|
||||
|
||||
if (message->mouseDown.clickChainCount == 1 || (~element->customStyleState & THEME_STATE_SELECTED)) {
|
||||
if (message->type == ES_MSG_MOUSE_MIDDLE_CLICK) {
|
||||
Select(item->group, item->index, false, false, false);
|
||||
EsMessage m = { ES_MSG_LIST_VIEW_CHOOSE_ITEM };
|
||||
m.chooseItem.group = item->group;
|
||||
m.chooseItem.index = item->index;
|
||||
m.chooseItem.source = ES_LIST_VIEW_CHOOSE_ITEM_MIDDLE_CLICK;
|
||||
EsMessageSend(this, &m);
|
||||
} else if (message->mouseDown.clickChainCount == 1 || (~element->customStyleState & THEME_STATE_SELECTED)) {
|
||||
Select(item->group, item->index, EsKeyboardIsShiftHeld(), EsKeyboardIsCtrlHeld(), false);
|
||||
} else if (message->mouseDown.clickChainCount == 2) {
|
||||
EsMessage m = { ES_MSG_LIST_VIEW_CHOOSE_ITEM };
|
||||
m.chooseItem.group = item->group;
|
||||
m.chooseItem.index = item->index;
|
||||
m.chooseItem.source = ES_LIST_VIEW_CHOOSE_ITEM_DOUBLE_CLICK;
|
||||
EsMessageSend(this, &m);
|
||||
}
|
||||
} else if (message->type == ES_MSG_MOUSE_MIDDLE_DOWN) {
|
||||
} else if (message->type == ES_MSG_MOUSE_RIGHT_DOWN) {
|
||||
EsMessage m = { ES_MSG_LIST_VIEW_IS_SELECTED };
|
||||
m.selectItem.index = item->index;
|
||||
|
@ -1602,6 +1611,7 @@ struct EsListView : EsElement {
|
|||
EsMessage m = { ES_MSG_LIST_VIEW_CHOOSE_ITEM };
|
||||
m.chooseItem.group = focusedItemGroup;
|
||||
m.chooseItem.index = focusedItemIndex;
|
||||
m.chooseItem.source = ES_LIST_VIEW_CHOOSE_ITEM_ENTER;
|
||||
EsMessageSend(this, &m);
|
||||
return true;
|
||||
} else if (!ctrl && !alt) {
|
||||
|
|
|
@ -715,10 +715,10 @@ define ES_DRIVE_TYPE_SSD (2)
|
|||
define ES_DRIVE_TYPE_CDROM (3)
|
||||
define ES_DRIVE_TYPE_USB_MASS_STORAGE (4)
|
||||
|
||||
define ES_ELEMENT_FOCUS_ENSURE_VISIBLE (1 << 0)
|
||||
define ES_ELEMENT_FOCUS_FROM_KEYBOARD (1 << 1)
|
||||
define ES_ELEMENT_FOCUS_ENSURE_VISIBLE (1 << 0)
|
||||
define ES_ELEMENT_FOCUS_FROM_KEYBOARD (1 << 1)
|
||||
define ES_ELEMENT_FOCUS_ONLY_IF_NO_FOCUSED_ELEMENT (1 << 2)
|
||||
|
||||
define ES_APPLICATION_STARTUP_MANUAL_PATH (1 << 0)
|
||||
define ES_APPLICATION_STARTUP_BACKGROUND_SERVICE (1 << 1)
|
||||
define ES_APPLICATION_STARTUP_IN_SAME_CONTAINER (1 << 2)
|
||||
|
||||
|
@ -772,6 +772,11 @@ private define ES_SCROLL_MANUAL (1 << 2) // The parent is responsible for updati
|
|||
define ES_SUBSYSTEM_ID_NATIVE (0)
|
||||
define ES_SUBSYSTEM_ID_POSIX (1)
|
||||
|
||||
define ES_LIST_VIEW_CHOOSE_ITEM_OTHER (1)
|
||||
define ES_LIST_VIEW_CHOOSE_ITEM_ENTER (2)
|
||||
define ES_LIST_VIEW_CHOOSE_ITEM_DOUBLE_CLICK (3)
|
||||
define ES_LIST_VIEW_CHOOSE_ITEM_MIDDLE_CLICK (4)
|
||||
|
||||
include desktop/icons.header
|
||||
|
||||
enum EsFatalError {
|
||||
|
@ -1434,13 +1439,13 @@ struct EsCommand {
|
|||
|
||||
struct EsApplicationStartupRequest {
|
||||
int64_t id;
|
||||
STRING filePath;
|
||||
STRING filePath; // Only contains the file name if the application does not have permission_all_files or the file is not on a file system.
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
private struct _EsApplicationStartupInformation {
|
||||
int64_t id;
|
||||
STRING filePath;
|
||||
STRING filePath; // See comment in EsApplicationStartupRequest.
|
||||
EsWindow *targetWindow;
|
||||
uint32_t flags;
|
||||
int32_t data;
|
||||
|
@ -1700,6 +1705,7 @@ struct EsMessageSelectItem {
|
|||
struct EsMessageChooseItem {
|
||||
EsListViewIndex group;
|
||||
EsListViewIndex index;
|
||||
uint8_t source;
|
||||
};
|
||||
|
||||
struct EsMessageSearchItem {
|
||||
|
@ -1759,13 +1765,13 @@ struct EsMessageEndEdit {
|
|||
|
||||
struct EsMessageInstanceOpen {
|
||||
EsFileStore *file;
|
||||
STRING name;
|
||||
STRING nameOrPath; // See comment in EsApplicationStartupRequest.
|
||||
bool update;
|
||||
};
|
||||
|
||||
struct EsMessageInstanceSave {
|
||||
EsFileStore *file;
|
||||
STRING name;
|
||||
STRING nameOrPath; // See comment in EsApplicationStartupRequest.
|
||||
};
|
||||
|
||||
// Internal system messages.
|
||||
|
@ -1990,8 +1996,8 @@ function_pointer void EsWorkCallback(EsGeneric context);
|
|||
|
||||
// System.
|
||||
|
||||
function void EsApplicationStart(ES_INSTANCE_TYPE *instance, const EsApplicationStartupRequest *request);
|
||||
function void EsApplicationRunTemporary(ES_INSTANCE_TYPE *instance, STRING path);
|
||||
function void EsApplicationStart(ES_INSTANCE_TYPE *instance, const EsApplicationStartupRequest *request); // The instance is optional, used only for ES_APPLICATION_STARTUP_IN_SAME_CONTAINER.
|
||||
function void EsApplicationRunTemporary(STRING path);
|
||||
function EsHandle EsTakeSystemSnapshot(int type, size_t *bufferSize);
|
||||
function EsInstance *_EsInstanceCreate(size_t bytes, EsMessage *message, STRING name = BLANK_STRING);
|
||||
function EsError EsHandleClose(EsHandle handle);
|
||||
|
@ -1999,7 +2005,7 @@ function void EsSystemShowShutdownDialog();
|
|||
|
||||
function void EsPOSIXInitialise(int *argc, char ***argv);
|
||||
function long EsPOSIXSystemCall(long n, long a1, long a2, long a3, long a4, long a5, long a6);
|
||||
function char *EsPOSIXConvertPath(const char *path, size_t *outNameLength, bool addPOSIXMountPointPrefix);
|
||||
function char *EsPOSIXConvertPath(const char *path, size_t *outNameLength, bool addPOSIXMountPointPrefix); // Converts a POSIX path to a native path. Free with EsHeapFree.
|
||||
|
||||
private function void EsBatch(EsBatchCall *calls, size_t count);
|
||||
private function uintptr_t _EsSyscall(uintptr_t a, uintptr_t b, uintptr_t c, uintptr_t d, uintptr_t e, uintptr_t f);
|
||||
|
@ -2060,7 +2066,7 @@ function void *EsFileStoreMap(EsFileStore *file, size_t *fileSize, uint32_t flag
|
|||
function bool EsMountPointGetVolumeInformation(const char *prefix, size_t prefixBytes, EsVolumeInformation *information); // Returns false if the mount point does not exist.
|
||||
function void EsMountPointEnumerate(EsMountPointEnumerationCallback callback, EsGeneric context);
|
||||
function void EsOpenDocumentQueryInformation(STRING filePath, EsOpenDocumentInformation *information);
|
||||
function void _EsPathAnnouncePathMoved(STRING oldPath, STRING newPath);
|
||||
function void _EsPathAnnouncePathMoved(STRING oldPath, STRING newPath); // Set oldPathBytes = 0 to make the file manager refresh the path.
|
||||
function void _EsOpenDocumentEnumerate(EsBuffer *outputBuffer);
|
||||
|
||||
function void EsDeviceEnumerate(EsDeviceEnumerationCallback callback, EsGeneric context);
|
||||
|
@ -2072,6 +2078,7 @@ function EsError EsProcessCreate(const EsProcessCreationArguments *arguments, Es
|
|||
function int EsProcessGetExitStatus(EsHandle process);
|
||||
function EsObjectID EsProcessGetID(EsHandle process);
|
||||
function void EsProcessGetState(EsHandle process, EsProcessState *state);
|
||||
function void EsProcessGetCreateData(EsProcessCreateData *data); // For the current process.
|
||||
function EsHandle EsProcessOpen(EsObjectID pid);
|
||||
function void EsProcessPause(EsHandle process, bool resume);
|
||||
function void EsProcessTerminate(EsHandle process, int status);
|
||||
|
@ -2498,7 +2505,7 @@ 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);
|
||||
function EsCheckState EsButtonGetCheck(EsButton *button);
|
||||
function void EsButtonOnCommand(EsButton *button, EsCommandCallback callback, EsCommand *command = ES_NULL); // TODO Public property?
|
||||
function void EsButtonOnCommand(EsButton *button, EsCommandCallback callback); // TODO Public property?
|
||||
function void EsButtonSetCheckBuddy(EsButton *button, EsElement *checkBuddy); // The buddy element is enabled/disabled when the button is checked/unchecked.
|
||||
function EsElement *EsButtonGetCheckBuddy(EsButton *button); // TODO Public property?
|
||||
|
||||
|
@ -2524,7 +2531,8 @@ function void EsTextboxSetTextSize(EsTextbox *textbox, uint16_t size);
|
|||
function void EsTextboxSetFont(EsTextbox *textbox, EsFont font);
|
||||
function void EsTextboxSetupSyntaxHighlighting(EsTextbox *textbox, uint32_t language, uint32_t *customColors = ES_NULL, size_t customColorCount = 0);
|
||||
function void EsTextboxStartEdit(EsTextbox *textbox);
|
||||
function void EsTextboxEnableSmartQuotes(EsTextbox *textbox, bool enabled);
|
||||
function void EsTextboxEnableSmartReplacement(EsTextbox *textbox, bool enabled); // e.g. smart quotes.
|
||||
function void EsTextboxSetReadOnly(EsTextbox *textbox, bool readOnly); // Prevents the user from modifying the contents of the textbox; EsTextboxInsert will still work. Incompatible with ES_TEXTBOX_EDIT_BASED. Undo events will not be created.
|
||||
|
||||
// Sliders.
|
||||
|
||||
|
|
|
@ -49,6 +49,11 @@ struct ChildProcess {
|
|||
|
||||
char *workingDirectory;
|
||||
Array<ChildProcess> childProcesses;
|
||||
Array<void *> _argv;
|
||||
|
||||
#ifdef ES_ARCH_X86_64
|
||||
Elf64_Phdr *tlsHeader;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_BUILD
|
||||
double syscallTimeSpent[1024];
|
||||
|
@ -680,9 +685,11 @@ long EsPOSIXSystemCall(long n, long a1, long a2, long a3, long a4, long a5, long
|
|||
}
|
||||
|
||||
#ifdef DEBUG_BUILD
|
||||
double endTime = EsTimeStampMs();
|
||||
syscallTimeSpent[n] += endTime - startTime;
|
||||
syscallCallCount[n]++;
|
||||
if ((uintptr_t) n < sizeof(syscallTimeSpent) / sizeof(syscallTimeSpent[0])) {
|
||||
double endTime = EsTimeStampMs();
|
||||
syscallTimeSpent[n] += endTime - startTime;
|
||||
syscallCallCount[n]++;
|
||||
}
|
||||
#endif
|
||||
|
||||
// EsPrint(":: %z %x %x %x -> %x; %Fms\n", syscallNames[n], a1, a2, a3, returnValue, endTime - startTime);
|
||||
|
@ -709,7 +716,6 @@ void EsPOSIXInitialise(int *argc, char ***argv) {
|
|||
|
||||
uintptr_t position = 0;
|
||||
char *start = environmentBuffer;
|
||||
Array<void *> _argv = {};
|
||||
*argc = 0;
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
|
@ -750,7 +756,7 @@ void EsPOSIXInitialise(int *argc, char ***argv) {
|
|||
// Add the auxillary vectors.
|
||||
|
||||
#ifdef ES_ARCH_X86_64
|
||||
Elf64_Phdr *tlsHeader = (Elf64_Phdr *) EsHeapAllocate(sizeof(Elf64_Phdr), true);
|
||||
tlsHeader = (Elf64_Phdr *) EsHeapAllocate(sizeof(Elf64_Phdr), true);
|
||||
tlsHeader->p_type = PT_TLS;
|
||||
tlsHeader->p_flags = 4 /* read */;
|
||||
tlsHeader->p_vaddr = startupInformation->tlsImageStart;
|
||||
|
@ -778,4 +784,11 @@ void EsPOSIXInitialise(int *argc, char ***argv) {
|
|||
*argv = (char **) _argv.array;
|
||||
}
|
||||
|
||||
void POSIXCleanup() {
|
||||
_argv.Free();
|
||||
childProcesses.Free();
|
||||
EsHeapFree(workingDirectory);
|
||||
EsHeapFree(tlsHeader);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -121,3 +121,4 @@ define ES_STYLE_TOOLBAR_BUTTON_GROUP_SEPARATOR (ES_STYLE_CAST(7))
|
|||
define ES_STYLE_TOOLBAR_SPACER_SMALL (ES_STYLE_CAST(3))
|
||||
define ES_STYLE_LIST_CHOICE_ITEM_2X (ES_STYLE_CAST(9))
|
||||
define ES_STYLE_TEXTBOX_BORDERED_SINGLE_MEDIUM (ES_STYLE_CAST(11))
|
||||
define ES_STYLE_SEPARATOR_VERTICAL (ES_STYLE_CAST(13))
|
||||
|
|
|
@ -118,6 +118,10 @@ int EsProcessGetExitStatus(EsHandle process) {
|
|||
return EsSyscall(ES_SYSCALL_PROCESS_GET_STATUS, process, 0, 0, 0);
|
||||
}
|
||||
|
||||
void EsProcessGetCreateData(EsProcessCreateData *data) {
|
||||
EsMemoryCopy(data, &api.startupInformation->data, sizeof(EsProcessCreateData));
|
||||
}
|
||||
|
||||
void ThreadInitialise(ThreadLocalStorage *local);
|
||||
|
||||
__attribute__((no_instrument_function))
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
// This file is part of the Essence operating system.
|
||||
// It is released under the terms of the MIT license -- see LICENSE.md.
|
||||
// Written by: nakst.
|
||||
|
||||
// TODO Caret blinking.
|
||||
// TODO Wrapped lines.
|
||||
// TODO Unicode grapheme/word boundaries.
|
||||
|
@ -53,6 +57,8 @@ struct EsTextbox : EsElement {
|
|||
|
||||
int verticalMotionHorizontalDepth;
|
||||
int oldHorizontalScroll;
|
||||
bool inRightClickDrag;
|
||||
bool colorUppercase;
|
||||
|
||||
EsUndoManager *undo;
|
||||
EsUndoManager localUndo;
|
||||
|
@ -67,12 +73,8 @@ struct EsTextbox : EsElement {
|
|||
uint32_t syntaxHighlightingLanguage;
|
||||
uint32_t syntaxHighlightingColors[8];
|
||||
|
||||
bool smartQuotes;
|
||||
|
||||
bool inRightClickDrag;
|
||||
|
||||
// For smart context menus:
|
||||
bool colorUppercase;
|
||||
bool smartReplacement;
|
||||
bool readOnly;
|
||||
};
|
||||
|
||||
#define MOVE_CARET_SINGLE (2)
|
||||
|
@ -364,7 +366,7 @@ void TextboxUpdateCommands(EsTextbox *textbox, bool noClipboard) {
|
|||
|
||||
command = EsCommandByID(textbox->instance, ES_COMMAND_DELETE);
|
||||
command->data = textbox;
|
||||
EsCommandSetDisabled(command, selectionEmpty);
|
||||
EsCommandSetDisabled(command, selectionEmpty || textbox->readOnly);
|
||||
|
||||
EsCommandSetCallback(command, [] (EsInstance *, EsElement *, EsCommand *command) {
|
||||
EsTextbox *textbox = (EsTextbox *) command->data.p;
|
||||
|
@ -397,7 +399,7 @@ void TextboxUpdateCommands(EsTextbox *textbox, bool noClipboard) {
|
|||
|
||||
command = EsCommandByID(textbox->instance, ES_COMMAND_CUT);
|
||||
command->data = textbox;
|
||||
EsCommandSetDisabled(command, selectionEmpty);
|
||||
EsCommandSetDisabled(command, selectionEmpty || textbox->readOnly);
|
||||
|
||||
EsCommandSetCallback(command, [] (EsInstance *, EsElement *, EsCommand *command) {
|
||||
EsTextbox *textbox = (EsTextbox *) command->data.p;
|
||||
|
@ -422,7 +424,7 @@ void TextboxUpdateCommands(EsTextbox *textbox, bool noClipboard) {
|
|||
if (!noClipboard) {
|
||||
command = EsCommandByID(textbox->instance, ES_COMMAND_PASTE);
|
||||
command->data = textbox;
|
||||
EsCommandSetDisabled(command, !EsClipboardHasText(ES_CLIPBOARD_PRIMARY));
|
||||
EsCommandSetDisabled(command, textbox->readOnly || !EsClipboardHasText(ES_CLIPBOARD_PRIMARY));
|
||||
|
||||
EsCommandSetCallback(command, [] (EsInstance *, EsElement *, EsCommand *command) {
|
||||
EsTextbox *textbox = (EsTextbox *) command->data.p;
|
||||
|
@ -790,14 +792,12 @@ void EsTextboxSelectAll(EsTextbox *textbox) {
|
|||
|
||||
void EsTextboxClear(EsTextbox *textbox, bool sendUpdatedMessage) {
|
||||
EsMessageMutexCheck();
|
||||
|
||||
EsTextboxSelectAll(textbox);
|
||||
EsTextboxInsert(textbox, "", 0, sendUpdatedMessage);
|
||||
}
|
||||
|
||||
size_t EsTextboxGetLineLength(EsTextbox *textbox, uintptr_t line) {
|
||||
EsMessageMutexCheck();
|
||||
|
||||
return textbox->lines[line].lengthBytes;
|
||||
}
|
||||
|
||||
|
@ -872,7 +872,7 @@ void EsTextboxInsert(EsTextbox *textbox, const char *string, ptrdiff_t stringByt
|
|||
}
|
||||
}
|
||||
|
||||
if (textbox->undo) {
|
||||
if (textbox->undo && !textbox->readOnly) {
|
||||
// Step 3: Allocate space for an undo item.
|
||||
|
||||
undoItemBytes = sizeof(TextboxUndoItemHeader) - deltaBytes + deleteTo.line - deleteFrom.line;
|
||||
|
@ -938,7 +938,7 @@ void EsTextboxInsert(EsTextbox *textbox, const char *string, ptrdiff_t stringByt
|
|||
TextboxLineCountChangeCleanup(textbox, deltaBytes, deleteFrom.line + 1);
|
||||
}
|
||||
} else {
|
||||
if (textbox->undo) {
|
||||
if (textbox->undo && !textbox->readOnly) {
|
||||
undoItemBytes = sizeof(TextboxUndoItemHeader);
|
||||
undoItem = (TextboxUndoItemHeader *) EsHeapAllocate(undoItemBytes, false);
|
||||
EsMemoryZero(undoItem, sizeof(TextboxUndoItemHeader));
|
||||
|
@ -1476,6 +1476,40 @@ void TextboxStyleChanged(EsTextbox *textbox) {
|
|||
EsElementRepaint(textbox);
|
||||
}
|
||||
|
||||
void TextboxAddSmartContextMenu(EsTextbox *textbox, EsMenu *menu) {
|
||||
if ((~textbox->flags & ES_TEXTBOX_NO_SMART_CONTEXT_MENUS) && textbox->carets[0].line == textbox->carets[1].line) {
|
||||
int32_t selectionFrom = textbox->carets[0].byte, selectionTo = textbox->carets[1].byte;
|
||||
|
||||
if (selectionTo < selectionFrom) {
|
||||
int32_t temporary = selectionFrom;
|
||||
selectionFrom = selectionTo;
|
||||
selectionTo = temporary;
|
||||
}
|
||||
|
||||
if (selectionTo - selectionFrom == 7) {
|
||||
char buffer[7];
|
||||
EsMemoryCopy(buffer, GET_BUFFER(&textbox->lines[textbox->carets[0].line]) + selectionFrom, 7);
|
||||
|
||||
if (buffer[0] == '#' && EsCRTisxdigit(buffer[1]) && EsCRTisxdigit(buffer[2]) && EsCRTisxdigit(buffer[3])
|
||||
&& EsCRTisxdigit(buffer[4]) && EsCRTisxdigit(buffer[5]) && EsCRTisxdigit(buffer[6])) {
|
||||
// It's a color hex-code!
|
||||
// TODO Versions with alpha.
|
||||
EsMenuNextColumn(menu);
|
||||
ColorPickerCreate(menu, { textbox }, EsColorParse(buffer, 7), false);
|
||||
|
||||
textbox->colorUppercase = true;
|
||||
|
||||
for (uintptr_t i = 1; i <= 6; i++) {
|
||||
if (buffer[i] >= 'a' && buffer[i] <= 'f') {
|
||||
textbox->colorUppercase = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ProcessTextboxMessage(EsElement *element, EsMessage *message) {
|
||||
EsTextbox *textbox = (EsTextbox *) element;
|
||||
|
||||
|
@ -1638,7 +1672,7 @@ int ProcessTextboxMessage(EsElement *element, EsMessage *message) {
|
|||
|
||||
textbox->Repaint(true);
|
||||
verticalMotion = true;
|
||||
} else if (message->keyboard.scancode == ES_SCANCODE_BACKSPACE || message->keyboard.scancode == ES_SCANCODE_DELETE) {
|
||||
} else if ((message->keyboard.scancode == ES_SCANCODE_BACKSPACE || message->keyboard.scancode == ES_SCANCODE_DELETE) && !textbox->readOnly) {
|
||||
if (!textbox->editing) {
|
||||
EsTextboxStartEdit(textbox);
|
||||
}
|
||||
|
@ -1659,7 +1693,7 @@ int ProcessTextboxMessage(EsElement *element, EsMessage *message) {
|
|||
TextboxEndEdit(textbox, true);
|
||||
} else if (message->keyboard.scancode == ES_SCANCODE_TAB && (~textbox->flags & ES_TEXTBOX_ALLOW_TABS)) {
|
||||
response = 0;
|
||||
} else {
|
||||
} else if (!textbox->readOnly) {
|
||||
if (!textbox->editing) {
|
||||
EsTextboxStartEdit(textbox);
|
||||
}
|
||||
|
@ -1669,7 +1703,7 @@ int ProcessTextboxMessage(EsElement *element, EsMessage *message) {
|
|||
true, textbox->flags & ES_TEXTBOX_MULTILINE);
|
||||
|
||||
if (inputString && (message->keyboard.modifiers & ~(ES_MODIFIER_SHIFT | ES_MODIFIER_ALT_GR)) == 0) {
|
||||
if (textbox->smartQuotes && api.global->useSmartQuotes) {
|
||||
if (textbox->smartReplacement && api.global->useSmartQuotes) {
|
||||
DocumentLine *currentLine = &textbox->lines[textbox->carets[0].line];
|
||||
const char *buffer = GET_BUFFER(currentLine);
|
||||
bool left = !textbox->carets[0].byte || buffer[textbox->carets[0].byte - 1] == ' ';
|
||||
|
@ -1701,6 +1735,8 @@ int ProcessTextboxMessage(EsElement *element, EsMessage *message) {
|
|||
} else {
|
||||
response = 0;
|
||||
}
|
||||
} else {
|
||||
response = 0;
|
||||
}
|
||||
|
||||
if (!verticalMotion) {
|
||||
|
@ -1757,52 +1793,26 @@ int ProcessTextboxMessage(EsElement *element, EsMessage *message) {
|
|||
|
||||
// TODO User customisation of menus.
|
||||
|
||||
if (textbox->editing) {
|
||||
EsMenuAddCommand(menu, 0, INTERFACE_STRING(CommonUndo), EsCommandByID(textbox->instance, ES_COMMAND_UNDO));
|
||||
EsMenuAddSeparator(menu);
|
||||
}
|
||||
|
||||
EsMenuAddCommand(menu, 0, INTERFACE_STRING(CommonClipboardCut), EsCommandByID(textbox->instance, ES_COMMAND_CUT));
|
||||
EsMenuAddCommand(menu, 0, INTERFACE_STRING(CommonClipboardCopy), EsCommandByID(textbox->instance, ES_COMMAND_COPY));
|
||||
EsMenuAddCommand(menu, 0, INTERFACE_STRING(CommonClipboardPaste), EsCommandByID(textbox->instance, ES_COMMAND_PASTE));
|
||||
|
||||
if (textbox->editing) {
|
||||
EsMenuAddSeparator(menu);
|
||||
if (textbox->readOnly) {
|
||||
EsMenuAddCommand(menu, 0, INTERFACE_STRING(CommonClipboardCopy), EsCommandByID(textbox->instance, ES_COMMAND_COPY));
|
||||
EsMenuAddCommand(menu, 0, INTERFACE_STRING(CommonSelectionSelectAll), EsCommandByID(textbox->instance, ES_COMMAND_SELECT_ALL));
|
||||
EsMenuAddCommand(menu, 0, INTERFACE_STRING(CommonSelectionDelete), EsCommandByID(textbox->instance, ES_COMMAND_DELETE));
|
||||
} else {
|
||||
if (textbox->editing) {
|
||||
EsMenuAddCommand(menu, 0, INTERFACE_STRING(CommonUndo), EsCommandByID(textbox->instance, ES_COMMAND_UNDO));
|
||||
EsMenuAddSeparator(menu);
|
||||
}
|
||||
|
||||
// Add the smart context menu, if necessary.
|
||||
EsMenuAddCommand(menu, 0, INTERFACE_STRING(CommonClipboardCut), EsCommandByID(textbox->instance, ES_COMMAND_CUT));
|
||||
EsMenuAddCommand(menu, 0, INTERFACE_STRING(CommonClipboardCopy), EsCommandByID(textbox->instance, ES_COMMAND_COPY));
|
||||
EsMenuAddCommand(menu, 0, INTERFACE_STRING(CommonClipboardPaste), EsCommandByID(textbox->instance, ES_COMMAND_PASTE));
|
||||
|
||||
if ((~textbox->flags & ES_TEXTBOX_NO_SMART_CONTEXT_MENUS) && textbox->carets[0].line == textbox->carets[1].line) {
|
||||
int32_t selectionFrom = textbox->carets[0].byte, selectionTo = textbox->carets[1].byte;
|
||||
if (textbox->editing) {
|
||||
EsMenuAddSeparator(menu);
|
||||
EsMenuAddCommand(menu, 0, INTERFACE_STRING(CommonSelectionSelectAll), EsCommandByID(textbox->instance, ES_COMMAND_SELECT_ALL));
|
||||
EsMenuAddCommand(menu, 0, INTERFACE_STRING(CommonSelectionDelete), EsCommandByID(textbox->instance, ES_COMMAND_DELETE));
|
||||
|
||||
if (selectionTo < selectionFrom) {
|
||||
int32_t temporary = selectionFrom;
|
||||
selectionFrom = selectionTo;
|
||||
selectionTo = temporary;
|
||||
}
|
||||
|
||||
if (selectionTo - selectionFrom == 7) {
|
||||
char buffer[7];
|
||||
EsMemoryCopy(buffer, GET_BUFFER(&textbox->lines[textbox->carets[0].line]) + selectionFrom, 7);
|
||||
|
||||
if (buffer[0] == '#' && EsCRTisxdigit(buffer[1]) && EsCRTisxdigit(buffer[2]) && EsCRTisxdigit(buffer[3])
|
||||
&& EsCRTisxdigit(buffer[4]) && EsCRTisxdigit(buffer[5]) && EsCRTisxdigit(buffer[6])) {
|
||||
// It's a color hex-code!
|
||||
// TODO Versions with alpha.
|
||||
EsMenuNextColumn(menu);
|
||||
ColorPickerCreate(menu, { textbox }, EsColorParse(buffer, 7), false);
|
||||
|
||||
textbox->colorUppercase = true;
|
||||
|
||||
for (uintptr_t i = 1; i <= 6; i++) {
|
||||
if (buffer[i] >= 'a' && buffer[i] <= 'f') {
|
||||
textbox->colorUppercase = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add the smart context menu, if necessary.
|
||||
TextboxAddSmartContextMenu(textbox, menu);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1890,7 +1900,7 @@ EsTextbox *EsTextboxCreate(EsElement *parent, uint64_t flags, const EsStyle *sty
|
|||
|
||||
textbox->style->GetTextStyle(&textbox->textStyle);
|
||||
|
||||
textbox->smartQuotes = true;
|
||||
textbox->smartReplacement = true;
|
||||
|
||||
DocumentLine firstLine = {};
|
||||
firstLine.height = TextGetLineHeight(textbox, &textbox->textStyle);
|
||||
|
@ -2133,8 +2143,14 @@ void EsTextboxSetupSyntaxHighlighting(EsTextbox *textbox, uint32_t language, uin
|
|||
textbox->Repaint(true);
|
||||
}
|
||||
|
||||
void EsTextboxEnableSmartQuotes(EsTextbox *textbox, bool enabled) {
|
||||
textbox->smartQuotes = enabled;
|
||||
void EsTextboxEnableSmartReplacement(EsTextbox *textbox, bool enabled) {
|
||||
textbox->smartReplacement = enabled;
|
||||
}
|
||||
|
||||
void EsTextboxSetReadOnly(EsTextbox *textbox, bool readOnly) {
|
||||
textbox->readOnly = readOnly;
|
||||
EsAssert(~textbox->flags & ES_TEXTBOX_EDIT_BASED);
|
||||
TextboxUpdateCommands(textbox, false);
|
||||
}
|
||||
|
||||
#undef GET_BUFFER
|
||||
|
|
|
@ -288,3 +288,62 @@ nakst
|
|||
—
|
||||
Yesterday at 8:44 AM
|
||||
They are not linked to any libraries. They call into the system api via the api table, which is an array of function pointers at a fixed memory address. The api header wraps these function pointers with macros, as needed.
|
||||
|
||||
= On UI layouting =
|
||||
|
||||
nakst
|
||||
—
|
||||
Today at 5:22 PM
|
||||
I really need to talk in depth about how layouting in Luigi (and by extension Essence) works, but let me try and describe the basic idea here:
|
||||
|
||||
- Each element has an associated callback function, called its message handler. "Sending a message" to an element just means calling into the message handler of that element.
|
||||
- Elements are arranged in a layout tree. Deciding the position of each element is the responsibility of its parent.
|
||||
- When an element need its layout updating it is sent a UI_MSG_LAYOUT message. The element must then position all its child elements with UIElementMove. If a child's bounding box changes, then it will be sent a UI_MSG_LAYOUT message. That is, the subtree that needs relayouting is iterated in recursive manner.
|
||||
- During the layout of element, it can request sizing information from its children by sending them the UI_MSG_GET_WIDTH and UI_MSG_GET_HEIGHT messages. As an optional argument, the former takes a desired height of the element, and the latter takes a desired width of the element.
|
||||
- On receiving a GET_WIDTH or GET_HEIGHT message, an element may also need to query its children with GET_WIDTH and GET_HEIGHT.
|
||||
|
||||
Example:
|
||||
Consider a vertical stack X with a child element Y, which is a text display containing word-wrapped text. X is sent a UI_MSG_LAYOUT message. X sends UI_MSG_GET_HEIGHT to Y with the desired width argument as X.width. Y performs its word-wrapping algorithm, and returns the needed vertical space to display its contents at the given width. X calls UIElementMove on Y, giving it a bounding box with width X.width and the height returned by the UI_MSG_GET_HEIGHT call. Y receives a UI_MSG_LAYOUT message (because UIElementMove was called), but it does nothing because it has no children.
|
||||
|
||||
ryanfleury
|
||||
—
|
||||
Today at 5:26 PM
|
||||
I see, okay. It seems sort of similar to what I do, actually, in some ways. In your case it's retained-mode, so you do message-passing-style "update propagation", whereas the immediate-mode equivalent would just do the full pass every frame. How do "upwards-dependent" sizes work? e.g. a widget that wants its width to be 50% of its parent's? Is that just decided by the parent?
|
||||
|
||||
nakst
|
||||
—
|
||||
Today at 5:37 PM
|
||||
It depends on what exactly type of layout you're going for, but generally this sort of thing is done by setting the UI_ELEMENT_H_FILL flag on the child element.
|
||||
For example,
|
||||
|
||||
UIPanel *panel = UIPanelCreate(parent, UI_PANEL_HORIZONTAL);
|
||||
UIButtonCreate(panel, UI_ELEMENT_H_FILL, "Button 1", -1);
|
||||
UIButtonCreate(panel, UI_ELEMENT_H_FILL, "Button 2", -1);
|
||||
|
||||
|
||||
creates a panel using a horizontal stack layout algorithm, with 2 buttons as its children. Because each button has the UI_ELEMENT_H_FILL, they will stretch to each fill 50% of the available width of the parent.
|
||||
Another example,
|
||||
|
||||
UIPanel *panel = UIPanelCreate(parent, UI_PANEL_HORIZONTAL);
|
||||
UIButtonCreate(panel, 0, "Button 1", -1);
|
||||
UISpacerCreate(panel, UI_ELEMENT_H_FILL, 0, 0);
|
||||
UIButtonCreate(panel, 0, "Button 2", -1);
|
||||
|
||||
|
||||
This time the 2 buttons have fixed width (determined by the length of their label) but the spacer element in the middle of the stack takes up 100% of the panel's width not used by the buttons. This has the effect of putting "Button 1" at the left side of the panel and "Button 2" at the right side.
|
||||
|
||||
ryanfleury
|
||||
—
|
||||
Today at 5:39 PM
|
||||
I see—what about something like a 70/30 split?
|
||||
nakst
|
||||
—
|
||||
Today at 5:43 PM
|
||||
You can't in Luigi. (Luckily it doesn't matter because this is not very common in user interfaces.)
|
||||
But in Essence you can do it, by setting it as a property on a panel using the TABLE layout algorithm:
|
||||
|
||||
EsPanelBand columns[2] = {
|
||||
{ .push = 7 }, /* column 1 */
|
||||
{ .push = 3 }, /* column 2 */
|
||||
};
|
||||
EsPanelSetBands(panel, 2 /* number of columns */, 0, &columns);
|
||||
|
|
Binary file not shown.
BIN
res/Theme.dat
BIN
res/Theme.dat
Binary file not shown.
|
@ -1187,6 +1187,23 @@ double EsDoubleParse(const char *nptr, ptrdiff_t maxBytes, char **endptr) {
|
|||
return value;
|
||||
}
|
||||
|
||||
void PathGetName(const char *path, ptrdiff_t pathBytes, const char **name, ptrdiff_t *nameBytes) {
|
||||
if (pathBytes == -1) {
|
||||
pathBytes = EsCStringLength(path);
|
||||
}
|
||||
|
||||
*name = path;
|
||||
*nameBytes = pathBytes;
|
||||
|
||||
for (uintptr_t i = pathBytes; i > 0; i--) {
|
||||
if (path[i - 1] == '/') {
|
||||
*name = path + i;
|
||||
*nameBytes = pathBytes - i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/////////////////////////////////
|
||||
|
|
|
@ -132,8 +132,9 @@ static void MemoryLeakDetectorAdd(EsHeap *heap, void *address, size_t bytes) {
|
|||
while (rbp && traceDepth < sizeof(entry->stack) / sizeof(entry->stack[0])) {
|
||||
uint64_t value = *(uint64_t *) (rbp + 8);
|
||||
entry->stack[traceDepth++] = value;
|
||||
if (!value) break;
|
||||
if (!value || (value & 0xFFFF000000000000) || (value < 0x100000)) break;
|
||||
rbp = *(uint64_t *) rbp;
|
||||
if ((rbp & 0xFFFF000000000000) || (rbp < 0x100000)) break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
|
@ -191,6 +191,7 @@ DEFINE_INTERFACE_STRING(FileCannotRename, "The file could not be renamed.");
|
|||
|
||||
DEFINE_INTERFACE_STRING(FileRenameSuccess, "Renamed");
|
||||
|
||||
DEFINE_INTERFACE_STRING(FileSaveAnnouncement, "Saved to %s");
|
||||
DEFINE_INTERFACE_STRING(FileSaveErrorFileDeleted, "Another application deleted the file.");
|
||||
DEFINE_INTERFACE_STRING(FileSaveErrorCorrupt, "The file has been corrupted, and it cannot be modified.");
|
||||
DEFINE_INTERFACE_STRING(FileSaveErrorDrive, "The drive containing the file was unable to modify it.");
|
||||
|
@ -379,6 +380,18 @@ DEFINE_INTERFACE_STRING(InstallerFailedGeneric, "The installation could not comp
|
|||
DEFINE_INTERFACE_STRING(InstallerFailedResources, "The installation could not complete. Your computer does not have enough memory to install " SYSTEM_BRAND_SHORT);
|
||||
DEFINE_INTERFACE_STRING(InstallerNotSupported, "Your computer does not meet the minimum system requirements to install " SYSTEM_BRAND_SHORT ". Remove the installer, and restart your computer.");
|
||||
|
||||
// Build Core.
|
||||
|
||||
DEFINE_INTERFACE_STRING(BuildCoreTitle, "Build Core");
|
||||
DEFINE_INTERFACE_STRING(BuildCoreNoConfigFileLoaded, "No config file is loaded.");
|
||||
DEFINE_INTERFACE_STRING(BuildCoreNoFilePath, "The config file is not in a real folder, so it can't be loaded.");
|
||||
DEFINE_INTERFACE_STRING(BuildCorePathCannotBeAccessedByPOSIXSubsystem, "The config file is not located on the 0:/ drive, so it can't be accessed by the POSIX subsystem.");
|
||||
DEFINE_INTERFACE_STRING(BuildCoreBuild, "Build");
|
||||
DEFINE_INTERFACE_STRING(BuildCoreLaunch, "Launch");
|
||||
DEFINE_INTERFACE_STRING(BuildCoreCannotCreateBuildThread, "The build thread could not be created.");
|
||||
DEFINE_INTERFACE_STRING(BuildCoreBuildFailed, "\n--- The build failed. ---\n");
|
||||
DEFINE_INTERFACE_STRING(BuildCoreBuildSuccess, "\n(success)\n");
|
||||
|
||||
// TODO System Monitor.
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
|
|
@ -70,6 +70,9 @@ EsThreadCreate=68
|
|||
EsThreadGetID=69
|
||||
EsCRTstrdup=70
|
||||
EsThreadTerminate=71
|
||||
EsProcessGetCreateData=72
|
||||
EsTextboxEnableSmartReplacement=73
|
||||
EsTextboxSetReadOnly=74
|
||||
EsConstantBufferCreate=75
|
||||
EsConstantBufferRead=76
|
||||
EsConstantBufferShare=77
|
||||
|
@ -445,7 +448,6 @@ _EsUISetFont=454
|
|||
EsWorkQueue=455
|
||||
EsWorkIsExiting=456
|
||||
EsPanelRadioGroupGetChecked=457
|
||||
EsTextboxEnableSmartQuotes=458
|
||||
EsBufferWriteInt8=459
|
||||
EsInstanceGetStartupRequest=460
|
||||
EsImageDisplayPaint=461
|
||||
|
|
|
@ -92,6 +92,7 @@ bool IsStringEqual(const char *string, size_t stringBytes, const char *cLiteral)
|
|||
|
||||
bool CheckDependencies(const char *applicationName) {
|
||||
#ifdef OS_ESSENCE
|
||||
(void) applicationName;
|
||||
// TODO.
|
||||
return true;
|
||||
#else
|
||||
|
@ -128,6 +129,9 @@ bool CheckDependencies(const char *applicationName) {
|
|||
|
||||
void ParseDependencies(const char *dependencyFile, const char *applicationName, bool append) {
|
||||
#ifdef OS_ESSENCE
|
||||
(void) dependencyFile;
|
||||
(void) applicationName;
|
||||
(void) append;
|
||||
// TODO.
|
||||
#else
|
||||
char *dependencies = (char *) LoadFile(dependencyFile, NULL);
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
// This file is part of the Essence operating system.
|
||||
// It is released under the terms of the MIT license -- see LICENSE.md.
|
||||
// Written by: nakst.
|
||||
|
||||
// TODO Better configuration over what files are imported to the drive image.
|
||||
// TODO Make build_core responsible for generating the header.
|
||||
// TODO Resetting after system builds.
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
|
@ -11,6 +15,12 @@
|
|||
#include <essence.h>
|
||||
#include <bits/syscall.h>
|
||||
|
||||
#define MSG_BUILD_SUCCESS (ES_MSG_USER_START + 1)
|
||||
#define MSG_BUILD_FAILED (ES_MSG_USER_START + 2)
|
||||
#define MSG_LOG (ES_MSG_USER_START + 3)
|
||||
|
||||
bool logSendMessages;
|
||||
|
||||
typedef struct File {
|
||||
bool error, ready;
|
||||
EsHandle handle;
|
||||
|
@ -27,10 +37,24 @@ void Log(const char *format, ...) {
|
|||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
char buffer[4096];
|
||||
int bytes = EsCRTvsnprintf(buffer, sizeof(buffer), format, arguments);
|
||||
EsAssert(bytes < sizeof(buffer));
|
||||
size_t bytes = EsCRTvsnprintf(buffer, sizeof(buffer), format, arguments);
|
||||
va_end(arguments);
|
||||
EsPOSIXSystemCall(SYS_write, (intptr_t) 1, (intptr_t) buffer, (intptr_t) bytes, 0, 0, 0);
|
||||
|
||||
if (bytes >= sizeof(buffer) - 1) {
|
||||
buffer[sizeof(buffer) - 1] = 0;
|
||||
bytes = sizeof(buffer) - 1;
|
||||
}
|
||||
|
||||
if (logSendMessages) {
|
||||
EsMessage m;
|
||||
m.type = MSG_LOG;
|
||||
char *copy = EsHeapAllocate(bytes + 1, false, NULL);
|
||||
EsCRTstrcpy(copy, buffer);
|
||||
m.user.context1.p = copy;
|
||||
EsMessagePost(NULL, &m);
|
||||
} else {
|
||||
EsPOSIXSystemCall(SYS_write, (intptr_t) 1, (intptr_t) buffer, (intptr_t) bytes, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
File FileOpen(const char *path, char mode) {
|
||||
|
@ -59,7 +83,7 @@ void _FilePrintFormat(File *file, const char *format, ...) {
|
|||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
char buffer[4096];
|
||||
int bytes = EsCRTvsnprintf(buffer, sizeof(buffer), format, arguments);
|
||||
size_t bytes = EsCRTvsnprintf(buffer, sizeof(buffer), format, arguments);
|
||||
EsAssert(bytes < sizeof(buffer));
|
||||
va_end(arguments);
|
||||
FileWrite((*file), bytes, buffer);
|
||||
|
@ -81,7 +105,7 @@ void _FilePrintFormat(File *file, const char *format, ...) {
|
|||
typedef uint64_t pid_t;
|
||||
|
||||
typedef uint64_t time_t;
|
||||
time_t time(time_t *timer) { return 0; } // TODO.
|
||||
time_t time(time_t *timer) { (void) timer; return 0; } // TODO.
|
||||
|
||||
#else
|
||||
|
||||
|
@ -170,6 +194,10 @@ char *builtinModules;
|
|||
volatile uint8_t encounteredErrors;
|
||||
volatile uint8_t encounteredErrorsInKernelModules;
|
||||
|
||||
size_t singleConfigFileBytes;
|
||||
void *singleConfigFileData;
|
||||
bool singleConfigFileUse;
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
#define COLOR_ERROR "\033[0;33m"
|
||||
|
@ -202,6 +230,8 @@ char *executeEnvironment[3] = {
|
|||
|
||||
int _Execute(char **output, const char *executable, ...) {
|
||||
char *argv[64];
|
||||
char *copies[64];
|
||||
size_t copyCount = 0;
|
||||
va_list argList;
|
||||
va_start(argList, executable);
|
||||
|
||||
|
@ -219,6 +249,8 @@ int _Execute(char **output, const char *executable, ...) {
|
|||
}
|
||||
|
||||
char *copy = (char *) malloc(strlen(string) + 2);
|
||||
assert(copyCount != 64);
|
||||
copies[copyCount++] = copy;
|
||||
strcpy(copy, string);
|
||||
strcat(copy, " ");
|
||||
uintptr_t start = 0;
|
||||
|
@ -307,6 +339,10 @@ int _Execute(char **output, const char *executable, ...) {
|
|||
Log("(status = %d)\n", status);
|
||||
}
|
||||
|
||||
for (uintptr_t i = 0; i < copyCount; i++) {
|
||||
free(copies[i]);
|
||||
}
|
||||
|
||||
if (status) __sync_fetch_and_or(&encounteredErrors, 1);
|
||||
return status;
|
||||
}
|
||||
|
@ -450,6 +486,7 @@ bool MakeBundle(const char *outputFile, BundleInput *inputFiles, size_t inputFil
|
|||
|
||||
if (output.error) {
|
||||
Log("Error: Could not open output file '%s'.\n", outputFile);
|
||||
__sync_fetch_and_or(&encounteredErrors, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -486,11 +523,13 @@ bool MakeBundle(const char *outputFile, BundleInput *inputFiles, size_t inputFil
|
|||
|
||||
if (!buffer) {
|
||||
Log("Error: Could not open input file '%s'.\n", inputFiles[i].path);
|
||||
__sync_fetch_and_or(&encounteredErrors, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size > 0xFFFFFFFF) {
|
||||
Log("Error: Input file '%s' too large (max: 4GB).\n", inputFiles[i].path);
|
||||
__sync_fetch_and_or(&encounteredErrors, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -499,6 +538,7 @@ bool MakeBundle(const char *outputFile, BundleInput *inputFiles, size_t inputFil
|
|||
FileSeek(output, outputPosition);
|
||||
FileWrite(output, size, buffer);
|
||||
outputPosition += size;
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
FileClose(output);
|
||||
|
@ -527,6 +567,8 @@ typedef struct DependencyFile {
|
|||
} DependencyFile;
|
||||
|
||||
typedef struct Application {
|
||||
char *manifest;
|
||||
|
||||
const char *name;
|
||||
EsINIState *properties;
|
||||
int id;
|
||||
|
@ -607,7 +649,9 @@ void BuildDesktop(Application *application) {
|
|||
ADD_BUNDLE_INPUT("res/Cursors.png", "Cursors.png", 16);
|
||||
ADD_BUNDLE_INPUT("bin/Stripped Executables/Desktop", "$Executables/x86_64", 0x1000); // TODO Don't hardcode the target.
|
||||
|
||||
MakeBundle("root/" SYSTEM_FOLDER_NAME "/Desktop.esx", application->bundleInputFiles, arrlenu(application->bundleInputFiles), 0);
|
||||
if (!application->error) {
|
||||
application->error = !MakeBundle("root/" SYSTEM_FOLDER_NAME "/Desktop.esx", application->bundleInputFiles, arrlenu(application->bundleInputFiles), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void BuildApplication(Application *application) {
|
||||
|
@ -699,7 +743,9 @@ void BuildApplication(Application *application) {
|
|||
}
|
||||
}
|
||||
|
||||
MakeBundle(executable, application->bundleInputFiles, arrlenu(application->bundleInputFiles), 0);
|
||||
if (!application->error) {
|
||||
application->error = !MakeBundle(executable, application->bundleInputFiles, arrlenu(application->bundleInputFiles), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -722,14 +768,22 @@ void *BuildApplicationThread(void *_unused) {
|
|||
|
||||
void ParseApplicationManifest(const char *manifestPath) {
|
||||
EsINIState s = {};
|
||||
char *manifest = (char *) LoadFile(manifestPath, &s.bytes);
|
||||
s.buffer = manifest;
|
||||
char *manifest;
|
||||
|
||||
if (singleConfigFileUse) {
|
||||
manifest = s.buffer = malloc(singleConfigFileBytes);
|
||||
s.bytes = singleConfigFileBytes;
|
||||
memcpy(s.buffer, singleConfigFileData, singleConfigFileBytes);
|
||||
} else {
|
||||
manifest = s.buffer = (char *) LoadFile(manifestPath, &s.bytes);
|
||||
}
|
||||
|
||||
const char *require = "";
|
||||
bool needsNativeToolchain = false;
|
||||
bool disabled = false;
|
||||
|
||||
Application application = {};
|
||||
application.manifest = manifest;
|
||||
application.id = nextID++;
|
||||
application.manifestPath = manifestPath;
|
||||
application.compileFlags = "";
|
||||
|
@ -1226,7 +1280,7 @@ int BuildCore(int argc, char **argv) {
|
|||
#else
|
||||
int main(int argc, char **argv) {
|
||||
#endif
|
||||
if (argc < 2) {
|
||||
if (argc < 2 && !singleConfigFileUse) {
|
||||
Log("Usage: build_core <mode> <options...>\n");
|
||||
return 1;
|
||||
}
|
||||
|
@ -1248,7 +1302,9 @@ int main(int argc, char **argv) {
|
|||
char *driverSource = NULL, *driverName = NULL;
|
||||
bool driverBuiltin = false;
|
||||
|
||||
if (0 == strcmp(argv[1], "standard")) {
|
||||
if (singleConfigFileUse) {
|
||||
arrput(applicationManifests, "$SingleConfigFile");
|
||||
} else if (0 == strcmp(argv[1], "standard")) {
|
||||
if (argc != 3) {
|
||||
Log("Usage: standard <configuration>\n");
|
||||
return 1;
|
||||
|
@ -1520,13 +1576,6 @@ int main(int argc, char **argv) {
|
|||
CopyFile("bin/Object Files/crt1.o", "cross/lib/gcc/x86_64-essence/" GCC_VERSION "/crt1.o", true); // TODO Don't hardcode the target.
|
||||
CopyFile("bin/Object Files/crtglue.o", "cross/lib/gcc/x86_64-essence/" GCC_VERSION "/crtglue.o", true);
|
||||
CopyFile("util/linker/userland64.ld", "root/Applications/POSIX/lib/linker/userland64.ld", false);
|
||||
|
||||
if (hasNativeToolchain) {
|
||||
snprintf(buffer, sizeof(buffer), "%s/linker/userland64.ld", toolchainLinkerScripts); // TODO Don't hardcode the target.
|
||||
Execute(toolchainCC, "util/build_core.c", "-o", "root/Applications/POSIX/bin/build_core", "-g",
|
||||
"-nostdlib", "bin/Object Files/crti.o", "bin/Object Files/crtbegin.o",
|
||||
"bin/Object Files/crtend.o", "bin/Object Files/crtn.o", "-T", buffer);
|
||||
}
|
||||
}
|
||||
|
||||
#define ADD_DEPENDENCY_FILE(application, _path, _name) \
|
||||
|
@ -1696,15 +1745,252 @@ int main(int argc, char **argv) {
|
|||
DependenciesListWrite();
|
||||
}
|
||||
|
||||
return encounteredErrors;
|
||||
for (uintptr_t i = 0; i < arrlenu(applications); i++) {
|
||||
free(applications[i].manifest);
|
||||
arrfree(applications[i].sources);
|
||||
arrfree(applications[i].dependencyFiles);
|
||||
arrfree(applications[i].bundleInputFiles);
|
||||
arrfree(applications[i].fileTypes);
|
||||
arrfree(applications[i].handlers);
|
||||
}
|
||||
|
||||
arrfree(applicationManifests);
|
||||
arrfree(applications);
|
||||
shfree(applicationDependencies);
|
||||
|
||||
uint8_t _encounteredErrors = encounteredErrors;
|
||||
encounteredErrors = false;
|
||||
|
||||
#ifdef PARALLEL_BUILD
|
||||
applicationsIndex = 0;
|
||||
#endif
|
||||
|
||||
return _encounteredErrors;
|
||||
}
|
||||
|
||||
#ifdef OS_ESSENCE
|
||||
|
||||
#include <shared/strings.cpp>
|
||||
|
||||
EsCommand commandBuild;
|
||||
EsCommand commandLaunch;
|
||||
|
||||
EsButton *buttonBuild;
|
||||
EsButton *buttonLaunch;
|
||||
|
||||
EsTextbox *textboxLog;
|
||||
|
||||
char *workingDirectory;
|
||||
size_t workingDirectoryBytes;
|
||||
|
||||
bool buildRunning;
|
||||
|
||||
const EsStyle stylePanelStack = {
|
||||
.metrics = {
|
||||
.mask = ES_THEME_METRICS_INSETS | ES_THEME_METRICS_GAP_MAJOR,
|
||||
.insets = ES_RECT_1(20),
|
||||
.gapMajor = 15,
|
||||
},
|
||||
};
|
||||
|
||||
const EsStyle stylePanelButtonStack = {
|
||||
.metrics = {
|
||||
.mask = ES_THEME_METRICS_GAP_MAJOR,
|
||||
.gapMajor = 5,
|
||||
},
|
||||
};
|
||||
|
||||
const EsStyle styleTextboxLog = {
|
||||
.inherit = ES_STYLE_TEXTBOX_BORDERED_MULTILINE,
|
||||
|
||||
.metrics = {
|
||||
.mask = ES_THEME_METRICS_FONT_FAMILY | ES_THEME_METRICS_TEXT_SIZE | ES_THEME_METRICS_PREFERRED_WIDTH,
|
||||
.textSize = 10,
|
||||
.fontFamily = ES_FONT_MONOSPACED,
|
||||
.preferredWidth = 400,
|
||||
},
|
||||
};
|
||||
|
||||
void ThreadBuild(EsGeneric argument) {
|
||||
(void) argument;
|
||||
logSendMessages = true;
|
||||
int result = BuildCore(0, NULL);
|
||||
EsMessage m;
|
||||
m.type = result ? MSG_BUILD_FAILED : MSG_BUILD_SUCCESS;
|
||||
EsMessagePost(NULL, &m);
|
||||
}
|
||||
|
||||
void CommandBuild(EsInstance *instance, EsElement *element, EsCommand *command) {
|
||||
(void) element;
|
||||
EsAssert(command == &commandBuild);
|
||||
|
||||
if (!buildRunning) {
|
||||
EsGeneric argument = { 0 };
|
||||
|
||||
if (ES_SUCCESS == EsThreadCreate(ThreadBuild, NULL, argument)) {
|
||||
buildRunning = true;
|
||||
EsCommandSetEnabled(&commandBuild, false);
|
||||
EsCommandSetEnabled(&commandLaunch, false);
|
||||
} else {
|
||||
EsDialogShow(instance->window, INTERFACE_STRING(BuildCoreCannotCreateBuildThread), NULL, 0, ES_ICON_DIALOG_ERROR, ES_DIALOG_ALERT_OK_BUTTON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommandLaunch(EsInstance *instance, EsElement *element, EsCommand *command) {
|
||||
(void) instance;
|
||||
(void) element;
|
||||
EsAssert(command == &commandLaunch);
|
||||
|
||||
if (!singleConfigFileData || !workingDirectory) {
|
||||
return;
|
||||
}
|
||||
|
||||
EsINIState s = {};
|
||||
s.buffer = singleConfigFileData;
|
||||
s.bytes = singleConfigFileBytes;
|
||||
|
||||
char *executablePath = NULL;
|
||||
size_t executablePathBytes = 0;
|
||||
|
||||
while (EsINIParse(&s)) {
|
||||
if (s.sectionClassBytes == 0
|
||||
&& 0 == EsStringCompareRaw(s.section, s.sectionBytes, EsLiteral("general"))
|
||||
&& 0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("name"))) {
|
||||
executablePath = EsStringAllocateAndFormat(&executablePathBytes, "%s/bin/%s.esx", workingDirectoryBytes, workingDirectory, s.valueBytes, s.value);
|
||||
}
|
||||
}
|
||||
|
||||
if (executablePath) {
|
||||
EsApplicationRunTemporary(executablePath, executablePathBytes);
|
||||
EsHeapFree(executablePath, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
int InstanceCallback(EsInstance *instance, EsMessage *message) {
|
||||
if (message->type == ES_MSG_INSTANCE_OPEN) {
|
||||
EsFileStore *fileStore = message->instanceOpen.file;
|
||||
const char *path = message->instanceOpen.nameOrPath;
|
||||
size_t pathBytes = message->instanceOpen.nameOrPathBytes;
|
||||
|
||||
bool pathIsReal = false;
|
||||
for (uintptr_t i = 0; i < pathBytes; i++) if (path[i] == '/') pathIsReal = true;
|
||||
|
||||
// HACK Remove this limitation.
|
||||
bool pathCanBeAccessedByPOSIXSubsystem = pathBytes > 3 && path[0] == '0' && path[1] == ':' && path[2] == '/';
|
||||
|
||||
if (!pathIsReal) {
|
||||
// Not a real file, we cannot build here.
|
||||
EsInstanceOpenComplete(instance, fileStore, false, INTERFACE_STRING(BuildCoreNoFilePath));
|
||||
} else if (!pathCanBeAccessedByPOSIXSubsystem) {
|
||||
EsInstanceOpenComplete(instance, fileStore, false, INTERFACE_STRING(BuildCorePathCannotBeAccessedByPOSIXSubsystem));
|
||||
} else {
|
||||
size_t newFileBytes;
|
||||
void *newFileData = EsFileStoreReadAll(fileStore, &newFileBytes);
|
||||
EsInstanceOpenComplete(instance, fileStore, newFileData != NULL, NULL, 0);
|
||||
|
||||
if (newFileData) {
|
||||
EsHeapFree(singleConfigFileData, 0, NULL);
|
||||
singleConfigFileBytes = newFileBytes;
|
||||
singleConfigFileData = newFileData;
|
||||
singleConfigFileUse = true;
|
||||
|
||||
// Change directory.
|
||||
size_t directoryBytes = pathBytes;
|
||||
|
||||
for (uintptr_t i = 0; i < pathBytes; i++) {
|
||||
if (path[i] == '/') {
|
||||
directoryBytes = i;
|
||||
}
|
||||
}
|
||||
|
||||
char *directory = EsHeapAllocate(directoryBytes + 1, false, NULL);
|
||||
directory[directoryBytes] = 0;
|
||||
EsMemoryCopy(directory, path, directoryBytes);
|
||||
EsPOSIXSystemCall(SYS_chdir, (intptr_t) (directory + 2 /* skip drive prefix */), 0, 0, 0, 0, 0);
|
||||
EsHeapFree(workingDirectory, workingDirectoryBytes ? workingDirectoryBytes + 1 : 0, NULL);
|
||||
workingDirectory = directory;
|
||||
workingDirectoryBytes = directoryBytes;
|
||||
|
||||
if (!buildRunning) {
|
||||
EsCommandSetEnabled(&commandBuild, true);
|
||||
EsCommandSetEnabled(&commandLaunch, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ES_HANDLED;
|
||||
}
|
||||
|
||||
void InstanceCreate(EsMessage *message) {
|
||||
EsInstance *instance = EsInstanceCreate(message, INTERFACE_STRING(BuildCoreTitle));
|
||||
instance->callback = InstanceCallback;
|
||||
EsCommandRegister(&commandBuild, instance, INTERFACE_STRING(BuildCoreBuild), CommandBuild, 1, "F5", false);
|
||||
EsCommandRegister(&commandLaunch, instance, INTERFACE_STRING(BuildCoreLaunch), CommandLaunch, 2, "F6", false);
|
||||
EsWindowSetIcon(instance->window, ES_ICON_TEXT_X_MAKEFILE);
|
||||
EsPanel *panelRoot = EsPanelCreate(instance->window, ES_CELL_FILL, ES_STYLE_PANEL_WINDOW_BACKGROUND);
|
||||
EsPanel *panelStack = EsPanelCreate(panelRoot, ES_CELL_FILL | ES_PANEL_HORIZONTAL, &stylePanelStack);
|
||||
EsPanel *panelButtonStack = EsPanelCreate(panelStack, ES_CELL_V_TOP | ES_PANEL_VERTICAL, &stylePanelButtonStack);
|
||||
buttonBuild = EsButtonCreate(panelButtonStack, ES_BUTTON_DEFAULT, ES_NULL, INTERFACE_STRING(BuildCoreBuild));
|
||||
EsCommandAddButton(&commandBuild, buttonBuild);
|
||||
buttonBuild->accessKey = 'B';
|
||||
buttonLaunch = EsButtonCreate(panelButtonStack, ES_FLAGS_DEFAULT, ES_NULL, INTERFACE_STRING(BuildCoreLaunch));
|
||||
EsCommandAddButton(&commandLaunch, buttonLaunch);
|
||||
buttonLaunch->accessKey = 'L';
|
||||
EsSpacerCreate(panelStack, ES_CELL_V_FILL, ES_STYLE_SEPARATOR_VERTICAL, 0, 0);
|
||||
textboxLog = EsTextboxCreate(panelStack, ES_TEXTBOX_MULTILINE | ES_CELL_FILL, &styleTextboxLog);
|
||||
textboxLog->accessKey = 'O';
|
||||
EsTextboxSetReadOnly(textboxLog, true);
|
||||
}
|
||||
|
||||
void _start() {
|
||||
_init();
|
||||
|
||||
int argc;
|
||||
char **argv;
|
||||
_init();
|
||||
EsPOSIXInitialise(&argc, &argv);
|
||||
exit(BuildCore(argc, argv));
|
||||
|
||||
EsProcessCreateData createData;
|
||||
EsProcessGetCreateData(&createData);
|
||||
|
||||
if (createData.subsystemID == ES_SUBSYSTEM_ID_POSIX) {
|
||||
exit(BuildCore(argc, argv));
|
||||
}
|
||||
|
||||
while (true) {
|
||||
EsMessage *message = EsMessageReceive();
|
||||
|
||||
if (message->type == ES_MSG_INSTANCE_CREATE) {
|
||||
InstanceCreate(message);
|
||||
} else if (message->type == ES_MSG_APPLICATION_EXIT) {
|
||||
EsHeapFree(singleConfigFileData, 0, NULL);
|
||||
singleConfigFileData = NULL;
|
||||
} else if (message->type == MSG_BUILD_SUCCESS || message->type == MSG_BUILD_FAILED) {
|
||||
buildRunning = false;
|
||||
EsCommandSetEnabled(&commandBuild, true);
|
||||
EsCommandSetEnabled(&commandLaunch, true);
|
||||
|
||||
EsTextboxMoveCaretRelative(textboxLog, ES_TEXTBOX_MOVE_CARET_ALL);
|
||||
|
||||
if (message->type == MSG_BUILD_FAILED) {
|
||||
EsTextboxInsert(textboxLog, INTERFACE_STRING(BuildCoreBuildFailed), false);
|
||||
EsElementFocus(buttonBuild, ES_ELEMENT_FOCUS_ONLY_IF_NO_FOCUSED_ELEMENT);
|
||||
} else {
|
||||
EsTextboxInsert(textboxLog, INTERFACE_STRING(BuildCoreBuildSuccess), false);
|
||||
EsElementFocus(buttonLaunch, ES_ELEMENT_FOCUS_ONLY_IF_NO_FOCUSED_ELEMENT);
|
||||
}
|
||||
|
||||
EsTextboxEnsureCaretVisible(textboxLog, false);
|
||||
_EsPathAnnouncePathMoved(NULL, 0, workingDirectory, workingDirectoryBytes);
|
||||
} else if (message->type == MSG_LOG) {
|
||||
char *copy = message->user.context1.p;
|
||||
EsTextboxMoveCaretRelative(textboxLog, ES_TEXTBOX_MOVE_CARET_ALL);
|
||||
EsTextboxInsert(textboxLog, copy, -1, false);
|
||||
EsTextboxEnsureCaretVisible(textboxLog, false);
|
||||
EsHeapFree(copy, 0, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
[general]
|
||||
name=Build Core
|
||||
hidden=1
|
||||
permission_all_files=1
|
||||
permission_posix_subsystem=1
|
||||
permission_run_temporary_application=1
|
||||
|
||||
[build]
|
||||
source=util/build_core.c
|
||||
|
||||
[@file_type]
|
||||
extension=build_core
|
||||
name=Build config
|
||||
icon=icon_text_x_makefile
|
||||
|
||||
[@handler]
|
||||
extension=build_core
|
||||
action=open
|
|
@ -1,3 +1,7 @@
|
|||
// This file is part of the Essence operating system.
|
||||
// It is released under the terms of the MIT license -- see LICENSE.md.
|
||||
// Written by: nakst.
|
||||
|
||||
// TODO Searching for a specific option.
|
||||
// TODO Option descriptions.
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
// This file is part of the Essence operating system.
|
||||
// It is released under the terms of the MIT license -- see LICENSE.md.
|
||||
// Written by: nakst.
|
||||
|
||||
#define UI_IMPLEMENTATION
|
||||
#define ES_CRT_WITHOUT_PREFIX
|
||||
#define EsPainter _EsPainter
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
// This file is part of the Essence operating system.
|
||||
// It is released under the terms of the MIT license -- see LICENSE.md.
|
||||
// Written by: nakst.
|
||||
|
||||
// TODO Extensions: binary search, shifting glyphs in editor, undo/redo.
|
||||
|
||||
#define UI_IMPLEMENTATION
|
||||
|
|
|
@ -145,7 +145,7 @@ Token NextToken() {
|
|||
|
||||
position--;
|
||||
|
||||
#define COMPARE_KEYWORD(x, y) if (strlen(x) == token.value && 0 == memcmp(x, token.text, token.value)) token.type = y
|
||||
#define COMPARE_KEYWORD(x, y) if (strlen(x) == (size_t) token.value && 0 == memcmp(x, token.text, token.value)) token.type = y
|
||||
COMPARE_KEYWORD("define", TOKEN_DEFINE);
|
||||
COMPARE_KEYWORD("enum", TOKEN_ENUM);
|
||||
COMPARE_KEYWORD("struct", TOKEN_STRUCT);
|
||||
|
@ -1005,6 +1005,8 @@ void OutputZigType(Entry *entry) {
|
|||
}
|
||||
|
||||
void OutputZigVariable(Entry *entry, bool expandStrings, const char *nameSuffix) {
|
||||
(void) expandStrings;
|
||||
|
||||
if (0 == strcmp(entry->name, "error")) {
|
||||
entry->name[3] = ' ';
|
||||
entry->name[4] = ' ';
|
||||
|
|
Loading…
Reference in New Issue