Smithay-based Wayland/XWayland compositor for HackerOS.
state.rs-HwdeState(core struct) +ManagedWindowbookkeeping (minimize/maximize/focus/close-by-id,is_ssddecoration tracking - backing the unified taskbar withstarthwde).handlers/- one file per Wayland protocol:compositor.rs- core compositor/buffer/shm.xdg_shell.rs- xdg-shell, incl. interactive move/resize (grabs.rs) and popup handling.seat.rs,output.rs- seat/output plumbing.data_device.rs- clipboard (wl_data_device_manager) and primary selection ("middle-click paste").decoration.rs- xdg-decoration negotiation; tracksis_ssdper window forrender_elements.rsto draw.layer_shell.rs- wlr-layer-shell protocol +LayerMaparrangement.xwayland_shell.rs(feature = "xwayland") - required byX11Wm.
render_elements.rs- the full per-frame render pipeline: wallpaper + all fourwlr-layer-shelllayers (via Smithay'sspace_render_elements, correctly interleaved in front of/behind the window stack) + windows + minimal server-side decorations, assembled back-to-front and driven through the lower-levelOutputDamageTracker::render_outputdirectly (bypassingspace::render_output's single-group-always-behind limitation - see this file's module doc for why).grabs.rs- interactive move/resize pointer grabs, shared by native Wayland (handlers/xdg_shell.rs), XWayland (xwayland.rs), and SSD grab bars (input.rs).winit_backend.rs- the default, wired-into-mainbackend: nested/windowed.input.rs- forwards winit keyboard/pointer/touch events into the seat; hit-tests SSD grab bars/close buttons before normal client dispatch; triggers popup-dismiss-on-outside-click.wallpaper.rs- loads HWDE's wallpaper and renders it as a full-screen background element every frame; reloadable live via IPC (see below).ipc.rs- thehwde-ipccontrol socket: external app launching, the unified window-list/focus/close/minimize/maximize protocol, live wallpaper changes (a Settings change now reaches an already-running compositor immediately, not just its next restart), shutdown.xwayland.rs(feature = "xwayland", default on) - starts XWayland, maps X11 clients into the same desktop space as native Wayland windows, real interactive move/resize.backend_drm.rs(feature = "drm-experimental", NOT wired intomain) - session/udev-hotplug/libinput wiring is real, ported from Smithay's ownanvilreference compositor. The atomic-KMS device/connector setup and vblank-driven render loop are deliberately left as a precise, sourced outline rather than a mechanical port - see the module's doc comment for why (short version: that specific ~300 lines of anvil code is exactly the kind of GPU-buffer-lifetime-sensitive code that's genuinely unsafe to "port and hope" without a real compile-run-fix loop against actual KMS hardware).
- wlr-layer-shell now actually renders (all four layers, not just Background/Bottom) - previously the protocol worked but nothing was painted.
- Minimal server-side decorations: a solid-color grab bar + close
button for windows that negotiate
Mode::ServerSidevia xdg-decoration - drag to move, click the red square to close, title text rasterized on the bar viatitle_text.rs(see below). - Primary selection ("middle-click paste") alongside the main clipboard.
- Touch input forwarded to clients (down/motion/up/cancel/frame).
- 3/4-finger touchpad swipe → workspace switch (
input.rs'sGestureSwipe*handling) - real hardware only (via the DRM backend's libinput source); nothing to recognize from the winit backend's synthetic events. - Live wallpaper reload: changing the wallpaper in Settings now reaches
an already-running
comphwdevia IPC instead of only taking effect on the compositor's next start. - DRM/udev backend: upgraded from a pure doc-comment plan to a real, substantial session/device-discovery/input scaffold - still not the default, still needs the atomic-KMS core built against real hardware.
- SSD title text:
title_text.rsrasterizes window titles withfontdue(pure-Rust, no fontconfig/freetype linkage) against whichever system font it finds first (see that module'sFONT_CANDIDATES), cached per-window as a GPU texture and truncated with an ellipsis when a window is too narrow for its own title. Degrades gracefully (bar + close button still work, title just isn't drawn) if no font is found. PinSurfacePID fallback: pinned-shell matching (sde-panel/sde-dockdocking to an edge) now also recognizes a window by the pid of the process that registered the pin (read viaSO_PEERCREDon thesde-ipcconnection - seestate.rs'sPinnedSurfaceSpec), not only by Waylandapp_id, so it degrades gracefully if the app_id Slint's winit backend sets doesn't end up being what was expected.zwp_idle_inhibit_manager_v1: clients (video players, browsers playing fullscreen video, conferencing apps) can now register an idle inhibitor instead of hitting a missing global. Seehandlers/extra_protocols.rs's module doc for the honest caveat: this compositor has no idle-timeout/lock/DPMS-blanking system of its own yet to actually act onHwdeState::is_idle_inhibited()- this adds the missing "clients can ask" half, not the "compositor honors it" half.wp_single_pixel_buffer_manager_v1: lets clients (Chromium/CEF- based ones in particular) create solid-color buffers without SHM - Smithay's GLES renderer imports these natively, so this was a two-line, no-handler-needed addition (seehandlers/extra_protocols.rs).zwp_virtual_keyboard_manager_v1: lets a client (on-screen keyboard, IME) synthesize keyboard input through the sameKeyboardHandlereal hardware keyboard input already uses - no forwarding code needed anywhere else, Smithay wires it straight into the seat. Security note: registered with a client filter that currently allows every client (|_| true) - seehandlers/extra_protocols.rs's module doc for why (this project has no trusted/untrusted client concept yet to filter on) and that this is a placeholder, not a considered decision, before this runs anywhere with untrusted Wayland clients.- Real mouse input on the DRM backend, and
zwp_relative_pointer_manager_v1:input.rsnever handledInputEvent::PointerMotion(relative motion - what an ordinary mouse sends, as opposed toPointerMotionAbsolute, which touchpads/touchscreens and the winit backend's synthetic events send) - meaning a plain mouse had no way to move the cursor at all outside the winit backend. Fixed, and paired with registeringzwp_relative_pointer_manager_v1so games/3D tools that want raw, unaccelerated deltas can ask for them too - seeinput.rs'sPointerMotionarm andhandlers/extra_protocols.rs. - Idle-dim overlay: after
config.idle_dim_timeout_secs(default 300,0disables) of no input with nothing holding an idle inhibitor (see the point above), the output dims under a near-opaque overlay -state.rs'sidle_dimmedfield, recomputed once per rendered frame inwinit_backend.rs/backend_drm.rs, drawn inrender_elements.rs. This is a screensaver, not a lock screen - it draws over everything but never intercepts a single input event, so whatever's focused underneath keeps receiving keystrokes/clicks exactly as before. A real lock screen needs authentication (PAM or equivalent) and input-interception that's correct under every edge case - deliberately not attempted here, since a lock screen that looks secure but isn't is worse than not having one. SeeHwdeState::idle_dimmed's doc comment for the full reasoning. Adim_nowkeybinding action can trigger it on demand (seeconfig.rs'sAction::DimNow) - deliberately shipped with no default keybinding, since the obvious muscle-memory shortcut for that (super+l) is what most desktops use for an actual, authenticated lock, and binding a non-authenticating dimmer to it risks someone genuinely believing they've locked their session. - A rendered cursor, at all.
HwdeState::cursor_statuswas tracked (set fromSeatHandler::cursor_image) but nothing ever read it to draw anything - onwinit_backend.rsthis was mostly masked by the host OS's own cursor showing through the window it renders into, but onbackend_drm.rs- a real, exclusive KMS session with no host cursor to fall back on - it meant no visible pointer at all. Fixed with a synthetic placeholder arrow (five small rectangles, seerender_elements.rs'scursor_elements), not yet the client's actual requested cursor surface/theme - see that function's doc comment for the deliberately narrower scope and why. - Stylus/tablet input (
zwp_tablet_manager_v2): proximity, motion (with pressure/tilt where the hardware reports them), tip-down/up, and buttons, forwarded viainput.rs's newTabletTool*match arms (handlers/tablet.rshandles registration and the cursor-image callback). This is genuinely the least-verified addition in this codebase - flagged as "not attempted" across several earlier passes specifically becausewlr-tablet-unstable-v2has more moving parts (per-tool and per-tablet-device registration, several sub-states) than anything else here, and per-line confidence varies a lot within the same block - seeinput.rs's comment on theTabletToolProximityarm for exactly which calls are more vs. less certain, andTabletDescriptor::from(&event.device())in particular as the one most likely to need adjusting for the genericInputBackendboundprocess_input_eventuses (tablet events realistically only ever come from the libinput/DRM backend, never winit's synthetic ones, so a conversion that only exists for the concrete libinput device type would still cover every real use of this - it just might need the function's generic bound narrowed, or the conversion written by hand, to actually compile). - X11 apps' own maximize/minimize now work.
XwmHandler'smaximize_request/unmaximize_request/minimize_request/unminimize_requesthad no override, so_NET_WM_STATE/WM_CHANGE_STATErequests from an X11 app's own window chrome or window-menu were silently swallowed by the trait's default no-op bodies - the Wayland-native equivalents already worked. Now wired to the samemaximize_window_by_id/minimize_window_by_id/unminimize_window_by_idstate functionshandlers/xdg_shell.rsalready uses - seexwayland.rs. - Configurable output transform/scale:
config.output_transform("normal"/"90"/"180"/"270"/"flipped"/"flipped-90"/"flipped-180"/"flipped-270") andconfig.output_scale(fractional HiDPI scaling) now flow intoOutput::change_current_stateon both backends - seeconfig.rs'sparse_transform. Previously hardcoded toTransform::Normalwith no way to configure either at all (this document used to list that specifically as an out-of-scope item, right below). - Per-output position config:
config.outputsmaps a connector name (e.g."DP-1") to an{x, y}position, consulted inbackend_drm.rs::connector_connectedbefore falling back to the existing automatic left-to-right placement. Only position - each output's own transform/scale is still the one globaloutput_transform/output_scalepair above, not per-output; giving every output independent transform/scale is a bigger feature than what this pass set out to add. (Caught and fixed a real use-after-move bug while wiring this up -output_namewas already moved intoOutput::newearlier in the same function before this pass's lookup code tried to read it again; fixed by cloning it at the point of the original move, not by restructuring the lookup.) - Configurable keyboard layout:
config.xkb_layout/xkb_variant/xkb_model/xkb_options(standard libxkbcommon RMLVO fields) now flow intoseat.add_keyboardon both backends viaCompositorConfig::xkb_config()- e.g.xkb_options = "ctrl:nocaps,grp:alt_shift_toggle"for caps-lock-as-ctrl plus an Alt+Shift layout-switch toggle. Previously hardcoded toDefault::default()(US layout, no options) with no way to configure a different layout at all - a real gap for any non-US keyboard.Action::ReloadConfigalso now applies a changed layout live (viaKeyboardHandle::set_xkb_config), not just every other setting - see that action's doc comment instate.rsfor why it needed its own, separate fix even after layout config itself already existed (the reload path never re-ranadd_keyboard, so a changed layout was invisible until restart without this). - Fullscreen support, from scratch (
ManagedWindow::is_fullscreen,fullscreen_window_by_id): this compositor had no fullscreen concept anywhere before this - not unhandled requests like X11's maximize/ minimize were, but no field, no state function, nothing. Wired up for both Wayland (handlers/xdg_shell.rs'sfullscreen_request/unfullscreen_request) and X11 (xwayland.rs, mirroring the maximize/minimize wiring from the previous pass). Real difference from maximize, not just a bigger rectangle: fullscreen also hides the SSD grab bar and focus border (render_elements.rs) - a fullscreen video player showing a drag handle across its top edge would defeat the point. Restoring correctly composes with maximize too: un-fullscreening a window that's also maximized returns it to the maximized rect, not all the way back to floating - only un-maximizing after that drops it the rest of the way.
Earlier revisions of this README (and this pass's own earlier commentary)
described the DRM/udev backend as needing its "atomic-KMS render core"
built - implying backend_drm.rs was mostly scaffolding around a missing
unsafe rendering piece. On closer reading, that undersold what's
actually there: backend_drm.rs already builds a real
smithay::backend::drm::compositor::DrmCompositor per lit connector (see
connector_connected), which is Smithay's own safe, tested high-level
abstraction over atomic KMS commits, GBM buffer allocation, and damage
tracking - the same building block real wlroots-family Rust compositors
use, not hand-rolled unsafe ioctl/property-setting code. render_surface
calls its render_frame/queue_frame, and the per-device DrmEvent::VBlank
handler calls frame_submitted() to keep the double-buffered cycle going
- a complete, self-sustaining render loop, kicked off explicitly after a
connector first lights up (
device_added's "nothing will vblank until a first frame has actually been queued" comment) and re-driven on every subsequent vblank. Hotplug (device_added/device_changed/device_removed), VT-switch pause/resume, and multi-GPU support are all real, not stubs.
What's genuinely still missing/uncertain is narrower than "the core":
whether it actually works correctly against real GPU drivers (no hardware
or cargo check was available to confirm this end-to-end), a hardware
or client-cursor-surface-aware cursor (see the point above - current
cursor is a synthetic placeholder), and DPMS/real display power control
(the idle-dim overlay above is a rendered dimmer, not actual monitor
power-down - deliberately not attempted, since mixing legacy DPMS
property writes with a surface that's also under active atomic-commit
management via DrmCompositor is exactly the kind of driver-specific
interaction that needs real hardware to get right, not more unverified
code stacked on top). This section exists to correct the record rather
than claim new work - nothing in backend_drm.rs itself changed as part
of writing it. (Output scale/rotation configuration was listed here
too as of this section's first version - that's since been addressed,
see "What's new" above; struck through here rather than silently
deleted, since this section is specifically about correcting the
record.)
- DPMS/real display power-down tied to
idle_dimmed(see the correction above for why). - Native Wayland output/display management (
xrandr-equivalent) beyond what already exists (wlr-output-management-style reconfiguration of mode/position/transform at runtime, not just at connector-connect time). - Pointer-gesture-recognizer coverage for pinch/rotate (only 3/4-finger
swipe got a recognizer - see
input.rs- since nothing in this compositor would consume pinch/rotate yet; no zoom/rotate-bindable concept exists at the compositor level). - Pointer constraints (
zwp_pointer_constraints_v1- lock/confine, the usual pairing withzwp_relative_pointer_manager_v1above for games that want the OS cursor to stay put while reading raw deltas). Not attempted this pass; the relative-motion fix and manager registration above are useful without it (any client can already ask for relative deltas), a locked/confined pointer is the next, separate piece. - Per-libinput-device output routing for true multi-monitor setups (see
run_udev's own NOTE comment on this) - every input device is routed to whatever output happens to be first, which only matters in practice for absolute-position devices (touchscreens) on a system with more than one output; not attempted here since correctly resolving "which physical output is this input device attached to" needs matching libinput device/udev properties that weren't confirmed available in the exact form assumed.
None of this is an accident - it's the deliberate "narrow but real" scope the project asked for, with the bigger/hardware-dependent pieces flagged for follow-up rather than faked.