Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
x1b6e6 committed Oct 15, 2021
0 parents commit ca6a583
Show file tree
Hide file tree
Showing 8 changed files with 624 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/target
/Cargo.lock
26 changes: 26 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "sway-alttab"
description = "alt-tab window switch implementaion for sway"
version = "0.1.0"
authors = [
"x1b6e6 <[email protected]>"
]
edition = "2018"

[dependencies]
evdev-rs-tokio = "0.4"
clap = "2.33"
nix = "0.22"

[dependencies.tokio]
version = "1.12.0"
features = [
"fs",
"macros",
"rt",
"rt-multi-thread",
"sync",
]

[dependencies.swayipc]
git = "https://github.com/jaycefayne/swayipc-rs"
53 changes: 53 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use clap::{App, AppSettings, Arg};

const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
const PKG_NAME: &str = env!("CARGO_PKG_NAME");
const PKG_AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
const PKG_DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION");

pub fn build_app() -> App<'static, 'static> {
let clap_color_setting = if std::env::var_os("NO_COLOR").is_none() {
AppSettings::ColoredHelp
} else {
AppSettings::ColorNever
};

App::new(PKG_NAME)
.version(PKG_VERSION)
.author(PKG_AUTHORS)
.about(PKG_DESCRIPTION)
.setting(clap_color_setting)
.arg(
Arg::with_name("device")
.short("d")
.long("device")
.value_name("DEVICE")
.help("keyboard device")
.takes_value(true)
.required(true),
)
.arg(
Arg::with_name("alt")
.long("key-alt")
.value_name("KEY_ALT")
.help("key for alt-tab with alt behavior")
.takes_value(true)
.default_value("KEY_LEFTALT"),
)
.arg(
Arg::with_name("shift")
.long("key-shift")
.value_name("KEY_SHIFT")
.help("key for inverse direction of alt-tab")
.takes_value(true)
.default_value("KEY_LEFTSHIFT"),
)
.arg(
Arg::with_name("tab")
.long("key-tab")
.value_name("KEY_TAB")
.help("key for alt-tab with tab behavior")
.takes_value(true)
.default_value("KEY_TAB"),
)
}
89 changes: 89 additions & 0 deletions src/keyboard.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use {
super::stack_holder::{Action, Direction},
evdev_rs_tokio::{
enums::{EventCode, EV_KEY},
InputEvent, ReadFlag, ReadStatus, UninitDevice,
},
std::sync::{mpsc::Sender, Arc, Mutex},
tokio::fs::File,
};

pub struct Keyboard {
meta_pressed: bool,
reverse_pressed: bool,
key_alt: EV_KEY,
key_tab: EV_KEY,
key_shift: EV_KEY,
sender: Arc<Mutex<Sender<Action>>>,
}

impl Keyboard {
pub fn new(
key_alt: EV_KEY,
key_tab: EV_KEY,
key_shift: EV_KEY,
sender: &Arc<Mutex<Sender<Action>>>,
) -> Self {
Self {
meta_pressed: false,
reverse_pressed: false,
key_alt,
key_tab,
key_shift,
sender: sender.clone(),
}
}

pub async fn wait(&mut self, filename: String) {
let f = File::open(filename);
let u_d = UninitDevice::new().unwrap();
let d = u_d.set_file(f.await.unwrap()).unwrap();

loop {
let ev = d.next_event(ReadFlag::NORMAL | ReadFlag::BLOCKING);
if let Ok(ev) = ev {
if let ReadStatus::Success = ev.0 {
self.process_event(ev.1).await;
} else {
dbg!("ignore sync event");
}
}
}
}

async fn process_event(&mut self, event: InputEvent) {
if let EventCode::EV_KEY(key) = event.event_code {
if self.key_alt == key {
self.meta_pressed = event.value != 0;
if event.value == 0 {
// end
self.sender
.lock()
.unwrap()
.send(Action::PreviewEnd)
.unwrap();
}
} else if self.key_shift == key {
self.reverse_pressed = event.value != 0;
} else if self.key_tab == key {
if event.value == 1 && self.meta_pressed {
if !self.reverse_pressed {
// normal
self.sender
.lock()
.unwrap()
.send(Action::Preview(Direction::Next))
.unwrap();
} else {
// reverse
self.sender
.lock()
.unwrap()
.send(Action::Preview(Direction::Prev))
.unwrap();
}
}
}
}
}
}
42 changes: 42 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use {
evdev_rs_tokio::enums::EV_KEY,
nix::unistd::{setuid, Uid},
std::str::FromStr,
};

mod app;
mod keyboard;
mod stack_holder;
mod sway_listener;
mod window_stack;

