Skip to content
📖 14 min🎯Difficulty:
Prerequisites:Core typesWayland protocols

The Wayland Surface Lifecycle ​

Every window in Quickshell is a Wayland surface with a layer-shell role. Understanding the lifecycle helps debug window issues (not appearing, wrong position, not receiving input).

Lifecycle Diagram ​

mermaid
graph TD
    QML[QML Component<br/>PanelWindow / PopupWindow]
    BASE[C++: WindowInterface<br/>abstract base]
    PROXY[C++: ProxyWindowBase<br/>reloadable proxy]
    CS[createSurface<br/>wl_surface]
    GLS[get_layer_surface<br/>requests layer role]
    WC[wl_surface.commit<br/>initial empty commit]
    CFG[configure event received<br/>compositor says OK]
    ACK[ack_configure(serial)<br/>+ attach buffer<br/>+ wl_surface.commit]
    VIS[Window Visible<br/>on screen]

    QML --> PROXY
    PROXY --> BASE
    BASE --> CS
    CS --> GLS
    GLS --> WC
    WC --> CFG
    CFG --> ACK
    ACK --> VIS

PanelWindow in Detail ​

PanelWindow (C++ class: PanelWindowInterface) inherits from WindowInterface and adds anchor-based positioning and layer exclusion zones.

C++ class: src/window/panelinterface.hppConcrete backend: src/window/panelinterface.cpp

Anchors Value Type ​

Anchors are defined as a Q_GADGET value type, not individual Q_PROPERTYs:

cpp
// panelinterface.hpp — Anchors value type
class Anchors {
  Q_GADGET;
  Q_PROPERTY(bool left MEMBER mLeft);
  Q_PROPERTY(bool right MEMBER mRight);
  Q_PROPERTY(bool top MEMBER mTop);
  Q_PROPERTY(bool bottom MEMBER mBottom);
  QML_VALUE_TYPE(panelAnchors);
  QML_STRUCTURED_VALUE;

public:
  [[nodiscard]] Qt::Edge exclusionEdge() const noexcept;
  bool mLeft = false;
  bool mRight = false;
  bool mTop = false;
  bool mBottom = false;
};

When you set QML anchors:

qml
anchors { top: true; left: true; right: true }

The Wayland backend translates these to layer-surface anchor flags in src/window/panelinterface.cpp:

cpp
uint32_t zwlrAnchors = 0;
if (anchors.mTop) zwlrAnchors |= ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
if (anchors.mBottom) zwlrAnchors |= ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
if (anchors.mLeft) zwlrAnchors |= ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT;
if (anchors.mRight) zwlrAnchors |= ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
zwlr_layer_surface_v1_set_anchor(m_layerSurface, zwlrAnchors);

Exclusion Modes ​

PanelWindow provides exclusionMode (an enum) in addition to exclusiveZone:

ModeValueBehavior
Normal0Respect other layers' zones; set one via exclusiveZone
Ignore1Ignore other layers' zones; cannot set one
Auto2Derive zone from window dimensions + anchors (default)
qml
PanelWindow {
  anchors { top: true }
  exclusionMode: ExclusionMode.Normal
  exclusiveZone: 36  // Reserve 36px at top
}

This prevents maximized windows from covering your panel.

PopupWindow (C++ class: ProxyPopupWindow) creates a separate wl_surface that positions relative to a parent window.

C++ class: src/window/popupwindow.hpp

Anchoring Logic ​

qml
PopupWindow {
  anchor.window: toplevel
  anchor.rect.x: parentWindow.width / 2 - width / 2
  anchor.rect.y: parentWindow.height
  width: 500
  height: 500
}

Internally, ProxyPopupWindow tracks the parent window's position via the anchor property (using the PopupAnchor type from src/core/popupanchor.hpp) and updates its own position when the parent moves or resizes.

Dismiss on Click Outside ​

PopupWindows can be set to dismiss when the user clicks outside their surface region by listening to Wayland seat pointer events.

FloatingWindow ​

FloatingWindow (C++ class: ProxyFloatingWindow) creates a standard xdg-shell toplevel surface — a regular desktop window with title bar and decorations (managed by the compositor).

C++ class: src/window/floatingwindow.hpp

It is the simplest window type but provides the least control over positioning.

ProxyWindowBase ​

All window types extend ProxyWindowBase (in src/window/proxywindow.hpp), which provides the reload mechanism. During a soft reload, Quickshell can transfer window state from the old to the new instance. During a hard reload, windows are fully recreated.

Window Layering ​

The layer-shell protocol defines four layers (bottom to top):

LayerZ-orderTypical Use
Background0Wallpaper
Bottom1Dock-like panels
Top2Taskbars, panels
Overlay3Popups, lock screens, OSD

Quickshell's PanelWindow defaults to the Top layer via aboveWindows: true. Set aboveWindows: false to place it in the Bottom layer.

Exercises ​

⭐Exercise
Read `src/window/panelinterface.hpp` and find the `Anchors` value type. Add a `center` anchor mode — when set to true, the panel is centered on screen regardless of individual anchors.
⭐⭐Exercise
Trace the PopupWindow dismiss path in `src/window/popupwindow.hpp` and its backend implementation. Find how `visible = false` is propagated and whether keyboard grabs are released. Add logging to verify the sequence.
⭐⭐⭐Exercise
Implement a custom window type `CornerWindow` that snaps to screen corners (top-left, top-right, bottom-left, bottom-right) with configurable size. Follow the pattern of `PanelWindowInterface` and register it as a new QML type via `QML_NAMED_ELEMENT`.
✅What You've Learned
  • Each window type maps to a Wayland surface with a specific role
  • PanelWindow anchors are a Q_GADGET value type translated to layer-surface flags
  • Exclusion modes (Normal, Ignore, Auto) control screen space reservation
  • ProxyWindowBase provides reload support across all window types

Built with VitePress. Licensed under MIT.