use crate::KeyCode;
use crate::data_types::{KeyBindings, Config};
use crate::listeners::winevent::WinEventListener;
use crate::ring::{Ring, Selector};
use crate::virtual_desktop::VirtualDesktop;
use crate::windows::{get_all_windows, Window};
use crossbeam_channel::{Receiver, bounded, Sender};
use std::collections::HashMap;
use std::thread;
use winapi::shared::windef::HWND;
use winapi::um::winuser::GetForegroundWindow;
use winvd::helpers::{
get_desktop_number_by_window,
go_to_desktop_number,
move_window_to_desktop_number,
rename_desktop_number,
};
use winvd::{
Desktop,
VirtualDesktopEvent,
create_desktop,
get_desktops,
get_event_receiver
};
pub enum Event {
KeyPress(KeyCode),
VirtualDesktopEvent(VirtualDesktopEvent),
WinEvent(WinEvent),
}
#[derive(Debug)]
pub enum WinEvent {
Foreground(usize),
MinimizeStart(usize),
MinimizeEnd(usize),
MoveSizeStart(usize),
MoveSizeEnd(usize),
}
pub struct WindowManager {
event_sender: Sender<Event>,
event_receiver: Receiver<Event>,
threads: Vec<thread::JoinHandle<()>>,
virtual_desktops: Ring<VirtualDesktop>,
virtual_desktop_ids: Vec<Desktop>,
hwnd_desktop: HashMap<HWND, usize>,
}
impl WindowManager {
pub fn init(config: Config) -> WindowManager {
let (sender, receiver) = bounded::<Event>(0);
WindowManager {
event_sender: sender,
event_receiver: receiver,
threads: Vec::new(),
virtual_desktops: Ring::new(config.virtual_desktops.iter()
.map(|name| VirtualDesktop::new(*name)).collect()),
virtual_desktop_ids: get_desktops().unwrap(),
hwnd_desktop: HashMap::new(),
}
}
pub fn run(mut self, mut key_bindings: KeyBindings) {
let hotkey_list = key_bindings.iter().map(|(key, _)| key.clone()).collect();
self.init_virtual_desktops();
self.init_windows();
self.init_virtual_desktop_event_listener();
self.init_keyboard_listener(hotkey_list);
self.init_window_event_listener();
loop {
match self.event_receiver.recv().unwrap() {
Event::KeyPress(key_code) =>
key_bindings.get_mut(&key_code).unwrap()(&mut self),
Event::VirtualDesktopEvent(event) =>
self.handle_virtual_desktop_event(event),
Event::WinEvent(event) => self.handle_win_event(event),
};
}
}
fn init_keyboard_listener(&mut self, hotkey_list: Vec<KeyCode>) {
let event_sender = self.event_sender.clone();
self.threads.push(thread::spawn(move || {
let mut hk = hotkey::Listener::new();
for keycode in hotkey_list {
let sender = event_sender.clone();
hk.register_hotkey(
keycode.mask, keycode.code,
move || sender.send(Event::KeyPress(keycode)).unwrap()
).unwrap();
}
hk.listen();
}));
}
fn init_virtual_desktop_event_listener(&mut self) {
let event_sender = self.event_sender.clone();
self.threads.push(thread::spawn(move || {
let receiver = get_event_receiver();
loop {
let event = receiver.recv()
.expect("Failed to receive a VirtualDesktopEvent");
if let Err(_) = event_sender.send(Event::VirtualDesktopEvent(event)) {
println!("Failed to send a VirtualDesktopEvent");
}
}
}));
}
fn init_window_event_listener(&mut self) {
let sender = self.event_sender.clone();
self.threads.push(thread::spawn(move || {
WinEventListener::init(sender).listen();
}));
}
fn handle_virtual_desktop_event(&mut self, event: VirtualDesktopEvent) {
match event {
VirtualDesktopEvent::DesktopChanged(_old, new) => self.handle_desktop_changed(&new),
VirtualDesktopEvent::DesktopCreated(desk) =>
println!("<- New desktop created {:?}", desk),
VirtualDesktopEvent::DesktopDestroyed(desk) =>
println!("<- Desktop destroyed {:?}", desk),
VirtualDesktopEvent::WindowChanged(hwnd) => self.handle_window_changed(hwnd as HWND),
};
}
fn handle_desktop_changed(&mut self, new: &Desktop) {
match self.virtual_desktop_ids.iter().position(|desktop: &Desktop| desktop == new) {
Some(vd_index) => {
match self.virtual_desktops.focus(&Selector::Index(vd_index)) {
Some(vd) => vd.grab_focus(),
None => println!("Did not find a desktop to focus. Did you manipulate \
virtual desktops manually after the start of the program?"),
}
}
None => println!("Did not find a desktop to switch. Did you manipulate \
virtual desktops manually after the start of the program?"),
}
}
fn handle_window_changed(&mut self, hwnd: HWND) {
match self.hwnd_desktop.get(&hwnd) {
Some(old_desktop_number) => {
match get_desktop_number_by_window(hwnd as u32) {
Ok(new_desktop_number) => {
println!("Window {:?} moved from desktop {} to desktop {}",
hwnd, old_desktop_number, new_desktop_number);
if *old_desktop_number != new_desktop_number {
let window = self.virtual_desktops
.get_mut(*old_desktop_number)
.expect(format!("Internal error: no desktop number {}",
old_desktop_number).as_str())
.pull_window_by_hwnd(hwnd)
.expect(format!(
"Internal error: window {:?} is found in self.hwnd_desktop on \
the desktop number {}, but the correspondent window is not \
present in the virtual desktop ring.",
hwnd, old_desktop_number).as_str());
self.virtual_desktops
.get_mut(new_desktop_number)
.expect(format!(
"Internal error: no desktop number {}",
new_desktop_number).as_str())
.push_window(window);
self.hwnd_desktop.insert(hwnd, new_desktop_number);
}
}
Err(_) => {
println!("Destroyed window {:?}", hwnd);
self.virtual_desktops
.get_mut(*old_desktop_number)
.expect(format!("Internal error: no desktop number {}",
old_desktop_number).as_str())
.pull_window_by_hwnd(hwnd)
.expect(format!(
"Internal error: window {:?} is found in self.hwnd_desktop on \
the desktop number {}, but the correspondent window is not \
present in the virtual desktop ring.",
hwnd, old_desktop_number).as_str());
self.hwnd_desktop.remove(&hwnd);
}
}
}
None => {
match get_desktop_number_by_window(hwnd as u32) {
Ok(desktop_number) => {
println!("Created new window {:?} on desktop {}", hwnd, desktop_number);
let window = Window::init(hwnd);
let vd = self.virtual_desktops
.get_mut(desktop_number)
.expect(format!(
"Internal error: no desktop number {}", desktop_number).as_str());
vd.push_window(window);
vd.update_focus();
self.hwnd_desktop.insert(hwnd, desktop_number);
},
Err(error) => println!(
"Created new window {:?}, but could not determine its desktop: {:?}",
hwnd, error),
}
}
}
}
fn handle_win_event(&mut self, event: WinEvent) {
match event {
WinEvent::Foreground(hwnd) => {
let hwnd= hwnd as HWND;
match self.hwnd_desktop.get(&hwnd) {
Some(desktop_number) => {
println!(
"Window {:?} went foreground", hwnd
);
self.virtual_desktops
.get_mut(*desktop_number)
.expect("Desktop is not present, try relaunching maybe?")
.set_focus_by_hwnd(hwnd);
}
None => println!(
"Window {:?} went foreground, but it is not present in \
self.hwnd_desktop mapping", hwnd
),
};
}
WinEvent::MinimizeStart(hwnd) => {
let hwnd= hwnd as HWND;
match self.hwnd_desktop.get_mut(&hwnd) {
Some(desktop_number) => {
println!(
"Window {:?} was minimized", hwnd
);
self.virtual_desktops
.get_mut(*desktop_number)
.expect("Desktop is not present, try relaunching maybe?")
.set_minimized(hwnd);
}
None => println!(
"Window {:?} was minimized, but it is not present in \
self.hwnd_desktop mapping", hwnd
),
}
},
WinEvent::MinimizeEnd(hwnd) => {
let hwnd= hwnd as HWND;
match self.hwnd_desktop.get_mut(&hwnd) {
Some(desktop_number) => {
println!(
"Window {:?} was unminimized", hwnd
);
let vd = self.virtual_desktops
.get_mut(*desktop_number)
.expect("Desktop is not present, try relaunching maybe?");
vd.set_unminimized(hwnd);
vd.update_focus();
}
None => println!(
"Window {:?} was minimized, but it is not present in \
self.hwnd_desktop mapping", hwnd
),
}
},
WinEvent::MoveSizeStart(_) => {}
WinEvent::MoveSizeEnd(_) => {}
}
}
fn init_virtual_desktops(&mut self) {
let n_desktops_current = self.virtual_desktop_ids.len();
let n_desktops = self.virtual_desktops.len();
if n_desktops_current < n_desktops {
for _ in 0..(n_desktops - n_desktops_current) {
self.virtual_desktop_ids.push(create_desktop().unwrap());
}
}
for (i, desktop) in self.virtual_desktops.iter().enumerate() {
rename_desktop_number(i, &*desktop.name).unwrap();
}
}
fn init_windows(&mut self) {
let windows = get_all_windows().unwrap();
for window in windows {
match window.desktop_number() {
Ok(n) => {
println!("Found window {:?} on desktop {}", window.hwnd(), n);
self.hwnd_desktop.insert(window.hwnd(), n);
self.virtual_desktops
.get_mut(n)
.expect(format!("Could not find a desktop number {}", n).as_str())
.push_window(window);
},
Err(_) => {},
}
}
let focused_window = unsafe { GetForegroundWindow() };
let focused_window_desktop = self.hwnd_desktop
.get(&focused_window)
.expect("Focused window was not enumerated in all windows, try relaunching may be?");
self.virtual_desktops
.get_mut(*focused_window_desktop)
.expect("Focused window desktop is larger than expected number of desktops")
.set_focus_by_hwnd(focused_window);
}
pub fn exit(&mut self) {
std::process::exit(0)
}
pub fn go_to_virtual_desktop(&mut self, number: usize) {
if go_to_desktop_number(number).is_err() {
println!("Could not go to the desktop number {}", number)
}
}
pub fn move_window_to_desktop(&mut self, number: usize) {
let foreground_window: HWND = unsafe { GetForegroundWindow() };
if move_window_to_desktop_number(foreground_window as winvd::HWND, number).is_err() {
println!("Could not move window {:?} to desktop number {}", foreground_window, number);
}
}
pub fn echo(&self, str: &str) {
println!("{}", str);
}
}