1
1
#![ deny( clippy:: unwrap_used, clippy:: expect_used) ]
2
2
3
3
use crate :: border_manager;
4
+ use crate :: Window ;
4
5
use crate :: WindowManager ;
6
+
7
+ use crossbeam_channel:: Receiver ;
8
+ use crossbeam_channel:: Sender ;
5
9
use parking_lot:: Mutex ;
10
+ use std:: collections:: HashMap ;
6
11
use std:: sync:: Arc ;
12
+ use std:: sync:: OnceLock ;
7
13
use std:: time:: Duration ;
14
+ use lazy_static:: lazy_static;
8
15
9
- pub fn watch_for_orphans ( wm : Arc < Mutex < WindowManager > > ) {
16
+ lazy_static ! {
17
+ pub static ref HWNDS_CACHE : Arc <Mutex <HashMap <isize , ( usize , usize ) >>> = Arc :: new( Mutex :: new( HashMap :: new( ) ) ) ;
18
+ }
19
+
20
+ pub struct OrphanNotification ( pub HashMap < isize , ( usize , usize ) > ) ;
21
+ pub struct HwndsNotification ( pub Vec < isize > ) ;
22
+
23
+ static ORPHAN_CHANNEL : OnceLock < ( Sender < OrphanNotification > , Receiver < OrphanNotification > ) > = OnceLock :: new ( ) ;
24
+ static HWNDS_CHANNEL : OnceLock < ( Sender < HwndsNotification > , Receiver < HwndsNotification > ) > = OnceLock :: new ( ) ;
25
+
26
+ pub fn orphan_channel ( ) -> & ' static ( Sender < OrphanNotification > , Receiver < OrphanNotification > ) {
27
+ ORPHAN_CHANNEL . get_or_init ( || crossbeam_channel:: bounded ( 50 ) )
28
+ }
29
+
30
+ fn orphan_event_tx ( ) -> Sender < OrphanNotification > {
31
+ orphan_channel ( ) . 0 . clone ( )
32
+ }
33
+
34
+ fn orphan_event_rx ( ) -> Receiver < OrphanNotification > {
35
+ orphan_channel ( ) . 1 . clone ( )
36
+ }
37
+
38
+ pub fn hwnds_channel ( ) -> & ' static ( Sender < HwndsNotification > , Receiver < HwndsNotification > ) {
39
+ HWNDS_CHANNEL . get_or_init ( || crossbeam_channel:: bounded ( 50 ) )
40
+ }
41
+
42
+ fn hwnds_event_tx ( ) -> Sender < HwndsNotification > {
43
+ hwnds_channel ( ) . 0 . clone ( )
44
+ }
45
+
46
+ fn hwnds_event_rx ( ) -> Receiver < HwndsNotification > {
47
+ hwnds_channel ( ) . 1 . clone ( )
48
+ }
49
+
50
+ pub fn send_notification ( hwnds : HashMap < isize , ( usize , usize ) > ) {
51
+ if orphan_event_tx ( ) . try_send ( OrphanNotification ( hwnds) ) . is_err ( ) {
52
+ tracing:: warn!( "channel is full; dropping notification" )
53
+ }
54
+ }
55
+
56
+ pub fn listen_for_notifications ( wm : Arc < Mutex < WindowManager > > ) {
57
+ watch_for_orphans ( wm. clone ( ) ) ;
10
58
std:: thread:: spawn ( move || loop {
11
- match find_orphans ( wm. clone ( ) ) {
59
+ match handle_notifications ( wm. clone ( ) ) {
12
60
Ok ( ( ) ) => {
13
61
tracing:: warn!( "restarting finished thread" ) ;
14
62
}
15
63
Err ( error) => {
16
- if cfg ! ( debug_assertions) {
17
- tracing:: error!( "restarting failed thread: {:?}" , error)
18
- } else {
19
- tracing:: error!( "restarting failed thread: {}" , error)
20
- }
64
+ tracing:: warn!( "restarting failed thread: {}" , error) ;
21
65
}
22
66
}
23
67
} ) ;
24
68
}
25
69
26
- pub fn find_orphans ( wm : Arc < Mutex < WindowManager > > ) -> color_eyre:: Result < ( ) > {
27
- tracing:: info!( "watching " ) ;
70
+ pub fn handle_notifications ( wm : Arc < Mutex < WindowManager > > ) -> color_eyre:: Result < ( ) > {
71
+ tracing:: info!( "listening " ) ;
28
72
29
- let arc = wm . clone ( ) ;
73
+ let receiver = orphan_event_rx ( ) ;
30
74
31
- loop {
32
- std:: thread:: sleep ( Duration :: from_secs ( 1 ) ) ;
33
-
34
- let mut wm = arc. lock ( ) ;
75
+ ' receiver: for notification in receiver {
76
+ let orphan_hwnds = notification. 0 ;
77
+ let mut wm = wm. lock ( ) ;
35
78
let offset = wm. work_area_offset ;
36
79
37
80
let mut update_borders = false ;
38
81
82
+ for ( hwnd, ( m_idx, w_idx) ) in orphan_hwnds. iter ( ) {
83
+ if let Some ( monitor) = wm. monitors_mut ( ) . get_mut ( m_idx) {
84
+ let work_area = * monitor. work_area_size ( ) ;
85
+ let window_based_work_area_offset = (
86
+ monitor. window_based_work_area_offset_limit ( ) ,
87
+ monitor. window_based_work_area_offset ( ) ,
88
+ ) ;
89
+
90
+ let offset = if monitor. work_area_offset ( ) . is_some ( ) {
91
+ monitor. work_area_offset ( )
92
+ } else {
93
+ offset
94
+ } ;
95
+
96
+ if let Some ( workspace) = monitor. workspaces_mut ( ) . get_mut ( ) {
97
+ workspace. remove_window ( hwnd) ;
98
+ workspace. update ( & work_area, offset, window_based_work_area_offset) ?;
99
+ update_borders = true ;
100
+ tracing:: info!(
101
+ "reaped {} orphan window(s) and {} orphaned container(s) on monitor: {}, workspace: {}" ,
102
+ reaped_orphans. 0 ,
103
+ reaped_orphans. 1 ,
104
+ i,
105
+ j
106
+ ) ;
107
+ }
108
+ }
109
+ }
110
+
39
111
for ( i, monitor) in wm. monitors_mut ( ) . iter_mut ( ) . enumerate ( ) {
40
112
let work_area = * monitor. work_area_size ( ) ;
41
113
let window_based_work_area_offset = (
@@ -62,11 +134,75 @@ pub fn find_orphans(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result<()> {
62
134
j
63
135
) ;
64
136
}
137
+
138
+ tracing:: info!(
139
+ "reaped {} orphan window(s) and {} orphaned container(s) on monitor: {}, workspace: {}" ,
140
+ reaped_orphans. 0 ,
141
+ reaped_orphans. 1 ,
142
+ i,
143
+ j
144
+ ) ;
65
145
}
66
146
}
67
147
68
148
if update_borders {
69
149
border_manager:: send_notification ( None ) ;
70
150
}
71
151
}
152
+
153
+ Ok ( ( ) )
154
+ }
155
+
156
+ pub fn watch_for_orphans ( wm : Arc < Mutex < WindowManager > > ) {
157
+ // Cache current hwnds
158
+ {
159
+ let mut cache = HWNDS_CACHE . lock ( ) ;
160
+ * cache = wm. lock ( ) . known_hwnds . clone ( ) ;
161
+ }
162
+
163
+ std:: thread:: spawn ( move || loop {
164
+ match find_orphans ( ) {
165
+ Ok ( ( ) ) => {
166
+ tracing:: warn!( "restarting finished thread" ) ;
167
+ }
168
+ Err ( error) => {
169
+ if cfg ! ( debug_assertions) {
170
+ tracing:: error!( "restarting failed thread: {:?}" , error)
171
+ } else {
172
+ tracing:: error!( "restarting failed thread: {}" , error)
173
+ }
174
+ }
175
+ }
176
+ } ) ;
177
+ }
178
+
179
+ pub fn find_orphans ( ) -> color_eyre:: Result < ( ) > {
180
+ tracing:: info!( "watching" ) ;
181
+
182
+ loop {
183
+ std:: thread:: sleep ( Duration :: from_millis ( 20 ) ) ;
184
+
185
+ let cache = HWNDS_CACHE . lock ( ) ;
186
+ let mut orphan_hwnds = HashMap :: new ( ) ;
187
+
188
+ for ( hwnd, ( m_idx, w_idx) ) in cache. iter ( ) {
189
+ let window = Window :: from ( * hwnd) ;
190
+
191
+ if !window. is_window ( )
192
+ // This one is a hack because WINWORD.EXE is an absolute trainwreck of an app
193
+ // when multiple docs are open, it keeps open an invisible window, with WS_EX_LAYERED
194
+ // (A STYLE THAT THE REGULAR WINDOWS NEED IN ORDER TO BE MANAGED!) when one of the
195
+ // docs is closed
196
+ //
197
+ // I hate every single person who worked on Microsoft Office 365, especially Word
198
+ || !window. is_visible ( )
199
+ {
200
+ orphan_hwnds. insert ( window. hwnd , ( * m_idx, * w_idx) ) ;
201
+ }
202
+ }
203
+
204
+ if !orphan_hwnds. is_empty ( ) {
205
+ orphan_event_tx ( ) . send ( OrphanNotification ( orphan_hwnds) ) ?;
206
+ }
207
+ }
72
208
}
0 commit comments