Skip to content

Commit ae91dda

Browse files
committed
perf(masir): introduce class and hwnd pair caches
This commit introduces caches for class names and pairs of hwnds which refer to the same application. Once again, these caches will be used to reduce the number of syscalls and file reads, and in the case of the hwnd pair cache, will allow masir to exit an iteration early if it has already discovered two hwnds with different classes that refer to the same application.
1 parent 3ec2ab1 commit ae91dda

File tree

3 files changed

+115
-54
lines changed

3 files changed

+115
-54
lines changed

.github/workflows/windows.yaml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,9 @@ jobs:
144144
checksums.txt
145145
*.zip
146146
*.msi
147-
- if: startsWith(github.ref, 'refs/tags/v')
148-
uses: vedantmgoyal2009/winget-releaser@v2
149-
with:
150-
identifier: LGUG2Z.masir
151-
token: ${{ secrets.WINGET_TOKEN }}
147+
# Can be uncommented after the first manual release
148+
# - if: startsWith(github.ref, 'refs/tags/v')
149+
# uses: vedantmgoyal2009/winget-releaser@v2
150+
# with:
151+
# identifier: LGUG2Z.masir
152+
# token: ${{ secrets.WINGET_TOKEN }}

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
name = "masir"
33
version = "0.1.0"
44
edition = "2021"
5+
license-file = "LICENSE.md"
56

67
[dependencies]
78
winput = "0.2"

src/main.rs

Lines changed: 108 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const CLASS_ALLOWLIST: [&str; 5] = [
2727
"Chrome_RenderWidgetHostHWND", // gross electron apps
2828
"Microsoft.UI.Content.DesktopChildSiteBridge", // windows explorer main panel
2929
"SysTreeView32", // windows explorer side panel
30-
"TITLE_BAR_SCAFFOLDING_WINDOW_CLASS", // windows explorer side panel
30+
"TITLE_BAR_SCAFFOLDING_WINDOW_CLASS", // windows explorer title bar
3131
"DirectUIHWND", // windows explorer after interaction
3232
];
3333

