Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,12 @@ jobs:
- uses: actions/checkout@v3
- run: |
make in-docker TARGET='dist' BUILD_TYPE='debug' TARGET_ARCH='aarch64-unknown-linux-gnu'

build-without-networking:
name: Run build without networking support
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- run: |
make in-docker TARGET='test' FEATURES=''
69 changes: 69 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ assets = [
name = "generate"
path = "./src/generate.rs"

# The networking feature adds websocket support (routing input over the
# network). It is enabled by default and can be disabled with:
# cargo build --no-default-features
[features]
default = ["networking"]
networking = ["dep:tokio-tungstenite"]

[dependencies]
clap = { version = "4.5.54", features = ["derive", "string"] }
clap_complete = "4.5.65"
Expand Down Expand Up @@ -138,6 +145,7 @@ serialport = "4.8.1"
tabled = { version = "0.20.0", features = ["ansi"] }
thiserror = "2.0.17"
tokio = { version = "1.49.0", features = ["full"] }
tokio-tungstenite = { version = "0.26.2", optional = true }
udev = { version = "0.9.3", features = ["mio"] }
uhid-virt = "0.0.8"
version-compare = "0.2.1"
Expand Down
23 changes: 17 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ PREFIX ?= /usr
CACHE_DIR := .cache
ENABLE_METRICS ?= 1

# Cargo features to build with. Networking (websocket) support is enabled by
# default; set FEATURES to an empty string to compile without it:
# make build FEATURES=""
FEATURES ?= networking
ifeq ($(FEATURES),)
FEATURE_FLAGS := --no-default-features
else
FEATURE_FLAGS := --features "$(FEATURES)"
endif

ifeq ($(ARCH),x86_64)
ARCH_DEB = amd64
endif
Expand Down Expand Up @@ -103,12 +113,12 @@ build: target/$(TARGET_ARCH)/$(BUILD_TYPE)/$(NAME)
.PHONY: debug
debug: target/$(TARGET_ARCH)/debug/$(NAME) ## Build debug build
target/$(TARGET_ARCH)/debug/$(NAME): $(ALL_RS) Cargo.lock
cargo build --target $(TARGET_ARCH)
cargo build --target $(TARGET_ARCH) $(FEATURE_FLAGS)

.PHONY: release
release: target/$(TARGET_ARCH)/release/$(NAME) ## Build release build
target/$(TARGET_ARCH)/release/$(NAME): $(ALL_RS) Cargo.lock
cargo build --release --target $(TARGET_ARCH)
cargo build --release --target $(TARGET_ARCH) $(FEATURE_FLAGS)

.PHONY: all
all: build debug ## Build release and debug builds
Expand All @@ -132,8 +142,8 @@ format: ## Run rustfmt on all source files

.PHONY: test
test: test-autostart-rules ## Run all tests
cargo clippy --all -- -D warnings
cargo test -- --show-output
cargo clippy --all $(FEATURE_FLAGS) -- -D warnings
cargo test $(FEATURE_FLAGS) -- --show-output

.PHONY: test-autostart-rules
test-autostart-rules: ## Test to ensure autostart rules are up-to-date
Expand All @@ -145,7 +155,7 @@ test-polkit-usage: ## Test to ensure polkit policy exists for all actions

.PHONY: generate
generate: ## Generate schema definitions for configs
cargo run --bin generate
cargo run --bin generate $(FEATURE_FLAGS)

.PHONY: setup
setup: /usr/share/dbus-1/system.d/$(DBUS_NAME).conf ## Install dbus policies
Expand Down Expand Up @@ -260,7 +270,8 @@ in-docker:
-e ARCH=$(ARCH) \
-e TARGET_ARCH=$(TARGET_ARCH) \
-e BUILD_TYPE=$(BUILD_TYPE) \
-e PKG_CONFIG_SYSROOT_DIR="/usr/$(ARCH)-linux-gnu" \
-e FEATURES=$(FEATURES) \
-e PKG_CONFIG_SYSROOT_DIR="/usr/$(ARCH)-linux-gnu" \
--user $(shell id -u):$(shell id -g) \
$(IMAGE_NAME):$(IMAGE_TAG) \
make BUILD_TYPE=$(BUILD_TYPE) $(TARGET)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ and translate their input to a variety of virtual device formats.
- [x] Emulate mouse, keyboard, and gamepad inputs
- [x] Intercept and route input over DBus for overlay interface control
- [x] Input mapping profiles to translate source input into the desired target input
- [ ] Route input over the network
- [x] Route input over the network (optional `networking` cargo feature, enabled by default)

## Documentation

Expand Down
13 changes: 13 additions & 0 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,19 @@
sudo make install
```

InputPlumber is built with the `networking` feature enabled by default, which
adds websocket support for routing input over the network. To compile without
it, pass an empty `FEATURES` value (or use cargo directly):

```bash
make build FEATURES=""
# or
cargo build --no-default-features
```

When built without the `networking` feature, websocket devices are not
supported and any `websocket` entries in device configs are ignored.

Then start the service with:

```bash
Expand Down
36 changes: 36 additions & 0 deletions rootfs/usr/share/inputplumber/devices/80-websockets.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/ShadowBlip/InputPlumber/main/rootfs/usr/share/inputplumber/schema/composite_device_v1.json
# Schema version number
version: 1

# The type of configuration schema
kind: CompositeDevice

# Name of the composite device mapping
name: Websockets

# Maximum number of source devices per CompositeDevice.
maximum_sources: 1

# Only use this profile if *any* of the given matches matches. If this list is
# empty, then the source devices will *always* be checked.
# /sys/class/dmi/id/product_name
matches: []

# One or more source devices to combine into a single virtual device. The events
# from these devices will be watched and translated according to the key map.
source_devices:
- group: gamepad
websocket:
address: "0.0.0.0"
port: 12907

# Optional configuration for the composite device
options:
# If true, InputPlumber will automatically try to manage the input device. If
# this is false, InputPlumber will not try to manage the device unless an
# external service enables management of the device. Defaults to 'false'
auto_manage: true

# The target input device(s) to emulate by default
target_devices:
- xbox-elite
23 changes: 23 additions & 0 deletions src/cli/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use zbus::Connection;
use crate::cli::get_managed_objects;
use crate::dbus::interface::composite_device::CompositeDeviceInterfaceProxy;
use crate::dbus::interface::manager::ManagerInterfaceProxy;
#[cfg(feature = "networking")]
use crate::dbus::interface::target::websocket::TargetWebsocketInterfaceProxy;
use crate::dbus::interface::target::TargetInterfaceProxy;
use crate::input::target::TargetDeviceTypeId;

Expand Down Expand Up @@ -48,6 +50,12 @@ pub enum DeviceCommand {
},
/// Test input menu
Test,
/// Connect this input device to a remote instance of InputPlumber
#[cfg(feature = "networking")]
Connect {
/// URL of the remote instance of InputPlumber (e.g. "ws://192.168.0.100:12901")
url: String,
},
}

#[derive(Subcommand, Debug, Clone)]
Expand Down Expand Up @@ -338,6 +346,21 @@ pub async fn handle_device(
return Err(e.to_string().into());
}
}
#[cfg(feature = "networking")]
DeviceCommand::Connect { url } => {
device
.set_target_devices(vec!["websocket".to_string()])
.await?;
let target_devices = device.target_devices().await?;
for path in target_devices {
let websocket = TargetWebsocketInterfaceProxy::builder(&conn)
.path(path)?
.build()
.await?;
websocket.connect(url.clone()).await?;
println!("Successfully connected to: {url}");
}
}
}

Ok(())
Expand Down
Loading
Loading