···
#![windows_subsystem = "windows"]
use std::collections::HashSet;
5
-
collections::HashMap,
8
-
atomic::{AtomicBool, Ordering},
4
+
use std::{collections::HashMap, thread, time::Duration};
use device_query::{DeviceState, Keycode};
use quad_snd::{AudioContext, Sound};
···
26
-
// Shared state for sound toggle
27
-
let sounds_enabled = Arc::new(AtomicBool::new(true));
28
-
let sounds_enabled_clone = Arc::clone(&sounds_enabled);
30
-
// Start audio/key processing thread
31
-
thread::spawn(move || {
32
-
audio_key_thread(sounds_enabled_clone);
let on_icon = trayicon::Icon::from_buffer(
Box::new(std::fs::read("osuclack.ico").unwrap()).leak(),
···
.on_click(TrayEvents::ToggleSound)
.on_right_click(TrayEvents::ShowMenu)
.menu(MenuBuilder::new().item("quit", TrayEvents::Quit))
54
-
.sender(move |e| tray_tx.send(e.clone()).unwrap())
38
+
let tray_tx = tray_tx.clone();
39
+
move |e| tray_tx.send(e.clone()).unwrap()
45
+
let ctx = AudioContext::new();
46
+
let sounds = std::fs::read_dir("sounds")
47
+
.expect("cant read sounds")
49
+
let p = f.ok()?.path();
50
+
let n = p.file_stem()?.to_string_lossy().into_owned();
51
+
(n != "LICENSE" && n != "README").then(|| {
54
+
Sound::load(&ctx, &std::fs::read(p).expect("can't load sound")),
58
+
.collect::<HashMap<String, Sound>>();
59
+
let play_sound = |name: &str| {
60
+
let sound = sounds.get(name).unwrap();
61
+
sound.play(&ctx, Default::default());
63
+
let play_sound_for_key = |key: Keycode| match key {
64
+
Keycode::CapsLock => play_sound("caps"),
65
+
Keycode::Delete | Keycode::Backspace => play_sound("delete"),
66
+
Keycode::Up | Keycode::Down | Keycode::Left | Keycode::Right => play_sound("movement"),
68
+
let no = fastrand::u8(1..=4);
69
+
play_sound(&format!("press-{no}"));
73
+
let state = DeviceState::new();
74
+
let mut previously_held_keys = HashSet::<Keycode>::new();
75
+
let mut key_press_times = HashMap::<Keycode, std::time::Instant>::new();
76
+
let mut last_sound_time = std::time::Instant::now();
77
+
let mut sound_enabled = true;
79
+
let initial_delay = Duration::from_millis(500); // Wait 500ms before starting to repeat
80
+
let repeat_interval = Duration::from_millis(50); // Then repeat every 50ms
60
-
let Ok(event) = tray_rx.recv() else {
83
+
let currently_held_keys: HashSet<Keycode> = state.query_keymap().into_iter().collect();
85
+
// Check for toggle hotkey (Ctrl + Alt + L/R Shift + C)
86
+
let hotkey_combo = [
87
+
[Keycode::LControl, Keycode::RControl], // Either left or right control
88
+
[Keycode::LAlt, Keycode::RAlt], // Either left or right alt
89
+
[Keycode::LShift, Keycode::RShift], // Either left or right shift
90
+
[Keycode::C, Keycode::C], // C key (duplicated for array consistency)
93
+
let check_hotkey = |current: &HashSet<Keycode>, previous: &HashSet<Keycode>| {
94
+
hotkey_combo.iter().all(|key_group| {
97
+
.any(|key| current.contains(key) || previous.contains(key))
64
-
TrayEvents::ToggleSound => {
65
-
let new = !sounds_enabled.load(Ordering::Relaxed);
66
-
sounds_enabled.store(new, Ordering::Relaxed);
68
-
.set_icon(new.then_some(&on_icon).unwrap_or(&off_icon))
101
+
let hotkey_active = check_hotkey(¤tly_held_keys, &previously_held_keys);
102
+
let hotkey_was_active = check_hotkey(&previously_held_keys, &HashSet::new());
104
+
if hotkey_active && !hotkey_was_active {
105
+
tray_tx.send(TrayEvents::ToggleSound).unwrap();
108
+
// handle tray events
109
+
if let Ok(event) = tray_rx.try_recv() {
111
+
TrayEvents::ToggleSound => {
112
+
sound_enabled = !sound_enabled;
114
+
.set_icon(sound_enabled.then_some(&on_icon).unwrap_or(&off_icon))
117
+
TrayEvents::Quit => {
118
+
std::process::exit(0);
120
+
TrayEvents::ShowMenu => {
121
+
tray_icon.show_menu().unwrap();
71
-
TrayEvents::Quit => {
72
-
std::process::exit(0);
126
+
// Only process sound logic if sounds are enabled
128
+
// Track when keys were first pressed
129
+
for key in ¤tly_held_keys {
130
+
if !previously_held_keys.contains(key) {
131
+
// Key just pressed, record the time and play initial sound
132
+
key_press_times.insert(*key, std::time::Instant::now());
133
+
play_sound_for_key(*key);
74
-
TrayEvents::ShowMenu => {
75
-
tray_icon.show_menu().unwrap();
137
+
// Remove timing info for released keys
138
+
key_press_times.retain(|key, _| currently_held_keys.contains(key));
140
+
// Play repeating sounds every 50ms, but only after initial delay
141
+
if last_sound_time.elapsed() >= repeat_interval {
142
+
let now = std::time::Instant::now();
143
+
for key in ¤tly_held_keys {
144
+
if is_modifier_key(key) {
147
+
if let Some(press_time) = key_press_times.get(key) {
148
+
// Only repeat if key has been held longer than initial delay
149
+
if now.duration_since(*press_time) >= initial_delay {
150
+
play_sound_for_key(*key);
154
+
last_sound_time = now;
157
+
// Clear key press times when sounds are disabled to avoid stale data
158
+
key_press_times.clear();
161
+
previously_held_keys = currently_held_keys;
163
+
// Buffer inputs at lower interval (5ms)
164
+
thread::sleep(Duration::from_millis(5));
···
98
-
fn audio_key_thread(sounds_enabled: Arc<AtomicBool>) {
99
-
let ctx = AudioContext::new();
100
-
let sounds = std::fs::read_dir("sounds")
101
-
.expect("cant read sounds")
103
-
let p = f.ok()?.path();
104
-
let n = p.file_stem()?.to_string_lossy().into_owned();
105
-
(n != "LICENSE" && n != "README").then(|| {
108
-
Sound::load(&ctx, &std::fs::read(p).expect("can't load sound")),
112
-
.collect::<HashMap<String, Sound>>();
113
-
let play_sound = |name: &str| {
114
-
let sound = sounds.get(name).unwrap();
115
-
sound.play(&ctx, Default::default());
117
-
let play_sound_for_key = |key: Keycode| match key {
118
-
Keycode::CapsLock => play_sound("caps"),
119
-
Keycode::Delete | Keycode::Backspace => play_sound("delete"),
120
-
Keycode::Up | Keycode::Down | Keycode::Left | Keycode::Right => play_sound("movement"),
122
-
let no = fastrand::u8(1..=4);
123
-
play_sound(&format!("press-{no}"));
127
-
let state = DeviceState::new();
128
-
let mut previously_held_keys = HashSet::<Keycode>::new();
129
-
let mut key_press_times = HashMap::<Keycode, std::time::Instant>::new();
130
-
let mut last_sound_time = std::time::Instant::now();
132
-
let initial_delay = Duration::from_millis(500); // Wait 500ms before starting to repeat
133
-
let repeat_interval = Duration::from_millis(50); // Then repeat every 50ms
136
-
let currently_held_keys: HashSet<Keycode> = state.query_keymap().into_iter().collect();
138
-
// Check for toggle hotkey (Ctrl + Alt + L/R Shift + C)
139
-
let hotkey_combo = [
140
-
[Keycode::LControl, Keycode::RControl], // Either left or right control
141
-
[Keycode::LAlt, Keycode::RAlt], // Either left or right alt
142
-
[Keycode::LShift, Keycode::RShift], // Either left or right shift
143
-
[Keycode::C, Keycode::C], // C key (duplicated for array consistency)
146
-
let check_hotkey = |current: &HashSet<Keycode>, previous: &HashSet<Keycode>| {
147
-
hotkey_combo.iter().all(|key_group| {
150
-
.any(|key| current.contains(key) || previous.contains(key))
154
-
let hotkey_active = check_hotkey(¤tly_held_keys, &previously_held_keys);
155
-
let hotkey_was_active = check_hotkey(&previously_held_keys, &HashSet::new());
157
-
let mut sounds_currently_enabled = sounds_enabled.load(Ordering::Relaxed);
158
-
if hotkey_active && !hotkey_was_active {
159
-
sounds_currently_enabled = !sounds_currently_enabled;
160
-
sounds_enabled.store(sounds_currently_enabled, Ordering::Relaxed);
163
-
// Only process sound logic if sounds are enabled
164
-
if sounds_currently_enabled {
165
-
// Track when keys were first pressed
166
-
for key in ¤tly_held_keys {
167
-
if !previously_held_keys.contains(key) {
168
-
// Key just pressed, record the time and play initial sound
169
-
key_press_times.insert(*key, std::time::Instant::now());
170
-
play_sound_for_key(*key);
174
-
// Remove timing info for released keys
175
-
key_press_times.retain(|key, _| currently_held_keys.contains(key));
177
-
// Play repeating sounds every 50ms, but only after initial delay
178
-
if last_sound_time.elapsed() >= repeat_interval {
179
-
let now = std::time::Instant::now();
180
-
for key in ¤tly_held_keys {
181
-
if is_modifier_key(key) {
184
-
if let Some(press_time) = key_press_times.get(key) {
185
-
// Only repeat if key has been held longer than initial delay
186
-
if now.duration_since(*press_time) >= initial_delay {
187
-
play_sound_for_key(*key);
191
-
last_sound_time = now;
194
-
// Clear key press times when sounds are disabled to avoid stale data
195
-
key_press_times.clear();
198
-
previously_held_keys = currently_held_keys;
200
-
// Buffer inputs at lower interval (5ms)
201
-
thread::sleep(Duration::from_millis(5));