Did you know ... Search Documentation:
Title for pldoc(default)
Branch: development (switch to stable),
version to version

SWI-Prolog Changelog from version 10.1.14 to 10.1.15

[Sep 15 2026]

  • BUILD: macOS deps: use Berkeley DB 5.3.28 instead of 6.1.26 Berkeley DB 6.x is AGPL, which would conflict with distributing the bundle, e.g. as a Homebrew cask. 5.3 is under the Sleepycat licence and, unlike 6.x, is still freely downloadable from Oracle. Pin it with a comment so it is not "updated" back.
  • BUILD: macOS: derive LSMinimumSystemVersion from the deployment target The app and framework Info.plist claimed 10.14 while the binaries are built for 10.15. Both templates now use CMAKE_OSX_DEPLOYMENT_TARGET.

[Sep 13 2026]

  • FIXED: #1526 Windows: tmp_free(NULL) corrupted the heap tmp_free() has two implementations. The MMAP_STACK one, used wherever <sys/mman.h> exists, accepts NULL like free() does. The fallback, which is what Windows builds get, walked back over the size header and called free() on NULL-sizeof(word). On Windows that is reported as heap corruption, exit code 0xC0000374.
  • FIXED: #1525 ~:f ignored the locale decimal point in a non-C environment localizeDecimalPoint() assumed the number in the buffer was printed with the decimal point of the `default' PL_locale, and skipped the rewrite when that matched the requested one. Since PL_initialise() no longer sets the process LC_NUMERIC (issue #1093) that assumption is wrong: the C library prints "." while `default' carries the environment's ",". In a German locale format('~2:f', [123456.7]) therefore produced '123.456.70' instead of '123.456,70'.
  • FIXED: #1524 Windows: undefined behaviour scanning UNC path host names

[Sep 12 2026]

  • FIXED: macOS release build: take system libs from the Xcode SDK make-distribution put /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr on CMAKE_PREFIX_PATH, while cmake uses the Xcode SDK as CMAKE_OSX_SYSROOT. The Command Line Tools 27.0 update made that symlink point at MacOSX27.0.sdk, whose .tbd stubs name an arm64e.x1 target the Xcode 26.5 linker rejects with "tapi error: malformed file". As libcurses.tbd/libform.tbd end up in CMAKE_REQUIRED_LIBRARIES, every check_function_exists() link failed and the configure reported signal(), signbit(), access(), fork(), ... as missing.
  • FIXED: Single threaded: record the module a file is loaded from source_file_property(File, load_context(Module, Location, Options)) had no solutions at all in a single threaded system: recording the context was done by the loader of the multi-threaded system only. make/0 could therefore not reload a file the way it was loaded, and a .qlf file did not depend on the files whose term or goal expansion went into it, nor on the file an XPCE class template was copied from.
  • BUILD: macOS deps: update versions, build both arches on one machine Update the bundle dependencies to current releases: OpenSSL 3.6.4, zlib 1.3.2, libarchive 3.8.9, unixODBC 2.3.14, pcre2 10.48, libffi 3.8.0, SDL3 3.4.16 (3.4.14 is broken), SDL3_image 3.4.6, pango 1.58.2. Berkeley DB and OSSP uuid stay where they are, being no longer downloadable. libjpeg is dropped: nothing references it any more, and with the CoreText font map neither do FreeType, fontconfig or libpng.
  • BUILD: Keep the C and the C++ compiler flags apart The flags of a build type used the optimization flags of C for C++ as well, and those were $CFLAGS entire: every option meant for the C compiler reached the C++ compiler too, so a $CFLAGS carrying, e.g., -Werror=implicit-function-declaration warned on every C++ file it compiled. Fixes #1518.

[Sep 11 2026]

  • FIXED: atom GC scanned 8 times too far above the local stack top markAtomsOnLocalStack() added LOCAL_MARGIN, which is in bytes, to a Word pointer and thus scanned about 67Kb above lTop instead of the frame header plus MAXARITY arguments a call may be building there. Atoms, and notably blobs, left in that area by deeper calls that had long returned were kept alive. This could also form a pointer beyond the local stack.
  • BUILD: install customize/ into the build home tree The user init file template customize/init.pl was installed, but not linked into the build tree home directory, so swi('customize/init.pl') did not resolve when running from a build directory. Move it from a plain install() to the SWIPL_DATA_DIRS machinery, which does both.

[Sep 10 2026]

  • FIXED: tmp_dir flag held an OS path rather than a Prolog one On Windows the flag was the raw value of %TEMP%, which uses "\" as the separator. Every other path Prolog hands out uses "/", so a file built from the flag with directory_file_path/3 did not compare equal to the same file named any other way: .qlf files recorded a source under one name and looked it up under the other, and a buffer opened on such a file was not recognised as being open.
  • ADDED: term_automaton/2 and automaton_minimal/2,3 term_automaton/2 converts between a term and its term graph written out as an explicit automaton, and automaton_minimal/2,3 minimises such an automaton under bisimulation, collapsing bisimilar states and dropping the ones the start state cannot reach. Composed, the two are term_minimal/2.

