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

SWI-Prolog Changelog from version 10.1.13 to 10.1.14

[Aug 29 2026]

  • MAINTENANCE: Added scripts/submodule-update Updates the submodules like `git submodule update`, but pulls a submodule that is on a branch tracking a remote branch instead of detaching its HEAD.
  • FIXED: only write OSC 8 hyperlinks to a terminal ansi_hyperlink/3 wrote the escape sequence whenever the hyperlink_term flag was set, also when the stream was a memory stream. A message captured with with_output_to/2 then came back with the escapes in it, which broke test_messages.pl for anyone running with hyperlinks enabled.
  • TEST: split tests/core into tests/core_text and tests/core_lang tests/core was the longest running swipl test at 13.8 seconds, holding back "ctest -j". Split it in two along the obvious line: core_text holds the tests for characters, strings, encodings, syntax, read/write and stream I/O, core_lang the tests for terms, arithmetic, control, compilation and the runtime.
  • TEST: test_agc_margin: build the filler atom once per size atom_of/3 rebuilt a 100,000 element code list for each of the 200 atoms in every trigger test, which made this file 10.7 of the 16.4 seconds of the core test suite. The list only depends on the requested size, so build one per size and share it. Runs in 0.1 second now; the atoms it creates are still distinct and still 100kB each.
  • ENHANCED: locale independent character classification and case conversion code_type/2, char_type/2, upcase_atom/2, downcase_atom/2 and the other text primitives no longer call <wctype.h>. Character classes and case conversion are derived from the Unicode Character Database at build time, so they answer the same in every locale and on every C library, cover the full Unicode range also on Windows where wint_t is 16 bits, and agree with the classification read/1 uses.

[Aug 28 2026]

  • FIXED: #1517 crash in atom-GC while destroying a thread or engine Destroying a thread or engine that holds many atoms, notably in its private tables, could crash in PL_unregister_atom() if the atom garbage collector reclaimed one of these atoms concurrently.
  • FIXED: normalize_space/2 to normalize Unicode separator characters normalize_space/2 used unicode_separator(), which implements the UAX #31 Pattern_White_Space set used by the Prolog reader. As a result it did not normalize the Unicode separator characters such as U+00A0 (no-break space) or U+3000 (ideographic space), while it did replace the invisible bidi format characters U+200E and U+200F by a space. It now uses fiswspace(), i.e., the same classification as code_type(Code, space).
  • DOC: document the maximum size of unbounded integers Unbounded integers are limited by the global stack limit and, if the system is built using GMP on a platform where long is 32 bits (i.e., Windows), to 512Mb. Document this in \secref{morelimits} and refer to it from the description of **/2.
  • FIXED: Windows crash creating very large integers If long is 32 bits, as on Windows, GMP integers are limited to 512Mb and GMP calls abort() if we ask for more. Our checks only verified the result fits in the global stack limit (1Gb by default), so for example X is 2^4294967294 took the entire process down. In addition, the exponent was truncated to `unsigned long`, which made X is 2^4294967296 silently evaluate to 1.

[Aug 27 2026]

  • CMAKE: test_libs() and test_lib() take OPTIONS Commandline options for swipl itself, handed to it before the test is loaded: a -D flag the test needs set before its libraries are, a -p that only it wants, and the like. There was nowhere to put such a thing: the command is built inside test_lib(), so a package that needed one had to register its tests by hand.
  • ADDED: PL_prompt_is_continuation() Says whether the prompt that comes next continues an input that was started earlier, which is what prompt1/1 and PrologPrompt() already decide between: the prompt of a first line is handed out once, and everything after it is the prompt of prompt/2.

[Aug 25 2026]

  • FIXED: crash when a second Epilog window is deleted Updates xpce. The terminals share the interned Colour objects of their palette but held them with a flag rather than a count, so deleting one Epilog window freed colours the others were still painting with.
  • ADDED: el_set/2 prompt_marks(Boolean) Updates libedit and xpce. Turns the OSC 133 prompt marks off for a terminal that would print them rather than read them. They are on by default.
  • ADDED: OSC 133 semantic prompt marks Updates libedit and xpce. The commandline editor marks its prompts and the line it reads with the FinalTerm OSC 133 sequences, and the Epilog terminal reads them: it now knows when there is a line being edited and where it starts, rather than deducing it from bracketed paste. That gives click-to-move-the-caret back to vi mode, and terminals with shell integration (iTerm2, kitty, WezTerm, VS Code) get prompt navigation over a swipl session for free.
  • FIXED: a click in the Epilog terminal disturbed a single character read Updates libedit and xpce. A click in the line being edited asks the client to move its caret with cursor keys; a client reading a single character answered with the ESC of the first of them, which made the command line tracer print "Unknown option (h for help)" when a link in its port message was clicked.
  • FIXED: Missing empty line after a bullet list in help/1 output A list nested in the body of a definition list (dd) was formatted as a nested list, losing the empty lines that separate it from the surrounding text.
  • DOC: ansi_format/3: use code markup for the '#RGB' colour names These are Prolog atoms, so they belong in code markup. This also avoids the LaTeX translation of the quotes into typographic quotes.
  • FIXED: Spacing of infix operators in term description items A comma binds to the left operand and is followed by a space. Adds \infixopsep{Op}, used by \infixterm and \infixtermitem, which also keeps a module qualifier free of surrounding spaces.