#[tokio::main]
async fn main() {
let args = app::build_app().get_matches_from(std::env::args_os());

let device = args.value_of("device").unwrap();
let key_alt = args.value_of("alt").unwrap();
let key_shift = args.value_of("shift").unwrap();
let key_tab = args.value_of("tab").unwrap();

let key_error = |key| format!("incorrect key {}", key);

let mut stack_holder = stack_holder::Service::new();
let sender = stack_holder.run_daemon().unwrap();

let mut sway_listener = sway_listener::Listener::new();
sway_listener.run_daemon(&sender).unwrap();

setuid(Uid::from_raw(0)).expect("error in setuid");

let mut kb = keyboard::Keyboard::new(
EV_KEY::from_str(key_alt).expect(&key_error(key_alt)),
EV_KEY::from_str(key_tab).expect(&key_error(key_tab)),
EV_KEY::from_str(key_shift).expect(&key_error(key_shift)),
&sender,
);
kb.wait(device.into()).await;

stack_holder.join().unwrap();
sway_listener.join().unwrap();
}
164 changes: 164 additions & 0 deletions src/stack_holder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
use {
super::window_stack,
std::any::Any,
std::sync::mpsc::{channel, Receiver, Sender},
std::sync::{Arc, Mutex},
std::thread,
swayipc::Connection,
};

#[derive(Debug)]
pub enum Direction {
Next,
Prev,
}

#[derive(Debug)]
pub enum Action {
MoveUp(i64),
Add(i64),
Remove(i64),
Preview(Direction),
PreviewEnd,
}

pub struct Service {
worker: Option<(thread::JoinHandle<()>, Arc<Mutex<Sender<Action>>>)>,
}

impl Service {
pub fn new() -> Self {
Self { worker: None }
}
pub fn run_daemon(
&mut self,
) -> Result<Arc<Mutex<Sender<Action>>>, Box<dyn Any + Send + 'static>> {
if let None = self.worker {
let (sender, receiver) = channel();
let mtx = Mutex::from(sender);
let sender = Arc::new(mtx);
self.worker = Some((
thread::spawn(move || SubWorker::new().run(receiver)),
Arc::clone(&sender),
));
Ok(sender)
} else {
Err(Box::<()>::from(()))
}
}

pub fn join(&mut self) -> Result<(), Box<(dyn Any + Send + 'static)>> {
if let Some((w, _)) = std::mem::replace(&mut self.worker, None) {
w.join()
} else {
Err(Box::<()>::from(()))
}
}
}

struct SubWorker {
window_stack: window_stack::WindowStack,
preview_depth: usize,
in_preview: bool,
}

impl SubWorker {
pub fn new() -> Self {
Self {
window_stack: window_stack::WindowStack::new(),
preview_depth: 0,
in_preview: false,
}
}

fn move_up(&mut self, id: i64) {
if !self.in_preview {
dbg!(self.window_stack.move_up(id));
}
}
fn add(&mut self, id: i64) {
dbg!(self.window_stack.add(id));
}
fn remove(&mut self, id: i64) {
dbg!(self.window_stack.remove(id));
}
fn preview_end(&mut self) {
if let Some(id) = self.window_stack.get(self.preview_depth) {
self.window_stack.move_up(id);
}
self.preview_depth = 0;
self.in_preview = false;
}
fn preview_next(&mut self) {
self.in_preview = true;
let mut depth = self.preview_depth;
let mut sway = Connection::new().unwrap();

depth += 1;

let id = if let Some(id) = self.window_stack.get(depth) {
id
} else if depth >= self.window_stack.depth() {
depth = 0;
if let Some(id) = self.window_stack.get(depth) {
id
} else {
dbg!("no windows");
return;
}
} else {
dbg!("unknown error");
return;
};

let command = format!("[con_id={}] focus", id);
dbg!(&command);
sway.run_command(command).unwrap();
self.preview_depth = depth;
}
fn preview_prev(&mut self) {
self.in_preview = true;
let mut depth = self.preview_depth as isize;
let mut sway = Connection::new().unwrap();

depth -= 1;

let id = if let Some(id) = self.window_stack.get(depth as usize) {
id
} else if depth <= -1 {
depth = self.window_stack.depth() as isize - 1;

if let Some(id) = self.window_stack.get(depth as usize) {
id
} else {
dbg!("no windows");
return;
}
} else {
dbg!("unknown error");
return;
};

let command = format!("[con_id={}] focus", id);
dbg!(&command);
sway.run_command(command).unwrap();
self.preview_depth = depth as usize;
}

pub fn run(&mut self, receiver: Receiver<Action>) -> ! {
loop {
let action = receiver.recv().unwrap();
dbg!(&action);
match action {
Action::MoveUp(id) => self.move_up(id),
Action::Add(id) => self.add(id),
Action::Remove(id) => self.remove(id),
Action::Preview(dir) => match dir {
Direction::Next => self.preview_next(),
Direction::Prev => self.preview_prev(),
},
Action::PreviewEnd => self.preview_end(),
}
}
}
}
Loading

0 comments on commit ca6a583

Please sign in to comment.