[Sep 9 2026]

  • FIXED: Windows build of pl-bisim.c The bs_graph field grp2 clashes with a macro from the Windows header dlgs.h, which defines the common dialog control ids grp1..grp4 as plain numbers. Renamed the field to grp_next.
  • ADDED: variant_hash/2 and variant_sha1/2 accept cyclic terms Both refused a cyclic term with type_error(acyclic_term, T). The manual said why: "Cyclic terms are not supported because this would require establishing a canonical cycle. That is, given A=[a|A] and B=[a,a|B], A and B should produce the same hash. This is not (yet) implemented."
  • FIXED: term_factorized/3: looping and under-factoring on cyclic terms term_factorized/3,4 are now built in, computed from the term graph quotient. The library(terms) implementation kept an rbtree of the subterms it had seen, keyed on the standard order, and the standard order is not antisymmetric on rational trees -- for A = s(A,0), B = s(C,1), C = s(B,0), compare/3 answers (>) in both directions. The tree could therefore lose a key that was present, and term_factorized/3 either looped forever or, less visibly, counted a subterm reached twice as reached once and did not factor it out at all. Over 4000 random cyclic terms the old implementation diverged or under-factored on 11 of them.
  • ADDED: term_minimal/2 and term_factorized/3,4 A term in the store is a graph, and a cyclic term is a finite graph denoting an infinite tree. Two terms denote the same tree exactly when their graphs are bisimilar, which is the equivalence ==/2 already decides. What was missing is a canonical representative: the smallest graph denoting the same tree. pl-bisim.c computes it, and the new predicates hand it to Prolog.
  • FIXED: =@=/2 and \=@=/2: use after free, and errors reported as answers isomorphic() held a node pointer across the second term_id() call. That call can grow the node buffer, and growing it moves it, so the pointer was read and written after the block it points into had been freed. It takes a term with enough sharing to enter isomorphic() and enough cells to grow the buffer; f(D,D) nested four deep against the same term written out in full was enough to crash. Number both cells before looking either of them up.

[Sep 8 2026]

[Sep 7 2026]

[Sep 6 2026]

  • ENHANCED: decide whether a .qlf file is out of date by content, not by time A .qlf file now records a hash of the content of every source that went into it, and a source is recompiled only when that hash says it really changed. Modification times cannot say: a tree that arrives by checkout, copy, unpack or install carries times of its own, in either direction and at the resolution of the file system it landed on, and a file edited in the second its .qlf file was written has the very time of that file. Touching all 707 sources of the system library now recompiles none of them, and hashing them to find that out takes 97ms -- against minutes for one file wrongly kept or wrongly rebuilt.

[Sep 5 2026]

[Sep 2 2026]

  • DOC: WindowsInstaller.md: document the SignPath signing step Describe how signing fits into the release-windows workflow, which artifact to download, how to verify the signature, the one-time SignPath setup and how to dispatch the workflow from a branch when testing changes to it.

[Sep 1 2026]

  • FIXED: library(persistency): CRLF handling of the database file on Windows The database file is now read and written using newline(posix). On Windows the file was written using CRLF, while the reader deletes all carriage returns. As a result the byte counts used to repair a truncated database were off and a file ending in a lone CR was considered to end in layout, causing a new term to be glued to the `.` of the previous one.

[Aug 30 2026]

  • ENHANCED: library(persistency): recover from an incomplete last term If a persistent database file ends in a partially written term, e.g., because the disk was full or the process was killed while writing, db_attach/2 now prints a warning and truncates the file to just after the last complete term instead of raising a syntax error.

Package json

[Sep 1 2026]

Package libedit

[Sep 13 2026]

  • FIXED: link the threading library into the binding libedit4pl.c uses a pthread mutex to serialise painting the input line, but the plugin never linked a threading library. On platforms where pthreads are not part of the C library, notably MSYS2 and MinGW, that leaves pthread_mutex_init() and friends undefined at link time.

Package nlp

[Sep 12 2026]

  • FIXED: isub/4 goal expansion to not raise an error Compiling a call to isub/4 that passes a constant as Normalize, or that passes options that are only known at runtime, raised an error while expanding the goal. The error dropped the clause that held the call from the program. This affected, e.g., apropos/1, whose isub/4 call sits in library(help).

Package pldoc

[Sep 12 2026]

  • TEST: man_links: skip the test if the documentation is not installed The test renders manual pages and resolves the links in them. Both library(help) and the manual index it reads only exist if the documentation is built, so without them there is nothing to test.

[Sep 1 2026]

  • FIXED: #9803 help/1 on a manual section that embeds a figure Rendering such a section raised `http_handler_id manual_file does not exist`. The handler id used for images in the main manual has never existed; it is pldoc_refman. That handler now serves the images, and without a PlDoc server (help/1) images become a `file://` URI.

Package ssl

[Sep 13 2026]

  • FIXED: macOS HTTPS hang in system_root_certificates/1 On macOS the system root certificates were obtained using SecTrustCopyAnchorCertificates(). This talks to securityd over Mach IPC and can block forever if the keychain is not accessible, e.g., in an ssh session while the login keychain is locked. Such a block cannot be interrupted using e.g. call_with_time_limit/2. We now read the PEM file provided by the OS as on other Unix systems.

[Sep 1 2026]

  • FIXED: #176 SSL streams became unusable after a read timeout with OpenSSL 4 Our BIO read and write callbacks returned -1 on a stream timeout without setting the BIO retry flags, so OpenSSL considered the failure fatal. Up to OpenSSL 3 this surfaced as SSL_ERROR_SYSCALL and left the SSL object usable, but since openssl/openssl@ebb188f0d0 (OpenSSL 4.0) a fatal record layer read latches the state machine in MSG_FLOW_ERROR and every further SSL_read() fails with SSL_ERROR_SSL and an empty error queue, reported as "SSL(00000000) : reason(0)".
  • FIXED: SWI-Prolog/packages-ssl#176

Package xpce

[Sep 17 2026]

  • FIXED: SDL: Windows live resize painted only the window backgrounds display_manager<-test_queue defaults to @on on Windows, which makes redrawDisplayManager() stop as soon as events are queued. Inside the modal resize loop the queue cannot drain, so the resize watch aborted every redraw and only the freshly cleared backing surfaces were shown. The watch now disables the queue test while it redraws.

