1use std::collections::BTreeMap;
2use std::fmt;
3
4use serde::{Deserialize, Serialize};
5
6use crate::device::{Axis, Button, Control, Key, MouseButton, Sign, Stick, Wheel};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub enum ActionKind {
10 Digital,
11 Axis,
12 Stick,
13}
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
16pub struct Repeat {
17 pub delay: u32,
18 pub interval: u32,
19}
20
21#[derive(Clone, Debug, PartialEq)]
22pub struct ActionSpec {
23 pub name: String,
24 pub kind: ActionKind,
25 pub context: String,
26 pub dead_zone: f32,
27 pub repeat: Option<Repeat>,
28 pub keyboard: Vec<Binding>,
29 pub gamepad: Vec<Binding>,
30}
31
32impl ActionSpec {
33 fn new(name: &str, kind: ActionKind, dead_zone: f32) -> Self {
34 Self {
35 name: name.to_owned(),
36 kind,
37 context: String::new(),
38 dead_zone,
39 repeat: None,
40 keyboard: Vec::new(),
41 gamepad: Vec::new(),
42 }
43 }
44
45 pub fn digital(name: &str) -> Self {
46 Self::new(name, ActionKind::Digital, 0.0)
47 }
48
49 pub fn axis(name: &str) -> Self {
50 Self::new(name, ActionKind::Axis, 0.2)
51 }
52
53 pub fn stick(name: &str) -> Self {
54 Self::new(name, ActionKind::Stick, 0.2)
55 }
56
57 pub fn context(mut self, context: &str) -> Self {
58 context.clone_into(&mut self.context);
59 self
60 }
61
62 pub fn dead_zone(mut self, dead_zone: f32) -> Self {
63 self.dead_zone = dead_zone.clamp(0.0, 0.95);
64 self
65 }
66
67 pub fn repeat(mut self, delay: u32, interval: u32) -> Self {
68 self.repeat = Some(Repeat {
69 delay: delay.max(1),
70 interval: interval.max(1),
71 });
72 self
73 }
74
75 pub fn key(mut self, binding: Binding) -> Self {
76 self.keyboard.push(binding);
77 self
78 }
79
80 pub fn pad(mut self, binding: Binding) -> Self {
81 self.gamepad.push(binding);
82 self
83 }
84}
85
86#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
87pub struct ActionId(pub u16);
88
89#[derive(Clone, Debug, Default, PartialEq)]
90pub struct Actions {
91 specs: Vec<ActionSpec>,
92 names: BTreeMap<String, ActionId>,
93}
94
95#[derive(Clone, Debug, PartialEq, Eq)]
96pub enum ActionsError {
97 Duplicate(String),
98 TooMany,
99}
100
101impl fmt::Display for ActionsError {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 match self {
104 ActionsError::Duplicate(name) => write!(f, "action {name:?} is named twice"),
105 ActionsError::TooMany => write!(f, "more than 65535 actions"),
106 }
107 }
108}
109
110impl std::error::Error for ActionsError {}
111
112impl Actions {
113 pub fn new(specs: Vec<ActionSpec>) -> Result<Self, ActionsError> {
114 if specs.len() > usize::from(u16::MAX) {
115 return Err(ActionsError::TooMany);
116 }
117 let mut names = BTreeMap::new();
118 for (index, spec) in specs.iter().enumerate() {
119 if names
120 .insert(spec.name.clone(), ActionId(index as u16))
121 .is_some()
122 {
123 return Err(ActionsError::Duplicate(spec.name.clone()));
124 }
125 }
126 Ok(Self { specs, names })
127 }
128
129 pub fn id(&self, name: &str) -> Option<ActionId> {
130 self.names.get(name).copied()
131 }
132
133 pub fn spec(&self, id: ActionId) -> &ActionSpec {
134 &self.specs[usize::from(id.0)]
135 }
136
137 pub fn len(&self) -> usize {
138 self.specs.len()
139 }
140
141 pub fn is_empty(&self) -> bool {
142 self.specs.is_empty()
143 }
144
145 pub fn iter(&self) -> impl Iterator<Item = (ActionId, &ActionSpec)> {
146 self.specs
147 .iter()
148 .enumerate()
149 .map(|(index, spec)| (ActionId(index as u16), spec))
150 }
151}
152
153#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
154pub enum Source {
155 Key(Key),
156 Mouse(MouseButton),
157 Wheel(Wheel),
158 Button(Button),
159 Axis(Axis, Sign),
160}
161
162impl Source {
163 pub fn is_keyboard(self) -> bool {
164 matches!(self, Source::Key(_))
165 }
166
167 pub fn is_mouse(self) -> bool {
168 matches!(self, Source::Mouse(_) | Source::Wheel(_))
169 }
170
171 pub fn is_gamepad(self) -> bool {
172 matches!(self, Source::Button(_) | Source::Axis(..))
173 }
174
175 pub fn control(self) -> Control {
176 match self {
177 Source::Key(key) => Control::Key(key),
178 Source::Mouse(button) => Control::Mouse(button),
179 Source::Wheel(wheel) => Control::Wheel(wheel),
180 Source::Button(button) => Control::Button(button),
181 Source::Axis(axis, _) => Control::Stick(axis.stick()),
182 }
183 }
184}
185
186#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
187pub enum Binding {
188 Press(Source),
189 Axis {
190 negative: Source,
191 positive: Source,
192 },
193 PadAxis(Axis),
194 Stick {
195 up: Source,
196 down: Source,
197 left: Source,
198 right: Source,
199 },
200 PadStick(Stick),
201}
202
203impl Binding {
204 pub fn key(key: Key) -> Self {
205 Binding::Press(Source::Key(key))
206 }
207
208 pub fn mouse(button: MouseButton) -> Self {
209 Binding::Press(Source::Mouse(button))
210 }
211
212 pub fn button(button: Button) -> Self {
213 Binding::Press(Source::Button(button))
214 }
215
216 pub fn keys(negative: Key, positive: Key) -> Self {
217 Binding::Axis {
218 negative: Source::Key(negative),
219 positive: Source::Key(positive),
220 }
221 }
222
223 pub fn buttons(negative: Button, positive: Button) -> Self {
224 Binding::Axis {
225 negative: Source::Button(negative),
226 positive: Source::Button(positive),
227 }
228 }
229
230 pub fn key_stick(up: Key, down: Key, left: Key, right: Key) -> Self {
231 Binding::Stick {
232 up: Source::Key(up),
233 down: Source::Key(down),
234 left: Source::Key(left),
235 right: Source::Key(right),
236 }
237 }
238
239 pub fn dpad() -> Self {
240 Binding::Stick {
241 up: Source::Button(Button::DPadUp),
242 down: Source::Button(Button::DPadDown),
243 left: Source::Button(Button::DPadLeft),
244 right: Source::Button(Button::DPadRight),
245 }
246 }
247
248 pub fn sources(&self) -> Vec<Source> {
249 match *self {
250 Binding::Press(source) => vec![source],
251 Binding::Axis { negative, positive } => vec![negative, positive],
252 Binding::PadAxis(axis) => vec![
253 Source::Axis(axis, Sign::Negative),
254 Source::Axis(axis, Sign::Positive),
255 ],
256 Binding::Stick {
257 up,
258 down,
259 left,
260 right,
261 } => vec![up, down, left, right],
262 Binding::PadStick(stick) => {
263 let (x, y) = stick.axes();
264 vec![
265 Source::Axis(y, Sign::Positive),
266 Source::Axis(y, Sign::Negative),
267 Source::Axis(x, Sign::Negative),
268 Source::Axis(x, Sign::Positive),
269 ]
270 }
271 }
272 }
273
274 pub fn part_count(&self) -> usize {
275 match self {
276 Binding::Press(_) => 1,
277 Binding::Axis { .. } => 2,
278 Binding::Stick { .. } => 4,
279 Binding::PadAxis(_) | Binding::PadStick(_) => 0,
280 }
281 }
282
283 fn part(&self, part: usize) -> Option<Source> {
284 if part >= self.part_count() {
285 return None;
286 }
287 self.sources().get(part).copied()
288 }
289
290 fn with_part(&self, part: usize, source: Source) -> Option<Binding> {
291 let mut binding = *self;
292 match &mut binding {
293 Binding::Press(slot) if part == 0 => *slot = source,
294 Binding::Axis { negative, .. } if part == 0 => *negative = source,
295 Binding::Axis { positive, .. } if part == 1 => *positive = source,
296 Binding::Stick { up, .. } if part == 0 => *up = source,
297 Binding::Stick { down, .. } if part == 1 => *down = source,
298 Binding::Stick { left, .. } if part == 2 => *left = source,
299 Binding::Stick { right, .. } if part == 3 => *right = source,
300 _ => return None,
301 }
302 Some(binding)
303 }
304
305 pub fn controls(&self) -> Vec<Control> {
306 match *self {
307 Binding::PadAxis(axis) => vec![Control::Stick(axis.stick())],
308 Binding::PadStick(stick) => vec![Control::Stick(stick)],
309 Binding::Stick {
310 up: Source::Button(Button::DPadUp),
311 down: Source::Button(Button::DPadDown),
312 left: Source::Button(Button::DPadLeft),
313 right: Source::Button(Button::DPadRight),
314 } => vec![Control::DPad],
315 _ => {
316 let mut controls = Vec::new();
317 for source in self.sources() {
318 let control = source.control();
319 if !controls.contains(&control) {
320 controls.push(control);
321 }
322 }
323 controls
324 }
325 }
326 }
327}
328
329#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
330pub enum Set {
331 Keyboard,
332 Gamepad,
333}
334
335#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
336pub struct Slot {
337 pub set: Set,
338 pub index: usize,
339 pub part: usize,
340}
341
342impl Slot {
343 pub fn keyboard(index: usize, part: usize) -> Self {
344 Self {
345 set: Set::Keyboard,
346 index,
347 part,
348 }
349 }
350}
351
352#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
353pub enum OnConflict {
354 Refuse,
355 Swap,
356 Replace,
357}
358
359#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
360pub struct Conflict {
361 pub action: String,
362 pub slot: Slot,
363 pub source: Source,
364}
365
366#[derive(Clone, Debug, PartialEq, Eq)]
367pub enum RebindError {
368 UnknownAction(String),
369 NoSlot(Slot),
370 WrongDevice(Source),
371 Conflicts(Vec<Conflict>),
372}
373
374impl fmt::Display for RebindError {
375 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376 match self {
377 RebindError::UnknownAction(name) => write!(f, "no action named {name:?}"),
378 RebindError::NoSlot(slot) => write!(f, "no binding at {slot:?}"),
379 RebindError::WrongDevice(source) => {
380 write!(f, "{source:?} is not a keyboard or mouse control")
381 }
382 RebindError::Conflicts(conflicts) => {
383 write!(f, "already bound to ")?;
384 for (index, conflict) in conflicts.iter().enumerate() {
385 if index > 0 {
386 write!(f, ", ")?;
387 }
388 write!(f, "{}", conflict.action)?;
389 }
390 Ok(())
391 }
392 }
393 }
394}
395
396impl std::error::Error for RebindError {}
397
398#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
399pub struct BindingMap {
400 pub keyboard: BTreeMap<String, Vec<Binding>>,
401 pub gamepad: BTreeMap<String, Vec<Binding>>,
402}
403
404impl BindingMap {
405 pub fn defaults(actions: &Actions) -> Self {
406 let mut map = Self::default();
407 for (_, spec) in actions.iter() {
408 map.keyboard
409 .insert(spec.name.clone(), spec.keyboard.clone());
410 map.gamepad.insert(spec.name.clone(), spec.gamepad.clone());
411 }
412 map
413 }
414
415 pub fn fit(&mut self, actions: &Actions) {
416 self.keyboard.retain(|name, _| actions.id(name).is_some());
417 self.gamepad.retain(|name, _| actions.id(name).is_some());
418 for (_, spec) in actions.iter() {
419 self.keyboard
420 .entry(spec.name.clone())
421 .or_insert_with(|| spec.keyboard.clone());
422 self.gamepad
423 .entry(spec.name.clone())
424 .or_insert_with(|| spec.gamepad.clone());
425 }
426 }
427
428 pub fn reset(&mut self, actions: &Actions, name: &str) -> Result<(), RebindError> {
429 let id = actions
430 .id(name)
431 .ok_or_else(|| RebindError::UnknownAction(name.to_owned()))?;
432 let spec = actions.spec(id);
433 self.keyboard
434 .insert(spec.name.clone(), spec.keyboard.clone());
435 self.gamepad.insert(spec.name.clone(), spec.gamepad.clone());
436 Ok(())
437 }
438
439 pub fn set(&self, set: Set) -> &BTreeMap<String, Vec<Binding>> {
440 match set {
441 Set::Keyboard => &self.keyboard,
442 Set::Gamepad => &self.gamepad,
443 }
444 }
445
446 fn set_mut(&mut self, set: Set) -> &mut BTreeMap<String, Vec<Binding>> {
447 match set {
448 Set::Keyboard => &mut self.keyboard,
449 Set::Gamepad => &mut self.gamepad,
450 }
451 }
452
453 pub fn bindings(&self, set: Set, name: &str) -> &[Binding] {
454 self.set(set).get(name).map_or(&[], Vec::as_slice)
455 }
456
457 pub fn source_at(&self, name: &str, slot: Slot) -> Option<Source> {
458 self.bindings(slot.set, name)
459 .get(slot.index)?
460 .part(slot.part)
461 }
462
463 pub fn conflicts(
464 &self,
465 actions: &Actions,
466 name: &str,
467 slot: Slot,
468 source: Source,
469 ) -> Vec<Conflict> {
470 let Some(id) = actions.id(name) else {
471 return Vec::new();
472 };
473 let context = &actions.spec(id).context;
474 let mut found = Vec::new();
475 for (other_name, bindings) in self.set(slot.set) {
476 let Some(other) = actions.id(other_name) else {
477 continue;
478 };
479 if &actions.spec(other).context != context {
480 continue;
481 }
482 for (index, binding) in bindings.iter().enumerate() {
483 for part in 0..binding.part_count() {
484 let at = Slot {
485 set: slot.set,
486 index,
487 part,
488 };
489 if other_name == name && at == slot {
490 continue;
491 }
492 if binding.part(part) == Some(source) {
493 found.push(Conflict {
494 action: other_name.clone(),
495 slot: at,
496 source,
497 });
498 }
499 }
500 }
501 }
502 found
503 }
504
505 pub fn rebind_key(
506 &mut self,
507 actions: &Actions,
508 name: &str,
509 slot: Slot,
510 source: Source,
511 on_conflict: OnConflict,
512 ) -> Result<Vec<Conflict>, RebindError> {
513 if slot.set != Set::Keyboard || !(source.is_keyboard() || source.is_mouse()) {
514 return Err(RebindError::WrongDevice(source));
515 }
516 self.rebind(actions, name, slot, source, on_conflict)
517 }
518
519 pub fn rebind(
520 &mut self,
521 actions: &Actions,
522 name: &str,
523 slot: Slot,
524 source: Source,
525 on_conflict: OnConflict,
526 ) -> Result<Vec<Conflict>, RebindError> {
527 if actions.id(name).is_none() {
528 return Err(RebindError::UnknownAction(name.to_owned()));
529 }
530 let previous = self
531 .source_at(name, slot)
532 .ok_or(RebindError::NoSlot(slot))?;
533 let conflicts = self.conflicts(actions, name, slot, source);
534 let mut slot = slot;
535 if !conflicts.is_empty() {
536 match on_conflict {
537 OnConflict::Refuse => return Err(RebindError::Conflicts(conflicts)),
538 OnConflict::Swap => {
539 for conflict in &conflicts {
540 self.write(&conflict.action, conflict.slot, previous);
541 }
542 }
543 OnConflict::Replace => {
544 let mut dropped: Vec<(&str, usize)> = Vec::new();
545 for conflict in conflicts.iter().rev() {
546 if conflict.action == name && conflict.slot.index == slot.index {
547 self.write(name, conflict.slot, previous);
548 continue;
549 }
550 if dropped.contains(&(conflict.action.as_str(), conflict.slot.index)) {
551 continue;
552 }
553 dropped.push((conflict.action.as_str(), conflict.slot.index));
554 self.drop_binding(&conflict.action, conflict.slot);
555 if conflict.action == name && conflict.slot.index < slot.index {
556 slot.index -= 1;
557 }
558 }
559 }
560 }
561 }
562 self.write(name, slot, source);
563 Ok(conflicts)
564 }
565
566 fn write(&mut self, name: &str, slot: Slot, source: Source) {
567 if let Some(binding) = self
568 .set_mut(slot.set)
569 .get_mut(name)
570 .and_then(|bindings| bindings.get_mut(slot.index))
571 && let Some(next) = binding.with_part(slot.part, source)
572 {
573 *binding = next;
574 }
575 }
576
577 fn drop_binding(&mut self, name: &str, slot: Slot) {
578 if let Some(bindings) = self.set_mut(slot.set).get_mut(name)
579 && slot.index < bindings.len()
580 {
581 bindings.remove(slot.index);
582 }
583 }
584
585 pub fn add(&mut self, name: &str, set: Set, binding: Binding) {
586 self.set_mut(set)
587 .entry(name.to_owned())
588 .or_default()
589 .push(binding);
590 }
591
592 pub fn remove(&mut self, name: &str, set: Set, index: usize) -> Option<Binding> {
593 let bindings = self.set_mut(set).get_mut(name)?;
594 (index < bindings.len()).then(|| bindings.remove(index))
595 }
596}