10.7 Multithreading and the XPCE graphics system
GUI applications written in XPCE can benefit from Prolog threads. Where XPCE 6 and earlier allowed only one Prolog thread to interact with XPCE, XPCE 7 and later allow multiple threads, but only one may be processing inside XPCE at any point in time. In other words, sending a message to XPCE requires obtaining the XPCE global lock, but if the method calls out back to Prolog, this lock is released and other Prolog threads may send messages to XPCE. This model is comparable to the Python GIL.
Due to the underlying SDL library, window and display related operations must be executed on the process main thread and GUI events originate from the (SDL) main thread. Other Prolog threads may execute goals in the SDL event loop and (thus) in the main thread using in_pce_thread/1 and in_pce_thread_sync/1.
SWI-Prolog/XPCE can operate in two setups. One is the console
application swipl that connects its I/O to a terminal (or Windows
console). In this setup, XPCE event processing is interleaved with
reading from the terminal. This implies that the GUI is not responsive
while a Prolog goal is running. Alternatively,
swipl-win is a GUI application that runs the Prolog REPL
(toplevel) loop in a Prolog thread connected to a terminal implemented
in XPCE (called Epilog). Now, the GUI runs in the process main
thread and interactive Prolog queries run in the thread connected to the
Epilog terminal, called conN. In this setup the
GUI remains responsive while user queries execute. The Epilog terminal:
- Fully implements
xterm-256colorescape sequences. - Is fully Unicode aware, including high (>
0xffff) Unicode code points, Unicode (color) Emoji, double-width characters and Unicode combining marks. It also provides a Unicode symbol picker through the background menu. - Provides a menu for common Prolog actions. This is responsive while a user query is running.
- Allows multiple Epilog sessions to be opened, each running their own
conN named thread. Epilog windows can be started using epilog/1, by splitting an existing one horizontally or vertically or by creating a new one through the menu. This allows for checking status and progress of other threads (see tbacktrace/1 and listing/2) as well as running and debugging several goals simultaneously. - Allows using shell/0 or shell/1 to run other programs that interact with the terminal, providing all features one would expect from a terminal.
- Renders (error) messages that are associated with a file location using OSC 8 terminal embedded URLs. Control-click opens the built-in editor (edit/1) or an editor of the user's choice. Clickable links are also supported for help/1 output.
- Moves the caret to a click in the line being edited. The terminal knows which line that is because the commandline editor marks its prompts using OSC 133 semantic prompt sequences; a shell or other program started in the window that does the same gets the same service.
- Knows, from the same marks, where each command it ran begins and
ends. A triangle in the left margin beside a command folds and unfolds
its output with a single click, and carries a menu of its own: fold,
copy the command, copy what it printed, copy or select both, type the
command again, or take it out of the buffer altogether. Right-clicking
the text instead gives the menu of the terminal. Control-Shift-Up and
Control-Shift-Down walk the prompts. Setting the
fold_previousclass variable of prolog_terminal closes each command as the next one is entered, leaving a history of commands with only the current output open. - Uses, as does swipl, a modified version of libedit for commandline editing. The same commandline editor is used on all platforms that support XPCE (Windows, macOS, Linux and other POSIX systems).
If SWI-Prolog was started as swipl, it may open an Epilog window by running epilog/0. See also section 4.35.4.
- [det]in_pce_thread(:Goal)
- This call gives any Prolog thread the opportunity to run calls in the XPCE thread. A call to in_pce_thread/1 succeeds immediately, copying Goal to the XPCE thread. Goal is added to the XPCE event queue and executed synchronous to normal user events like typing, clicking and XPCE timers.
- [semidet]in_pce_thread_sync(:Goal)
- Same as in_pce_thread/1,
but wait for Goal to be completed. Success depends on the
success of executing Goal. Variable bindings inside Goal
are visible to the caller, but it should be noted that the values are
being copied. If Goal throws an exception, this
exception is re-thrown by in_pce_thread/1.
If the calling thread is the XPCE thread, in_pce_thread_sync/1
executes a direct meta-call. See also in_pce_thread/1.
Note that in_pce_thread_sync/1 is slow and is typically only required for operations that act on windows or the display. A warning is printed if an SDL operation that may only be called from the main thread is called from another thread. Many common operations that require to be called in the main thread are automatically proxied to the main thread.