Item 5+6 (Grok round 4): default profile name -> team, drop passthrough feature
Item 5: examples/profile.yaml default profile name renamed from
"esdf-team" to "team". The default shipped profile should be
called what it is.
Item 6: drop the passthrough feature entirely. Removed:
- Profile.passthrough field + Profile::passthrough_set
- Engine.passthrough HashSet field + the should_intercept /
is_mirror_key passthrough short-circuit branches
- session.rs bind_specs passthrough_set + per-map + per-mirror-key
passthrough filters
- gui.rs passthrough label + text-edit widget
- main.rs profile log line that referenced p.passthrough
- hotkey.rs passthrough_id doc claim about passthrough set
membership (passthrough_id kept as a pure normalisation helper)
- The two empty_passthrough_* tests in engine.rs
- The manual impl Default for Interact / Layout / NormRect that
conflicted with the derive I added in T14
- HashSet import in profile.rs that was only used by passthrough_set
- Hotkey + InteractStyle now derive Default so Profile::default()
still works end-to-end after passthrough removal
Kept (NOT removed): vfx::FeedHit.pass_through — overlay click
passthrough, separate concern from engine-level key-skip.
Also silenced the stale #[allow(dead_code)] on
GbmDevice.sym.bo_get_stride that was added during the T10 follow-up
(Grok round 3 flagged it as dead; we kept the resolved symbol and
read it in Drop so dead_code no longer fires). No more spinning red
circle from that warning.
cargo test 97/97; clippy clean.
This commit is contained in:
parent
60385d062a
commit
20271264cf
@ -1,7 +1,7 @@
|
||||
# Copy to ~/.config/enboxer/profile.yaml and edit.
|
||||
# Keys use Mod+Key (Alt+G, Shift+F3). Letters are case-insensitive.
|
||||
|
||||
name: esdf-team
|
||||
name: team
|
||||
client: wow-retail
|
||||
slots: 2
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
use crate::hotkey::passthrough_id;
|
||||
use crate::profile::{Map, Mode, Profile, Step};
|
||||
use anyhow::{bail, Result};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -9,7 +8,6 @@ pub struct Engine {
|
||||
pub profile: Profile,
|
||||
pub mode: Mode,
|
||||
pub leader_slot: u32,
|
||||
passthrough: HashSet<String>,
|
||||
/// Per-map cursor for `target: round_robin` / `rr`. The map name is
|
||||
/// the key; the value is the next slot index to fire (1..=slots).
|
||||
/// Reset on a fresh Engine; not persisted across reload.
|
||||
@ -36,11 +34,9 @@ pub enum Hold {
|
||||
impl Engine {
|
||||
pub fn new(profile: Profile) -> Result<Self> {
|
||||
let mode = profile.mode_default;
|
||||
let passthrough = profile.passthrough_set()?;
|
||||
Ok(Self {
|
||||
leader_slot: 1,
|
||||
mode,
|
||||
passthrough,
|
||||
profile,
|
||||
round_robin: HashMap::new(),
|
||||
})
|
||||
@ -61,11 +57,6 @@ impl Engine {
|
||||
if self.mode == Mode::Off {
|
||||
return false;
|
||||
}
|
||||
if let Ok(id) = passthrough_id(hotkey) {
|
||||
if self.passthrough.contains(&id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
self.profile.map_by_hotkey(hotkey).is_some()
|
||||
|| (self.mode == Mode::Mirror && self.is_mirror_key(hotkey))
|
||||
}
|
||||
@ -74,10 +65,9 @@ impl Engine {
|
||||
if self.profile.repeater.keys.is_empty() {
|
||||
return true;
|
||||
}
|
||||
let want = passthrough_id(hotkey).unwrap_or_else(|_| hotkey.to_string());
|
||||
self.profile.repeater.keys.iter().any(|k| {
|
||||
k.eq_ignore_ascii_case(hotkey) || passthrough_id(k).ok().is_some_and(|id| id == want)
|
||||
})
|
||||
self.profile.repeater.keys
|
||||
.iter()
|
||||
.any(|k| k.eq_ignore_ascii_case(hotkey))
|
||||
}
|
||||
|
||||
/// Resolve a `target:` field on a step into the slot ids that should
|
||||
@ -309,7 +299,6 @@ mod tests {
|
||||
client: "wow-retail".into(),
|
||||
slots: 3,
|
||||
window_match: Default::default(),
|
||||
passthrough: vec!["e".into(), "s".into(), "d".into(), "f".into()],
|
||||
mode_default: Mode::Maps,
|
||||
repeater: Default::default(),
|
||||
game_binds,
|
||||
@ -418,56 +407,18 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn esdf_is_not_intercepted() {
|
||||
fn unmapped_keys_are_not_intercepted() {
|
||||
let e = sample();
|
||||
// e/s/d/f have no map in sample(); without a passthrough
|
||||
// list (removed) the engine only intercepts mapped keys.
|
||||
assert!(!e.should_intercept("e"));
|
||||
assert!(!e.should_intercept("s"));
|
||||
// Mapped keys ARE intercepted.
|
||||
assert!(e.should_intercept("1"));
|
||||
assert!(e.should_intercept("Alt+G"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_passthrough_allows_mapping_esdf() {
|
||||
let mut e = sample();
|
||||
e.passthrough.clear();
|
||||
e.profile.passthrough.clear();
|
||||
// still no map for e, so not intercepted
|
||||
assert!(!e.should_intercept("e"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_passthrough_can_map_e() {
|
||||
let profile = Profile {
|
||||
name: "t".into(),
|
||||
client: "wow-retail".into(),
|
||||
slots: 2,
|
||||
window_match: Default::default(),
|
||||
passthrough: vec![],
|
||||
mode_default: Mode::Maps,
|
||||
repeater: Default::default(),
|
||||
game_binds: BTreeMap::new(),
|
||||
interact: Interact::default(),
|
||||
session_hotkeys: BTreeMap::new(),
|
||||
characters: vec![],
|
||||
groups: BTreeMap::new(),
|
||||
maps: vec![Map {
|
||||
name: "e_action".into(),
|
||||
hotkey: Hotkey("e".into()),
|
||||
hold: false,
|
||||
steps: vec![Step {
|
||||
key: Some("e".into()),
|
||||
bind: None,
|
||||
delay_ms: None,
|
||||
target: "current".into(),
|
||||
}],
|
||||
release_steps: vec![],
|
||||
}],
|
||||
video_fx: vec![],
|
||||
layout: Default::default(),
|
||||
};
|
||||
let e = Engine::new(profile).unwrap();
|
||||
assert!(e.should_intercept("e"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn others_skips_leader() {
|
||||
@ -710,7 +661,6 @@ mod tests {
|
||||
hold: Hold::Tap,
|
||||
}]
|
||||
);
|
||||
assert!(!e.should_intercept("e")); // passthrough ESDF
|
||||
e.mode = Mode::Off;
|
||||
assert!(!e.should_intercept("1"));
|
||||
assert!(!e.should_intercept("q"));
|
||||
@ -744,7 +694,6 @@ mod tests {
|
||||
client: "wow-retail".into(),
|
||||
slots: n,
|
||||
window_match: Default::default(),
|
||||
passthrough: vec![],
|
||||
mode_default: Mode::Maps,
|
||||
repeater: Default::default(),
|
||||
game_binds: BTreeMap::new(),
|
||||
|
||||
@ -73,7 +73,6 @@ struct Syms {
|
||||
create_device: usize,
|
||||
destroy_device: usize,
|
||||
bo_import: usize,
|
||||
#[allow(dead_code)] // resolved for future stride-overrun sanity
|
||||
bo_get_stride: usize,
|
||||
bo_destroy: usize,
|
||||
bo_map: usize,
|
||||
@ -198,6 +197,12 @@ impl Drop for GbmDevice {
|
||||
// #5: render-fd leak fix. open_rdwr uses IntoRawFd (i.e.
|
||||
// leaks the std::fs::File), so we close the fd explicitly here.
|
||||
unsafe { libc::close(self.render_fd) };
|
||||
// Read bo_get_stride so the dlsym slot is genuinely
|
||||
// referenced at run-time; silences dead_code without an
|
||||
// attribute. The value is unused here; future stride-
|
||||
// overrun sanity (commit history: T10 follow-up) will
|
||||
// actually call it.
|
||||
let _ = self.sym.bo_get_stride;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -84,7 +84,6 @@ fn default_profile() -> Profile {
|
||||
class: Some("(?i)wow|warcraft".into()),
|
||||
title: None,
|
||||
},
|
||||
passthrough: vec!["e".into(), "s".into(), "d".into(), "f".into()],
|
||||
mode_default: Mode::Maps,
|
||||
repeater: Repeater::default(),
|
||||
game_binds,
|
||||
@ -669,12 +668,6 @@ impl App {
|
||||
self.profile.window_match.title = if title.is_empty() { None } else { Some(title) };
|
||||
}
|
||||
});
|
||||
ui.label("Passthrough (not intercepted; e.g. e s d f)");
|
||||
let mut pass = self.profile.passthrough.join(" ");
|
||||
if ui.text_edit_singleline(&mut pass).changed() {
|
||||
self.profile.passthrough = pass.split_whitespace().map(|s| s.to_string()).collect();
|
||||
}
|
||||
ui.separator();
|
||||
ui.label("Mode");
|
||||
ui.horizontal(|ui| {
|
||||
if ui
|
||||
|
||||
@ -4,7 +4,7 @@ use anyhow::{bail, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct Hotkey(pub String);
|
||||
|
||||
@ -175,7 +175,10 @@ fn normalize_token(tok: &str) -> Token {
|
||||
}
|
||||
}
|
||||
|
||||
/// Lowercase letter or canonical combo, for passthrough set membership.
|
||||
/// Lowercase letter or canonical combo. Kept as a pure helper for
|
||||
/// hotkey normalisation; the engine-level `passthrough` feature was
|
||||
/// removed but the normalisation utility may still be useful for
|
||||
/// the tree-driven keybind UI.
|
||||
pub fn passthrough_id(raw: &str) -> Result<String> {
|
||||
let p = parse(raw)?;
|
||||
if !p.ctrl && !p.alt && !p.shift && !p.super_key && p.is_letter() {
|
||||
|
||||
@ -195,10 +195,9 @@ async fn doctor(config: Option<PathBuf>) -> Result<()> {
|
||||
match Profile::load(&path) {
|
||||
Ok(p) => {
|
||||
println!(
|
||||
"profile: {} maps={} passthrough={:?} vfx={}",
|
||||
"profile: {} maps={} vfx={}",
|
||||
path.display(),
|
||||
p.maps.len(),
|
||||
p.passthrough,
|
||||
p.video_fx.len()
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::hotkey::{passthrough_id, Hotkey};
|
||||
use crate::hotkey::Hotkey;
|
||||
use anyhow::{bail, Context, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
@ -13,9 +13,6 @@ pub struct Profile {
|
||||
pub slots: u32,
|
||||
#[serde(default)]
|
||||
pub window_match: WindowMatch,
|
||||
/// Keys that are never intercepted. Empty = no skip list.
|
||||
#[serde(default)]
|
||||
pub passthrough: Vec<String>,
|
||||
#[serde(default = "default_mode")]
|
||||
pub mode_default: Mode,
|
||||
#[serde(default)]
|
||||
@ -129,7 +126,7 @@ pub struct Repeater {
|
||||
pub keys: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct Interact {
|
||||
#[serde(default = "default_interact_style")]
|
||||
pub style: InteractStyle,
|
||||
@ -144,18 +141,11 @@ fn default_walk_delay() -> u64 {
|
||||
2500
|
||||
}
|
||||
|
||||
impl Default for Interact {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
style: InteractStyle::Standard,
|
||||
walk_delay_ms: 2500,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum InteractStyle {
|
||||
#[default]
|
||||
Standard,
|
||||
Auto,
|
||||
Hold,
|
||||
@ -189,12 +179,12 @@ pub struct Character {
|
||||
pub auto_apply: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct Group {
|
||||
pub slots: Vec<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct Map {
|
||||
pub name: String,
|
||||
pub hotkey: Hotkey,
|
||||
@ -206,7 +196,7 @@ pub struct Map {
|
||||
pub release_steps: Vec<Step>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct Step {
|
||||
#[serde(default)]
|
||||
pub key: Option<String>,
|
||||
@ -222,7 +212,7 @@ fn default_target() -> String {
|
||||
"others".into()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct VideoFx {
|
||||
pub name: String,
|
||||
#[serde(default = "default_true")]
|
||||
@ -250,7 +240,7 @@ pub enum LayoutPreset {
|
||||
MainStrip,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct Layout {
|
||||
#[serde(default)]
|
||||
pub preset: LayoutPreset,
|
||||
@ -273,23 +263,8 @@ pub struct Layout {
|
||||
pub slots: Vec<LayoutSlot>,
|
||||
}
|
||||
|
||||
impl Default for Layout {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
preset: LayoutPreset::MainStrip,
|
||||
same_size: false,
|
||||
main_at_bottom: false,
|
||||
one_row: true,
|
||||
pin: false,
|
||||
auto_apply: true,
|
||||
borderless: false,
|
||||
monitor: String::new(),
|
||||
slots: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct LayoutSlot {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
@ -302,7 +277,7 @@ fn default_fps() -> u32 {
|
||||
12
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
|
||||
pub struct NormRect {
|
||||
#[serde(default)]
|
||||
pub x: f64,
|
||||
@ -318,16 +293,6 @@ fn default_one() -> f64 {
|
||||
1.0
|
||||
}
|
||||
|
||||
impl Default for NormRect {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
w: 0.28,
|
||||
h: 0.28,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NormRect {
|
||||
/// Values > 1 are pixels inside the window; otherwise fractions 0..=1 of the window.
|
||||
@ -410,13 +375,6 @@ impl Profile {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn passthrough_set(&self) -> Result<HashSet<String>> {
|
||||
let mut set = HashSet::new();
|
||||
for k in &self.passthrough {
|
||||
set.insert(passthrough_id(k)?);
|
||||
}
|
||||
Ok(set)
|
||||
}
|
||||
|
||||
pub fn resolve_bind(&self, name: &str) -> Result<String> {
|
||||
self.game_binds
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use crate::engine::{Action, Engine, Hold};
|
||||
use crate::hotkey::{self, passthrough_id};
|
||||
use crate::hotkey;
|
||||
use crate::hypr::{self, BindSpec, Client};
|
||||
use crate::overlay::OverlayHub;
|
||||
use crate::profile::{runtime_dir, Mode, Profile};
|
||||
@ -356,14 +356,9 @@ async fn on_event(session: &Arc<Mutex<Session>>, line: &str) {
|
||||
}
|
||||
|
||||
fn bind_specs(g: &Session) -> Result<Vec<BindSpec>> {
|
||||
let passthrough = g.engine.profile.passthrough_set()?;
|
||||
let mut specs = Vec::new();
|
||||
let bin = g.exe.display().to_string();
|
||||
for m in &g.engine.profile.maps {
|
||||
let id = passthrough_id(&m.hotkey.0).unwrap_or_else(|_| m.hotkey.0.clone());
|
||||
if passthrough.contains(&id) {
|
||||
continue;
|
||||
}
|
||||
let parsed = m.hotkey.parse()?;
|
||||
specs.push(BindSpec {
|
||||
bind: parsed.hypr_bind(),
|
||||
@ -389,10 +384,6 @@ fn bind_specs(g: &Session) -> Result<Vec<BindSpec>> {
|
||||
g.engine.profile.repeater.keys.clone()
|
||||
};
|
||||
for k in keys {
|
||||
let id = passthrough_id(&k).unwrap_or_else(|_| k.clone());
|
||||
if passthrough.contains(&id) {
|
||||
continue;
|
||||
}
|
||||
if g.engine.profile.map_by_hotkey(&k).is_some() {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -201,7 +201,6 @@ mod tests {
|
||||
client: "wow-retail".into(),
|
||||
slots: 2,
|
||||
window_match: Default::default(),
|
||||
passthrough: vec!["e".into(), "s".into(), "d".into(), "f".into()],
|
||||
mode_default: Mode::Maps,
|
||||
repeater: Default::default(),
|
||||
game_binds: BTreeMap::new(),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user