[Sep 16 2026]

  • FIXED: SDL: ws_draw_frame() crashed on a frame that is not created The guard lacked its return, so a frame without an SDL window fell through and dereferenced a NULL WsFrame instead of failing.
  • FIXED: SDL: redraw windows while the user is dragging a border On MacOS and Windows the window system runs a modal event loop for the duration of a resize, leaving our main loop blocked in SDL_WaitEvent(). The resulting RESIZED and EXPOSED events were queued but only handled after the drag, so MacOS stretched the last drawable and Windows padded the window with black. We now relayout and repaint from an SDL_AddEventWatch() callback, which SDL calls from inside that modal loop, giving the immediate resize of X11 and Wayland on all platforms.
  • FIXED: bitmap->image left a dangling <-bitmap reference in the old image tab_frame ->drop took anything dragged over a pane for a pane, warping the pointer to a nested window used the wrong offset, and epilog/0 was non-deterministic. The display_manager obtains its class variables early, getting the windows of panes and tiles is simpler, and a C method can be edited directly where addr2line is available.
  • FIXED: terminal: Home and End with a modifier were not passed to the client Only the plain keys have a binding. With a modifier they now send the xterm sequence, e.g. Ctrl-End sends `CSI 1;5F'. The wheel events of the test set the rotation slot, which replaced the attribute in 37170e65.
  • FIXED: two memory errors AddressSanitizer found Each font name lookup leaked the string it formatted the name with, and @pce<-unresolved_types read beyond a name object: a type for a class that does not yet exist has the class name as its context.
  • FIXED: pane_frame: rebuilding the menu bar leaked the old menus Taking a popup off the bar does not free it: its items refer to it. Everything that fills the bar makes its popups anew on every rebuild, so the old ones are freed now.
  • FIXED: xpce: free objects when their last reference disappears Since b1dec9a9 delRefObject() only did so for objects being inspected, so everything else that lost its last reference leaked, e.g. the argument vector of every message with two or more arguments. Objects with code references, as well as locked, protected and answer objects are left alone. Code that used an object after dropping the last reference to it now holds a code reference while it does: cleanTile(), the receiver of a running method, ->erase and the recompute of a device, and the cycle between a menu_item and its pull-right.
  • ADDED: tools to find what leaks: class<-no_reachable and object->_check(silent := @on) say which instances a check reaches, object->_find_holders which objects refer to a set of objects, and pce_lost_objects/1, pce_lost_instances/2 and pcerefer/1 turn that into the classes that leak, their lost instances and the chain of holders of one of them. These report Prolog references, and take care neither to pin what they report nor to hold on to what they collect.

[Sep 15 2026]

  • ENHANCED: Epilog: only terminals and unsaved editors keep the application alive Added `frame<->keep_alive` (class variable, default @off) and an optional keep_alive argument to `->has_visible_frames`. pane_frame asks its panes: Epilog terminals and PceEmacs views with unsaved file changes say @on. ep_main/0 now halts when no such frame is visible, so tools, demos and user windows no longer block exit. Closing the main console halts immediately unless keep-alive frames remain.
  • ENHANCED: Profiler GUI opens showing the most interesting predicate For a cumulative profile this is the first predicate using less than 90% of the time; otherwise it is the first in the list.
  • FIXED: PceEmacs: error from the history buttons after closing a pane The tool bar of a pane_frame kept the client it was created with, the pane in view at that time. After closing that pane, updating the history buttons raised an existence error. pane_menu_dialog ->client now also updates the client of the tool bar.
  • ENHANCED: terminal_image scrolls while the selection is dragged past its edge Dragging a selection above or below the terminal scrolls the scroll-back and extends the selection over what comes into view, faster the further out the pointer is. Not on the alternate screen or while the application tracks the mouse.
  • FIXED: freeing a terminal_image without a scroll_bar sent ->destroy to @nil Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018urSFBQAQZtC1sjsbkrdwm
  • FIXED: MacOS: Option held while the focus moved to a new window was lost SDL forgets the modifiers when a window loses the focus. Holding Option from M-x into PceEmacs' command prompt made M-p type π rather than walk the history. Take Option from Cocoa if SDL misses it.
  • FIXED: SDL keyboard debug message printed garbage for the timestamp Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018urSFBQAQZtC1sjsbkrdwm

[Sep 14 2026]

  • MODIFIED: meta (Alt) is a modifier in event <-buttons, not an offset on <-id A key typed with meta held now has the plain character as <-id and BUTTON_meta in <-buttons, like shift, control and gui. META_OFFSET and Meta() are gone, char no longer accepts M-x or \ex, and ->typed methods that must see the meta key take event|event_id. User code that compares <-id against offset values, or declares Id:event_id on a method that forwards keys, must be updated.
  • ENHANCED: store wheel rotation in event <-rotation instead of an attribute Wheel events are frequent and attaching an attribute to each event object is expensive. Class event now has a rotation slot (int*), @nil for all other events. Code using get(Ev, attribute, rotation, R) must use get(Ev, rotation, R).
  • FIXED: editing a tab label left the pane's focus active and lost it afterwards A double click on a tab label opened the label editor while the window of the tab kept an active cursor, and after the edit that window looked focused without receiving keys. The editor now takes the frame's focus, the tabbed window no longer passes it on to its tab while a label is edited, and the focus goes back to the window the user was typing in.
  • FIXED: PceEmacs: tabs of the editor had no popup menu The editor keeps its source tabs in an emacs_pane, which never got the label popup the window puts on its own tabs. It now has one with Close tab, Close other tabs and Move to new window. Closing other tabs goes through ->close_tab so modified buffers are still asked about.
  • ENHANCED: PceEmacs Prolog mode: inject consult buffer and make/0 If there is a current Epilog terminal, inject the command there.
  • ADDED: @prolog_ide<-current_epilog Find the current Epilog terminal.
  • CLEANUP: Removed swi_console. Outdated.
  • ENHANCED: Epilog: safely inject commands. Handles preserving partial input and signalling if the console is not waiting.
  • FIXED: Wayland: native message boxes froze all xpce windows On Wayland, SDL_ShowMessageBox() runs zenity and blocks the calling thread until the box is closed. When called from the SDL main thread this stopped event dispatching, so windows were not redrawn. The box is now shown from a helper thread while the main thread keeps dispatching events. User input is discarded during the wait, except for mouse button releases, which are re-queued afterwards.
  • ADDED: prolog_terminal->inject Check that the terminal can be used now, i.e., that it is at the prompt or reading while we can save/restore the already typed input.
  • FIXED: swipl-win: capture messages. The terminal is created in frame->create, which runs asynchronously. This means we must drive the event loop until the terminal is ready before we can enable capturing.
  • ENHANCED: Answer stack: constant time delete; reclaim leftovers at the host boundary Deleting an object from the answer stack no longer scans the stack. Objects left on the answer stack when control returns to Prolog are reclaimed, so code that creates objects outside a mark/rewind pair no longer leaks them. Use debugpce(gc) to list such objects.
  • FIXED: SDL events were never reclaimed The SDL backend created events as answer objects before marking the answer stack, so the rewind after dispatching did not reach them and freeableObj() refused them while they carried the answer flag. Every event leaked, keeping the windows and frames it referred to allocated after they were freed.

[Sep 13 2026]

  • ENHANCED: checkpce/0: show reference counts and freed state along the path Each object in the "Path from checked object" listing, as well as object keys of hash tables, now prints its reference and code reference counts and the freed, freeing, creating, locked, protected and answer flags.
  • ENHANCED: A new tab opens beside the current one, not at the end of the row Command-T and Control-Shift-T in Epilog put the terminal they make at the far end of the tab row, which in a window with a few tabs is nowhere near where the user is looking. A tab made on purpose now goes straight after the tab it was made from. The new-tab button keeps appending at the end: the button is at the end of the label row, which is where the tab it makes belongs.
  • FIXED: Epilog windows claimed a keyboard focus they did not have An Epilog window went on drawing an active caret after the window system gave the keyboard to another window, kept the platform text input enabled, and never sent its client the focus-out report (CSI ? 1004). The mirror image was true of a new window: it showed a pane, but the first keystroke went nowhere until the pointer entered that pane. Moving the focus between the panes of one window always worked, which is what made both hard to place.
  • FIXED: Epilog terminal: a character's column width must not depend on the font The terminal promoted a character to two columns when the font drew its glyph more than 1.5 cells wide. The column grid is a contract with whatever writes to the pty though: that program lays its output out with its own wcwidth() and cannot see our font, so a character we make a column wider puts us ahead of it for the rest of the line, and the frame it redraws over that lands on the wrong rows. On macOS the heuristic caught U+23BF LEFT PARENTHESIS LOWER HOOK, resolved from a symbol face that draws it at ~1.6 cells -- and U+23BF opens every tool-result line Claude Code prints, so its display came apart into overwritten and interleaved rows.
  • ADDED: Chart gallery demo covering library(plot) One window with a cell per feature of the two libraries that draw charts: plot_graph's poly, smooth and points_only curves, plot_axis grid lines, formats, labels and log scale, an axis that prints its own tick labels, plot_mark, and bar charts in both orientations with stacks, groups, ->sort, bars that can be dragged and bars with a bar_button_group of their own. Listed in the demo browser as "Chart gallery", or run ?- charts_demo.
  • DOC: bitmap: transparency comes from the image, not from the bitmap Class bitmap documented a <-transparent and a bug about bitmaps not being partly transparent. Both went away with bitmap<->transparent (cb34992c): a bitmap is transparent where its image's alpha channel says so, which is also what clears its solid flag.
  • MODIFIED: image ->save: PNG is the only format, and only to a file The `format' argument was typed {xbm,xpm,pnm,pbm,pgm,ppm,jpeg,gif} and `in' as a source_sink, but the only thing that can be written is a PNG to a file. Every value the type accepted was refused by the backend, and png -- the default -- was not in the type. Both are now honest: in=[file], format=[{png}].
  • FIXED: barchart: vertical bar charts could not be created at all Appending a bar to a vertical bar_chart raised "no implementation for: ->transparent" and left the chart empty. Class bar_label is now a figure that rotates its text with ->rotate rather than drawing it into an image and rotating that, so the label is anti-aliased and any angle works, not just multiples of 90.
  • FIXED: PceEmacs: the editor's reports never reached the bar of its window A report climbs from the window that made it to the frame, and every pane on the way is asked whether it is the one in view. PceEmacs is a group -- one pane holding a tab per source -- and `pane_frame <-current_pane' looks inside a group, so the group compared itself against the view it holds, decided it was not the pane in view, and dropped everything its own editors had to say: `Mark set', the mode it switched to, and the running feedback of an incremental search.

[Sep 12 2026]

  • ENHANCED: Navigator: the "Show:" menu sits on the tool bar The menu that chooses between the loaded files, the Prolog files and all files moved from the filter dialog to the tool bar of the Prolog Navigator, which leaves the whole dialog below it to the file filter. That filter now says what it is for -- "Filter files" -- rather than carrying a label.
  • FIXED: PceEmacs: do not load the user's files if xpce_defaults is `none' The mode extensions in app_config('xpce/emacs') and the init file user_profile('.pceemacsrc') are the customisation of whoever runs PceEmacs, so the flag that says a program is not to depend on their preferences now also says to leave those alone.
  • TEST: test_undefined: help/1 is only there if the documentation is built src/CMakeLists.txt only installs library(help) if the documentation is built, so help/1, called by the mode's ->prolog_manual, has nothing to autoload from in such a build.
  • FIXED: the alternate screen dropped the text of the line it was started on A client that had written to the line without a newline after it -- a prompt, a progress line -- found it empty on coming back from a full screen application.
  • FIXED: the folds of a session were lost by running a full screen application Coming back from `less' or `man' the session had all its folds open, because the application had cleared its own screen along the way.
  • FIXED: full screen applications scrambled the screen on a session with folds Running `less', `man' or any other application that takes the whole window left it with rows missing and the caret in the middle of it, on a terminal that had closed folds on the screen. Clearing the terminal was the way out.
  • FIXED: xpce:bookmark_store and xpce:bookmarks failed without a display Both were registered with test_libs() but left out of XPCE_EXTRA_TESTS, which is the list that gets SDL_VIDEODRIVER=dummy in its environment. Without it swipl-rc's has_display/0 fails on a headless machine, xpce is never set up, and library(emacs/bookmark_store) cannot be found.

