fix(wm): prevent hidden_hwnds deadlock

I used a parking_lot to detect what I suspected to be the deadlock
resulting in issue #13.

I was pleasantly surprised by the alternative to std::sync::Mutex
provided by parking_lot, especially not having to unlock it to use it,
and of course the excellent and intuitive (even if experimental)
deadlock detector.

I have decided to use parking_lot::Mutex as an almost-drop-in
replacement for std::sync::Mutex, as I expect that this isn't the last
time komorebi will have a deadlocking issue, and I have put the deadlock
detection code which runs in a separate thread behind a
"deadlock_detection" feature.

The actual deadlock itself was solved by scoping the first lock in the
handler for WindowManagerEvent::Hide and then executing any required
operations (some of which, like window.maximize(), may require another
lock on HIDDEN_HWNDS) in a separate scope once the previous lock has
been dropped.

In the future I should look at integrating globals like HIDDEN_HWNDS
into WindowManager in a way that won't lead to double-mutable-borrow
issues.

fix #13
This commit is contained in:
LGUG2Z
2021-08-18 19:01:59 -07:00
parent 98f731ba13
commit 209cd82892
10 changed files with 202 additions and 49 deletions

84
Cargo.lock generated
View File

@@ -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"

View File

@@ -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

View File

@@ -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"
which = "4"
[features]
deadlock_detection = []

View File

@@ -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<Mutex<Vec<String>>> = 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(()),

View File

@@ -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<Mutex<WindowManager>>) {
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<Mutex<WindowManager>>) {
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);
}

View File

@@ -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<Mutex<WindowManager>>) {
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<Mutex<WindowManager>>) {
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)?;
}

View File

@@ -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<WindowManagerEvent>) -> Result<bool> {
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()?;

View File

@@ -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(),
}
}
}

View File

@@ -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");

View File

@@ -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<Mutex<Sender<WindowManagerEvent>>>) -> 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) => {