Dear SWI-Prolog user,
SWI-Prolog 10.1.16 is ready for download. This version comes with a lot of
fixes, notably for comparing cyclic terms, tabling and CLP(Q,R). Highlights:
Core
- Standard order of terms and cyclic terms. compare/3 on cyclic terms could
give a different order depending on how the terms are shared. This is now
fixed where possible. Some cyclic terms, however, have no standard order.- The new Prolog flag
incomparablecan be set toerrorto make
compare/3, sort/2, etc. raise an exception on such terms rather than
silently returning an arbitrary order. - Added partial_compare/3, a variant of compare/3 that only decides the
order if binding variables cannot change it. It returns
undecided(Sub1,Sub2) or incomparable(Sub1,Sub2) otherwise.
- The new Prolog flag
- The Prolog flag
var_prefixnow takes a character. Setting it to?
allows for variables such as?x.trueis still accepted as alias for
'_'. - Tabling: fixed several crashes and wrong answers in the well-founded
semantics, notably after abolishing tables and re-evaluating incremental
tables. - Printing C backtraces and finding the source of foreign predicates is
now more than 300 times faster by keeping addr2line (atos on MacOS)
running. - write_term/2 now writes a space between a prefix operator and a symbol,
e.g.not -arather thannot-a. - Fixed listing/2 to pass its options to portray_clause/3.
- Windows: the
tmp_dirflag no longer uses 8+3 short file names.
CLP(Q,R)
- These libraries had no tests. Now they have a test suite of about 470
tests and a design document. Writing these revealed many issues that
are fixed. Notably, the toplevel and copy_term/3 no longer show internal
slack variables, solver calls no longer leave choice points, ordering/1
works, the solver no longer clobbers user global variables and all
errors are proper error(Formal, Context) terms.
XPCE (GUI)
-
Class stream (process, socket) now encodes and decodes text, by default
as UTF-8. Used to send wide characters as raw wchar_t data and read
input as ISO Latin-1. -
MacOS: Control-click opens popup menus everywhere. Popup menus are confined to the visual display.
-
Drop a pane on the tab bar to give it a tab of its own.
-
Epilog and PceEmacs: ^X 2, ^X 3 and ^X 0 to split and close views.
-
Many Epilog terminal improvements: Page Up/Down and the complete set of
function keys are passed to clients such aslessandvim, background
colour erase, the caret follows the mouse through a selection, fixed
crash narrowing a window with a full scrollback and properly allow the
terminal client to read from stdout. -
Many more small fixes

