prepping for release
This commit is contained in:
parent
907daea38e
commit
4bf8b57d5f
18 changed files with 885 additions and 814 deletions
33
Cargo.lock
generated
33
Cargo.lock
generated
|
|
@ -1808,9 +1808,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "swayipc"
|
||||
version = "3.0.3"
|
||||
version = "4.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b8c50cb2e98e88b52066a35ef791fffd8f6fa631c3a4983de18ba41f718c736"
|
||||
checksum = "7fd6ee13016b1ae2fbf741ddb2133d983a3fecdc34686eec6202e2a80612d82f"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
@ -1819,13 +1819,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "swayipc-types"
|
||||
version = "1.4.2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4f6205b8f8ea7cd6244d76adce0a0f842525a13c47376feecf04280bda57231"
|
||||
checksum = "a29165dc67819a052bd67962b6c9de7c5d4461f0e8d224eb0f7cdd26cf05a650"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1955,9 +1955,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.23"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
|
||||
checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
|
|
@ -1967,33 +1967,26 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.11"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
||||
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.27"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_write",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_write"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.41"
|
||||
|
|
@ -2530,9 +2523,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
|||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.13"
|
||||
version = "0.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
|
||||
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ serde = "1.0.219"
|
|||
serde_json = "1.0.142"
|
||||
sha2 = "0.10.9"
|
||||
shellexpand = "3.1.1"
|
||||
swayipc = "3.0.3"
|
||||
swayipc = "4.0.0"
|
||||
systemctl = "0.5.0"
|
||||
tokio = "1.47.1"
|
||||
walkdir = "2.5.0"
|
||||
|
|
|
|||
49
README.md
49
README.md
|
|
@ -1,6 +1,9 @@
|
|||
# sway-de-utils
|
||||
This is a tool heavily targeting my own personal use-cases.
|
||||
It is a collection of utility commands which primarily serve to manage a multiple-workspace setup in Sway.
|
||||
|
||||
SDU is a tool heavily targeting my own personal use-cases. It is a collection of utility commands which primarily serve to manage a multiple-workspace setup in Sway.
|
||||
|
||||
*<center>Made with no "vibe coding"</center>*
|
||||
*<center>Fuck you, learn how to have fun</center>*
|
||||
|
||||
**This software is unfinished and in early development. It is not ready for use, and its behavior is subject to change drastically between now and release.**
|
||||
|
||||
|
|
@ -18,7 +21,7 @@ todo
|
|||
```
|
||||
## Usage
|
||||
|
||||
`sway-profiles-rs --help` for now
|
||||
`sdu --help` for now
|
||||
|
||||
## FAQ
|
||||
|
||||
|
|
@ -28,45 +31,15 @@ For my uses, yeah
|
|||
|
||||
> Is it fast?
|
||||
|
||||
Probably, idk man don't judge
|
||||
Probably, idk girl don't judge
|
||||
|
||||
> Is it good?
|
||||
|
||||
Probably not
|
||||
Probably not, if you:
|
||||
* have opinions
|
||||
* know what a computer is
|
||||
|
||||
## todo
|
||||
|
||||
* better failure/error handling
|
||||
* daemonize?
|
||||
|
||||
## sway-profiles parity
|
||||
|
||||
* <span style="color:red"></span> sp-clipboard - not implemented, consider using something like `clipvault list | head -n10 | sed -e 's/^\w*\s*//'`
|
||||
* <span style="color:green"></span> sp-launcher - implemented
|
||||
* `sdu launch --program [program]`
|
||||
* <span style="color:green"></span> sp-powermenu - implemented
|
||||
* `sdu power [shutdown|reboot|suspend|lock|logout]`
|
||||
* <span style="color:green"></span> sp-profile-icon - implemented
|
||||
* `sdu profile get (--monitor) icon`
|
||||
* <span style="color:red"></span> sp-rename - not implemented
|
||||
* <span style="color:red"></span> sp-shortcuts - not yet implemented, plan to do so as `sdu shortcuts ...`
|
||||
* <span style="color:red"></span> sp-global-shortcuts - not yet implemented, plan to do so as `sdu shortcuts -g ...`
|
||||
* <span style="color:green"></span> sp-lock - implemented
|
||||
* `sdu lock`
|
||||
* <span style="color:yellow"></span> sp-profile - partially implemented
|
||||
* `sdu profile get (--monitor) name`
|
||||
* <span style="color:green"></span> sp-profiles - implemented
|
||||
* `sdu profile init`
|
||||
* `sdu profile switch [next|prev]`
|
||||
* `sdu profile switch to (--name|--index) [name or index]`
|
||||
* <span style="color:red"></span> sp-screenshot - not implemented, consider using [ferrishot](https://github.com/nik-rev/ferrishot) instead
|
||||
* <span style="color:red"></span> sp-wallpaper - not planned, use [multibg-wayland](https://github.com/gergo-salyi/multibg-wayland) instead
|
||||
|
||||
|
||||
## Exit Codes (reference)
|
||||
|
||||
* 0: success
|
||||
* 1: generic
|
||||
* 2: sway ipc/exec failure
|
||||
* 3: bad configuration
|
||||
* 4: bad cli args
|
||||
* daemonize?
|
||||
16
docs/sway-profiles-parity.md
Normal file
16
docs/sway-profiles-parity.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# sway-profiles parity
|
||||
|
||||
If you used [the original version of this project](https://git.pogmom.me/pogmommy/sway-profiles) (unlikely, it was basically just a bunch of messily packaged scripts) then you might be interested in how sway-desktop-utils provides the information/functionality of sway-profiles. This table was mostly used to prep alpha development for release.
|
||||
|
||||
|sway-profiles|status|sway-desktop-utils|
|
||||
|-|-|-|
|
||||
|`sp-launcher`|<span style="color:green"> implemented</span>|`sdu launch --program <program>`
|
||||
|`sp-powermenu`|<span style="color:yellow"> partially implemented</span>|`sdu power [shutdown\|reboot\|suspend\|lock\|logout]` <hr>Currently prints power options in dmenu-compatible-ish list. Could be wrapped in script and passed back to self|
|
||||
|`sp-rename`|<span style="color:orange"> not planned for initial release</span>|`sdu workspace rename` maybe?|
|
||||
|`sp-shortcuts`<hr>`sp-global-shortcuts`|<span style="color:green"> implemented</span>|`sdu [profile] shortcuts get [--mode dmenu\|{json}]`<br>`sdu profile shortcuts exec <shortcut_name>`|
|
||||
|`sp-lock`|<span style="color:green"> implemented</span>|`sdu lock [--force-render-background]`|
|
||||
|`sp-profile`<hr>`sp-profile-icon`|<span style="color:green"> implemented</span>|`sdu profile get [--monitor] [name\|icon\|json]`<br>`sdu profile shortcuts`|
|
||||
|`sp-profiles`|<span style="color:green"> implemented</span>|`sdu profile init`<br>`sdu profile switch <next\|prev>`<br>`sdu profile switch to [--name\|--index] <name or index>`|
|
||||
|`sp-screenshot`|<span style="color:red"> not planned</span>|consider using [ferrishot](https://github.com/nik-rev/ferrishot) instead|
|
||||
|`sp-clipboard`|<span style="color:red"> not planned</span>|consider using [clipvault](https://github.com/densumesh/clip-vault) instead, something like:<br>`clipvault list \| head -n10 \| sed -e 's/^\w*\s*//'`|
|
||||
|`sp-wallpaper`|<span style="color:red"> not planned</span>|consider using [multibg-wayland](https://github.com/gergo-salyi/multibg-wayland) instead|
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::HashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize,Deserialize,Clone)]
|
||||
#[derive(Serialize,Deserialize,Clone, Debug)]
|
||||
pub struct WindowIcon {
|
||||
pub icon: String,
|
||||
pub substring: String
|
||||
|
|
@ -31,17 +31,18 @@ pub struct ScriptConf {
|
|||
pub icon: String,
|
||||
pub command: String
|
||||
}
|
||||
#[derive(Serialize,Deserialize,Clone)]
|
||||
#[derive(Serialize,Deserialize,Clone,Debug)]
|
||||
pub struct Config {
|
||||
// pub title_length: Option<usize>,
|
||||
pub index_starts_at_1: bool,
|
||||
pub initial_workspace: usize,
|
||||
pub preserve_keyboard_order: bool,
|
||||
// pub initial_workspace: Option<usize>,
|
||||
pub window_icons: Vec<WindowIcon>,
|
||||
pub programs: HashMap<String, Programs>,
|
||||
pub lock: LockConf,
|
||||
pub profiles: Vec<Profile>,
|
||||
pub scripts: Vec<ScriptConf>,
|
||||
}
|
||||
|
||||
impl ::std::default::Default for Config {
|
||||
fn default() -> Self { Self { index_starts_at_1: false, initial_workspace: 1, window_icons: vec![], programs: HashMap::new(), lock: LockConf { wallpaper_path: None, blur: 10.0, scale: 0.75 }, profiles: vec![] } }
|
||||
fn default() -> Self { Self { preserve_keyboard_order: false, window_icons: vec![], programs: HashMap::new(), lock: LockConf { wallpaper_path: None, blur: 10.0, scale: 0.75 }, profiles: vec![], scripts: vec![] } }
|
||||
}
|
||||
217
src/lib/cli.rs
Normal file
217
src/lib/cli.rs
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
use clap::{
|
||||
ArgAction,
|
||||
Parser,
|
||||
Subcommand, ValueEnum
|
||||
};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(version, about, long_about = None)]
|
||||
pub struct Cli {
|
||||
/// Disable truncation of window titles
|
||||
#[arg(short = 'T', long = "no-truncate-title", action = ArgAction::SetTrue)]
|
||||
no_truncate_title: Option<bool>,
|
||||
|
||||
/// Enables monitoring for supported event types
|
||||
#[arg(short = 'm', long = "monitor", action = ArgAction::SetTrue)]
|
||||
monitor: Option<bool>,
|
||||
|
||||
/// Turn debugging information on
|
||||
#[arg(short, long, action = ArgAction::Count)]
|
||||
debug: u8,
|
||||
|
||||
#[command(subcommand)]
|
||||
pub(crate) command: Commands,
|
||||
// command: Commands,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum Commands {
|
||||
/// Prints Information about Sway Environment
|
||||
#[clap(alias = "s")]
|
||||
Sway {
|
||||
#[command(subcommand)]
|
||||
sway_command: SwayCommand,
|
||||
},
|
||||
/// Launch Program with Current Workspace's Profile Configuration
|
||||
#[clap(alias = "L")]
|
||||
Launch {
|
||||
#[arg(short, long)]
|
||||
program: String
|
||||
},
|
||||
/// Set up blurred wallpaper for screen lock, and locks screen
|
||||
#[clap(alias = "l")]
|
||||
Lock {
|
||||
#[arg(short, long, action = ArgAction::SetTrue)]
|
||||
force_render_background: Option<bool>,
|
||||
},
|
||||
/// Profile Management and Information,
|
||||
#[clap(alias = "p")]
|
||||
Profile {
|
||||
#[command(subcommand)]
|
||||
profile_command: ProfileCommand,
|
||||
},
|
||||
/// Lists global/profile shortcuts and optionally executes them
|
||||
#[clap(alias = "scut")]
|
||||
Shortcuts {
|
||||
/// List shortcuts from global shortcut list rather than
|
||||
// #[arg(short, long, action = ArgAction::SetTrue)]
|
||||
// global: Option<bool>,
|
||||
#[command(subcommand)]
|
||||
shortcut_command: ShortcutCommand,
|
||||
// /// Execute foud command
|
||||
// #[arg(short, long, action = ArgAction::SetTrue)]
|
||||
// execute: bool,
|
||||
// /// Mode to print shortcut information
|
||||
// #[command(subcommand)]
|
||||
// mode: Option<ShortcutMode>
|
||||
|
||||
},
|
||||
/// List or Execute Power/Session Commands
|
||||
#[clap(alias = "pow")]
|
||||
Power {
|
||||
#[command(subcommand)]
|
||||
power_command: Option<PowerCommand>,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Subcommand)]
|
||||
pub enum ShortcutCommand {
|
||||
/// Execute command by passed name
|
||||
#[clap(alias = "exec")]
|
||||
Execute {
|
||||
shortcut_name: String
|
||||
},
|
||||
/// Mode to print shortcut information
|
||||
#[clap(alias = "g")]
|
||||
Get {
|
||||
#[arg(short,long)]
|
||||
mode: Option<ShortcutMode>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, ValueEnum)]
|
||||
#[derive(Subcommand)]
|
||||
pub enum ShortcutMode {
|
||||
/// Print formatted for dmenu/wofi
|
||||
#[clap(alias = "d")]
|
||||
Dmenu,
|
||||
/// Print formatted as json/for eww
|
||||
#[clap(alias = "j")]
|
||||
Json
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum SwayCommand {
|
||||
#[clap(alias = "g")]
|
||||
Get {
|
||||
/// Enable Monitoring for Sway IPC Events
|
||||
#[arg(short, long, action = ArgAction::SetTrue)]
|
||||
monitor: Option<bool>,
|
||||
/// Get Sway Environment Information
|
||||
#[command(subcommand)]
|
||||
get_command: Option<SwayGetCommand>,
|
||||
},
|
||||
}
|
||||
#[derive(Subcommand)]
|
||||
pub enum SwayGetCommand {
|
||||
/// Get Workspace Information
|
||||
#[clap(alias = "ws")]
|
||||
Workspaces,
|
||||
/// Get Window Information
|
||||
#[clap(alias = "n")]
|
||||
Window,
|
||||
/// Get Window and Workspace Information in one JSON output
|
||||
#[clap(alias = "f")]
|
||||
Full
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum PowerCommand {
|
||||
/// Powers down the system
|
||||
#[clap(alias = "s")]
|
||||
Shutdown,
|
||||
/// Reboots the system
|
||||
#[clap(alias = "r")]
|
||||
Reboot,
|
||||
/// Suspends (idles) the system
|
||||
#[clap(alias = "i")]
|
||||
Suspend,
|
||||
/// Locks the system
|
||||
#[clap(alias = "l")]
|
||||
Lock {
|
||||
#[arg(short, long, action = ArgAction::SetTrue)]
|
||||
force_render_background: Option<bool>,
|
||||
},
|
||||
/// Logs out of (exits) the session
|
||||
#[clap(alias = "e")]
|
||||
Logout,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum ProfileCommand {
|
||||
/// Switch Profiles
|
||||
#[clap(alias = "s")]
|
||||
Switch {
|
||||
#[command(subcommand)]
|
||||
switch_command: Option<ProfileSwitchCommand>,
|
||||
},
|
||||
/// Get Profile Information
|
||||
#[clap(alias = "g")]
|
||||
Get {
|
||||
/// Monitor Profile Information
|
||||
#[arg(short, long, action = ArgAction::SetTrue)]
|
||||
monitor: Option<bool>,
|
||||
|
||||
#[command(subcommand)]
|
||||
get_command: Option<ProfileGetCommand>,
|
||||
},
|
||||
/// Get profile shortcut and optionall execute them
|
||||
#[clap(alias = "scut")]
|
||||
Shortcuts {
|
||||
#[command(subcommand)]
|
||||
shortcut_command: ShortcutCommand,
|
||||
},
|
||||
/// Initialize Sway-DE-Utils
|
||||
#[clap(alias = "i")]
|
||||
Init
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum ProfileSwitchCommand {
|
||||
/// Switch to Next Profile
|
||||
#[clap(alias = "n")]
|
||||
Next,
|
||||
/// Switch to Previous Profile
|
||||
#[clap(alias = "p")]
|
||||
Prev,
|
||||
/// Switch to Profile by Name or Index
|
||||
#[clap(alias = "t")]
|
||||
To {
|
||||
/// Explicitly Specify Profile by Name
|
||||
#[clap(group = "switch_to")]
|
||||
#[arg(short, long)]
|
||||
name: Option<String>,
|
||||
/// Explicitly Specify Profile by Index
|
||||
#[clap(group = "switch_to")]
|
||||
#[arg(short, long)]
|
||||
index: Option<usize>,
|
||||
/// Target Profile Name or Index
|
||||
query: Option<String>
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Subcommand)]
|
||||
pub enum ProfileGetCommand {
|
||||
/// Get Profile Details in JSON Format
|
||||
#[clap(alias = "j")]
|
||||
Json,
|
||||
/// Get Profile Name in Plaintext
|
||||
#[clap(alias = "n")]
|
||||
Name,
|
||||
/// Get Profile Icon in Plaintext
|
||||
#[clap(alias = "i")]
|
||||
Icon
|
||||
}
|
||||
|
|
@ -1,37 +1,32 @@
|
|||
//use crate::config::Profile;
|
||||
use crate::{config::{Config,Profile}, profile::{active_profile_index,profile_from_index}, ErrorMessage, ProfileGetCommand};
|
||||
//use crate::utils::config::c;
|
||||
use serde_json::json;
|
||||
use watchexec::Watchexec;
|
||||
use watchexec_events::Tag;
|
||||
use miette::{IntoDiagnostic, Result};
|
||||
use {
|
||||
crate::{
|
||||
cli::ProfileGetCommand, config::Profile, profile::{
|
||||
active_profile_index,
|
||||
profile_from_index
|
||||
}
|
||||
}, miette::{
|
||||
IntoDiagnostic,
|
||||
Result
|
||||
}, serde_json::json, watchexec::Watchexec, watchexec_events::Tag
|
||||
};
|
||||
|
||||
pub fn profile_info(profiles: Vec<Profile>,info: ProfileGetCommand) -> Result<String,ErrorMessage> {
|
||||
match active_profile_index() {
|
||||
Ok(i) => {
|
||||
match profile_from_index(profiles, i) {
|
||||
Ok(p) => {
|
||||
match info {
|
||||
ProfileGetCommand::Json => Ok(json!(p).to_string()),
|
||||
ProfileGetCommand::Name => Ok(p.name),
|
||||
ProfileGetCommand::Icon => Ok(p.icon),
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
pub fn profile_info(profiles: Vec<Profile>,info: ProfileGetCommand) -> String {
|
||||
let active_profile = profile_from_index(profiles, active_profile_index());
|
||||
match info {
|
||||
ProfileGetCommand::Json => json!(active_profile).to_string(),
|
||||
ProfileGetCommand::Name => active_profile.name,
|
||||
ProfileGetCommand::Icon => active_profile.icon,
|
||||
//ProfileGetCommand::Shortcuts => json!(active_profile.scripts).to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn watch(config: Config,info: ProfileGetCommand,watch_path: String) -> Result<()> {
|
||||
let profiles = config.profiles;
|
||||
pub async fn watch(profiles_config: Vec<Profile>,info: ProfileGetCommand,watch_path: String) -> Result<()> {
|
||||
let wx = Watchexec::new(move |mut action| {
|
||||
//yeah, this is... fine
|
||||
action.events.iter().find(|_event|_event.tags.iter().any(|tag| match tag {
|
||||
Tag::FileEventKind(watchexec_events::filekind::FileEventKind::Access(watchexec_events::filekind::AccessKind::Close(watchexec_events::filekind::AccessMode::Write))) => {
|
||||
println!("{}",profile_info(profiles.clone(),info).unwrap_or_default());
|
||||
println!("{}",profile_info(profiles_config.clone(),info.clone()));
|
||||
true
|
||||
},
|
||||
_ => false
|
||||
|
|
|
|||
|
|
@ -1,6 +1,19 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::{config::{Profile, Programs}, profile::active_profile, ErrorMessage};
|
||||
use log::debug;
|
||||
|
||||
use crate::{SDUError, config::{Profile, Programs}, profile::active_profile, sway_ipc::{get_sway_connection, run_sway_command}};
|
||||
|
||||
pub fn launch( exec: &String, profiles: Vec<Profile>, programs: HashMap<String, Programs> ) -> Result<(), SDUError> {
|
||||
debug!("{}",exec);
|
||||
match profile_launch(profiles, programs, exec) {
|
||||
Ok(p) => {
|
||||
debug!("{}",p);
|
||||
run_sway_command(&mut get_sway_connection(),p)
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append_arguments(mut command: String, args: Vec<String>) -> String {
|
||||
for a in args {
|
||||
|
|
@ -9,7 +22,7 @@ pub fn append_arguments(mut command: String, args: Vec<String>) -> String {
|
|||
command
|
||||
}
|
||||
|
||||
pub fn profile_launch(profiles: Vec<Profile>, programs_config: HashMap<String, Programs>, program_arg: &String) -> Result<String,ErrorMessage> {
|
||||
pub fn profile_launch(profiles: Vec<Profile>, programs_config: HashMap<String, Programs>, program_arg: &String) -> Result<String,SDUError> {
|
||||
match programs_config.iter().find(|x|x.0.eq(program_arg)) {
|
||||
Some(p) => {
|
||||
let mut swaymsg_command = format!("exec {}", p.1.command);
|
||||
|
|
@ -26,7 +39,7 @@ pub fn profile_launch(profiles: Vec<Profile>, programs_config: HashMap<String, P
|
|||
}
|
||||
},
|
||||
None => {
|
||||
Err(ErrorMessage { message: String::from("No matching program found"), code: 3 })
|
||||
Err(SDUError { message: String::from("No matching program found") })
|
||||
},
|
||||
}
|
||||
}
|
||||
251
src/lib/lock.rs
251
src/lib/lock.rs
|
|
@ -1,15 +1,15 @@
|
|||
use std::{env::consts, fs::{self, DirBuilder, exists, write}, io, ops::Sub, os::unix::fs::DirBuilderExt, path::{Path, PathBuf}, vec};
|
||||
use std::{env::consts, fs::{self, DirBuilder, write}, io, path::PathBuf, vec};
|
||||
use log::debug;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use image::{imageops::GaussianBlurParameters, DynamicImage};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use sha2::{Sha256, Digest};
|
||||
use swayipc::Connection;
|
||||
use xdg::BaseDirectories;
|
||||
use shellexpand::tilde;
|
||||
|
||||
use crate::{ErrorMessage, config::{Config, LockConf}, error_handler};
|
||||
use crate::{DirectoryType, SDUError, config::LockConf, sway_ipc::get_sway_connection};
|
||||
|
||||
#[derive(Serialize,Deserialize,Debug)]
|
||||
pub struct WallpaperCache {
|
||||
|
|
@ -17,6 +17,82 @@ pub struct WallpaperCache {
|
|||
pub hash: String
|
||||
}
|
||||
|
||||
pub fn lock(lock_config: LockConf,force_bg_render: bool) -> Result<(),SDUError> {
|
||||
let mut sway_connection = get_sway_connection();
|
||||
match &lock_config.wallpaper_path {
|
||||
Some(wallpaper_path) => {
|
||||
let wallpaper_root_path = PathBuf::from(tilde(&wallpaper_path).to_string());
|
||||
let wp_cache_dir = get_path(DirectoryType::Cache, "sway-profiles-rs/lock/");
|
||||
|
||||
DirBuilder::new().recursive(true).create(&wp_cache_dir).expect("Failed to create cache directory");
|
||||
|
||||
let wp_hash_json = wallpaper_check_hash(wp_cache_dir.join("hashes.json"));
|
||||
|
||||
debug!("{:?}",wp_hash_json);
|
||||
|
||||
let mut wp_hash_array: Vec<WallpaperCache> = vec![];
|
||||
|
||||
let sway_visible_workspaces = match sway_connection.get_workspaces() {
|
||||
Ok(w) => w.iter().filter(|w|w.visible).cloned().collect(),
|
||||
Err(_) => vec![],
|
||||
};
|
||||
for workspace in sway_visible_workspaces {
|
||||
debug!("operating on workspace {} ({})",workspace.num, workspace.name);
|
||||
let wp_output_path = wallpaper_root_path.join(&workspace.output);
|
||||
let output_image_path = wp_output_path.join(format!("{:0>2}.jpg",workspace.num));
|
||||
let mut wp_sha256 = Sha256::new();
|
||||
let img_file_data = match fs::File::open(&output_image_path) {
|
||||
Ok(mut img_file_data) => Ok(io::copy(&mut img_file_data, &mut wp_sha256).unwrap_or_default()),
|
||||
Err(_) => Err(SDUError { message: format!("Could not open wallpaper image for output {}", workspace.output) }),
|
||||
};
|
||||
if img_file_data.is_ok() {
|
||||
let wp_hash = hex::encode(wp_sha256.finalize());
|
||||
let wp_image = image::open(output_image_path).expect("Could not open wallpaper file as image");
|
||||
let output_cache_image_path = wp_cache_dir.join(format!("{}.jpg",workspace.output));
|
||||
|
||||
match wp_hash_json.iter().find(|wc|wc.display == workspace.output) {
|
||||
Some (saved_hash) => {
|
||||
if !(saved_hash.hash == wp_hash && output_cache_image_path.exists()) || force_bg_render {
|
||||
generate_image(wp_image,output_cache_image_path, lock_config.clone())
|
||||
};
|
||||
},
|
||||
None => generate_image(wp_image, output_cache_image_path, lock_config.clone())
|
||||
}
|
||||
|
||||
wp_hash_array.push(WallpaperCache { display: workspace.output, hash: wp_hash });
|
||||
};
|
||||
};
|
||||
|
||||
debug!("{:?}",wp_hash_array);
|
||||
|
||||
let new_json = json!(wp_hash_array);
|
||||
let _ = write(wp_cache_dir.join("hashes.json"), new_json.to_string());
|
||||
|
||||
let gtklock_modules_dir = format!("/usr/lib/{}-linux-gnu/gtklock/",consts::ARCH);//.to_owned() + consts::ARCH + "-linux-gnu/gtklock/";
|
||||
let gktlock_modules_path = PathBuf::from(gtklock_modules_dir);
|
||||
let mut gtklock_args: Vec<String> = vec![];
|
||||
|
||||
for gtklock_module in WalkDir::new(gktlock_modules_path).into_iter().filter(|x|x.as_ref().expect("TODO").file_name().to_str().unwrap_or_default().ends_with("-module.so")) {
|
||||
let gtklock_module_path = gtklock_module.expect("TODO").path().to_string_lossy().to_string();
|
||||
debug!("Foud gtklock module: {}", gtklock_module_path);
|
||||
gtklock_args.push("-m".to_owned());
|
||||
gtklock_args.push(gtklock_module_path);
|
||||
}
|
||||
let mut gtklock_command = "exec gtklock".to_owned();
|
||||
for gtklock_arg in gtklock_args {
|
||||
gtklock_command = gtklock_command + " " + >klock_arg;
|
||||
}
|
||||
debug!("{}", gtklock_command);
|
||||
|
||||
match sway_connection.run_command(gtklock_command) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(SDUError { message: "Could not lock screen!".to_owned() }),
|
||||
}
|
||||
},
|
||||
None => Err(SDUError { message: "Wallpaper path is not set!".to_owned() }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_image(orig_img_data: DynamicImage,gen_image_path: PathBuf, lock_conf: LockConf) {// Result<(), ImageError> {
|
||||
//let img_data = image::open(image_path).unwrap();
|
||||
let w = orig_img_data.width() as f32 * lock_conf.scale;
|
||||
|
|
@ -31,60 +107,10 @@ pub fn generate_image(orig_img_data: DynamicImage,gen_image_path: PathBuf, lock_
|
|||
}
|
||||
}
|
||||
|
||||
pub fn wallpaper_setup() -> Result<(),ErrorMessage> {
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn wallpaper_check_hash(hashes_json_path: PathBuf) -> Vec<WallpaperCache> {
|
||||
//let mut wallpaper_hashes: Vec<WallpaperCache> = vec![];
|
||||
//let mut wallpaper_hashes: Vec<WallpaperCache> = if hashes_json_path.exists().then(||{
|
||||
|
||||
/*let wallpaper_hashes: Vec<WallpaperCache> = fs::File::open(&hashes_json_path).map(|f| -> Vec<WallpaperCache> {match serde_json::from_reader(f) {
|
||||
Ok(p) => p,
|
||||
Err(_) => vec![],
|
||||
}}).unwrap(); //.or_else(|_|Ok(vec![])).expect("Wallpaper cache vec should be empty if it can't be mapped");
|
||||
*/
|
||||
let wallpaper_hashes: Vec<WallpaperCache> = fs::File::open(hashes_json_path).ok()
|
||||
.and_then(|f| serde_json::from_reader(f).ok()).unwrap_or(vec![]);
|
||||
|
||||
todo!();
|
||||
/* let wallpaper_hashes = match fs::File::open(hashes_json_path).and_then(|f| Ok(serde_json::from_reader(f))) {
|
||||
Ok(o) => {
|
||||
let t = o.unwrap();
|
||||
},
|
||||
Err(_) => vec![],
|
||||
};*/
|
||||
/* let mut wallpaper_hashes = match fs::File::open(hashes_json_path) {
|
||||
Ok(f) => {
|
||||
let json = serde_json::from_reader(f).unwrap_or_default();
|
||||
}
|
||||
Err(_) => todo!(),
|
||||
};*/
|
||||
// let mut wallpaper_hashes: Vec<WallpaperCache> = hashes_json_path.try_exists();
|
||||
// exists().then_some(||{
|
||||
|
||||
todo!();
|
||||
// }); //.unwrap_or(vec![])
|
||||
|
||||
/*if hashes_json_path.exists() {
|
||||
serde_json::from_reader(fs::File::open(hashes_json_path))?
|
||||
} else { vec![] };*/ /*{
|
||||
match fs::File::open(hashes_json_path) {
|
||||
Ok(hash_file) => serde_json::from_reader(hash_file).unwrap_or(vec![]),
|
||||
Err(_) => vec![],
|
||||
}
|
||||
} else { vec![] };*/
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn wallpaper_hash_write() -> Result<(),ErrorMessage>{
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub enum DirectoryType {
|
||||
Cache,
|
||||
Config
|
||||
wallpaper_hashes
|
||||
}
|
||||
|
||||
pub fn get_path(dir_type: DirectoryType, suffix: &str) -> PathBuf {
|
||||
|
|
@ -95,120 +121,3 @@ pub fn get_path(dir_type: DirectoryType, suffix: &str) -> PathBuf {
|
|||
}.expect("HOME could not be found");
|
||||
base_dir.join(suffix)
|
||||
}
|
||||
|
||||
pub fn lock_screen(config: &Config, mut sway_connection: Connection, force_bg_render: bool) -> Result<Vec<Result<(), swayipc::Error>>, swayipc::Error> {
|
||||
match &config.lock.wallpaper_path {
|
||||
Some(wallpaper_path) => {
|
||||
let wallpaper_root_path = PathBuf::from(tilde(&wallpaper_path).to_string());
|
||||
let wp_cache_dir = get_path(DirectoryType::Cache, "sway-profiles-rs/lock/");
|
||||
|
||||
DirBuilder::new().recursive(true).create(wp_cache_dir).expect("Failed to create cache directory");
|
||||
|
||||
let mut wp_hash_json = wallpaper_check_hash(wp_cache_dir.join("hashes.json"));
|
||||
|
||||
//let mut wp_hash_array: Vec<WallpaperCache> = vec![];
|
||||
match sway_connection.get_outputs() {
|
||||
Ok(outputs) => {
|
||||
for output in outputs {
|
||||
match sway_connection.get_workspaces() {
|
||||
Ok(workspaces) => {
|
||||
let current_workspace = match output.current_workspace.clone() {
|
||||
Some(current_workspace_name) => current_workspace_name,
|
||||
None => todo!(),
|
||||
};
|
||||
let output_workspace = match workspaces.iter().find(|x|x.name == current_workspace) {
|
||||
Some(output_workspace) => output_workspace,
|
||||
None => todo!(),
|
||||
};
|
||||
format!("{:0>2}",output_workspace.num.sub(1)).chars().next().unwrap_or_default();
|
||||
let output_ws_num = match format!("{:0>2}",output_workspace.num.sub(1)).chars().next() {
|
||||
Some(output_ws_num) => output_ws_num,
|
||||
None => todo!(),
|
||||
};
|
||||
let image_path = wallpaper_root_path.join(output.name.clone()).join(format!("p_{}.jpg",output_ws_num));
|
||||
let mut img_file_data = match fs::File::open(&image_path) {
|
||||
Ok(img_file_data) => img_file_data,
|
||||
Err(_) => todo!(),
|
||||
};
|
||||
let mut sha256 = Sha256::new();
|
||||
match io::copy(&mut img_file_data, &mut sha256) {
|
||||
Ok(_) => {
|
||||
let hash = hex::encode(sha256.finalize());
|
||||
wp_hash_array.push(WallpaperCache { display: output.name, hash });
|
||||
let wallpaper_data = match image::open(&image_path) {
|
||||
Ok(wallpaper_data) => wallpaper_data,
|
||||
Err(_) => todo!(),
|
||||
};
|
||||
let wallpaper_cache_path = match cache_dir.as_ref() {
|
||||
Some(wallpaper_cache_path) => wallpaper_cache_path.join(format!("sway-profiles-rs/lock/{}.jpg",output.name)),
|
||||
None => todo!(),
|
||||
};
|
||||
match wp_hash_json.iter().find(|x|x.display == output.name) {
|
||||
Some (saved_hash) => {
|
||||
if !(saved_hash.hash == hash && wallpaper_cache_path.exists()) || force_bg_render {
|
||||
generate_image(wallpaper_data,wallpaper_cache_path, config.lock.clone())
|
||||
};
|
||||
},
|
||||
None => generate_image(wallpaper_data, wallpaper_cache_path, config.lock.clone())
|
||||
}
|
||||
},
|
||||
Err(_) => error_handler( crate::ErrorMessage { message: "unable to read image file".to_string(), code: 99 }),
|
||||
}
|
||||
},
|
||||
Err(_) => todo!(),
|
||||
};
|
||||
}
|
||||
},
|
||||
Err(_) => todo!(),
|
||||
}
|
||||
},
|
||||
None => todo!(),
|
||||
}
|
||||
//let wallpaper_root_path = tilde(&config.lock.wallpaper_path).to_string();
|
||||
//let wallpaper_root_path = Path::new(&wallpaper_root_path);
|
||||
|
||||
// for output in sway_connection.get_outputs().unwrap() {
|
||||
//let sway_workspaces = sway_connection.get_workspaces().unwrap().clone();
|
||||
//let output_ws_num = format!("{:0>2}",sway_workspaces.iter().find(|x|x.name == output.current_workspace
|
||||
// .clone().unwrap()).unwrap().num.sub(1)).chars().next().unwrap();
|
||||
//let image_path = wallpaper_root_path.join(output.name.clone()).join(format!("p_{}.jpg",output_ws_num));
|
||||
|
||||
//let mut img_file_data = fs::File::open(&image_path).unwrap();
|
||||
//let mut sha256 = Sha256::new();
|
||||
//let _ = io::copy(&mut img_file_data, &mut sha256);
|
||||
//let hash = format!("{:x}",sha256.finalize());
|
||||
//wp_hash_array.push(WallpaperCache { display: output.name.clone(), hash: hash.clone() });
|
||||
|
||||
// if hashes_json_path.exists() {
|
||||
//let wallpaper_data = image::open(&image_path).unwrap();
|
||||
//let wallpaper_cache_path = cache_dir.as_ref().unwrap().join(format!("sway-profiles-rs/lock/{}.jpg",output.name));
|
||||
/* match wp_hash_json.iter().find(|x|x.display == output.name) {
|
||||
Some (saved_hash) => {
|
||||
if !(saved_hash.hash == hash && wallpaper_cache_path.exists()) || force_bg_render {
|
||||
generate_image(wallpaper_data,wallpaper_cache_path, config.lock.clone())
|
||||
};
|
||||
},
|
||||
None => generate_image(wallpaper_data, wallpaper_cache_path, config.lock.clone())
|
||||
} */
|
||||
// }
|
||||
todo!();
|
||||
/* let gtklock_modules_dir = format!("/usr/lib/{}-linux-gnu/gtklock/",consts::ARCH);//.to_owned() + consts::ARCH + "-linux-gnu/gtklock/";
|
||||
let gktlock_modules_path = Path::new(>klock_modules_dir);
|
||||
let mut gtklock_args: Vec<String> = vec![];
|
||||
for file in WalkDir::new(gktlock_modules_path).into_iter() {//.find(|x|x.as_ref().unwrap().file_name().to_str().unwrap().ends_with("-module.so")) {
|
||||
|
||||
if let Some(f) = file.iter().find(|&x|x.path().to_str().unwrap().ends_with("-module.so")) {
|
||||
gtklock_args.push("-m".to_owned());
|
||||
gtklock_args.push(f.path().to_str().unwrap().to_string());
|
||||
}
|
||||
}
|
||||
let new_json = json!(wp_hash_array);
|
||||
let _ = write(hashes_json_path, new_json.to_string());
|
||||
let mut gtklock_command = "exec gtklock".to_owned();
|
||||
for a in gtklock_args {
|
||||
gtklock_command = gtklock_command + " " + &a;
|
||||
}
|
||||
println!("{:?}",gtklock_command);
|
||||
// let _ = sway_connection.run_command(gtklock_command);
|
||||
sway_connection.run_command(gtklock_command)*/
|
||||
}
|
||||
49
src/lib/power.rs
Normal file
49
src/lib/power.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
use {
|
||||
crate::{
|
||||
SDUError, cli::PowerCommand, config::LockConf, lock, run_sway_command, sway_ipc::get_sway_connection
|
||||
}, dialog::DialogBox, log::debug
|
||||
};
|
||||
|
||||
pub fn power(power_command_arg: &Option<PowerCommand>, lock_config: LockConf ) -> Result<(),SDUError> {
|
||||
match power_command_arg {
|
||||
Some(power_command) => {
|
||||
let action_prompt = match power_command {
|
||||
PowerCommand::Shutdown => vec!["shut down","poweroff.target"],
|
||||
PowerCommand::Reboot => vec!["restart","reboot.target"],
|
||||
PowerCommand::Suspend => vec!["suspend","suspend.target"],
|
||||
PowerCommand::Lock { force_render_background: _ } => vec!["lock"],
|
||||
PowerCommand::Logout => vec!["log out"],
|
||||
};
|
||||
let action = action_prompt.first().unwrap_or(&"not found");
|
||||
if let Ok(dialog::Choice::Yes) = dialog::Question::new(format!("Are you sure you would like to {}?",action)).title(format!("Confirm {}...",action)).show() {
|
||||
match power_command {
|
||||
PowerCommand::Lock { force_render_background } => {
|
||||
debug!("running lock_screen function");
|
||||
return lock(lock_config, force_render_background.unwrap_or_default())
|
||||
},
|
||||
PowerCommand::Logout => {
|
||||
debug!("sending exit command to sway");
|
||||
return run_sway_command(&mut get_sway_connection(), "exit".to_string())
|
||||
},
|
||||
_ => {
|
||||
let target = action_prompt.get(1).unwrap_or(&"not found");
|
||||
let systemctl = systemctl::SystemCtl::default();
|
||||
debug!("looking for {}",target);
|
||||
if let Ok(true) = systemctl.exists(target) {
|
||||
debug!("found and starting {}",target);
|
||||
match systemctl.start(target) {
|
||||
Ok(_) => return Ok(todo!("properly handle this return")),
|
||||
Err(_) => return Err(SDUError { message: "Could not start systemctl target".to_string() }),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
},
|
||||
None => {
|
||||
println!("⏻ Shutdown\n Reboot\n Suspend\n Lock\n Logout");
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -1,65 +1,154 @@
|
|||
use std::fs::{self, write};
|
||||
use {
|
||||
crate::{
|
||||
SDUError,
|
||||
cli::{
|
||||
ProfileCommand,
|
||||
ProfileGetCommand,
|
||||
ProfileSwitchCommand
|
||||
}, config::{ Config, Profile},
|
||||
/*error_handler,*/ get,
|
||||
get_xdg_dirs,
|
||||
setup_runtime_dir,
|
||||
shortcuts::shortcuts,
|
||||
sway_ipc::{get_sway_connection, run_sway_command}
|
||||
},
|
||||
log::{debug, error},
|
||||
serde_json::json,
|
||||
std::fs::{self, DirBuilder, write},
|
||||
swayipc::Connection,
|
||||
xdg::BaseDirectories
|
||||
};
|
||||
|
||||
use log::debug;
|
||||
use serde_json::json;
|
||||
use swayipc::Connection;
|
||||
use xdg::BaseDirectories;
|
||||
|
||||
use crate::{config::{Config, Profile}, sway::run_sway_command, ErrorMessage};
|
||||
|
||||
pub fn initialize(mut sway_connection: Connection, profile: Profile,profile_index: String,uindex: usize, initial_workspace: usize) -> Result<(),ErrorMessage> { //payload: String) {
|
||||
let workspace_index_start = (uindex * 10) + initial_workspace;
|
||||
debug!("{}",workspace_index_start);
|
||||
todo!();
|
||||
//todo: preserve keyboard layout, where 0 is last in workspace index
|
||||
for i in 0..10 {
|
||||
let _ = match run_sway_command(&mut sway_connection, format!("bindsym $mod+{} workspace number {}{}:{}",i,profile_index,i,profile.icon)) {
|
||||
Ok(_) => {
|
||||
let base_directories = BaseDirectories::new();
|
||||
let active_profile_cache = base_directories.get_runtime_directory().unwrap().join("sway-profiles-rs/active-profile.json");
|
||||
let active_profile = json!(uindex);
|
||||
let _ = write(active_profile_cache, active_profile.to_string());
|
||||
Ok(())
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
pub fn profile(profile_command: &ProfileCommand, profiles_config: Vec<Profile>, preserve_keyboard_order: bool) -> Result<(),SDUError> {
|
||||
// debug!("{:?}",xdg_directories.get_runtime_directory());
|
||||
let xdg_dirs = get_xdg_dirs();
|
||||
setup_runtime_dir(xdg_dirs);
|
||||
match profile_command {
|
||||
ProfileCommand::Init => {
|
||||
//let index_start = if config.preserve_keyboard_order { 1 } else { 0 };
|
||||
//debug!("profile index will start at {}",index_start);
|
||||
let active_profile_index = active_profile_index();
|
||||
match switch_by_index(profiles_config, active_profile_index, preserve_keyboard_order) {
|
||||
Ok(_) => Ok(debug!("successfully initialized sway-desktop-utils")),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
},
|
||||
ProfileCommand::Switch { switch_command} => {
|
||||
match switch_command {
|
||||
Some(ProfileSwitchCommand::To { index,name,query }) => {
|
||||
match index {
|
||||
Some(i) => {
|
||||
switch_by_index(profiles_config, *i, preserve_keyboard_order)
|
||||
},
|
||||
None => {
|
||||
match name {
|
||||
Some(n) => {
|
||||
switch_by_name(profiles_config, n.to_string(), preserve_keyboard_order)
|
||||
},
|
||||
None => match query {
|
||||
Some(q) => match q.parse::<usize>() {
|
||||
Ok(i) => match switch_by_index(profiles_config.clone(), i, preserve_keyboard_order) {
|
||||
Ok(o) => Ok(o),
|
||||
Err(_) => switch_by_name(profiles_config, q.to_string(), preserve_keyboard_order),
|
||||
},
|
||||
Err(_) => switch_by_name(profiles_config, q.to_string(), preserve_keyboard_order),
|
||||
},
|
||||
None => Err(SDUError { message: "No profile index or name provided.".to_string() }),
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
Some(ProfileSwitchCommand::Next) => match next(profiles_config, preserve_keyboard_order) {
|
||||
Ok(_) => Ok(debug!("Successfully switched to next profile")),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
Some(ProfileSwitchCommand::Prev) => match previous(profiles_config, preserve_keyboard_order) {
|
||||
Ok(_) => Ok(debug!("successfully switched to previous profile")),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
None => {
|
||||
Ok(for profile in profiles_config {
|
||||
println!("{} {}", profile.icon, profile.name);
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
ProfileCommand::Get { get_command, monitor } => {
|
||||
let profile_detail = get_command.clone().unwrap_or(ProfileGetCommand::Json);
|
||||
println!("{}",get::profile_info(profiles_config.clone(),profile_detail.clone()));
|
||||
Ok(if monitor.unwrap_or_default() {
|
||||
let _ = get::watch(profiles_config, profile_detail,get_xdg_dirs().runtime_dir.expect("XDG Runtime dir could not be found").join("sway-desktop-utils/active-profile.json").to_str().expect("could not create str from path").to_string());
|
||||
})
|
||||
}
|
||||
ProfileCommand::Shortcuts { shortcut_command } => {
|
||||
let active_profile = active_profile(profiles_config).expect("could not determine active profile");
|
||||
shortcuts(shortcut_command, active_profile.scripts.expect("could not find scripts for profile"))
|
||||
},
|
||||
}
|
||||
match run_sway_command(&mut sway_connection, format!("workspace number {}1:{}",profile_index,profile.icon)) {
|
||||
}
|
||||
|
||||
pub fn initialize(mut sway_connection: Connection, profile: Profile,uindex: usize, preserve_keyboard_order: bool) -> Result<(),SDUError> { //payload: String) {
|
||||
//let payload = "bindsym $mod+{} workspace number {}{}:{}";
|
||||
// let profile_initial_workspace: usize = if preserve_keyboard_order { 1 } else { 0 };
|
||||
for i in 0..10 {
|
||||
let workspace = (uindex*10) + match i.eq(&0) && !preserve_keyboard_order {
|
||||
true => 10,
|
||||
false => i,
|
||||
};
|
||||
debug!("key {}: workspace {}",i,workspace);
|
||||
//debug!("profile index: {}, uindex: {}",profile_index,uindex);
|
||||
debug!("bindsym $mod+{} workspace number {}:{}",i,workspace,profile.icon);
|
||||
|
||||
match run_sway_command(&mut sway_connection, format!("bindsym $mod+{} workspace number {}:{}",i,workspace,profile.icon)) {
|
||||
Ok(_) => {
|
||||
},
|
||||
Err(_) => todo!(),
|
||||
}
|
||||
};
|
||||
|
||||
let base_directories = BaseDirectories::new();
|
||||
let active_profile_cache = base_directories.get_runtime_directory().expect("why tf do xdg dirs not exist").join("sway-desktop-utils/active-profile.json");
|
||||
let active_profile = json!(uindex);
|
||||
|
||||
DirBuilder::new().recursive(true).create(active_profile_cache.parent().expect("Unable to determine cache dir")).expect("Failed to create cache directory");
|
||||
match write(active_profile_cache, active_profile.to_string()) {
|
||||
Ok(o) => debug!("{:#?}",o),
|
||||
Err(e) => debug!("{:#?}",e),
|
||||
};
|
||||
|
||||
match run_sway_command(&mut sway_connection, format!("workspace number {}:{}",(uindex*10)+1,profile.icon)) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn profile_from_index(profiles: Vec<Profile>, index: usize) -> Result<Profile,ErrorMessage>{
|
||||
match profiles.get(index) {
|
||||
Some(p) => Ok(p.clone()),
|
||||
None => Err(ErrorMessage { message: "Profile not found for index".to_string(), code: 3 }),
|
||||
}
|
||||
pub fn profile_from_index(profiles: Vec<Profile>, index: usize) -> Profile {
|
||||
profiles.get(index).expect("Profile not found for index").clone() // {
|
||||
// Some(p) => Ok(p.clone())
|
||||
}
|
||||
|
||||
pub fn _profile_from_name(config: Config, name: String) -> Result<Profile,ErrorMessage> {
|
||||
pub fn _profile_from_name(config: Config, name: String) -> Result<Profile,SDUError> {
|
||||
match config.profiles.iter().find(|x|x.name == name) {
|
||||
Some(p) => Ok(p.clone()),
|
||||
None => Err(ErrorMessage { message: format!("Profile not found with name {}",name), code: 3 }),
|
||||
None => Err(SDUError { message: format!("Profile not found with name {}",name) }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn switch_by_index(config: Config,index: usize,sway_connection: Connection, initial_workspace: usize) -> Result<(),ErrorMessage> {
|
||||
match profile_from_index(config.profiles, index) {
|
||||
Ok(p) => match initialize(sway_connection, p.clone(), index_string(index), index, initial_workspace) {
|
||||
Ok(_) => {
|
||||
println!("successfully switched to profile at index {}",index);
|
||||
Ok(())
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
pub fn switch_by_index(profiles_config: Vec<Profile>,index: usize, preserve_keyboard_order: bool) -> Result<(),SDUError> {
|
||||
let profile = profile_from_index(profiles_config, index);
|
||||
match initialize(get_sway_connection(), profile, index, preserve_keyboard_order) {
|
||||
Ok(_) => {
|
||||
println!("successfully switched to profile at index {}",index);
|
||||
Ok(())
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn switch_by_name(config: Config,name: String,sway_connection: Connection) -> Result<(),ErrorMessage> {
|
||||
match index_from_name(config.clone(), name.clone()) {
|
||||
Ok(index) => match switch_by_index(config.clone(), index, sway_connection, config.initial_workspace) {
|
||||
pub fn switch_by_name(profiles_config: Vec<Profile>,name: String,preserve_keyboard_order: bool) -> Result<(),SDUError> {
|
||||
match index_from_name(profiles_config.clone(), name.clone()) {
|
||||
Ok(index) => match switch_by_index(profiles_config, index,preserve_keyboard_order) {
|
||||
Ok(_) => {
|
||||
println!("Successfully switched to profile with name {}",name);
|
||||
Ok(())
|
||||
|
|
@ -68,93 +157,93 @@ pub fn switch_by_name(config: Config,name: String,sway_connection: Connection) -
|
|||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
/* match profile_from_name(config, name) {
|
||||
Ok(p) => match {
|
||||
pub fn active_profile_index() -> usize {
|
||||
let base_directories = BaseDirectories::new();
|
||||
let active_profile_cache_json = base_directories.get_runtime_directory().expect("xdg dirs do not exist?").join("sway-desktop-utils/active-profile.json");
|
||||
/* match active_profile_cache_json.exists().then(|| fs::File::open(active_profile_cache_json).and_then(|f| Ok(serde_json::from_reader::<fs::File,usize>(f)))) {
|
||||
Some(a) => todo!(),
|
||||
None => todo!(),
|
||||
} */
|
||||
/* Ok(u) => {
|
||||
Ok(u)
|
||||
},
|
||||
Err(_) => Err(ErrorMessage { message: "could not parse json from active profile cache file".to_string(), code: 3 }),
|
||||
Err(_) => Err(ErrorMessage { message: "could not open active profile cache file".to_string(), code: 3 }),
|
||||
} */
|
||||
if active_profile_cache_json.exists() {
|
||||
fs::File::open(active_profile_cache_json).ok()
|
||||
.and_then(|f|serde_json::from_reader::<fs::File,usize>(f).ok())
|
||||
.expect("could not parse active profile cache file")
|
||||
//serde_json::from_reader::<fs::File,usize>(cache_json_reader).expect("could not parse json from active profile cache file")
|
||||
// let cache_json_reader = fs::File::open(active_profile_cache_json).expect("could not open active profile cache file");
|
||||
// serde_json::from_reader::<fs::File,usize>(cache_json_reader).expect("could not parse json from active profile cache file")
|
||||
} else {
|
||||
error!("no active profile cache file");
|
||||
0
|
||||
}
|
||||
/* match fs::File::open(active_profile_cache_json) {
|
||||
Ok(f) => {
|
||||
match serde_json::from_reader::<fs::File,usize>(f) {
|
||||
Ok(u) => {
|
||||
Ok(u)
|
||||
},
|
||||
Err(_) => Err(ErrorMessage { message: "could not parse json from active profile cache file".to_string(), code: 3 }),
|
||||
}
|
||||
},
|
||||
Err(_) => Err(ErrorMessage { message: "could not open active profile cache file".to_string(), code: 3 }),
|
||||
} */
|
||||
// )
|
||||
/* {
|
||||
true => {
|
||||
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
false => Err(ErrorMessage { message: "no active profile cache file".to_string(), code: 3 }),
|
||||
} */
|
||||
}
|
||||
|
||||
pub fn active_profile_index() -> Result<usize,ErrorMessage> {
|
||||
let base_directories = BaseDirectories::new();
|
||||
let active_profile_cache_json = base_directories.get_runtime_directory().unwrap().join("sway-profiles-rs/active-profile.json");
|
||||
match active_profile_cache_json.exists() {
|
||||
true => {
|
||||
match fs::File::open(active_profile_cache_json) {
|
||||
Ok(f) => {
|
||||
match serde_json::from_reader::<fs::File,usize>(f) {
|
||||
Ok(u) => {
|
||||
Ok(u)
|
||||
},
|
||||
Err(_) => Err(ErrorMessage { message: "could not parse json from active profile cache file".to_string(), code: 3 }),
|
||||
}
|
||||
},
|
||||
Err(_) => Err(ErrorMessage { message: "could not open active profile cache file".to_string(), code: 3 }),
|
||||
}
|
||||
},
|
||||
false => Err(ErrorMessage { message: "no active profile cache file".to_string(), code: 3 }),
|
||||
pub fn active_profile(profiles: Vec<Profile>) -> Result<Profile,SDUError> {
|
||||
match profiles.get(active_profile_index()) {
|
||||
Some(p) => Ok(p.clone()),
|
||||
None => Err(SDUError { message: "Could not get profile by index".to_string() }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn active_profile(profiles: Vec<Profile>) -> Result<Profile,ErrorMessage> {
|
||||
//let active_profile_index =
|
||||
match active_profile_index() {
|
||||
Ok(i) => match profiles.get(i) {
|
||||
Some(p) => Ok(p.clone()),
|
||||
None => Err(ErrorMessage { message: "Could not get profile by index".to_string(), code: 1 }),
|
||||
pub fn next(profiles_config: Vec<Profile>, preserve_keyboard_order: bool) -> Result<(),SDUError> {
|
||||
let profile_count = profiles_config.len();
|
||||
let mut next_profile = active_profile_index() + 1;
|
||||
if next_profile.ge(&profile_count) {
|
||||
next_profile = 0
|
||||
}
|
||||
match switch_by_index(profiles_config, next_profile, preserve_keyboard_order) {
|
||||
Ok(_) => {
|
||||
println!("switched to next profile ({})",next_profile);
|
||||
Ok(())
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next(config: Config, sway_connection: Connection) -> Result<(),ErrorMessage> {
|
||||
match active_profile_index() {
|
||||
Ok(u) => {
|
||||
let profile_count = config.profiles.len();
|
||||
let mut next_profile = u + 1;
|
||||
if next_profile.ge(&profile_count) {
|
||||
next_profile = 0
|
||||
}
|
||||
match switch_by_index(config.clone(), next_profile, sway_connection, config.initial_workspace) {
|
||||
Ok(_) => {
|
||||
println!("switched to next profile ({})",next_profile);
|
||||
Ok(())
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
pub fn previous(profiles_config: Vec<Profile>, preserve_keyboard_order: bool) -> Result<(),SDUError> {
|
||||
let profile_index = active_profile_index();
|
||||
let prev_profile: usize = if profile_index.eq(&0) {
|
||||
profiles_config.len() - 1
|
||||
} else {
|
||||
profile_index - 1
|
||||
};
|
||||
match switch_by_index(profiles_config, prev_profile, preserve_keyboard_order) {
|
||||
Ok(_) => {
|
||||
println!("switched to prev profile ({})",prev_profile);
|
||||
Ok(())
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn previous(config: Config, sway_connection: Connection) -> Result<(),ErrorMessage> {
|
||||
match active_profile_index() {
|
||||
Ok(u) => {
|
||||
let prev_profile: usize = if u.eq(&0) {
|
||||
config.profiles.len() - 1
|
||||
} else {
|
||||
u - 1
|
||||
};
|
||||
match switch_by_index(config.clone(), prev_profile, sway_connection, config.initial_workspace) {
|
||||
Ok(_) => {
|
||||
println!("switched to prev profile ({})",prev_profile);
|
||||
Ok(())
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index_from_name(config: Config, name: String) -> Result<usize, ErrorMessage>{
|
||||
match config.profiles.iter().position(|x|x.name == name) {
|
||||
pub fn index_from_name(profiles_config: Vec<Profile>, name: String) -> Result<usize, SDUError>{
|
||||
match profiles_config.iter().position(|x|x.name == name) {
|
||||
Some(i) => Ok(i),
|
||||
None => Err(ErrorMessage { message: "Index not found for profile?".to_string(), code: 3 }),
|
||||
None => Err(SDUError { message: "Index not found for profile?".to_string() }),
|
||||
}
|
||||
}
|
||||
pub fn index_string(index: usize) -> String {
|
||||
index.to_string().trim_matches('0').to_string()
|
||||
}
|
||||
37
src/lib/shortcuts.rs
Normal file
37
src/lib/shortcuts.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
use log::debug;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{SDUError, cli::{ShortcutCommand, ShortcutMode}, config::ScriptConf, sway_ipc::{get_sway_connection, run_sway_command}};
|
||||
|
||||
pub fn print_shortcuts(mode: &ShortcutMode, shortcuts: Vec<ScriptConf>) -> String {
|
||||
match mode {
|
||||
ShortcutMode::Dmenu => {
|
||||
let mut dmenu_shortcuts: String = String::new();
|
||||
for shortcut in shortcuts {
|
||||
dmenu_shortcuts = format!("{}\n{} {}",dmenu_shortcuts,shortcut.icon, shortcut.name)
|
||||
};
|
||||
dmenu_shortcuts
|
||||
},
|
||||
ShortcutMode::Json => {
|
||||
json!(shortcuts).to_string()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exec_shortcut(shortcut_name: String, shortcuts: Vec<ScriptConf>) -> Result<(),SDUError> {
|
||||
let shortcut = shortcuts.iter().find(|x|x.name.eq(&shortcut_name)).expect("cannot find shortcut with name");
|
||||
debug!("running [{}]", shortcut.command);
|
||||
run_sway_command(&mut get_sway_connection(), format!("exec {}",shortcut.command.clone()))
|
||||
}
|
||||
|
||||
pub fn shortcuts(shortcut_command: &ShortcutCommand, shortcuts: Vec<ScriptConf> ) -> Result<(),SDUError> {
|
||||
match shortcut_command {
|
||||
ShortcutCommand::Execute { shortcut_name } => exec_shortcut(shortcut_name.to_string(), shortcuts),
|
||||
ShortcutCommand::Get { mode } => {
|
||||
let shortcuts_string = print_shortcuts(&mode.unwrap_or(ShortcutMode::Json), shortcuts);
|
||||
println!("{}", shortcuts_string);
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,43 +1,33 @@
|
|||
use std::time::Instant;
|
||||
|
||||
use swayipc::{Connection, EventType};
|
||||
use crate::{config::Config, windows::print_window_info, workspaces::print_workspace_info, Cli, ErrorMessage};
|
||||
|
||||
pub fn run_sway_command(sway_connection: &mut Connection,payload: String) -> Result<(),ErrorMessage> {
|
||||
match sway_connection.run_command(&payload) {
|
||||
Ok(_) => {
|
||||
println!("Command [{}] ran successfully",payload);
|
||||
Ok(())
|
||||
},
|
||||
Err(e) => Err(ErrorMessage{ message: e.to_string(), code: 2 }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn monitor_events(event_type: Vec<EventType>, cli: Cli, config: Config) {
|
||||
let sway_connection = Connection::new().unwrap();
|
||||
for event in sway_connection.subscribe(event_type).unwrap() {
|
||||
let start = Instant::now();
|
||||
let e = event.unwrap();
|
||||
match e {
|
||||
swayipc::Event::Window(w) => {
|
||||
print_window_info(w.container, &cli, &config)
|
||||
},
|
||||
swayipc::Event::Workspace(_) => {
|
||||
match cli.command {
|
||||
crate::Commands::Window { window_command: _ } => {
|
||||
let mut con = Connection::new().unwrap();
|
||||
print_window_info(con.get_tree().unwrap().find(|x|x.focused).unwrap(), &cli, &config)
|
||||
},
|
||||
crate::Commands::Workspace { workspace_command: _ } => {
|
||||
let mut con = Connection::new().unwrap();
|
||||
print_workspace_info(con.get_workspaces().unwrap())
|
||||
},
|
||||
_ => todo!(),
|
||||
}
|
||||
},
|
||||
swayipc::Event::Tick(_) => todo!(),
|
||||
_ => unreachable!(),
|
||||
use {
|
||||
crate::{
|
||||
SDUError, cli::{
|
||||
SwayCommand,
|
||||
SwayGetCommand
|
||||
}, config::WindowIcon, get_window_info, get_workspace_info, sway_ipc::{
|
||||
self,
|
||||
get_sway_info
|
||||
}
|
||||
log::debug!("time taken: {:?}",start.elapsed());
|
||||
},
|
||||
swayipc::EventType
|
||||
};
|
||||
|
||||
pub fn sway( sway_command: &SwayCommand, window_icons: &[WindowIcon] ) -> Result<(),SDUError> {
|
||||
match sway_command {
|
||||
SwayCommand::Get { monitor, get_command } => {
|
||||
let requested_info = get_command.as_ref().unwrap_or(&SwayGetCommand::Full);
|
||||
let sway_json = match requested_info {
|
||||
SwayGetCommand::Workspaces => get_workspace_info(),
|
||||
SwayGetCommand::Window => get_window_info(window_icons),
|
||||
SwayGetCommand::Full => get_sway_info(window_icons),
|
||||
};
|
||||
println!("{}",sway_json);
|
||||
Ok(if monitor.unwrap_or_default() {
|
||||
let event_monitors = match requested_info {
|
||||
SwayGetCommand::Workspaces => vec![EventType::Workspace],
|
||||
SwayGetCommand::Window | SwayGetCommand::Full => vec![EventType::Window,EventType::Workspace],
|
||||
};
|
||||
sway_ipc::monitor_events(event_monitors, requested_info, window_icons);
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
56
src/lib/sway_ipc.rs
Normal file
56
src/lib/sway_ipc.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
use {
|
||||
crate::{
|
||||
SDUError, cli::SwayGetCommand, config::WindowIcon, windows::get_window_info, workspaces::get_workspace_info
|
||||
}, log::debug, serde_json::{Value, json}, std::time::Instant, swayipc::{
|
||||
Connection,
|
||||
EventType
|
||||
}
|
||||
};
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct SwayInfo {
|
||||
window_info: Value,
|
||||
workspace_info: Value,
|
||||
}
|
||||
|
||||
pub fn get_sway_connection() -> Connection {
|
||||
Connection::new().expect("Unable to connect to Sway IPC")
|
||||
}
|
||||
|
||||
pub fn run_sway_command(sway_connection: &mut Connection,payload: String) -> Result<(),SDUError> {
|
||||
match sway_connection.run_command(&payload) {
|
||||
Ok(sway_result) => {
|
||||
debug!("{:#?}", sway_result);
|
||||
println!("Command [{}] ran successfully",payload);
|
||||
Ok(todo!("properly handle this exit"))
|
||||
},
|
||||
Err(e) => Err(SDUError{ message: e.to_string() }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_sway_info(window_icons: &[WindowIcon]) -> Value {
|
||||
let window_info = get_window_info(window_icons);
|
||||
let workspace_info = get_workspace_info();
|
||||
let full_info = SwayInfo{ window_info, workspace_info };
|
||||
json!(full_info)
|
||||
}
|
||||
|
||||
pub fn monitor_events(event_type: Vec<EventType>, get_command: &SwayGetCommand, window_icons: &[WindowIcon]) {
|
||||
let sway_connection = get_sway_connection();
|
||||
for event in sway_connection.subscribe(event_type).expect("unable to subscribe to sway events") {
|
||||
let start = Instant::now();
|
||||
let sway_json = match event.expect("unable to parse sway event") {
|
||||
swayipc::Event::Workspace(_) | swayipc::Event::Window(_) => {
|
||||
match get_command {
|
||||
SwayGetCommand::Window => get_window_info(window_icons),
|
||||
SwayGetCommand::Workspaces => get_workspace_info(),
|
||||
SwayGetCommand::Full => get_sway_info(window_icons),
|
||||
}
|
||||
},
|
||||
//swayipc::Event::Tick(_) => todo!(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
println!("{}",sway_json);
|
||||
log::debug!("time taken: {:?}",start.elapsed());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +1,64 @@
|
|||
use serde_json::json;
|
||||
use swayipc::{Connection, Node};
|
||||
use crate::{config::Config, Cli, ErrorMessage};
|
||||
use {
|
||||
log::debug,
|
||||
serde_json::{
|
||||
Value,
|
||||
json
|
||||
},
|
||||
swayipc::Connection,
|
||||
crate::config::WindowIcon,
|
||||
};
|
||||
|
||||
/*#[derive(serde::Serialize)]
|
||||
pub struct WindowContext {
|
||||
pub window_count: usize,
|
||||
pub
|
||||
}*/
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct WindowInfo {
|
||||
pub title: String,
|
||||
pub window_count: usize
|
||||
}
|
||||
|
||||
pub fn get_window_name(window_node: Node, cli: &Cli, config: &Config) -> Result<String,ErrorMessage> {
|
||||
match window_node.name {
|
||||
Some(mut window_title) => {
|
||||
for pair in config.window_icons.clone() {
|
||||
if window_title.contains(&pair.substring) {
|
||||
window_title = format!("{} {}", pair.icon, window_title.replace(&pair.substring,""))
|
||||
}
|
||||
}
|
||||
/* if cli.no_truncate_title.is_some_and(|x|!x) && config.title_length.is_some_and(|x|x.lt(&window_title.chars().count())) {
|
||||
let trunc_point = window_title.char_indices().map(|(i, _)| i).nth(config.title_length.unwrap()).unwrap_or(window_title.chars().count());
|
||||
window_title.truncate(trunc_point);
|
||||
window_title.push('…');
|
||||
} */
|
||||
Ok(window_title)
|
||||
},
|
||||
None => {
|
||||
Ok("".to_string())
|
||||
pub fn get_window_info(window_icons: &[WindowIcon]) -> Value {
|
||||
let mut sway_connection = Connection::new().expect("Unable to connect to sway ipc socket");
|
||||
let nodes_tree = sway_connection.get_tree().expect("Unable to parse Sway node tree");
|
||||
/* RETRIEVING NODE LAYOUT
|
||||
for output in nodes_tree.nodes.iter() {
|
||||
println!("|({:#?}) {:#?}", output.node_type, output.name.clone().unwrap());
|
||||
for workspace in output.nodes.iter() {
|
||||
println!("||({:#?}) {}", workspace.node_type, workspace.name.clone().unwrap());
|
||||
println!("|||Children: {:#?}",workspace.iter().filter(|x|(x.node_type.eq(&swayipc::NodeType::Con) || x.node_type.eq(&swayipc::NodeType::FloatingCon)) && x.name.is_some()).count());
|
||||
println!("|||Focused: {:#?}",workspace.iter().find(|x|x.focused).is_some());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_window_info(window_node: Node,cli: &Cli,config: &Config) {
|
||||
let mut sc = Connection::new().unwrap();
|
||||
let window_count = sc.get_workspaces().unwrap().iter().find(|x|x.focused).unwrap().focus.len();
|
||||
let window_title = match window_count.gt(&0) {
|
||||
true => get_window_name(window_node, cli, config).unwrap(),
|
||||
false => "".to_string(),
|
||||
*/
|
||||
let focused_workspace_raw = nodes_tree.iter()
|
||||
.find(|x|x.node_type.eq(&swayipc::NodeType::Workspace) && x.iter().any(|x|x.focused));
|
||||
let window_count = match focused_workspace_raw {
|
||||
Some(ws) => ws.iter().filter(|x|(x.node_type.eq(&swayipc::NodeType::Con) || x.node_type.eq(&swayipc::NodeType::FloatingCon)) && x.name.is_some()).count(),
|
||||
None => 0,
|
||||
};
|
||||
let window_info = WindowInfo { title: window_title, window_count};
|
||||
let window_output = json!(window_info);
|
||||
println!("{}",window_output);
|
||||
debug!("{:#?}",window_count);
|
||||
let window_title = if window_count.gt(&0) {
|
||||
let window_node = nodes_tree.iter().find(|x|x.focused);
|
||||
debug!("{:#?}",window_node);
|
||||
match window_node {
|
||||
Some(n) => {
|
||||
let mut window_name = n.name.clone().unwrap_or_default();
|
||||
for pair in window_icons {
|
||||
if window_name.contains(&pair.substring) {
|
||||
window_name = format!("{} {}", pair.icon, window_name.replace(&pair.substring, ""))
|
||||
}
|
||||
}
|
||||
Some(window_name)
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let window_info = WindowInfo { title: window_title.unwrap_or_default(), window_count};
|
||||
json!(window_info)
|
||||
// let window_output = json!(window_info);
|
||||
// window_output.to_string()
|
||||
}
|
||||
|
|
@ -1,5 +1,10 @@
|
|||
use serde_json::json;
|
||||
use swayipc::Workspace;
|
||||
use {
|
||||
swayipc::Connection,
|
||||
serde_json::{
|
||||
Value,
|
||||
json,
|
||||
}
|
||||
};
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct WorkspaceInfo {
|
||||
|
|
@ -9,8 +14,9 @@ pub struct WorkspaceInfo {
|
|||
pub position: char
|
||||
}
|
||||
|
||||
pub fn print_workspace_info(workspaces: Vec<Workspace>) {
|
||||
let current_ws = workspaces.iter().find(|&x| x.focused).unwrap().num;
|
||||
pub fn get_workspace_info() -> Value {
|
||||
let workspaces = Connection::new().expect("unable to connect to sway IPC").get_workspaces().expect("Unable to parse sway workspaces");
|
||||
let current_ws = workspaces.iter().find(|&x| x.focused).expect("unable to find focused workspace").num;
|
||||
let mut workspaces_info: Vec<WorkspaceInfo> = vec![];
|
||||
for workspace in workspaces {
|
||||
let mut ws_position = 'c';
|
||||
|
|
@ -23,6 +29,7 @@ pub fn print_workspace_info(workspaces: Vec<Workspace>) {
|
|||
workspaces_info.push(workspace_info);
|
||||
}
|
||||
workspaces_info.sort_by(|a, b| a.num.cmp(&b.num));
|
||||
let workspace_output = json!(workspaces_info);
|
||||
println!("{}",workspace_output)
|
||||
json!(workspaces_info)
|
||||
// let workspace_output = json!(workspaces_info);
|
||||
// workspace_output.to_string()
|
||||
}
|
||||
430
src/main.rs
430
src/main.rs
|
|
@ -1,397 +1,101 @@
|
|||
#![warn(unused_crate_dependencies)]
|
||||
|
||||
use std::{any::Any, process::exit, time::Instant};
|
||||
use log::{info,debug,error,trace};
|
||||
use clap::{ArgAction, Parser, Subcommand};
|
||||
use swayipc::{Connection, EventType, Fallible};
|
||||
use systemctl;
|
||||
use dialog::{DialogBox, backends::Backend};
|
||||
#![allow(clippy::restriction)]
|
||||
|
||||
mod config;
|
||||
#[path = "lib/windows.rs"]
|
||||
mod windows;
|
||||
use windows::print_window_info;
|
||||
#[path = "lib/workspaces.rs"]
|
||||
mod workspaces;
|
||||
use workspaces::print_workspace_info;
|
||||
#[path = "lib/lock.rs"]
|
||||
mod lock;
|
||||
use lock::lock_screen;
|
||||
#[path = "lib/launch.rs"]
|
||||
mod launch;
|
||||
use launch::profile_launch;
|
||||
#[path = "lib/sway.rs"]
|
||||
mod sway;
|
||||
use sway::run_sway_command;
|
||||
#[path = "lib/sway_ipc.rs"]
|
||||
mod sway_ipc;
|
||||
#[path = "lib/profile.rs"]
|
||||
mod profile;
|
||||
#[path = "lib/get.rs"]
|
||||
mod get;
|
||||
#[path = "lib/sway.rs"]
|
||||
mod sway;
|
||||
#[path = "lib/power.rs"]
|
||||
mod power;
|
||||
#[path = "lib/cli.rs"]
|
||||
mod cli;
|
||||
#[path = "lib/shortcuts.rs"]
|
||||
mod shortcuts;
|
||||
|
||||
use xdg::BaseDirectories;
|
||||
|
||||
use crate::profile::switch_by_index;
|
||||
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(version, about, long_about = None)]
|
||||
pub struct Cli {
|
||||
/// Disable truncation of window titles
|
||||
#[arg(short = 'T', long = "no-truncate-title", action = ArgAction::SetTrue)]
|
||||
no_truncate_title: Option<bool>,
|
||||
|
||||
/// Enables monitoring for supported event types
|
||||
#[arg(short = 'm', long = "monitor", action = ArgAction::SetTrue)]
|
||||
monitor: Option<bool>,
|
||||
|
||||
/// Turn debugging information on
|
||||
#[arg(short, long, action = ArgAction::Count)]
|
||||
debug: u8,
|
||||
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Commands {
|
||||
/// Prints the Currently Active Window Title
|
||||
Window {
|
||||
#[command(subcommand)]
|
||||
window_command: WindowCommand,
|
||||
use {
|
||||
crate::{
|
||||
cli::{
|
||||
Cli,
|
||||
Commands
|
||||
},
|
||||
config::Config,
|
||||
power::power,
|
||||
shortcuts::shortcuts,
|
||||
sway::sway
|
||||
},
|
||||
/// Prints the Currently Active Workspace layout
|
||||
Workspace {
|
||||
#[command(subcommand)]
|
||||
workspace_command: WorkspaceCommand,
|
||||
},
|
||||
/// Launch Program with Current Workspace's Profile Configuration
|
||||
Launch {
|
||||
#[arg(short, long)]
|
||||
program: String
|
||||
},
|
||||
/// Set up blurred wallpaper for screen lock, and load gtklock modules
|
||||
Lock {
|
||||
#[arg(short, long, action = ArgAction::SetTrue)]
|
||||
force_render_background: Option<bool>,
|
||||
},
|
||||
/// Profile WIP,
|
||||
Profile {
|
||||
#[command(subcommand)]
|
||||
profile_command: ProfileCommand,
|
||||
},
|
||||
/// Shortcuts TODO
|
||||
Shortcuts {
|
||||
#[arg(short, long, action = ArgAction::SetTrue)]
|
||||
global: Option<bool>,
|
||||
},
|
||||
Power {
|
||||
#[command(subcommand)]
|
||||
power_command: PowerCommand,
|
||||
}
|
||||
}
|
||||
clap::Parser,
|
||||
launch::launch,
|
||||
lock::lock,
|
||||
log::{debug, error},
|
||||
profile::profile,
|
||||
std::process::exit,
|
||||
sway_ipc::run_sway_command,
|
||||
windows::get_window_info,
|
||||
workspaces::get_workspace_info,
|
||||
xdg::BaseDirectories
|
||||
};
|
||||
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum WorkspaceCommand {
|
||||
/// todo
|
||||
#[clap(alias = "g")]
|
||||
Get,
|
||||
#[clap(alias = "r")]
|
||||
Rename
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum WindowCommand {
|
||||
/// todo
|
||||
#[clap(alias = "g")]
|
||||
Get
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum PowerCommand {
|
||||
/// power down the system
|
||||
#[clap(alias = "s")]
|
||||
Shutdown,
|
||||
/// Reboot the system
|
||||
#[clap(alias = "r")]
|
||||
Reboot,
|
||||
/// Suspend (idle) the system
|
||||
#[clap(alias = "i")]
|
||||
Suspend,
|
||||
/// Lock the system
|
||||
#[clap(alias = "l")]
|
||||
Lock {
|
||||
#[arg(short, long, action = ArgAction::SetTrue)]
|
||||
force_render_background: Option<bool>,
|
||||
},
|
||||
/// Log out of (exit) the session
|
||||
#[clap(alias = "e")]
|
||||
Logout,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum ProfileCommand {
|
||||
/// switch profiles
|
||||
#[clap(alias = "s")]
|
||||
Switch {
|
||||
#[command(subcommand)]
|
||||
switch_command: Option<ProfileSwitchCommand>,
|
||||
|
||||
},
|
||||
/// get profile info
|
||||
#[clap(alias = "g")]
|
||||
Get {
|
||||
/// monitor profile information
|
||||
#[arg(short, long, action = ArgAction::SetTrue)]
|
||||
monitor: Option<bool>,
|
||||
#[command(subcommand)]
|
||||
get_command: ProfileGetCommand,
|
||||
},
|
||||
/// Initialize sway-profiles-rs
|
||||
#[clap(alias = "i")]
|
||||
Init
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum ProfileSwitchCommand {
|
||||
/// switch to next profile
|
||||
#[clap(alias = "n")]
|
||||
Next,
|
||||
/// switch to previous profile
|
||||
#[clap(alias = "p")]
|
||||
Prev,
|
||||
/// switch to profile by name or id
|
||||
#[clap(alias = "t")]
|
||||
To {
|
||||
#[clap(group = "switch_to")]
|
||||
#[arg(short, long)]
|
||||
name: Option<String>,
|
||||
#[clap(group = "switch_to")]
|
||||
#[arg(short, long)]
|
||||
index: Option<usize>,
|
||||
query: Option<String>
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Subcommand)]
|
||||
enum ProfileGetCommand {
|
||||
/// get profile json
|
||||
#[clap(alias = "j")]
|
||||
Json,
|
||||
/// get profile name
|
||||
#[clap(alias = "n")]
|
||||
Name,
|
||||
/// get profile icon
|
||||
#[clap(alias = "i")]
|
||||
Icon
|
||||
pub enum DirectoryType {
|
||||
Cache,
|
||||
Config
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ErrorMessage {
|
||||
message: String,
|
||||
code: i32
|
||||
pub struct SDUError {
|
||||
message: String
|
||||
}
|
||||
|
||||
pub fn error_handler(error: ErrorMessage) {
|
||||
/*pub fn error_handler(error: ErrorMessage) {
|
||||
debug!("ERROR: {}",error.message);
|
||||
exit(error.code)
|
||||
}
|
||||
}*/
|
||||
|
||||
pub fn setup_runtime_dir(xdg_directories: BaseDirectories) {
|
||||
match xdg_directories.create_runtime_directory("sway-profiles-rs") {
|
||||
Ok(_) => debug!("success"),
|
||||
Err(_) => debug!("failed"),
|
||||
match xdg_directories.create_runtime_directory("sway-desktop-utils") {
|
||||
Ok(_) => debug!("created runtime directories"),
|
||||
Err(_) => debug!("failed to create runtime directories"),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Fallible<()> {
|
||||
env_logger::init();
|
||||
// env_logger::
|
||||
let xdg_directories = BaseDirectories::new();
|
||||
let cli = Cli::parse();
|
||||
let config = confy::load("sway-profiles-rs", "config").unwrap();
|
||||
pub fn get_xdg_dirs() -> BaseDirectories {
|
||||
BaseDirectories::new()
|
||||
}
|
||||
|
||||
let mut sway_connection = Connection::new()?;
|
||||
match &cli.command {
|
||||
Commands::Window { window_command } => {
|
||||
match window_command {
|
||||
WindowCommand::Get => {
|
||||
//let start = Instant::now();
|
||||
match sway_connection.get_tree() {
|
||||
Ok(t) => {
|
||||
match t.iter().find(|&x | x.focused) {
|
||||
Some(f) => print_window_info(f.clone(), &cli, &config),
|
||||
None => error_handler(ErrorMessage { message: "Could not find focused window".to_string(), code: 99 }),
|
||||
}
|
||||
|
||||
},
|
||||
Err(_) => error_handler(ErrorMessage { message: "Could not unwrap sway tree".to_string(), code: 99}),
|
||||
}
|
||||
//print_window_info(sway_connection.get_tree().unwrap().iter().find(|&x | x.focused).unwrap().clone(), &cli, &config);
|
||||
//debug!("time taken: {:?}",start.elapsed());
|
||||
if cli.monitor.unwrap() {
|
||||
sway::monitor_events(vec![EventType::Window,EventType::Workspace], cli, config);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
Commands::Workspace { workspace_command } => {
|
||||
match workspace_command {
|
||||
WorkspaceCommand::Get => {
|
||||
print_workspace_info(sway_connection.get_workspaces().unwrap());
|
||||
if cli.monitor.unwrap() {
|
||||
sway::monitor_events(vec![EventType::Workspace], cli, config);
|
||||
}
|
||||
},
|
||||
WorkspaceCommand::Rename => todo!(),
|
||||
}
|
||||
}
|
||||
Commands::Launch { program } => {
|
||||
trace!("{:?}",config.programs);
|
||||
match profile_launch(config.profiles,config.programs, program) {
|
||||
Ok(p) => {
|
||||
trace!("{}",p);
|
||||
match run_sway_command(&mut sway_connection,p) {
|
||||
Ok(_) => debug!("launch successful"),
|
||||
Err(e) => error_handler(e),
|
||||
}
|
||||
},
|
||||
Err(e) => error_handler(e),
|
||||
}
|
||||
},
|
||||
Commands::Lock { force_render_background } => {
|
||||
match lock_screen(&config, sway_connection, force_render_background.unwrap_or(false)) {
|
||||
Ok(o) => debug!("Screen locked successfully{:?}",o.first().unwrap()),
|
||||
Err(e) => error!("{:?}",e),
|
||||
};
|
||||
},
|
||||
Commands::Profile { profile_command} => {
|
||||
debug!("{:?}",xdg_directories.get_runtime_directory());
|
||||
setup_runtime_dir(xdg_directories.clone());
|
||||
match profile_command {
|
||||
ProfileCommand::Init => {
|
||||
let index_start = if config.index_starts_at_1 { 1 } else { 0 };
|
||||
let initial_workspace = config.initial_workspace;
|
||||
debug!("profile index will start at {}",index_start);
|
||||
let active_profile_index = match profile::active_profile_index() {
|
||||
Ok(i) => i,
|
||||
Err(_) => 0,
|
||||
};
|
||||
// todo!();
|
||||
// match profile::profile_from_index(config.profiles.clone(), 0) {
|
||||
// Ok(_) => {
|
||||
match switch_by_index(config, active_profile_index, sway_connection, initial_workspace) {
|
||||
Ok(_) => info!("successfully initialized sway-profiles-rs"),
|
||||
Err(e) => error_handler(e),
|
||||
}
|
||||
// },
|
||||
// Err(e) => error_handler(e),
|
||||
// }
|
||||
},
|
||||
ProfileCommand::Switch { switch_command} => {
|
||||
let initial_workspace = config.initial_workspace;
|
||||
match switch_command {
|
||||
Some(ProfileSwitchCommand::To { index,name,query }) => {
|
||||
match index {
|
||||
Some(i) => {
|
||||
match profile::switch_by_index(config, *i, sway_connection, initial_workspace) {
|
||||
Ok(_) => info!("successfully switched to profile at index {}",i),
|
||||
Err(e) => error_handler(e),
|
||||
};
|
||||
},
|
||||
None => {
|
||||
match name {
|
||||
Some(n) => {
|
||||
match profile::switch_by_name(config, n.to_string(), sway_connection) {
|
||||
Ok(_) => (),
|
||||
Err(e) => error_handler(e),
|
||||
}
|
||||
},
|
||||
None => match query {
|
||||
Some(q) => match q.parse::<usize>() {
|
||||
Ok(i) => match profile::switch_by_index(config.clone(), i, sway_connection, initial_workspace) {
|
||||
Ok(_) => (),
|
||||
Err(_) => match profile::switch_by_name(config, q.to_string(), Connection::new().unwrap()) {
|
||||
Ok(_) => (),
|
||||
Err(_) => error_handler(ErrorMessage { message: format!("Could not find profile with index or name: {}",q), code: 4 }),
|
||||
},
|
||||
},
|
||||
Err(_) => match profile::switch_by_name(config, q.to_string(), sway_connection) {
|
||||
Ok(_) => (),
|
||||
Err(_) => error_handler(ErrorMessage { message: format!("Could not find profile with index or name: {}",q), code: 4 }),
|
||||
},
|
||||
},
|
||||
None => error_handler(ErrorMessage { message: "No profile index or name provided.".to_string(), code: 4 }),
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
Some(ProfileSwitchCommand::Next) => match profile::next(config, sway_connection) {
|
||||
Ok(_) => info!("Successfully switched to next profile"),
|
||||
Err(e) => error_handler(e),
|
||||
},
|
||||
Some(ProfileSwitchCommand::Prev) => match profile::previous(config, sway_connection) {
|
||||
Ok(_) => info!("successfully switched to previous profile"),
|
||||
Err(e) => error_handler(e),
|
||||
},
|
||||
None => {
|
||||
for profile in config.profiles {
|
||||
println!("{} {}", profile.icon, profile.name);
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
ProfileCommand::Get { get_command, monitor } => {
|
||||
match Some(get_command) {
|
||||
Some(&g) => {
|
||||
println!("{}",get::profile_info(config.profiles.clone(),g).unwrap());
|
||||
if monitor.unwrap() {
|
||||
let _ = get::watch(config, g,xdg_directories.runtime_dir.unwrap().join("sway-profiles-rs/active-profile.json").to_str().unwrap().to_string());
|
||||
}
|
||||
},
|
||||
None => error_handler(ErrorMessage { message: "No matching Profile Detail".to_string(), code: 4 }),
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Commands::Shortcuts { global: _ } => {
|
||||
todo!()
|
||||
},
|
||||
Commands::Power { power_command } => {
|
||||
let action_prompt = match power_command {
|
||||
PowerCommand::Shutdown => vec!["shut down","poweroff.target"],
|
||||
PowerCommand::Reboot => vec!["restart","reboot.target"],
|
||||
PowerCommand::Suspend => vec!["suspend","suspend.target"],
|
||||
PowerCommand::Lock { force_render_background: _ } => vec!["lock"],
|
||||
PowerCommand::Logout => vec!["log out"],
|
||||
};
|
||||
let action = action_prompt.iter().nth(0).unwrap_or(&"not found");
|
||||
if let Ok(dialog::Choice::Yes) = dialog::Question::new(format!("Are you sure you would like to {}?",action)).title(format!("Confirm {}...",action)).show() {
|
||||
match power_command {
|
||||
PowerCommand::Lock { force_render_background } => {
|
||||
debug!("running lock_screen function");
|
||||
let _ = lock_screen(&config, sway_connection, force_render_background.unwrap_or(false));
|
||||
},
|
||||
PowerCommand::Logout => {
|
||||
debug!("sending exit command to sway");
|
||||
let _ = run_sway_command(&mut sway_connection, "exit".to_string());
|
||||
},
|
||||
_ => {
|
||||
let target = action_prompt.iter().nth(1).unwrap_or(&"not found");
|
||||
let systemctl = systemctl::SystemCtl::default();
|
||||
debug!("looking for {}",target);
|
||||
if let Ok(true) = systemctl.exists(target) {
|
||||
debug!("found and starting {}",target);
|
||||
let _ = systemctl.start(&target);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
fn main() -> Result<(),()> {
|
||||
env_logger::init();
|
||||
// let xdg_directories = BaseDirectories::new();
|
||||
let cli = Cli::parse();
|
||||
let config = if confy::get_configuration_file_path("sway-desktop-utils", "config").expect("Unable to determine config file location").exists() {
|
||||
confy::load("sway-desktop-utils", "config").unwrap_or_default()
|
||||
} else {
|
||||
let _ = confy::store("sway-desktop-utils", "config", Config::default());
|
||||
Config::default()
|
||||
};
|
||||
|
||||
let sdu_result = match &cli.command {
|
||||
Commands::Sway { sway_command } => sway(sway_command, &config.window_icons),
|
||||
Commands::Launch { program } => launch(program, config.profiles, config.programs),
|
||||
Commands::Lock { force_render_background } => lock(config.lock, force_render_background.unwrap_or_default()),
|
||||
Commands::Profile { profile_command} => profile(profile_command, config.profiles, config.preserve_keyboard_order),
|
||||
Commands::Shortcuts { shortcut_command } => shortcuts(shortcut_command, config.scripts),
|
||||
Commands::Power { power_command } => power(power_command, config.lock)
|
||||
};
|
||||
match sdu_result {
|
||||
Ok(_) => debug!("Command ran successfully"),
|
||||
Err(e) => error!("SDU Encountered an error: {}", e.message),
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
|
|
|||
0
src/utils.rs
Normal file
0
src/utils.rs
Normal file
Loading…
Add table
Reference in a new issue