SceneDeck is a Rust 2021 GTK4/libadwaita application. GTK owns the UI thread; Tokio owns asynchronous OBS work; controller events bridge the two.
Install:
- Rust stable.
- GTK4 development libraries.
- libadwaita development libraries.
- GLib tools, including
glib-compile-resources. - OBS Studio for manual integration testing.
Build and run:
cargo runRun with more detailed logs:
RUST_LOG=scenedeck=trace,obws=debug cargo runThe default logger is scenedeck=debug,warn when RUST_LOG is not set.
Run these before committing:
cargo fmt --all -- --check
cargo check --workspace --all-features
cargo test --workspace --all-features
cargo clippy --workspace --all-targets --all-features -- -D warningsUse cargo fmt --all to apply formatting.
- Identify the boundary you are changing: UI, controller, OBS adapter, domain, service, or storage.
- Keep OBS protocol calls in
src/obs/. - Keep command routing and async session lifecycle in
src/controller/. - Keep pure rules and derived behavior in
src/domain/orsrc/services/. - Keep GTK widget construction in
src/ui/. - Add tests for pure domain, service, config, and storage behavior when possible.
- Validate with the commands above.
Avoid holding std::sync::Mutex guards across .await. The shared OBS client
slot is intentionally locked briefly, cloned, and released before async work is
spawned.
Add a Live page control:
- Add a command to
src/controller/command.rs. - Handle it in
src/controller/app_controller.rs. - Add OBS calls in
src/obs/client.rsif needed. - Add state or events in
src/controller/event.rsandsrc/controller/state.rsif the UI needs updates. - Wire GTK widgets in
src/ui/pages/live.rsor a widget undersrc/ui/widgets/.
Add a new OBS capability:
- Wrap the
obwscall insrc/obs/client.rs. - Convert external OBS types into domain types before returning.
- Route the action through
AppCommandor the state throughAppEvent. - Update docs in
docs/obs-websocket.md.
Add a new page:
- Add a page variant in
src/controller/state.rs. - Add it to
NAV_PAGESinsrc/ui/window.rs. - Create a page module in
src/ui/pages/. - Add the module to
src/ui/pages/mod.rs. - Wire refresh behavior if the page depends on app state.
Add a symbolic icon:
- Place the SVG under
resources/icons/. - Add it to
resources/scenedeck.gresource.xml. - Add a
cargo:rerun-if-changedline inbuild.rsif needed. - Use the icon name without
.svgin GTK.
Add a built-in theme:
- Add both light and dark CSS files under
resources/themes/. - Register the theme family in
src/ui/theme.rswith oneBuiltInThemeentry that points to both files. - Keep CSS as a narrow overlay on stable SceneDeck classes.
- Update
docs/custom-themes.mdanddocs/theme-css-reference.mdif new stable classes or semantics are introduced.
- Connect with no password.
- Connect with password.
- Disconnect and reconnect.
- Switch scenes from SceneDeck and from OBS.
- Start and stop streaming if OBS is configured for a safe test target.
- Start and stop recording.
- Change mute and volume in SceneDeck and confirm OBS follows.
- Change mute and volume in OBS and confirm SceneDeck follows.
- Switch OBS profile and scene collection.
- Rename, create, or remove an input and confirm audio refresh behavior.
- Add or remove nested scene items and confirm Graph and active scene audio refresh behavior.
Flatpak packaging lives in packaging/flatpak/. See
../packaging/flatpak/README.md.
Pushing a semantic version tag such as v0.2.0 runs the Linux release workflow.
After the packages build, git-cliff generates the GitHub release body from
commits since the previous version tag using cliff.toml.
Use Conventional Commit subjects so changes land in the intended section:
feat: add a user-visible capabilityfix: correct broken behaviorperf: reduce runtime overheadrefactor: reorganize internalsdocs: update documentationtest: expand automated coveragebuild:,ci:, orchore:for maintenance
Older imperative subjects are also categorized. Version-only subjects such as
Bump version to 0.2.0 and Release v0.2.0 are excluded automatically. The
release job fails before publication if note generation produces an empty file.