2 Globals
dxtr edited this page 2026-10-11 18:19:54 +02:00

Globals

A compositor offers its services as globals. cl-wl binds them without registry bookkeeping. You name the proxy class you want, and cl-wl finds the global, picks the version and returns the proxy.

Binding one global

(wl:bind display 'wl-compositor)                 ; => #<WL-COMPOSITOR id 4 v6 …>
(wl:bind display 'wl-seat :version 9)            ; at most version 9
(wl:bind display 'zxdg-decoration-manager-v1
         :errorp nil)                            ; NIL if not offered

bind uses the highest version that the compositor, the bindings and :version all allow. proxy-version tells you which version that was.

If the compositor does not offer the interface, bind signals missing-global-error:

The compositor does not offer xdg_toplevel, so XDG-TOPLEVEL cannot be bound.
GLOBAL-VERSION tells whether a global is offered.

Pass :errorp nil for interfaces your program can do without, and check for NIL.

If the compositor offers several globals of the interface, bind binds the first one announced. Use bind-all or follow-globals for interfaces with one global per device, such as wl_output and wl_seat.

Binding all of them

(wl:bind-all display 'wl-output :version 4)   ; => list of WL-OUTPUT proxies

bind-all takes the same :version as bind and returns one proxy per global offered now.

Asking what is offered

(wl:global-version display 'wl-seat)    ; => 7, or NIL if not offered
(wl:list-globals display)
;; => (("wl_compositor" 4) ("wl_subcompositor" 1) ("wp_viewporter" 1) …)
(wl:find-proxy-class "xdg_wm_base")     ; => XDG-WM-BASE, or NIL
(wl:interface-wire-name 'xdg-wm-base)   ; => "xdg_wm_base"
(wl:supported-version 'xdg-toplevel)    ; => 7, the newest the bindings know

list-globals returns (interface version) pairs in the order the compositor announced them. The interface is the protocol name. find-proxy-class maps a protocol name to its proxy class, or to NIL if no loaded bindings define it. With it you can bind every global your loaded bindings know about:

(loop for (name version) in (wl:list-globals display)
      for class = (wl:find-proxy-class name)
      when class
        collect (wl:bind display class))

Globals that come and go

Monitors are plugged in and out, and seats can appear later. When that happens the compositor announces new globals and withdraws old ones. follow-globals binds every global of an interface, now and as they are announced, and gives you each proxy as it arrives. When the compositor withdraws a global, it gives the proxy back to you:

(defvar *follower*
  (wl:follow-globals display 'wl-output
                     :version 4
                     :added (lambda (output)
                              (watch-output output))
                     :removed (lambda (output)
                                (forget-output output)
                                (wl:destroy-proxy output))))
  • :added is called with each proxy. It is called at once, before follow-globals returns, for the globals offered now. For globals announced later, it is called while you dispatch events.
  • :removed is called with the proxy when its global is withdrawn. The proxy is then yours to destroy. destroy-proxy sends wl_output's release request when the proxy's version has it (version 3 and later) and otherwise destroys it on the client side. By default :removed is #'destroy-proxy.
  • stop-following ends it. The proxies it bound stay yours.

follow-globals returns a global-follower. You can have several followers, for the same interface or for different ones.

How it works

The first call to bind, bind-all, follow-globals, global-version or list-globals on a display creates its wl_registry and makes a roundtrip. The roundtrip ensures that the initial set of globals is known. It also delivers any events that were pending at the time, so do not be surprised if handlers you set earlier run during that first call. Later calls reuse the registry and do not block.

The registry's global and global_remove events update the display's list of globals as you dispatch. bind therefore always sees the current set.

The registry is created with wl-display-get-registry like any other object. You can still create registries of your own and handle their :global events yourself. cl-wl's registry belongs to cl-wl: do not set its handlers or destroy it.

Binding by hand

wl-registry-bind is available like every other request. Its new_id has no fixed interface, so it takes the proxy class and version as arguments:

(wl-registry-bind registry name 'wl-compositor 4)   ; => #<WL-COMPOSITOR …>

It signals version-error if the version is newer than the bindings support.