# Ann: SWI-Prolog 10.1.15

**URL:** <https://swi-prolog.discourse.group/t/ann-swi-prolog-10-1-15/9820>\
**Category:** Releases\
**Created:** [September 17, 2026, 9:12am UTC](https://swi-prolog.discourse.group/t/ann-swi-prolog-10-1-15/9820 "2026-09-17T09:12:22Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [September 17, 2026, 9:12am UTC](https://swi-prolog.discourse.group/t/ann-swi-prolog-10-1-15/9820/1 "2026-09-17T09:12:22Z")

</div>

Dear SWI-Prolog user,

SWI-Prolog 10.1.15 is ready for download. This version comes with a lot of  
enhancements. Highlights:

**Core**

- Make library(persistency) recover from an incomplete last term if it  
crashes hard while writing or the device was full. Attaching used to  
fail. Now it truncates the file to the last correct term and issues a  
warning.
- More reliable detection that a .qlf file is out of date. We now do a  
quick filter on time. If that is not decisive we compare a content hash.
- Cyclic term handling. @kuniaki.mukai contributed bisimulation of Prolog  
terms. This provides several new features and fixes several issues:
  - Added term\_minimal/2, term\_automaton/2 and automaton\_minimal/2,3.  
term\_minimal/2 allows creates a canonical form of a term with maximal  
sharing (smallest number of compound terms).
  - These primitives are used for a new implementation of term\_factorized/3,  
which is about 6 times faster and reliably handles arbitrary cyclic terms.
  - These primitives are also used for creating cycle-safe versions of  
variant\_hash/2 and variant\_sha1/2. This provides support for cyclic terms in e.g., library(nb\_set), used by distinct/1,2

**SSL**

- Support OpenSSL 4. A bug in our BIO handling caused timeouts to fail with  
OpenSSL 4.
- MacOS: By default get certificates from /etc/ssl/cert.pem. This avoids a  
possible hang getting them from the system key chains. By setting the Prolog  
flag `system_cacert_filename` to `keychain`, the old behaviour is restored.

**XPCE (GUI)**

Notably thanks to @mike.elston for his daily reports on issues!

- Complete overhaul of the development tools. Rather than every tool creating  
a toplevel window, there is now a generic tool container window that holds a  
menu, one or more tabs and an optional status area. A tab can hold one or more  
_pane_. The panes are _tiled_. There are some generic rules that determine  
how a new tool appears. You can rearrange panes (tools) arbitrarily: put them  
in a new window, put them in a tab on their pwn, drag them to split an existing  
pane in this or another window, etc. The system learns from what what you do and  
you can use _Settings/Remember how this window is arranged_ to explicitly prefer  
a combination of panes. With this comes horizontal and vertical splitting of  
PceEmacs editor windows as GNU Emacs does (^X2, ^X3).
- MacOS: Use Apple central menu rather than per-window menu. This effectively  
copies the content of the native window to the MacOS Cocoa menu and forwards  
selected options back. The per-window menu is hidden.
- Several terminal (Epilog) enhancements:
  - Avoid deadlocks if the input queue fills completely.
  - Several fixes to escape sequences
  - Drawing enhancements: deal with _block elements_ and scale glyphs to their  
cell size (either single or double if wcwidth() says so).
  - Fixed reporting initial size to clients.
  - Safe command injection: validates the terminal is reading a command. If  
a partial command is typed, wipe it, inject and restore the partial command.  
Injection is now also used by PceEmacs Compile commands.

- Faster partial window redraw. Old versions used to refresh the whole window  
on any change. We now only refresh the damaged area, keeping track of maximal  
8 damage areas.
- Life resizing of windows. Used to work on Linux only, now also works on MacOS  
and Windows.
- Fixed SDL native message box API to stop freezing other windows on Wayland.
- Several memory management changes provide better performance and more eager  
reclaiming of objects. Several leaks are fixed.
- Many more small improvements 🙂

**MacOS**

- Updated dependencies for binary bundle. Changed BDB dependency from 6.1 to  
5.3 due to license issues. This upgrade might also fix binary portabiluty  
issues. If all is right, it should run on 10.15 or later for Intel macs and  
11.0 for later for Apple Silicon (M\*).

# SWI-Prolog Changelog since version 10.1.14

- 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.

- 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

- 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.

- 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.

- 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.

- 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.

- FIXED: term\_factorized/2, term\_subsumer/3: deal with zero-arity terms.

- FIXED: term\_factorized/2: handle zero-arity compounds.

- 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.

- FIXED: Make toplevel stop on thread\_exit/1.

- 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.

- 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.

- 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

- CMAKE: always install json\_rpc\_common.pl It was installed only  
if the pcre package is available, while json\_rpc\_client.pl, which  
needs it, is installed for any multi-threaded build. Without pcre  
this breaks loading library(json\_rpc\_client) with “source\_sink  
`library(json\_rpc\_common)’ does not exist”. The module has no  
conditional dependencies of its own, so there is no reason to gate it.

## Package libedit

- 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

- 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

- 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.

- 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

- 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.

- 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

- 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.

- 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.

- 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](mailto:noreply@anthropic.com)  
Claude-Session: [https://claude.ai/code/session\_018urSFBQAQZtC1sjsbkrdwm](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](mailto:noreply@anthropic.com)  
Claude-Session: [https://claude.ai/code/session\_018urSFBQAQZtC1sjsbkrdwm](https://claude.ai/code/session_018urSFBQAQZtC1sjsbkrdwm)

- 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.

- 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.

- 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 emacs\_language\_mode: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.

- 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 /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.

- 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.

- 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.

- 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.

- 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.

- 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.

- 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.

- 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 ; 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.

- 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, ))’ 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.

- 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.

- 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.

- 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.