[Aug 24 2026]

  • ADDED: Epilog terminal: highlight the other occurrences of the selection Updated xpce. The dark theme had no isearch styles for terminal_image, so the pale turquoise of the other matches was drawn with the terminal's white foreground; both are now given colour := black, as the editor entry already does.

[Aug 23 2026]

  • FIXED: user streams lose FD_CLOEXEC when restore_ctty() reconnects them dup2() does not carry the flag over, so after a child revoked our controlling terminal and restore_ctty() opened it again, the standard streams were inherited by the next process we start.
  • MODIFIED: ansi_get_color/2 now reports floats in 0.0..1.0 The components of the rgb/3 term were integers in the range 0..65535. That range is ambiguous with the rgb/3 term of win_window_color/2, whose components are in 0..255, and it suggested a precision the terminal does not offer: it reports one to four hexadecimal digits per component, which we scaled up to 16 bits. A component is now the fraction of its maximum, which needs no scale to be agreed upon. This is an incompatible change to a predicate that is rarely used outside library(theme/auto).
  • ENHANCED: Hyperlink and stripe the goals printed by the debugger Goals printed by the debugger are now a hyperlink to their call site, i.e., the position in the clause of the parent frame from which the goal is called. They also carry the new colour class goal(Port, Parity), where Parity alternates over the steps of a trace. This allows themes to separate successive steps using a soft background colour and/or to colour the goal by port. The dark and light themes stripe the goals.
  • ENHANCED: Centralise predicate references in messages and make them links boot/messages.pl formatted a predicate reference in some forty places, using four different module-hiding policies, three copies of the source location lookup and a mix of ~q and ~p. Only one of them produced a link. All of them now go through predicate_reference//1,2, which prints the predicate indicator using the code class and turns it into a hyperlink to its definition when the location is known. Messages that list candidates, such as the alternatives for an unknown procedure, tag each predicate with its kind using predicate_kind/2 and the new themeable class predicate(Kind).

[Aug 22 2026]

  • ENHANCED: Central source location for predicates, including C predicates Added '$predicate_source_location'/2, which resolves a predicate to File:Line. It follows imported_from/1 to the module that defines the predicate and falls back to the location of the implementing C function for foreign predicates. The (expensive) foreign lookup runs addr2line or atos in a child process and is therefore memoised.

[Aug 23 2026]

  • ENHANCED: Hook prolog:line_editor_attributes/2 Tell the line editor how to switch the decoration of the input line off and on again. It must switch these off before erasing the rows the line no longer uses, as erasing paints with the current background colour. Called each time we build a prompt, so a theme loaded at run time is picked up.
  • FIXED: ansi_get_color/2: accept the colour replies terminals really send The reply was matched against a fixed pattern of exactly four hex digits per component terminated by BEL, which is what xterm sends. An X11 colour specification allows one to four digits per component and the string may be terminated by BEL or ST, which made ansi_get_color/2 fail on terminals that answer the query perfectly well. The Epilog terminal is one of these: it replies with two digits and ST.
  • ENHANCED: Colour the toplevel prompt, the typed query and the answers The interactive toplevel now decorates the prompt, the text typed by the user and the answers using the theme classes prompt, input, answer(Parity) and binding(name). Parity is odd or even and alternates over the answers of a single query, which allows a theme to stripe the answers of a non-deterministic query. The default theme only makes the prompt and the variable names bold; the background colours are defined by library(theme/dark) and library(theme/light) as these require knowing the background colour of the terminal.
  • SUBMODULE: xpce: background colour erase and terminal identification Provides two fixes needed by the coloured toplevel:

[Aug 22 2026]

  • CMAKE: Create .dSYM bundles on macOS On macOS the DWARF stays in the object files and the linked binary only carries a debug map that points at them. atos(1) follows that map, which is how '$foreign_predicate_source'/2 finds the source of a predicate defined in C, used by edit/1 and for the links in messages. The map breaks when the objects are removed and some toolchains produce one atos cannot follow: a MacPorts gcc build resolves no source line at all. Running dsymutil(1) after the link collects the DWARF into a .dSYM bundle, which atos locates by UUID, and fixes both.
  • FIXED: addr2line: do not report "at" without a source location Without source line information atos(1) emits "func (in lib.dylib) + 0" and addr2line(1) emits "??:0". We turned both into "func() at ", which is worse than the bare "func()". The " at " is now only added once we know a location follows.

[Aug 21 2026]

  • FIXED: halt/0 from an event callback did not take effect until input arrived With the SDL backend XPCE has no separate pce thread, so events for the main thread are dispatched from PL_dispatch() while the toplevel waits for terminal input. A callback such as Epilog's "Halt Prolog" therefore raises unwind(halt(Status)) in the waiting thread itself, but the wait loop only asked PL_handle_signals(), which returns 0 when no signal is pending. Dispatching continued until the user typed a character, and $raw_read/2 then returned end_of_file with the exception still pending, printing "did not clear exception: unwind(halt(0))".

