Window Systems and Windows
Opening a window system
(win:open-window-system &rest options &key backend &allow-other-keys)
=> window-system
open-window-system connects to the platform's window system and returns a
window-system. Close it with close-window-system, or use the macro:
(win:with-window-system (window-system :backend :wayland)
...)
with-window-system closes the window system however its body is left,
unless it was closed already.
Choosing a backend
With :backend, the window system uses that backend: a keyword such as
:wayland or :win32. Without it, the window system uses the platform's
backend: :win32 on Windows and :wayland elsewhere.
The backend's system must be loaded, or open-window-system signals
no-backend-error, which names the system to load and the backends that
are loaded:
The window system backend :WAYLAND is not loaded. Load the system
cl-windowing/wayland first. Loaded backends: none.
Backend options
The other options go to the backend. A backend ignores options it does
not know. The Wayland backend takes:
| Option | Meaning |
|---|---|
:display-name |
The compositor's socket, such as "wayland-1". By default $WAYLAND_DISPLAY. |
:connection |
A cl-wl:display you connected already. The window system uses it and does not disconnect it when it closes. |
See Wayland Backend. The Win32 backend takes no options; see Win32 Backend.
Failure
If the window system cannot be reached, open-window-system signals
window-system-error, and its message says what to check:
Cannot connect to the Wayland compositor "no-such-socket": Could not
connect to the Wayland display "no-such-socket". Is a compositor running,
and is $WAYLAND_DISPLAY or :DISPLAY-NAME right?
What a window system tells you
| Function | Returns |
|---|---|
(window-system-backend ws) |
The backend's keyword, such as :wayland. |
(window-system-open-p ws) |
True until close-window-system. |
(window-system-windows ws) |
A fresh list of its windows that are alive, popups included. |
(window-system-capabilities ws) |
What it can do beyond the basics; see below. |
Capabilities
window-system-capabilities returns a list of keywords:
| Capability | Meaning |
|---|---|
:server-side-decorations |
The window system can draw title bars and borders for windows. |
:fractional-scale |
Windows may have scales between whole numbers, such as 3/2. |
:clipboard |
clipboard-text can read and set the clipboard. See Clipboard. |
:pointer-capture |
window-pointer-mode can confine and capture the pointer. See Cursors and Pointer Modes. |
:keyboard |
A keyboard is attached now. |
:pointer |
A pointer is attached now. |
Ask instead of assuming: capabilities differ between backends, between
compositors and between machines. :keyboard and :pointer come and go
as devices do, and they appear only after process-events has read the
compositor's announcement of them. Right after open-window-system they
are usually missing. A headless compositor may have neither.
Creating a window
(win:create-window window-system &key (title "") (width 800) (height 600) app-id parent modal)
=> window
create-window creates a top-level window and asks for a size of width
by height units. With parent, the window is a dialog of that window,
and with modal too, a modal one: see Dialogs. For popups, see
Popups. Destroy it with
destroy-window, or use the macro:
(win:with-window (window window-system :title "Editor" :width 1280 :height 720
:app-id "org.example.Editor")
...)
app-id names the application to the window system, which uses it to
group the application's windows and find its icon and .desktop file. Use
a reverse domain name, such as "org.example.Editor". Windows does not use
it: it groups the windows of a process itself.
Before the window is drawn
On Wayland, a new window is not on screen yet. Two things happen first:
- The window system proposes a first size.
window-configured-pbecomes true, and a:resizeevent reports the size. This first size is taken at once, with no handshake. - Something is drawn into the window. Only then does it appear.
On Windows, the first size is proposed before create-window returns, so
window-configured-p is true at once, and the window is on screen before
anything is drawn.
Nothing may be drawn before the first size: on Wayland, a frame presented
before it is a protocol error, and the compositor disconnects the program.
So until then window-pixel-size is 0 by 0, and a renderer sized by it has
nothing to draw into. cl-gfx surfaces return nil from
acquire-surface-texture meanwhile, as for a minimised window, so a
cl-gfx program needs to do nothing.
If you present with Vulkan directly, present nothing before
window-configured-p is true. To wait for it, bound the wait:
(loop repeat 100
until (win:window-configured-p window)
do (win:process-events window-system :timeout 0.05))
(unless (win:window-configured-p window)
(error "The window system did not configure the window within 5 seconds."))
The first size may differ from the size asked for. A tiling compositor, for example, chooses sizes itself.
What a window tells you
| Function | Returns |
|---|---|
(window-window-system window) |
The window system it belongs to. |
(window-alive-p window) |
True until destroy-window. |
(window-title window) |
Its title. setf it to change the title. |
(window-configured-p window) |
True once the window system has proposed its first size. |
(window-size window) |
Its width and height in units, as last adopted, or as asked for until configured. |
(window-scale window) |
Its scale, as last adopted. |
(window-pixel-size window) |
The width and height of its frames in pixels, or 0 and 0 until configured. |
(window-pending-size window) |
The width, height and scale proposed and not yet adopted, or nil, nil, nil. |
(window-pending-pixel-size window) |
The same in pixels, or nil, nil. |
(window-decorations window) |
:server-side or :none; see below. |
(window-fullscreen-p window) |
True while it is fullscreen; see below. |
(window-cursor window) |
The pointer's look over it, :default at first. See Cursors and Pointer Modes. |
(window-pointer-mode window) |
How the pointer is held over it, :free at first. |
(window-popups window) |
Its popups that are alive, oldest first. |
(window-dialogs window) |
Its dialogs that are alive, oldest first. |
(window-parent window) |
The window it was made on, for a dialog or a popup; nil for a window of its own. |
(window-modal-p window) |
True for a modal dialog. |
Size and scale are covered on Resizing and Scaling.
Titles
(setf (win:window-title window) "Untitled - Editor")
The title is sent to the window system at once.
Decorations
Title bars and borders are decorations. window-decorations says who
draws them:
:server-side: the window system does.:none: nobody does yet. The engine will draw its own.
On Wayland, cl-windowing asks for server-side decorations through
xdg-decoration wherever the compositor offers it, as KDE Plasma and Sway
do. Some compositors, such as GNOME's Mutter and Weston, do not offer it,
and windows are undecorated there. A window's decorations start as :none
and change to :server-side once the compositor agrees, which happens
during process-events. On Windows, windows have the system's title bars
and borders: :server-side. Popups have none.
Fullscreen
(setf (win:window-fullscreen-p window) t) ; on F11, say
(setf (win:window-fullscreen-p window) nil) ; and back
A fullscreen window covers the display it is on, without decorations, and
the window system sizes it: its size comes as a :resize event, as any
other, and set-window-size asks nothing while it lasts. The window system
decides: window-fullscreen-p says what it did, once it has. Back from
fullscreen, the window is as it was. A popup is never fullscreen: setting
it signals popup-error.
- On Wayland, the compositor makes the window fullscreen on the output it
chooses, and says so in a configure, so
window-fullscreen-pchanges as events are processed. - On Windows, a fullscreen window is a window without a frame covering its display, at once. Leaving fullscreen puts it back where it was, maximised if it was.
Destroying
(win:destroy-window window)
(win:close-window-system window-system)
Destroy what draws into a window first, such as its cl-gfx surface or
Vulkan surface. destroy-window destroys a window's popups and dialogs
first, the newest first. close-window-system destroys the windows still alive,
then disconnects. Both succeed even when the connection to the window
system has failed.
Events about a window may still be queued after it is destroyed.
event-window then returns a window whose window-alive-p is false. A
window that is used after it is destroyed signals closed-error, and so
does a window system after it is closed. See Errors.