Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix hotkey selector panic #110

Merged
merged 5 commits into from
Jul 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/component/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
use super::setting::Chat;
use crate::widget::ComboBoxItem;

#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize, Hash, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum Function {
Rewrite,
Expand Down
130 changes: 130 additions & 0 deletions src/component/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,23 @@ impl FromStr for Keys {
}
}

impl ToString for Keys {
fn to_string(&self) -> String {
let mut s: Vec<String> = Vec::new();

for k in &self.0 {
match k {
Key::Control => s.push("CTRL".to_string()),
Key::Shift => s.push("SHIFT".to_string()),
Key::Alt => s.push("ALT".to_string()),
Key::Meta => s.push("META".to_string()),
_ => s.push(key_to_str(k).unwrap_or_else(|_| "not set").to_uppercase()),
}
}
s.join("+")
}
}

// We can't use [`enigo::Key::Unicode`], it will cause panic.
// Don't know why, maybe that can only be used in main thread.
fn key_of(key: char) -> Result<Key> {
Expand Down Expand Up @@ -180,3 +197,116 @@ fn key_of(key: char) -> Result<Key> {

Ok(k)
}

pub fn key_to_str(key: &Key) -> Result<&'static str> {
match key {
#[cfg(all(unix, not(target_os = "macos")))]
Key::Unicode(c) => Ok(c),

#[cfg(target_os = "windows")]
Key::Other(v) => Ok(match v {
0x30 => "0",
0x31 => "1",
0x32 => "2",
0x33 => "3",
0x34 => "4",
0x35 => "5",
0x36 => "6",
0x37 => "7",
0x38 => "8",
0x39 => "9",
0x41 => "A",
0x42 => "B",
0x43 => "C",
0x44 => "D",
0x45 => "E",
0x46 => "F",
0x47 => "G",
0x48 => "H",
0x49 => "I",
0x4A => "J",
0x4B => "K",
0x4C => "L",
0x4D => "M",
0x4E => "N",
0x4F => "O",
0x50 => "P",
0x51 => "Q",
0x52 => "R",
0x53 => "S",
0x54 => "T",
0x55 => "U",
0x56 => "V",
0x57 => "W",
0x58 => "X",
0x59 => "Y",
0x5A => "Z",
0xBB => "=",
0xBD => "-",
0xBA => ";",
0xBC => ",",
0xBE => ".",
0xBF => "/",
0xC0 => "`",
0xDB => "[",
0xDD => "]",
0xDC => "\\",
0xDE => "'",
_ => return Err(Error::UnsupportedKey(format!("{:x}", v))),
}),

#[cfg(target_os = "macos")]
Key::Other(v) => Ok(match v {
0 => "A",
1 => "S",
2 => "D",
3 => "F",
4 => "H",
5 => "G",
6 => "Z",
7 => "X",
8 => "C",
9 => "V",
11 => "B",
12 => "Q",
13 => "W",
14 => "E",
15 => "R",
16 => "Y",
17 => "T",
18 => "1",
19 => "2",
20 => "3",
21 => "4",
22 => "6",
23 => "5",
24 => "=",
25 => "9",
26 => "7",
27 => "-",
28 => "8",
29 => "0",
30 => "]",
31 => "O",
32 => "U",
33 => "[",
34 => "I",
35 => "P",
37 => "L",
38 => "J",
39 => "'",
40 => "K",
41 => ";",
42 => "\\",
43 => ",",
44 => "/",
45 => "N",
46 => "M",
47 => ".",
50 => "`",
_ => return Err(Error::UnsupportedKey(format!("{:x}", v))),
}),

_ => Err(Error::UnsupportedKey(format!("{:?}", key))),
}
}
139 changes: 99 additions & 40 deletions src/service/hotkey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::{

pub struct Hotkey {
// The manager need to be kept alive during the whole program life.
_manager: GlobalHotKeyManager,
ghk_manager: GlobalHotKeyManager,
manager: Arc<RwLock<Manager>>,
abort: ArtBool,
}
Expand All @@ -37,7 +37,7 @@ impl Hotkey {
) -> Result<Self> {
let ctx = ctx.to_owned();
let _manager = GlobalHotKeyManager::new().map_err(GlobalHotKeyError::Main)?;
let manager = Arc::new(RwLock::new(Manager::new(&_manager, hotkeys)?));
let manager = Arc::new(RwLock::new(Manager::new(&_manager, hotkeys)));
let manager_ = manager.clone();
let notification_sound = state.general.notification_sound.clone();
let activated_function = state.chat.activated_function.clone();
Expand Down Expand Up @@ -107,17 +107,20 @@ impl Hotkey {
}
});

Ok(Self { _manager, manager, abort })
Ok(Self { ghk_manager: _manager, manager, abort })
}

