Ann: SWI-Prolog 10.1.16

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 incomparable can be set to error to 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 Prolog flag var_prefix now takes a character. Setting it to ?
    allows for variables such as ?x. true is 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 -a rather than not-a.
  • Fixed listing/2 to pass its options to portray_clause/3.
  • Windows: the tmp_dir flag 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 as less and vim, 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 :slight_smile:

    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 says pass_to(portray_clause/3, 3),
    but list_clause/6 called portray_clause/1 and the options were dropped.
    So listing(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 -a was written not-a. That reads back correctly – the reader
    always prefers the prefix operator, and even zzz - x parses 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_prefix is either false (default) or the character that starts
    a variable. '_' is the old true; true is 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
    flag incomparable set to error, 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 reports undefined for 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 the delays buffer 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
    allvars branch 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(Prefix for any alias.
    Used to work only for library(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's prov_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 variable prov_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, default utf8 (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 ?x to ?_x or ?_ if the
    buffer sets var_prefix to ?, 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 under named_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 as CSI 2~‘,
    Find and Select as CSI 1~' and CSI 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 left less', 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's CSI 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 the color_term and hyperlink_term flags
    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 global false and 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 removes menu <->popup_image and 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,
    style normal, 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.

1 Like