@@ -119,70 +119,128 @@ fn main() -> Result<()> {
119119
pub fn listen_for_movements(hwnds: Option<PathBuf>) {
120120
std::thread::spawn(move || {
121121
let receiver = message_loop::start().expect("could not start winput message loop");
122+
122123
let mut eligibility_cache = HashMap::new();
123-
let mut map_instantiation_time = Instant::now();
124-
let map_max_age = Duration::from_secs(60) * 10; // 10 minutes
124+
let mut class_cache: HashMap<isize, String> = HashMap::new();
125+
let mut hwnd_pair_cache: HashMap<isize, isize> = HashMap::new();
126+
127+
let mut cache_instantiation_time = Instant::now();
128+
let max_cache_age = Duration::from_secs(60) * 10; // 10 minutes
125129

126130
loop {
127-
// clear our cache every 10 minutes
128-
if map_instantiation_time.elapsed() > map_max_age {
129-
tracing::info!("clearing hwnd cache, max map age is >10 minutes");
131+
// clear our caches every 10 minutes
132+
if cache_instantiation_time.elapsed() > max_cache_age {
133+
tracing::info!("clearing caches, cache age is >10 minutes");
134+
130135
eligibility_cache = HashMap::new();
131-
map_instantiation_time = Instant::now();
136+
class_cache = HashMap::new();
137+
hwnd_pair_cache = HashMap::new();
138+
139+
cache_instantiation_time = Instant::now();
132140
}
133141

134142
if let Event::MouseMoveRelative { .. } = receiver.next_event() {
135143
if let (Ok(cursor_pos_hwnd), Ok(foreground_hwnd)) =
136144
(window_at_cursor_pos(), foreground_window())
137145
{
138146
if cursor_pos_hwnd != foreground_hwnd {
147+
if let Some(paired_hwnd) = hwnd_pair_cache.get(&cursor_pos_hwnd) {
148+
if foreground_hwnd == *paired_hwnd {
149+
tracing::trace!("hwnds {cursor_pos_hwnd} and {foreground_hwnd} are known to refer to the same application, skipping");
150+
continue;
151+
}
152+
}
153+
139154
let mut should_raise = false;
140-
let mut should_cache = false;
155+
let mut should_cache_eligibility = false;
156+
157+
// check our class cache to avoid syscalls
158+
let mut cursor_pos_class = class_cache.get(&cursor_pos_hwnd).cloned();
159+
let mut foreground_class = class_cache.get(&foreground_hwnd).cloned();
160+
161+
// make syscalls if necessary and populate the class cache
162+
match &cursor_pos_class {
163+
None => {
164+
if let Ok(class) = real_window_class_w(cursor_pos_hwnd) {
165+
class_cache.insert(cursor_pos_hwnd, class.clone());
166+
cursor_pos_class = Some(class);
167+
}
168+
}
169+
Some(class) => {
170+
tracing::debug!(
171+
"hwnd {cursor_pos_hwnd} class was found in the cache: {class}"
172+
);
173+
}
174+
}
141175

142-
// check our cache first
143-
if let Some(eligible) = eligibility_cache.get(&cursor_pos_hwnd) {
144-
if *eligible {
145-
should_raise = true;
176+
// make syscalls if necessary and populate the class cache
177+
match &foreground_class {
178+
None => {
179+
if let Ok(class) = real_window_class_w(foreground_hwnd) {
180+
class_cache.insert(foreground_hwnd, class.clone());
181+
foreground_class = Some(class);
182+
}
183+
}
184+
Some(class) => {
146185
tracing::debug!(
147-
"hwnd {cursor_pos_hwnd} was found as eligible in the cache"
186+
"hwnd {foreground_hwnd} class was found in the cache: {class}"
148187
);
149188
}
150-
} else {
151-
should_cache = true;
152189
}
153190

154-
if !should_raise {
155-
// step one: test against known classes
156-
if let (Ok(cursor_pos_class), Ok(foreground_class)) = (
157-
real_window_class_w(cursor_pos_hwnd),
158-
real_window_class_w(foreground_hwnd),
159-
) {
160-
// windows explorer related focus loop weirdness fixes in this block
191+
if let (Some(cursor_pos_class), Some(foreground_class)) =
192+
(cursor_pos_class, foreground_class)
193+
{
194+
// windows explorer fixes - populate the hwnd pair cache if necessary
195+
{
196+
if cursor_pos_class == "DirectUIHWND"
197+
&& foreground_class == "CabinetWClass"
161198
{
162-
if cursor_pos_class == "DirectUIHWND"
163-
&& foreground_class == "CabinetWClass"
164-
{
165-
continue;
166-
}
199+
hwnd_pair_cache.insert(cursor_pos_hwnd, foreground_hwnd);
200+
continue;
201+
}
167202

168-
if cursor_pos_class
169-
== "Microsoft.UI.Content.DesktopChildSiteBridge"
170-
&& foreground_class == "CabinetWClass"
171-
{
172-
continue;
173-
}
203+
if cursor_pos_class == "Microsoft.UI.Content.DesktopChildSiteBridge"
204+
&& foreground_class == "CabinetWClass"
205+
{
206+
hwnd_pair_cache.insert(cursor_pos_hwnd, foreground_hwnd);
207+
continue;
208+
}
209+
}
210+
211+
// steam fixes - populate the hwnd pair cache if necessary
212+
{
213+
if cursor_pos_class == "Chrome_RenderWidgetHostHWND"
214+
&& foreground_class == "SDL_app"
215+
{
216+
hwnd_pair_cache.insert(cursor_pos_hwnd, foreground_hwnd);
217+
continue;
174218
}
219+
}
175220

176-
// fail fast, exit this iteration of the loop and avoid any processing
177-
// if we hit a blocklist entry
178-
if CLASS_BLOCKLIST.contains(&&*cursor_pos_class) {
221+
// check our eligibility cache
222+
if let Some(eligible) = eligibility_cache.get(&cursor_pos_hwnd) {
223+
if *eligible {
224+
should_raise = true;
225+
tracing::debug!(
226+
"hwnd {cursor_pos_hwnd} was found as eligible in the cache"
227+
);
228+
}
229+
} else {
230+
should_cache_eligibility = true;
231+
}
232+
233+
// if the eligibility for this hwnd isn't cached, then do some tests
234+
if !should_raise {
235+
// step one: test against known classes
236+
if CLASS_BLOCKLIST.contains(&cursor_pos_class.as_str()) {
179237
tracing::debug!(
180238
"window class {cursor_pos_class} is blocklisted"
181239
);
182240
continue;
183241
}
184242

185-
if CLASS_ALLOWLIST.contains(&&*cursor_pos_class) {
243+
if CLASS_ALLOWLIST.contains(&cursor_pos_class.as_str()) {
186244
tracing::debug!(
187245
"window class {cursor_pos_class} is allowlisted"
188246
);
@@ -192,26 +250,27 @@ pub fn listen_for_movements(hwnds: Option<PathBuf>) {
192250
if !should_raise {
193251
tracing::trace!("window class is {cursor_pos_class}");
194252
}
195-
}
196253

197-
// step two: if available, test against known hwnds
198-
if !should_raise {
199-
if let Some(hwnds) = &hwnds {
200-
if let Ok(raw_hwnds) = std::fs::read_to_string(hwnds) {
201-
if raw_hwnds.contains(&cursor_pos_hwnd.to_string()) {
202-
tracing::debug!(
203-
"hwnd {cursor_pos_hwnd} was found in {}",
204-
hwnds.display()
205-
);
206-
should_raise = true;
254+
// step two: if available, test against known hwnds
255+
if !should_raise {
256+
if let Some(hwnds) = &hwnds {
257+
if let Ok(raw_hwnds) = std::fs::read_to_string(hwnds) {
258+
if raw_hwnds.contains(&cursor_pos_hwnd.to_string()) {
259+
tracing::debug!(
260+
"hwnd {cursor_pos_hwnd} was found in {}",
261+
hwnds.display()
262+
);
263+
should_raise = true;
264+
}
207265
}
208266
}
209267
}
210268
}
211269
}
212270

213271
if should_raise {
214-
if should_cache {
272+
if should_cache_eligibility {
273+
// ensure we cache eligibility to avoid syscalls and tests next time
215274
eligibility_cache.insert(cursor_pos_hwnd, true);
216275
}
217276

0 commit comments

Comments
 (0)