pub fn renew(&self, hotkeys: &Hotkeys) {
pub fn renew(&self, hotkeys: &mut Hotkeys) {
tracing::info!("renewing hotkey manager");

let mut manager = self.manager.write();

self._manager.unregister_all(&manager.hotkeys).expect("unregister must succeed");
manager.unregister_hotkeys(&self.ghk_manager);

*manager = Manager::new(&self._manager, hotkeys).expect("renew must succeed");
*manager = Manager::new(&self.ghk_manager, hotkeys);

// Write hotkey texts back into the settings.
manager.read_hotkeys_into_settings(hotkeys);
}

pub fn abort(&self) {
Expand All @@ -130,47 +133,103 @@ impl Debug for Hotkey {
}
}

#[derive(Debug)]
struct HotKeyPair {
hotkey: HotKey,
keys: Keys,
}
struct Manager {
hotkeys: [HotKey; 4],
hotkeys_keys: [Keys; 4],
/// Hotkeys. The order follows the order in settings
hotkeys_list: [Option<HotKeyPair>; 4],
}
impl Manager {
fn new(_manager: &GlobalHotKeyManager, hotkeys: &Hotkeys) -> Result<Self> {
let hotkeys_raw = [
&hotkeys.rewrite,
&hotkeys.rewrite_directly,
&hotkeys.translate,
&hotkeys.translate_directly,
/// Creates new manager. Registers hotkeys with global manager.
/// If any hotkey did not gersister, stores None instead of it.
fn new(ghk_manager: &GlobalHotKeyManager, settings_hotkeys: &Hotkeys) -> Self {
let hotkey_str_list = [
&settings_hotkeys.rewrite,
&settings_hotkeys.rewrite_directly,
&settings_hotkeys.translate,
&settings_hotkeys.translate_directly,
];
let hotkeys: [HotKey; 4] = hotkeys_raw
.iter()
.map(|h| h.parse())
.collect::<Result<Vec<_>, _>>()
.map_err(GlobalHotKeyError::Parse)?
.try_into()
.expect("array must fit");

_manager.register_all(&hotkeys).map_err(GlobalHotKeyError::Main)?;

let hotkeys_keys = hotkeys_raw
.iter()
.map(|h| h.parse())
.collect::<Result<Vec<_>, _>>()?
.try_into()
.expect("array must fit");

Ok(Self { hotkeys, hotkeys_keys })

let mut hotkeys_list: Vec<Option<HotKeyPair>> = Vec::with_capacity(4);

for hotkey_str in hotkey_str_list.into_iter() {
//Parse error possible when str value is "Not set"
let hotkey: HotKey = match hotkey_str.parse() {
Ok(v) => v,
Err(_) => {
hotkeys_list.push(None);
continue;
},
};
// Same goes for Keys
let keys: Keys = match hotkey_str.parse() {
Ok(v) => v,
Err(_) => {
hotkeys_list.push(None);
continue;
},
};
// If manager.register fails, ignore the key, and it becomes "Not set".
let e = ghk_manager.register(hotkey);
if e.is_err() {
hotkeys_list.push(None);
continue;
}

// If key has been registered, add it to new_hotkeys
hotkeys_list.push(Some(HotKeyPair { hotkey, keys }));
}

Self { hotkeys_list: hotkeys_list.try_into().expect("hotkeys_list must have 4 elements") }
}

fn match_func(&self, id: u32) -> (Function, Keys) {
match id {
i if i == self.hotkeys[0].id => (Function::Rewrite, self.hotkeys_keys[0].clone()),
i if i == self.hotkeys[1].id =>
(Function::RewriteDirectly, self.hotkeys_keys[1].clone()),
i if i == self.hotkeys[2].id => (Function::Translate, self.hotkeys_keys[2].clone()),
i if i == self.hotkeys[3].id =>
(Function::TranslateDirectly, self.hotkeys_keys[3].clone()),
_ => unreachable!(),
// Must follow the same order of functions as in settings
const FUNCTION_LIST: [Function; 4] = [
Function::Rewrite,
Function::RewriteDirectly,
Function::Translate,
Function::TranslateDirectly,
];

for (i, pair) in self.hotkeys_list.iter().enumerate() {
if let Some(pair) = pair {
if pair.hotkey.id == id {
return (FUNCTION_LIST[i], pair.keys.clone());
}
}
}
unreachable!();
}

// Copies text representation of actually registered hotkeys back to settings.
// Replaces text for unset hotkeys with "Not set"
fn read_hotkeys_into_settings(&self, settings_hotkeys: &mut Hotkeys) {
let hotkey_str_list = [
&mut settings_hotkeys.rewrite,
&mut settings_hotkeys.rewrite_directly,
&mut settings_hotkeys.translate,
&mut settings_hotkeys.translate_directly,
];

for (i, k) in hotkey_str_list.into_iter().enumerate() {
*k = match &self.hotkeys_list[i] {
Some(p) => p.keys.to_string(),
None => "Not set".to_string(),
};
}
}

/// Unregisters all hotkeys with given global hotkey manager.
fn unregister_hotkeys(&mut self, ghk_manager: &GlobalHotKeyManager) {
for list_entry in self.hotkeys_list.iter_mut() {
if let Some(pair) = list_entry {
ghk_manager.unregister(pair.hotkey).expect("unregister must succeed");
}
*list_entry = None;
}
}
}
2 changes: 1 addition & 1 deletion src/ui/panel/setting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ impl Setting {

changed
}) {
ctx.services.hotkey.renew(&ctx.components.setting.hotkeys);
ctx.services.hotkey.renew(&mut ctx.components.setting.hotkeys);
}
});
});
Expand Down