⚠️ This is a fork ofOpenActionAPI/rustOur changes live on
main(this branch). It has intentionally diverged from upstream.Why this fork exists The upstream crate has no inbound event for
touchTap, so when an OpenDeck host sends atouchTap(a tap or long-press on a Stream Deck + / + XL encoder LCD touchscreen) the event fails to deserialize and is silently dropped — a plugin can never react to it. This fork adds that event end-to-end: aTouchTapinbound variant, ahandle_touch_tapruntime handler, and a default-no-opAction::touch_tap(instance, settings, hold, tap_pos)trait method. The change is purely additive and backward-compatible.What depends on this fork
koconnorgit/opendeck-volume-controller— itsCargo.tomlhas[patch.crates-io] openaction = { git = ".../koconnorgit/rust", branch = "main" }, and itsCargo.lockpins a commit on this branch. This is how its "tap the LCD to mute" feature receives touch events.The matching host side (emitting
touchTap) lives inkoconnorgit/OpenDeckas part of that fork's Stream Deck + XL support; stocknekename/OpenDeckdoes not emittouchTap.
⚠️ Do not "Sync fork" / resetmainto upstream. GitHub's Sync fork button would discard these commits and breakcargo buildfor the volume-controller plugin (itsCargo.lockpins a commit on this branch). Likewise, don't delete this repo or rewritemain's history without re-pinning the plugin.Upstreaming: considered and intentionally not pursued — this stays a permanent personal fork rather than an upstream PR.
A Rust crate for creating plugins for the OpenAction API (backwards-compatible with the Stream Deck SDK)
use openaction::*;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Default, Clone)]
#[serde(default)]
struct CounterSettings {
value: u32,
}
struct CounterAction;
#[async_trait]
impl Action for CounterAction {
const UUID: ActionUuid = "com.example.counter.counter";
type Settings = CounterSettings;
async fn key_up(
&self,
instance: &Instance,
settings: &Self::Settings,
) -> OpenActionResult<()> {
let mut clone = settings.clone();
clone.value = settings.value + 1;
instance.set_settings(&clone).await?;
instance.set_title(Some(clone.value.to_string()), None).await
}
}
#[tokio::main]
async fn main() -> OpenActionResult<()> {
{
use simplelog::*;
if let Err(error) = TermLogger::init(
LevelFilter::Debug,
Config::default(),
TerminalMode::Stdout,
ColorChoice::Never,
) {
eprintln!("Logger initialization failed: {}", error);
}
}
register_action(CounterAction).await;
run(std::env::args().collect()).await
}