[Sep 11 2026]

  • FIXED: checkpce/0: only take pce blobs held by clauses or records as roots A pce blob that lives only on a Prolog stack is no longer a root for the check. The atom GC scans the stacks conservatively, including a margin above the top of the local stack, so a window that was just closed leaves blobs for its tiles and chains there. They keep those objects alive, and checkpce/0 reported them as holding freed objects. Stack garbage can neither be collected nor reliably wiped, so a blob now only counts if '$atom_references'/2 says a clause, a record or foreign code registered it. Calling garbage_collect_atoms/0 first is dropped: it cannot change that set.
  • FIXED: bookmark editor: bookmarks lost between instances, notes lost when unfocussed The bookmarks were written out as a whole tree when a session ended, so the last instance of PceEmacs to leave overwrote whatever the others had marked, and one that never got to leave wrote nothing at all. They are kept as an append-only log now -- the new library(emacs/bookmark_store), over library(log_store) -- in which a bookmark made, annotated or thrown away is written down there and then, and which is tidied to one record per bookmark once it has grown long. A note is given to its bookmark when the caret leaves it as well as when another bookmark is selected, and the note field is inactive while no bookmark is selected, there being nowhere to put what is typed into it.
  • ADDED: library(log_store): a file of Prolog terms kept as an append-only log The window arrangements the IDE learns are kept this way already, so that several instances running at once do not overwrite each other's; the bookmarks of PceEmacs need the same. Moved the file, the lock and the terms out of library(pane_layouts), which now says only what a record of its own means and when the log has grown long enough to summarise.
  • FIXED: Settings/User init file when the init file does not exist Answering "yes" to "Preferences file ... doesn't exist. Create it?" did not create the file, after which PceEmacs reported "No source for ...". The template was looked up as customize/swipl.ini or customize/dotswiplrc, names that vanished with the autoconf build; it is customize/init.pl. If no template is found at all we now create an empty file rather than nothing. Also create the user config directory if it is missing: locating the init file used absolute_file_name/3 with access(write), which fails when <config>/swi-prolog does not exist yet, silently failing the whole menu action.
  • FIXED: emacs_prolog_mode <-forward_clause handed itself to a regex `regex ->search' wants a char_array, text_buffer or fragment. A mode is none of those: it only delegates to its editor, so the send raised a type error, <-forward_clause failed and ->fill_paragraph filled the clause as if it were a paragraph of prose rather than indenting it. Alt-Q in Prolog code printed the error and did the wrong thing.
  • FIXED: objects that outlived what owned them Three leaks that checkpce/0 reported by the thousand after using the IDE for a while: cloning an event copied the whole user interface, a window left the graphicals of its fixed layer behind when it was destroyed, and the manual kept freed tools in its tool table.
  • ENHANCED: object->_check tells where the faulty object was found Each inconsistency found by object->_check (checkpce/0) is now followed by the path from the checked object to the object that holds the illegal value. A step of this path is printed as the object and the slot we descend in: `<-name' for an instance variable, `[index]' for an element of a chain or vector and `{key}' for a key of a hash table. Without it a report names an anonymous chain and there is no way to tell what put the freed object in it.
  • ENHANCED: The symbol picker is a pane, and shows a range from the top It was a frame of its own. It is a `tool_pane' now, so it docks like the navigator or the list of buffers; the arrangements the system comes with give it a window of its own, which is where a thing you pick from while typing elsewhere belongs.
  • FIXED: A tool given a window of its own could not be learned Two things stood in the way of "this tool lives in a window of its own" ever becoming what the IDE does.
  • ADDED: Settings/Remember how this window is arranged The IDE learns where panes go from how long a window is worked in, which takes a while to come round and fades again. This says it outright: the window as it stands is where panes of these kinds go.
  • ENHANCED: The event viewer counts a repeated event rather than listing it Dragging the mouse across the box filled the history with a screenful of `loc_move' and pushed what came before it out of sight. A run of the same event now counts up on the line it is already on -- "loc_move (5×)" -- and that line keeps the last of them, which is the one selecting it shows.
  • FIXED: A text_item that cannot be typed in showed its clear button The button clears the field, which a field that is not <-editable does not allow, and clicking it there did nothing. It is not drawn now, and the room kept for it goes to the text: a read-only field has no use for a strip down its right. ->editable asks for a recompute, as the width the field wants from its dialog changes with it.
  • FIXED: The popup of the buffer list asked for things that are not there Three items on it had gone stale:
  • ADDED: editor ->placeholder An editor that holds no text writes its <-placeholder where the text would be, in the text colour faded to `editor.placeholder_opacity' (0.5), so that an empty one can say what it is for. Class text_item has had one for a while; this is the same thing for the classes built on an editor, and class view answers for its editor, so a `view ->placeholder' works as well.
  • ENHANCED: The PceEmacs bookmarks are a pane that can be docked The bookmark editor was a frame of its own, holding a tool bar, the tree of bookmarks, the note being written and a reporter, so it could only float over the window it was asked for. It is a `tool_pane' now, like the navigator and the list of buffers, and the arrangements the system comes with put it right of the editor at a third of the width. `find_references_editor/2' makes hit lists of the same class, so those dock too, pin and all.
  • ENHANCED: The PceEmacs buffer menu is a pane that can be docked It was a frame of its own, holding a tool bar, the list of buffers and a reporter, so it could only float over the window it was asked for. It is a `tool_pane' now, like the navigator and the debug monitor, and the arrangements the system comes with put it down the left of the editor at a fifth of the width. Asking for it again brings the one there is into view rather than making a second.
  • FIXED: The menus of the bar ignored where their items belong The File menu put "Close window" and "Halt prolog" at the top, and the Settings menu showed a tick beside "User init file", which is a command rather than a setting. Both come of a menu of the bar being filled from two sides: the application puts its own items on it and the pane in view adds what it can do afterwards.

[Sep 10 2026]

  • FIXED: bookmarks: a bookmark on another drive was silently dropped ->bookmark walks the root of the bookmark tree up until it holds the file being marked. On Windows the walk runs out of parents before it gets there whenever the file is on another drive than the one the tree is rooted on, and the bookmark was then thrown away. Root the tree at "My Computer" instead: that root holds every drive, and ->append already knows how to place a file below it.
  • FIXED: terminal: Block Elements drawn as glyphs when the font lacks them A Block Element is drawn as rectangles of the cell rather than from the font, but its width was still classified by asking the font how wide the glyph is. Where the font has no glyph for it -- the quadrants are missing from the fixed font on Windows -- that measures the hex box the shaper substitutes, which is wider than a cell, so the character was taken to be double width. A double-width cell is a two-cell cluster, which the painter does not draw itself, and the hex box went to the screen.
  • FIXED: font copy left the `creating' flag set getCopyFont() builds the instance with allocObject() but never called createdClass(), so the copy kept F_CREATING for its entire life. Such an object is reported by `Object ->_check' as
  • FIXED: char type rejected Meta-modified key codes Raising META_OFFSET above the Unicode range (b996819) made every Meta-modified key code larger than UNICODE_MAX, so the `char' type validation started rejecting them. As a result `Object ->_check' (and thus checkpce/0) reported e.g.
  • CLEANUP: epilog: leave fading an inactive terminal to the frame A prolog_terminal faded itself on losing the keyboard focus, from an `inactive_opacity' class variable of its own. A pane_frame already does that for every pane it holds, driven by `pane_frame.inactive_opacity', so the terminal was saying twice what the frame says once -- and only for the terminal image, not for the pane around it.
  • FIXED: window <-opacity had no effect Fading a window is how a pane_frame shows which of its panes has the focus, but nothing rendered it: <-opacity was applied by pushing a Cairo group around what a graphical paints into the surface it shares with the graphicals around it, and a window paints nothing there. It has a surface of its own that the frame composites, so the group wrapped only the selection and inversion of the window rectangle, which the window's own texture then covered. Setting `pane_frame.inactive_opacity' in a Defaults file reached <-opacity of the pane and stopped there.
  • DOC: how the panes of a window are managed, and how to configure them Defaults.user gains the class variables that govern the layout of a window of the IDE, and the User Guide a chapter on panes: how a window is put together, what the user can do to it by hand, how the system decides where a new pane goes, how library(pane_layouts) learns that from the way windows have been arranged, and the class variables that change any of it.
  • FIXED: pane_menu_bar <-menu_order taken from a Defaults file The order of the pulldown menus is a class variable so that a user can name it in their Defaults file, but doing so had no effect: the XPCE tokeniser reads a quoted name as a string, and `GUI' and `*' have to be quoted there. `chain <-index' then matched neither, so <-rank failed for GUI and for every menu that should have taken the place of `*', leaving all of them at the end of the bar.
  • FIXED: initial tty size of an Epilog terminal was 0x0 The pty is created lazily, when the Prolog thread is connected, and nothing gave it a size: TIOCSWINSZ was only issued from a resize, and the terminal is created at the size it is laid out at, so the first resize never comes. tty_size/2 therefore reported 0,0 until the user resized the window. Tell the pty the size the terminal already has when creating it. On Windows the same size now reaches CreatePseudoConsole() instead of a hardcoded 80x25.
  • ENHANCED: SDL: upload only the part of a window a redraw changed Drawing a frame uploaded every window's full Cairo backing into its texture, so the cost of an update was proportional to the total pixel area of the frame rather than to what changed. A window that updates by itself, such as the thread monitor, therefore got more expensive the more windows it shared its frame with. Updating one small text now costs 0.16ms instead of 1.73ms in a frame of four 800x600 windows, and 0.29ms instead of 2.07ms in one of sixteen 400x300 windows.
  • FIXED: the busy cursor could stay on screen until the mouse moves Dropping `display ->busy_cursor' only forgot the cursor, leaving it to the updateCursorWindow() that runs after each event to put the normal shape back. That does not happen if the event went to a frame rather than to one of its windows, which is the case for a keystroke while the pointer is outside the frame: typing a command in the GUI debugger left the watch cursor until the pointer moved.
  • ADDED: PceEmacs keeps its tabs to itself: the editor is a pane with tabs A source the user asks to see -- edit/1, M-. find-definition, a location picked in a tool -- takes a tab of the editor rather than the editor that is there. The tabs of the window are whole layouts, so putting a source in one of those would take whatever sits beside the editor off the screen; the editor now holds a tab per source of its own, and splitting it keeps those tabs with it.
  • FIXED: emacs_view ->pane_term: a buffer restored by its name `dict <-member' answers the dict_item rather than the object in it, so a window description naming a buffer rather than a file -- `editor([buffer('*scratch*')])' -- restored nothing and reported a type error.
  • FIXED: tab_frame <-current: the keyboard focus in a window of a window A window on a tab may itself hold windows -- a tabbed_window used as a pane, as class tool_pane is -- and then the frame's keyboard focus is on one of those rather than on a window the tab holds. The tab answered whichever window it last made current instead, so clicking inside such a pane left the tab, and its frame, believing the user was working somewhere else.
  • FIXED: the menu bar could be dragged shut A tile that was given a size held on to it and stayed resizable, whether the size came from the user's hand or from the window in it asking for the room its content needs. The menu bar of a pane_frame asks for that room whenever the strip changes -- a second row of menus, a tool bar coming or going -- and so does the status bar when a prompter does not fit, which marks the tiles above it as well. The gap under the menu bar then became a handle, and a strip dragged shut cannot be opened again: `tile ->rebalance' leaves a tile with no size alone, and what is left to grab lies under the frame's border.
  • FIXED: Call sites using an outdated signature for ->inform, ->confirm and ->report `display ->inform' and `display ->confirm' are `for=[visual], title=[char_array], message=char_array, any ...' and `->report' is `kind={status,inform,progress,done,warning,error,fatal}, format=[char_array], argument=any ...'. A number of call sites still passed the message as the first argument, raising a type error instead of showing the message or asking for confirmation.