[Aug 11 2026]

  • FIXED: Windows: Control-C only interrupted the first computation #1515 The console control handler runs on a thread of its own, which has no Prolog engine, so PL_raise() had nothing to raise SIGINT on. It should call PL_w32thread_raise(), but the `#ifdef O_PLMT` guarding that never held: pl-main.c does not see config.h. What made ^C work at all was the C runtime's own handler, installed when PL_initialise() calls signal(SIGINT, ...) and therefore called first. As it resets the handler to SIG_DFL before calling it, it only answered the first ^C.
  • FIXED: Print and link the column of a source location counting from 1. The column of a syntax error and of the hyperlinks we emit for a location was taken from the line_position of the stream, which counts from 0, while the C compiler, ripgrep, editors and edit/1 all count the first column as 1.

[Aug 10 2026]

Package clib

[Aug 23 2026]

  • FIXED: sockets are inherited by processes we start A socket that lacks FD_CLOEXEC goes to every process we exec. A child that inherits a listening socket keeps its port in use for as long as it lives, so an HTTP server does not come back after a restart, and the child may accept connections meant for us. Prolog streams get the flag from Snew(), but a socket is a plain OS handle until it is turned into a stream, and a server socket never is.

[Aug 10 2026]

  • ADDED: library(desktop): desktop_open/1,2 to open a file, directory or URL desktop_open/1,2 hands a document over to the application the desktop associates with it, e.g., a PDF viewer for a `.pdf` file or a file browser for a directory. It uses the Windows ShellExecute() API, open on MacOS and one of xdg-open, gio, kde-open, etc. on other systems. The Prolog flag desktop_opener and the option opener(Command) allow selecting the command explicitly.
  • ENHANCED: process_create/3: detached(true) no longer waits for the process If detached(true) is used without process(PID) and without pipes, process_create/3 now returns as soon as the process is started instead of waiting for its completion. On POSIX systems this uses a second fork(), such that the process is inherited by init and we do not have to reclaim it. On Windows we close the process and job handles.

Package http

[Aug 22 2026]

  • FIXED: HTML rendering of url/2 message elements The HTML message renderer passed the URL of an url/2 element straight into href, which raises a type error if it is a File:Line source location rather than a web link, and it wrote the label as-is, which is wrong now that a label may be a Format-Args pair or an ansi/3 term. Labels are now rendered to text and a source location prints as text rather than as a broken link.

Package libedit

[Aug 26 2026]

  • FIXED: a prompt the program wrote itself was lost on a redraw A read that starts where the output left the caret has no prompt of its own: `format("name: "), read_line_to_string(user_input, S)' puts those six columns on the screen as ordinary output and then reads. libedit is handed an empty prompt and takes the line to start in column 0, six columns to the left of where it does. While it moves the caret relatively that goes unseen, but a full refresh -- a SIGWINCH, a resize it notices at a keypress, ^L -- redraws the input over `name: ' and leaves the caret six columns out.

[Aug 27 2026]

  • ADDED: OSC 133: mark the end of the output and a continuation prompt The prompt, the input and the start of the output were marked; where the output ended was left for the reader to guess from the prompt after it, and a term typed over several lines was marked as several commands because every line got the mark that starts one.

