mirror of
https://github.com/nekename/OpenDeck.git
synced 2026-10-28 09:44:01 +00:00
refactor: bring starter pack plugin source in-tree
This commit is contained in:
23 files changed
+3504
-6
No files matched your search
@@ -0,0 +1 @@
|
||||
plugins/com.amansprojects.starterpack.sdPlugin/files/propertyInspector/sdpi.css linguist-vendored
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/src-tauri/target
|
||||
**/target
|
||||
/src-tauri/gen
|
||||
.env
|
||||
.env.*
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[submodule "plugins/com.amansprojects.starterpack.sdPlugin"]
|
||||
path = plugins/com.amansprojects.starterpack.sdPlugin
|
||||
url = https://github.com/nekename/opendeck-starterpack.git
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"fmt": {
|
||||
"exclude": ["src-tauri/", "plugins/", "*.md"],
|
||||
"exclude": ["src-tauri/", "*.md"],
|
||||
"useTabs": true,
|
||||
"lineWidth": 200
|
||||
},
|
||||
|
||||
Submodule plugins/com.amansprojects.starterpack.sdPlugin deleted from 7bc1110102.
+1258
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "opendeck-starterpack"
|
||||
version = "1.4.1"
|
||||
authors = ["Aman Khanna (nekename)"]
|
||||
license = "GPL-3.0-or-later"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
openaction = "1.1"
|
||||
log = "0.4"
|
||||
simplelog = "0.12"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
ron = "0.8"
|
||||
enigo = { version = "0.3", features = ["serde", "x11rb"] }
|
||||
tokio = { version = "1.42", features = ["full"] }
|
||||
tungstenite = "0.26"
|
||||
os_pipe = "1.2"
|
||||
@@ -0,0 +1,18 @@
|
||||
import { copy } from "jsr:@std/fs";
|
||||
import { join } from "jsr:@std/path";
|
||||
|
||||
if (Deno.args.length < 1) Deno.exit(1);
|
||||
const outDir = Deno.args[0];
|
||||
try {
|
||||
Deno.remove(outDir, { recursive: true });
|
||||
} catch (error: any) {
|
||||
if (!(error instanceof Deno.errors.NotFound)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
copy("files", outDir);
|
||||
|
||||
if (!(await new Deno.Command("cargo", { args: ["build", "--release"] }).spawn().status).success) Deno.exit(1);
|
||||
const builtExec = Deno.build.os == "windows" ? "opendeck-starterpack.exe" : "opendeck-starterpack";
|
||||
const targetExec = Deno.build.os == "windows" ? "opendeck-starterpack.exe" : Deno.build.os == "darwin" ? "opendeck-starterpack-mac" : "opendeck-starterpack";
|
||||
copy(join("target", "release", builtExec), join(outDir, targetExec));
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"Name": "OpenDeck Starter Pack",
|
||||
"Description": "OpenDeck starter pack plugin",
|
||||
"Author": "Aman Khanna (nekename)",
|
||||
"Version": "1.4.1",
|
||||
"CodePathWin": "opendeck-starterpack.exe",
|
||||
"CodePathMac": "opendeck-starterpack-mac",
|
||||
"CodePathLin": "opendeck-starterpack",
|
||||
"Icon": "icons/plugin",
|
||||
"Category": "OpenDeck",
|
||||
"OS": [
|
||||
{
|
||||
"Platform": "mac",
|
||||
"MinimumVersion": "10.11"
|
||||
},
|
||||
{
|
||||
"Platform": "windows",
|
||||
"MinimumVersion": "10"
|
||||
},
|
||||
{
|
||||
"Platform": "linux"
|
||||
}
|
||||
],
|
||||
"Actions": [
|
||||
{
|
||||
"Icon": "icons/runCommand",
|
||||
"Name": "Run Command",
|
||||
"States": [{ "Image": "actionDefaultImage" }],
|
||||
"Controllers": ["Keypad"],
|
||||
"SupportedInMultiActions": true,
|
||||
"Tooltip": "Run a command",
|
||||
"UUID": "com.amansprojects.starterpack.runcommand",
|
||||
"PropertyInspectorPath": "propertyInspector/runCommand.html"
|
||||
},
|
||||
{
|
||||
"Icon": "icons/inputSimulation",
|
||||
"Name": "Input Simulation",
|
||||
"States": [{ "Image": "actionDefaultImage" }],
|
||||
"Controllers": ["Keypad"],
|
||||
"SupportedInMultiActions": true,
|
||||
"Tooltip": "Simulate mouse or keyboard input",
|
||||
"UUID": "com.amansprojects.starterpack.inputsimulation",
|
||||
"PropertyInspectorPath": "propertyInspector/inputSimulation.html"
|
||||
},
|
||||
{
|
||||
"Icon": "icons/switchProfile",
|
||||
"Name": "Switch Profile",
|
||||
"States": [{ "Image": "actionDefaultImage" }],
|
||||
"Controllers": ["Keypad"],
|
||||
"SupportedInMultiActions": true,
|
||||
"Tooltip": "Switch the selected profile",
|
||||
"UUID": "com.amansprojects.starterpack.switchprofile",
|
||||
"PropertyInspectorPath": "propertyInspector/switchProfile.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="10" viewBox="0 0 12 10">
|
||||
<polygon fill="#FFF" points="7.2 7.5 7.2 -1.3 8.7 -1.3 8.6 9.1 2.7 8.7 2.7 7.2" transform="rotate(37 5.718 3.896)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 212 B |
+77
@@ -0,0 +1,77 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<script>
|
||||
let update = () => {};
|
||||
|
||||
function connectElgatoStreamDeckSocket(inPort, inPropertyInspectorUUID, inRegisterEvent, inInfo, inActionInfo) {
|
||||
const websocket = new WebSocket("ws://localhost:" + inPort);
|
||||
inActionInfo = JSON.parse(inActionInfo);
|
||||
websocket.onopen = () => {
|
||||
websocket.send(JSON.stringify({
|
||||
event: inRegisterEvent,
|
||||
uuid: inPropertyInspectorUUID,
|
||||
}));
|
||||
};
|
||||
|
||||
const down = document.getElementById("down");
|
||||
down.value = inActionInfo.payload.settings.down ?? "";
|
||||
const up = document.getElementById("up");
|
||||
up.value = inActionInfo.payload.settings.up ?? "";
|
||||
|
||||
update = () => {
|
||||
websocket.send(JSON.stringify({
|
||||
event: "setSettings",
|
||||
context: inActionInfo.context,
|
||||
payload: {
|
||||
down: down.value,
|
||||
up: up.value,
|
||||
},
|
||||
}));
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<link rel="stylesheet" href="sdpi.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div type="textarea" class="sdpi-item">
|
||||
<div class="sdpi-item-label">Key down</div>
|
||||
<span class="sdpi-item-value textarea">
|
||||
<textarea oninput="update();" id="down" style="font-family: monospace" placeholder="Input to be executed on key down"></textarea>
|
||||
</span>
|
||||
</div>
|
||||
<div type="textarea" class="sdpi-item">
|
||||
<div class="sdpi-item-label">Key up</div>
|
||||
<span class="sdpi-item-value textarea">
|
||||
<textarea oninput="update();" id="up" style="font-family: monospace" placeholder="Input to be executed on key up"></textarea>
|
||||
</span>
|
||||
</div>
|
||||
<details open>
|
||||
<summary style="margin-bottom: 0.25rem; font-size: 1.25rem">Details</summary>
|
||||
<code style="word-break: break-all">[t("hello world!"),m(10,10,r),s(5),b(l),k(ctrl,p),k(uni('a')),k(ctrl,r)]</code>
|
||||
<p>The example above becomes something like this:</p>
|
||||
<!-- deno-fmt-ignore -->
|
||||
<code style="white-space: pre;">[
|
||||
Text("hello world!"),
|
||||
MoveMouse(10, 10, Relative),
|
||||
Scroll(5, Vertical),
|
||||
Button(Left, Click),
|
||||
Key(Control, Press),
|
||||
Key(Unicode('a'), Click),
|
||||
Key(Control, Release),
|
||||
]</code>
|
||||
<p>
|
||||
As you can see, <code>Vertical</code> is the default for the <code>Scroll</code> action if an axis is not specified,
|
||||
<code>Click</code> (press followed by release) is the default for the <code>Button</code> and <code>Key</code> actions if a direction is not specified, and so on.
|
||||
</p>
|
||||
<!-- deno-fmt-ignore -->
|
||||
<p>
|
||||
You can find a list of all of the keycodes
|
||||
<span style="color: dodgerblue; cursor: pointer;" onclick="window.open('https://github.com/enigo-rs/enigo/blob/v0.3.0/src/keycodes.rs', '_blank');">here</span>.
|
||||
Please note that not all keycodes are available on all operating systems, as is marked on each such keycode on the line above it.
|
||||
You can find the possible buttons, directions, axis and coordinate types
|
||||
<span style="color: dodgerblue; cursor: pointer;" onclick="window.open('https://github.com/enigo-rs/enigo/blob/v0.3.0/src/lib.rs', '_blank');">here</span>.
|
||||
</p>
|
||||
</details>
|
||||
</body>
|
||||
</html>
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<script>
|
||||
let update = () => {};
|
||||
|
||||
function connectElgatoStreamDeckSocket(inPort, inPropertyInspectorUUID, inRegisterEvent, inInfo, inActionInfo) {
|
||||
const websocket = new WebSocket("ws://localhost:" + inPort);
|
||||
inActionInfo = JSON.parse(inActionInfo);
|
||||
websocket.onopen = () => {
|
||||
websocket.send(JSON.stringify({
|
||||
event: inRegisterEvent,
|
||||
uuid: inPropertyInspectorUUID,
|
||||
}));
|
||||
};
|
||||
|
||||
const down = document.getElementById("down");
|
||||
down.value = inActionInfo.payload.settings.down ?? "";
|
||||
const up = document.getElementById("up");
|
||||
up.value = inActionInfo.payload.settings.up ?? "";
|
||||
const file = document.getElementById("file");
|
||||
file.value = inActionInfo.payload.settings.file ?? "";
|
||||
const show = document.getElementById("show");
|
||||
show.checked = inActionInfo.payload.settings.show ?? false;
|
||||
|
||||
update = () => {
|
||||
websocket.send(JSON.stringify({
|
||||
event: "setSettings",
|
||||
context: inActionInfo.context,
|
||||
payload: {
|
||||
down: down.value,
|
||||
up: up.value,
|
||||
file: file.value,
|
||||
show: show.checked,
|
||||
},
|
||||
}));
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<link rel="stylesheet" href="sdpi.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="sdpi-item">
|
||||
<div class="sdpi-item-label">Key down</div>
|
||||
<input id="down" oninput="update();" class="sdpi-item-value" />
|
||||
</div>
|
||||
<div class="sdpi-item">
|
||||
<div class="sdpi-item-label">Key up</div>
|
||||
<input id="up" oninput="update();" class="sdpi-item-value" />
|
||||
</div>
|
||||
<br />
|
||||
<div class="sdpi-item">
|
||||
<div class="sdpi-item-label">Write to path</div>
|
||||
<input id="file" oninput="update();" class="sdpi-item-value" />
|
||||
</div>
|
||||
<div class="sdpi-item">
|
||||
<div class="sdpi-item-label">Show on key</div>
|
||||
<input id="show" type="checkbox" oninput="update();" class="sdpi-item-value" />
|
||||
<label for="show"><span></span></label>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
+1747
File diff suppressed because it is too large.
Load diff
+46
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<script>
|
||||
let update = () => {};
|
||||
|
||||
function connectElgatoStreamDeckSocket(inPort, inPropertyInspectorUUID, inRegisterEvent, inInfo, inActionInfo) {
|
||||
const websocket = new WebSocket("ws://localhost:" + inPort);
|
||||
inActionInfo = JSON.parse(inActionInfo);
|
||||
websocket.onopen = () => {
|
||||
websocket.send(JSON.stringify({
|
||||
event: inRegisterEvent,
|
||||
uuid: inPropertyInspectorUUID,
|
||||
}));
|
||||
};
|
||||
|
||||
const device = document.getElementById("device");
|
||||
device.value = inActionInfo.payload.settings.device ?? inActionInfo.device;
|
||||
const profile = document.getElementById("profile");
|
||||
profile.value = inActionInfo.payload.settings.profile ?? "";
|
||||
|
||||
update = () => {
|
||||
websocket.send(JSON.stringify({
|
||||
event: "setSettings",
|
||||
context: inActionInfo.context,
|
||||
payload: {
|
||||
device: device.value ?? inActionInfo.device,
|
||||
profile: profile.value,
|
||||
},
|
||||
}));
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<link rel="stylesheet" href="sdpi.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="sdpi-item">
|
||||
<div class="sdpi-item-label">Device ID</div>
|
||||
<input id="device" oninput="update();" class="sdpi-item-value" />
|
||||
</div>
|
||||
<div class="sdpi-item">
|
||||
<div class="sdpi-item-label">Profile</div>
|
||||
<input id="profile" oninput="update();" class="sdpi-item-value" />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,3 @@
|
||||
edition = "2021"
|
||||
hard_tabs = true
|
||||
newline_style = "Unix"
|
||||
@@ -0,0 +1,38 @@
|
||||
use openaction::*;
|
||||
|
||||
use enigo::{
|
||||
agent::{Agent, Token},
|
||||
Enigo, Settings,
|
||||
};
|
||||
|
||||
pub fn key_down(event: KeyEvent) -> EventHandlerResult {
|
||||
if let Some(value) = event.payload.settings.as_object().unwrap().get("down") {
|
||||
let value = value.as_str().unwrap();
|
||||
if value.trim().is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut enigo = Enigo::new(&Settings::default())?;
|
||||
let tokens: Vec<Token> = ron::from_str(value)?;
|
||||
for token in tokens {
|
||||
enigo.execute(&token).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn key_up(event: KeyEvent) -> EventHandlerResult {
|
||||
if let Some(value) = event.payload.settings.as_object().unwrap().get("up") {
|
||||
let value = value.as_str().unwrap();
|
||||
if value.trim().is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut enigo = Enigo::new(&Settings::default())?;
|
||||
let tokens: Vec<Token> = ron::from_str(value)?;
|
||||
for token in tokens {
|
||||
enigo.execute(&token).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
mod input_simulation;
|
||||
mod run_command;
|
||||
mod switch_profile;
|
||||
|
||||
use openaction::*;
|
||||
|
||||
struct GlobalEventHandler {}
|
||||
impl openaction::GlobalEventHandler for GlobalEventHandler {}
|
||||
|
||||
struct ActionEventHandler {}
|
||||
impl openaction::ActionEventHandler for ActionEventHandler {
|
||||
async fn key_down(
|
||||
&self,
|
||||
event: KeyEvent,
|
||||
_outbound: &mut openaction::OutboundEventManager,
|
||||
) -> EventHandlerResult {
|
||||
match &event.action[..] {
|
||||
"com.amansprojects.starterpack.runcommand" => run_command::key_down(event),
|
||||
"com.amansprojects.starterpack.inputsimulation" => input_simulation::key_down(event),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn key_up(
|
||||
&self,
|
||||
event: KeyEvent,
|
||||
_outbound: &mut openaction::OutboundEventManager,
|
||||
) -> EventHandlerResult {
|
||||
match &event.action[..] {
|
||||
"com.amansprojects.starterpack.runcommand" => run_command::key_up(event),
|
||||
"com.amansprojects.starterpack.inputsimulation" => input_simulation::key_up(event),
|
||||
"com.amansprojects.starterpack.switchprofile" => switch_profile::key_up(event),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
simplelog::TermLogger::init(
|
||||
simplelog::LevelFilter::Debug,
|
||||
simplelog::Config::default(),
|
||||
simplelog::TerminalMode::Stdout,
|
||||
simplelog::ColorChoice::Never,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if let Err(error) = init_plugin(GlobalEventHandler {}, ActionEventHandler {}).await {
|
||||
log::error!("Failed to initialise plugin: {}", error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
use std::io::Read;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
use openaction::*;
|
||||
|
||||
async fn run_command(event: KeyEvent, action: &str) -> EventHandlerResult {
|
||||
let settings = event.payload.settings.as_object().unwrap();
|
||||
let Some(value) = settings.get(action).map(|v| v.as_str().unwrap()) else {
|
||||
return Ok(());
|
||||
};
|
||||
if value.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
let command = if std::env::var("container").is_ok() {
|
||||
"flatpak-spawn"
|
||||
} else {
|
||||
"sh"
|
||||
};
|
||||
#[cfg(unix)]
|
||||
let extra_args = if std::env::var("container").is_ok() {
|
||||
vec!["--host", "sh", "-c"]
|
||||
} else {
|
||||
vec!["-c"]
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
let command = "cmd";
|
||||
#[cfg(windows)]
|
||||
let extra_args = ["/C"];
|
||||
|
||||
let (mut reader, writer) = os_pipe::pipe()?;
|
||||
let mut command = Command::new(command);
|
||||
command
|
||||
.args(extra_args)
|
||||
.arg(value)
|
||||
.stdout(Stdio::from(writer.try_clone()?))
|
||||
.stderr(Stdio::from(writer));
|
||||
command.spawn()?.wait()?;
|
||||
drop(command);
|
||||
let mut output = String::new();
|
||||
reader.read_to_string(&mut output)?;
|
||||
|
||||
if let Some(path) = settings.get("file").map(|v| v.as_str().unwrap()) {
|
||||
if !path.is_empty() {
|
||||
tokio::fs::write(path, &output).await?;
|
||||
}
|
||||
}
|
||||
|
||||
if settings
|
||||
.get("show")
|
||||
.unwrap_or(&serde_json::Value::Bool(false))
|
||||
.as_bool()
|
||||
.unwrap()
|
||||
{
|
||||
let mut lock = OUTBOUND_EVENT_MANAGER.lock().await;
|
||||
let outbound = lock.as_mut().unwrap();
|
||||
outbound
|
||||
.set_title(event.context, Some(output.trim().to_owned()), None)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn key_down(event: KeyEvent) -> EventHandlerResult {
|
||||
tokio::spawn(async move {
|
||||
if let Err(error) = run_command(event, "down").await {
|
||||
log::warn!("Failed to run command: {error}");
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn key_up(event: KeyEvent) -> EventHandlerResult {
|
||||
tokio::spawn(async move {
|
||||
if let Err(error) = run_command(event, "up").await {
|
||||
log::warn!("Failed to run command: {error}");
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
use openaction::*;
|
||||
|
||||
// Non-spec OpenDeck-specific protocols are used in this file.
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
struct SwitchProfileEvent {
|
||||
event: &'static str,
|
||||
device: String,
|
||||
profile: String,
|
||||
}
|
||||
|
||||
pub fn key_up(event: KeyEvent) -> EventHandlerResult {
|
||||
if let Some(value) = event.payload.settings.as_object().unwrap().get("profile") {
|
||||
let (mut stream, _) = tungstenite::connect(format!(
|
||||
"ws://localhost:{}",
|
||||
std::env::args().nth(2).unwrap()
|
||||
))?;
|
||||
stream.write(tungstenite::Message::text(serde_json::to_string(
|
||||
&SwitchProfileEvent {
|
||||
event: "switchProfile",
|
||||
device: event
|
||||
.payload
|
||||
.settings
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.get("device")
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.to_owned(),
|
||||
profile: value.as_str().unwrap().to_owned(),
|
||||
},
|
||||
)?))?;
|
||||
stream.close(None)?;
|
||||
stream.flush()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in new issue
Block a user