[Sep 9 2026]

  • ENHANCED: The debugger opens in the window the traced thread runs in A debugger used to take a window of its own, so tracing a thread that talks in a terminal of the IDE left the user with a second window to watch it in. It is now a tab of the window that thread is talking in: `prolog_ide <-thread_frame' finds that window from the terminal the thread runs in, and the arrangements the system comes with put a debugger in a tab beside a terminal.
  • FIXED: laying out, drawing and resizing the panes of a window The room a window has is divided in pixels, so it has to be there before the shares are handed out. A frame that is not open has only been fitted -- every pane at the least it will take -- and dividing that gives nothing, so building a window from a term now resizes it first; an open window is not laid out again from ->resize, which would put the panes back where they were before the menu strip and the status bar took theirs. `tile ->rebalance' turns the layout as it stands into the wish, so a hand resize of the panes is kept when the window is resized: the resize gesture, the frame's own separator drag and the shares a `window_tree' asks for all end with one.

[Sep 7 2026]

  • ADDED: library(pane_layouts): the IDE learns where you put your panes Where a tool or a source appeared was decided by one setting for all of it, `prolog_ide.tool_placement', which gives the same answer whatever is already on the screen. library(pane_layouts) reads arrangements instead: a window written down with the content left out. Given the kinds of pane a window holds and the kind of the one to add, it answers `window', `tab', or `split(Kinds, Side, Share)' -- beside the panes of those kinds, on that edge, at that share of their room. It answers in kinds rather than in panes, so all of it is plain Prolog that is read and tested without a window on the screen. The setting gains a fourth value, `as_arranged', and it is the default.
  • MODIFIED: every source the user asks to see opens where they asked A window of the IDE offers one setting on its Settings menu for where new things go -- in a window of its own, in a tab, or beside what is there -- and a source is now placed by it, as a tool is. The item says "New tools and sources open" now that it means both.

