Skip to content

typester/riverql

Repository files navigation

RiverQL

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.

Example Status Bar (eww + RiverQL)

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.

eww status bar preview

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.

Features

  • 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

Installation

cargo install riverql

This installs a riverql binary in your Cargo bin directory.

Getting Started

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).

GraphQL Endpoints

  • 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 }
  }
}

Working with Tag Lists

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 }
  }
}

Client Mode

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 both unix://path#/graphql and ws://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.

License

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.

About

GraphQL bridge for the River Wayland compositor, plus a lightweight CLI client.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •  

Languages