Branch: development (switch to
stable),
SWI-Prolog Changelog from version 10.1.18 to 10.1.19
[Oct 20 2026]
- FIXED: waiting on a queue or thread_wait/2 spun with a pending signal
in sig_atomic/1 Inside sig_atomic/1 or a cleanup handler, signals
cannot be handled.
cv_timedwait() nevertheless reported a pending
signal as an interrupt, PL_handle_signals() did nothing and the wait
was retried immediately. As a result, e.g., thread_get_message/3 with
timeout(0) never returned. Waits now only stop for signals that can
be handled and time out normally otherwise.
- BUILD: scripts/configure: add `deps' to build against ~/deps for macOS
10.15 build.deps builds what scripts/make-distribution builds for the
bundle, using the dependencies from scripts/macos-deps.sh, but for this
machine's CPU and without the bundle. MACOS_DEPS overrides ~/deps.
- BUILD: macOS deps: add a small LGPL ffmpeg and, with WITH_SRT=yes,
libsrt ffmpeg 8.1.3 with only libavformat, libavcodec and libavutil,
encoding through VideoToolbox/AudioToolbox and using the bundled
OpenSSL for TLS. xpce uses it if SWI-Prolog is configured with
-DXPCE_VIDEO=ffmpeg. Not yet built on a Mac.
- BUILD: MinGW: also copy DLLs named <name>-<version>.dll for an import
library lib<name>.dll.a, as ffmpeg's Co-Authored-By: Claude Opus
5.5 <noreply@anthropic.com>
[Oct 19 2026]
- ENHANCED: check_installation/0 reports the video codecs xpce can play
and write Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- FIXED: macOS: only use dependencies from one of Macports,
Homebrew or a directory Macports builds picked up packages from
Homebrew, e.g., SDL3_sound, which then loads Homebrew's libSDL3
next to the one from Macports.
find_package() derives prefixes
from $PATH, which CMAKE_IGNORE_PATH does not stop. Now also use
CMAKE_IGNORE_PREFIX_PATH and ignore the other package managers in
all modes. If MACOSX_DEPENDENCIES_FROM is a directory, add it to
CMAKE_PREFIX_PATH and limit pkg-config to its .pc files.
- FIXED: profile_data/1: ticks_self ignored nested calls of the same
predicate If a predicate is called (indirectly) recursively, only
the self ticks of its outermost nodes in the call tree were counted.
The ticks of the nested nodes are now added to the self ticks and
removed from the sibling ticks, such that the self ticks of all
predicates plus the accounting ticks add up to the total ticks.
[Oct 18 2026]
- ENHANCED: check_installation/0 reports the image formats xpce can
save and the sound files it can load Both depend on how SDL_image
and SDL3_sound were built. Nothing is reported if xpce cannot tell,
e.g., without a display or with an older xpce.
- FIXED: macOS: allow the app to use the camera and microphone
swipl-win was killed by TCC when XPCE opened the camera because
Info.plist lacked NSCameraUsageDescription. The sound recorder
needs NSMicrophoneUsageDescription for the same reason. Also add
the camera and audio-input entitlements: under the hardened runtime,
access is silently denied without them.
- BUILD: macOS deps: add SDL3_sound, use libpng for SDL3_image, update
versions SDL3_sound 3.2.0 (icculus/SDL_sound) is a CMake project
built universal next to SDL3 and SDL3_image. All its decoders are
built in; MIDI stays off as it needs patch files. 3.2.0 never sets
SDLSOUND_VERSION, which sdl3-sound.pc.in uses, so we pass it.
- TEST: cleanup of tests only removes what the test added retractall/1
on file_search_path/2 and message_property/2 also removed
the clauses with a variable head, destroying the library search path.
The settings test removed all settings and loaded settings files.
Erase the asserted clauses by reference and restrict the settings
reset to the test module.
- TEST: removed search path setup for the library alias Co-Authored-By:
Claude Opus 5.5 <noreply@anthropic.com>
- BUILD: Build the MacOS release using Apple Clang in a single pass The
MacOS release is now a universal (arm64 + x86_64) Release build using
Apple Clang with CMAKE_OSX_ARCHITECTURES, on both Apple Silicon and
Intel hosts. This replaces the two GCC PGO builds that were merged
using lipo. `build --no-sign` creates an unsigned .pkg.
[Oct 17 2026]
- WASM: default to the switch-based VM again The 19% gain of tail-calling
VMI functions measured with Node 22 was an artifact of V8's tiering:
wasm functions start in the baseline compiler and V8 has no on-stack
replacement for wasm, so the single large PL_next_solution() may
run as baseline code for an entire benchmark while the small VMI
functions are optimized quickly. With all code optimized, bench/
takes for tail calls relative to the switch:
- DOC: src/wasm/README.md describes tiered compilation and the VM
variants, and fixes the commands to create a self-contained .qlf
of bench/.
- WASM: use tail-calling VMI functions by default Clang reports
musttail for WebAssembly, but the backend can only emit tail calls
with the tail-call feature (-mtail-call). VMI_TAILCALL now requires
wasm_tail_call on WebAssembly.
- PORT: Enable VMI functions by default when using AppleClang
- TEST: only test AGC reclaiming trie atoms if
SWIPL_TEST_FAIL_ON_UNLIKELY=y Atom-GC may find stale references to
the atoms on the stacks, making the test fail depending on the build
(e.g., VMI_FUNCTIONS).
[Oct 16 2026]
- FIXED: term_minimal/2 and friends copied attributed variables
term_minimal/2, term_factorized/3,4, term_automaton/2 and
automaton_minimal/2,3 copied the cell of an attributed variable in
the input into the answer rather than referencing it. That made a
second attributed variable that is not on the attvar chain: binding the
original did not show in the answer, and GC could corrupt the stacks.
In the WASM version this showed as test_bisim failing and crashing.
- TEST: tabling tests failed without threads (WASM) Co-Authored-By:
Claude Opus 5.5 <noreply@anthropic.com>
- BUILD: GCC PGO -Wstringop-overflow false positive in
trie_gen_raw()
trie_gen() passed a NULL trie when retrying. With profile data, GCC
(MinGW 16) cloned trie_gen_raw() for that constant and warned about
the first-call branch that dereferences it, though a retry never
takes that branch.
- BUILD: compile without O_PROFILE (WASM) Co-Authored-By: Claude Opus
5.5 <noreply@anthropic.com>
- BUILD: GCC PGO build failed on inconsistent profiles of the VMI
functions With VMI_FUNCTIONS using musttail, GCC finds the profile
of most VMI functions not flow-consistent and refuses to use it.
Add -fprofile-correction. The resulting build is 2-4% faster on bench/
than a PGO build using computed goto.
- FIXED: Windows (MinGW GCC): crash compiling the boot file with VMI
tail calls MinGW GCC accepts preserve_none for the VMI functions,
but the result enters a VMI with LD == NULL. Do not use preserve_none
for MinGW GCC.
- ENHANCED: HOT and hot VMI functions for tabling Mark
trie_insert_flat(), trie_lookup_flat() and push_continuation_list()
HOT and add S_TABLED to src/pl-vmi-hot.h.
- ENHANCED: tabling: cheaper insertion of variant answers
trie_insert_flat() dereferences the arguments once and looks
up children inline; TRIE_STAT_INC() is a relaxed non-atomic
increment. Variant answers are added without a foreign frame:
tbl_reset_add_answer() calls trie_insert_flat() itself and only for
a new or conditional answer continues in tbl_add_answer_found().
500pDoubleFirst about 72%.
- ENHANCED: tabling: resume suspended goals cheaply I_RESET pushes
all environments of a continuation at once instead of meta-calling
call_continuation/1 (which remains for reset/3), the answer is unified
directly into the skeleton of the suspension, '$tbl_reset'/6 prunes
and fails on an answer that completes the table and is no longer
a meta-predicate.
- ENHANCED: compiled tries: save the trie VM state only at choice
points The trie VM trails its frame slots at most once per choice
point; T_TRY_* saves TrieOffset in the CHP_JUMP choice point instead
of trailing it (
setJumpChoice()). accManyComp 78%, accOneComp 63%,
accManyAtom 88%.
- ENHANCED: tabling: add answers on exit of the worker Answers are added
by I_EXITRESET when the worker of '$tbl_reset'/6 exits rather than by a
Prolog call. Unconditional answers without delay information are added
inline and tries no longer keep reference counts for built-in atoms.
- ENHANCED: tabling: cheaper creation and completion of tables
Call internal deterministic foreign setup and cleanup goals
directly, cheaper creation of a private table, run the leader using
'$tbl_leader'/5 with a C cleanup and in fewer steps, return zero or
one answer of a leader directly and reuse freed components, worklists
and clusters.
[Oct 15 2026]
- ADDED: Prolog flag table_drop_intermediate (experimental) If non-zero,
abolish tables of completed child components that merely served the
outermost tabled goal when this goal completes. Tables are kept
if they are small, called again after completion or expensive to
recompute (in inferences, including the dropped tables below them).
Trie node chunks count their live nodes and empty chunks are freed,
so pruning many variants releases memory. Deleting a node in a full
chunk only locks if atom-GC may register the chunk.
[Oct 16 2026]
- ENHANCED: tries: allocate nodes from chunks and avoid atomic counters
Trie nodes are allocated from chunks. Atom-GC marks the atom keys
of the chunks and registers the keys of full chunks.
- ENHANCED: tabling: compile answer tries lazily and discard their
nodes Answer tries are compiled when called often enough (Prolog flag
table_compile_threshold, default 32) and their nodes are discarded.
trie_gen/2,3 use a compact path for deep tries. table_statistics/0
handles answer tries with discarded nodes.
[Oct 14 2026]
- ENHANCED: tries: compact children representation with optional striped
write locks Trie node children are now a tagged word: no children, a
single child or a block of {key,child} entries. Blocks up to 8 entries
are searched linearly; larger blocks have an open addressing index.
This replaces the lock-free hash tables, making a hashed child lookup
about 3 times cheaper and reducing memory usage of tries.
- ENHANCED: tabling: add flat answers to the answer trie without the
generic term walk
trie_insert_flat() adds an atom or a compound whose
arguments are atomic or plain variables, creating the same keys as
trie_lookup_abstract(). '$tbl_wkl_add_answer'/4 uses it first and
falls back to trie_lookup_abstract() for other answers. This reduces
the cost of an answer insertion (mostly duplicate checks) by about 25%.
[Oct 16 2026]
- ENHANCED: tabling: S_TABLED supervisor calls complete tables directly
The S_TABLED supervisor looks up the variant of a call directly from
the frame arguments (flat calls, cached start node of the predicate
in the variant table) and calls a complete table without going
through Prolog. S_INCR_DYNAMIC uses
put_supervisor_goal().
[Oct 13 2026]
- FIXED: '$tbl_variant_table'/6 crashed if the first argument is
not a closure start_tabling/3 documents that the closure may be
passed as 0, in which case the predicate is found from the wrapper.
'$tbl_variant_table'/6 then used a NULL predicate. Resolve the
predicate from the variant if there is no closure.
- FIXED: calling a complete shared moded table raised a type error For
a shared table, '$tbl_variant_table'/6 returned the compiled answer
trie clause if the table is complete. start_moded_tabling/5 passes
this to trie_gen/2, which requires the trie. Moded tabling does not
use the compiled answer trie, so do not request it.
[Oct 16 2026]
- MODIFIED: GCC defaults to VMI_FUNCTIONS if it supports musttail With
musttail (GCC >= 15) the VMI functions use guaranteed tail calls,
which is faster than the single function using computed goto (GCC 16:
bench/ 97%, tabling 91-101%). Older GCC keeps computed goto.
- ENHANCED: keep the hot VMI and C functions of the VM together
src/pl-vmi-hot.h lists the 80 VMI/VMH functions with the most cycles
on bench/ and the tabling benchmarks (doc/tabling/tools/vmi-hot.sh).
pl-wam.c redeclares these with attribute((hot)) when using
tail-calling VMI functions, which groups them in .text.hot (33.5Kb
contiguous instead of spread over 94Kb). HOT marks the hottest C
functions of the VM (clause selection, unify, tries, continuations,
fastheap terms), so they are placed next to these: the instruction
cache behaviour of the hot code then no longer depends on the placement
of unrelated code.
- ENHANCED: tail-calling VMI functions With VMI_FUNCTIONS and a compiler
supporting musttail (GCC >= 15, Clang >= 13), VMI and VMH functions
continue with a guaranteed tail call of the next instruction instead
of returning to the dispatch loop. Helper arguments are passed
in the register file so all functions have the same signature.
With preserve_none the hot registers LD, PC, FR, the register file
(r15) and ARGP are arguments. VMI functions are noinline (GCC 16 drops
musttail in inlined VMIs). VMCODE_IS_ADDRESS is used for Clang too.
VMI_TAILCALL=0 and VMI_CC= select the fallbacks.
- ENHANCED: mark slow-path functions COLD Add COLD
(attribute((cold))) and apply it to functions that are only called
on slow paths: stack overflows and growing the stacks, errors, the
debugger, tracing, coverage, profiling, etc. GCC moves the blocks
calling these into the .cold part of the caller, which shrinks the
hot part of PL_next_solution() from 92.9Kb to 74.2Kb.
- ENHANCED:
put_fastheap(): relocate by tag in a single pass Drop the
relocation array from fastheap records: the term part holds only tagged
cells, so a cell is relocated iff it has STG_GLOBAL storage and is
not a functor (mask table). Restoring is a single branch-free pass
with post-passes for attvars and atoms only if needed. Records are
about 40% smaller.
- ENHANCED: avoid store-to-load forwarding stalls in arithmetic and
clause choice points A struct copy or
memcpy() of data just written by
narrower stores compiles to wide loads that cannot be store-forwarded
and stall until the stores retired. moveNumber() copies small integers
and floats by field; clause choices and moved choice points are
copied using scalar moves (copyWordsScalarUp()/copyWordsScalarDown(),
copyClauseChoice()).
- ENHANCED: use non-atomic updates for the flags of local frames
set()
and clear() use atomic operations. Local frames are only accessed by
their own thread, so the VM can update their flags with plain stores.
The locked instructions were paid on every non-deterministic exit,
SSU commit, cut, etc. Adds setLocal() and clearLocal().
[Oct 15 2026]
- ENHANCED: per-thread cache for looking up modules by name Meta calls
look up the module of M:Goal by name. Each thread now caches the
most recent lookups in a direct-mapped cache of 8 entries, which is
flushed when a module is destroyed. Calling a qualified goal takes
10% fewer instructions.
- ENHANCED: do not validate term_t for internal PL_get_blob() and
PL_get_pointer_ex() As for the other PL_*() functions, the exported
functions validate their arguments and internal callers use the ___LD
version without validation. These two are used by the tabling and
trie predicates for each table.
[Oct 16 2026]
- ENHANCED: dark theme: colours for the CPU gauges of the thread monitor
and the code ranges of the symbol picker Co-Authored-By: Claude Opus
5.5 <noreply@anthropic.com>
- ENHANCED: dark theme: colours for error and warning reports, the
failing part of an incremental search and code in running text
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
[Oct 14 2026]
- ENHANCED: dark theme: colour for filter matches in PceEmacs hit lists
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session:
https://claude.ai/code/session_01UgGX2Hqkp4kavsXYTKX8Sf
- FIXED: #1552 crash when unwinding a stack overflow in debug mode If
there is no space to start the tracer, b_throw_unwind_debug retries
while unwinding using
dbg_except_start_tracer(). This runs GC, trims
the stacks and prints the message, which may move the local stack.
The VM registers were not saved and reloaded around this call,
leaving FR pointing at the old stack.
[Oct 13 2026]
- FIXED: JIT indexing: a single remaining candidate argument was
dropped
candidate_indexes() only merged the remaining single-argument
candidates inside the block that builds two-argument combinations,
which requires at least two assessments. If exactly one poor
but useful argument remained (e.g., the parent argument of
bintree(Parent,Child) with the child argument as good index), it
was silently discarded. As static predicates have fixed candidate
indexes, calls on that argument then scanned all clauses. This made
the bintree18 tabling benchmark about 60 times slower.
- BUILD: macOS deps: build libpng so that cairo has the SVG surface
cairo builds its SVG surface only along with the PNG functions,
so XPCE's ->svg was unavailable in the bundle.
- TEST: monotonic propagation to reused tables and trie value/delete
cases test_mono_reuse.pl covers several binding patterns on one
subsumptive table, a subsumed call on a recursive table, a dependency
created during propagation, incremental+monotonic subsumptive tables,
indirect keys in abstracted calls and reevaluation of an invalidated
incremental abstract table. test_trie.pl adds an emptied root
that held a variable key and balanced atom references over repeated
trie_update/3 calls.
- DOC: trie_gen/3: do not claim the cost only depends on matching paths
A variable in Key still visits every stored subterm at its place.
Also state that the enumeration order is undefined and that a choice
point may be left after the last answer.
- FIXED: monotonic dependency index left a foreign frame open on errors
mono_index_find(), mono_index_insert() and mono_index_build() returned
on an error without closing the foreign frame they opened. Close it,
keeping the exception. Also update the comment on cluster.sindex,
which now indexes on several key paths.
- ENHANCED: trie_gen/2,3 keep pruning on bound arguments after a variable
trie_gen/2,3 followed the bound arguments of Key only up to the
first variable, in Key or in a stored key. From there it enumerated
the whole subtrie and left it to unification to reject the paths,
so looking up
p(a, 25000) in a trie of p(_, I) keys walked every I.
dyn_affected/2 in boot/tabling.pl makes such a lookup on the variant
table of an incremental predicate for every fact the predicate gains
or loses.
[Oct 9 2026]
- ENHANCED: tabling: index suspensions on key paths during completion
During completion '$tbl_wkl_work'/6 offers every answer of an answer
cluster to every suspension of the suspension cluster it is paired
with. A suspension of a call answered from a more general table
carries the call as an instance. The scan rejects it cheaply only
when a key of an argument of that instance differs from the answer's,
and otherwise copies the instance from the fastheap and unifies it
with the answer.
- ENHANCED: monotonic tabling: index a table's dependencies by skeleton
Calls answered from an abstract or more general table each record
a monotonic dependency on that table with their own skeleton.
The dependencies of one (source, consumer) edge form a chain:
find_dep() walks it to find a duplicate of each new dependency,
and propagating an answer copies and unifies every dependency of
the source. One consumer making N bound calls to one abstracted table
cost 2.1 s to evaluate for N = 32000, against 0.40 s through N variant
tables, and each later answer 6.6 ms to propagate, against 8 us.
- FIXED: monotonic tabling: propagate to calls answered from subsumptive
and abstract tables A call to a monotonic tabled predicate that is
answered from a table other than a new variant table of its own did
not record its dependency on that table: a subsumed call reading a
more general table, a call reusing an existing variant table of a
subsumptive predicate, and a call abstracted by
subgoal_abstract(N).
Answers that monotonic propagation later added to that table never
reached the caller. For example, with
[Oct 13 2026]
- FIXED: trie_gen/2,3 crashed on a trie emptied by trie_delete/3
When trie_delete/3 removes the last key below a root whose children
are hashed,
prune_node() stops at the root and leaves it the empty
table. add_choice() enumerated that table without testing whether
advanceTableEnum() produced a child and used the undefined key
and child:
- FIXED: trie_gen/2,3 missed a stored attvar in a hashed trie node
When the children of a trie node are hashed and the query is bound at
that position,
add_choice() looks up the query's own key and then the
variable keys recorded in the node's var_mask. update_var_mask() only
records plain variables (TAG_VAR), so the key of a stored attributed
variable, (N<<LMASK_BITS)|TAG_ATTVAR|STG_STATIC, was never tried and
the answer was lost. With a single child (TN_KEY) the attributed
variable was found:
- FIXED: trie_update/3 and trie_insert/3 corrupted a stored compound
value
variantRecords(), which equal_value() uses to compare the value
stored at a trie node with a new one, called memcpy() where memcmp()
was meant. For two records of the same size it copied the new record
over the old one and, as memcpy() returns its destination, reported
them as different.
- FIXED: clause_info/4 failed on optimised clauses using dicts in
maplist/N or debug/3 library(apply_macros) reorders the arguments
of maplist/N, which changes the order of the '.'/3 calls that
evaluate dicts. Decompiled '.'/3 goals may now match any goal in a
run of '.'/3 calls. Also skip the '.'/3 calls of a goal removed by
optimisation such as debug/3.
- FIXED: clause_info/4 failed on clauses with
Var is Int + Var The
compiler turns this into A_ADD_FC, which decompiles as Var is Var
+ Int. The clause body unification now handles this argument swap.
- FIXED: reloading a file that deletes clauses from an indexed
static predicate left the deleted clauses reachable through the
index
reconsultFinalizePredicate() only updated the clause indexes
for dynamic predicates. Since the static code fast path skips the
visibility check, the erased clauses remained visible through the
JIT indexes after make/0.
[Oct 12 2026]
- FIXED: with optimise, var/1 and nonvar/1 on a fresh variable did
not mark the variable as initialised. As 7dba157ea, this could
leave the variable uninitialised after \+ inside a branch of ;/2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ADDED: Build the ARM64 Windows installer in the release workflow Turn
the single x64 job into a matrix over {x64, arm64}. The ARM64 leg runs
on a native aarch64 runner (ubuntu-24.04-arm), as the ARM64 host tools
execute under Wine and need a 4K-page aarch64 kernel. It uses the
swipl-mingw-build-arm64 image and --winarm64-from-git. fail-fast is
disabled so a failure on one architecture does not cancel the other.
Package RDF
[Oct 18 2026]
- TEST: removed search path setup for the library and foreign aliases
src/swipl in the build tree already finds the libraries and foreign
modules of all packages.
Package chr
[Oct 18 2026]
- TEST: removed search path setup for the library and foreign aliases
src/swipl in the build tree already finds the libraries and foreign
modules of all packages.
Package clib
[Oct 18 2026]
- TEST: removed search path setup for the library and foreign aliases
src/swipl in the build tree already finds the libraries and foreign
modules of all packages.
Package cpp
[Oct 18 2026]
- TEST: removed search path setup for the library and foreign aliases
src/swipl in the build tree already finds the libraries and foreign
modules of all packages.
Package http
[Oct 18 2026]
- TEST: removed search path setup for the library and foreign aliases
src/swipl in the build tree already finds the libraries and foreign
modules of all packages.
Package inclpr
[Oct 18 2026]
- TEST: removed search path setup for the library and foreign aliases
src/swipl in the build tree already finds the libraries and foreign
modules of all packages.
Package jpl
[Oct 18 2026]
- TEST: removed search path setup for the library and foreign aliases
src/swipl in the build tree already finds the libraries and foreign
modules of all packages.
Package odbc
[Oct 18 2026]
- TEST: removed search path setup for the library and foreign aliases
src/swipl in the build tree already finds the libraries and foreign
modules of all packages.
Package pengines
[Oct 18 2026]
- TEST: removed search path setup for the library and foreign aliases
src/swipl in the build tree already finds the libraries and foreign
modules of all packages.
Package pldoc
[Oct 18 2026]
Package protobufs
[Oct 18 2026]
- TEST: removed search path setup for the library and foreign aliases
src/swipl in the build tree already finds the libraries and foreign
modules of all packages.
Package semweb
[Oct 18 2026]
- TEST: removed search path setup for the library and foreign aliases
src/swipl in the build tree already finds the libraries and foreign
modules of all packages.
Package ssl
[Oct 18 2026]
- TEST: removed search path setup for the library and foreign aliases
src/swipl in the build tree already finds the libraries and foreign
modules of all packages.
Package xpce
[Oct 21 2026]
- MODIFIED: xdot demo only lists the installed graphviz layout engines
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- FIXED: clicking a menu bar moved the keyboard focus of the frame If
the window holding the menu bar wants the keyboard, e.g., a dialog
with a text_item, a click on the menu bar made it the keyboard focus
of the frame. After the menu, or a dialog opened from it, such as
PceEmacs Edit/Replace, closed, the keys no longer went to the window
the user was working in. A click on a menu bar now leaves the focus
alone. Keyboard operation of the menu is not affected: while open,
the menu bar grabs the keyboard.
- FIXED: closing a transient frame could leave its owner without the
keyboard Window managers are supposed to give the keyboard back to the
window a dialog is transient for when the dialog closes. GNOME with
focus-follows-mouse does not always do so, leaving the owner visible
but without a caret or keys. If the transient had the keyboard
and nothing took it over, raise the transient_for frame ourselves,
on all platforms.
[Oct 20 2026]
- DOC: Versioned userguide upload
- BUILD: take the xpce version from the VERSION file CMake, the C
kernel (via config.h) and the User Guide's version.tex now all get
PCE_VERSION from VERSION. Removed the stale fallback definition
from h/interface.h.
- DOC: User Guide: SDL's PipeWire camera driver does not work
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- FIXED: demo video player: loading a file raised `@nil <-duration'
->open reports the status synchronously for a local file, so
->video_status ran ->prepare before ->load had set the video slot.
- FIXED: SDL timer: a tick queued before ->stop still ran the message
Closing the Asteroids or Pong window stops its timer in ->unlink,
but a tick that was already in the event queue then sent ->tick to
the freed window.
- DOC: User Guide section "Drawing with the GPU" with mesh and canvas
examples; refman links to sdl_mesh and sdl_canvas The User Guide
documents pce_sdl_mesh_set/3. The anchors of the sdl_mesh and
sdl_canvas pages used hyphens, which broke links to them.
- ADDED: demo Particles; demos Asteroids, Pong and Camera use the new
GPU drawing Particles draws thousands of glowing particles using a
single sdl_mesh. Asteroids has glowing sparks, drifting tiled dust
and an exhaust trail drawn into an sdl_canvas, Pong glowing sparks
and a net of one tiled layer and Camera ripple and fisheye effects
that warp the video using an sdl_mesh.
- ADDED: class sdl_canvas draws sdl_layer objects into a texture that a
layer shows Moves many sprites as one, shows the same layers again,
e.g., as a minimap, and leaves trails with sdl_canvas<->clear @off.
The renderer draws a canvas before the frame and only after it changed.
- MODIFIED: sdl_layer<-window is now sdl_layer<-device, as
graphical<-device This prepares for layers shown on an off-screen
sdl_canvas.
- ADDED: class sdl_mesh: triangles with coloured corners that an
sdl_layer draws, mapping its texture onto them sdl_layer<->mesh makes
a layer draw the triangles of a mesh using SDL_RenderGeometry().
This draws gradients, warps a texture and draws many particles in
one layer. pce_sdl_mesh_set/3 replaces all triangles of a mesh in
one call.
- FIXED: translucent sdl_layer textures drawn with a shear replaced
what is below on the software renderer SDL's software renderer draws
triangles without the premultiplied blend modes. It now gets textures
with straight alpha.
- ADDED: sdl_layer blend modes, tint, scale mode, tiling and 9-slice
scaling sdl_layer<->blend selects SDL's blend modes (blend, add,
mod, mul, none), <->tint multiplies the colours of the texture and
<->scale_mode selects linear, nearest or pixelart sampling. Scale
tile repeats the texture over <-area, shifted by <->offset, and
<->slices scales it as nine parts that keep the size of the corners.
- FIXED: sdl_layer<-alpha was too bright for images and ignored for
camera and video frames SDL does not scale the RGB of premultiplied
textures by the alpha mod, and textures without an alpha channel were
drawn without blending.
- DOC: xpce refman: fix links to classes whose name contains an
underscore Several newer class pages used hyphens in their anchor
(e.g. {#class-sdl-layer}), giving the label sec:class-sdl-layer,
while \class{sdl_layer} links to sec:class-sdllayer (PlDoc strips the
_). Links to these classes were therefore missing.
- DOC: User Guide: video backends, sprite stacking, cursor none and
class summary entries for SDL and video classes Co-Authored-By:
Claude Opus 5.5 <noreply@anthropic.com>
- ADDED: demo Pong: the good old game for one or two players Play against
the computer or with two players using the keyboard or gamepads.
Like the Asteroids demo, the paddles, ball, trail, sparks and net
are GPU sprites driven by a timer, the controls are polled and the
beeps are generated sound. Before the first game the computer plays
against itself.
- ENHANCED: demos Camera and Sound: tidier controls with tooltips,
split buttons and a REC/LIVE/PLAY indicator Start/Stop, Record/Stop
and Play/Stop are single buttons whose label changes. Snapshot,
Record and Open are split buttons whose menu holds the other actions,
e.g., saving a snapshot, streaming to a URL or recording to a WAV file.
- ADDED: classes video and video_writer play, record and stream encoded
video with sound A video plays a file, a URL (http, HLS, RTSP,
UDP, ...) or encoded data written to it, e.g., using pce_open/3.
Its frames go into an sdl_texture, so an sdl_layer shows them in an
sdl_window without Cairo, and its sound sets the time. It can play,
pause, seek and loop, gives thumbnails and keeps up with live streams.
A video_writer encodes images, textures, frames, windows, cameras
and the microphone into a file, streams them to a URL (RTMP, SRT,
UDP, TCP, RTP, HLS) or gives the data to code. The backends are
ffmpeg (Linux, and Windows and macOS with -DXPCE_VIDEO=ffmpeg), Media
Foundation (Windows) and AVFoundation (macOS); streaming needs ffmpeg.
library(pce_video) writes video to a Prolog stream. Demos: Video
player, Camera records and streams.
- FIXED: A slider without label or values was too high and its bar was
not aligned with the items in its row Without a label, the reference
point of a slider is the baseline of its values or, without these,
such that its bar is at the middle of the text of the other items.
The height only includes the label and the values if they are shown.
- FIXED: Compile with SDL < 3.4.16, where pen proximity events have no
pen_state Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
[Oct 19 2026]
- FIXED: Crash decoding data SDL_sound does not recognise on macOS
SDL3_sound's CoreAudio decoder crashes if no file extension is given,
which happens for an audio_stream with encoded data and when loading a
file whose name has no extension. Pass "" instead, which also makes
SDL_sound try all decoders.
- TEST: sdl_keyboard:text_input only checks the key events Dispatching
events for 0.2 seconds also delivers the events of the real pointer,
e.g., area_enter and loc_move on Windows if the pointer is over the
new window.
- ENHANCED: Asteroids demo draws its objects as GPU sprites, has buttons
to play, edit the settings and show the controls, and hides the pointer
while playing The field is an sdl_window and the ship, rocks, bullets,
particles and stars are sdl_layer subclasses with a texture drawn
once from a figure. The buttons appear on start-up, after the game
is over and while it is paused. Settings opens the class variable
editor on the preferences of the game. F1 shows the controls.
- ADDED: predefined cursor none hides the pointer Co-Authored-By:
Claude Opus 5.5 <noreply@anthropic.com>
- ADDED: classes sdl_texture, sdl_layer and sdl_window draw pixels with
the SDL renderer instead of Cairo An sdl_texture keeps its pixels in
their own format. An sdl_layer places it in an sdl_window, scaled
to an area or as a sprite that is moved, rotated and scaled using a
transform around the texture's hot spot. The layers are drawn below
the graphicals of the window, so these can annotate, e.g., video.
camera->texture delivers frames to a texture without converting them,
which the Camera demo uses. Textures are made from an image or a
graphical, image->copy converts them back and sdl_texture->draw_in
draws on them.
- ENHANCED: The class variable editor serves applications: Control-comma
opens it and it shows the class variables of application classes
Control-comma (Command-comma on MacOS) opens the class variable
editor on the object below the pointer in any xpce window, or shows
that object in the editor that is open. Without a preferences
specification, the editor shows the class variables the manual
classifies as basic. The manual does not know the classes of an
application, so all class variables of these classes are now basic.
- ENHANCED: Asteroids demo: game objects are graphicals that hold their
own state The ship, asteroids, bullets, particles, stars and scoreboard
texts are now classes that keep their position, velocity and timers
in slots and update themselves in a ->tick method. The window holds
the state of the game as a whole and ticks everything using
- ADDED: display_manager<-audio_output tells whether there is a device to
play sound on The first request starts the audio subsystem. From then
on the value follows the playback devices that are added and removed.
This allows an application to make sound optional: playing a sound
or audio_stream without an audio device prints an error.
- ENHANCED: The event viewer shows the button, click, rotation, device
and event<-detail of an event; event_node<->summary describes each
event type All types of @event_tree have a summary, including those
of pens, touch screens, pinches, gamepads, devices, drag-and-drop
and key_up. The event viewer shows it; a key that types a character
shows the summary of its kind. Fixed the description of cursor_down
and the function keys.
- ADDED: key_up event for releasing a key and event<-repeat for
auto-repeated key-downs A key_up is not a keyboard event, so key
bindings and text editing do not see it. It goes to the keyboard focus
and its event<-key is that of the key-down it releases. Both carry a
new event_detail, key_data. frame->sdl_test_event accepts text_input.
- FIXED: A window without text input did not get the keys of plain
characters Without text input, as in a plain window without a focused
text_item or editor, SDL sends no text input event. The key-down now
gives the character, shifted as the keymap says, without waiting
for text. Dead keys and input methods still need text input.
frame->sdl_test_event accepts key_down and key_up.
- FIXED: A read-only text_item that has the keyboard focus showed an
active caret A read-only item still takes the focus when clicked,
so text can be selected with Shift-cursor and copied with C-c, but
it shows no caret.
- ENHANCED: file_item has a browse icon;
library(find_file) uses the
file dialog of the desktop unless display.native_file_dialog is @off
text_item<->icon and <->icon_message show a clickable image right
of the field. file_item uses it to run ->browse (class variable
browse_image), replacing the separate "Browse ..." buttons of the
PceEmacs prompter and the class variable editor. file_item<->filter
sets the extensions offered. @finder uses frame<-open_file,
<-save_file and the new <-open_directory if display.native_file_dialog
is @on and a frame is shown. The sound demo uses a file_item for
the location it streams.
- ADDED: class audio_stream plays audio while it is written;
audio_recorder->
record(to=[source_sink]) writes while recording An
audio_stream is a source_sink that plays raw samples or encoded data
(WAV, MP3, FLAC, ...; not Ogg Vorbis) while Prolog writes it using
pce_open/3 or ->append. <-low_water_message asks for more data,
closing the stream ends the data and <-message runs when everything
is played. audio_recorder->record(to=File) writes a WAV file while
recording; any other source_sink gets the raw samples.
- ENHANCED: pce_open/3: binary streams on a source_sink with
encoding octet; ->close_as_file pce_open/3 opens a binary stream
on a source_sink whose encoding is octet, so ->write_as_file gets
the bytes unchanged. Closing a stream opened for writing sends
->close_as_file to the object, a new virtual method of source_sink.
- ADDED: variable<-types, as method<-types The argument types of the send
method: <-type if the variable can be set, none if it can only be read.
Fails if the variable is not accessible. Fixes M-x comment-column
in PceEmacs language modes, where comment_column is a slot.
- FIXED: PceEmacs grep: report a failing command in PceEmacs and show
the outcome of the next grep A failing grep reported its exit status
on the console, and its error message stayed on the PceEmacs status
bar, hiding the outcome of later greps.
- ENHANCED: Theme preferences edit display.bell_sound The bell sound
is edited using a file_item with a Browse button that starts in the
desktop's sound directory. Bell pitch and duration are inactive if
a bell sound file is set.
[Oct 18 2026]
- ENHANCED: display->bell plays a sound: display.bell_sound or a
tone made by the new sound->tone display<-bell_sound returns the
sound ->bell plays. The class variable display.bell_sound names a
sound file to play instead of the tone of display.bell_pitch and
display.bell_duration. The tone now fades in and out, so it no
longer clicks.
- FIXED: PlDoc warning on ->goto_history in pce_html_manual.pl xpce
member comments take a bare header; the signature is derived from
the method definition.
- TEST: frame->sdl_test_event lost negative device ids on ARM64 (macOS)
It converted the float field value directly to the unsigned id type,
which is undefined for negative values: ARM64 gives 0 instead of,
e.g., SDL_PEN_MOUSEID. The tests sdl_emulation:pen_mouse_ignored
and touch_mouse_ignored failed on macOS.
- DOC: no "PlDoc: unknown tag @cursors" warning when building the
reference manual `@see @cursors` makes PlDoc read @cursors as a tag.
Added it, and the other globals that follow an @see (@completer,
@errors and @key_bindings), to the globals for which pldoc_xpce
suppresses this warning.
- ENHANCED: demos ImageViewer and Juggler use class animation ImageViewer
plays animated PNG, GIF, WebP and AVIF files and shows WebP, AVIF,
BMP, ICO, CUR and ANI files. Juggler plays an animation made from
its SVG images; its speed sets the delays of the frames. Also fixed
the spelling of the Juggler's description in the demo browser.
- FIXED: image->copy painted the copy over the old pixels, losing
transparency As a result, animation->append also made the transparent
parts of a frame, e.g., of an SVG image, opaque white.
- FIXED: semi-transparent pixels were too dark when saved, copied or
used in a cursor, and image->pixel ignored premultiplied alpha Images
are Cairo surfaces with premultiplied alpha. Converting them for SDL
(saving, the clipboard, cursors) now unpremultiplies, and image->pixel
and image<-pixel convert between the straight alpha of colours and
the premultiplied pixels.
- ADDED: animated cursors:
cursor(Name, Animation), e.g., from a
Windows .ani file Images keep the hot spot of CUR and ANI files in
image<-hot_spot and save it, so cursor/2 uses the hot spot of the
file by default.
- ADDED: animation->append, ->record and ->save: make and save animated
PNG, GIF and WebP files, with their loop count and metadata An
animation can now be made from images (->append) or by recording a
frame or window (->record, ->stop), and saved as an animated image file
(->save). ->save writes <->loop_count and the new <->title, <->author,
<->description and <->copyright, as far as the format can hold them.
Loading finds the format from the data rather than the file name.
- ENHANCED: image->save writes JPEG, WebP, AVIF, GIF, BMP, TGA, ICO
and CUR The format defaults to the one the file extension selects,
PNG if the extension is not one of these. A new third argument sets
the quality of JPEG, WebP and AVIF. display<-image_save_formats lists
the formats SDL_image can write.
- MODIFIED: PceEmacs loads its init file from
user_app_config('xpce/emacs/init') On all platforms, PceEmacs now
loads xpce/emacs/init.pl from the user's configuration directory
(e.g., ~/.config/swi-prolog) instead of ~/.pceemacsrc or, on Windows,
pceemacs.ini. If only the old file exists, PceEmacs prints a warning
asking to move it.
- ADDED: demo Animated images: play animated PNG, GIF and WebP files
Plays an animation in a bitmap, with controls to play, stop, step
and select a frame, and a film strip of all frames. The default
animation is the public domain bouncing beach ball APNG by Holger
Will from Wikimedia Commons.
- ADDED: class animation holds the frames of animated PNG, GIF,
WebP and AVIF files; a bitmap plays it
animation(File) decodes all
frames using SDL_image, with <-delays per frame and <-loop_count.
bitmap(Animation) shows its first frame; bitmap->play, ->stop and
->frame play it or show a frame, and <-playing tells whether it plays.
- FIXED: Windows: class sound only loaded WAV files although SDL3_sound
was found Windows did not use the config.h that CMake generates,
but the hand-written src/md/md-win64.h, so HAVE_SDL3_SOUND was never
defined. All platforms now use config.h. src/md/md-windows.h is
reduced to what CMake cannot know about Windows or finds although it
does not work for xpce on Windows, such as
select() and mkstemp().
- FIXED: Windows: the drop shadow of popups was black SDL's transparent
windows show black where they should be transparent, both on Windows
11 and on Wine. Popups on Windows are now opaque and drawn without
a shadow, as on X11 without a compositor. Windows 11 rounds their
corners.
- ENHANCED: Epilog: a pane whose shell waits for a command closes
without asking An interactive shell is the foreground process group
of its terminal while it waits for input, so closing the pane asked
for confirmation because "a program is running". Epilog now remembers
the pid of the shell it starts and only asks if another process group,
i.e., a command run by the shell, holds the terminal.
- ENHANCED: demo browser shows the demos in groups with headings The
demos are grouped as Applications and tools, Dialogs and windows,
Graphics, Media and devices and Games and animation, followed by the
contributions. Demos that are not in a group appear under Other demos.
- ADDED: demo Asteroids: a game using keyboard polling, gamepads and
sound Fly a ship through an asteroid field and shoot the asteroids,
which break up into smaller ones. The game polls the keyboard
(keyboard<-key_down) and a gamepad on each tick of a timer, moves
and rotates figures using figure->transform and plays sound effects
that it generates as WAV data. The asteroids are shaded using radial
gradients and glow, and explosions fade out using opacity.
- ADDED: class keyboard; @keyboard_device<-key_down polls whether a
key is held @keyboard_device is now an instance of class keyboard, a
subclass of input_device, like class gamepad. There are no events for
releasing a key, so applications such as games can poll the keyboard,
e.g., get(@keyboard_device, key_down, cursor_left, @on).
- ADDED: demo Sound: record, replay, load and save sound Records from
the microphone while showing the input level and the growing waveform,
plays the sound back, loads sound files (MP3, Ogg, FLAC, ... if xpce
has SDL3_sound) and saves the sound as WAV.
- ENHANCED: class sound loads MP3, Ogg Vorbis, FLAC, ... using SDL3_sound
If SDL3_sound is available, sound->load decodes WAV, MP3, Ogg Vorbis,
FLAC, AIFF, AU, VOC and module files. @pce then has the feature
sound_decoders. display<-sound_file_extensions lists the extensions
that can be loaded. Added sound<-peaks to draw a waveform.
- ENHANCED: camera decodes Motion JPEG frames; camera->open accepts a
size Most webcams deliver Motion JPEG frames without Huffman tables,
which SDL cannot convert. The frames are now decoded using SDL_image.
camera->
open(Size) uses the supported frame size that is closest, with
the highest frame rate, and camera<-sizes lists the supported sizes.
The Camera demo lets the user choose the size.
- ADDED: demo Camera: show the video of a camera and take snapshots The
demo shows the video of the camera selected in a menu, the status of
the camera, the frame size and the frame rate. Snapshot copies the
current frame, which can be saved as PNG or copied to the clipboard.
- DOC: User Guide chapter "Input devices and media" Describes
event<-device and event<-detail, sub-pixel positions and smooth
scrolling, pens, touch screens and pinch gestures, gamepads, sound,
cameras, images on the clipboard, the system tray, power and locale
and how to test input without the devices. The examples are in
examples/devices.pl. Other chapters refer to it.
- ADDED: class tray_icon shows an icon with a menu in the system tray
tray_icon(Image, Tooltip, Popup) shows Image in the system tray
(notification area). Its menu is built from the popup: selecting an
entry executes the popup with the tray_icon as context. Sub-popups
become submenus. On Linux this requires libayatana-appindicator.
- ADDED: display<-power_state, <-battery_percent,
<-battery_seconds and <-preferred_locales Co-Authored-By:
Claude Opus 5.5 <noreply@anthropic.com> Claude-Session:
https://claude.ai/code/session_01R86ZKx3ZMkfQcXM87545Tt
- ADDED: copy and paste images and graphicals using the clipboard
display->copy and display->selection accept an image (offered as
PNG) or a graphical (offered as SVG and PNG). display<-selection
with target
image returns an image from PNG, JPEG or another
image format on the clipboard and with target svg the SVG text.
display<-selection_targets lists the MIME types on the clipboard.
- ADDED: class camera delivers video from a camera as an image
camera->open starts the camera. Each frame is copied into the same
camera<-image, so a bitmap showing this image shows the video, and
camera<-message is executed. <-status tells whether the user gave
permission (waiting, open, denied); <-status_message reports changes.
display<-cameras lists the available cameras.
- ADDED: class sound to play WAV files and class audio_recorder
sound(File) loads a WAV file. ->play plays it, possibly several
times at once, ->stop stops it and <-message is executed when a play
is finished. ->save writes a WAV file. An audio_recorder records
from the default input (->record, ->stop) into its <-sound and executes
its <-level_message with the input level while recording.
- ADDED: gamepads: events, hot-plug, polling, rumble and LED
display<-gamepads enables gamepads and returns the connected ones.
Their buttons and axes generate gamepad_button_down, gamepad_button_up
and gamepad_axis events for the window with the keyboard focus,
whose <-detail is a gamepad_input (event<-control, event<-value).
Controls are named by position (south, east, dpad_up, left_x, ...).
A gamepad can be polled (<-button, <-axis) and may ->rumble and show a
colour (->led). When a device is added or removed, the keyboard-focus
window of each frame receives a device_added or device_removed event.
- ADDED: pen, touch screen and trackpad pinch input A pen and the
first finger on a touch screen act as the mouse, so mouse-only
applications work. Their events carry the device and its data:
event<-pressure, <-xtilt, <-eraser, ... (class pen_state) and
event<-finger (class touch_point). Other fingers generate touch_*
events. A trackpad zoom gesture generates pinch_begin, pinch_update
and pinch_end with event<-scale;
library(pan_zoom) zooms on a pinch.
frame->sdl_test_event allows testing this input without the devices.
- ADDED: precise and smooth scrolling for trackpads Wheel events
report the precise scroll distance of trackpads and high resolution
wheels as event<-dx and <-dy (class wheel_data). In a window with
window<->smooth_scroll @on, every scroll step generates a wheel event
whose <-rotation may be a fraction of a notch. The built-in handlers
add up the fractions: a window scrolls by whole pixels, an editor or
list_browser by whole lines.
- ENHANCED: each input device has its own event state The defaults
for a new event, the multiclick detection, the implicit grab from a
button-down to its up and the window holding the pointer are kept
per input_device. Interleaved events from the mouse and a pen or
touch screen thus no longer disturb each other. Keyboard events use
the state of the last pointer device, as before.
- MODIFIED: drop events answer <-path and <-text instead of attributes
The detail of a drop_* event is a drop_data, and class event delegates
to its <-detail. So the dropped file and text are now available as
event<-path and event<-text, and event<-source gives the application
that sent the drop. Use
get(Ev, path, Path) instead of get(Ev,
attribute, path, Path).
- ADDED: event<-device and event<-detail, classes input_device and
event_detail Each event refers to the input_device that generated
it: @mouse_device, @keyboard_device or, later, a pen, touch
screen or gamepad. display<-input_devices lists the known devices.
event<-detail will hold device specific data such as pen pressure; it
is @nil for now. event->initialise accepts the device as an optional
7th argument. If omitted, keyboard events use @keyboard_device and
other events use the device of the last pointer event.
- ADDED: window<->subpixel_events: events keep the fractional position
Events now store the position the window system reports, including
the fraction. The position getters (event<-x, <-y, <-position, ...)
return it for a window with subpixel_events @on. Otherwise they
return whole pixels, rounded down, as before. This prepares for pen
and touch input.
- FIXED: terminal: an erase to the end of the line that starts beyond
the text of the line used the default background Whiptail dialogs
(e.g., raspi-config) draw the shadow of their window this way, so
the shadow was missing. Cells that are added to a line to reach the
caret now also keep the colour of an earlier erase.
- TEST: test cleanup only removes the search paths the test added
retractall/1 on file_search_path/2 also removed the clauses
with a variable head, destroying the library search path. Erase the
asserted clauses by reference. The tracer configuration sandbox
moved to xpce_test_sandbox.pl and is removed after the tests.
[Oct 17 2026]
- ENHANCED: font alias label for the labels of the user interface The
label_font of dialog items, dialog groups, list browsers, editors,
window decorations and the display defaults to the new alias
label
rather than bold. It is bold, so nothing changes by default, but
labels can now use a normal font without affecting bold text, e.g.,
using font.system_fonts: [label := font(sans, normal, 12)].
- ENHANCED: menu items and dialog labels can be markup The label of a
menu_item, dialog item, dialog group or tab may be a markup object,
which is displayed with its fonts, colours, etc. The mnemonic is
chosen from the text without the markup and underlined. A cycle menu
that is too narrow ellipsizes the markup.
- ENHANCED: font_item offers the installed Pango families The family
menu lists the generic families sans, serif and mono, followed by the
installed families, shown in their own font. Items for a mono_font
and the monospaced font aliases only offer the monospaced families.
The list that opens from the family menu shows 15 lines.
- FIXED: the list of a cycle menu did not show the selection The list
was scrolled before its frame had its final size, so it always opened
at the top.
- ENHANCED: menu->max_value_width ellipsizes the value of a cycle menu
A cycle menu with many long values, such as font families, no longer
needs to be as wide as its longest value. A selection that does not
fit ends in an ellipsis and menu<-ellipsized_label provides the full
label, which
library(help_message) shows as tooltip. The list that
opens from the menu is as wide as its labels.
- FIXED: class variable help window could not be resized The help window
is ->transient_for the editor, which makes it a frame of kind transient
that cannot be resized.
- MODIFIED: font alias fixed is now an old name for tt
fixed and tt
were two aliases for the same font. fixed is no longer an alias
of its own: converting it to a font yields the theme font tt, and
fixed := Font in font.system_fonts defines tt. As a result,
@font_aliases no longer has an entry fixed. All code now uses tt.
- FIXED: class variable editor: help window failed without the IDE
The help window uses class tool_bar, which was only available if
library(toolbar) was already loaded, e.g., by the IDE.
- ENHANCED: preferences editor: edit the font aliases in the Text
tab The Text tab of the GUI preferences editor shows a family,
style and size per font alias, followed by a sample text in the
font and a tooltip that describes the role of the alias. If "Same
size for related fonts" is on, changing the size of an alias also
changes the aliases that only differ by style. Class font_item has
a Theme⦠button to select a theme font. Documented font.scale,
font.system_fonts and font.pango_families for users and added the
manual page of class theme_font.
- FIXED: tab labels were not laid out again after fonts_changed If
a font changed in place, e.g., after changing font.scale or a font
alias, the tab labels kept their old size and overlapped.
- ENHANCED: font aliases are theme fonts that can be changed in place
Font aliases such as
normal and bold are now instances of the
new class theme_font, a font whose properties are copied from the
font it is derived from. Changing that font using ->derived_from
or by changing font.system_fonts changes the theme font in place,
so all users follow after display_manager ->fonts_changed.
- TEST: key_binding_browser: expect macOS key symbols on macOS
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- FIXED: thread monitor failed to open if the font descent is fractional
On Windows, font<-descent is a float, making the icon size a float
and between/3 in thread_browser->icon_styles raise a type error.
[Oct 16 2026]
- BUILD: avoid clang -Wabsolute-value warning in failing isearch report
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- FIXED: profiler: call graph and browser issues reported on Discourse
Sorted on the time of the children, the browser showed ticks. In the
call graph, recursive calls are summed over all cycles and shown for
every node, edges into a cycle no longer show inflated times but the
number of calls, tooltips show the fail count if ports were recorded
and the graph is redrawn when its preferences change.
- ENHANCED: thread monitor, theme colour chooser and symbol picker
follow the theme; icons of list browsers use the icon gap The CPU
gauges of the thread monitor use the theme colours thread_cpu_idle and
thread_cpu_busy and are drawn again if the theme or the font changes.
All thread status icons have the size of the gauge, so the names
line up. The swatches of the theme colour chooser follow a change
of theme. The code ranges of the symbol picker use the theme colours
symbol_picker_user and symbol_picker_prolog.
- ENHANCED: markup in list_browser items and the labels of list_browser,
editor and decorated windows A dict_item whose label is a markup shows
its text using the fonts, colours and underlining of the markup on
top of the item's style; selection and search feedback stay on top.
Searching and sorting a dict ignore the markup. The labels of
list_browser, editor and window_decorator are displayed by a
markup_text, so they accept a markup:
- TEST: terminal rows: use the whole-pixel cell metrics of the terminal
The terminal truncates the metrics of its font, which are fractional
on Windows.
- TEST: emacs grep: only run grep if it is installed Co-Authored-By:
Claude Opus 5.5 <noreply@anthropic.com>
[Oct 15 2026]
- ENHANCED: key bindings browser: summaries next to the keys, choose
functions from a list of commands, moved to the Settings menu The
list of keys shows the summary of the function, which is the tooltip
of the row. Changing the function of a key shows the commands with
their summaries, a filter and a menu of classes; selecting one shows
its documentation. Only commands are offered: built-in methods
in a group of commands, such as
caret or search, and methods
defined in Prolog that are bound to a key or refine such a method.
The browser moved from the Help to the Settings menu of the IDE.
- ENHANCED: Pango markup: <tt> shows code in xpce's fixed-width font
on a faint background <tt> and font_family use xpce's font families,
so <tt> uses the same font as fixed-width text elsewhere in xpce.
The background is the new theme colour ui_code_background.
- FIXED: markup in a clipped label read past the end of its text
A clipped label draws a one-line copy of its text that is not
0-terminated, while the markup code assumed it was. This showed as
a failing isearch in PceEmacs that printed the raw markup and a Pango
warning about invalid UTF-8.
- ENHANCED: a failing incremental search shows which part of the target
was found The warning shows the part of the search string that was
found on the background of the isearch hit,
ui_isearch_background,
and the part that cannot be found on the new theme colour
ui_isearch_failed_background.
- FIXED: xpce format: incomplete and unknown %-sequences and
*
widths An incomplete or unknown conversion is copied verbatim.
A `%` at the end of the format was written as \, a format ending
in a flag or width, such as "%-" or "%5", read past the end of the
format and wrote a NUL character, and an unknown conversion such as
"%-5z" lost its flags and width. A * width, as in "%*d" and "%-*s",
consumed its argument but printed the directive literally.
- ENHANCED: reporter shows errors and warnings in the theme
colours ui_error and ui_warning Co-Authored-By: Claude Opus 5.5
<noreply@anthropic.com>
- ADDED: class markup, a string holding Pango markup; formatting it
escapes the arguments, and a label renders it, also for ->report
messages
send(Window, report, warning,
markup("Cannot open <b>%s</b>", File))
- MODIFIED: keyboard control of the panes and tabs of IDE windows;
panes agree to be closed using ->can_close. Control-Page-Down/Up
show the next or previous tab, F6, Shift-F6 and Control-X o move
between the panes of a tab, Control-X 1 closes the other panes of
the tab and Control-X 0 the current one. These work from any pane,
including tools. Control-X 1 moves panes that do not agree to be
closed, such as a terminal running a query or the console showing
the messages of Prolog, to a new tab. `<-can_close: bool` is now
`->
can_close(Ask:[bool])`.
- FIXED: a window of the IDE that was not open kept its dropped status
bar in its layout. Laying out the window then sent ->geometry to
the freed bar, e.g., when an editor was moved to the tab beside it.
frame->delete keeps the tile of a window if the frame is not created,
as tab_frame relies on that.
- FIXED: a key after a prefix that no table binds ran the default
function. For example, Control-X 1 in Epilog inserted "1". Such a
sequence is now reported as undefined.
- ENHANCED: key bindings browser for PceEmacs (Ctrl-H b) and any window
of the IDE (Help/Key bindings). The browser is a tool pane that
lists the keys per table, with a filter that works as you type and
the manual entry of the method a key invokes. Keys can be bound to
another function or key, unbound, and added to a table by selecting
its header. Changes apply at once; Save writes them to the Defaults
file as `key_binding.<table>: [Key := Function, ...]`, one binding
per line. Functions are completed from the commands of the target,
which PceEmacs modes declare with
Name = command; M-x completes
the same commands.
- FIXED: Preferences editor saved values with a backslash so they did
not read back Prolog writes the atom \C-x as '\\C-x', which xpce reads
with two backslashes: xpce reads quoted text as is. Such atoms are
now written as xpce reads them.
- FIXED: goal stack of xpce warnings lost the arguments of Prolog methods
The arguments of a goal handled by Prolog were written to the current
output instead of user_output, where xpce writes the rest of the line.
In swipl-win they were missing from the warning and appeared on the
terminal when the program halted.
- CLEANUP: Delete PceEmacs C++ support for defining xpce classes in C++.
[Oct 14 2026]
- TEST: terminal: the first attribute shot painted on the input
background out/2 writes to the terminal directly, bypassing the line
editor, whose input decoration is still on at the prompt. Start each
shot with SGR 0.
- FIXED: Epilog windows were one row short of the rows asked for Since
the terminal leaves room below the last row for the caret, a window
sized as rows times the font height fits one row less, so the default
Epilog window had 24 rows rather than 25. Added terminal_image
<-cells_size, the inverse of how the terminal derives its rows and
columns from its size, and size Epilog windows with it.
- FIXED: Epilog on Windows: libedit assumed 80 columns until the window
was resized Discourse 9090/232: editing a wrapped input line in a fresh
window climbed up the screen, deleting characters from the rows above.
- FIXED: terminal: italic fonts ignored after changing the font size
If the user changed terminal_image.font (and bold_font), the italic
variants still named the default size and were dropped with a "pitch
differs" warning. A variant that does not fit is now derived from
<-font.
- FIXED: frame <-open_file and <-save_file: Cancel returned '' SDL 3.2's
zenity file dialog reports a cancel as a single empty file name.
Treat that as a cancel. Reported for File/Edit in swipl-win on
Debian Trixie.
- ENHANCED: PceEmacs hit list: filter as you type The bookmark pane used
for grep, git grep and find-references has a filter field. It shows
only the hits whose line or file name matches the (case insensitive)
regular expression and highlights what the filter matches using the
new theme colour
emacs_bookmark_filter.
- FIXED: terminal: the caret on the last row could be truncated The
rows of the terminal now leave room below the last row for the part
of the caret that reaches below its cell, such as the triangle of
the default xpce caret style.
- ENHANCED: PceEmacs grep and git grep show their hits in a hit list
The hits appear in the same reusable pane as find-references instead
of a compilation buffer. The commands run with --color=always, so
the colour tells where the match is, which is highlighted in the hit
list and the editor. Output that is not a hit is reported when the
command is done.
- TEST: check the reference manual card signatures against the classes
xpce:refman_signatures compares the signature line of each card
in man/refmanual/md with the live behaviour. Updated the cards it
found stale, removed cards for figure->shadow, figure<-shadow and
figure->convert_old_slot, and fixed the argument declarations of
frame->y (named x) and pen->initialise.
- ENHANCED: regex<->newline_sensitive; PceEmacs search/replace do
not match newlines with . or [^...] Class regex compiles with
REG_NLANCH only, so `.
and [^...]` match a newline and .* in
PceEmacs query-replace-regex ran to the end of the buffer. The new
option adds REG_NLSTOP. It defaults to @off, as library patterns
rely on spanning lines. PceEmacs query-replace-regex and the find
dialog switch it on for the pattern typed by the user.
[Oct 13 2026]
- ENHANCED: manual browsers adapt the name column to the window width
The name column of the class, module, group and search browsers is
as wide as the widest name, but at most half the window, and follows
the window when it is resized. Longer names are truncated with an
ellipsis and show the full name as tooltip.
- ADDED: @pce has the feature svg if ->svg can write SVG images Cairo
may be built without the SVG surface, as by MacPorts. Test using
send(@pce, has_feature, svg). The demo galleries only offer to save
as SVG if it is supported.
- FIXED: graphical->graphics_state used a freed stroke_style A
stroke_style created for the texture argument is not referenced, so
it was freed when the send returned while the drawing context still
used it. ->restore_graphics_state then crashed on macOS.
[Oct 12 2026]
- ADDED: graphics demos and the XPCE userguide chapter "Paths, paint
and text" Demos Path gallery, Paint gallery, Text gallery and Canvas
show paths, gradients, line styles, markup text, clipping, blending
and the canvas.
- ADDED: class canvas for Cairo-style vector drawing; graphical->svg
and window->svg A canvas is drawn on using paths, stroke, fill,
clip, transformations, text and images, recorded as vectors.
graphical->draw_path draws a path object from a '_redraw_area' method.
graphical->svg and window->svg write the graphical or window contents
as an SVG image.
- ENHANCED: clipping regions nest; added figure->clip_path and
figure->blend ->clip intersects with the enclosing region and ->unclip
restores it. figure->clip_path clips the contents of a figure to
a path and figure->blend composites a figure onto what is below it
using a blend mode such as multiply, screen or difference.
- FIXED:
colour('#RRGGBB') created through new/2 or as a term argument
was not parsed as hexadecimal Only conversion of a name such as
'#RRGGBB' parsed the hexadecimal notation; creating the colour
explicitly, e.g. send(Box, fill, colour('#ff8000')), produced an
unknown named colour. The parsing is now shared by both.
- FIXED: a colour with an unknown name was black instead of the grey50
the warning announces The fallback stored the RGBA value untagged.
- FIXED: a rounded box with an elevation lifted the clip of its window,
painting outside the area being redrawn
r_3d_box() reset the clip after
drawing each half of the box rather than restoring the enclosing one.
- ADDED: class markup_text displays text formatted using Pango markup,
with Pango wrapping, alignment, justification, ellipsizing and line
spacing; graphical->draw_markup draws markup from a '_redraw_area'
method Markup can mix fonts, sizes, styles and colours, e.g.
"Some <b>bold</b> and <span foreground='red'>red</span> text".
Unlike class text, a markup_text is read-only. It uses a layout
of its own from the same Pango context as the fonts, so its metrics
match those of plain text.
- ADDED: line styles, image fills and gradient extend and units Class
stroke_style provides line caps, joins, miter limit, arbitrary dash
patterns and gradient lines, usable as graphical ->texture. Graphicals
can be filled with a repeated image. gradient<-extend selects what
is painted beyond the stops and gradient<-units: bounding_box makes
a gradient relative to the shape it fills.
- FIXED: the arrows of a path were never drawn The arrow heads of a
path were positioned but not marked as displayed, so RedrawArea()
skipped them. Line, arc and bezier_curve already did this.
- ADDED: class path supports Bezier curves, elliptical arcs, sub-paths, a
fill rule, rounded corners and SVG path data Curves, arcs and sub-paths
use the new point subclasses curve_point, arc_point and move_point.
path->svg_data and path<-svg_data set and get the points as SVG
path data.
- FIXED: graphical->draw_poly filled after stroking, painting the
fill over the inner half of the outline Also removes a transparent
cairo_paint() from r_fill_polygon() that had no effect.
- FIXED:
r_pixel() and r_get_pixel() accepted x == width and y == height
The callers in image.c check bounds with inImage(), so this could not
be reached from Prolog; there is therefore no regression test. The
bounds check and address computation are now shared in target_pixel().
- FIXED: ->draw_fill drew nothing unless the fill was @default, and
r_fill() rejected gradients graphical->draw_fill now fills with any
colour or gradient, and a figure with a gradient background draws it.
- FIXED: gradient colour stops were scaled by 1/256, so no stop reached
its full colour A red stop rendered as ffff0101 instead of ffff0000.
Adds tests/test_cairo_render.pl, which checks drawn pixels via
image->draw_in.
[Oct 13 2026]
- FIXED: graphical ->graphics_state used a freed colour A colour
converted from a name is not referenced, so it was freed when the
send returned while the drawing context still used it. Depending on
the reuse of the memory, lines were drawn in a random colour or not
at all. This made the graphics_state test fail on Windows.
- FIXED: a process was reported to exit with status 0 at the end of its
output End of file on the output of a process was taken as ->exited 0,
losing the real exit status or signal and leaving the child unreaped.
On Unix the termination is now only reported when the child is reaped,
after handling the output it wrote. process->wait also waits for
the process to terminate, such that <-status and <-code are known.
On Windows the end of the output still means normal termination.
- FIXED: crashes and hangs after a process (e.g., PceEmacs grep)
terminated The SIGCHLD handler reaped the children and sent ->exited
or ->killed, running the terminate message (Prolog, frame labels, the
window system) inside the signal handler, interrupting whatever the
main thread was doing. This crashed in GTK (libdecor on Wayland),
corrupted the Prolog VM or crashed on halt. The handler now only
writes to a pipe that is watched by the fd watcher thread, so the
children are reaped from the main thread's event loop.
- FIXED: X11 with SDL 3.2: clicking a menu_bar did not keep the popup
open With SDL 3.2 under a window manager such as xfwm4, the up-event of
the click that opens a popup is reported for the popup window as that
takes the focus. It is redirected to the window of the down-event, but
the translation used SDL_GetWindowPosition(), which is relative to the
parent for a popup. The menu_bar saw the up far from the down, so it
did not keep the popup open and the popup closed without a selection.
Compute screen positions through the parents of popup windows.
- FIXED: possible crash on pressing Alt when a freed popup frame is
still listed by the display
acceleratorCuesFrame() walks the popup
frames of the display to update their accelerator underlines. A freed
frame keeps its <-kind (names are not reset) while its <-members is
@nil, which crashed. Skip freed frames there and in wsid_to_frame().
Reported on Debian/Xfce with 10.1.18.
- TEST: theme:colour_trace failed on systems without a GNOME or KDE
desktop There, sys_window_background uses xpce's default, so the
trace source is
default rather than desktop.
[Oct 12 2026]
- FIXED: various issues found while updating the userguide - Interface:
new(X, 3.5) crashed on an assertion. A float is not an
object and now raises a type error, as for integers. - Interface:
prolog(Float) did not pass the float. - Interface: calling Prolog
from xpce while a Prolog exception is pending,
e.g., reporting the type error after an unknown @Ref, printed
"did not clear exception" and lost the exception. The pending
exception is now saved and restored around the call. - Arithmetic
objects truncated float arguments: 3+2.5 evaluated to 5. - Class
compiler: unquoted range types such as Vol:[0..130] did not
expand. - A frame larger than its display, e.g., from view
->size in
characters, could crash the graphics driver. Frames are now limited
to the size of their display. - program_object <-dflags is an
alien slot. It held raw flag bits in
an int slot, which showed as a garbage float. - bezier_curve term
names were those of box. - <-man_summary warned about ->has_help if
the manual is not loaded. - toc_tree ->selection selected a graphical
used as node identifier
rather than the node. - Visual hierarchy: show anonymous object
arguments as <class> rather
than their long and varying reference.
- DOC: XPCE userguide: recreate the screenshots Added
screenshots/screenshots.pl to create the screenshots in figs from the
code in the guide, using `make screenshots`. screenshots/shoot.sh
runs it against a private headless mutter display at scale 2, or
Xvfb if mutter is not available. A logical pixel is printed as 0.6
point, such that the text has about the size of the running text.
Recreated all screenshots of the guide and removed the explicit widths
that scaled them.
- DOC: XPCE userguide: update the content for version 9 Revised all
chapters for the SDL/Cairo/Pango based version. Removed material
on features and systems that no longer exist (X11 resources,
PostScript, ProWindows, other Prolog systems, old Windows versions,
removed classes and methods), described themes, the flat dialog look,
keyboard operation, OS drag and drop, image formats and the IDE tools
as panes, and fixed code examples that no longer work. The class
summary has entries for new classes such as bool_item, theme_colour,
gradient and shadow.
- DOC: XPCE userguide: automatic version, title page and build fixes -
The Makefile creates version.tex with \productversion from PCE_VERSION
in CMakeLists.txt and \productdate and \productyear from the date.
- Updated the title page and the overview of changes since version 6.
- Class summaries listed all classes as
name(). The header is now
created from the class <-term_names. - Removed the httpd section. -
Added a section on printing and exporting PDF, replacing the PostScript
section that was removed before. - doc2tex: did not terminate if
the input ends with a code block and
did not translate <CamelCase> and <x> meta names and ->_name.
- FIXED: graphical ->restore_graphics_state raised "Unknown texture"
g_save() no longer recorded the pen, texture and colours because
graphstate.c was included in sdldraw.c before the drawing context
was declared. The initial dash of a context is now none rather than
NULL, so it can be restored. Also fixed g_level(), which tested the
inverse condition.
Package yaml
[Oct 18 2026]
- TEST: removed search path setup for the library and foreign aliases
src/swipl in the build tree already finds the libraries and foreign
modules of all packages.
Package zlib
[Oct 18 2026]
- TEST: removed search path setup for the library and foreign aliases
src/swipl in the build tree already finds the libraries and foreign
modules of all packages.