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 riverql
This 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--endpoint
if needed (supports bothunix://path#/graphql
andws://host:port/path
formats)
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.