[Sep 6 2026]

  • MODIFIED: the tools of the IDE are panes of the IDE Every tool that was a window of its own is a pane now, so it sits in a tab of any window of the IDE or beside a terminal or an editor, is dragged around by its grip like anything else, and says what it is doing on the status bar of the window it is in. Where a tool goes is the "New tools open as" setting. Being panes rather than persistent_frames, they no longer remember their own geometry: they take the size of the pane they are given.

[Sep 5 2026]

  • ADDED: class tool_pane and window ->display_fixed: a tool is a pane too A tool that shows several windows -- the thread monitor is the first of them -- could only be a tab. Class tool_pane makes it a pane: a tabbed_window holding a single tab_frame, which lays its windows out with a tile the way class frame does for its members while presenting one window to the outside. ->append_window adds a window beside the ones it has, <-window finds one by class, and it carries the grip, so a tool can sit beside a terminal or an editor in one tab and be dragged to another window like anything else.

[Sep 4 2026]

  • ADDED: library(pane_frame): one main window holding tools in tabs and panes A pane_frame carries a menu bar on top, a set of tabs below it that each hold one or more panes side by side, and optionally a bar at the bottom that reports and prompts. It is the one window Epilog, PceEmacs and the IDE tools live in, so that a terminal and an editor can sit beside one another. PceEmacs and Epilog run in one, and their windows belong to @prolog_ide.
  • ADDED: library(tab_frame): a tab that holds a tiled set of windows A tab could show one window. Class tab_frame lays a set of them out with a tile hierarchy, the way class frame does for its members, so a tab can hold a split window: windows are split, resized and dragged from one tab to another. ->split and ->drop put a window beside another, a separator is drawn in every gap that can be resized, and class split_handle is the grip in the corner of a pane that splits or moves it.