[Aug 25 2026]

  • ADDED: el_set/2 and el_get/2 prompt_marks(Boolean) Turns the OSC 133 prompt marks off for a terminal that would print them rather than read them. They are on by default. The marks live in the prompt string, so setting the option throws the cached prompt away for update_prompt() to build again.
  • ADDED: mark the prompt and the input with OSC 133 While reading a line, surround the prompt and the input with the FinalTerm "semantic prompt" marks: `A' before the prompt, `B' before the input and `C' when the line was entered. A terminal that reads them -- Epilog, iTerm2, kitty, WezTerm, VS Code -- can tell prompt, input and output apart, and so jump between prompts, act on the output of one command, or move the caret to a click in the line being edited.
  • FIXED: no bracketed paste while reading a single character Bracketed paste (DEC private mode 2004) says that a line editor owns the input. A read that asks for one character -- get_single_char/1, the tracer at its `? ' prompt, with_tty_raw/1 -- is not such a read, so turn the mode off for its duration.
  • FIXED: resize of a wrapped input line missed when SIGWINCH is lost Resizing an Epilog window while editing a line that wraps could leave libedit painting on rows that hold something else: the next key moved the caret by rows and columns computed from the width the terminal no longer had, and the repaint left a copy of the first row behind.

[Aug 23 2026]

  • ENHANCED: Do not leave the input decoration on the rows libedit erases The toplevel decorates the line being edited by leaving the attributes of the colour class input in effect while the user is typing. If that class has a background colour, the rows libedit erases because the line got shorter were painted with it and stayed that way: unwrap a long line or move from a multi-line history entry to a shorter one and a coloured bar was left below the line.

[Aug 22 2026]

  • FIXED: Do not decorate completions and incremental search as input The toplevel decorates the input line by leaving the attributes of the colour class input in effect while the user is typing. The completion alternatives and the incremental search prompt are written while these are active, so they were coloured as input. Worse, input may have a background colour, which the `\e[0K' used by the search prompt then paints to the right margin. Both now reset the attributes first; the `redisplay' that ends these commands re-installs the decoration.

[Aug 21 2026]

  • FIXED: honour a PL_dispatch() exception before reading the terminal The PL_RAWTTY/PL_NOTTY branch of Sread_libedit() ignored the result of PL_dispatch() and blocked in read(2), so an exception raised by a signal or an event callback was only noticed after the user typed a character. Hand it to the stream instead, as read_char() already does.

[Aug 11 2026]

  • FIXED: Windows: do not report a modifier key press as a NUL character Windows queues a key-down event for the modifier of every combination typed. Reporting it to libedit as a character handed it a NUL. For ^C that NUL was answered to the interrupt handler's "Action (h for help) ?" prompt, which printed "Unknown option" before the user had touched a key. See swipl-devel issue #1515.

Package ltx2htm

[Aug 25 2026]

  • FIXED: Spacing of infix operators in term description items A comma binds to the left operand and is followed by a space. Rendering it with a symmetric separator turned e.g. the ansi_format/3 attributes into fg(Color) , bg(Color),hfg(Color),hbg(Color). Replaces nospace_op/1 with op_spacing/4 and adds the LaTeX \infixopsep{Op} to do the same for the printed manual.
  • FIXED: Preserve spaces around single and back quotes The LaTeX tokenizer classifies `' and ` ` `` as words. As spaces between two adjacent words are dropped on input and regenerated on output this loses the spaces around them: the translation turns the quotes into the HTML entities `&lsquo; and &rsquo;` and such command tokens do not take part in the regeneration. Tokenize the quotes as TOK_NOSPACEWORD such that the surrounding spaces are preserved in the token list.

Package pengines

[Aug 22 2026]

  • FIXED: HTML rendering of url/2 message elements The HTML message renderer passed the URL of an url/2 element straight into href, which is wrong if it is a File:Line source location rather than a web link, and it wrote the label as-is, which is wrong now that a label may be a Format-Args pair or an ansi/3 term. Labels are now rendered to text and a source location prints as text rather than as a broken link.

Package pldoc

[Aug 25 2026]

  • FIXED: Spacing of infix operators in term description items A comma binds to the left operand and is followed by a space. Adds \infixopsep{Op}, used by \infixterm and \infixtermitem, which also keeps a module qualifier free of surrounding spaces.

Package xpce

[Aug 30 2026]

  • FIXED: print dialog proposed a printer queue named `postscript' When $PRINTER is not set, <-print_command_template now proposes plain lpr, letting lpr(1) pick the default queue, rather than expanding %p to the bogus queue name `postscript'. That name was also a leftover from printing PostScript.
  • FIXED: resources referring to .png icons that were replaced by .svg Several libraries still referred to 16x16 and 32x32 .png icons that were removed when the development tools moved to .svg. Notably, this made the PceEmacs buffer menu fail to open.
  • MODIFIED: print graphics as PDF rather than PostScript graphical<-postscript and class win_printer are gone, leaving both library(print_graphics) and PceDraw unable to print. Both now render to a temporary PDF file and hand that to the print spooler.
  • REMOVED: PceDraw and grapher Windows metafile support Class win_metafile and display->selection_owner no longer exist and the clipboard handles text only, so none of this can work. Removes the PceDraw clipboard menu with its ->export_win_metafile and ->import_win_metafile, the draw_metafile shape and grapher->copy_graph.
  • FIXED: PceEmacs C mode: GDB commands called a non-existing method The method is ->tell_gdb_warn; only ->break_at_line used the right name.
  • FIXED: calls to the deleted graphical->synchronise Both callers only wanted the pending changes to become visible, which is what window->flush does.
  • FIXED: the Dialog Editor used the deleted display<-visual_type Colour is now always available, so the monochrome alternatives and the ifcolour/2 helper selecting between them are dropped.
  • FIXED: PceEmacs buffer menu and Dialog Editor used deleted frame methods frame<->icon and frame<->icon_label were deleted, but two callers were missed. As a result, opening the PceEmacs buffer menu reported an error and failed to create the window.
  • FIXED: crash when ->initialise fails on a class with non-slot C fields Instance prototypes initialise the declared slots only, leaving C fields such as file.fd filled with uninitialised memory. As a failed ->initialise still sends ->unlink, new(_, file(SomeFileObject)) crashed in Sclose().

[Aug 28 2026]

  • ENHANCED: Epilog: toggle fold_previous from the Settings menu Added a marked "Fold previous command" item to the Settings submenu that toggles the fold_previous setting of the current terminal. As with the Debug menu items, a condition updates the mark from the terminal state each time the menu is opened.
  • FIXED: Epilog: a window row is not a buffer line under a closed fold Writing below a closed fold scrolled the window although it had room for the caret still, taking what was above the fold off the top, and a client that placed its caret with CUP landed in the text the fold hides, where what it wrote next never showed. Both counted lines where the window counts rows.
  • FIXED: Epilog: the alternate screen over a closed fold Entering the alternate screen saved one line per row of the window, but a closed fold hides lines the window does not show and the erase that follows takes those away too: what came back after e.g. help/0 was the text the fold hid, with everything below it gone. The window is now saved in the lines its rows cover, and the blocks that name them stay while the application owns the window, so the folds close again over the text that comes back.
  • TEST: terminal: wait for the echo of a repeated command The fixed pause was never long enough on Windows, where the input reaches the client over a pipe a character to the dispatch round.
  • FIXED: terminal: let go of a block whose end was erased An erase takes the lines below the caret and leaves the ones above, so a block can keep its prompt and lose the mark that closed it. It can no longer say how far it reaches, but rlc_sweep_blocks() kept it as long as one anchor was live and ED 0 did not sweep at all. Windows hit this on every ^L: libedit's built-in termcap clears the screen with CUP home + ED 0, and the stale blocks broke terminal_blocks.
  • TEST: terminal: a C0 control around an OSC 133 mark is not printed
  • FIXED: terminal: ignore unhandled C0 control characters Characters below 0x20 that have no VT100 meaning were rendered as glyphs. A bash PS0 carrying readline's \[...\] markers, for example, makes bash emit \001 and \002 verbatim before every command's output. Real terminals discard such controls.
  • ENHANCED: Use ->opacity to grey out an inactive terminal.
  • ADDED: terminal_image ->input_focus: bool Handling the keyboard focus is now a method of its own rather than inline code in ->event, allowing subclasses to react on gaining or losing the focus.

[Aug 26 2026]

  • TEST: terminal: a prompt the program wrote survives a resize Resize while a read that started where the output left the caret is pending, and check that the row is unchanged, that the caret stayed put and that ^A goes to the start of the input rather than to column 0.
  • TEST: terminal: dump the screen when the no-prompt click misses click_moves_the_caret_without_a_prompt asserted the caret column with a bare comparison, which says `4=:=10' and nothing about how the caret got there. It is the one caret assertion in the suite a redraw can move: the prompt is the program's own output, so libedit takes the line to start at column 0, and a full refresh paints the input over `name: ' and leaves the caret six columns to the left of the click. assert_cursor/3 reports the row and the screen, on which that is plain.
  • TEST: terminal: sample the terminal rather than the frame chrome term_screenshot/2 read the top 41 pixel rows of `frame <-image', where the Epilog menu bar sits: the terminal starts 38 pixels down. Every shot therefore sampled the same unchanging chrome, two shots compared equal whatever the terminal was told to paint, and terminal_attributes failed on each of its "must differ" assertions from the day it was added.

[Aug 27 2026]

  • ADDED: Epilog: hide and show the output of every command from the menu The menu of a fold marker gains Hide/Show all outputs, and Remove moves up beside the folding it belongs with.
  • ADDED: the xpce_defaults flag: decline the user's Defaults file XPCE read the class variable defaults on the first lookup of a class variable, wherever in the program that happened to fall, and read the file belonging to whoever was running it along with them. A test suite or an application that must not depend on the preferences of its user had no way to say so: by the time it could speak they had been read. This is not hypothetical -- a terminal test failed here because the author had turned a mode on in his own Defaults.
  • ADDED: Epilog: close the previous command as one is entered Reading an OSC 133 mark was a static function with nowhere to put a policy, and the policy this wanted is not the terminal's. It becomes ->prompt_mark, sent from the OSC decoder as ->window_label and ->working_directory already are, with the letters decoded to what they mean here: where a prompt starts, where the line the user edits starts, where that line was entered and its output begins, and where the output ends. The default implementation is what read them before.
  • ADDED: terminal_block->remove: take a command out of the buffer Folding hides the output of a command; this takes the whole of it away. The lines it covers go and the text under them moves up into the gap, which for an input typed over several lines is all of them at once -- they are one block, so they go as one.
  • ADDED: Epilog: act on the command the mouse or the caret is on The terminal knows what its window has been used for; this is what it takes to reach that. Right-clicking a command offers to copy it, to copy what it printed, or to hide that output; \C-\S-h folds the command being edited, and \C-\S-<cursor_up> and \C-\S-<cursor_down> walk the prompts, as terminals with shell integration do.
  • ADDED: Epilog terminal: commands as terminal_block objects, and folding The OSC 133 marks were read for the one thing the mouse needed and thrown away. They say more: `A' is where a prompt starts, `B' where the line the user edits starts, `C' where it was entered and the output begins, and `D' where that output ends. Kept, they are a model of what the window has been used for, and <-blocks hands it over as a chain of terminal_block objects, oldest first.

[Aug 26 2026]

  • FIXED: xpce terminal: XPCE_TERM_TRACE printed the wrong parser state The name table in rlc_putansi() still held the seven states of an older ansi_state, in the wrong order, so every trace line past CMD_ESC named a state the parser was not in. It now lists all twelve in enum order.
  • FIXED: xpce terminal: ESC > (DECKPNM) left the keypad in application mode Both ESC = and ESC > set app_keypad_mode to true, so leaving application keypad mode never took effect. Nothing reads the flag yet, which is why this went unnoticed.
  • FIXED: xpce terminal: enabled the ptsname() NULL check rlc_open_pty_pair() passed the result of ptsname() straight to strncpy() because the guard against it returning NULL sat in an #if 0. The name it reports, NAME_cannotPtsname, is already in the generated table, so the check only had to be turned back on.
  • CLEANUP: xpce terminal: removed the dead Windows-console API terminal.h still declared the plterm.dll console interface the emulator grew out of: the Rlc*Hook typedefs and their setters, rlc_read/write/ close, getch/kbhit, ScreenCols/ScreenRows, the line-edit and completion structs, the history ring buffer and the lqueued/user_data types. None of it was ever defined. With them go the five LINE-READ SUPPORT functions in terminal.c that had no callers, rlc_caret_forward() which only rlc_goto_mark() used, the rlc_data fields for colours and scrollbar state that ti-> and the palette replaced, and the unused GWL_DATA, IMODE_* and OPT_* macros -- the last two also shadowed the OPT_SIZE enum of SWI-Prolog.h.

[Aug 25 2026]

  • FIXED: xpce terminal: a second terminal took the palette Colours with it A terminal resolves the colour cube and the 24 bit colours of its client to Colour objects and holds them in its palette between paints. It held them with ->lock_object, whose F_LOCKED is a flag rather than a count, while ws_pixel_to_colour() hands every terminal that resolves an RGBA value the same interned Colour. The first terminal to go therefore took the protection of all the others with it: deleting a second Epilog window freed a Colour the first still had in its palette, and the next paint that used that colour handed r_clear() a Colour that was gone. Which is a crash in r_set_fill_fgbg(), as reported.
  • TEST: terminal: a click with nothing to say a line is being edited Exercises el_set/2 prompt_marks(false): with the marks and bracketed paste both off the client says nothing about the line it is editing, and the caret stays where it is.
  • ADDED: Epilog terminal: read the OSC 133 semantic prompt marks A client that marks its prompts says outright what the terminal could until now only guess: between `B' and `C' there is a line being edited, and `B' landed where it starts. That is what a click needs to know before it asks the client to move its caret there, and a click in front of the input now stops at the input rather than walking into the prompt.
  • FIXED: Epilog terminal: click sent cursor keys at a single char read A click in the line being edited asks the client to move its caret, by sending as many cursor keys as there are grapheme clusters in between. A client that reads a single character has no caret to move and answers with the ESC of the first of them: clicking the source link in a port message of the command line tracer made it print "Unknown option (h for help)".
  • TEST: run the terminal resize tests with SIGWINCH held back EPILOG_NO_SIGWINCH keeps rlc_resize_pty() from raising the signal on the client thread, which leaves libedit where a Windows console always is and where a lost signal puts us: with nothing but the size on the pty to go on. That is the path the three resize tests that press a key were failing on about one run in five, and the only way to reach it deliberately.
  • FIXED: xpce: crash when the window system has no display On a machine where SDL_Init() fails -- headless, or no DISPLAY -- xpce loads without a display object and `new(F, frame(hello))' then stored a display that is not there. Reading it crashed the process, in ->create, in <-display, or in the first window that wanted its default colours. Say it instead: with no display there is no frame, and no window either.
  • TEST: move the alternate screen background erase test to the bce suite It belongs with the other background colour erase tests, and it runs there without a pty and without libedit: the whole file takes a second, where the terminal suite is off unless asked for. Reading pixels is still the only way to see the painted tail, so the window is sized to its terminal and the test lets the paint happen before it samples.
  • FIXED: xpce: a freed colour left a dangling entry in @rgba A Colour registers itself in @colours, by name, and in @rgba, by its encoded RGBA value. Both hold their members with `refer' none, so neither keeps the Colour alive, but ->unlink only took the @colours entry out. The @rgba entry then pointed at memory that had been handed out again, and the next lookup of that colour value returned it: `Image <-pixel' does one per pixel it reads, `Colour <-lookup' one per new/2 on a colour with an RGB value, and the terminal one per palette slot it resolves. This crashed the xpce terminal test suite in about half of its runs.
  • FIXED: Epilog terminal: background colour erase lost over the alternate screen The full-line background of the input lines disappeared after help/1, and only the text kept its background. The tail of such a line is painted from the background colour erase (bce) that `\e[K' records on the line rather than from cells, and rlc_copy_line() did not carry that record along when smcup saved the screen.
  • FIXED: Epilog: warning when inserting a non-ASCII character Typing a character above 127 into a terminal_image, e.g. using the Unicode symbol picker, reported "Cannot construct 1-th argument for ->insert_self". The key binding maps any character >= 128 to ->insert_self and fills the argument from the event id, which requires the declared type to include char or event_id. terminal_image declared it as the int range "0..".
  • ADDED: Epilog terminal: a double-clicked word is matched as a word Picking a word with a double click says to look for that word, not for the letters it happens to be made of: a double click on `Bar' passes over `Barn' while dragging over the same three characters does not. It also makes a one-character word worth looking for, which a single character picked any other way is not. A search seeded from such a selection with ^S or ^R keeps it; one started with \C-\S-f has no selection to take it from.

[Aug 24 2026]

  • ADDED: Epilog terminal: ^S and ^R search on from the selection With a selection whose other occurrences are highlighted, ^S and ^R start an incremental search for what it holds, from where it is, and step to the next resp. previous match at once -- so the matches that are already lit up are the ones they walk. From there it is the ordinary search: repeats, Backspace to widen, M-c and M-w, Escape to leave the hit selected. ^G gives back the view and the selection the search started from, so a search one did not mean to start costs nothing.
  • ADDED: Epilog terminal: highlight the other occurrences of the selection Selecting text in an Epilog terminal now says what to look for as well as what to copy: the other places the selected text occurs on the page are painted in <-isearch_other_style, the way the other matches of an incremental search are, and the bar reports which of them the selection is and how many there are, as `Selection: Bar (1/5)'. Picking out a variable or an atom finds the rest of them without typing it again.
  • TEST: terminal: sync with the client before handing out a new terminal The first test of a unit could find the prompt back on top of what it painted: the window sends the client a SIGWINCH while it is still settling on its size, and the client answers that with a redraw of its input line. Neither wait_for_prompt/1 nor wait_settled/1 covers that redraw -- it paints the prompt where the prompt already is, so it leaves the screen exactly as it found it.
  • TEST: terminal: check that SGR attributes select a font New unit terminal_attributes. It paints the same glyphs wrapped in each SGR sequence, samples the window through the new term_screenshot/2 primitive and compares the samples: two shots of the same text differ exactly when it was painted in a different font, so the test need not know which font the painter chose.
  • ENHANCED: terminal: render italic text (SGR 3) The terminal now draws italic (SGR 3) text in a slanted font and turns it off again on SGR 23, and combines italic with bold. Epilog claims to be an xterm, whose terminfo advertises sitm/ritm, so programs were already sending the sequence and it was being dropped.
  • ENHANCED: class style: added ->italic and <-italic A style can now ask for slanted text through an attribute, the way it already asks for bold, instead of having to name an italic font in its ->font slot.
  • FIXED: class style: setting a second attribute had no effect A style that already carried one of ->bold, ->highlight, ->grey or ->hidden silently ignored requests to set or clear another one.
  • FIXED: build on Linux: O_CLOEXEC undeclared in the xpce terminal The pty code asks for `_XOPEN_SOURCE 600` to get the pty API, and glibc hides everything newer than POSIX 2001 once that macro is set by hand, O_CLOEXEC among it. The close-on-exec flags added by the previous commit therefore did not compile on Linux. MacOS declares the symbol at any level because this file also defines _DARWIN_C_SOURCE, which is why it went unnoticed.

[Aug 23 2026]

  • FIXED: shell of an Epilog window survives halting Prolog The pty descriptors were inherited by every process the client starts. The client hands its own three to a shell of shell/0 or process_create/3 as stdin, stdout and stderr, and those it is welcome to, but a copy of the master is another matter: as long as the child holds it, closing our side is not the last reference and the pty never hangs up. The shell is then never sent the SIGHUP that closing the master owes it, and it stays behind, holding whatever else it inherited, after Prolog is gone.
  • FIXED: epilog: describe this terminal to the programs that run in it Epilog claims to be an xterm compatible terminal by setting TERM, but left TERM_PROGRAM at the value it inherited from the terminal it was started from, and run_shell/0 let the shell inherit both. Programs run from an Epilog window or its shell therefore applied the quirks of that other terminal to ours. Notably ansi_get_color/2 does not query a terminal that says it is Apple_Terminal, so no program started from an Epilog window could find out the colours of the window it runs in, which is what library(theme/auto) uses to pick a theme.

[Aug 21 2026]

  • FIXED: terminal: paint the erased tail of a line with the background (bce) `ESC [ K` (Erase in Line) dropped the cells to the right of the caret rather than painting them with the background colour that is in effect. As a result a coloured block, such as the striped answers of the interactive toplevel, only covered its own text and stopped short of the right margin.
  • FIXED: destroying a frame leaked the backing store of its windows unlinkFrame() uncreated the frame before it freed its members, but ws_created_window() is false from the moment the frame is uncreated. uncreateWindow(), reached from freeObject() on each member, therefore skipped ws_uncreate_window() and leaked the Cairo backing surface and the WsWindow of every window of every destroyed frame.
  • FIXED: SDL: dangling pointer to the window holding the pointer grab grabbing_window held a bare pointer, so a window that grabbed the pointer and then destroyed itself -- Epilog's "Halt Prolog" menu item does exactly that -- was unallocated while the event loop still referred to it. Hold a code reference as we do for the mouse tracking window, which also makes the F_FREED test a valid read, and report the recovery under DEBUG(event) rather than on the console.
  • FIXED: Epilog: "Halt Prolog" when Epilog runs inside a Prolog toplevel The menu item asserted quit_requested/0, which is only examined by ep_wait__/0, i.e., when Epilog is the main goal. Started using epilog/0 from a running Prolog there is no such loop and the item did nothing. Track whether ep_wait/1 is active in ep_main_running/0 and call halt/0 directly if it is not.

[Aug 11 2026]

  • FIXED: gcc -Wstringop-truncation warning on MacOS and FreeBSD. The path fields of the OS structs used to find the working directory of the foreground process are fixed size arrays that need not be terminated. Bound the copy by the field size and fail if the path does not fit rather than returning a truncated one.
  • ADDED: Epilog profile/2: a profile/1 option to refine another profile. A profile that gives profile(Super) takes Super's options for the ones it does not give itself, so a profile can be a shell in a particular directory or a toplevel with a particular background without repeating what it inherits. This is the option epilog/1 already takes to pick a profile, now meaningful in a profile of its own.
  • ADDED: epilog/1: inject(Spec) option to type at a new terminal. Spec is an item or a list of items. A string is typed as a line of text, any other item as a Prolog goal, using ->inject. As we cannot tell whether the terminal runs a shell or Prolog, the caller says so by choosing the type.
  • ADDED: xpce timer ->start: number of times to fire, and <-times. A timer started as ->start(repeat, N) fires N times and goes idle. <-times is what is left of that, counted off before the message runs, so a message that sees 0 knows it was the last one and may start the timer again, as it may for a `once' timer. Without the argument a timer repeats as before.
  • FIXED: xpce terminal ->send: write to a pty we never opened or already closed. rlc_send() tested the master fd for non-zero. The struct is zeroed before the pty is opened, so "never opened" was caught, but rlc_close_connection() leaves -1, which passed the test and reached write(-1, ...).
  • PORT: Make compile on MacOS again.
  • FIXED: Epilog and PceEmacs: the column of a source location counts from 1. Clicking a link with a column, e.g., produced by `rg --hyperlink-format=file://{path}#L{line}:{column}`, placed the caret one character to the right, as the line_pos of an xpce source_location counts from 0 while edit/1 and the tools that emit such links count from 1. Epilog now also accepts the #L<line> form used by GitHub and rg.
  • FIXED: xpce terminal: a coloured hyperlink label kept the colours of its client. The link_style colours now only apply to cells for which the client did not select a colour itself. A client that colours its links, e.g., `rg --hyperlink-format=...`, had its colours replaced by the link style. The underline of the link style still marks the link, and the link_armed_style used while hovering keeps replacing the colours: that is transient feedback which must show on a coloured link as well.
  • MODIFIED: PceEmacs File/Shell now starts a shell in an Epilog window.
  • FIXED: xpce terminal: translating a click to a cell was one cell off. Clicking the last character of a hyperlink did not open it and clicking the cell before it did, as the pixel of a click was mapped to the cell boundary at or after it rather than to the cell containing it.
  • FIXED: xpce terminal: process escape sequences inside an OSC 8 hyperlink. An OSC 8 hyperlink is a state rather than a self contained sequence: the label between `ESC ] 8 ; <params> ; <URL> ST` and the same sequence with an empty URL is ordinary output that may carry SGR sequences. We used to collect the label as text, so a client that colours its links, e.g., `rg --hyperlink-format=...`, painted the escape sequences on the screen. Parameters such as `id=` are now accepted as well.

[Aug 10 2026]

  • ADDED: library(epilog): profiles for new terminal windows. A profile is a named set of options for epilog/1. The built-in profiles are prolog, which runs the Prolog top level, and shell, which runs an interactive OS shell in the terminal. New and split windows inherit the profile and start in the working directory reported by the window they were created from. Profiles are added using the multifile hook profile/2.
  • ADDED: xpce terminal_image <-working_directory, <-foreground_directory and <-host A client that reports its directory using OSC 7 (ESC ] 7 ; file://host/path ST), which shells emit from their prompt hook, or using OSC 9;9, the Windows spelling, now updates <-working_directory and <-host. <-foreground_directory instead asks the OS where <-foreground_process is, which needs no cooperation from the client, but only works on Linux, macOS, FreeBSD, NetBSD and Solaris.
  • FIXED: terminal: pass Ctrl+Z to a process running in the terminal Ctrl+Z was dropped unconditionally, so a shell started on a terminal could not suspend its jobs.
  • FIXED: Ctrl+\, Ctrl+], Ctrl+^ and Ctrl+_ to produce a control character These keys were passed on as the plain punctuation character, so a program running in a terminal could not be sent SIGQUIT with Ctrl+\.

[Aug 9 2026]

  • FIXED: terminal: clear(1) left the old screen behind ED 3 (erase saved lines) rebased the ring on slot 0 when the window was already empty, but left the text of those slots and the caret where they were, so the screen came back with old content. `clear' sends ED 2 before ED 3 and hit exactly that.
  • ADDED: profile/2 Multifile hook. This hook can be used to define new Epilog profiles. For example epilog:profile(shell, [title('Shell'), goal(shell)]).
  • ADDED: epilog: a bar over the terminal for messages and search options The incremental search says what it is looking for, how many there are and whether it found anything; until now nothing in an epilog window could show that. Its frame has no reporter, so the ->report calls already there -- the ones the font commands make -- went nowhere.
  • ADDED: terminal_image: Emacs style incremental search on Ctrl-Shift-F Type and the terminal highlights what it finds and scrolls to it. ^S and ^R walk the matches, backspace widens the search again, ESC leaves the hit where it is and ^G gives back the view the search started from. As in class editor, a focus function sits in the way of ->typed and sees every key until the search ends.
  • ADDED: terminal_image: search the buffer with <-find <-find, <-length and <-contents address the terminal's buffer as a flat sequence of characters, and ->selection and ->scroll_to take an index in that same space, so a hit can be highlighted and brought into view. <-find follows text_buffer<-find in its arguments, its defaults and in failing rather than raising when there is no match.

[Aug 8 2026]

  • ADDED: epilog/1: option background(Colour).
  • ADDED: epilog/1: option goal_split(Goal) to determine the goal to run in a new splitted terminal.
  • MODIFIED: Class terminal_image: first try to process Shift-Ctrl-Key or Gui-Key as accelerator
  • TEST: terminal: wait for the shell of shell/1 to be reading start_interactive_shell/1 slept a second and hoped; it now waits for the shell's prompt. What is typed next then reaches a shell that is reading, and ^D in particular is no longer lost: on an empty line it is end of input to the line discipline, which hands it to whoever reads next as a zero-length read. A shell that has not put its own line editor on the terminal yet is not reading, and by the time it does the terminal is in raw mode, where the ^D the line discipline already consumed cannot come back.