RiverQL exposes River window manager state over GraphQL. It ships a server that bridges River's Wayland status protocol into GraphQL queries and subscriptions, plus a CLI client for driving graphql-transport-ws streams.
examples/eww contains a ready-to-run setup that wires RiverQL into an eww status bar. The example launches a snapshot poller for river outputs, reacts to live tag updates, and renders the result as a panel.
This indicates that:
- Tags 1 and 3 are focused.
- Tags 1, 2, 3, and 0 are currently occupied.
- Urgent event comes up in tag 9.
- Focused view is Emacs.
Try it by copying the directory into your own eww config (~/.config/eww) or by running eww --config ./examples/eww open bar-window-1 inside the repository while the RiverQL server is running.
This example supports multiple monitors. Open bar-window-1 for DP-1, bar-window-2 for DP-2 respectively. If your environment has a different name e.g. eDP-1, you need to modify the code.
- GraphQL access to River output/seat state (tags, layouts, focused view, mode)
- Real-time subscriptions via
graphql-transport-ws - Lightweight CLI client for ad-hoc GraphQL subscriptions
cargo install riverqlThis installs a riverql binary in your Cargo bin directory.
Most setups launch the server inside River's init script:
riverql --server &Note that you might need to set this with full executable path like: ~/.cargo/bin/riverql --server &.
By default this creates a Unix socket under $XDG_RUNTIME_DIR/riverql.sock. To override, use --listen, e.g. riverql --server --listen tcp://127.0.0.1:8080.
The server logs via tracing; tune with RUST_LOG (for instance RUST_LOG=riverql=debug).
- HTTP/WS endpoint:
/graphql - GraphiQL UI:
/graphiql - Schema SDL:
/schema
Example query:
{ outputs { name focusedTags viewTags urgentTags layoutName } seatFocusedOutput { name } }Fetch a single output by name when you only care about one:
query ($name: String!, $tagList: Boolean = true) { output(name: $name, tagList: $tagList) { name focusedTags focusedTagsList layoutName } }Subscription example:
subscription { events { __typename ... on OutputFocusedTags { name tags } ... on SeatFocusedOutput { name } } }By default RiverQL exposes tag bitmasks as river does. Some environments — notably eww — struggle with bit operations, so any query or subscription can opt into decoded lists by passing tagList: true. When enabled, focusedTagsList / urgentTagsList fields become non-null while the original mask fields remain available for backward compatibility.
query ($tagList: Boolean = true) { outputs(tagList: $tagList) { name focusedTags focusedTagsList urgentTags urgentTagsList } }subscription ($name: String!, $tagList: Boolean = true) { eventsForOutput(outputName: $name, tagList: $tagList) { __typename ... on OutputFocusedTags { name tags tagsList } } }When a widget or script (for example an eww widget) needs data, invoke riverql without --server:
riverql 'subscription { events { __typename } }'Key points:
- Inline queries or
@file.graphql - Reads stdin when no query argument is supplied
- Uses the default endpoint derived from
--listen; override with--endpointif needed (supports bothunix://path#/graphqlandws://host:port/pathformats)
Using with eww
Add the server to your River init script (riverql --server &). Then, inside eww.yuck, you can consume RiverQL in two ways:
Polling a query:
(defpoll river_outputs :interval "5s" "riverql 'query { outputs { name focusedTags } }' | jq --unbuffered -c '.data.outputs'") (defwidget river-tags [] (box :orientation "vertical" (for output in river_outputs (box :class "tag-row" (label :text (format "%s" (. output 'name))) (label :text (format "%s" (. output 'focusedTags)))))))Listening for live events:
(deflisten events :initial "{}" "riverql 'subscription { events { __typename ... on OutputFocusedTags { name tags } } }' | jq --unbuffered -c '.data.events'") (defwidget river-event-feed [] (box :orientation "vertical" (label :text (format "Latest event: %s" events))))defpoll is ideal for periodic snapshots (e.g. populating a list of outputs), while deflisten reacts instantly to subscription pushes. Both examples assume riverql is on PATH and that jq is available to compact JSON.
Code in this repository is licensed under MIT; see LICENSE.
The XML files under protocol/ are copied from upstream River (GPL-3.0-or-later) and wlroots (MIT). They retain their original licensing. Consult the upstream projects for full details.
