diff --git a/Cargo.lock b/Cargo.lock index 52f4422a..8c76439d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -270,10 +270,16 @@ checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.2.10", "winapi 0.3.9", ] +[[package]] +name = "fixedbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + [[package]] name = "fs-tail" version = "0.1.4" @@ -423,6 +429,15 @@ dependencies = [ "libc", ] +[[package]] +name = "instant" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "iovec" version = "0.1.4" @@ -465,6 +480,7 @@ dependencies = [ "komorebi-core", "lazy_static", "nanoid", + "parking_lot", "paste", "serde", "serde_json", @@ -524,6 +540,15 @@ version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" +[[package]] +name = "lock_api" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.14" @@ -726,12 +751,50 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55" +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +dependencies = [ + "backtrace", + "cfg-if 1.0.0", + "instant", + "libc", + "petgraph", + "redox_syscall 0.2.10", + "smallvec", + "thread-id", + "winapi 0.3.9", +] + [[package]] name = "paste" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58" +[[package]] +name = "petgraph" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "pin-project-lite" version = "0.2.7" @@ -894,6 +957,12 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + [[package]] name = "redox_syscall" version = "0.2.10" @@ -910,7 +979,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ "getrandom", - "redox_syscall", + "redox_syscall 0.2.10", ] [[package]] @@ -1106,6 +1175,17 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thread-id" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" +dependencies = [ + "libc", + "redox_syscall 0.1.57", + "winapi 0.3.9", +] + [[package]] name = "thread_local" version = "1.1.3" diff --git a/README.md b/README.md index d7e5f349..f73b86a7 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,8 @@ This means that: ## Getting Started +### GitHub Releases + Prebuilt binaries are available on the [releases page](https://github.com/LGUG2Z/komorebi/releases) in a `zip` archive. Once downloaded, you will need to move the `komorebi.exe` and `komorebic.exe` binaries to a directory in your `Path` ( you can see these directories by running `$Env:Path.split(";")` at a PowerShell prompt). @@ -68,6 +70,8 @@ using [`setx`](https://docs.microsoft.com/en-us/windows-server/administration/wi Variables pop up in System Properties Advanced (which can be launched with `SystemPropertiesAdvanced.exe` at a PowerShell prompt), and then move the binaries to that directory. +### Scoop + If you use the [Scoop](https://scoop.sh/) command line installer, you can run the following commands to install the binaries from the latest GitHub Release: @@ -79,6 +83,8 @@ scoop install komorebi If you install _komorebi_ using Scoop, the binaries will automatically be added to your `Path` and a command will be shown for you to run in order to get started using the sample configuration file. +### Building from Source + If you prefer to compile _komorebi_ from source, you will need a [working Rust development environment on Windows 10](https://rustup.rs/). The `x86_64-pc-windows-msvc` toolchain is required, so make sure you have also installed @@ -91,6 +97,8 @@ cargo install --path komorebi --locked cargo install --path komorebic --locked ``` +### Running + Once you have either the prebuilt binaries in your `Path`, or have compiled the binaries from source (these will already be in your `Path` if you installed Rust with [rustup](https://rustup.rs), which you absolutely should), you can run `komorebic start` at a Powershell prompt, and you will see the following output: @@ -102,6 +110,8 @@ Start-Process komorebi -WindowStyle hidden This means that `komorebi` is now running in the background, tiling all your windows, and listening for commands sent to it by `komorebic`. You can similarly stop the process by running `komorebic stop`. +### Configuring + Once `komorebi` is running, you can execute the `komorebi.sample.ahk` script to set up the default keybindings via AHK (the file includes comments to help you start building your own configuration). @@ -113,6 +123,8 @@ the `AutoHotKey64.exe` executable for AutoHotKey v2 is in your `Path`. If both ` exist in your home directory, only `komorebi.ahk` will be loaded. An example of an AutoHotKey v2 configuration file for _komorebi_ can be found [here](https://gist.github.com/crosstyan/dafacc0778dabf693ce9236c57b201cd). +### Common First-Time Troubleshooting + If you are experiencing behaviour where [closing a window leaves a blank tile, but minimizing the same window does not](https://github.com/LGUG2Z/komorebi/issues/6) , you have probably enabled a 'close/minimize to tray' option for that application. You can tell _komorebi_ to handle @@ -123,7 +135,7 @@ komorebic.exe identify-tray-application exe Discord.exe komorebic.exe identify-tray-application exe Telegram.exe ``` -## Configuration +## Configuration with `komorebic` As previously mentioned, this project does not handle anything related to keybindings and shortcuts directly. I personally use AutoHotKey to manage my window management shortcuts, and have provided a @@ -151,6 +163,7 @@ focus-workspace Focus the specified workspace on the focused monito new-workspace Create and append a new workspace on the focused monitor adjust-container-padding Adjust container padding on the focused workspace adjust-workspace-padding Adjust workspace padding on the focused workspace +change-layout Set the layout on the focused workspace flip-layout Flip the layout on the focused workspace (BSP only) promote Promote the focused window to the top of the tree retile Force the retiling of all managed windows @@ -164,7 +177,7 @@ toggle-pause Toggle the window manager on and off across all mon toggle-tiling Toggle window tiling on the focused workspace toggle-float Toggle floating mode for the focused window toggle-monocle Toggle monocle mode for the focused container -toggle-maximize Toggle native window fullscreen for the focused window +toggle-maximize Toggle native maximization for the focused window restore-windows Restore all hidden windows (debugging command) reload-configuration Reload ~/komorebi.ahk (if it exists) watch-configuration Toggle the automatic reloading of ~/komorebi.ahk (if it exists) @@ -242,9 +255,22 @@ ensures that all hidden windows are restored before termination. If however, you ever end up with windows that are hidden and cannot be restored, a list of window handles known to `komorebi` are stored and continuously updated in `~/komorebi.hwnd.json`. +### Restoring Windows + Running `komorebic restore-windows` will read the list of window handles and forcibly restore them, regardless of whether the main `komorebi` process is running. +### Panics and Deadlocks + +If `komorebi` ever stops responding, it is most likely either due to either a panic or a deadlock. In the case of a +panic, this will be reported in the log. In the case of a deadlock, there will not be any errors in the log, but the +process and the log will appear frozen. + +If you believe you have encountered a deadlock, you can compile `komorebi` with `--feature deadlock_detection` and try +reproducing the deadlock again. This will check for deadlocks every 5 seconds in the background, and if a deadlock is +found, information about it will appear in the log which can be shared when opening an issu which can be shared when +opening an issue. + ## Window Manager State and Integrations The current state of the window manager can be queried using the `komorebic state` command, which returns a JSON diff --git a/komorebi/Cargo.toml b/komorebi/Cargo.toml index 479ba6c0..48727931 100644 --- a/komorebi/Cargo.toml +++ b/komorebi/Cargo.toml @@ -20,6 +20,7 @@ getset = "0.1" hotwatch = "0.4" lazy_static = "1" nanoid = "0.4" +parking_lot = { version = "0.11", features = ["deadlock_detection"] } paste = "1" serde = { version = "1", features = ["derive"] } serde_json = "1" @@ -29,4 +30,7 @@ tracing = "0.1" tracing-appender = "0.1" tracing-subscriber = "0.2" uds_windows = "1" -which = "4" \ No newline at end of file +which = "4" + +[features] +deadlock_detection = [] \ No newline at end of file diff --git a/komorebi/src/main.rs b/komorebi/src/main.rs index f119a49c..4208fc73 100644 --- a/komorebi/src/main.rs +++ b/komorebi/src/main.rs @@ -3,13 +3,19 @@ use std::process::Command; use std::sync::Arc; -use std::sync::Mutex; +#[cfg(feature = "deadlock_detection")] +use std::thread; +#[cfg(feature = "deadlock_detection")] +use std::time::Duration; use color_eyre::eyre::ContextCompat; use color_eyre::Result; use crossbeam_channel::Receiver; use crossbeam_channel::Sender; use lazy_static::lazy_static; +#[cfg(feature = "deadlock_detection")] +use parking_lot::deadlock; +use parking_lot::Mutex; use sysinfo::SystemExt; use tracing_appender::non_blocking::WorkerGuard; use tracing_subscriber::layer::SubscriberExt; @@ -54,7 +60,7 @@ lazy_static! { "chrome.exe".to_string(), "idea64.exe".to_string(), "ApplicationFrameHost.exe".to_string(), - "steam.exe".to_string() + "steam.exe".to_string(), ])); static ref OBJECT_NAME_CHANGE_ON_LAUNCH: Arc>> = Arc::new(Mutex::new(vec![ "firefox.exe".to_string(), @@ -161,6 +167,29 @@ pub fn load_configuration() -> Result<()> { Ok(()) } +#[cfg(feature = "deadlock_detection")] +#[tracing::instrument] +fn detect_deadlocks() { + // Create a background thread which checks for deadlocks every 10s + thread::spawn(move || loop { + tracing::info!("running deadlock detector"); + thread::sleep(Duration::from_secs(5)); + let deadlocks = deadlock::check_deadlock(); + if deadlocks.is_empty() { + continue; + } + + tracing::error!("{} deadlocks detected", deadlocks.len()); + for (i, threads) in deadlocks.iter().enumerate() { + tracing::error!("deadlock #{}", i); + for t in threads { + tracing::error!("thread id: {:#?}", t.thread_id()); + tracing::error!("{:#?}", t.backtrace()); + } + } + }); +} + #[tracing::instrument] fn main() -> Result<()> { match std::env::args().count() { @@ -176,6 +205,9 @@ fn main() -> Result<()> { // File logging worker guard has to have an assignment in the main fn to work let (_guard, _color_guard) = setup()?; + #[cfg(feature = "deadlock_detection")] + detect_deadlocks(); + let process_id = WindowsApi::current_process_id(); WindowsApi::allow_set_foreground_window(process_id)?; @@ -189,7 +221,7 @@ fn main() -> Result<()> { incoming, )))?)); - wm.lock().unwrap().init()?; + wm.lock().init()?; listen_for_commands(wm.clone()); listen_for_events(wm.clone()); @@ -210,7 +242,7 @@ fn main() -> Result<()> { "received ctrl-c, restoring all hidden windows and terminating process" ); - wm.lock().unwrap().restore_all_windows(); + wm.lock().restore_all_windows(); std::process::exit(130); } _ => Ok(()), diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index 01824bee..8bed0fb2 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -3,11 +3,11 @@ use std::io::BufReader; use std::io::Write; use std::str::FromStr; use std::sync::Arc; -use std::sync::Mutex; use std::thread; use color_eyre::eyre::ContextCompat; use color_eyre::Result; +use parking_lot::Mutex; use uds_windows::UnixStream; use komorebi_core::ApplicationIdentifier; @@ -26,7 +26,6 @@ use crate::TRAY_AND_MULTI_WINDOW_EXES; pub fn listen_for_commands(wm: Arc>) { let listener = wm .lock() - .unwrap() .command_listener .try_clone() .expect("could not clone unix listener"); @@ -35,7 +34,7 @@ pub fn listen_for_commands(wm: Arc>) { tracing::info!("listening"); for client in listener.incoming() { match client { - Ok(stream) => match wm.lock().unwrap().read_commands(stream) { + Ok(stream) => match wm.lock().read_commands(stream) { Ok(()) => {} Err(error) => tracing::error!("{}", error), }, @@ -74,19 +73,19 @@ impl WindowManager { self.set_workspace_padding(monitor_idx, workspace_idx, size)?; } SocketMessage::FloatClass(target) => { - let mut float_classes = FLOAT_CLASSES.lock().unwrap(); + let mut float_classes = FLOAT_CLASSES.lock(); if !float_classes.contains(&target) { float_classes.push(target); } } SocketMessage::FloatExe(target) => { - let mut float_exes = FLOAT_EXES.lock().unwrap(); + let mut float_exes = FLOAT_EXES.lock(); if !float_exes.contains(&target) { float_exes.push(target); } } SocketMessage::FloatTitle(target) => { - let mut float_titles = FLOAT_TITLES.lock().unwrap(); + let mut float_titles = FLOAT_TITLES.lock(); if !float_titles.contains(&target) { float_titles.push(target); } @@ -183,13 +182,13 @@ impl WindowManager { } SocketMessage::IdentifyTrayApplication(identifier, id) => match identifier { ApplicationIdentifier::Exe => { - let mut exes = TRAY_AND_MULTI_WINDOW_EXES.lock().unwrap(); + let mut exes = TRAY_AND_MULTI_WINDOW_EXES.lock(); if !exes.contains(&id) { exes.push(id); } } ApplicationIdentifier::Class => { - let mut classes = TRAY_AND_MULTI_WINDOW_CLASSES.lock().unwrap(); + let mut classes = TRAY_AND_MULTI_WINDOW_CLASSES.lock(); if !classes.contains(&id) { classes.push(id); } diff --git a/komorebi/src/process_event.rs b/komorebi/src/process_event.rs index 5bb816dc..884aa793 100644 --- a/komorebi/src/process_event.rs +++ b/komorebi/src/process_event.rs @@ -1,11 +1,11 @@ use std::fs::OpenOptions; use std::sync::Arc; -use std::sync::Mutex; use std::thread; use color_eyre::eyre::ContextCompat; use color_eyre::Result; use crossbeam_channel::select; +use parking_lot::Mutex; use komorebi_core::OperationDirection; use komorebi_core::Rect; @@ -20,7 +20,7 @@ use crate::TRAY_AND_MULTI_WINDOW_EXES; #[tracing::instrument] pub fn listen_for_events(wm: Arc>) { - let receiver = wm.lock().unwrap().incoming_events.lock().unwrap().clone(); + let receiver = wm.lock().incoming_events.lock().clone(); thread::spawn(move || { tracing::info!("listening"); @@ -28,7 +28,7 @@ pub fn listen_for_events(wm: Arc>) { select! { recv(receiver) -> mut maybe_event => { if let Ok(event) = maybe_event.as_mut() { - match wm.lock().unwrap().process_event(event) { + match wm.lock().process_event(event) { Ok(()) => {}, Err(error) => tracing::error!("{}", error) } @@ -91,21 +91,28 @@ impl WindowManager { } WindowManagerEvent::Hide(_, window) => { + let mut hide = false; // Some major applications unfortunately send the HIDE signal when they are being // minimized or destroyed. Applications that close to the tray also do the same, // and will have is_window() return true, as the process is still running even if // the window is not visible. - let tray_and_multi_window_exes = TRAY_AND_MULTI_WINDOW_EXES.lock().unwrap(); - let tray_and_multi_window_classes = TRAY_AND_MULTI_WINDOW_CLASSES.lock().unwrap(); - - // We don't want to purge windows that have been deliberately hidden by us, eg. when - // they are not on the top of a container stack. - let programmatically_hidden_hwnds = HIDDEN_HWNDS.lock().unwrap(); - - if (!window.is_window() || tray_and_multi_window_exes.contains(&window.exe()?)) - || tray_and_multi_window_classes.contains(&window.class()?) - && !programmatically_hidden_hwnds.contains(&window.hwnd) { + let tray_and_multi_window_exes = TRAY_AND_MULTI_WINDOW_EXES.lock(); + let tray_and_multi_window_classes = TRAY_AND_MULTI_WINDOW_CLASSES.lock(); + + // We don't want to purge windows that have been deliberately hidden by us, eg. when + // they are not on the top of a container stack. + let programmatically_hidden_hwnds = HIDDEN_HWNDS.lock(); + + if (!window.is_window() || tray_and_multi_window_exes.contains(&window.exe()?)) + || tray_and_multi_window_classes.contains(&window.class()?) + && !programmatically_hidden_hwnds.contains(&window.hwnd) + { + hide = true; + } + } + + if hide { self.focused_workspace_mut()?.remove_window(window.hwnd)?; self.update_focused_workspace(false)?; } @@ -120,6 +127,12 @@ impl WindowManager { return Ok(()); } + if let Some(w) = workspace.maximized_window() { + if w.hwnd == window.hwnd { + return Ok(()); + } + } + self.focused_workspace_mut()? .focus_container_by_window(window.hwnd)?; } diff --git a/komorebi/src/window.rs b/komorebi/src/window.rs index 24fb5c03..c5ccd9fc 100644 --- a/komorebi/src/window.rs +++ b/komorebi/src/window.rs @@ -110,7 +110,7 @@ impl Window { } pub fn hide(self) { - let mut programmatically_hidden_hwnds = HIDDEN_HWNDS.lock().unwrap(); + let mut programmatically_hidden_hwnds = HIDDEN_HWNDS.lock(); if !programmatically_hidden_hwnds.contains(&self.hwnd) { programmatically_hidden_hwnds.push(self.hwnd); } @@ -119,7 +119,7 @@ impl Window { } pub fn restore(self) { - let mut programmatically_hidden_hwnds = HIDDEN_HWNDS.lock().unwrap(); + let mut programmatically_hidden_hwnds = HIDDEN_HWNDS.lock(); if let Some(idx) = programmatically_hidden_hwnds .iter() .position(|&hwnd| hwnd == self.hwnd) @@ -131,7 +131,7 @@ impl Window { } pub fn maximize(self) { - let mut programmatically_hidden_hwnds = HIDDEN_HWNDS.lock().unwrap(); + let mut programmatically_hidden_hwnds = HIDDEN_HWNDS.lock(); if let Some(idx) = programmatically_hidden_hwnds .iter() .position(|&hwnd| hwnd == self.hwnd) @@ -204,9 +204,9 @@ impl Window { #[tracing::instrument(fields(exe, title))] pub fn should_manage(self, event: Option) -> Result { - let classes = FLOAT_CLASSES.lock().unwrap(); - let exes = FLOAT_EXES.lock().unwrap(); - let titles = FLOAT_TITLES.lock().unwrap(); + let classes = FLOAT_CLASSES.lock(); + let exes = FLOAT_EXES.lock(); + let titles = FLOAT_TITLES.lock(); if self.title().is_err() { return Ok(false); @@ -239,7 +239,7 @@ impl Window { } } - let allow_layered = LAYERED_EXE_WHITELIST.lock().unwrap().contains(&exe_name); + let allow_layered = LAYERED_EXE_WHITELIST.lock().contains(&exe_name); let style = self.style()?; let ex_style = self.ex_style()?; diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index ba8e036d..5effa7fb 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -3,7 +3,6 @@ use std::io::ErrorKind; use std::num::NonZeroUsize; use std::path::PathBuf; use std::sync::Arc; -use std::sync::Mutex; use std::thread; use color_eyre::eyre::ContextCompat; @@ -11,6 +10,7 @@ use color_eyre::Result; use crossbeam_channel::Receiver; use hotwatch::notify::DebouncedEvent; use hotwatch::Hotwatch; +use parking_lot::Mutex; use serde::Serialize; use uds_windows::UnixListener; @@ -63,12 +63,12 @@ impl From<&mut WindowManager> for State { Self { monitors: wm.monitors.clone(), is_paused: wm.is_paused, - float_classes: FLOAT_CLASSES.lock().unwrap().clone(), - float_exes: FLOAT_EXES.lock().unwrap().clone(), - float_titles: FLOAT_TITLES.lock().unwrap().clone(), - layered_exe_whitelist: LAYERED_EXE_WHITELIST.lock().unwrap().clone(), - tray_and_multi_window_exes: TRAY_AND_MULTI_WINDOW_EXES.lock().unwrap().clone(), - tray_and_multi_window_classes: TRAY_AND_MULTI_WINDOW_CLASSES.lock().unwrap().clone(), + float_classes: FLOAT_CLASSES.lock().clone(), + float_exes: FLOAT_EXES.lock().clone(), + float_titles: FLOAT_TITLES.lock().clone(), + layered_exe_whitelist: LAYERED_EXE_WHITELIST.lock().clone(), + tray_and_multi_window_exes: TRAY_AND_MULTI_WINDOW_EXES.lock().clone(), + tray_and_multi_window_classes: TRAY_AND_MULTI_WINDOW_CLASSES.lock().clone(), } } } diff --git a/komorebi/src/windows_callbacks.rs b/komorebi/src/windows_callbacks.rs index 293fb5cf..89daa5a2 100644 --- a/komorebi/src/windows_callbacks.rs +++ b/komorebi/src/windows_callbacks.rs @@ -80,7 +80,7 @@ pub extern "system" fn win_event_hook( // // [yatta\src\windows_event.rs:110] event = 32780 ObjectNameChange // [yatta\src\windows_event.rs:110] event = 32779 ObjectLocationChange - let object_name_change_on_launch = OBJECT_NAME_CHANGE_ON_LAUNCH.lock().unwrap(); + let object_name_change_on_launch = OBJECT_NAME_CHANGE_ON_LAUNCH.lock(); if let Ok(exe) = window.exe() { if winevent == WinEvent::ObjectNameChange { @@ -101,7 +101,6 @@ pub extern "system" fn win_event_hook( if should_manage { WINEVENT_CALLBACK_CHANNEL .lock() - .unwrap() .0 .send(event_type) .expect("could not send message on WINEVENT_CALLBACK_CHANNEL"); diff --git a/komorebi/src/winevent_listener.rs b/komorebi/src/winevent_listener.rs index 7d979a91..10f63f81 100644 --- a/komorebi/src/winevent_listener.rs +++ b/komorebi/src/winevent_listener.rs @@ -1,13 +1,13 @@ use std::sync::atomic::AtomicIsize; use std::sync::atomic::Ordering; use std::sync::Arc; -use std::sync::Mutex; use std::thread; use std::time::Duration; use crossbeam_channel::Receiver; use crossbeam_channel::Sender; use lazy_static::lazy_static; +use parking_lot::Mutex; use bindings::Windows::Win32::Foundation::HWND; use bindings::Windows::Win32::UI::Accessibility::SetWinEventHook; @@ -43,7 +43,7 @@ pub fn new(outgoing: Arc>>) -> WinEventListener impl WinEventListener { pub fn start(self) { let hook = self.hook.clone(); - let outgoing = self.outgoing_events.lock().unwrap().clone(); + let outgoing = self.outgoing_events.lock().clone(); thread::spawn(move || unsafe { let hook_ref = SetWinEventHook( @@ -61,7 +61,7 @@ impl WinEventListener { // The code in the callback doesn't work in its own loop, needs to be within // the MessageLoop callback for the winevent callback to even fire MessageLoop::start(10, |_msg| { - if let Ok(event) = WINEVENT_CALLBACK_CHANNEL.lock().unwrap().1.try_recv() { + if let Ok(event) = WINEVENT_CALLBACK_CHANNEL.lock().1.try_recv() { match outgoing.send(event) { Ok(_) => {} Err(error) => {