swaysome-2.1.2/.cargo_vcs_info.json0000644000000001360000000000100127050ustar { "git": { "sha1": "bc80f6a650483fd7014ddb5d3538752472970ab3" }, "path_in_vcs": "" }swaysome-2.1.2/.gitignore000064400000000000000000000000101046102023000134540ustar 00000000000000/target swaysome-2.1.2/.gitlab-ci.yml000064400000000000000000000004741046102023000141360ustar 00000000000000image: "rust:latest" lint:cargo: stage: build script: - rustc --version && cargo --version - rustup component add rustfmt - cargo fmt build:cargo: stage: build script: - rustc --version && cargo --version - cargo build --release artifacts: paths: - target/release/swaysomeswaysome-2.1.2/Cargo.lock0000644000000163430000000000100106670ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "anstream" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys", ] [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "clap" version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" dependencies = [ "clap_builder", "clap_derive", ] [[package]] name = "clap_builder" version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", ] [[package]] name = "clap_derive" version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck", "proc-macro2", "quote", "syn", ] [[package]] name = "clap_lex" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "itoa" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "proc-macro2" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "ryu" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "serde" version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "swaysome" version = "2.1.2" dependencies = [ "byteorder", "clap", "serde", "serde_json", ] [[package]] name = "syn" version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" swaysome-2.1.2/Cargo.toml0000644000000022570000000000100107110ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "swaysome" version = "2.1.2" authors = [ "Skia ", "Nabos ", ] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "swaysome provides an awesome way to manage your multiple outputs with the sway windows manager" homepage = "https://gitlab.com/hyask/swaysome" readme = "README.md" license = "MIT" repository = "https://gitlab.com/hyask/swaysome" [[bin]] name = "swaysome" path = "src/main.rs" [dependencies.byteorder] version = "1.5.0" [dependencies.clap] version = "4.4.8" features = ["derive"] [dependencies.serde] version = "1.0.192" features = ["derive"] [dependencies.serde_json] version = "1.0.108" swaysome-2.1.2/Cargo.toml.orig000064400000000000000000000011421046102023000143620ustar 00000000000000[package] name = "swaysome" version = "2.1.2" authors = ["Skia ", "Nabos "] edition = "2021" description = "swaysome provides an awesome way to manage your multiple outputs with the sway windows manager" license = "MIT" repository = "https://gitlab.com/hyask/swaysome" homepage = "https://gitlab.com/hyask/swaysome" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] byteorder = "1.5.0" clap = { version = "4.4.8", features = ["derive"] } serde = { version = "1.0.192", features = ["derive"] } serde_json = "1.0.108" swaysome-2.1.2/LICENSE000064400000000000000000000020551046102023000125040ustar 00000000000000Copyright © 2021 Skia Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. swaysome-2.1.2/README.md000064400000000000000000000077751046102023000127740ustar 00000000000000# Swaysome An awesome way to manage your workspaces on [sway](https://swaywm.org/). Join us on [#swaysome:matrix.hya.sk](https://matrix.to/#/%23swaysome:matrix.hya.sk)! ## Description This binary helps you configure sway to work a bit more like [AwesomeWM](https://awesomewm.org/). This means that **workspaces** are namespaced in what are called **workspace groups**, and **workspace groups** can be moved around the differents outputs easily. For example, with workspace `11` on the first output, and workspace `21` on the second output, triggering the `swaysome focus 1` shortcut to get workspace `1` would lead you to workspace `11` if your focus is on the first output, and workspace `21` is the focus is on the second one. By default, `swaysome init` will create a **workspace group** per active output, but you may create other groups while working, by either triggering `swaysome focus-group ` and opening a new window, or sending an existing window to it first with `swaysome move-to-group `. Here is a common use-case for this: * `output-1`: * **workspace group** 1: * workspace `11`: chats * workspace `12`: emails * `output-2`: * **workspace group** 2: * workspace `21`: IDE for first project * workspace `22`: browser for first project * workspace `23`: terminals for first project * **workspace group** 3: * workspace `31`: IDE for second project * workspace `32`: browser for second project * workspace `33`: terminals for second project That way, when `output-2` is focused on **workspace group** 2, be it workspace `21` or `22`, the quick `$mod+` (bound to `swaysome focus `) shortcut won't leave **workspace group** 2, allowing you to open multiple projects in parallel without the hassle of manually remembering how to namespace them. In that situation, suppose you plug in a new output, `output-3`, you may then want to focus **workspace group 3** to send it to `output-3`: this is simply done by typing the shortcuts `$mod+Alt+3` (`swaysome focus-group 3`) then `$mod+Alt+o` (`swaysome workspace-group-next-output`). `swaysome` may also work with i3, but this is untested. `swaysome` should be compatible with [sworkstyle](https://lib.rs/crates/sworkstyle). If this is broken, please report a bug. ## Installation Arch Linux: Found on the AUR as [swaysome-git](https://aur.archlinux.org/packages/swaysome-git). Void Linux: `xbps-install swaysome` If you have Rust installed, then you can just `cargo install swaysome` and you're good to go. Otherwise, you may grab a [pre-built binary](https://gitlab.com/hyask/swaysome/-/jobs/artifacts/master/raw/target/release/swaysome?job=build:cargo) from the CI and put it in your `$PATH`. **WARNING**: please double-check that your `swaysome` binary is in `sway`'s `$PATH`. Depending on your setup, the `$PATH` you have in your shell may not be the same as `sway`'s, and if `swaysome` can't be called by `sway`, the symptoms will only look like non-functional shortcuts. If you're in this situation, a quick workaround is to call `swaysome` with its full absolute path from `sway`'s config to check that everything works before fixing your `$PATH` issue. ## Usage Copy the `swaysome.conf` file in `~/.config/sway/config.d/swaysome.conf`. Then append your `sway` configuration with this: ``` include ~/.config/sway/config.d/*.conf ``` On next startup of `sway`, you should end-up with workspaces from `1` to `0`, prefixed with a screen index, giving you workspace `11` on the first screen, and workspace `21` on the second one, both accessible with shortcut `$mod+1` when focused on the right output. The `init` command simply walks through every screen to initialize a prefixed workspace. It does it backwards so that you end-up focused on the first screen, as usual. ## Exhaustive swaysome commands list Just run `swaysome --help` for the most up to date list of command and documentation. ## Breaking changes * Starting with 2.0, `next_output` and `prev_output` have been changed to `next-output` and `prev-output`. swaysome-2.1.2/pkgbuild/.SRCINFO000064400000000000000000000005241046102023000144030ustar 00000000000000pkgbase = swaysome-git pkgdesc = Awesome WM like workspaces pkgver = 0.0.0 pkgrel = 1 url = https://gitlab.com/hyask/swaysome arch = x86_64 license = MIT makedepends = git makedepends = rust provides = swaysome conflicts = swaysome source = swaysome::git+https://gitlab.com/hyask/swaysome md5sums = SKIP pkgname = swaysome-git swaysome-2.1.2/pkgbuild/PKGBUILD000064400000000000000000000011561046102023000144250ustar 00000000000000# Maintainer: XXX pkgname=swaysome-git _pkgname=swaysome pkgver=0.0.0 pkgrel=1 pkgdesc='Awesome WM like workspaces' arch=('x86_64') url='https://gitlab.com/hyask/swaysome' license=('MIT') makedepends=('git' 'rust') provides=("$_pkgname") conflicts=("$_pkgname") source=("$_pkgname::git+$url") md5sums=('SKIP') pkgver() { cd "$_pkgname" echo $(grep '^version =' Cargo.toml|head -n1|cut -d\" -f2).r$(git rev-list --count HEAD).g$(git rev-parse --short HEAD) } build() { cd "$_pkgname" cargo build --release } package() { cd "$_pkgname" install -Dm755 "target/release/$_pkgname" "$pkgdir/usr/bin/$_pkgname" } swaysome-2.1.2/src/main.rs000064400000000000000000000661311046102023000135650ustar 00000000000000extern crate byteorder; extern crate clap; extern crate serde_json; use serde::{Deserialize, Serialize}; use clap::{Args, Parser, Subcommand}; use std::cell::Cell; use std::env; use std::io::Cursor; use std::io::{Read, Write}; use std::mem; use std::os::unix::net::UnixStream; use std::path::Path; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; // Maximum workspaces per group. This will determine the naming. // Examples: // 10 → 17, 27 // 100 → 107, 207 // 15 → 22, 37 const MAX_GROUP_WS: usize = 10; const RUN_COMMAND: u32 = 0; const GET_WORKSPACES: u32 = 1; // const SUBSCRIBE: u32 = 2; const GET_OUTPUTS: u32 = 3; #[derive(Parser, Debug)] #[clap(author, version, about = "Better multimonitor handling for sway", long_about = None)] #[clap(propagate_version = true)] struct Cli { #[clap(subcommand)] command: Command, } #[derive(Subcommand, Debug)] enum Command { #[clap(about = "Initialize the workspace groups for all the outputs")] Init(Index), #[clap(about = "Move the focused container to another workspace on the same workspace group")] Move(Index), #[clap( about = "Move the focused container to the same workspace index on another workspace group" )] MoveToGroup(Index), #[clap(about = "Focus to another workspace on the same workspace group")] Focus(Index), #[clap(about = "Focus to workspace group")] FocusGroup(Index), #[clap(about = "Focus to another workspace on all the outputs")] FocusAllOutputs(Index), #[clap(about = "Move the focused container to the next output")] NextOutput, #[clap(about = "Move the focused container to the previous output")] PrevOutput, #[clap(about = "Move the focused workspace group to the next output")] WorkspaceGroupNextOutput, #[clap(about = "Move the focused workspace group to the previous output")] WorkspaceGroupPrevOutput, #[clap(about = "Move the focused container to the next group")] NextGroup, #[clap(about = "Move the focused container to the previous group")] PrevGroup, #[clap( about = "Rearrange already opened workspaces to the correct outputs, useful when plugging new monitors" )] RearrangeWorkspaces, } #[derive(Args, Debug)] struct Index { #[clap(value_name = "index", help = "The workspace index to work with")] index: usize, } struct SwaySome { socket: Cell>, outputs: Vec, // current_output: Output, workspaces: Vec, // current_workspace: Workspace, } #[derive(Serialize, Deserialize, Debug)] struct Output { name: String, #[serde(default)] focused: bool, #[serde(default)] active: bool, } #[derive(Serialize, Deserialize, Debug)] struct Workspace { num: usize, output: String, visible: bool, } impl SwaySome { fn new() -> SwaySome { let mut swaysome = SwaySome { socket: Cell::new(Some(SwaySome::get_stream())), outputs: vec![], workspaces: vec![], }; swaysome.outputs = swaysome.get_outputs(); swaysome.workspaces = swaysome.get_workspaces(); swaysome } fn get_stream() -> UnixStream { for socket_var in ["SWAYSOCK", "I3SOCK"] { let socket_path = match env::var(socket_var) { Ok(val) => val, Err(_e) => { eprintln!("{} not found in environment", socket_var); continue; } }; let socket = Path::new(&socket_path); match UnixStream::connect(&socket) { Err(_) => { eprintln!( "counldn't connect to socket '{}' found in ${}", socket_path, socket_var ); continue; } Ok(stream) => { eprintln!( "successful connection to socket '{}' found in ${}", socket_path, socket_var ); return stream; } } } panic!("couldn't find any i3/sway socket") } fn send_msg(&self, msg_type: u32, payload: &str) { let payload_length = payload.len() as u32; let mut msg_prefix: [u8; 6 * mem::size_of::() + 2 * mem::size_of::()] = *b"i3-ipc00000000"; msg_prefix[6..] .as_mut() .write_u32::(payload_length) .expect("Unable to write"); msg_prefix[10..] .as_mut() .write_u32::(msg_type) .expect("Unable to write"); let mut msg: Vec = msg_prefix[..].to_vec(); msg.extend(payload.as_bytes()); let mut socket = self .socket .take() .expect("Unexisting socket, there probably is a logic error"); if socket.write_all(&msg[..]).is_err() { panic!("couldn't send message"); } self.socket.set(Some(socket)); } fn send_command(&self, command: &str) { eprint!("Sending command: '{}' - ", &command); self.send_msg(RUN_COMMAND, command); self.check_success(); } fn read_msg(&self) -> Result { let mut response_header: [u8; 14] = *b"uninitialized."; let mut socket = self .socket .take() .expect("Unexisting socket, there probably is a logic error"); socket.read_exact(&mut response_header).unwrap(); if &response_header[0..6] == b"i3-ipc" { let mut v = Cursor::new(vec![ response_header[6], response_header[7], response_header[8], response_header[9], ]); let payload_length = v.read_u32::().unwrap(); let mut payload = vec![0; payload_length as usize]; socket.read_exact(&mut payload[..]).unwrap(); let payload_str = String::from_utf8(payload).unwrap(); self.socket.set(Some(socket)); Ok(payload_str) } else { eprint!("Not an i3-icp packet, emptying the buffer: "); let mut v = vec![]; socket.read_to_end(&mut v).unwrap(); eprintln!("{:?}", v); self.socket.set(Some(socket)); Err("Unable to read i3-ipc packet") } } fn check_success(&self) { match self.read_msg() { Ok(msg) => { let r: Vec = serde_json::from_str(&msg).unwrap(); match r[0]["success"] { serde_json::Value::Bool(true) => eprintln!("Command successful"), _ => panic!("Command failed: {:#?}", r), } } Err(_) => panic!("Unable to read response"), }; } fn get_outputs(&self) -> Vec { self.send_msg(GET_OUTPUTS, ""); let o = match self.read_msg() { Ok(msg) => msg, Err(_) => panic!("Unable to get outputs"), }; let mut outputs: Vec = serde_json::from_str::>(&o) .unwrap() .into_iter() .filter(|x| x.active) .collect(); outputs.sort_by(|x, y| x.name.cmp(&y.name)); // sort_by_key doesn't work here (https://stackoverflow.com/a/47126516) outputs } fn get_workspaces(&self) -> Vec { self.send_msg(GET_WORKSPACES, ""); let ws = match self.read_msg() { Ok(msg) => msg, Err(_) => panic!("Unable to get current workspace"), }; let mut workspaces: Vec = serde_json::from_str(&ws).unwrap(); workspaces.sort_by_key(|x| x.num); workspaces } fn get_current_output_index(&self) -> usize { // Do not use `self.outputs`, as the information here could be outdated, especially the `focused` attribute let outputs = self.get_outputs(); match outputs.iter().position(|x| x.focused) { Some(i) => i, None => panic!("WTF! No focused output???"), } } fn get_current_output_name(&self) -> String { // Do not use `self.outputs`, as the information here could be outdated, especially the `focused` attribute let outputs = self.get_outputs(); let focused_output_index = match outputs.iter().find(|x| x.focused) { Some(i) => i.name.as_str(), None => panic!("WTF! No focused output???"), }; focused_output_index.to_string() } fn get_current_workspace_index(&self) -> usize { // Do not use `self.outputs`, as the information here could be outdated, especially the `focused` attribute let outputs = self.get_outputs(); // Do not use `self.workspaces`, as the information here could be outdated, especially the `visible` attribute self.get_workspaces() .iter() .find(|w| w.visible && outputs.iter().find(|o| o.name == w.output).unwrap().focused) .unwrap() .num } fn move_container_to_workspace(&self, workspace_index: usize) { if workspace_index < MAX_GROUP_WS { self.move_container_to_workspace_relative(workspace_index); } else { self.move_container_to_workspace_absolute(workspace_index); } } fn move_container_to_workspace_group(&self, target_group: usize) { let current_workspace_index = self.get_current_workspace_index(); let current_workspace_index_relative = (current_workspace_index % MAX_GROUP_WS) as usize; self.move_container_to_workspace_absolute( current_workspace_index_relative + target_group * MAX_GROUP_WS, ); } fn move_container_to_workspace_absolute(&self, workspace_index: usize) { let group_index = (workspace_index / MAX_GROUP_WS) as usize; let full_ws_name = format!( "{}", group_index * MAX_GROUP_WS + workspace_index % MAX_GROUP_WS ); // If the workspace already exists match self.workspaces.iter().find(|w| w.num == workspace_index) { Some(_) => { let mut focus_cmd: String = "move container to workspace number ".to_string(); focus_cmd.push_str(&full_ws_name); self.send_command(&focus_cmd); } None => { let target_group = workspace_index / MAX_GROUP_WS; let target_screen_index = match self .workspaces .iter() .find(|w| w.num / MAX_GROUP_WS == target_group) { // If other workspaces on the same group exists Some(other_workspace) => Some( self.outputs .iter() .enumerate() .find(|i| i.1.name == other_workspace.output) .unwrap() .0, ), None => { // Or if the targeted output is currently connected if group_index < self.outputs.len() { Some(group_index) } else { None } } }; match target_screen_index { Some(target_screen_index) => { let target_output = &self.outputs[target_screen_index]; let current_output_name = self.get_current_output_name(); if target_output.name == current_output_name { let mut focus_cmd: String = "move container to workspace ".to_string(); focus_cmd.push_str(&full_ws_name); self.send_command(&focus_cmd); } else { // If we have to send it to another screen let mut focus_cmd: String = "focus output ".to_string(); focus_cmd.push_str(&target_output.name); self.send_command(&focus_cmd); let focused_workspace_index = self.get_current_workspace_index(); let mut focus_cmd: String = "workspace ".to_string(); focus_cmd.push_str(&full_ws_name); self.send_command(&focus_cmd); let mut focus_cmd: String = "focus output ".to_string(); focus_cmd.push_str(¤t_output_name); self.send_command(&focus_cmd); let mut focus_cmd: String = "move container to workspace ".to_string(); focus_cmd.push_str(&full_ws_name); self.send_command(&focus_cmd); let mut focus_cmd: String = "focus output ".to_string(); focus_cmd.push_str(&target_output.name); self.send_command(&focus_cmd); let mut focus_cmd: String = "workspace ".to_string(); focus_cmd.push_str(&focused_workspace_index.to_string()); self.send_command(&focus_cmd); let mut focus_cmd: String = "focus output ".to_string(); focus_cmd.push_str(¤t_output_name); self.send_command(&focus_cmd); } } None => { // Else, we send the container on the current output let mut focus_cmd: String = "move container to workspace ".to_string(); focus_cmd.push_str(&full_ws_name); self.send_command(&focus_cmd); } }; } } } fn move_container_to_workspace_relative(&self, workspace_index: usize) { let current_workspace_index: usize = self.get_current_workspace_index(); let focused_output_index = current_workspace_index / MAX_GROUP_WS; let mut cmd: String = "move container to workspace number ".to_string(); let full_ws_name = format!("{}", focused_output_index * MAX_GROUP_WS + workspace_index); cmd.push_str(&full_ws_name); self.send_command(&cmd); } fn focus_to_workspace(&self, workspace_index: usize) { if workspace_index < MAX_GROUP_WS { self.focus_to_workspace_relative(workspace_index); } else { self.focus_to_workspace_absolute(workspace_index); } } fn focus_to_workspace_absolute(&self, workspace_index: usize) { let output_index = (workspace_index / MAX_GROUP_WS) as usize; // If the workspace already exists match self.workspaces.iter().find(|w| w.num == workspace_index) { Some(_) => { let mut focus_cmd: String = "workspace number ".to_string(); focus_cmd.push_str(&workspace_index.to_string()); self.send_command(&focus_cmd); } None => { let target_group = workspace_index / MAX_GROUP_WS; let target_screen_index = match self .workspaces .iter() .find(|w| w.num / MAX_GROUP_WS == target_group) { // If other workspaces on the same group exists Some(other_workspace) => Some( self.outputs .iter() .enumerate() .find(|i| i.1.name == other_workspace.output) .unwrap() .0, ), None => { // Or if the targeted output is currently connected if output_index < self.outputs.len() { Some(output_index) } else { None } } }; match target_screen_index { // If we have to send it to another screen Some(target_screen_index) => { let target_output = &self.outputs[target_screen_index - 1]; let mut focus_cmd: String = "focus output ".to_string(); focus_cmd.push_str(&target_output.name); self.send_command(&focus_cmd); } None => {} }; // Then we focus the workspace let mut focus_cmd: String = "workspace number ".to_string(); focus_cmd.push_str(&workspace_index.to_string()); self.send_command(&focus_cmd); } } } fn focus_to_workspace_relative(&self, workspace_index: usize) { let current_workspace_index: usize = self.get_current_workspace_index(); let focused_output_index = current_workspace_index / MAX_GROUP_WS; let mut cmd: String = "workspace number ".to_string(); let full_ws_name = format!("{}", focused_output_index * MAX_GROUP_WS + workspace_index); cmd.push_str(&full_ws_name); self.send_command(&cmd); } fn focus_to_group(&self, group_index: usize) { let current_workspace_index: usize = self.get_current_workspace_index(); let target_workspace_relative_index = current_workspace_index % MAX_GROUP_WS; let target_workspace_index = group_index * MAX_GROUP_WS + target_workspace_relative_index; let full_ws_name = format!( "{}", group_index * MAX_GROUP_WS + target_workspace_relative_index ); // If the workspace already exists match self .workspaces .iter() .find(|w| w.num == target_workspace_index) { Some(_) => { let mut focus_cmd: String = "workspace number ".to_string(); focus_cmd.push_str(&full_ws_name); self.send_command(&focus_cmd); } None => { let target_screen_index = match self .workspaces .iter() .find(|w| w.num / MAX_GROUP_WS == group_index) { // If other workspaces on the same group exists Some(other_workspace) => Some( self.outputs .iter() .enumerate() .find(|i| i.1.name == other_workspace.output) .unwrap() .0 + 1, ), None => { // Or if the targeted output is currently connected if group_index > 0 && group_index <= self.outputs.len() { Some(group_index) } else { None } } }; match target_screen_index { // If we have to send it to another screen Some(target_screen_index) => { let target_output = &self.outputs[target_screen_index - 1]; let mut focus_cmd: String = "focus output ".to_string(); focus_cmd.push_str(&target_output.name); self.send_command(&focus_cmd); } None => {} }; // Then we focus the workspace let mut focus_cmd: String = "workspace number ".to_string(); focus_cmd.push_str(&target_workspace_index.to_string()); self.send_command(&focus_cmd); } } } fn focus_all_outputs_to_workspace(&self, workspace_index: usize) { let current_output = self.get_current_output_name(); // Iterate on all outputs to focus on the given workspace for output in self.outputs.iter() { let mut cmd: String = "focus output ".to_string(); cmd.push_str(output.name.as_str()); self.send_command(&cmd); self.focus_to_workspace(workspace_index); } // Get back to currently focused output let mut cmd: String = "focus output ".to_string(); cmd.push_str(¤t_output); self.send_command(&cmd); } fn move_container_to_next_output(&self) { self.move_container_to_next_or_prev_output(false); } fn move_container_to_prev_output(&self) { self.move_container_to_next_or_prev_output(true); } fn move_container_to_next_or_prev_output(&self, go_to_prev: bool) { let focused_output_index = self.get_current_output_index(); let target_output = if go_to_prev { &self.outputs[(focused_output_index + self.outputs.len() - 1) % self.outputs.len()] } else { &self.outputs[(focused_output_index + 1) % self.outputs.len()] }; let workspaces = self.get_workspaces(); let target_workspace = workspaces .iter() .find(|x| x.output == target_output.name && x.visible) .unwrap(); let group_index = (target_workspace.num / MAX_GROUP_WS) as usize; let full_ws_name = format!( "{}", group_index * MAX_GROUP_WS + target_workspace.num % MAX_GROUP_WS ); // Move container to target workspace let mut cmd: String = "move container to workspace number ".to_string(); cmd.push_str(&full_ws_name); self.send_command(&cmd); // Focus that workspace to follow the container let mut cmd: String = "workspace number ".to_string(); cmd.push_str(&full_ws_name); self.send_command(&cmd); } fn move_workspace_group_to_next_output(&self) { self.move_workspace_group_to_next_or_prev_output(false); } fn move_workspace_group_to_prev_output(&self) { self.move_workspace_group_to_next_or_prev_output(true); } fn move_workspace_group_to_next_or_prev_output(&self, go_to_prev: bool) { let focused_output_index = self.get_current_output_index(); let target_output = if go_to_prev { &self.outputs[(focused_output_index + self.outputs.len() - 1) % self.outputs.len()] } else { &self.outputs[(focused_output_index + 1) % self.outputs.len()] }; let current_workspace = self.get_current_workspace_index(); let current_group_index = (current_workspace / MAX_GROUP_WS) as usize; for workspace in self.get_workspaces() { let ws_index = workspace.num / MAX_GROUP_WS; if ws_index == current_group_index { let cmd: String = format!("workspace number {}", workspace.num); self.send_command(&cmd); let cmd: String = format!("move workspace to {}", target_output.name); self.send_command(&cmd); } } let cmd: String = format!("workspace number {}", current_workspace); self.send_command(&cmd); } fn focus_container_to_next_group(&self) { self.focus_container_to_next_or_prev_group(false); } fn focus_container_to_prev_group(&self) { self.focus_container_to_next_or_prev_group(true); } fn focus_container_to_next_or_prev_group(&self, go_to_prev: bool) { let current_workspace_index: usize = self.get_current_workspace_index(); let focused_group_index = current_workspace_index / MAX_GROUP_WS; let highest_group = self.workspaces.last().unwrap().num / MAX_GROUP_WS; let target_group; if go_to_prev { if focused_group_index == 0 { target_group = highest_group; } else { target_group = focused_group_index - 1; } } else { if focused_group_index >= highest_group { target_group = 0; } else { target_group = focused_group_index + 1; } }; self.focus_to_group(target_group); } fn init_workspaces(&self, workspace_index: usize) { let cmd_prefix: String = "focus output ".to_string(); for output in self.outputs.iter().rev() { let mut cmd = cmd_prefix.clone(); cmd.push_str(output.name.as_str()); self.send_command(&cmd); let mut cmd: String = "workspace number ".to_string(); let full_ws_name = format!( "{}", (self.get_current_output_index() + 1) * MAX_GROUP_WS + workspace_index ); cmd.push_str(&full_ws_name); self.send_command(&cmd); } } fn rearrange_workspaces(&self) { let focus_cmd_prefix: String = "workspace number ".to_string(); let move_cmd_prefix: String = "move workspace to ".to_string(); for workspace in self.workspaces.iter() { let mut focus_cmd = focus_cmd_prefix.clone(); focus_cmd.push_str(&workspace.num.to_string()); self.send_command(&focus_cmd); let group_index = workspace.num / MAX_GROUP_WS; if group_index <= self.outputs.len() - 1 { let mut move_cmd = move_cmd_prefix.clone(); move_cmd.push_str(&self.outputs[group_index.max(1) - 1].name); self.send_command(&move_cmd); } } } } fn main() { let cli = Cli::parse(); let swaysome = SwaySome::new(); match &cli.command { Command::Init(action) => { swaysome.init_workspaces(action.index); } Command::Move(action) => { swaysome.move_container_to_workspace(action.index); } Command::MoveToGroup(action) => { swaysome.move_container_to_workspace_group(action.index); } Command::Focus(action) => { swaysome.focus_to_workspace(action.index); } Command::FocusGroup(action) => { swaysome.focus_to_group(action.index); } Command::FocusAllOutputs(action) => { swaysome.focus_all_outputs_to_workspace(action.index); } Command::NextOutput => { swaysome.move_container_to_next_output(); } Command::PrevOutput => { swaysome.move_container_to_prev_output(); } Command::WorkspaceGroupNextOutput => { swaysome.move_workspace_group_to_next_output(); } Command::WorkspaceGroupPrevOutput => { swaysome.move_workspace_group_to_prev_output(); } Command::NextGroup => { swaysome.focus_container_to_next_group(); } Command::PrevGroup => { swaysome.focus_container_to_prev_group(); } Command::RearrangeWorkspaces => { swaysome.rearrange_workspaces(); } } } swaysome-2.1.2/swaysome.1000064400000000000000000000116771046102023000134420ustar 00000000000000.TH SWAYSOME "1" "Feb 2024" "2.1.2" "User Commands" . . .SH NAME swaysome - improve multi-output handling on sway . . .SH SYNOPSIS .B swaysome \fI\,[OPTIONS]\/\fR \fI\,\/\fR . . .SH DESCRIPTION \fBswaysome\fR helps you configure \fBsway\fR to work a bit more like \fBAwesomeWM\fR (\fIhttps://awesomewm\.org/\fR). This means that "\fBworkspaces\fR" are namespaced in what are called "\fBworkspace groups\fR", and "\fBworkspace groups\fR" can be moved around the differents outputs easily. .P For example, with workspace 11 on the first output, and workspace 21 on the second output, triggering the `swaysome focus 1` shortcut to get workspace 1 would lead you to workspace 11 if your focus is on the first output, and workspace 21 is the focus is on the second one. .P By default, `swaysome init` will create a \fBworkspace group\fR per active output, but you may create other groups while working, by either triggering `swaysome focus-group ` and opening a new window, or sending an existing window to it first with `swaysome move-to-group `. .P Here is a common use-case for this: .EX \fBoutput-1\fR: \fBworkspace group 1\fR: workspace 11: chats workspace 12: emails \fBoutput-2\fR: \fBworkspace group 2\fR: workspace 21: IDE for first project workspace 22: browser for first project workspace 23: terminals for first project \fBworkspace group 3\fR: workspace 31: IDE for second project workspace 32: browser for second project workspace 33: terminals for second project .EE .P That way, when \fBoutput-2\fR is focused on \fBworkspace group 2\fR, be it workspace 21 or 22, the quick `$mod+` (bound to `swaysome focus `) shortcut won't leave \fBworkspace group 2\fR, allowing you to open multiple projects in parallel without the hassle of manually remembering how to namespace them. .P In that situation, suppose you plug in a new output, `output-3`, you may then want to focus \fBworkspace group 3\fR to send it to `output-3`: this is simply done by typing the shortcuts `$mod+Alt+3` (`swaysome focus-group 3`) then `$mod+Alt+o` (`swaysome workspace-group-next-output`)\. .P \fBswaysome\fR may also work with \fBi3\fR, but this is untested. Patches welcome if needed. .P \fBswaysome\fR should be compatible with \fBsworkstyle\fR (\fIhttps://lib\.rs/crates/sworkstyle\fR). If this is broken, please report a bug. . . .SH USAGE .P If you installed \fBswaysome\fR from your distribution's package manager, it should have provided your system with the default configuration file. In that case, you can just include from your \fBsway\fR configuration by appending the following to it: include /etc/sway/config.d/swaysome.conf Otherwise, if installing by any other way, copy the \fBswaysome.conf\fR file in \fB~/.config/sway/config.d/swaysome.conf\fR, then append your \fBsway\fR configuration with this: include ~/.config/sway/config.d/sway.conf On next startup of `sway`, you should end-up with workspaces from `1` to `0`, prefixed with a screen index, giving you workspace `11` on the first screen, and workspace `21` on the second one, both accessible with shortcut `$mod+1` when focused on the right output. . . .SH COMMANDS .TP \fBinit\fR \fIINDEX\fR Initialize the workspace groups for all the outputs on workspace \fIINDEX\fR. This command simply walks through every screen to initialize a prefixed workspace. It does it backwards so that you end-up focused on the first screen, as usual. .TP \fBmove\fR \fIINDEX\fR Move the focused container to workspace \fIINDEX\fR (stay in the same workspace group) .TP \fBmove-to-group\fR \fIINDEX\fR Move the focused container to workspace group \fIINDEX\fR (keep the same workspace index) .TP \fBfocus\fR \fIINDEX\fR Focus to workspace \fIINDEX\fR (stay in the same workspace group) .TP \fBfocus-group\fR \fIINDEX\fR Focus to workspace group \fIINDEX\fR (keep the same workspace index) .TP \fBfocus-all-outputs\fR \fIINDEX\fR Focus to workspace \fIINDEX\fR on all the outputs at once (not bound by default) .TP \fBnext-output\fR Move the focused container to the next output .TP \fBprev-output\fR Move the focused container to the previous output .TP \fBworkspace-group-next-output\fR Move the focused workspace group to the next output .TP \fBworkspace-group-prev-output\fR Move the focused workspace group to the previous output .TP \fBnext-group\fR Move the focused container to the next group .TP \fBprev-group\fR Move the focused container to the previous group .TP \fBrearrange-workspaces\fR Rearrange already opened workspaces to the correct outputs, useful when plugging new monitors .TP \fBhelp\fR Print this message or the help of the given subcommand(s) . . .SH OPTIONS .TP \fB-h\fR, \fB--help\fR Print help .TP \fB-V\fR, \fB--version\fR Print version . . .SH "VERSION" 2.1.2 . . .SH "HOMEPAGE" \fIhttps://gitlab.com/hyask/swaysome\fP .sp Please report any bug or feature requests on the bug tracker. . . .SH AUTHORS Florent 'Skia' Jacquet <\fIskia@hya.sk\fP> swaysome-2.1.2/swaysome.conf000064400000000000000000000056261046102023000142240ustar 00000000000000# Use (un)bindcode or (un)bindsym, depending on what you used in your main sway config file. # The `--no-warn` setting is only added to shortcuts that exist in the default config. You may want to add or remove # that flag on some bindings depending on your config. # Change focus between workspaces bindsym --no-warn $mod+1 exec "swaysome focus 1" bindsym --no-warn $mod+2 exec "swaysome focus 2" bindsym --no-warn $mod+3 exec "swaysome focus 3" bindsym --no-warn $mod+4 exec "swaysome focus 4" bindsym --no-warn $mod+5 exec "swaysome focus 5" bindsym --no-warn $mod+6 exec "swaysome focus 6" bindsym --no-warn $mod+7 exec "swaysome focus 7" bindsym --no-warn $mod+8 exec "swaysome focus 8" bindsym --no-warn $mod+9 exec "swaysome focus 9" bindsym --no-warn $mod+0 exec "swaysome focus 0" # Move containers between workspaces bindsym --no-warn $mod+Shift+1 exec "swaysome move 1" bindsym --no-warn $mod+Shift+2 exec "swaysome move 2" bindsym --no-warn $mod+Shift+3 exec "swaysome move 3" bindsym --no-warn $mod+Shift+4 exec "swaysome move 4" bindsym --no-warn $mod+Shift+5 exec "swaysome move 5" bindsym --no-warn $mod+Shift+6 exec "swaysome move 6" bindsym --no-warn $mod+Shift+7 exec "swaysome move 7" bindsym --no-warn $mod+Shift+8 exec "swaysome move 8" bindsym --no-warn $mod+Shift+9 exec "swaysome move 9" bindsym --no-warn $mod+Shift+0 exec "swaysome move 0" # Focus workspace groups bindsym $mod+Alt+1 exec "swaysome focus-group 1" bindsym $mod+Alt+2 exec "swaysome focus-group 2" bindsym $mod+Alt+3 exec "swaysome focus-group 3" bindsym $mod+Alt+4 exec "swaysome focus-group 4" bindsym $mod+Alt+5 exec "swaysome focus-group 5" bindsym $mod+Alt+6 exec "swaysome focus-group 6" bindsym $mod+Alt+7 exec "swaysome focus-group 7" bindsym $mod+Alt+8 exec "swaysome focus-group 8" bindsym $mod+Alt+9 exec "swaysome focus-group 9" bindsym $mod+Alt+0 exec "swaysome focus-group 0" # Move containers to other workspace groups bindsym $mod+Alt+Shift+1 exec "swaysome move-to-group 1" bindsym $mod+Alt+Shift+2 exec "swaysome move-to-group 2" bindsym $mod+Alt+Shift+3 exec "swaysome move-to-group 3" bindsym $mod+Alt+Shift+4 exec "swaysome move-to-group 4" bindsym $mod+Alt+Shift+5 exec "swaysome move-to-group 5" bindsym $mod+Alt+Shift+6 exec "swaysome move-to-group 6" bindsym $mod+Alt+Shift+7 exec "swaysome move-to-group 7" bindsym $mod+Alt+Shift+8 exec "swaysome move-to-group 8" bindsym $mod+Alt+Shift+9 exec "swaysome move-to-group 9" bindsym $mod+Alt+Shift+0 exec "swaysome move-to-group 0" # Move focused container to next output bindsym $mod+o exec "swaysome next-output" # Move focused container to previous output bindsym $mod+Shift+o exec "swaysome prev-output" # Move focused workspace group to next output bindsym $mod+Alt+o exec "swaysome workspace-group-next-output" # Move focused workspace group to previous output bindsym $mod+Alt+Shift+o exec "swaysome workspace-group-prev-output" # Init workspaces for every screen exec "swaysome init 1"