[Sep 2 2026]

  • ENHANCED: xpce: window<-image, beside frame<-image Reads back the pixels of one window, on a surface of the window's own size and with the window at its origin. A window decorator and any subwindows come along, as they do for the frame.
  • FIXED: epilog: draw the Block Elements as rectangles of the cell A font lays its glyphs out in its own em box, which is not the cell: the box is placed by the baseline and is shorter than the line height a terminal gives a row. Drawn from the font, U+2588 FULL BLOCK left a strip of the cell background above it and a quadrant sat below the top of its cell, so a picture built out of these characters -- Claude Code's logo -- came out in stripes, with a dark line where the client had set a background colour and a gap between the body and the feet.
  • FIXED: epilog: Shift+Tab is the back-tab, not a tab The key sent a plain tab, so a client that walks a form both ways -- Claude Code cycles its modes with it -- could not tell it from Tab. It now sends the CBT xterm spells `CSI Z', and `CSI 1 ; <mod> Z' when another modifier is held with it.
  • FIXED: epilog: the CSI parameter prefixes `<' and `=' `ESC[<u', which Claude Code writes when it exits to pop the kitty keyboard protocol's flags, printed a stray `u' on the screen: `<' was not recognised as one of the ECMA-48 parameter prefixes, so the sequence ended there and its final byte was taken for text. For the same reason `ESC[>5u' was read as the `CSI u' that restores the caret.
  • TEST: epilog: the copy keys must not reach the client Covers b5dfc657: Command-C and Ctrl+Shift-C with nothing selected used to fall through to the program on the terminal, as a `c' and as ^C.
  • FIXED: epilog: Command-C and Ctrl+Shift-C must not reach the client A key the terminal has a binding for is now kept by the window even when the bound method declines. Copying with nothing selected made Command-C insert a `c' into the program running on the terminal, and Ctrl+Shift-C reach it as the ^C the keymap makes of it.

[Sep 1 2026]

  • FIXED: text_item_combo_width() read <-style of a non-text_item The completion browser is shared with class menu: a ->kind: cycle menu opens it as its combo box and becomes @completer's <-client. destroyCompleter() asks the client for its combo width, so text_item_combo_width() was reached with a menu and read a slot that class does not have.
  • FIXED: crash when `menu ->initialise' fails A failed ->initialise is still followed by ->unlink, and unlinkMenu() walked <-members, which is still @nil when the argument type check rejects the call before initialiseMenu() ever runs. `new(M, menu(test, cycle, <bad message>))' crashed rather than failing.
  • ENHANCED: profile GUI: do not allow scrolling the table off screen.
  • FIXED: terminal_image: drop a selection the line editor left behind A selection on the line being edited stayed as it was made while the line moved on under it: the cursor keys walk the caret away from it, and what a key does to the text can leave the highlight over something else entirely. Every key that goes to the program now drops it, the way an editor drops the selection as soon as one types on, unless the key edits the selection itself -- typing replaces it and Delete takes it, which ->delete_selection already did.
  • TEST: terminal_input_selection: the selection over the line being edited, with the two ends of what is left alone.
  • ENHANCED: Epilog: keep the report bar off the line being worked on The bar with the short messages is drawn over a line of the terminal, which is a nuisance when that is the line being edited. The new `epilog_report.placement' class variable says which line it takes: `smart' (the default) takes the last one unless the caret or an end of the selection is there, in which case it takes the first, `bottom' and `top' pin it and `none' sends the messages to the normal XPCE reporting instead.
  • ADDED: terminal_image <-selection_start, <-selection_end: the ends of the selection as point(col, row), in the coordinates <-cursor_position uses. `smart' needs them to see where the hit of a running incremental search is, as that hit is the selection.
  • FIXED: colour: the alpha of a `#rgba' colour name Expanding the four nibbles of the short form to bytes doubled the blue one into the alpha rather than the alpha itself, so `#f008' came out at an alpha of 8 rather than 136.

[Aug 31 2026]

  • ADDED: edit the selection of the line being typed A left click already moves the caret inside the line the program on the terminal is reading. This lets the selection be edited as well: Delete and Backspace take it, ^X and Command-X cut it, typing replaces it, and so does a paste from the clipboard.
  • ADDED: Epilog: Command-Z undoes on MacOS Command-Z is how MacOS asks for undo, and terminal_image->typed already offers a Command key to the terminal's own bindings before the program on it. Add prolog_terminal->undo, which sends ^_ -- the key libedit binds undo to -- and bind it in the `apple' epilog table beside the other Command keys.
  • FIXED: Control-Shift-`-' did not reach the client as ^_ SDL reports the unshifted key, so Control-Shift-`-' arrived as `-' (0x2D). That is below `@', so the C0 conversion skipped it and the client was sent a bare `-'. libedit binds undo to ^_, which could therefore never be typed in a terminal.
  • ENHANCED: show keyboard accelerators in the native menu bar An item of a natively displayed menu_bar showed no shortcut, because nothing turned menu_item<-accelerator into an NSMenuItem key equivalent. Doing so needs two things.
  • FIXED: Apple bindings for Command-arrow never fired characterName() writes a named key between angle brackets, so Command-Left is `\s-<cursor_left>', not `\s-cursor_left'. The four cursor entries of the `apple' editor bindings used the latter and could therefore never match an event, leaving Command-Left/Right/Up/Down without the beginning_of_line, end_of_line, point_to_top_of_file and point_to_bottom_of_file they were meant to have.
  • ENHANCED: PceEmacs: reach the location history without the tool bar The two history buttons in the mode dialog's tool_bar were the only way to walk the location history: no menu item and no key binding. With the menu bar displayed natively (MacOS) the menu_bar is not drawn, which left those buttons alone in an otherwise empty strip.
  • ADDED: `menu_bar<-native`
  • FIXED: PceDraw: type error setting an attribute from the editor colour->equal declared its argument as `colour', so comparing a colour against anything else raised a type mismatch rather than answering false. The attribute editor does exactly that when it checks whether the value changed, so setting the colour of a shape raised an error. equalColour() already guards both arguments with instanceOfObject(), so the declaration is what was wrong.
  • FIXED: drain the terminal pty on the watcher thread The console pty was read only by receiveTerminalImage(), which runs on the main thread from ws_dispatch(). The main thread is also what writes to it, so once the pty filled, a main-thread write could never complete: the only thread that could make room was the one stuck in write(). Any sufficiently large write hung the application -- reporting an XPCE error with a backtrace was simply the easiest way to get there.
  • MODIFIED: library(dragdrop): use the `move' cursor Also brings the file header up to date.
  • FIXED: let an empty window collapse instead of claiming a strip A dialog holding only a menu_bar that is displayed natively (MacOS) has nothing to show, but still occupied a visible strip at the top of the frame, so the frame appeared to draw a double-height border above its content.

[Aug 30 2026]

  • BUILD: compile the Objective-C menu bar with Clang, not the C compiler Cocoa headers use blocks, nullability and instancetype, which only Clang understands, so building XPCE with e.g. MacPorts GCC failed: enable_language(OBJC) defaults CMAKE_OBJC_COMPILER to CMAKE_C_COMPILER, and gcc-mp-15 cannot parse Foundation.h.

[Aug 31 2026]

  • ENHANCED: native MacOS menu bar for class menu_bar On MacOS the menus of a `menu_bar' are now shown in the system menu bar at the top of the screen rather than drawn inside the frame. The XPCE `menu_bar' object remains the model: its `popup' members are mirrored into an NSMenu, and selecting an item is routed back through the normal `popup->execute' path, so applications need no changes. Controlled by the new class variable `menu_bar.native', which defaults to @on on MacOS and @off elsewhere.