Enjoy — Jan
SWI-Prolog Changelog since version 10.1.15
[Sep 24 2026]
-
FIXED: listing/2 ignored the options it forwards to portray_clause/3
The predicate_options/3 declaration sayspass_to(portray_clause/3, 3),
but list_clause/6 called portray_clause/1 and the options were dropped.
Solisting(M:p/1, [module(M2)])– the operator table to write the
clauses with – had no effect. -
MODIFIED: write_term/2: space between a prefix operator and a symbol
not -awas writtennot-a. That reads back correctly – the reader
always prefers the prefix operator, and evenzzz - xparses as
zzz(-(x))– but it suggests an infix-to a human reader. -
FIXED: #1531 tmp_dir flag kept 8+3 short names on Windows TEMP
is often a short name such as C:\Users\RUNNER~1\AppData\Local\Temp,
while a file loaded from it is known by its long, on-disk name. A file
built from the flag therefore did not compare equal to the same file
as the system reports it, e.g., in the load context or the sources of a
.qlf file. The flag is now normalised the same way as other file names. -
MODIFIED: Prolog flag var_prefix now takes a character The flag
var_prefixis eitherfalse(default) or the character that starts
a variable.'_'is the oldtrue;trueis still accepted as an
alias, but current_prolog_flag/2 now reports'_'. Setting it to
a symbol character such as?allows for variables such as?x.
[Sep 23 2026]
-
ENHANCED: Keep addr2line(1) running to look up C source locations.
Printing C backtraces and ‘$foreign_predicate_source’/2 used to start
a new addr2line (atos on MacOS) process per address. Looking up all
foreign predicates in module system now takes 0.07 sec instead of 23
sec on Linux and 0.09 sec instead of 35 sec on MacOS. -
FIXED: #1530 use-after-free when abolishing all tables
clear_variant_table() releases the tables one by one. Once a table is
released, atom-GC may reclaim its answer trie while a table released
later still holds a conditional answer that delays on it, after which
destroy_delay_info() accesses the freed trie. We now destroy the
delay info of all tables before emptying the variant table. -
FIXED: Internal PL_compare() calls ignored CMP_ERROR. PL_compare()
returns CMP_ERROR with a pending exception if the terms cannot
be compared. Callers now propagate this error. Sset_exception()
uses an equality test instead, as raising an exception there would
replace the pending one. -
ADDED: Prolog flag incomparable to raise an error on incomparable terms.
Cyclic terms such as A = s(B,0), B = s(A,1) have no standard order.
compare/3 returns an arbitrary order for them, which silently
breaks sort/2, library(assoc), library(ordsets), etc. With the
flagincomparableset toerror, compare/3, sort/2, etc. raise
representation_error(standard_order(Sub1,Sub2)). The flag is thread
specific. -
ENHANCED: partial_compare/3: report incomparable cyclic terms.
If two cyclic terms have no order, i.e., their lexicographic
comparison runs into a cycle, partial_compare/3 now unifies Order
with incomparable(Sub1,Sub2), where Sub1 and Sub2 are the pair of
subterms at which the comparison cycles. -
FIXED: #1529 compare/3 on cyclic terms depended on sharing.
When comparing cyclic terms, compare/3 assumes two compounds whose
comparison is in progress are equal. That is sound for ==/2, but when
the terms turn out to differ, the order derived from this assumption
may be wrong, so == terms could compare differently depending on how
they are shared. If such an assumption was used and one of the terms
is cyclic, the order is now recomputed by skipping == arguments and
descending into the first pair that differs. If this descent cycles,
no consistent order exists and the old result is kept. Also applies
to partial_compare/3. -
DOC: CLP(Q,R): answers no longer show slack variables The “Status and
known problems” section showed {X+Y>=1} printing as {Y=1-X+_A, _A>=0},
which no longer happens. Describe what projection does guarantee
instead, that copy_term/3 is projected onto the constraint store
reachable from the copied term rather than onto the term itself,
and that dump/3 is the way to project onto chosen variables.
[Sep 22 2026]
- ADDED: partial_compare/3: distinguish stable from unstable comparison.
partial_compare/3 is a variant of compare/3 that only decides the
order of two terms if that decision cannot be invalidated by binding
a variable. If the order depends on a variable it unifies Order
with undecided(Sub1,Sub2), the first pair of subterms that could not
be compared.
[Sep 21 2026]
-
FIXED: library(prolog_colour): handle aggregate/3 and aggregate/4.
-
FIXED: xref_source/1: handle aggregate/3 and aggregate/4
[Sep 20 2026]
- FIXED: representation errors on UTF-16 and wchar_t streams reperror()
wrote the &#NN;, \xNN\ or \uNNNN replacement text using put_byte(),
which produces garbage on streams that do not use a byte encoding,
e.g. the UTF-16 encoded console on Windows. It now writes the (ASCII)
replacement using put_code().
[Sep 19 2026]
-
FIXED: report of a condition that was simplified away A completed
table answers from its compiled clause (trie_gen_compiled/2), and
compile_trie_node() writes a T_DELAY instruction for every answer
that is conditional at that moment. Simplification can make such an
answer unconditional without changing the trie – destroy_delay_info()
clears the delay info of the answer – so nothing discards the compiled
clause and T_DELAY keeps pushing a condition that no longer exists.
call_delays/2 then reportsundefinedfor an answer the table holds
as unconditionally true: -
FIXED: re-evaluation to leave a table claiming undefined answers
Preparing a table for re-evaluation removes the delay lists of its
conditional answers (reeval_prep_node()) but left wl->undefined
counting them, while the flags beside it (has_answers, negative,
neg_delayed) are reset. The stale count makes propagate_to_answer()
skip the negative case: it cannot decide tnot(Table) while the table
may still have undefined answers. So answers of other tables delaying
on tnot(Table) stayed conditional for ever, although Table had been
re-evaluated to definitely false: -
FIXED: simplification to write to a freed SCC A worklist
that is kept when its table completes (because the table has
conditional answers or other answers delay on it) outlives its
component: ‘$tbl_free_component’/1 frees that while the worklist
stays attached to the table. propagate_to_answer() incremented
wl->component->simplifications for every worklist it propagates into,
and the delay lists lead to tables of older SCCs – so it wrote to
freed memory. The reproducer is plain Prolog: a chain a/1 → o/1
→ x/1 where x/1 is incremental and its conditional answer becomes
unconditional on re-evaluation, which propagates through o/1 into
a/1 (AddressSanitizer: heap-use-after-free in propagate_to_answer(),
freed by free_component()). -
FIXED: keep the delay back-references a later abolish needs A
conditional answer is recorded twice: in its own delay list and, per
delay element, in thedelaysbuffer of the worklist of the table
it delays on. The latter is what destroy_depending_worklists()
uses to find the tables that depend on a table being abolished,
and what propagate_result() walks to simplify them. -
FIXED: crash abolishing a table another table under evaluation delays
on Abolishing a table destroys the tables holding conditional answers
that delay on it (destroy_depending_worklists()). These were destroyed
immediately, also when such a table is still under evaluation, and
free_worklist() then released the worklist the running evaluation uses.
The next answer added to it read freed memory: -
FIXED: retractall/1 to report an exception raised while retracting
If retractClauseDefinition() raises an exception, the loop of the
allvarsbranch breaks out with rc = false, but the next statement
unconditionally set rc back to true. retractall/1 thus returned true
with the exception still pending, so the caller was told the predicate
was emptied while it still held all its clauses and never saw the error. -
ADDED: edit/0: use last load command as default file. This is used
as fallback if there is no commandline file.
[Sep 18 2026]
-
ADDED: source_file_property/2: property loaded(Stamp) Get access to
the last time this file was loaded. -
ENHANCED: Command line completion on
[Alias(Prefixfor any alias.
Used to work only forlibrary(Prefix. -
ADDED: demo/trace.pl: demo program for the command line tracer A small
route planner over a cyclic map, meant to be traced rather than run.
The module documentation holds three exercises: creeping through the
call, exit, fail and redo ports, using the search commands and spy
points on a query that fails after a long search, and watching the
exception port unwind the recursion. -
FIXED: crash in the command line tracer searching for a goal The tracer
commands /c, /e, /r, /f, /u and /a take an optional goal to search for.
Comparing that goal against the goal of the current frame addressed
the arguments of the search goal using the local frame layout and an
unshifted word, which made the search crash.
[Sep 17 2026]
- FIXED: macOS app: declare NSLocalNetworkUsageDescription Without this
key macOS may silently block access to hosts on the local network
from swipl-win and processes started from it, which shows up as
“No route to host”.
Package RDF
[Sep 20 2026]
- ENHANCED: MacOS: Control-click asks for a popup menu Use `event
->is_popup’ rather than testing for the right button going down,
so that the popups of the RDF diagram and the W3C test browser also
open on Control-click, which is the secondary click on MacOS.
Package clpqr
[Sep 23 2026]
-
FIXED: CLP(Q,R): copy_term/3 and the toplevel reported the internal
store Answers mentioned the slack variables of the simplex tableau:
{X+Y>=1} printed as {Y=1-X+_A, _A>=0} and copy_term/3 returned the
same. The cause was that attribute_goals//1 projected onto every
attributed variable it could reach, so projection had nothing left
to eliminate and dump/3 ran as an expensive identity. Variables the
solver introduces for itself – slack variables, the objective of
minimize/1, the witness of a disequation – are now marked in the
clpqr_itf record and left out of the projection target. -
FIXED: CLP(Q,R): delayed goals that had already run stayed attached When
two variables that both carry delayed non-linear goals are unified,
the goals of both run and the survivor’s attribute is supposed to be
cleared; the goals re-attach themselves if they are still non-linear.
geler.pl wrote -
FIXED: CLP(Q,R): every solver call left a choice point Not one of {}/1,
inf/2, minimize/1, bb_inf/3, dump/3, entailed/1 or a plain unification
with a constrained variable was deterministic, so a program using
CLP(Q,R) accumulated choice points it could never use and could not
be last-call optimised. Nearly a tenth of the test suite carried
[nondet] because of it.
[Sep 22 2026]
-
TEST: CLP(Q,R): finish the test suite Three rounds of work on the
suite, squashed: -
DOC: CLP(Q,R): add a section on working on this code Section 16
collects what the repairs of section 14 taught, so the next person
does not rediscover it: -
FIXED: CLP(Q,R): inf/2,4 clobbered the global variable
inf' The same defect as bb_inf/3'sprov_opt’, in the other optimiser.
inf_lin/4 reaches the extremal vertex by pivoting, stores the result,
then fails so that backtracking undoes the pivots. It kept the result
in the global variable `inf’, and global variables share one namespace
with the calling program: -
CLEANUP: CLP(Q,R): remove predicates that have no caller A call graph
over the package turns up eleven predicates that nothing reaches.
Removing them takes about 300 lines out and lifts clause coverage
of the test suite – bv_*.pl from 80% to 83%, clpqr/dump.pl from 81%
to 89%, clpqr/geler.pl from 67% to 80%. -
ENHANCED: CLP(Q): implement the isolating axiom for X = Y^Z The
manual documents “X = pow(Y,Z) … X and Y are ground => 8 = 2^Z”
and “X = exp(Y,Z) … X and Z are ground => 8 = Y^3” as isolating
axioms, but only CLP(R) ever solved the second one. In CLP(Q),
{8 =:= Y^3} succeeded with Y unbound and a residual goal, which is
both undocumented and unsound looking: the query reports success for
a store it has not decided. -
FIXED: CLP(Q,R): several exceptions were not error/2 terms They
printed as “Unknown message”, and Q and R disagreed on which of them
were malformed: -
FIXED: CLP(Q,R): bb_inf/3,4,5 clobbered the global variable
prov_opt' The branch and bound incumbent was kept in the global variableprov_opt’. Global variables share one namespace with the calling
program, so bb_inf/3 destroyed a user variable of that name: -
FIXED: CLP(Q,R): a delayed optimisation made copy_term/3 answers
unrunnable minimize/1, inf/2 and bb_inf/3 delay through wait_linear/3
until their expression becomes linear. transg//1 reported such a
pending optimisation by emitting the solver’s own continuation into
the {}/1 residual: -
FIXED: CLP(Q,R): pivot/2 read the class from the order field pivot/2
in bv_q.pl and bv_r.pl did -
FIXED: CLP(Q,R): a contradictory ordering/1 threw a bare term
arrangement/2 threw the atom `unsatisfiable_ordering’, which is not
an error(Formal, Context) term, so it printed as -
FIXED: CLP(Q,R): ordering/1 had no effect at all Four defects conspired
to make ordering/1 a no-op, and none of the four worked examples of the
“Variable Ordering” section of the OFAI manual (Holzbaur, TR-95-09)
could be reproduced. -
FIXED: CLP(Q,R): non-linear residual goals were reported once per
variable attribute_goals//1 reports the constraints on all attributed
variables of the term at once and then removes the solver attribute
from each of them, so that the next variable does not report the
same conjunction again. It only removed clpqr_itf, but a variable
that carries nothing but a delayed non-linear goal has no clpqr_itf
attribute – only clpqr_geler. Such a store was therefore printed
once per variable: -
FIXED: CLP(Q): inverting A = B^X returned a huge inexact rational
nl_invertible/4 computed the exponent as -
FIXED: CLP(Q,R): arrange_pivot/1 could never fire arrange_pivot/1 in
clpqr/project.pl guarded on -
FIXED: CLP(R): #(pi) was 3.14259265 instead of 3.14159265 The two
digits were transposed, making #(pi) and #(p) wrong by 1.0e-3, four
orders of magnitude above the solver’s own epsilon of 1.0e-10. -
TEST: Add a test suite and design document for CLP(Q,R) The clpq
and clpr libraries had no tests at all. This adds test_clpq.pl
(242 tests) and test_clpr.pl (229 tests), registered with CMake as
ctest -R clpqr:, and doc/design.md describing the implementation
and the literature it derives from.
Package json
[Sep 20 2026]
- FIXED: #3 emitting non-BMP characters on Windows json_write/2 and
friends turned characters outside the BMP into garbage on Windows.
json.c has no config.h, so SIZEOF_WCHAR_T was never defined and
the code that combines UTF-16 surrogate pairs into a code point was
compiled out. As a result the surrogates were passed to Sputcode(),
which cannot represent them.
Package sgml
[Sep 20 2026]
- ENHANCED: MacOS: Control-click opens the insert menu Use `event
->is_popup’ rather than testing for the right button going down,
so that the SGML mode’s insert menu also opens on Control-click,
which is the secondary click on MacOS.
Package xpce
[Sep 25 2026]
-
ENHANCED: demo(ispell). Deal with Unicode, do not block the event loop.
-
ENHANCED: class stream (process, socket) encodes and decodes text Added
stream<->encoding, defaultutf8(class variable). Output of ->append,
->format, etc. is encoded using the Prolog stream layer and input is
decoded into characters before it is handed to the record_separator,
input_message and <-read_line. Previously wide text was written as
raw wchar_t data and input was read as ISO Latin-1. -
FIXED: SDL: process->wait hung when the child’s output pipe reached
EOF The fd watcher only reacted to POLLIN, but Linux reports EOF
on a pipe as POLLHUP without POLLIN. The EOF was never delivered,
so the input side was never closed and process->wait looped forever
(while the watcher thread busy-looped on poll()). -
FIXED: xpce: a fixed graphical moved to another window was not painted
there Moving the grip of a pane between the windows of its tabs left it
displayed, so ->display_fixed did not ask for it to be painted. Also
apply the scroll offset of the fixed layer in changedImageGraphical().
[Sep 24 2026]
-
FIXED: xpce: a hidden tab forgot the pane clicked last Clicking a
pane moved the keyboard focus without telling its tab_frame, so a
tab switched away from fell back on the pane last made current by
->current, e.g., the thread monitor split beside a console. The tab
was relabelled after it and gave it the focus when raised again. -
ENHANCED: Epilog: Emacs window keys ^X 2, ^X 3 and ^X 0; ^X 0 closes
a view beside other panes Epilog now splits with ^X 2 and ^X 3 and
closes a terminal with ^X 0. In both Epilog and PceEmacs, ^X 0 closes
the only view of a tab as long as the window shows something else,
such as a console beside the editor. New method pane->last_in_frame
tells whether closing a pane would leave its window empty. -
FIXED: xpce: crash printing to a closed Prolog output stream during
halt Console output from xpce, e.g., debug messages while windows
are destroyed, now goes to C stderr if Prolog’s output stream is gone. -
ENHANCED: PceEmacs: ESC aborts the argument prompter, as ^G does
Co-Authored-By: Claude Opus 5.5 (1M context) noreply@anthropic.com -
ENHANCED: PceEmacs: support the Prolog flag var_prefix set to a
character Singleton replacement renames?xto?_xor?_if the
buffer setsvar_prefixto?, and reading a selection and finding
dependencies use the buffer’s var_prefix.
[Sep 23 2026]
-
FIXED: terminal_image: size in cells ignored the initial size in
pixels A terminal_image started with 25 rows and the width of its
pixel size without the margins, and only a later change of size or
font computed these from the pixel size. A terminal that was never
resized had more rows than it showed, so a line feed on its last
visible row did not scroll. -
ENHANCED: Epilog: lines scrolled into the terminal take the background
colour As xterm’s background colour erase (bce), the empty lines of IL,
DL, SU, SD and a scroll of the region, as well as a line a line feed
scrolls into the window, are painted in the current background colour. -
FIXED: Epilog: background colour of an erased line leaked into later
lines An erase in line with a background colour records that colour
on the line. Freeing a line without text (as opened by IL, DL or a
scroll region) did not reset it, so a line later written in the same
slot was painted on the old background. ED 1 also left the recorded
erase of the lines it cleared in place. -
ENHANCED: Epilog: the link popup item opens non-Prolog files and
copies URLs A file:// link to a Prolog file is still consulted.
Other file links are opened with the desktop application and other
links are copied. -
FIXED: Epilog: hovering a link that wraps over lines arms all of it
Also rejoin the pieces of a link when rewrapping moves part of it to
a line holding the rest, so a resize no longer leaves it split. -
FIXED: lazy method binding refused all nested binds Binding a method
runs Prolog, which may need to bind other methods, e.g. when a signal
handler halts inside the callback. Only refuse re-binding the same
method, which would loop.
[Sep 22 2026]
- FIXED: complete font.system_fonts and font.pango_families with the
built-in values A Defaults file that overrules one of these class
variables replaces it entirely, so every alias or family it left
out was simply undefined. makeBuiltinFonts() now walks over the
value class font declares itself after the user defined values have
been loaded. As neither fontAlias() nor the family loader overwrites
an entry that is already there, this adds exactly the omitted ones,
so a Defaults file only needs to mention what it wants to change.
[Sep 21 2026]
-
ENHANCED: xpce: complete the SDL3 function key coverage
keycode_to_name() stopped at F12 and the four arrows, so the second
row of function keys, Insert, the VT220 editing keys and the Sun and
HP keys beside the alphabet produced no event at all. They are now
matched to the event ids xpce has had for them all along – most
of initial_tree undernamed_function' was unreachable -- with f13..f24 and the clipboard keys added to the tree, and the terminal sends the ones that have a meaning to a client: Insert asCSI 2~‘,
Find and Select asCSI 1~' andCSI 4~’, the middle of the keypad as
`CSI E’ and F13..F24 as the Shift+F1..F12 that xterm’s terminfo entry
spells them as. -
FIXED: do not re-enter the redraw from the live resize watch SDL runs
an event watch at SDL_PushEvent() time, before the event is queued,
so an SDL call made while we are painting lands in live_resize_watch()
with a drawing context open: `frame ->size’ asked for by a ->compute
or a ->_redraw_area is enough. It then laid the frame out and painted
it from under that context, which paints a window a second time inside
its own redraw – clipped to the area the outer redraw was given –
and lets ws_geometry_window() destroy the backing store the open
context draws into. This is what printed “d_window(…): Context is
already open”. Leave the event in the queue for the main loop instead. -
FIXED: crash narrowing a terminal window whose scrollback is full
Rewrapping a narrower terminal needs more lines than the ring holds,
so the oldest text scrolls off to pay for them. If the line to be
wrapped was itself the oldest one, rlc_shift_lines_down() dropped
exactly that line to make the room, leaving the wrap to copy from a
line that no longer held its text: a segmentation fault on shrinking
a console with a full buffer. Drop the head of the line instead,
which is the text that scrolls off anyway.
[Sep 20 2026]
-
FIXED: MacOS: closing a window did not reactivate another one of
the app AppKit activates the next window of the application when the
key window closes, and does not do so for the windows SDL creates:
closing the window opened with Command-N left the one it was opened
from visible but inactive, with no caret and no keys. Hand the
keyboard on ourselves. -
FIXED: tab_frame: closing a pane moved the focus to the far side of
the tab `tab_frame ->update_current’ fell back on the first window of
<-windows, which is the order the windows were displayed in rather than
the order they are laid out in. Closing a pane from a split therefore
pulled the focus to whichever pane happened to be displayed first.
Hand it to the pane that takes over the room instead: the one after
it, and the one before it when it was the last. -
ENHANCED: MacOS: Control-click asks for a popup menu Control-click is
the documented alternative for the secondary click on MacOS. The new
`event ->is_popup’ tests whether an event asks for a popup (context)
menu: the right button going down or, on MacOS, Control-left. Class
popup_gesture activates on it, as does the code that opens a popup
without using a gesture: list_browser, toc windows, the inspector,
the thread monitor, PceEmacs fragments and the Epilog terminal. -
FIXED: terminal: pass Page Up and Page Down to the client The page keys
were bound to ->cursor_page_up and ->cursor_page_down, which scroll
the scroll back whatever is running on the terminal. A client never
saw them, which leftless',man’,vim' and every other program that is paged with those keys a key short. They now follow the rule the control characters and the function keys already follow: while a client owns the pty, or while the alternate screen is up, the keys are its own and are sent as xterm'sCSI 5~’ and `CSI 6~'.
With nothing running they scroll the scroll back as before, and Shift
is the way out of the client, as it is for the wheel. -
ENHANCED: drop a pane on the tab bar to give it a tab of its own
Dragging a pane by its grip onto another window splits that window;
dropping it on the tab bar – the label row, or the menu bar above it,
which is the whole of the bar while a single tab hides its label –
now gives the pane a tab of its own instead. Where in the row the
pointer is says where the new tab goes, and a ghost of the label the
tab will carry shows that while the pointer is there. -
FIXED: xpce: typing did nothing in a window that claims the keyboard
focus After a confirmer, popup or completer that holds a pointer grab
took the focus away and closed again, SDL text input stayed disabled
for the frame that regains the focus. A terminal or editor then
still drew an active caret, but typing printable characters had no
effect until the focus was moved to another window and back.
[Sep 19 2026]
-
FIXED: terminal: let the caret follow the mouse through a selection
The input caret of the terminal now moves as soon as the button goes
down and travels with a drag, so that it ends up at the end of the
selection, as it does in an editor. It used to wait for the button
to come up and then only move if no selection had been made. A click
or drag outside the line being edited still leaves the caret alone. -
FIXED: colour and hyperlinks for output from the xpce thread
Epilog enabled thecolor_termandhyperlink_termflags
using set_prolog_flag/2, which creates a thread-local copy of
the flag once a second thread is running. The xpce event thread
therefore kept the globalfalseand messages it printed, e.g. from
in_pce_thread(xref_source(File)), reached the terminal as plain text
without a clickable source location.
[Sep 18 2026]
-
FIXED: “No implementation for: ->current_terminal” on a detached
terminal A terminal that is in no window of the IDE told a plain frame
it had the keyboard, and a plain frame knows nothing about terminals. -
STYLE: draw the pullright marker as a Windows style “>” Popup menu
items with a pullright submenu are now marked with a plain chevron
drawn by draw_popup_indicator() rather than an image or a 3d triangle.
This removesmenu <->popup_imageand the global @ms_left_arrow_image. -
FIXED: Epilog: open the client’s stdout and stderr read-write The
three descriptors a terminal hands its client are the terminal as far
as it, and anything it starts, is concerned, and a terminal is one
device that a program both reads and writes. Ours opened stdout and
stderr write-only, so a client that asked the terminal a question on
stdout could not read the answer back from it, which is how termenv,
and with it every Go program with a terminal user interface, learns the
colours it draws on. Its read failed with EBADF, the answer stayed in
the input queue, and the next program to read stdin found an escape
sequence it never asked for: `gh run download’ in an Epilog OS shell
died with “could not prompt: unexpected escape sequence from terminal”.
[Sep 17 2026]
-
CLEANUP: Remove all usage of old font names. E.g. family
screen,
stylenormal, etc. -
FIXED: MacOS: keep popup menus inside the display SDL’s popup
constraining is disabled on MacOS because it misplaces popups on
secondary displays. Clamp the popup ourselves against the usable
bounds of the display holding it. -
TEST: Only run the too-wide glyph test if SWIPL_TEST_FAIL_ON_UNLIKELY=y
The outcome depends on the installed fonts. With only DejaVu, U+23BF
has no glyph and the missing-glyph box crosses the cell boundary. -
FIXED: PceEmacs failed to start if there is no config directory If
HOME does not exist, user_app_config has no expansion and locating the
bookmark and pane layout stores raised an existence error, so @emacs
could not be created. The stores now live in memory only in that case.