-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit ca6a583
Showing
8 changed files
with
624 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/target | ||
/Cargo.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"), | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(), | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.