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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions rust/Cargo.toml.in
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ version = "0.0.0" # This is a placeholder version
edition = "2021"

[dependencies]
clap = { version = "3.2", features = ["derive"] }
serde_json = "1.0"
log = "0.4"
simplelog = "0.12"
libwmctl = { version = "0.0.51", optional = true }
serde_json = { version = "1.0", optional = true }
x11rb = { version = "0.13", optional = true }
xcb = { version = "1", optional = true }

[dev-dependencies]
ctor = "0.1"
[features]
default = ["i3", "xcb"]
i3 = ["dep:serde_json"]
xcb = ["dep:xcb"]
wmctl = ["dep:libwmctl", "dep:x11rb"]
2 changes: 1 addition & 1 deletion rust/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ dist/i3switch: target/x86_64-unknown-linux-gnu/release/i3switch

.PHONY: test
test: Cargo.toml $(SOURCE_FILES)
cargo test
cargo test --all-features

.PHONY: all
all: dist/i3switch
Expand Down
20 changes: 20 additions & 0 deletions rust/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,23 @@ or install to rust binary directory (must be in PATH to be usable) with:
To build tests just use make.

make test

### Build features

The project provides option to enable or disable features. By default features that
are known to be stable are enabled, and features that are known to be unstable
are disabled. You can enable or disable features by setting `RUSTFLAGS` environment
variable before running `make`:

# enable unstable features
RUSTFLAGS="--all-features" \
make
# enable stable features
RUSTFLAGS="-F i3,xcb --no-default-features" \
make

#### Available features:

- `i3`: i3ipc-based backend for window switching (default)
- `xcb`: xcb-based backend for window switching (default)
- `wmctl`: wmctl-based backend for window switching (non-default)
69 changes: 69 additions & 0 deletions rust/src/backend/backend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#[cfg(feature = "i3")]
use crate::backend::i3;
#[cfg(feature = "wmctl")]
use crate::backend::wmctl;
#[cfg(feature = "xcb")]
use crate::backend::xcb;

use crate::backend::traits::*;
use crate::types::Windows;

pub enum UsedBackend {
#[cfg(feature = "i3")]
I3(i3::Backend),
#[cfg(feature = "wmctl")]
WmCtl(wmctl::Backend),
#[cfg(feature = "xcb")]
Xcb(xcb::Backend),
}

pub struct Backend {
used_backend: UsedBackend,
}

impl Backend {
pub fn new(use_backend: UsedBackend) -> Self {
Self {
used_backend: use_backend,
}
}
}

impl GetTabs for Backend {
fn get_tabs(&self) -> Result<Windows, String> {
match self.used_backend {
#[cfg(feature = "i3")]
UsedBackend::I3(ref i3) => i3.get_tabs(),
#[cfg(feature = "wmctl")]
UsedBackend::WmCtl(ref wmctl) => wmctl.get_tabs(),
#[cfg(feature = "xcb")]
UsedBackend::Xcb(ref xcb) => xcb.get_tabs(),
}
}
}

impl GetVisible for Backend {
fn get_visible(&self) -> Result<Windows, String> {
match self.used_backend {
#[cfg(feature = "i3")]
UsedBackend::I3(ref i3) => i3.get_visible(),
#[cfg(feature = "wmctl")]
UsedBackend::WmCtl(ref wmctl) => wmctl.get_visible(),
#[cfg(feature = "xcb")]
UsedBackend::Xcb(ref xcb) => xcb.get_visible(),
}
}
}

impl SetFocus for Backend {
fn set_focus(& mut self, id: &u64) {
match self.used_backend {
#[cfg(feature = "i3")]
UsedBackend::I3(ref mut i3) => i3.set_focus(id),
#[cfg(feature = "wmctl")]
UsedBackend::WmCtl(ref mut wmctl) => wmctl.set_focus(id),
#[cfg(feature = "xcb")]
UsedBackend::Xcb(ref mut xcb) => xcb.set_focus(id),
}
}
}
60 changes: 60 additions & 0 deletions rust/src/backend/i3/backend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use crate::backend::traits::*;
use crate::logging::ResultExt;
use crate::logging;
use crate::types::Windows;
use super::client::{Client, Request};
use super::compass;

use serde_json as json;
use std::process;

pub struct Backend {
client: Client,
root: json::Value,
}

impl Backend {
pub fn new() -> Self {
// Establish a connection to the i3 IPC server and get the tree structure
let i3_socket_path_output = process::Command::new("i3").arg("--get-socketpath").output()
.expect_log("Failed to get i3 socket path");
let i3_path = String::from_utf8(i3_socket_path_output.stdout)
.expect_log("Failed to parse i3 socket path output");
let mut client = Client::new(&i3_path.trim())
.expect_log("Failed to connect to i3 IPC server");
let root_string = client.request(Request::GetTree, "")
.expect_log("Failed to get i3 tree JSON");

// Parse the i3 tree to get the current workspace and window information
let root = json::from_str::<json::Value>(&root_string)
.expect_log("Failed to parse i3 tree JSON");
Self {
client,
root,
}
}
}

impl GetTabs for Backend {
fn get_tabs(&self) -> Result<Windows, String> {
let nodes = compass::available_tabs(&self.root);
Ok(compass::to_windows(nodes))
}
}

impl GetVisible for Backend {
fn get_visible(&self) -> Result<Windows, String> {
let nodes = compass::visible_nodes(&self.root);
Ok(compass::to_windows(nodes))
}
}

impl SetFocus for Backend {
fn set_focus(& mut self, window_id: &u64) {
// Focus the window with the determined ID
logging::info!("Focusing window with ID: {}", window_id);
let payload = format!("[con_id={}] focus", window_id);
self.client.request(Request::Command, &payload)
.expect_log("Failed to send focus command");
}
}
File renamed without changes.
Loading
Loading