input_actions/
system.rs

1use crate::{
2	action,
3	binding::{self, ActionSet, ActionSetId, LayoutId},
4	device::{self, GamepadKind},
5	event,
6	source::{Axis, Button},
7	User,
8};
9use std::{collections::HashMap, time::Instant};
10
11pub type UserId = usize;
12
13/// Contains the setup for a particular application.
14pub struct System {
15	gamepad_input: gilrs::Gilrs,
16	users: Vec<User>,
17	actions: HashMap<action::Id, action::Action>,
18	layouts: Vec<LayoutId>,
19	action_sets: HashMap<ActionSetId, ActionSet>,
20	unassigned_devices: Vec<device::Id>,
21	device_to_user: HashMap<device::Id, UserId>,
22	disconnected_device_users: HashMap<device::Id, UserId>,
23}
24
25impl System {
26	pub fn new() -> Self {
27		Self {
28			gamepad_input: gilrs::Gilrs::new().unwrap(),
29			users: Vec::new(),
30			actions: HashMap::new(),
31			layouts: Vec::new(),
32			action_sets: HashMap::new(),
33			unassigned_devices: vec![device::Id::Mouse, device::Id::Keyboard],
34			device_to_user: HashMap::new(),
35			disconnected_device_users: HashMap::new(),
36		}
37		.initialize_gamepads()
38	}
39
40	/// Grabs all gamepads from gilrs and attempts to connect them (or cache them if there are no users).
41	/// User internally when constructing the singleton.
42	fn initialize_gamepads(mut self) -> Self {
43		let existing_gamepad_ids = self
44			.gamepad_input
45			.gamepads()
46			.map(|(id, _)| id)
47			.collect::<Vec<_>>();
48		for id in existing_gamepad_ids {
49			self.connect_gamepad(id);
50		}
51		self
52	}
53
54	/// Adds an amount of users to the system,
55	/// connecting any unassigned devices to the new users as available.
56	pub fn add_users(&mut self, count: usize) -> &mut Self {
57		self.users.extend((0..count).map(|_| User::default()));
58		self.assign_unused_devices();
59		self
60	}
61
62	/// Adds an action to the list of actions the system supports.
63	pub fn add_action(&mut self, name: action::Id, action: action::Action) -> &mut Self {
64		self.actions.insert(name, action);
65		self
66	}
67
68	/// Adds a layout to the list of layouts the system supports.
69	pub fn add_layout(&mut self, layout: LayoutId) -> &mut Self {
70		self.layouts.push(layout);
71		self
72	}
73
74	/// Associates an [`action set`](ActionSet) with an [`id`](ActionSetId).
75	pub fn add_action_set(&mut self, id: ActionSetId, set: ActionSet) -> &mut Self {
76		self.action_sets.insert(id, set);
77		self
78	}
79
80	/// Sets the layout of a user.
81	/// If not called, all user's start with a `None` layout (default layout).
82	pub fn set_user_layout(&mut self, user_id: UserId, layout: LayoutId) -> &mut Self {
83		if let Some(user) = self.users.get_mut(user_id) {
84			user.set_layout(layout, &self.actions);
85		}
86		self
87	}
88
89	/// Enables and disables a provided [`action set`](ActionSet) for a given user.
90	/// When enabled, a user will receive input events for the actions in the [`action set`](ActionSet),
91	/// until the set is disabled (or until [`System::update`] stops being called).
92	pub fn mark_action_set_enabled(
93		&mut self,
94		user_id: UserId,
95		set_id: ActionSetId,
96		enabled: bool,
97	) -> &mut Self {
98		if let Some(user) = self.users.get_mut(user_id) {
99			if enabled {
100				if let Some(action_set) = self.action_sets.get(&set_id) {
101					user.enable_action_set(set_id, action_set, &self.actions);
102				}
103			} else {
104				user.disable_action_set(set_id);
105			}
106		}
107		self
108	}
109
110	/// Enables an [`action set`](ActionSet) for all existing users.
111	/// See [`System::mark_action_set_enabled`] for further details.
112	pub fn enable_action_set_for_all(&mut self, id: ActionSetId) -> &mut Self {
113		if let Some(action_set) = self.action_sets.get(&id) {
114			for user in self.users.iter_mut() {
115				user.enable_action_set(id, action_set, &self.actions);
116			}
117		}
118		self
119	}
120
121	/// Iterates over all unassigned devices and attempts to assign them to users.
122	/// Used predominately to assign devices to users on initialization
123	/// (where users are added after the system queries all the gamepads).
124	fn assign_unused_devices(&mut self) {
125		let unused_devices = self.unassigned_devices.drain(..).collect::<Vec<_>>();
126		for device in unused_devices {
127			match device {
128				// Mouse and Keyboard devices should always go to the first user
129				device::Id::Mouse | device::Id::Keyboard => {
130					if let Some(first_user_id) = self.users.iter().position(|_| true) {
131						self.assign_device(device, first_user_id);
132					} else {
133						self.unassigned_devices.push(device);
134					}
135				}
136				// Assign gamepads to users without gamepads
137				device::Id::Gamepad(_, _) => {
138					if let Some(user_id) = self
139						.users
140						.iter()
141						.position(|user| !user.has_gamepad_device())
142					{
143						self.assign_device(device, user_id);
144					} else {
145						self.unassigned_devices.push(device);
146					}
147				}
148			}
149		}
150	}
151
152	/// Assigns a device to a specific user - does not validate if this operation is actually desired.
153	fn assign_device(&mut self, device: device::Id, user_id: UserId) {
154		self.users[user_id].add_device(device);
155		self.device_to_user.insert(device, user_id);
156		if cfg!(feature = "log") {
157			log::info!(
158				target: crate::LOG,
159				"assigning {} to user {}",
160				device,
161				user_id
162			);
163		}
164	}
165
166	/// Unassigns a device from a user it may belong to.
167	/// The device is put in `unassigned_devices` if it had no user,
168	/// or in `disconnected_device_users` if it had a user so that we can track the device
169	/// should it become available again.
170	fn unassign_device(&mut self, device: device::Id) {
171		if let Some(user_id) = self.device_to_user.remove(&device) {
172			self.users[user_id].remove_device(device);
173			self.disconnected_device_users.insert(device, user_id);
174			if cfg!(feature = "log") {
175				log::info!(
176					target: crate::LOG,
177					"unassigning {} from user {}",
178					device,
179					user_id
180				);
181			}
182		} else {
183			self.unassigned_devices.push(device);
184		}
185	}
186
187	fn get_gamepad_kind(&self, _id: &gilrs::GamepadId) -> GamepadKind {
188		// GILRS seems to always provide "Xbox Controller" as the gamepad name (`name()` AND `os_name()`)
189		// regardless of what kind of controller is actually is.
190		// Until this can be addressed, assume all controllers are Dual-Axis-Gamepad.
191		// let gamepad = self.gamepad_input.gamepad(id);
192		GamepadKind::DualAxisGamepad
193	}
194
195	/// Connects a gamepad to user data.
196	///
197	/// If the gamepad previously disconnected from a user, it is automatically assigned to the same user
198	/// (assuming the provided `id` is the same).
199	///
200	/// If user already has another gamepad or the gamepad was never previously connected,
201	/// then it is assigned to the first user without a gamepad.
202	fn connect_gamepad(&mut self, id: gilrs::GamepadId) {
203		let device = device::Id::Gamepad(self.get_gamepad_kind(&id), id);
204
205		if let Some(user_id) = self.disconnected_device_users.remove(&device) {
206			if !self.users[user_id].has_gamepad_device() {
207				self.assign_device(device, user_id);
208				return;
209			}
210		}
211
212		if let Some(user_id) = self
213			.users
214			.iter()
215			.position(|user| !user.has_gamepad_device())
216		{
217			self.assign_device(device, user_id);
218			return;
219		}
220
221		self.unassigned_devices.push(device);
222	}
223
224	/// Unassigns a gilrs gamepad from an user it may be assigned to.
225	fn disconnect_gamepad(&mut self, id: gilrs::GamepadId) {
226		self.unassign_device(device::Id::Gamepad(self.get_gamepad_kind(&id), id));
227	}
228
229	/// Queries the gilrs system to get all gamepad input events.
230	/// Sends relevant events to `process_event` (or connects/disconnects the gamepad if required).
231	fn read_gamepad_events(&mut self) {
232		use gilrs::EventType;
233		use std::convert::TryFrom;
234		while let Some(gilrs::Event {
235			id,
236			event, /*system time*/
237			..
238		}) = self.gamepad_input.next_event()
239		{
240			let time = Instant::now();
241			let gamepad_kind = self.get_gamepad_kind(&id);
242			let device = device::Id::Gamepad(gamepad_kind, id);
243			match event {
244				// Gamepad has been connected. If gamepad's UUID doesn't match one of disconnected gamepads,
245				// newly connected gamepad will get new ID.
246				EventType::Connected => self.connect_gamepad(id),
247				// Gamepad has been disconnected. Disconnected gamepad will not generate any new events.
248				EventType::Disconnected => self.disconnect_gamepad(id),
249				// There was an `Event`, but it was dropped by one of filters. You should ignore it.
250				EventType::Dropped => {}
251				// Some button on gamepad has been pressed.
252				EventType::ButtonPressed(btn, _) => {
253					if let Some(button) = Button::try_from(btn).ok() {
254						self.process_event(
255							device,
256							event::Event::new(
257								binding::Source::Gamepad(
258									gamepad_kind,
259									binding::Gamepad::Button(button),
260								),
261								event::State::ButtonState(event::ButtonState::Pressed),
262							),
263							time,
264						);
265					}
266				}
267				// Previously pressed button has been released.
268				EventType::ButtonReleased(btn, _) => {
269					if let Some(button) = Button::try_from(btn).ok() {
270						self.process_event(
271							device,
272							event::Event::new(
273								binding::Source::Gamepad(
274									gamepad_kind,
275									binding::Gamepad::Button(button),
276								),
277								event::State::ButtonState(event::ButtonState::Released),
278							),
279							time,
280						);
281					}
282				}
283				// This event can be generated by [`ev::Repeat`](filter/struct.Repeat.html) event filter.
284				EventType::ButtonRepeated(_btn, _) => {}
285				// Value of button has changed. Value can be in range [0.0, 1.0].
286				EventType::ButtonChanged(btn, value, _) => {
287					if let Some(button) = Button::try_from(btn).ok() {
288						self.process_event(
289							device,
290							event::Event::new(
291								binding::Source::Gamepad(
292									gamepad_kind,
293									binding::Gamepad::Button(button),
294								),
295								event::State::ValueChanged(value),
296							),
297							time,
298						);
299					}
300				}
301				// Value of axis has changed. Value can be in range [-1.0, 1.0].
302				EventType::AxisChanged(axis, value, _) => {
303					if let Some(axis) = Axis::try_from(axis).ok() {
304						self.process_event(
305							device,
306							event::Event::new(
307								binding::Source::Gamepad(
308									gamepad_kind,
309									binding::Gamepad::Axis(axis),
310								),
311								event::State::ValueChanged(value),
312							),
313							time,
314						);
315					}
316				}
317			}
318		}
319	}
320
321	/// Sends an input event to the system.
322	/// Use with caution! Gamepad events are already handled/built-in,
323	/// but Mouse and Keyboard events should come from the relevant feature/extension (like winit).
324	pub fn send_event(&mut self, source: event::Source, event: event::Event) {
325		self.process_event(
326			match source {
327				event::Source::Mouse => device::Id::Mouse,
328				event::Source::Keyboard => device::Id::Keyboard,
329			},
330			event,
331			Instant::now(),
332		);
333	}
334
335	/// Parses and processes a provided input event from a device.
336	fn process_event(&mut self, device: device::Id, event: event::Event, time: Instant) {
337		for event in self.parse_event(event) {
338			self.update_user_actions(device, event, time);
339		}
340	}
341
342	// Based on the platform and the event, we may need to split the event into multiple events.
343	// For example: the bottom and right face buttons on a gamepad may need to also trigger
344	// `Button::VirtualConfirm` or `Button::VirtualDeny` in addition to the original button.
345	fn parse_event(&mut self, event: event::Event) -> Vec<event::Event> {
346		let mut events = vec![event.clone()];
347		if let event::Event {
348			source: binding::Source::Gamepad(kind, binding::Gamepad::Button(Button::FaceBottom)),
349			..
350		} = event
351		{
352			events.push(event::Event {
353				source: binding::Source::Gamepad(
354					kind,
355					binding::Gamepad::Button(Button::VirtualConfirm),
356				),
357				..event
358			});
359		}
360		if let event::Event {
361			source: binding::Source::Gamepad(kind, binding::Gamepad::Button(Button::FaceRight)),
362			..
363		} = event
364		{
365			events.push(event::Event {
366				source: binding::Source::Gamepad(
367					kind,
368					binding::Gamepad::Button(Button::VirtualDeny),
369				),
370				..event
371			});
372		}
373		events
374	}
375
376	/// Processes an event for a specific user based on the device.
377	fn update_user_actions(&mut self, device: device::Id, event: event::Event, time: Instant) {
378		if let Some(user_id) = self.device_to_user.get(&device) {
379			if let Some(user) = self.users.get_mut(*user_id) {
380				user.process_event(&event, &time);
381			}
382		}
383	}
384
385	/// Collects gamepad input and updates relevant actions.
386	pub fn update(&mut self) {
387		self.read_gamepad_events();
388		let time = Instant::now();
389		for user in self.users.iter_mut() {
390			user.update(&time);
391		}
392	}
393
394	/// Returns a list of active user ids.
395	pub fn get_user_ids(&self) -> Vec<UserId> {
396		(0..self.users.len()).collect()
397	}
398
399	/// Returns the state for an action on a given user.
400	/// If the action is invalid or is not enabled for the user's layout
401	/// or list of enabled action sets, `None` will be returned.
402	pub fn get_user_action(
403		&self,
404		user_id: UserId,
405		action_id: action::Id,
406	) -> Option<&action::State> {
407		self.users
408			.get(user_id)
409			.map(|user| user.get_action(action_id))
410			.flatten()
411	}
412}