sway-launchpad

A small rust script to switch sway workspaces using a Launchpad S
git clone git://archive.git.mtrnord.blog/MTRNord/sway-launchpad.git
Log | Files | Refs | LICENSE

commit 7381e34f95b46976a2630c036bac10c14d3b87ab
parent 64c6a344d08963a596503df7011defc231abcffd
Author: MTRNord <mtrnord1@gmail.com>
Date:   Sat,  5 Dec 2020 18:12:29 +0100

feat: Add ability to have multiple actions beyond workspace managment

Took 1 hour 4 minutes

Diffstat:
M.idea/LaunchpadSwayControler.iml | 1+
M.idea/workspace.xml | 61+++++++++++++++++++++++++++++++++++++++++++++++++++----------
MCargo.lock | 11+++++++++++
MCargo.toml | 1+
Mlaunchpad_sway_controler/src/main.rs | 66+++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Alaunchpad_sway_controler/src/plugin_actions/mod.rs | 29+++++++++++++++++++++++++++++
Mlaunchpad_sway_controler/src/sway/mod.rs | 44--------------------------------------------
Alsc_example_plugin/Cargo.toml | 15+++++++++++++++
Alsc_example_plugin/src/main.rs | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 214 insertions(+), 65 deletions(-)

diff --git a/.idea/LaunchpadSwayControler.iml b/.idea/LaunchpadSwayControler.iml @@ -4,6 +4,7 @@ <exclude-output /> <content url="file://$MODULE_DIR$"> <sourceFolder url="file://$MODULE_DIR$/launchpad_sway_controler/src" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/lsc_example_plugin/src" isTestSource="false" /> <excludeFolder url="file://$MODULE_DIR$/target" /> </content> <orderEntry type="inheritedJdk" /> diff --git a/.idea/workspace.xml b/.idea/workspace.xml @@ -2,17 +2,19 @@ <project version="4"> <component name="CargoProjects"> <cargoProject FILE="$PROJECT_DIR$/Cargo.toml" /> + <cargoProject FILE="$PROJECT_DIR$/lsc_example_plugin/Cargo.toml" /> </component> <component name="ChangeListManager"> <list default="true" id="cfc5721f-cc06-4df2-8555-3745919e4c56" name="Default Changelist" comment=""> - <change beforePath="$PROJECT_DIR$/.idea/.gitignore" beforeDir="false" /> - <change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" /> - <change beforePath="$PROJECT_DIR$/.idea/modules.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/modules.xml" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/launchpad_sway_controler/src/plugin_actions/mod.rs" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/lsc_example_plugin/Cargo.toml" afterDir="false" /> + <change afterPath="$PROJECT_DIR$/lsc_example_plugin/src/main.rs" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/.idea/LaunchpadSwayControler.iml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/LaunchpadSwayControler.iml" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/Cargo.lock" beforeDir="false" afterPath="$PROJECT_DIR$/Cargo.lock" afterDir="false" /> <change beforePath="$PROJECT_DIR$/Cargo.toml" beforeDir="false" afterPath="$PROJECT_DIR$/Cargo.toml" afterDir="false" /> - <change beforePath="$PROJECT_DIR$/LaunchpadSwayControler.iml" beforeDir="false" /> - <change beforePath="$PROJECT_DIR$/src/main.rs" beforeDir="false" /> - <change beforePath="$PROJECT_DIR$/src/sway/mod.rs" beforeDir="false" /> + <change beforePath="$PROJECT_DIR$/launchpad_sway_controler/src/main.rs" beforeDir="false" afterPath="$PROJECT_DIR$/launchpad_sway_controler/src/main.rs" afterDir="false" /> + <change beforePath="$PROJECT_DIR$/launchpad_sway_controler/src/sway/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/launchpad_sway_controler/src/sway/mod.rs" afterDir="false" /> </list> <option name="SHOW_DIALOG" value="false" /> <option name="HIGHLIGHT_CONFLICTS" value="true" /> @@ -20,11 +22,13 @@ <option name="LAST_RESOLUTION" value="IGNORE" /> </component> <component name="DarkyenusTimeTracker"> - <option name="totalTimeSeconds" value="4" /> + <option name="totalTimeSeconds" value="4158" /> <option name="idleThresholdMs" value="60000" /> <option name="autoCountIdleSeconds" value="0" /> <option name="stopWhenIdleRatherThanPausing" value="true" /> <option name="pauseOtherTrackerInstances" value="false" /> + <option name="gitIntegration" value="true" /> + <option name="naggedAbout" value="1" /> </component> <component name="Git.Settings"> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> @@ -48,9 +52,21 @@ <property name="project.structure.last.edited" value="Project" /> <property name="project.structure.proportion" value="0.0" /> <property name="project.structure.side.proportion" value="0.2" /> - <property name="settings.editor.selected.configurable" value="language.rust" /> + <property name="settings.editor.selected.configurable" value="language.rust.rustfmt" /> </component> - <component name="RunManager"> + <component name="RunManager" selected="Cargo.Run"> + <configuration name="Run lsc_example_plugin" type="CargoCommandRunConfiguration" factoryName="Cargo Command" temporary="true"> + <option name="channel" value="DEFAULT" /> + <option name="command" value="run --package lsc_example_plugin --bin lsc_example_plugin" /> + <option name="allFeatures" value="false" /> + <option name="emulateTerminal" value="false" /> + <option name="backtrace" value="SHORT" /> + <option name="workingDirectory" value="file://$PROJECT_DIR$/lsc_example_plugin" /> + <envs /> + <method v="2"> + <option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" /> + </method> + </configuration> <configuration name="Run" type="CargoCommandRunConfiguration" factoryName="Cargo Command" nameIsGenerated="true"> <option name="channel" value="DEFAULT" /> <option name="command" value="run -p launchpad_sway_controler" /> @@ -63,9 +79,21 @@ <option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" /> </method> </configuration> + <list> + <item itemvalue="Cargo.Run" /> + <item itemvalue="Cargo.Run lsc_example_plugin" /> + </list> + <recent_temporary> + <list> + <item itemvalue="Cargo.Run lsc_example_plugin" /> + </list> + </recent_temporary> </component> <component name="RustProjectSettings"> + <option name="runRustfmtOnSave" value="true" /> <option name="toolchainHomeDirectory" value="/usr/bin" /> + <option name="useRustfmt" value="true" /> + <option name="version" value="2" /> </component> <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" /> <component name="TaskManager"> @@ -75,10 +103,23 @@ <option name="number" value="Default" /> <option name="presentableId" value="Default" /> <updated>1607182424184</updated> - <workItem from="1607182426972" duration="210000" /> + <workItem from="1607182426972" duration="523000" /> + <workItem from="1607182962786" duration="5115000" /> + </task> + <task id="LOCAL-00001" summary="refactor: Use Workspace setup"> + <created>1607182699156</created> + <option name="number" value="00001" /> + <option name="presentableId" value="LOCAL-00001" /> + <option name="project" value="LOCAL" /> + <updated>1607182699156</updated> </task> + <option name="localTasksCounter" value="2" /> <servers /> </component> + <component name="VcsManagerConfiguration"> + <MESSAGE value="refactor: Use Workspace setup" /> + <option name="LAST_COMMIT_MESSAGE" value="refactor: Use Workspace setup" /> + </component> <component name="XSLT-Support.FileAssociations.UIState"> <expand /> <select /> diff --git a/Cargo.lock b/Cargo.lock @@ -491,6 +491,17 @@ dependencies = [ ] [[package]] +name = "lsc_example_plugin" +version = "0.1.0" +dependencies = [ + "color-eyre", + "futures-util", + "tokio", + "tracing", + "tracing-subscriber", +] + +[[package]] name = "matchers" version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "launchpad_sway_controler", + "lsc_example_plugin" ] [profile.dev.package.backtrace] diff --git a/launchpad_sway_controler/src/main.rs b/launchpad_sway_controler/src/main.rs @@ -1,22 +1,68 @@ -use crate::sway::{ - get_workspaces, listen_for_workspace_changes, LaunchpadWorkspaceMapping, - WorkspaceLaunchpadMapping, -}; +use crate::plugin_actions::PluginActions; +use crate::sway::{get_workspaces, listen_for_workspace_changes}; use color_eyre::Result; use midir::{Ignore, MidiInput, MidiOutputConnection}; use std::convert::{TryFrom, TryInto}; use std::env; use std::io::stdin; use std::process::exit; +use strum::EnumIter; use strum::IntoEnumIterator; use swayipc_async::Connection; use tokio::runtime::Runtime; use tracing::*; +mod plugin_actions; mod sway; +#[non_exhaustive] +#[derive(Debug, Copy, Clone, EnumIter)] +pub enum LaunchpadMapping { + Firefox = 119, + Chats = 118, + Email = 117, + Coding = 116, + Steam = 115, + Games = 114, + Youtube = 113, + Test = 112, +} + +impl Into<PluginActions<'_>> for LaunchpadMapping { + fn into(self) -> PluginActions<'static> { + match self { + LaunchpadMapping::Firefox => PluginActions::SwayWorkspace("1:\u{e007}"), + LaunchpadMapping::Chats => PluginActions::SwayWorkspace("2:\u{f086}"), + LaunchpadMapping::Email => PluginActions::SwayWorkspace("3:\u{f0e0}"), + LaunchpadMapping::Coding => PluginActions::SwayWorkspace("5:\u{f1c9}"), + LaunchpadMapping::Steam => PluginActions::SwayWorkspace("11:\u{f1b6}"), + LaunchpadMapping::Games => PluginActions::SwayWorkspace("12:\u{f11b}"), + LaunchpadMapping::Youtube => PluginActions::SwayWorkspace("13:\u{f167}"), + LaunchpadMapping::Test => PluginActions::ExamplePlugin, + } + } +} + +impl TryFrom<i32> for LaunchpadMapping { + type Error = (); + + fn try_from(input: i32) -> Result<LaunchpadMapping, Self::Error> { + match input { + 119 => Ok(LaunchpadMapping::Firefox), + 118 => Ok(LaunchpadMapping::Chats), + 117 => Ok(LaunchpadMapping::Email), + 116 => Ok(LaunchpadMapping::Coding), + 115 => Ok(LaunchpadMapping::Steam), + 114 => Ok(LaunchpadMapping::Games), + 113 => Ok(LaunchpadMapping::Youtube), + 112 => Ok(LaunchpadMapping::Test), + _ => Err(()), + } + } +} + fn reset_colors(conn_out: &mut MidiOutputConnection) { - for value in WorkspaceLaunchpadMapping::iter() { + for value in LaunchpadMapping::iter() { conn_out .send(&[144, (value as i32).try_into().unwrap(), 15]) .unwrap(); @@ -64,13 +110,11 @@ async fn main() -> Result<()> { move |_, message, _| { if message[2] == 127 { debug!("{:?}", message); - if let Ok(v) = LaunchpadWorkspaceMapping::try_from(message[1] as i32) { + if let Ok(v) = LaunchpadMapping::try_from(message[1] as i32) { rt.block_on(async { - let mut connection = Connection::new().await.unwrap(); - connection - .run_command(format!("workspace --no-auto-back-and-forth {}", v)) - .await - .unwrap(); + if let Err(e) = Into::<PluginActions>::into(v).run().await { + error!("Error while executing action: {}", e); + } }); } } diff --git a/launchpad_sway_controler/src/plugin_actions/mod.rs b/launchpad_sway_controler/src/plugin_actions/mod.rs @@ -0,0 +1,29 @@ +use color_eyre::Result; +use swayipc_async::Connection; +use tokio::io::AsyncWriteExt; +use tokio::net::UnixStream; + +pub enum PluginActions<'a> { + SwayWorkspace(&'a str), + ExamplePlugin, +} + +impl PluginActions<'_> { + pub async fn run(&self) -> Result<()> { + match self { + PluginActions::SwayWorkspace(workspace) => { + let mut connection = Connection::new().await?; + connection + .run_command(format!("workspace --no-auto-back-and-forth {}", workspace)) + .await + .unwrap(); + } + PluginActions::ExamplePlugin => { + let mut socket = UnixStream::connect("/run/lsc_example_plugin.sock").await?; + + socket.write(b"test\n").await?; + } + } + Ok(()) + } +} diff --git a/launchpad_sway_controler/src/sway/mod.rs b/launchpad_sway_controler/src/sway/mod.rs @@ -3,8 +3,6 @@ use color_eyre::Result; use futures_util::stream::StreamExt; use midir::MidiOutput; use std::convert::{TryFrom, TryInto}; -use std::fmt::{Display, Formatter}; -use strum::EnumIter; use swayipc_async::{Connection, Event, EventType, WorkspaceChange}; use tracing::*; @@ -31,48 +29,6 @@ pub async fn get_workspaces(connection: &mut Connection) -> Result<()> { #[non_exhaustive] #[derive(Debug, Copy, Clone)] -pub enum LaunchpadWorkspaceMapping { - Firefox, - Chats, - Email, - Coding, - Steam, - Games, - Youtube, -} - -impl Display for LaunchpadWorkspaceMapping { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Self::Firefox => write!(f, "1:\u{e007}"), - Self::Chats => write!(f, "2:\u{f086}"), - Self::Email => write!(f, "3:\u{f0e0}"), - Self::Coding => write!(f, "5:\u{f1c9}"), - Self::Steam => write!(f, "11:\u{f1b6}"), - Self::Games => write!(f, "12:\u{f11b}"), - Self::Youtube => write!(f, "13:\u{f167}"), - } - } -} - -impl TryFrom<i32> for LaunchpadWorkspaceMapping { - type Error = (); - - fn try_from(input: i32) -> Result<LaunchpadWorkspaceMapping, Self::Error> { - match input { - 119 => Ok(LaunchpadWorkspaceMapping::Firefox), - 118 => Ok(LaunchpadWorkspaceMapping::Chats), - 117 => Ok(LaunchpadWorkspaceMapping::Email), - 116 => Ok(LaunchpadWorkspaceMapping::Coding), - 115 => Ok(LaunchpadWorkspaceMapping::Steam), - 114 => Ok(LaunchpadWorkspaceMapping::Games), - 113 => Ok(LaunchpadWorkspaceMapping::Youtube), - _ => Err(()), - } - } -} -#[non_exhaustive] -#[derive(Debug, Copy, Clone, EnumIter)] pub enum WorkspaceLaunchpadMapping { Firefox = 119, Chats = 118, diff --git a/lsc_example_plugin/Cargo.toml b/lsc_example_plugin/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "lsc_example_plugin" +version = "0.1.0" +authors = ["MTRNord <mtrnord1@gmail.com>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tracing = "0.1" +tracing-subscriber = "0.2" +tokio = { version = "0.3.2", features = ["full"] } +futures-util = "0.3.7" +color-eyre = "0.5" +\ No newline at end of file diff --git a/lsc_example_plugin/src/main.rs b/lsc_example_plugin/src/main.rs @@ -0,0 +1,51 @@ +use color_eyre::Result; +use futures_util::StreamExt; +use std::os::unix::fs::PermissionsExt; +use std::path::Path; +use std::{env, fs}; +use tokio::io::{AsyncBufReadExt, BufStream}; +use tokio::net::UnixListener; +use tracing::*; + +#[tokio::main] +async fn main() -> Result<()> { + // Enable the logging crates + color_eyre::install()?; + if let Err(_) = env::var("RUST_LOG") { + env::set_var("RUST_LOG", "INFO"); + } + tracing_subscriber::fmt::init(); + + let socket_path = Path::new("/run/lsc_example_plugin.sock"); + if socket_path.exists() { + fs::remove_file(socket_path)?; + } + + // First we create a unix socket. This is required to be a) The same as the crate name and b) to be in /run + let mut listener = UnixListener::bind(socket_path).unwrap(); + + let mut perms = fs::metadata(socket_path)?.permissions(); + perms.set_mode(0o666); + fs::set_permissions(socket_path, perms)?; + // This listens for new connections + while let Some(stream) = listener.next().await { + match stream { + Ok(stream) => { + tokio::spawn(async move { + println!("new client!"); + let buffer = BufStream::new(stream); + let mut lines = buffer.lines(); + while let Ok(line) = lines.next_line().await { + if let Some(line) = line { + println!("{}", line); + } + } + }); + } + Err(e) => { + error!("Unix socket connection failed: {}", e); + } + } + } + Ok(()) +}