=> ab3ed41e6d6a2f30e781c7202741e066a2b21d7b
[1mdiff --git a/src/app.c b/src/app.c[m [1mindex 3c354438..a6a2a0a2 100644[m [1m--- a/src/app.c[m [1m+++ b/src/app.c[m [36m@@ -1645,19 +1645,21 @@[m [mvoid processEvents_App(enum iAppEventMode eventMode) {[m }[m }[m #endif[m [31m- /* Per-window processing. */[m iBool wasUsed = iFalse;[m [31m- listWindows_App_(d, &windows);[m [31m- iConstForEach(PtrArray, iter, &windows) {[m [31m- iWindow *window = iter.ptr;[m [31m- setCurrent_Window(window);[m [31m- window->lastHover = window->hover;[m [31m- wasUsed = processEvent_Window(window, &ev);[m [31m- if (ev.type == SDL_MOUSEMOTION) {[m [31m- /* Only offered to the frontmost window. */[m [31m- break;[m [32m+[m[32m /* Per-window processing. */[m [32m+[m[32m if (!isEmpty_PtrArray(&d->mainWindows)) {[m [32m+[m[32m listWindows_App_(d, &windows);[m [32m+[m[32m iConstForEach(PtrArray, iter, &windows) {[m [32m+[m[32m iWindow *window = iter.ptr;[m [32m+[m[32m setCurrent_Window(window);[m [32m+[m[32m window->lastHover = window->hover;[m [32m+[m[32m wasUsed = processEvent_Window(window, &ev);[m [32m+[m[32m if (ev.type == SDL_MOUSEMOTION) {[m [32m+[m[32m /* Only offered to the frontmost window. */[m [32m+[m[32m break;[m [32m+[m[32m }[m [32m+[m[32m if (wasUsed) break;[m }[m [31m- if (wasUsed) break;[m }[m setCurrent_Window(d->window);[m if (!wasUsed) {[m [36m@@ -1778,7 +1780,14 @@[m [mstatic int resizeWatcher_(void *user, SDL_Event *event) {[m dispatchEvent_Window(as_Window(d->window), &u);[m }[m #endif[m [31m- drawWhileResizing_MainWindow(d->window, winev->data1, winev->data2);[m [32m+[m[32m /* Find the window that is being resized and redraw it immediately. */[m [32m+[m[32m iConstForEach(PtrArray, i, &d->mainWindows) {[m [32m+[m[32m const iMainWindow *win = i.ptr;[m [32m+[m[32m if (SDL_GetWindowID(win->base.win) == winev->windowID) {[m [32m+[m[32m drawWhileResizing_MainWindow(d->window, winev->data1, winev->data2);[m [32m+[m[32m break;[m [32m+[m[32m }[m [32m+[m[32m }[m }[m return 0;[m }[m [36m@@ -2051,6 +2060,9 @@[m [mvoid addWindow_App(iMainWindow *win) {[m void removeWindow_App(iMainWindow *win) {[m iApp *d = &app_;[m removeOne_PtrArray(&d->mainWindows, win);[m [32m+[m[32m if (isEmpty_PtrArray(&d->mainWindows)) {[m [32m+[m[32m d->window = NULL;[m [32m+[m[32m }[m }[m [m size_t numWindows_App(void) {[m [36m@@ -2386,11 +2398,6 @@[m [mvoid closeWindow_App(iMainWindow *win) {[m }[m }[m }[m [31m- if (isEmpty_PtrArray(&d->mainWindows)) {[m [31m- d->window = NULL;[m [31m- setActiveWindow_App(NULL);[m [31m- setCurrent_Window(NULL);[m [31m- }[m }[m [m static iBool handleIdentityCreationCommands_(iWidget *dlg, const char *cmd) {[m [36m@@ -2552,36 +2559,10 @@[m [mstatic void invalidateCachedDocuments_App_(void) {[m }[m }[m [m [31m-iBool handleCommand_App(const char *cmd) {[m [31m- iApp *d = &app_;[m [32m+[m[32mstatic iBool handleNonWindowRelatedCommand_App_(iApp *d, const char *cmd) {[m const iBool isFrozen = !d->window || d->window->isDrawFrozen;[m [31m- /* TODO: Maybe break this up a little bit? There's a very long list of ifs here. */[m [31m- if (equal_Command(cmd, "config.error")) {[m [31m- makeSimpleMessage_Widget(uiTextCaution_ColorEscape "CONFIG ERROR",[m [31m- format_CStr("Error in config file: %s\n"[m [31m- "See \"about:debug\" for details.",[m [31m- suffixPtr_Command(cmd, "where")));[m [31m- return iTrue;[m [31m- }[m [31m-#if 0 /* disabled in v1.11 */[m [31m- else if (equal_Command(cmd, "fontpack.suggest.classic")) {[m [31m- /* TODO: Don't use this when system fonts are accessible. */[m [31m- if (!isInstalled_Fonts("classic-set") && !isInstalled_Fonts("cjk")) {[m [31m- makeQuestion_Widget([m [31m- uiHeading_ColorEscape "${heading.fontpack.classic}",[m [31m- "${dlg.fontpack.classic.msg}",[m [31m- (iMenuItem[]){[m [31m- { "${cancel}" },[m [31m- { uiTextAction_ColorEscape "${dlg.fontpack.classic}",[m [31m- 0,[m [31m- 0,[m [31m- "!open newtab:1 url:gemini://skyjake.fi/fonts/classic-set.fontpack" } },[m [31m- 2);[m [31m- }[m [31m- return iTrue;[m [31m- }[m [31m-#endif[m [31m- else if (equal_Command(cmd, "prefs.changed")) {[m [32m+[m[32m /* Commands related to preferences. */[m [32m+[m[32m if (equal_Command(cmd, "prefs.changed")) {[m savePrefs_App_(d);[m return iTrue;[m }[m [36m@@ -2641,28 +2622,6 @@[m [miBool handleCommand_App(const char *cmd) {[m d->prefs.langTo = argLabel_Command(cmd, "to");[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "ui.split")) {[m [31m- if (argLabel_Command(cmd, "swap")) {[m [31m- swapRoots_MainWindow(d->window);[m [31m- return iTrue;[m [31m- }[m [31m- if (argLabel_Command(cmd, "focusother")) {[m [31m- iWindow *baseWin = &d->window->base;[m [31m- if (baseWin->roots[1]) {[m [31m- baseWin->keyRoot =[m [31m- (baseWin->keyRoot == baseWin->roots[1] ? baseWin->roots[0] : baseWin->roots[1]);[m [31m- }[m [31m- }[m [31m- d->window->pendingSplitMode =[m [31m- (argLabel_Command(cmd, "axis") ? vertical_WindowSplit : 0) | (arg_Command(cmd) << 1);[m [31m- const char *url = suffixPtr_Command(cmd, "url");[m [31m- setCStr_String(d->window->pendingSplitUrl, url ? url : "");[m [31m- if (hasLabel_Command(cmd, "origin")) {[m [31m- set_String(d->window->pendingSplitOrigin, string_Command(cmd, "origin"));[m [31m- }[m [31m- postRefresh_App();[m [31m- return iTrue;[m [31m- }[m else if (equal_Command(cmd, "window.retain")) {[m d->prefs.retainWindowSize = arg_Command(cmd);[m return iTrue;[m [36m@@ -2671,186 +2630,174 @@[m [miBool handleCommand_App(const char *cmd) {[m d->prefs.customFrame = arg_Command(cmd);[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "window.maximize")) {[m [31m- const size_t winIndex = argU32Label_Command(cmd, "index");[m [31m- if (winIndex < size_PtrArray(&d->mainWindows)) {[m [31m- iMainWindow *win = at_PtrArray(&d->mainWindows, winIndex);[m [31m- if (!argLabel_Command(cmd, "toggle")) {[m [31m- setSnap_MainWindow(win, maximized_WindowSnap);[m [31m- }[m [31m- else {[m [31m- setSnap_MainWindow([m [31m- win, snap_MainWindow(win) == maximized_WindowSnap ? 0 : maximized_WindowSnap);[m [31m- }[m [31m- }[m [31m- return iTrue;[m [31m- }[m [31m- else if (equal_Command(cmd, "window.fullscreen")) {[m [31m- const iBool wasFull = snap_MainWindow(d->window) == fullscreen_WindowSnap;[m [31m- setSnap_MainWindow(d->window, wasFull ? 0 : fullscreen_WindowSnap);[m [31m- postCommandf_App("window.fullscreen.changed arg:%d", !wasFull);[m [31m- return iTrue;[m [31m- }[m else if (equal_Command(cmd, "prefs.retaintabs.changed")) {[m d->prefs.retainTabs = arg_Command(cmd);[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "font.reset")) {[m [31m- resetFonts_App();[m [32m+[m[32m else if (equal_Command(cmd, "prefs.font.smooth.changed")) {[m [32m+[m[32m if (!isFrozen) {[m [32m+[m[32m setFreezeDraw_MainWindow(get_MainWindow(), iTrue);[m [32m+[m[32m }[m [32m+[m[32m d->prefs.fontSmoothing = arg_Command(cmd) != 0;[m [32m+[m[32m if (!isFrozen) {[m [32m+[m[32m resetFontCache_Text(text_Window(get_MainWindow())); /* clear the glyph cache */[m [32m+[m[32m postCommand_App("font.changed");[m [32m+[m[32m postCommand_App("window.unfreeze");[m [32m+[m[32m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "font.reload")) {[m [31m- reload_Fonts(); /* also does font cache reset, window invalidation */[m [32m+[m[32m else if (equal_Command(cmd, "prefs.gemtext.ansi.fg.changed")) {[m [32m+[m[32m iChangeFlags(d->prefs.gemtextAnsiEscapes, allowFg_AnsiFlag, arg_Command(cmd));[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "font.find")) {[m [31m- searchOnlineLibraryForCharacters_Fonts(string_Command(cmd, "chars"));[m [32m+[m[32m else if (equal_Command(cmd, "prefs.gemtext.ansi.bg.changed")) {[m [32m+[m[32m iChangeFlags(d->prefs.gemtextAnsiEscapes, allowBg_AnsiFlag, arg_Command(cmd));[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "font.found")) {[m [31m- if (hasLabel_Command(cmd, "error")) {[m [31m- makeSimpleMessage_Widget("${heading.glyphfinder}",[m [31m- format_CStr("%d %s",[m [31m- argLabel_Command(cmd, "error"),[m [31m- suffixPtr_Command(cmd, "msg")));[m [31m- return iTrue;[m [31m- }[m [31m- iString *src = collectNew_String();[m [31m- setCStr_String(src, "# ${heading.glyphfinder.results}\n\n");[m [31m- iRangecc path = iNullRange;[m [31m- iBool isFirst = iTrue;[m [31m- while (nextSplit_Rangecc(range_Command(cmd, "packs"), ",", &path)) {[m [31m- if (isFirst) {[m [31m- appendCStr_String(src, "${glyphfinder.results}\n\n");[m [31m- }[m [31m- iRangecc fpath = path;[m [31m- iRangecc fsize = path;[m [31m- fpath.end = strchr(fpath.start, ';');[m [31m- fsize.start = fpath.end + 1;[m [31m- const uint32_t size = strtoul(fsize.start, NULL, 10);[m [31m- appendFormat_String(src, "=> gemini://skyjake.fi/fonts/%s %s (%.1f MB)\n",[m [31m- cstr_Rangecc(fpath),[m [31m- cstr_Rangecc(fpath),[m [31m- (double) size / 1.0e6);[m [31m- isFirst = iFalse;[m [31m- }[m [31m- if (isFirst) {[m [31m- appendFormat_String(src, "${glyphfinder.results.empty}\n");[m [31m- }[m [31m- appendCStr_String(src, "\n=> about:fonts ${menu.fonts}");[m [31m- iDocumentWidget *page = newTab_App(NULL, iTrue);[m [31m- translate_Lang(src);[m [31m- setUrlAndSource_DocumentWidget(page,[m [31m- collectNewCStr_String(""),[m [31m- collectNewCStr_String("text/gemini"),[m [31m- utf8_String(src));[m [32m+[m[32m else if (equal_Command(cmd, "prefs.gemtext.ansi.fontstyle.changed")) {[m [32m+[m[32m iChangeFlags(d->prefs.gemtextAnsiEscapes, allowFontStyle_AnsiFlag, arg_Command(cmd));[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "font.set")) {[m [32m+[m[32m else if (equal_Command(cmd, "prefs.mono.gemini.changed") ||[m [32m+[m[32m equal_Command(cmd, "prefs.mono.gopher.changed")) {[m [32m+[m[32m const iBool isSet = (arg_Command(cmd) != 0);[m if (!isFrozen) {[m setFreezeDraw_MainWindow(get_MainWindow(), iTrue);[m }[m [31m- struct {[m [31m- const char *label;[m [31m- enum iPrefsString ps;[m [31m- int fontId;[m [31m- } params[] = {[m [31m- { "ui", uiFont_PrefsString, default_FontId },[m [31m- { "mono", monospaceFont_PrefsString, monospace_FontId },[m [31m- { "heading", headingFont_PrefsString, documentHeading_FontId },[m [31m- { "body", bodyFont_PrefsString, documentBody_FontId },[m [31m- { "monodoc", monospaceDocumentFont_PrefsString, documentMonospace_FontId },[m [31m- };[m [31m- iBool wasChanged = iFalse;[m [31m- iForIndices(i, params) {[m [31m- if (hasLabel_Command(cmd, params[i].label)) {[m [31m- iString *ps = &d->prefs.strings[params[i].ps];[m [31m- const iString *newFont = string_Command(cmd, params[i].label);[m [31m- if (!equal_String(ps, newFont)) {[m [31m- set_String(ps, newFont);[m [31m- wasChanged = iTrue;[m [31m- }[m [31m- }[m [32m+[m[32m if (startsWith_CStr(cmd, "prefs.mono.gemini")) {[m [32m+[m[32m d->prefs.monospaceGemini = isSet;[m }[m [31m- if (wasChanged) {[m [31m- if (isFinishedLaunching_App()) { /* there's a reset when launch is finished */[m [31m- resetFonts_Text(text_Window(get_MainWindow()));[m [31m- }[m [31m- postCommand_App("font.changed");[m [32m+[m[32m else {[m [32m+[m[32m d->prefs.monospaceGopher = isSet;[m }[m if (!isFrozen) {[m [32m+[m[32m postCommand_App("font.changed");[m postCommand_App("window.unfreeze");[m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "zoom.set")) {[m [31m- if (!isFrozen) {[m [31m- setFreezeDraw_MainWindow(get_MainWindow(), iTrue); /* no intermediate draws before docs updated */[m [32m+[m[32m else if (equal_Command(cmd, "prefs.boldlink.dark.changed") ||[m [32m+[m[32m equal_Command(cmd, "prefs.boldlink.light.changed") ||[m [32m+[m[32m equal_Command(cmd, "prefs.boldlink.visited.changed")) {[m [32m+[m[32m const iBool isSet = (arg_Command(cmd) != 0);[m [32m+[m[32m if (startsWith_CStr(cmd, "prefs.boldlink.visited")) {[m [32m+[m[32m d->prefs.boldLinkVisited = isSet;[m }[m [31m- if (arg_Command(cmd) != d->prefs.zoomPercent) {[m [31m- d->prefs.zoomPercent = arg_Command(cmd);[m [31m- invalidateCachedDocuments_App_();[m [32m+[m[32m else if (startsWith_CStr(cmd, "prefs.boldlink.dark")) {[m [32m+[m[32m d->prefs.boldLinkDark = isSet;[m }[m [31m- setDocumentFontSize_Text(text_Window(d->window), (float) d->prefs.zoomPercent / 100.0f);[m [31m- if (!isFrozen) {[m [32m+[m[32m else {[m [32m+[m[32m d->prefs.boldLinkLight = isSet;[m [32m+[m[32m }[m [32m+[m[32m if (!d->isLoadingPrefs) {[m postCommand_App("font.changed");[m [31m- postCommand_App("window.unfreeze");[m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "zoom.delta")) {[m [31m- if (!isFrozen) {[m [31m- setFreezeDraw_MainWindow(get_MainWindow(), iTrue); /* no intermediate draws before docs updated */[m [31m- }[m [31m- int delta = arg_Command(cmd);[m [31m- if (d->prefs.zoomPercent < 100 || (delta < 0 && d->prefs.zoomPercent == 100)) {[m [31m- delta /= 2;[m [31m- }[m [31m- d->prefs.zoomPercent = iClamp(d->prefs.zoomPercent + delta, 50, 200);[m [31m- invalidateCachedDocuments_App_();[m [31m- setDocumentFontSize_Text(text_Window(d->window), (float) d->prefs.zoomPercent / 100.0f);[m [31m- if (!isFrozen) {[m [31m- postCommand_App("font.changed");[m [31m- postCommand_App("window.unfreeze");[m [32m+[m[32m else if (equal_Command(cmd, "prefs.biglede.changed")) {[m [32m+[m[32m d->prefs.bigFirstParagraph = arg_Command(cmd) != 0;[m [32m+[m[32m if (!d->isLoadingPrefs) {[m [32m+[m[32m postCommand_App("document.layout.changed");[m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "smoothscroll")) {[m [31m- d->prefs.smoothScrolling = arg_Command(cmd);[m [32m+[m[32m else if (equal_Command(cmd, "prefs.justify.changed")) {[m [32m+[m[32m d->prefs.justifyParagraph = arg_Command(cmd) != 0;[m [32m+[m[32m if (!d->isLoadingPrefs) {[m [32m+[m[32m postCommand_App("document.layout.changed");[m [32m+[m[32m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "scrollspeed")) {[m [31m- const int type = argLabel_Command(cmd, "type");[m [31m- if (type == keyboard_ScrollType || type == mouse_ScrollType) {[m [31m- d->prefs.smoothScrollSpeed[type] = iClamp(arg_Command(cmd), 1, 40);[m [32m+[m[32m else if (equal_Command(cmd, "prefs.plaintext.wrap.changed")) {[m [32m+[m[32m d->prefs.plainTextWrap = arg_Command(cmd) != 0;[m [32m+[m[32m if (!d->isLoadingPrefs) {[m [32m+[m[32m postCommand_App("document.layout.changed");[m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "decodeurls")) {[m [31m- d->prefs.decodeUserVisibleURLs = arg_Command(cmd);[m [32m+[m[32m else if (equal_Command(cmd, "prefs.sideicon.changed")) {[m [32m+[m[32m d->prefs.sideIcon = arg_Command(cmd) != 0;[m [32m+[m[32m postRefresh_App();[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "imageloadscroll")) {[m [31m- d->prefs.loadImageInsteadOfScrolling = arg_Command(cmd);[m [32m+[m[32m else if (equal_Command(cmd, "prefs.centershort.changed")) {[m [32m+[m[32m d->prefs.centerShortDocs = arg_Command(cmd) != 0;[m [32m+[m[32m if (!isFrozen) {[m [32m+[m[32m invalidate_Window(d->window);[m [32m+[m[32m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "hidetoolbarscroll")) {[m [31m- d->prefs.hideToolbarOnScroll = arg_Command(cmd);[m [31m- if (!d->prefs.hideToolbarOnScroll) {[m [31m- showToolbar_Root(get_Root(), iTrue);[m [31m- }[m [32m+[m[32m else if (equal_Command(cmd, "prefs.collapsepreonload.changed")) {[m [32m+[m[32m d->prefs.collapsePreOnLoad = arg_Command(cmd) != 0;[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "returnkey.set")) {[m [31m- d->prefs.returnKey = arg_Command(cmd);[m [32m+[m[32m else if (equal_Command(cmd, "prefs.hoverlink.changed")) {[m [32m+[m[32m d->prefs.hoverLink = arg_Command(cmd) != 0;[m [32m+[m[32m postRefresh_App();[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "pinsplit.set")) {[m [31m- d->prefs.pinSplit = arg_Command(cmd);[m [32m+[m[32m else if (equal_Command(cmd, "prefs.hoverlink.toggle")) {[m [32m+[m[32m d->prefs.hoverLink = !d->prefs.hoverLink;[m [32m+[m[32m postRefresh_App();[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "theme.set")) {[m [31m- const int isAuto = argLabel_Command(cmd, "auto");[m [32m+[m[32m else if (equal_Command(cmd, "prefs.dataurl.openimages.changed")) {[m [32m+[m[32m d->prefs.openDataUrlImagesOnLoad = arg_Command(cmd) != 0;[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "prefs.archive.openindex.changed")) {[m [32m+[m[32m d->prefs.openArchiveIndexPages = arg_Command(cmd) != 0;[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "prefs.bookmarks.addbottom.changed")) {[m [32m+[m[32m d->prefs.addBookmarksToBottom = arg_Command(cmd) != 0;[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "prefs.font.warnmissing.changed")) {[m [32m+[m[32m d->prefs.warnAboutMissingGlyphs = arg_Command(cmd) != 0;[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "prefs.animate.changed")) {[m [32m+[m[32m d->prefs.uiAnimations = arg_Command(cmd) != 0;[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "prefs.blink.changed")) {[m [32m+[m[32m d->prefs.blinkingCursor = arg_Command(cmd) != 0;[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "prefs.time.24h.changed")) {[m [32m+[m[32m d->prefs.time24h = arg_Command(cmd) != 0;[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "smoothscroll")) {[m [32m+[m[32m d->prefs.smoothScrolling = arg_Command(cmd);[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "scrollspeed")) {[m [32m+[m[32m const int type = argLabel_Command(cmd, "type");[m [32m+[m[32m if (type == keyboard_ScrollType || type == mouse_ScrollType) {[m [32m+[m[32m d->prefs.smoothScrollSpeed[type] = iClamp(arg_Command(cmd), 1, 40);[m [32m+[m[32m }[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "decodeurls")) {[m [32m+[m[32m d->prefs.decodeUserVisibleURLs = arg_Command(cmd);[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "imageloadscroll")) {[m [32m+[m[32m d->prefs.loadImageInsteadOfScrolling = arg_Command(cmd);[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "returnkey.set")) {[m [32m+[m[32m d->prefs.returnKey = arg_Command(cmd);[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "pinsplit.set")) {[m [32m+[m[32m d->prefs.pinSplit = arg_Command(cmd);[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "theme.set")) {[m [32m+[m[32m const int isAuto = argLabel_Command(cmd, "auto");[m d->prefs.theme = arg_Command(cmd);[m if (!isAuto) {[m if (isDark_ColorTheme(d->prefs.theme) && d->isDarkSystemTheme) {[m [36m@@ -2923,246 +2870,426 @@[m [miBool handleCommand_App(const char *cmd) {[m postCommand_App("document.layout.changed redo:1");[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.font.smooth.changed")) {[m [32m+[m[32m else if (equal_Command(cmd, "ansiescape")) {[m [32m+[m[32m d->prefs.gemtextAnsiEscapes = arg_Command(cmd);[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "saturation.set")) {[m [32m+[m[32m d->prefs.saturation = (float) arg_Command(cmd) / 100.0f;[m if (!isFrozen) {[m [31m- setFreezeDraw_MainWindow(get_MainWindow(), iTrue);[m [32m+[m[32m invalidate_Window(d->window);[m }[m [31m- d->prefs.fontSmoothing = arg_Command(cmd) != 0;[m [31m- if (!isFrozen) {[m [31m- resetFontCache_Text(text_Window(get_MainWindow())); /* clear the glyph cache */[m [31m- postCommand_App("font.changed");[m [31m- postCommand_App("window.unfreeze");[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "cachesize.set")) {[m [32m+[m[32m d->prefs.maxCacheSize = arg_Command(cmd);[m [32m+[m[32m if (d->prefs.maxCacheSize <= 0) {[m [32m+[m[32m d->prefs.maxCacheSize = 0;[m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "ansiescape")) {[m [31m- d->prefs.gemtextAnsiEscapes = arg_Command(cmd);[m [32m+[m[32m else if (equal_Command(cmd, "memorysize.set")) {[m [32m+[m[32m d->prefs.maxMemorySize = arg_Command(cmd);[m [32m+[m[32m if (d->prefs.maxMemorySize <= 0) {[m [32m+[m[32m d->prefs.maxMemorySize = 0;[m [32m+[m[32m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.gemtext.ansi.fg.changed")) {[m [31m- iChangeFlags(d->prefs.gemtextAnsiEscapes, allowFg_AnsiFlag, arg_Command(cmd));[m [32m+[m[32m else if (equal_Command(cmd, "urlsize.set")) {[m [32m+[m[32m d->prefs.maxUrlSize = arg_Command(cmd);[m [32m+[m[32m if (d->prefs.maxUrlSize < 1024) {[m [32m+[m[32m d->prefs.maxUrlSize = 1024; /* Gemini protocol requirement */[m [32m+[m[32m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.gemtext.ansi.bg.changed")) {[m [31m- iChangeFlags(d->prefs.gemtextAnsiEscapes, allowBg_AnsiFlag, arg_Command(cmd));[m [32m+[m[32m else if (equal_Command(cmd, "searchurl")) {[m [32m+[m[32m iString *url = &d->prefs.strings[searchUrl_PrefsString];[m [32m+[m[32m setCStr_String(url, suffixPtr_Command(cmd, "address"));[m [32m+[m[32m if (startsWith_String(url, "//")) {[m [32m+[m[32m prependCStr_String(url, "gemini:");[m [32m+[m[32m }[m [32m+[m[32m if (!isEmpty_String(url) && !startsWithCase_String(url, "gemini://")) {[m [32m+[m[32m prependCStr_String(url, "gemini://");[m [32m+[m[32m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.gemtext.ansi.fontstyle.changed")) {[m [31m- iChangeFlags(d->prefs.gemtextAnsiEscapes, allowFontStyle_AnsiFlag, arg_Command(cmd));[m [32m+[m[32m else if (equal_Command(cmd, "proxy.gemini")) {[m [32m+[m[32m setCStr_String(&d->prefs.strings[geminiProxy_PrefsString], suffixPtr_Command(cmd, "address"));[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.mono.gemini.changed") ||[m [31m- equal_Command(cmd, "prefs.mono.gopher.changed")) {[m [31m- const iBool isSet = (arg_Command(cmd) != 0);[m [31m- if (!isFrozen) {[m [31m- setFreezeDraw_MainWindow(get_MainWindow(), iTrue);[m [32m+[m[32m else if (equal_Command(cmd, "proxy.gopher")) {[m [32m+[m[32m setCStr_String(&d->prefs.strings[gopherProxy_PrefsString], suffixPtr_Command(cmd, "address"));[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "proxy.http")) {[m [32m+[m[32m setCStr_String(&d->prefs.strings[httpProxy_PrefsString], suffixPtr_Command(cmd, "address"));[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m#if defined (LAGRANGE_ENABLE_DOWNLOAD_EDIT)[m [32m+[m[32m else if (equal_Command(cmd, "downloads")) {[m [32m+[m[32m setCStr_String(&d->prefs.strings[downloadDir_PrefsString], suffixPtr_Command(cmd, "path"));[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m#endif[m [32m+[m[32m else if (equal_Command(cmd, "downloads.open")) {[m [32m+[m[32m postCommandf_App("open newtab:%d url:%s",[m [32m+[m[32m argLabel_Command(cmd, "newtab"),[m [32m+[m[32m cstrCollect_String(makeFileUrl_String(downloadDir_App())));[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "ca.file")) {[m [32m+[m[32m setCStr_String(&d->prefs.strings[caFile_PrefsString], suffixPtr_Command(cmd, "path"));[m [32m+[m[32m if (!argLabel_Command(cmd, "noset")) {[m [32m+[m[32m setCACertificates_TlsRequest(&d->prefs.strings[caFile_PrefsString], &d->prefs.strings[caPath_PrefsString]);[m }[m [31m- if (startsWith_CStr(cmd, "prefs.mono.gemini")) {[m [31m- d->prefs.monospaceGemini = isSet;[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "ca.path")) {[m [32m+[m[32m setCStr_String(&d->prefs.strings[caPath_PrefsString], suffixPtr_Command(cmd, "path"));[m [32m+[m[32m if (!argLabel_Command(cmd, "noset")) {[m [32m+[m[32m setCACertificates_TlsRequest(&d->prefs.strings[caFile_PrefsString], &d->prefs.strings[caPath_PrefsString]);[m }[m [31m- else {[m [31m- d->prefs.monospaceGopher = isSet;[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "search")) {[m [32m+[m[32m const int newTab = argLabel_Command(cmd, "newtab");[m [32m+[m[32m const iString *query = collect_String(suffix_Command(cmd, "query"));[m [32m+[m[32m if (!isLikelyUrl_String(query)) {[m [32m+[m[32m const iString *url = searchQueryUrl_App(query);[m [32m+[m[32m if (!isEmpty_String(url)) {[m [32m+[m[32m postCommandf_App("open newtab:%d url:%s", newTab, cstr_String(url));[m [32m+[m[32m }[m }[m [31m- if (!isFrozen) {[m [31m- postCommand_App("font.changed");[m [31m- postCommand_App("window.unfreeze");[m [32m+[m[32m else {[m [32m+[m[32m postCommandf_App("open newtab:%d url:%s", newTab, cstr_String(query));[m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.boldlink.dark.changed") ||[m [31m- equal_Command(cmd, "prefs.boldlink.light.changed") ||[m [31m- equal_Command(cmd, "prefs.boldlink.visited.changed")) {[m [31m- const iBool isSet = (arg_Command(cmd) != 0);[m [31m- if (startsWith_CStr(cmd, "prefs.boldlink.visited")) {[m [31m- d->prefs.boldLinkVisited = isSet;[m [31m- }[m [31m- else if (startsWith_CStr(cmd, "prefs.boldlink.dark")) {[m [31m- d->prefs.boldLinkDark = isSet;[m [32m+[m[32m else if (equal_Command(cmd, "reveal")) {[m [32m+[m[32m const iString *path = NULL;[m [32m+[m[32m if (hasLabel_Command(cmd, "path")) {[m [32m+[m[32m path = suffix_Command(cmd, "path");[m }[m [31m- else {[m [31m- d->prefs.boldLinkLight = isSet;[m [32m+[m[32m else if (hasLabel_Command(cmd, "url")) {[m [32m+[m[32m path = collect_String(localFilePathFromUrl_String(suffix_Command(cmd, "url")));[m }[m [31m- if (!d->isLoadingPrefs) {[m [31m- postCommand_App("font.changed");[m [32m+[m[32m if (path) {[m [32m+[m[32m revealPath_App(path);[m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.biglede.changed")) {[m [31m- d->prefs.bigFirstParagraph = arg_Command(cmd) != 0;[m [31m- if (!d->isLoadingPrefs) {[m [31m- postCommand_App("document.layout.changed");[m [32m+[m[32m else if (equal_Command(cmd, "window.new")) {[m [32m+[m[32m iMainWindow *newWin = new_MainWindow(initialWindowRect_App_(d, numWindows_App()));[m [32m+[m[32m addWindow_App(newWin); /* takes ownership */[m [32m+[m[32m SDL_ShowWindow(newWin->base.win);[m [32m+[m[32m setCurrent_Window(newWin);[m [32m+[m[32m if (hasLabel_Command(cmd, "url")) {[m [32m+[m[32m postCommandf_Root(newWin->base.roots[0], "~open %s", cmd + 11 /* all arguments passed on */);[m }[m [32m+[m[32m else {[m [32m+[m[32m postCommand_Root(newWin->base.roots[0], "~navigate.home");[m [32m+[m[32m }[m [32m+[m[32m postCommand_Root(newWin->base.roots[0], "~window.unfreeze");[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.justify.changed")) {[m [31m- d->prefs.justifyParagraph = arg_Command(cmd) != 0;[m [31m- if (!d->isLoadingPrefs) {[m [31m- postCommand_App("document.layout.changed");[m [32m+[m[32m else if (equal_Command(cmd, "bookmarks.changed")) {[m [32m+[m[32m save_Bookmarks(d->bookmarks, dataDir_App_());[m [32m+[m[32m return iFalse;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "bookmarks.sort")) {[m [32m+[m[32m sort_Bookmarks(d->bookmarks, arg_Command(cmd), cmpTitleAscending_Bookmark);[m [32m+[m[32m postCommand_App("bookmarks.changed");[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "bookmarks.reload.remote")) {[m [32m+[m[32m fetchRemote_Bookmarks(bookmarks_App());[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "bookmarks.request.finished")) {[m [32m+[m[32m requestFinished_Bookmarks(bookmarks_App(), pointerLabel_Command(cmd, "req"));[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "feeds.refresh")) {[m [32m+[m[32m refresh_Feeds();[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "visited.changed")) {[m [32m+[m[32m save_Visited(d->visited, dataDir_App_());[m [32m+[m[32m return iFalse;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "idents.changed")) {[m [32m+[m[32m saveIdentities_GmCerts(d->certs);[m [32m+[m[32m return iFalse;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "ident.signin")) {[m [32m+[m[32m const iString *url = collect_String(suffix_Command(cmd, "url"));[m [32m+[m[32m signIn_GmCerts([m [32m+[m[32m d->certs,[m [32m+[m[32m findIdentity_GmCerts(d->certs, collect_Block(hexDecode_Rangecc(range_Command(cmd, "ident")))),[m [32m+[m[32m url);[m [32m+[m[32m postCommand_App("navigate.reload");[m [32m+[m[32m postCommand_App("idents.changed");[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "ident.signout")) {[m [32m+[m[32m iGmIdentity *ident = findIdentity_GmCerts([m [32m+[m[32m d->certs, collect_Block(hexDecode_Rangecc(range_Command(cmd, "ident"))));[m [32m+[m[32m if (arg_Command(cmd)) {[m [32m+[m[32m clearUse_GmIdentity(ident);[m [32m+[m[32m }[m [32m+[m[32m else {[m [32m+[m[32m setUse_GmIdentity(ident, collect_String(suffix_Command(cmd, "url")), iFalse);[m }[m [32m+[m[32m postCommand_App("navigate.reload");[m [32m+[m[32m postCommand_App("idents.changed");[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.plaintext.wrap.changed")) {[m [31m- d->prefs.plainTextWrap = arg_Command(cmd) != 0;[m [31m- if (!d->isLoadingPrefs) {[m [31m- postCommand_App("document.layout.changed");[m [32m+[m[32m else if (equal_Command(cmd, "os.theme.changed")) {[m [32m+[m[32m const int dark = argLabel_Command(cmd, "dark");[m [32m+[m[32m d->isDarkSystemTheme = dark;[m [32m+[m[32m if (d->prefs.useSystemTheme) {[m [32m+[m[32m const int contrast = argLabel_Command(cmd, "contrast");[m [32m+[m[32m const int preferred = d->prefs.systemPreferredColorTheme[dark ^ 1];[m [32m+[m[32m postCommandf_App("theme.set arg:%d auto:1",[m [32m+[m[32m preferred >= 0 ? preferred[m [32m+[m[32m : dark ? (contrast ? pureBlack_ColorTheme : dark_ColorTheme)[m [32m+[m[32m : (contrast ? pureWhite_ColorTheme : light_ColorTheme));[m }[m [32m+[m[32m return iFalse;[m [32m+[m[32m }[m [32m+[m[32m else if (equal_Command(cmd, "updater.check")) {[m [32m+[m[32m checkNow_Updater();[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.sideicon.changed")) {[m [31m- d->prefs.sideIcon = arg_Command(cmd) != 0;[m [31m- postRefresh_App();[m [32m+[m[32m else if (equal_Command(cmd, "fontpack.enable")) {[m [32m+[m[32m const iString *packId = collect_String(suffix_Command(cmd, "id"));[m [32m+[m[32m enablePack_Fonts(packId, arg_Command(cmd));[m [32m+[m[32m postCommand_App("navigate.reload");[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.centershort.changed")) {[m [31m- d->prefs.centerShortDocs = arg_Command(cmd) != 0;[m [31m- if (!isFrozen) {[m [31m- invalidate_Window(d->window);[m [32m+[m[32m#if defined (LAGRANGE_ENABLE_IPC)[m [32m+[m[32m else if (equal_Command(cmd, "ipc.list.urls")) {[m [32m+[m[32m iProcessId pid = argLabel_Command(cmd, "pid");[m [32m+[m[32m if (pid) {[m [32m+[m[32m iString *urls = collectNew_String();[m [32m+[m[32m iConstForEach(ObjectList, i, iClob(listDocuments_App(NULL))) {[m [32m+[m[32m append_String(urls, url_DocumentWidget(i.object));[m [32m+[m[32m appendCStr_String(urls, "\n");[m [32m+[m[32m }[m [32m+[m[32m write_Ipc(pid, urls, response_IpcWrite);[m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.collapsepreonload.changed")) {[m [31m- d->prefs.collapsePreOnLoad = arg_Command(cmd) != 0;[m [32m+[m[32m else if (equal_Command(cmd, "ipc.active.url")) {[m [32m+[m[32m write_Ipc(argLabel_Command(cmd, "pid"),[m [32m+[m[32m collectNewFormat_String([m [32m+[m[32m "%s\n", d->window ? cstr_String(url_DocumentWidget(document_App())) : ""),[m [32m+[m[32m response_IpcWrite);[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.hoverlink.changed")) {[m [31m- d->prefs.hoverLink = arg_Command(cmd) != 0;[m [31m- postRefresh_App();[m [32m+[m[32m else if (equal_Command(cmd, "ipc.signal")) {[m [32m+[m[32m if (argLabel_Command(cmd, "raise")) {[m [32m+[m[32m if (d->window && d->window->base.win) {[m [32m+[m[32m SDL_RaiseWindow(d->window->base.win);[m [32m+[m[32m }[m [32m+[m[32m }[m [32m+[m[32m signal_Ipc(arg_Command(cmd));[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.hoverlink.toggle")) {[m [31m- d->prefs.hoverLink = !d->prefs.hoverLink;[m [31m- postRefresh_App();[m [31m- return iTrue;[m [32m+[m[32m#endif /* defined (LAGRANGE_ENABLE_IPC) */[m [32m+[m[32m else if (equal_Command(cmd, "quit")) {[m [32m+[m[32m SDL_Event ev;[m [32m+[m[32m ev.type = SDL_QUIT;[m [32m+[m[32m SDL_PushEvent(&ev);[m }[m [31m- else if (equal_Command(cmd, "prefs.dataurl.openimages.changed")) {[m [31m- d->prefs.openDataUrlImagesOnLoad = arg_Command(cmd) != 0;[m [32m+[m[32m return iFalse;[m [32m+[m[32m}[m [32m+[m [32m+[m[32miBool handleCommand_App(const char *cmd) {[m [32m+[m[32m iApp *d = &app_;[m [32m+[m[32m const iBool isFrozen = !d->window || d->window->isDrawFrozen;[m [32m+[m[32m const iBool isHeadless = numWindows_App() == 0;[m [32m+[m[32m if (handleNonWindowRelatedCommand_App_(d, cmd)) {[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.archive.openindex.changed")) {[m [31m- d->prefs.openArchiveIndexPages = arg_Command(cmd) != 0;[m [32m+[m[32m if (isHeadless) {[m [32m+[m[32m /* All the subsequent commands assume that a window exists. */[m [32m+[m[32m return iFalse;[m [32m+[m[32m }[m [32m+[m[32m /* TODO: Maybe break this up a little bit? There's a very long list of ifs here. */[m [32m+[m[32m if (equal_Command(cmd, "config.error")) {[m [32m+[m[32m makeSimpleMessage_Widget(uiTextCaution_ColorEscape "CONFIG ERROR",[m [32m+[m[32m format_CStr("Error in config file: %s\n"[m [32m+[m[32m "See \"about:debug\" for details.",[m [32m+[m[32m suffixPtr_Command(cmd, "where")));[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.bookmarks.addbottom.changed")) {[m [31m- d->prefs.addBookmarksToBottom = arg_Command(cmd) != 0;[m [32m+[m[32m else if (equal_Command(cmd, "ui.split")) {[m [32m+[m[32m if (argLabel_Command(cmd, "swap")) {[m [32m+[m[32m swapRoots_MainWindow(d->window);[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m if (argLabel_Command(cmd, "focusother")) {[m [32m+[m[32m iWindow *baseWin = &d->window->base;[m [32m+[m[32m if (baseWin->roots[1]) {[m [32m+[m[32m baseWin->keyRoot =[m [32m+[m[32m (baseWin->keyRoot == baseWin->roots[1] ? baseWin->roots[0] : baseWin->roots[1]);[m [32m+[m[32m }[m [32m+[m[32m }[m [32m+[m[32m d->window->pendingSplitMode =[m [32m+[m[32m (argLabel_Command(cmd, "axis") ? vertical_WindowSplit : 0) | (arg_Command(cmd) << 1);[m [32m+[m[32m const char *url = suffixPtr_Command(cmd, "url");[m [32m+[m[32m setCStr_String(d->window->pendingSplitUrl, url ? url : "");[m [32m+[m[32m if (hasLabel_Command(cmd, "origin")) {[m [32m+[m[32m set_String(d->window->pendingSplitOrigin, string_Command(cmd, "origin"));[m [32m+[m[32m }[m [32m+[m[32m postRefresh_App();[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.font.warnmissing.changed")) {[m [31m- d->prefs.warnAboutMissingGlyphs = arg_Command(cmd) != 0;[m [32m+[m[32m else if (equal_Command(cmd, "window.maximize")) {[m [32m+[m[32m const size_t winIndex = argU32Label_Command(cmd, "index");[m [32m+[m[32m if (winIndex < size_PtrArray(&d->mainWindows)) {[m [32m+[m[32m iMainWindow *win = at_PtrArray(&d->mainWindows, winIndex);[m [32m+[m[32m if (!argLabel_Command(cmd, "toggle")) {[m [32m+[m[32m setSnap_MainWindow(win, maximized_WindowSnap);[m [32m+[m[32m }[m [32m+[m[32m else {[m [32m+[m[32m setSnap_MainWindow([m [32m+[m[32m win, snap_MainWindow(win) == maximized_WindowSnap ? 0 : maximized_WindowSnap);[m [32m+[m[32m }[m [32m+[m[32m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.animate.changed")) {[m [31m- d->prefs.uiAnimations = arg_Command(cmd) != 0;[m [32m+[m[32m else if (equal_Command(cmd, "window.fullscreen")) {[m [32m+[m[32m const iBool wasFull = snap_MainWindow(d->window) == fullscreen_WindowSnap;[m [32m+[m[32m setSnap_MainWindow(d->window, wasFull ? 0 : fullscreen_WindowSnap);[m [32m+[m[32m postCommandf_App("window.fullscreen.changed arg:%d", !wasFull);[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.blink.changed")) {[m [31m- d->prefs.blinkingCursor = arg_Command(cmd) != 0;[m [32m+[m[32m else if (equal_Command(cmd, "font.reset")) {[m [32m+[m[32m resetFonts_App();[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "prefs.time.24h.changed")) {[m [31m- d->prefs.time24h = arg_Command(cmd) != 0;[m [32m+[m[32m else if (equal_Command(cmd, "font.reload")) {[m [32m+[m[32m reload_Fonts(); /* also does font cache reset, window invalidation */[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "saturation.set")) {[m [31m- d->prefs.saturation = (float) arg_Command(cmd) / 100.0f;[m [31m- if (!isFrozen) {[m [31m- invalidate_Window(d->window);[m [31m- }[m [32m+[m[32m else if (equal_Command(cmd, "font.find")) {[m [32m+[m[32m searchOnlineLibraryForCharacters_Fonts(string_Command(cmd, "chars"));[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "cachesize.set")) {[m [31m- d->prefs.maxCacheSize = arg_Command(cmd);[m [31m- if (d->prefs.maxCacheSize <= 0) {[m [31m- d->prefs.maxCacheSize = 0;[m [32m+[m[32m else if (equal_Command(cmd, "font.found")) {[m [32m+[m[32m if (hasLabel_Command(cmd, "error")) {[m [32m+[m[32m makeSimpleMessage_Widget("${heading.glyphfinder}",[m [32m+[m[32m format_CStr("%d %s",[m [32m+[m[32m argLabel_Command(cmd, "error"),[m [32m+[m[32m suffixPtr_Command(cmd, "msg")));[m [32m+[m[32m return iTrue;[m }[m [32m+[m[32m iString *src = collectNew_String();[m [32m+[m[32m setCStr_String(src, "# ${heading.glyphfinder.results}\n\n");[m [32m+[m[32m iRangecc path = iNullRange;[m [32m+[m[32m iBool isFirst = iTrue;[m [32m+[m[32m while (nextSplit_Rangecc(range_Command(cmd, "packs"), ",", &path)) {[m [32m+[m[32m if (isFirst) {[m [32m+[m[32m appendCStr_String(src, "${glyphfinder.results}\n\n");[m [32m+[m[32m }[m [32m+[m[32m iRangecc fpath = path;[m [32m+[m[32m iRangecc fsize = path;[m [32m+[m[32m fpath.end = strchr(fpath.start, ';');[m [32m+[m[32m fsize.start = fpath.end + 1;[m [32m+[m[32m const uint32_t size = strtoul(fsize.start, NULL, 10);[m [32m+[m[32m appendFormat_String(src, "=> gemini://skyjake.fi/fonts/%s %s (%.1f MB)\n",[m [32m+[m[32m cstr_Rangecc(fpath),[m [32m+[m[32m cstr_Rangecc(fpath),[m [32m+[m[32m (double) size / 1.0e6);[m [32m+[m[32m isFirst = iFalse;[m [32m+[m[32m }[m [32m+[m[32m if (isFirst) {[m [32m+[m[32m appendFormat_String(src, "${glyphfinder.results.empty}\n");[m [32m+[m[32m }[m [32m+[m[32m appendCStr_String(src, "\n=> about:fonts ${menu.fonts}");[m [32m+[m[32m iDocumentWidget *page = newTab_App(NULL, iTrue);[m [32m+[m[32m translate_Lang(src);[m [32m+[m[32m setUrlAndSource_DocumentWidget(page,[m [32m+[m[32m collectNewCStr_String(""),[m [32m+[m[32m collectNewCStr_String("text/gemini"),[m [32m+[m[32m utf8_String(src));[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "memorysize.set")) {[m [31m- d->prefs.maxMemorySize = arg_Command(cmd);[m [31m- if (d->prefs.maxMemorySize <= 0) {[m [31m- d->prefs.maxMemorySize = 0;[m [32m+[m[32m else if (equal_Command(cmd, "font.set")) {[m [32m+[m[32m if (!isFrozen) {[m [32m+[m[32m setFreezeDraw_MainWindow(get_MainWindow(), iTrue);[m }[m [31m- return iTrue;[m [31m- }[m [31m- else if (equal_Command(cmd, "urlsize.set")) {[m [31m- d->prefs.maxUrlSize = arg_Command(cmd);[m [31m- if (d->prefs.maxUrlSize < 1024) {[m [31m- d->prefs.maxUrlSize = 1024; /* Gemini protocol requirement */[m [32m+[m[32m struct {[m [32m+[m[32m const char *label;[m [32m+[m[32m enum iPrefsString ps;[m [32m+[m[32m int fontId;[m [32m+[m[32m } params[] = {[m [32m+[m[32m { "ui", uiFont_PrefsString, default_FontId },[m [32m+[m[32m { "mono", monospaceFont_PrefsString, monospace_FontId },[m [32m+[m[32m { "heading", headingFont_PrefsString, documentHeading_FontId },[m [32m+[m[32m { "body", bodyFont_PrefsString, documentBody_FontId },[m [32m+[m[32m { "monodoc", monospaceDocumentFont_PrefsString, documentMonospace_FontId },[m [32m+[m[32m };[m [32m+[m[32m iBool wasChanged = iFalse;[m [32m+[m[32m iForIndices(i, params) {[m [32m+[m[32m if (hasLabel_Command(cmd, params[i].label)) {[m [32m+[m[32m iString *ps = &d->prefs.strings[params[i].ps];[m [32m+[m[32m const iString *newFont = string_Command(cmd, params[i].label);[m [32m+[m[32m if (!equal_String(ps, newFont)) {[m [32m+[m[32m set_String(ps, newFont);[m [32m+[m[32m wasChanged = iTrue;[m [32m+[m[32m }[m [32m+[m[32m }[m }[m [31m- return iTrue;[m [31m- }[m [31m- else if (equal_Command(cmd, "searchurl")) {[m [31m- iString *url = &d->prefs.strings[searchUrl_PrefsString];[m [31m- setCStr_String(url, suffixPtr_Command(cmd, "address"));[m [31m- if (startsWith_String(url, "//")) {[m [31m- prependCStr_String(url, "gemini:");[m [32m+[m[32m if (wasChanged) {[m [32m+[m[32m if (isFinishedLaunching_App()) { /* there's a reset when launch is finished */[m [32m+[m[32m resetFonts_Text(text_Window(get_MainWindow()));[m [32m+[m[32m }[m [32m+[m[32m postCommand_App("font.changed");[m }[m [31m- if (!isEmpty_String(url) && !startsWithCase_String(url, "gemini://")) {[m [31m- prependCStr_String(url, "gemini://");[m [32m+[m[32m if (!isFrozen) {[m [32m+[m[32m postCommand_App("window.unfreeze");[m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "proxy.gemini")) {[m [31m- setCStr_String(&d->prefs.strings[geminiProxy_PrefsString], suffixPtr_Command(cmd, "address"));[m [31m- return iTrue;[m [31m- }[m [31m- else if (equal_Command(cmd, "proxy.gopher")) {[m [31m- setCStr_String(&d->prefs.strings[gopherProxy_PrefsString], suffixPtr_Command(cmd, "address"));[m [31m- return iTrue;[m [31m- }[m [31m- else if (equal_Command(cmd, "proxy.http")) {[m [31m- setCStr_String(&d->prefs.strings[httpProxy_PrefsString], suffixPtr_Command(cmd, "address"));[m [31m- return iTrue;[m [31m- }[m [31m-#if defined (LAGRANGE_ENABLE_DOWNLOAD_EDIT)[m [31m- else if (equal_Command(cmd, "downloads")) {[m [31m- setCStr_String(&d->prefs.strings[downloadDir_PrefsString], suffixPtr_Command(cmd, "path"));[m [31m- return iTrue;[m [31m- }[m [31m-#endif[m [31m- else if (equal_Command(cmd, "downloads.open")) {[m [31m- postCommandf_App("open newtab:%d url:%s",[m [31m- argLabel_Command(cmd, "newtab"),[m [31m- cstrCollect_String(makeFileUrl_String(downloadDir_App())));[m [31m- return iTrue;[m [31m- }[m [31m- else if (equal_Command(cmd, "ca.file")) {[m [31m- setCStr_String(&d->prefs.strings[caFile_PrefsString], suffixPtr_Command(cmd, "path"));[m [31m- if (!argLabel_Command(cmd, "noset")) {[m [31m- setCACertificates_TlsRequest(&d->prefs.strings[caFile_PrefsString], &d->prefs.strings[caPath_PrefsString]);[m [32m+[m[32m else if (equal_Command(cmd, "zoom.set")) {[m [32m+[m[32m if (!isFrozen) {[m [32m+[m[32m setFreezeDraw_MainWindow(get_MainWindow(), iTrue); /* no intermediate draws before docs updated */[m }[m [31m- return iTrue;[m [31m- }[m [31m- else if (equal_Command(cmd, "ca.path")) {[m [31m- setCStr_String(&d->prefs.strings[caPath_PrefsString], suffixPtr_Command(cmd, "path"));[m [31m- if (!argLabel_Command(cmd, "noset")) {[m [31m- setCACertificates_TlsRequest(&d->prefs.strings[caFile_PrefsString], &d->prefs.strings[caPath_PrefsString]);[m [32m+[m[32m if (arg_Command(cmd) != d->prefs.zoomPercent) {[m [32m+[m[32m d->prefs.zoomPercent = arg_Command(cmd);[m [32m+[m[32m invalidateCachedDocuments_App_();[m [32m+[m[32m }[m [32m+[m[32m setDocumentFontSize_Text(text_Window(d->window), (float) d->prefs.zoomPercent / 100.0f);[m [32m+[m[32m if (!isFrozen) {[m [32m+[m[32m postCommand_App("font.changed");[m [32m+[m[32m postCommand_App("window.unfreeze");[m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "search")) {[m [31m- const int newTab = argLabel_Command(cmd, "newtab");[m [31m- const iString *query = collect_String(suffix_Command(cmd, "query"));[m [31m- if (!isLikelyUrl_String(query)) {[m [31m- const iString *url = searchQueryUrl_App(query);[m [31m- if (!isEmpty_String(url)) {[m [31m- postCommandf_App("open newtab:%d url:%s", newTab, cstr_String(url));[m [31m- }[m [32m+[m[32m else if (equal_Command(cmd, "zoom.delta")) {[m [32m+[m[32m if (!isFrozen) {[m [32m+[m[32m setFreezeDraw_MainWindow(get_MainWindow(), iTrue); /* no intermediate draws before docs updated */[m }[m [31m- else {[m [31m- postCommandf_App("open newtab:%d url:%s", newTab, cstr_String(query));[m [32m+[m[32m int delta = arg_Command(cmd);[m [32m+[m[32m if (d->prefs.zoomPercent < 100 || (delta < 0 && d->prefs.zoomPercent == 100)) {[m [32m+[m[32m delta /= 2;[m [32m+[m[32m }[m [32m+[m[32m d->prefs.zoomPercent = iClamp(d->prefs.zoomPercent + delta, 50, 200);[m [32m+[m[32m invalidateCachedDocuments_App_();[m [32m+[m[32m setDocumentFontSize_Text(text_Window(d->window), (float) d->prefs.zoomPercent / 100.0f);[m [32m+[m[32m if (!isFrozen) {[m [32m+[m[32m postCommand_App("font.changed");[m [32m+[m[32m postCommand_App("window.unfreeze");[m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "reveal")) {[m [31m- const iString *path = NULL;[m [31m- if (hasLabel_Command(cmd, "path")) {[m [31m- path = suffix_Command(cmd, "path");[m [31m- }[m [31m- else if (hasLabel_Command(cmd, "url")) {[m [31m- path = collect_String(localFilePathFromUrl_String(suffix_Command(cmd, "url")));[m [31m- }[m [31m- if (path) {[m [31m- revealPath_App(path);[m [32m+[m[32m else if (equal_Command(cmd, "hidetoolbarscroll")) {[m [32m+[m[32m d->prefs.hideToolbarOnScroll = arg_Command(cmd);[m [32m+[m[32m if (!d->prefs.hideToolbarOnScroll) {[m [32m+[m[32m showToolbar_Root(get_Root(), iTrue);[m }[m return iTrue;[m }[m [36m@@ -3337,20 +3464,6 @@[m [miBool handleCommand_App(const char *cmd) {[m #endif[m return iFalse;[m }[m [31m- else if (equal_Command(cmd, "window.new")) {[m [31m- iMainWindow *newWin = new_MainWindow(initialWindowRect_App_(d, numWindows_App()));[m [31m- addWindow_App(newWin); /* takes ownership */[m [31m- SDL_ShowWindow(newWin->base.win);[m [31m- setCurrent_Window(newWin);[m [31m- if (hasLabel_Command(cmd, "url")) {[m [31m- postCommandf_Root(newWin->base.roots[0], "~open %s", cmd + 11 /* all arguments passed on */);[m [31m- }[m [31m- else {[m [31m- postCommand_Root(newWin->base.roots[0], "~navigate.home");[m [31m- }[m [31m- postCommand_Root(newWin->base.roots[0], "~window.unfreeze");[m [31m- return iTrue;[m [31m- }[m else if (equal_Command(cmd, "tabs.new")) {[m const iBool isDuplicate = argLabel_Command(cmd, "duplicate") != 0;[m newTab_App(isDuplicate ? document_App() : NULL, iTrue);[m [36m@@ -3411,12 +3524,16 @@[m [miBool handleCommand_App(const char *cmd) {[m }[m }[m }[m [32m+[m[32m#if defined (iPlatformAppleDesktop)[m [32m+[m[32m closeWindow_App(d->window);[m [32m+[m[32m#else[m else if (numWindows_App() > 1) {[m closeWindow_App(d->window);[m }[m else {[m postCommand_App("quit");[m }[m [32m+[m[32m#endif[m return iTrue;[m }[m else if (equal_Command(cmd, "keyroot.next")) {[m [36m@@ -3426,11 +3543,6 @@[m [miBool handleCommand_App(const char *cmd) {[m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "quit")) {[m [31m- SDL_Event ev;[m [31m- ev.type = SDL_QUIT;[m [31m- SDL_PushEvent(&ev);[m [31m- }[m else if (equal_Command(cmd, "preferences")) {[m iWidget *dlg = makePreferences_Widget();[m updatePrefsThemeButtons_(dlg);[m [36m@@ -3618,27 +3730,6 @@[m [miBool handleCommand_App(const char *cmd) {[m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "bookmarks.sort")) {[m [31m- sort_Bookmarks(d->bookmarks, arg_Command(cmd), cmpTitleAscending_Bookmark);[m [31m- postCommand_App("bookmarks.changed");[m [31m- return iTrue;[m [31m- }[m [31m- else if (equal_Command(cmd, "bookmarks.reload.remote")) {[m [31m- fetchRemote_Bookmarks(bookmarks_App());[m [31m- return iTrue;[m [31m- }[m [31m- else if (equal_Command(cmd, "bookmarks.request.finished")) {[m [31m- requestFinished_Bookmarks(bookmarks_App(), pointerLabel_Command(cmd, "req"));[m [31m- return iTrue;[m [31m- }[m [31m- else if (equal_Command(cmd, "bookmarks.changed")) {[m [31m- save_Bookmarks(d->bookmarks, dataDir_App_());[m [31m- return iFalse;[m [31m- }[m [31m- else if (equal_Command(cmd, "feeds.refresh")) {[m [31m- refresh_Feeds();[m [31m- return iTrue;[m [31m- }[m else if (startsWith_CStr(cmd, "feeds.update.")) {[m const iWidget *navBar = findChild_Widget(get_Window()->roots[0]->widget, "navbar");[m iAnyObject *prog = findChild_Widget(navBar, "feeds.progress");[m [36m@@ -3663,10 +3754,6 @@[m [miBool handleCommand_App(const char *cmd) {[m }[m return iFalse;[m }[m [31m- else if (equal_Command(cmd, "visited.changed")) {[m [31m- save_Visited(d->visited, dataDir_App_());[m [31m- return iFalse;[m [31m- }[m else if (equal_Command(cmd, "document.changed")) {[m /* Set of open tabs has changed. */[m postCommand_App("document.openurls.changed");[m [36m@@ -3691,29 +3778,6 @@[m [miBool handleCommand_App(const char *cmd) {[m postRefresh_App();[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "ident.signin")) {[m [31m- const iString *url = collect_String(suffix_Command(cmd, "url"));[m [31m- signIn_GmCerts([m [31m- d->certs,[m [31m- findIdentity_GmCerts(d->certs, collect_Block(hexDecode_Rangecc(range_Command(cmd, "ident")))),[m [31m- url);[m [31m- postCommand_App("navigate.reload");[m [31m- postCommand_App("idents.changed");[m [31m- return iTrue;[m [31m- }[m [31m- else if (equal_Command(cmd, "ident.signout")) {[m [31m- iGmIdentity *ident = findIdentity_GmCerts([m [31m- d->certs, collect_Block(hexDecode_Rangecc(range_Command(cmd, "ident"))));[m [31m- if (arg_Command(cmd)) {[m [31m- clearUse_GmIdentity(ident);[m [31m- }[m [31m- else {[m [31m- setUse_GmIdentity(ident, collect_String(suffix_Command(cmd, "url")), iFalse);[m [31m- }[m [31m- postCommand_App("navigate.reload");[m [31m- postCommand_App("idents.changed");[m [31m- return iTrue;[m [31m- }[m else if (equal_Command(cmd, "ident.switch")) {[m /* This is different than "ident.signin" in that the currently used identity's activation[m URL is used instead of the current one. */[m [36m@@ -3733,33 +3797,6 @@[m [miBool handleCommand_App(const char *cmd) {[m }[m return iTrue;[m }[m [31m- else if (equal_Command(cmd, "idents.changed")) {[m [31m- saveIdentities_GmCerts(d->certs);[m [31m- return iFalse;[m [31m- }[m [31m- else if (equal_Command(cmd, "os.theme.changed")) {[m [31m- const int dark = argLabel_Command(cmd, "dark");[m [31m- d->isDarkSystemTheme = dark;[m [31m- if (d->prefs.useSystemTheme) {[m [31m- const int contrast = argLabel_Command(cmd, "contrast");[m [31m- const int preferred = d->prefs.systemPreferredColorTheme[dark ^ 1];[m [31m- postCommandf_App("theme.set arg:%d auto:1",[m [31m- preferred >= 0 ? preferred[m [31m- : dark ? (contrast ? pureBlack_ColorTheme : dark_ColorTheme)[m [31m- : (contrast ? pureWhite_ColorTheme : light_ColorTheme));[m [31m- }[m [31m- return iFalse;[m [31m- }[m [31m- else if (equal_Command(cmd, "updater.check")) {[m [31m- checkNow_Updater();[m [31m- return iTrue;[m [31m- }[m [31m- else if (equal_Command(cmd, "fontpack.enable")) {[m [31m- const iString *packId = collect_String(suffix_Command(cmd, "id"));[m [31m- enablePack_Fonts(packId, arg_Command(cmd));[m [31m- postCommand_App("navigate.reload");[m [31m- return iTrue;[m [31m- }[m else if (equal_Command(cmd, "fontpack.delete")) {[m const iString *packId = collect_String(suffix_Command(cmd, "id"));[m if (isEmpty_String(packId)) {[m [36m@@ -3840,35 +3877,6 @@[m [miBool handleCommand_App(const char *cmd) {[m }[m return iTrue;[m }[m [31m-#if defined (LAGRANGE_ENABLE_IPC)[m [31m- else if (equal_Command(cmd, "ipc.list.urls")) {[m [31m- iProcessId pid = argLabel_Command(cmd, "pid");[m [31m- if (pid) {[m [31m- iString *urls = collectNew_String();[m [31m- iConstForEach(ObjectList, i, iClob(listDocuments_App(NULL))) {[m [31m- append_String(urls, url_DocumentWidget(i.object));[m [31m- appendCStr_String(urls, "\n");[m [31m- }[m [31m- write_Ipc(pid, urls, response_IpcWrite);[m [31m- }[m [31m- return iTrue;[m [31m- }[m [31m- else if (equal_Command(cmd, "ipc.active.url")) {[m [31m- write_Ipc(argLabel_Command(cmd, "pid"),[m [31m- collectNewFormat_String("%s\n", cstr_String(url_DocumentWidget(document_App()))),[m [31m- response_IpcWrite);[m [31m- return iTrue;[m [31m- }[m [31m- else if (equal_Command(cmd, "ipc.signal")) {[m [31m- if (argLabel_Command(cmd, "raise")) {[m [31m- if (d->window && d->window->base.win) {[m [31m- SDL_RaiseWindow(d->window->base.win);[m [31m- }[m [31m- }[m [31m- signal_Ipc(arg_Command(cmd));[m [31m- return iTrue;[m [31m- }[m [31m-#endif /* defined (LAGRANGE_ENABLE_IPC) */[m else {[m return iFalse;[m }[m [1mdiff --git a/src/ui/keys.c b/src/ui/keys.c[m [1mindex 3428bcc8..40859f7c 100644[m [1m--- a/src/ui/keys.c[m [1m+++ b/src/ui/keys.c[m [36m@@ -451,7 +451,7 @@[m [mvoid setLabel_Keys(int id, const char *label) {[m [m iBool processEvent_Keys(const SDL_Event *ev) {[m iKeys *d = &keys_;[m [31m- iRoot *root = get_Window()->keyRoot;[m [32m+[m[32m iRoot *root = get_Window() ? get_Window()->keyRoot : NULL;[m if (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) {[m const iBinding *bind = find_Keys_(d, ev->key.keysym.sym, keyMods_Sym(ev->key.keysym.mod));[m if (bind) {[m [1mdiff --git a/src/ui/widget.c b/src/ui/widget.c[m [1mindex cb373c43..138b219e 100644[m [1m--- a/src/ui/widget.c[m [1m+++ b/src/ui/widget.c[m [36m@@ -2154,6 +2154,9 @@[m [mstatic const iWidget *findFocusRoot_Widget_(const iWidget *d) {[m }[m [m iAny *findFocusable_Widget(const iWidget *startFrom, enum iWidgetFocusDir focusDir) {[m [32m+[m[32m if (!get_Window()) {[m [32m+[m[32m return NULL;[m [32m+[m[32m }[m iRoot *uiRoot = (startFrom ? startFrom->root : get_Window()->keyRoot);[m const iWidget *focusRoot = findFocusRoot_Widget_(uiRoot->widget);[m iAssert(focusRoot != NULL);[m [1mdiff --git a/src/ui/window.c b/src/ui/window.c[m [1mindex cbfd0f5c..2ec64f0e 100644[m [1m--- a/src/ui/window.c[m [1m+++ b/src/ui/window.c[m [36m@@ -186,22 +186,32 @@[m [mconst iMenuItem topLevelMenus_Window[6] = {[m [m #if defined (LAGRANGE_MAC_MENUBAR)[m [m [32m+[m[32mstatic iBool macMenusInserted_;[m [32m+[m static void insertMacMenus_(void) {[m [32m+[m[32m if (macMenusInserted_) {[m [32m+[m[32m return;[m [32m+[m[32m }[m insertMenuItems_MacOS("${menu.title.file}", 1, fileMenuItems_, iElemCount(fileMenuItems_));[m insertMenuItems_MacOS("${menu.title.edit}", 2, editMenuItems_, iElemCount(editMenuItems_));[m insertMenuItems_MacOS("${menu.title.view}", 3, viewMenuItems_, iElemCount(viewMenuItems_));[m insertMenuItems_MacOS("${menu.title.bookmarks}", 4, bookmarksMenuItems_, iElemCount(bookmarksMenuItems_));[m insertMenuItems_MacOS("${menu.title.identity}", 5, identityMenuItems_, iElemCount(identityMenuItems_));[m insertMenuItems_MacOS("${menu.title.help}", 7, helpMenuItems_, iElemCount(helpMenuItems_));[m [32m+[m[32m macMenusInserted_ = iTrue;[m }[m [m static void removeMacMenus_(void) {[m [32m+[m[32m if (!macMenusInserted_) {[m [32m+[m[32m return;[m [32m+[m[32m }[m removeMenu_MacOS(7);[m removeMenu_MacOS(5);[m removeMenu_MacOS(4);[m removeMenu_MacOS(3);[m removeMenu_MacOS(2);[m removeMenu_MacOS(1);[m [32m+[m[32m macMenusInserted_ = iFalse;[m }[m [m #endif /* LAGRANGE_MAC_MENUBAR */[m [36m@@ -246,9 +256,7 @@[m [mstatic void windowSizeChanged_MainWindow_(iMainWindow *d) {[m [m static void setupUserInterface_MainWindow(iMainWindow *d) {[m #if defined (LAGRANGE_MAC_MENUBAR)[m [31m- if (numWindows_App() == 0) {[m [31m- insertMacMenus_(); /* TODO: Shouldn't this be in the App? */[m [31m- }[m [32m+[m[32m insertMacMenus_(); /* TODO: Shouldn't this be in the App? */[m #endif[m /* One root is created by default. */[m d->base.roots[0] = new_Root();[m [36m@@ -1652,6 +1660,9 @@[m [mvoid setKeyboardHeight_MainWindow(iMainWindow *d, int height) {[m [m iObjectList *listDocuments_MainWindow(iMainWindow *d, const iRoot *rootOrNull) {[m iObjectList *docs = new_ObjectList();[m [32m+[m[32m if (!d) {[m [32m+[m[32m return docs;[m [32m+[m[32m }[m iForIndices(i, d->base.roots) {[m iRoot *root = d->base.roots[i];[m if (!root) continue;[m
text/gemini; charset=utf-8
This content has been proxied by September (ba2dc).